From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- ui/qt/CMakeLists.txt | 873 + ui/qt/CMakeListsCustom.txt.example | 23 + ui/qt/about_dialog.cpp | 646 + ui/qt/about_dialog.h | 105 + ui/qt/about_dialog.ui | 334 + ui/qt/accordion_frame.cpp | 107 + ui/qt/accordion_frame.h | 42 + ui/qt/address_editor_frame.cpp | 300 + ui/qt/address_editor_frame.h | 63 + ui/qt/address_editor_frame.ui | 138 + ui/qt/bluetooth_att_server_attributes_dialog.cpp | 385 + ui/qt/bluetooth_att_server_attributes_dialog.h | 85 + ui/qt/bluetooth_att_server_attributes_dialog.ui | 250 + ui/qt/bluetooth_device_dialog.cpp | 688 + ui/qt/bluetooth_device_dialog.h | 102 + ui/qt/bluetooth_device_dialog.ui | 300 + ui/qt/bluetooth_devices_dialog.cpp | 455 + ui/qt/bluetooth_devices_dialog.h | 86 + ui/qt/bluetooth_devices_dialog.ui | 233 + ui/qt/bluetooth_hci_summary_dialog.cpp | 935 + ui/qt/bluetooth_hci_summary_dialog.h | 105 + ui/qt/bluetooth_hci_summary_dialog.ui | 755 + ui/qt/byte_view_tab.cpp | 373 + ui/qt/byte_view_tab.h | 78 + ui/qt/capture_event.h | 68 + ui/qt/capture_file.cpp | 386 + ui/qt/capture_file.h | 164 + ui/qt/capture_file_dialog.cpp | 1036 + ui/qt/capture_file_dialog.h | 146 + ui/qt/capture_file_properties_dialog.cpp | 650 + ui/qt/capture_file_properties_dialog.h | 71 + ui/qt/capture_file_properties_dialog.ui | 86 + ui/qt/capture_filter_syntax_worker.cpp | 147 + ui/qt/capture_filter_syntax_worker.h | 31 + ui/qt/capture_info_dialog.cpp | 205 + ui/qt/capture_info_dialog.h | 72 + ui/qt/capture_info_dialog.ui | 90 + ui/qt/capture_options_dialog.cpp | 1534 + ui/qt/capture_options_dialog.h | 126 + ui/qt/capture_options_dialog.ui | 976 + ui/qt/capture_preferences_frame.cpp | 184 + ui/qt/capture_preferences_frame.h | 55 + ui/qt/capture_preferences_frame.ui | 156 + ui/qt/coloring_rules_dialog.cpp | 459 + ui/qt/coloring_rules_dialog.h | 79 + ui/qt/coloring_rules_dialog.ui | 245 + ui/qt/column_editor_frame.cpp | 198 + ui/qt/column_editor_frame.h | 52 + ui/qt/column_editor_frame.ui | 169 + ui/qt/column_preferences_frame.cpp | 128 + ui/qt/column_preferences_frame.h | 48 + ui/qt/column_preferences_frame.ui | 100 + ui/qt/compiled_filter_output.cpp | 120 + ui/qt/compiled_filter_output.h | 52 + ui/qt/compiled_filter_output.ui | 80 + ui/qt/conversation_colorize_action.cpp | 62 + ui/qt/conversation_colorize_action.h | 65 + ui/qt/conversation_dialog.cpp | 193 + ui/qt/conversation_dialog.h | 49 + ui/qt/conversation_hash_tables_dialog.cpp | 147 + ui/qt/conversation_hash_tables_dialog.h | 34 + ui/qt/conversation_hash_tables_dialog.ui | 67 + ui/qt/credentials_dialog.cpp | 120 + ui/qt/credentials_dialog.h | 47 + ui/qt/credentials_dialog.ui | 51 + ui/qt/decode_as_dialog.cpp | 231 + ui/qt/decode_as_dialog.h | 65 + ui/qt/decode_as_dialog.ui | 161 + ui/qt/display_filter_expression_dialog.cpp | 533 + ui/qt/display_filter_expression_dialog.h | 79 + ui/qt/display_filter_expression_dialog.ui | 292 + ui/qt/dissector_tables_dialog.cpp | 54 + ui/qt/dissector_tables_dialog.h | 37 + ui/qt/dissector_tables_dialog.ui | 92 + ui/qt/enabled_protocols_dialog.cpp | 125 + ui/qt/enabled_protocols_dialog.h | 49 + ui/qt/enabled_protocols_dialog.ui | 146 + ui/qt/endpoint_dialog.cpp | 156 + ui/qt/endpoint_dialog.h | 51 + ui/qt/expert_info_dialog.cpp | 353 + ui/qt/expert_info_dialog.h | 77 + ui/qt/expert_info_dialog.ui | 254 + ui/qt/export_dissection_dialog.cpp | 287 + ui/qt/export_dissection_dialog.h | 66 + ui/qt/export_object_action.cpp | 41 + ui/qt/export_object_action.h | 40 + ui/qt/export_object_dialog.cpp | 303 + ui/qt/export_object_dialog.h | 74 + ui/qt/export_object_dialog.ui | 181 + ui/qt/export_pdu_dialog.cpp | 46 + ui/qt/export_pdu_dialog.h | 35 + ui/qt/export_pdu_dialog.ui | 106 + ui/qt/extcap_argument.cpp | 1036 + ui/qt/extcap_argument.h | 285 + ui/qt/extcap_argument_file.cpp | 170 + ui/qt/extcap_argument_file.h | 46 + ui/qt/extcap_argument_multiselect.cpp | 230 + ui/qt/extcap_argument_multiselect.h | 54 + ui/qt/extcap_options_dialog.cpp | 701 + ui/qt/extcap_options_dialog.h | 69 + ui/qt/extcap_options_dialog.ui | 51 + ui/qt/file_set_dialog.cpp | 157 + ui/qt/file_set_dialog.h | 58 + ui/qt/file_set_dialog.ui | 136 + ui/qt/filter_action.cpp | 299 + ui/qt/filter_action.h | 97 + ui/qt/filter_dialog.cpp | 246 + ui/qt/filter_dialog.h | 84 + ui/qt/filter_dialog.ui | 158 + ui/qt/filter_expression_frame.cpp | 183 + ui/qt/filter_expression_frame.h | 52 + ui/qt/filter_expression_frame.ui | 198 + ui/qt/firewall_rules_dialog.cpp | 206 + ui/qt/firewall_rules_dialog.h | 60 + ui/qt/firewall_rules_dialog.ui | 124 + ui/qt/follow_stream_action.cpp | 29 + ui/qt/follow_stream_action.h | 39 + ui/qt/follow_stream_dialog.cpp | 1202 + ui/qt/follow_stream_dialog.h | 128 + ui/qt/follow_stream_dialog.ui | 158 + ui/qt/font_color_preferences_frame.cpp | 414 + ui/qt/font_color_preferences_frame.h | 82 + ui/qt/font_color_preferences_frame.ui | 401 + ui/qt/funnel_statistics.cpp | 619 + ui/qt/funnel_statistics.h | 126 + ui/qt/funnel_string_dialog.cpp | 103 + ui/qt/funnel_string_dialog.h | 66 + ui/qt/funnel_string_dialog.ui | 67 + ui/qt/funnel_text_dialog.cpp | 236 + ui/qt/funnel_text_dialog.h | 75 + ui/qt/funnel_text_dialog.ui | 88 + ui/qt/geometry_state_dialog.cpp | 69 + ui/qt/geometry_state_dialog.h | 67 + ui/qt/glib_mainloop_on_qeventloop.cpp | 125 + ui/qt/glib_mainloop_on_qeventloop.h | 57 + ui/qt/gpl-template.txt | 8 + ui/qt/gsm_map_summary_dialog.cpp | 385 + ui/qt/gsm_map_summary_dialog.h | 41 + ui/qt/gsm_map_summary_dialog.ui | 71 + ui/qt/i18n.qrc.in | 4 + ui/qt/iax2_analysis_dialog.cpp | 1244 + ui/qt/iax2_analysis_dialog.h | 130 + ui/qt/iax2_analysis_dialog.ui | 391 + ui/qt/import_text_dialog.cpp | 1089 + ui/qt/import_text_dialog.h | 126 + ui/qt/import_text_dialog.ui | 868 + ui/qt/interface_frame.cpp | 542 + ui/qt/interface_frame.h | 99 + ui/qt/interface_frame.ui | 83 + ui/qt/interface_toolbar.cpp | 1018 + ui/qt/interface_toolbar.h | 98 + ui/qt/interface_toolbar.ui | 72 + ui/qt/interface_toolbar_reader.cpp | 181 + ui/qt/interface_toolbar_reader.h | 63 + ui/qt/io_console_dialog.cpp | 139 + ui/qt/io_console_dialog.h | 57 + ui/qt/io_console_dialog.ui | 90 + ui/qt/io_graph_dialog.cpp | 2379 ++ ui/qt/io_graph_dialog.h | 262 + ui/qt/io_graph_dialog.ui | 578 + ui/qt/layout_preferences_frame.cpp | 396 + ui/qt/layout_preferences_frame.h | 82 + ui/qt/layout_preferences_frame.ui | 509 + ui/qt/lbm_lbtrm_transport_dialog.cpp | 1610 + ui/qt/lbm_lbtrm_transport_dialog.h | 110 + ui/qt/lbm_lbtrm_transport_dialog.ui | 836 + ui/qt/lbm_lbtru_transport_dialog.cpp | 2207 ++ ui/qt/lbm_lbtru_transport_dialog.h | 134 + ui/qt/lbm_lbtru_transport_dialog.ui | 1300 + ui/qt/lbm_stream_dialog.cpp | 420 + ui/qt/lbm_stream_dialog.h | 60 + ui/qt/lbm_stream_dialog.ui | 158 + ui/qt/lte_mac_statistics_dialog.cpp | 931 + ui/qt/lte_mac_statistics_dialog.h | 80 + ui/qt/lte_rlc_graph_dialog.cpp | 887 + ui/qt/lte_rlc_graph_dialog.h | 116 + ui/qt/lte_rlc_graph_dialog.ui | 398 + ui/qt/lte_rlc_statistics_dialog.cpp | 1013 + ui/qt/lte_rlc_statistics_dialog.h | 70 + ui/qt/main.cpp | 1149 + ui/qt/main_application.cpp | 1383 + ui/qt/main_application.h | 244 + ui/qt/main_status_bar.cpp | 697 + ui/qt/main_status_bar.h | 103 + ui/qt/main_window.cpp | 205 + ui/qt/main_window.h | 109 + ui/qt/main_window_layout.cpp | 235 + ui/qt/main_window_preferences_frame.cpp | 222 + ui/qt/main_window_preferences_frame.h | 65 + ui/qt/main_window_preferences_frame.ui | 307 + ui/qt/manage_interfaces_dialog.cpp | 640 + ui/qt/manage_interfaces_dialog.h | 82 + ui/qt/manage_interfaces_dialog.ui | 266 + ui/qt/manager/preference_manager.cpp | 70 + ui/qt/manager/preference_manager.h | 64 + ui/qt/manager/wireshark_preference.cpp | 248 + ui/qt/manager/wireshark_preference.h | 38 + ui/qt/manuf_dialog.cpp | 210 + ui/qt/manuf_dialog.h | 44 + ui/qt/manuf_dialog.ui | 146 + ui/qt/models/astringlist_list_model.cpp | 305 + ui/qt/models/astringlist_list_model.h | 107 + ui/qt/models/atap_data_model.cpp | 850 + ui/qt/models/atap_data_model.h | 329 + ui/qt/models/cache_proxy_model.cpp | 100 + ui/qt/models/cache_proxy_model.h | 47 + ui/qt/models/coloring_rules_delegate.cpp | 123 + ui/qt/models/coloring_rules_delegate.h | 43 + ui/qt/models/coloring_rules_model.cpp | 569 + ui/qt/models/coloring_rules_model.h | 102 + ui/qt/models/column_list_model.cpp | 518 + ui/qt/models/column_list_model.h | 96 + ui/qt/models/credentials_model.cpp | 155 + ui/qt/models/credentials_model.h | 53 + ui/qt/models/decode_as_delegate.cpp | 390 + ui/qt/models/decode_as_delegate.h | 59 + ui/qt/models/decode_as_model.cpp | 849 + ui/qt/models/decode_as_model.h | 120 + ui/qt/models/dissector_tables_model.cpp | 434 + ui/qt/models/dissector_tables_model.h | 89 + ui/qt/models/enabled_protocols_model.cpp | 520 + ui/qt/models/enabled_protocols_model.h | 143 + ui/qt/models/expert_info_model.cpp | 424 + ui/qt/models/expert_info_model.h | 128 + ui/qt/models/expert_info_proxy_model.cpp | 290 + ui/qt/models/expert_info_proxy_model.h | 61 + ui/qt/models/export_objects_model.cpp | 316 + ui/qt/models/export_objects_model.h | 91 + ui/qt/models/fileset_entry_model.cpp | 155 + ui/qt/models/fileset_entry_model.h | 52 + ui/qt/models/filter_list_model.cpp | 314 + ui/qt/models/filter_list_model.h | 71 + ui/qt/models/info_proxy_model.cpp | 119 + ui/qt/models/info_proxy_model.h | 47 + ui/qt/models/interface_sort_filter_model.cpp | 396 + ui/qt/models/interface_sort_filter_model.h | 87 + ui/qt/models/interface_tree_cache_model.cpp | 584 + ui/qt/models/interface_tree_cache_model.h | 66 + ui/qt/models/interface_tree_model.cpp | 545 + ui/qt/models/interface_tree_model.h | 99 + ui/qt/models/manuf_table_model.cpp | 220 + ui/qt/models/manuf_table_model.h | 90 + ui/qt/models/numeric_value_chooser_delegate.cpp | 93 + ui/qt/models/numeric_value_chooser_delegate.h | 45 + ui/qt/models/packet_list_model.cpp | 1014 + ui/qt/models/packet_list_model.h | 130 + ui/qt/models/packet_list_record.cpp | 246 + ui/qt/models/packet_list_record.h | 84 + ui/qt/models/path_selection_delegate.cpp | 63 + ui/qt/models/path_selection_delegate.h | 35 + ui/qt/models/percent_bar_delegate.cpp | 91 + ui/qt/models/percent_bar_delegate.h | 52 + ui/qt/models/pref_delegate.cpp | 85 + ui/qt/models/pref_delegate.h | 37 + ui/qt/models/pref_models.cpp | 774 + ui/qt/models/pref_models.h | 165 + ui/qt/models/profile_model.cpp | 1299 + ui/qt/models/profile_model.h | 165 + ui/qt/models/proto_tree_model.cpp | 252 + ui/qt/models/proto_tree_model.h | 48 + ui/qt/models/related_packet_delegate.cpp | 373 + ui/qt/models/related_packet_delegate.h | 51 + ui/qt/models/resolved_addresses_models.cpp | 210 + ui/qt/models/resolved_addresses_models.h | 48 + ui/qt/models/sparkline_delegate.cpp | 109 + ui/qt/models/sparkline_delegate.h | 35 + ui/qt/models/supported_protocols_model.cpp | 261 + ui/qt/models/supported_protocols_model.h | 97 + ui/qt/models/timeline_delegate.cpp | 128 + ui/qt/models/timeline_delegate.h | 66 + ui/qt/models/tree_model_helpers.h | 89 + ui/qt/models/uat_delegate.cpp | 221 + ui/qt/models/uat_delegate.h | 42 + ui/qt/models/uat_model.cpp | 545 + ui/qt/models/uat_model.h | 82 + ui/qt/models/url_link_delegate.cpp | 51 + ui/qt/models/url_link_delegate.h | 36 + ui/qt/models/voip_calls_info_model.cpp | 249 + ui/qt/models/voip_calls_info_model.h | 71 + ui/qt/module_preferences_scroll_area.cpp | 659 + ui/qt/module_preferences_scroll_area.h | 59 + ui/qt/module_preferences_scroll_area.ui | 41 + ui/qt/mtp3_summary_dialog.cpp | 387 + ui/qt/mtp3_summary_dialog.h | 40 + ui/qt/mtp3_summary_dialog.ui | 71 + ui/qt/multicast_statistics_dialog.cpp | 510 + ui/qt/multicast_statistics_dialog.h | 53 + ui/qt/packet_comment_dialog.cpp | 44 + ui/qt/packet_comment_dialog.h | 37 + ui/qt/packet_comment_dialog.ui | 67 + ui/qt/packet_diagram.cpp | 828 + ui/qt/packet_diagram.h | 74 + ui/qt/packet_dialog.cpp | 211 + ui/qt/packet_dialog.h | 61 + ui/qt/packet_dialog.ui | 101 + ui/qt/packet_format_group_box.cpp | 148 + ui/qt/packet_format_group_box.h | 60 + ui/qt/packet_format_group_box.ui | 136 + ui/qt/packet_list.cpp | 2164 ++ ui/qt/packet_list.h | 209 + ui/qt/packet_range_group_box.cpp | 424 + ui/qt/packet_range_group_box.h | 71 + ui/qt/packet_range_group_box.ui | 290 + ui/qt/preference_editor_frame.cpp | 296 + ui/qt/preference_editor_frame.h | 64 + ui/qt/preference_editor_frame.ui | 116 + ui/qt/preferences_dialog.cpp | 337 + ui/qt/preferences_dialog.h | 78 + ui/qt/preferences_dialog.ui | 205 + ui/qt/print_dialog.cpp | 361 + ui/qt/print_dialog.h | 71 + ui/qt/print_dialog.ui | 152 + ui/qt/profile_dialog.cpp | 736 + ui/qt/profile_dialog.h | 94 + ui/qt/profile_dialog.ui | 195 + ui/qt/progress_frame.cpp | 323 + ui/qt/progress_frame.h | 91 + ui/qt/progress_frame.ui | 76 + ui/qt/proto_tree.cpp | 909 + ui/qt/proto_tree.h | 116 + ui/qt/protocol_hierarchy_dialog.cpp | 464 + ui/qt/protocol_hierarchy_dialog.h | 63 + ui/qt/protocol_hierarchy_dialog.ui | 177 + ui/qt/protocol_preferences_menu.cpp | 436 + ui/qt/protocol_preferences_menu.h | 49 + ui/qt/qt6-migration-links.txt | 12 + ui/qt/recent_file_status.cpp | 36 + ui/qt/recent_file_status.h | 33 + ui/qt/remote_capture_dialog.cpp | 169 + ui/qt/remote_capture_dialog.h | 48 + ui/qt/remote_capture_dialog.ui | 179 + ui/qt/remote_settings_dialog.cpp | 77 + ui/qt/remote_settings_dialog.h | 42 + ui/qt/remote_settings_dialog.ui | 164 + ui/qt/resolved_addresses_dialog.cpp | 204 + ui/qt/resolved_addresses_dialog.h | 53 + ui/qt/resolved_addresses_dialog.ui | 271 + ui/qt/response_time_delay_dialog.cpp | 270 + ui/qt/response_time_delay_dialog.h | 54 + ui/qt/rpc_service_response_time_dialog.cpp | 422 + ui/qt/rpc_service_response_time_dialog.h | 71 + ui/qt/rsa_keys_frame.cpp | 273 + ui/qt/rsa_keys_frame.h | 57 + ui/qt/rsa_keys_frame.ui | 117 + ui/qt/rtp_analysis_dialog.cpp | 1191 + ui/qt/rtp_analysis_dialog.h | 174 + ui/qt/rtp_analysis_dialog.ui | 246 + ui/qt/rtp_audio_stream.cpp | 953 + ui/qt/rtp_audio_stream.h | 235 + ui/qt/rtp_player_dialog.cpp | 2798 ++ ui/qt/rtp_player_dialog.h | 314 + ui/qt/rtp_player_dialog.ui | 837 + ui/qt/rtp_stream_dialog.cpp | 1064 + ui/qt/rtp_stream_dialog.h | 133 + ui/qt/rtp_stream_dialog.ui | 396 + ui/qt/scsi_service_response_time_dialog.cpp | 86 + ui/qt/scsi_service_response_time_dialog.h | 34 + ui/qt/sctp_all_assocs_dialog.cpp | 118 + ui/qt/sctp_all_assocs_dialog.h | 58 + ui/qt/sctp_all_assocs_dialog.ui | 166 + ui/qt/sctp_assoc_analyse_dialog.cpp | 322 + ui/qt/sctp_assoc_analyse_dialog.h | 77 + ui/qt/sctp_assoc_analyse_dialog.ui | 610 + ui/qt/sctp_chunk_statistics_dialog.cpp | 330 + ui/qt/sctp_chunk_statistics_dialog.h | 83 + ui/qt/sctp_chunk_statistics_dialog.ui | 157 + ui/qt/sctp_graph_arwnd_dialog.cpp | 178 + ui/qt/sctp_graph_arwnd_dialog.h | 62 + ui/qt/sctp_graph_arwnd_dialog.ui | 125 + ui/qt/sctp_graph_byte_dialog.cpp | 181 + ui/qt/sctp_graph_byte_dialog.h | 60 + ui/qt/sctp_graph_byte_dialog.ui | 125 + ui/qt/sctp_graph_dialog.cpp | 524 + ui/qt/sctp_graph_dialog.h | 121 + ui/qt/sctp_graph_dialog.ui | 166 + ui/qt/search_frame.cpp | 532 + ui/qt/search_frame.h | 63 + ui/qt/search_frame.ui | 205 + ui/qt/sequence_diagram.cpp | 401 + ui/qt/sequence_diagram.h | 80 + ui/qt/sequence_dialog.cpp | 864 + ui/qt/sequence_dialog.h | 137 + ui/qt/sequence_dialog.ui | 434 + ui/qt/service_response_time_dialog.cpp | 363 + ui/qt/service_response_time_dialog.h | 74 + ui/qt/show_packet_bytes_dialog.cpp | 932 + ui/qt/show_packet_bytes_dialog.h | 118 + ui/qt/show_packet_bytes_dialog.ui | 151 + ui/qt/simple_dialog.cpp | 450 + ui/qt/simple_dialog.h | 51 + ui/qt/simple_statistics_dialog.cpp | 308 + ui/qt/simple_statistics_dialog.h | 57 + ui/qt/stats_tree_dialog.cpp | 208 + ui/qt/stats_tree_dialog.h | 47 + ui/qt/strip_headers_dialog.cpp | 47 + ui/qt/strip_headers_dialog.h | 35 + ui/qt/strip_headers_dialog.ui | 106 + ui/qt/supported_protocols_dialog.cpp | 98 + ui/qt/supported_protocols_dialog.h | 51 + ui/qt/supported_protocols_dialog.ui | 108 + ui/qt/tabnav_tree_widget.cpp | 44 + ui/qt/tabnav_tree_widget.h | 30 + ui/qt/tap_parameter_dialog.cpp | 613 + ui/qt/tap_parameter_dialog.h | 111 + ui/qt/tap_parameter_dialog.ui | 142 + ui/qt/tcp_stream_dialog.cpp | 2217 ++ ui/qt/tcp_stream_dialog.h | 196 + ui/qt/tcp_stream_dialog.ui | 685 + ui/qt/time_shift_dialog.cpp | 266 + ui/qt/time_shift_dialog.h | 66 + ui/qt/time_shift_dialog.ui | 253 + ui/qt/tlskeylog_launcher_dialog.cpp | 225 + ui/qt/tlskeylog_launcher_dialog.h | 48 + ui/qt/tlskeylog_launcher_dialog.ui | 217 + ui/qt/traffic_table_dialog.cpp | 163 + ui/qt/traffic_table_dialog.h | 84 + ui/qt/traffic_table_dialog.ui | 177 + ui/qt/uat_dialog.cpp | 413 + ui/qt/uat_dialog.h | 71 + ui/qt/uat_dialog.ui | 192 + ui/qt/uat_frame.cpp | 372 + ui/qt/uat_frame.h | 66 + ui/qt/uat_frame.ui | 161 + ui/qt/utils/color_utils.cpp | 216 + ui/qt/utils/color_utils.h | 92 + ui/qt/utils/data_printer.cpp | 264 + ui/qt/utils/data_printer.h | 60 + ui/qt/utils/field_information.cpp | 214 + ui/qt/utils/field_information.h | 76 + ui/qt/utils/frame_information.cpp | 103 + ui/qt/utils/frame_information.h | 57 + ui/qt/utils/idata_printable.h | 34 + ui/qt/utils/proto_node.cpp | 166 + ui/qt/utils/proto_node.h | 63 + ui/qt/utils/qt_ui_utils.cpp | 347 + ui/qt/utils/qt_ui_utils.h | 280 + ui/qt/utils/rtp_audio_file.cpp | 384 + ui/qt/utils/rtp_audio_file.h | 93 + ui/qt/utils/rtp_audio_routing.cpp | 110 + ui/qt/utils/rtp_audio_routing.h | 55 + ui/qt/utils/rtp_audio_routing_filter.cpp | 114 + ui/qt/utils/rtp_audio_routing_filter.h | 42 + ui/qt/utils/rtp_audio_silence_generator.cpp | 47 + ui/qt/utils/rtp_audio_silence_generator.h | 35 + ui/qt/utils/stock_icon.cpp | 253 + ui/qt/utils/stock_icon.h | 40 + ui/qt/utils/tango_colors.h | 75 + ui/qt/utils/variant_pointer.h | 34 + ui/qt/utils/wireshark_mime_data.cpp | 52 + ui/qt/utils/wireshark_mime_data.h | 47 + ui/qt/utils/wireshark_zip_helper.cpp | 283 + ui/qt/utils/wireshark_zip_helper.h | 36 + ui/qt/voip_calls_dialog.cpp | 842 + ui/qt/voip_calls_dialog.h | 144 + ui/qt/voip_calls_dialog.ui | 214 + ui/qt/welcome_page.cpp | 541 + ui/qt/welcome_page.h | 87 + ui/qt/welcome_page.ui | 360 + ui/qt/widgets/additional_toolbar.cpp | 574 + ui/qt/widgets/additional_toolbar.h | 76 + ui/qt/widgets/apply_line_edit.cpp | 156 + ui/qt/widgets/apply_line_edit.h | 57 + ui/qt/widgets/byte_view_text.cpp | 803 + ui/qt/widgets/byte_view_text.h | 153 + ui/qt/widgets/capture_filter_combo.cpp | 133 + ui/qt/widgets/capture_filter_combo.h | 44 + ui/qt/widgets/capture_filter_edit.cpp | 562 + ui/qt/widgets/capture_filter_edit.h | 79 + ui/qt/widgets/clickable_label.cpp | 55 + ui/qt/widgets/clickable_label.h | 33 + ui/qt/widgets/copy_from_profile_button.cpp | 136 + ui/qt/widgets/copy_from_profile_button.h | 43 + ui/qt/widgets/detachable_tabwidget.cpp | 212 + ui/qt/widgets/detachable_tabwidget.h | 89 + ui/qt/widgets/display_filter_combo.cpp | 189 + ui/qt/widgets/display_filter_combo.h | 40 + ui/qt/widgets/display_filter_edit.cpp | 846 + ui/qt/widgets/display_filter_edit.h | 100 + ui/qt/widgets/dissector_syntax_line_edit.cpp | 106 + ui/qt/widgets/dissector_syntax_line_edit.h | 39 + ui/qt/widgets/dissector_tables_view.cpp | 26 + ui/qt/widgets/dissector_tables_view.h | 27 + ui/qt/widgets/drag_drop_toolbar.cpp | 295 + ui/qt/widgets/drag_drop_toolbar.h | 54 + ui/qt/widgets/drag_label.cpp | 28 + ui/qt/widgets/drag_label.h | 27 + ui/qt/widgets/editor_file_dialog.cpp | 109 + ui/qt/widgets/editor_file_dialog.h | 50 + ui/qt/widgets/elided_label.cpp | 79 + ui/qt/widgets/elided_label.h | 56 + ui/qt/widgets/expert_info_view.cpp | 43 + ui/qt/widgets/expert_info_view.h | 30 + ui/qt/widgets/export_objects_view.cpp | 23 + ui/qt/widgets/export_objects_view.h | 30 + ui/qt/widgets/field_filter_edit.cpp | 226 + ui/qt/widgets/field_filter_edit.h | 54 + ui/qt/widgets/filter_expression_toolbar.cpp | 465 + ui/qt/widgets/filter_expression_toolbar.h | 63 + ui/qt/widgets/find_line_edit.cpp | 77 + ui/qt/widgets/find_line_edit.h | 42 + ui/qt/widgets/follow_stream_text.cpp | 52 + ui/qt/widgets/follow_stream_text.h | 35 + ui/qt/widgets/interface_toolbar_lineedit.cpp | 132 + ui/qt/widgets/interface_toolbar_lineedit.h | 47 + ui/qt/widgets/label_stack.cpp | 178 + ui/qt/widgets/label_stack.h | 60 + ui/qt/widgets/overlay_scroll_bar.cpp | 277 + ui/qt/widgets/overlay_scroll_bar.h | 84 + ui/qt/widgets/packet_list_header.cpp | 375 + ui/qt/widgets/packet_list_header.h | 62 + ui/qt/widgets/path_selection_edit.cpp | 94 + ui/qt/widgets/path_selection_edit.h | 47 + ui/qt/widgets/pref_module_view.cpp | 99 + ui/qt/widgets/pref_module_view.h | 38 + ui/qt/widgets/profile_tree_view.cpp | 119 + ui/qt/widgets/profile_tree_view.h | 69 + ui/qt/widgets/qcustomplot.cpp | 35541 +++++++++++++++++++++ ui/qt/widgets/qcustomplot.h | 7770 +++++ ui/qt/widgets/range_syntax_lineedit.cpp | 43 + ui/qt/widgets/range_syntax_lineedit.h | 33 + ui/qt/widgets/rtp_audio_graph.cpp | 88 + ui/qt/widgets/rtp_audio_graph.h | 40 + ui/qt/widgets/splash_overlay.cpp | 179 + ui/qt/widgets/splash_overlay.h | 46 + ui/qt/widgets/splash_overlay.ui | 115 + ui/qt/widgets/stock_icon_tool_button.cpp | 87 + ui/qt/widgets/stock_icon_tool_button.h | 31 + ui/qt/widgets/syntax_line_edit.cpp | 555 + ui/qt/widgets/syntax_line_edit.h | 91 + ui/qt/widgets/tabnav_tree_view.cpp | 57 + ui/qt/widgets/tabnav_tree_view.h | 38 + ui/qt/widgets/traffic_tab.cpp | 618 + ui/qt/widgets/traffic_tab.h | 246 + ui/qt/widgets/traffic_tree.cpp | 879 + ui/qt/widgets/traffic_tree.h | 177 + ui/qt/widgets/traffic_types_list.cpp | 284 + ui/qt/widgets/traffic_types_list.h | 125 + ui/qt/widgets/wireless_timeline.cpp | 646 + ui/qt/widgets/wireless_timeline.h | 103 + ui/qt/widgets/wireshark_file_dialog.cpp | 80 + ui/qt/widgets/wireshark_file_dialog.h | 38 + ui/qt/wireless_frame.cpp | 393 + ui/qt/wireless_frame.h | 65 + ui/qt/wireless_frame.ui | 208 + ui/qt/wireshark_application.cpp | 39 + ui/qt/wireshark_application.h | 27 + ui/qt/wireshark_de.ts | 14844 +++++++++ ui/qt/wireshark_dialog.cpp | 177 + ui/qt/wireshark_dialog.h | 148 + ui/qt/wireshark_en.ts | 14673 +++++++++ ui/qt/wireshark_es.ts | 14690 +++++++++ ui/qt/wireshark_fr.ts | 14743 +++++++++ ui/qt/wireshark_it.ts | 14839 +++++++++ ui/qt/wireshark_ja_JP.ts | 14812 +++++++++ ui/qt/wireshark_ko.ts | 14809 +++++++++ ui/qt/wireshark_main_window.cpp | 3250 ++ ui/qt/wireshark_main_window.h | 529 + ui/qt/wireshark_main_window.ui | 3108 ++ ui/qt/wireshark_main_window_slots.cpp | 4062 +++ ui/qt/wireshark_pl.ts | 14754 +++++++++ ui/qt/wireshark_ru.ts | 14812 +++++++++ ui/qt/wireshark_sv.ts | 14743 +++++++++ ui/qt/wireshark_tr_TR.ts | 14758 +++++++++ ui/qt/wireshark_uk.ts | 14643 +++++++++ ui/qt/wireshark_zh_CN.ts | 14707 +++++++++ ui/qt/wlan_statistics_dialog.cpp | 757 + ui/qt/wlan_statistics_dialog.h | 53 + 567 files changed, 380326 insertions(+) create mode 100644 ui/qt/CMakeLists.txt create mode 100644 ui/qt/CMakeListsCustom.txt.example create mode 100644 ui/qt/about_dialog.cpp create mode 100644 ui/qt/about_dialog.h create mode 100644 ui/qt/about_dialog.ui create mode 100644 ui/qt/accordion_frame.cpp create mode 100644 ui/qt/accordion_frame.h create mode 100644 ui/qt/address_editor_frame.cpp create mode 100644 ui/qt/address_editor_frame.h create mode 100644 ui/qt/address_editor_frame.ui create mode 100644 ui/qt/bluetooth_att_server_attributes_dialog.cpp create mode 100644 ui/qt/bluetooth_att_server_attributes_dialog.h create mode 100644 ui/qt/bluetooth_att_server_attributes_dialog.ui create mode 100644 ui/qt/bluetooth_device_dialog.cpp create mode 100644 ui/qt/bluetooth_device_dialog.h create mode 100644 ui/qt/bluetooth_device_dialog.ui create mode 100644 ui/qt/bluetooth_devices_dialog.cpp create mode 100644 ui/qt/bluetooth_devices_dialog.h create mode 100644 ui/qt/bluetooth_devices_dialog.ui create mode 100644 ui/qt/bluetooth_hci_summary_dialog.cpp create mode 100644 ui/qt/bluetooth_hci_summary_dialog.h create mode 100644 ui/qt/bluetooth_hci_summary_dialog.ui create mode 100644 ui/qt/byte_view_tab.cpp create mode 100644 ui/qt/byte_view_tab.h create mode 100644 ui/qt/capture_event.h create mode 100644 ui/qt/capture_file.cpp create mode 100644 ui/qt/capture_file.h create mode 100644 ui/qt/capture_file_dialog.cpp create mode 100644 ui/qt/capture_file_dialog.h create mode 100644 ui/qt/capture_file_properties_dialog.cpp create mode 100644 ui/qt/capture_file_properties_dialog.h create mode 100644 ui/qt/capture_file_properties_dialog.ui create mode 100644 ui/qt/capture_filter_syntax_worker.cpp create mode 100644 ui/qt/capture_filter_syntax_worker.h create mode 100644 ui/qt/capture_info_dialog.cpp create mode 100644 ui/qt/capture_info_dialog.h create mode 100644 ui/qt/capture_info_dialog.ui create mode 100644 ui/qt/capture_options_dialog.cpp create mode 100644 ui/qt/capture_options_dialog.h create mode 100644 ui/qt/capture_options_dialog.ui create mode 100644 ui/qt/capture_preferences_frame.cpp create mode 100644 ui/qt/capture_preferences_frame.h create mode 100644 ui/qt/capture_preferences_frame.ui create mode 100644 ui/qt/coloring_rules_dialog.cpp create mode 100644 ui/qt/coloring_rules_dialog.h create mode 100644 ui/qt/coloring_rules_dialog.ui create mode 100644 ui/qt/column_editor_frame.cpp create mode 100644 ui/qt/column_editor_frame.h create mode 100644 ui/qt/column_editor_frame.ui create mode 100644 ui/qt/column_preferences_frame.cpp create mode 100644 ui/qt/column_preferences_frame.h create mode 100644 ui/qt/column_preferences_frame.ui create mode 100644 ui/qt/compiled_filter_output.cpp create mode 100644 ui/qt/compiled_filter_output.h create mode 100644 ui/qt/compiled_filter_output.ui create mode 100644 ui/qt/conversation_colorize_action.cpp create mode 100644 ui/qt/conversation_colorize_action.h create mode 100644 ui/qt/conversation_dialog.cpp create mode 100644 ui/qt/conversation_dialog.h create mode 100644 ui/qt/conversation_hash_tables_dialog.cpp create mode 100644 ui/qt/conversation_hash_tables_dialog.h create mode 100644 ui/qt/conversation_hash_tables_dialog.ui create mode 100644 ui/qt/credentials_dialog.cpp create mode 100644 ui/qt/credentials_dialog.h create mode 100644 ui/qt/credentials_dialog.ui create mode 100644 ui/qt/decode_as_dialog.cpp create mode 100644 ui/qt/decode_as_dialog.h create mode 100644 ui/qt/decode_as_dialog.ui create mode 100644 ui/qt/display_filter_expression_dialog.cpp create mode 100644 ui/qt/display_filter_expression_dialog.h create mode 100644 ui/qt/display_filter_expression_dialog.ui create mode 100644 ui/qt/dissector_tables_dialog.cpp create mode 100644 ui/qt/dissector_tables_dialog.h create mode 100644 ui/qt/dissector_tables_dialog.ui create mode 100644 ui/qt/enabled_protocols_dialog.cpp create mode 100644 ui/qt/enabled_protocols_dialog.h create mode 100644 ui/qt/enabled_protocols_dialog.ui create mode 100644 ui/qt/endpoint_dialog.cpp create mode 100644 ui/qt/endpoint_dialog.h create mode 100644 ui/qt/expert_info_dialog.cpp create mode 100644 ui/qt/expert_info_dialog.h create mode 100644 ui/qt/expert_info_dialog.ui create mode 100644 ui/qt/export_dissection_dialog.cpp create mode 100644 ui/qt/export_dissection_dialog.h create mode 100644 ui/qt/export_object_action.cpp create mode 100644 ui/qt/export_object_action.h create mode 100644 ui/qt/export_object_dialog.cpp create mode 100644 ui/qt/export_object_dialog.h create mode 100644 ui/qt/export_object_dialog.ui create mode 100644 ui/qt/export_pdu_dialog.cpp create mode 100644 ui/qt/export_pdu_dialog.h create mode 100644 ui/qt/export_pdu_dialog.ui create mode 100644 ui/qt/extcap_argument.cpp create mode 100644 ui/qt/extcap_argument.h create mode 100644 ui/qt/extcap_argument_file.cpp create mode 100644 ui/qt/extcap_argument_file.h create mode 100644 ui/qt/extcap_argument_multiselect.cpp create mode 100644 ui/qt/extcap_argument_multiselect.h create mode 100644 ui/qt/extcap_options_dialog.cpp create mode 100644 ui/qt/extcap_options_dialog.h create mode 100644 ui/qt/extcap_options_dialog.ui create mode 100644 ui/qt/file_set_dialog.cpp create mode 100644 ui/qt/file_set_dialog.h create mode 100644 ui/qt/file_set_dialog.ui create mode 100644 ui/qt/filter_action.cpp create mode 100644 ui/qt/filter_action.h create mode 100644 ui/qt/filter_dialog.cpp create mode 100644 ui/qt/filter_dialog.h create mode 100644 ui/qt/filter_dialog.ui create mode 100644 ui/qt/filter_expression_frame.cpp create mode 100644 ui/qt/filter_expression_frame.h create mode 100644 ui/qt/filter_expression_frame.ui create mode 100644 ui/qt/firewall_rules_dialog.cpp create mode 100644 ui/qt/firewall_rules_dialog.h create mode 100644 ui/qt/firewall_rules_dialog.ui create mode 100644 ui/qt/follow_stream_action.cpp create mode 100644 ui/qt/follow_stream_action.h create mode 100644 ui/qt/follow_stream_dialog.cpp create mode 100644 ui/qt/follow_stream_dialog.h create mode 100644 ui/qt/follow_stream_dialog.ui create mode 100644 ui/qt/font_color_preferences_frame.cpp create mode 100644 ui/qt/font_color_preferences_frame.h create mode 100644 ui/qt/font_color_preferences_frame.ui create mode 100644 ui/qt/funnel_statistics.cpp create mode 100644 ui/qt/funnel_statistics.h create mode 100644 ui/qt/funnel_string_dialog.cpp create mode 100644 ui/qt/funnel_string_dialog.h create mode 100644 ui/qt/funnel_string_dialog.ui create mode 100644 ui/qt/funnel_text_dialog.cpp create mode 100644 ui/qt/funnel_text_dialog.h create mode 100644 ui/qt/funnel_text_dialog.ui create mode 100644 ui/qt/geometry_state_dialog.cpp create mode 100644 ui/qt/geometry_state_dialog.h create mode 100644 ui/qt/glib_mainloop_on_qeventloop.cpp create mode 100644 ui/qt/glib_mainloop_on_qeventloop.h create mode 100644 ui/qt/gpl-template.txt create mode 100644 ui/qt/gsm_map_summary_dialog.cpp create mode 100644 ui/qt/gsm_map_summary_dialog.h create mode 100644 ui/qt/gsm_map_summary_dialog.ui create mode 100644 ui/qt/i18n.qrc.in create mode 100644 ui/qt/iax2_analysis_dialog.cpp create mode 100644 ui/qt/iax2_analysis_dialog.h create mode 100644 ui/qt/iax2_analysis_dialog.ui create mode 100644 ui/qt/import_text_dialog.cpp create mode 100644 ui/qt/import_text_dialog.h create mode 100644 ui/qt/import_text_dialog.ui create mode 100644 ui/qt/interface_frame.cpp create mode 100644 ui/qt/interface_frame.h create mode 100644 ui/qt/interface_frame.ui create mode 100644 ui/qt/interface_toolbar.cpp create mode 100644 ui/qt/interface_toolbar.h create mode 100644 ui/qt/interface_toolbar.ui create mode 100644 ui/qt/interface_toolbar_reader.cpp create mode 100644 ui/qt/interface_toolbar_reader.h create mode 100644 ui/qt/io_console_dialog.cpp create mode 100644 ui/qt/io_console_dialog.h create mode 100644 ui/qt/io_console_dialog.ui create mode 100644 ui/qt/io_graph_dialog.cpp create mode 100644 ui/qt/io_graph_dialog.h create mode 100644 ui/qt/io_graph_dialog.ui create mode 100644 ui/qt/layout_preferences_frame.cpp create mode 100644 ui/qt/layout_preferences_frame.h create mode 100644 ui/qt/layout_preferences_frame.ui create mode 100644 ui/qt/lbm_lbtrm_transport_dialog.cpp create mode 100644 ui/qt/lbm_lbtrm_transport_dialog.h create mode 100644 ui/qt/lbm_lbtrm_transport_dialog.ui create mode 100644 ui/qt/lbm_lbtru_transport_dialog.cpp create mode 100644 ui/qt/lbm_lbtru_transport_dialog.h create mode 100644 ui/qt/lbm_lbtru_transport_dialog.ui create mode 100644 ui/qt/lbm_stream_dialog.cpp create mode 100644 ui/qt/lbm_stream_dialog.h create mode 100644 ui/qt/lbm_stream_dialog.ui create mode 100644 ui/qt/lte_mac_statistics_dialog.cpp create mode 100644 ui/qt/lte_mac_statistics_dialog.h create mode 100644 ui/qt/lte_rlc_graph_dialog.cpp create mode 100644 ui/qt/lte_rlc_graph_dialog.h create mode 100644 ui/qt/lte_rlc_graph_dialog.ui create mode 100644 ui/qt/lte_rlc_statistics_dialog.cpp create mode 100644 ui/qt/lte_rlc_statistics_dialog.h create mode 100644 ui/qt/main.cpp create mode 100644 ui/qt/main_application.cpp create mode 100644 ui/qt/main_application.h create mode 100644 ui/qt/main_status_bar.cpp create mode 100644 ui/qt/main_status_bar.h create mode 100644 ui/qt/main_window.cpp create mode 100644 ui/qt/main_window.h create mode 100644 ui/qt/main_window_layout.cpp create mode 100644 ui/qt/main_window_preferences_frame.cpp create mode 100644 ui/qt/main_window_preferences_frame.h create mode 100644 ui/qt/main_window_preferences_frame.ui create mode 100644 ui/qt/manage_interfaces_dialog.cpp create mode 100644 ui/qt/manage_interfaces_dialog.h create mode 100644 ui/qt/manage_interfaces_dialog.ui create mode 100644 ui/qt/manager/preference_manager.cpp create mode 100644 ui/qt/manager/preference_manager.h create mode 100644 ui/qt/manager/wireshark_preference.cpp create mode 100644 ui/qt/manager/wireshark_preference.h create mode 100644 ui/qt/manuf_dialog.cpp create mode 100644 ui/qt/manuf_dialog.h create mode 100644 ui/qt/manuf_dialog.ui create mode 100644 ui/qt/models/astringlist_list_model.cpp create mode 100644 ui/qt/models/astringlist_list_model.h create mode 100644 ui/qt/models/atap_data_model.cpp create mode 100644 ui/qt/models/atap_data_model.h create mode 100644 ui/qt/models/cache_proxy_model.cpp create mode 100644 ui/qt/models/cache_proxy_model.h create mode 100644 ui/qt/models/coloring_rules_delegate.cpp create mode 100644 ui/qt/models/coloring_rules_delegate.h create mode 100644 ui/qt/models/coloring_rules_model.cpp create mode 100644 ui/qt/models/coloring_rules_model.h create mode 100644 ui/qt/models/column_list_model.cpp create mode 100644 ui/qt/models/column_list_model.h create mode 100644 ui/qt/models/credentials_model.cpp create mode 100644 ui/qt/models/credentials_model.h create mode 100644 ui/qt/models/decode_as_delegate.cpp create mode 100644 ui/qt/models/decode_as_delegate.h create mode 100644 ui/qt/models/decode_as_model.cpp create mode 100644 ui/qt/models/decode_as_model.h create mode 100644 ui/qt/models/dissector_tables_model.cpp create mode 100644 ui/qt/models/dissector_tables_model.h create mode 100644 ui/qt/models/enabled_protocols_model.cpp create mode 100644 ui/qt/models/enabled_protocols_model.h create mode 100644 ui/qt/models/expert_info_model.cpp create mode 100644 ui/qt/models/expert_info_model.h create mode 100644 ui/qt/models/expert_info_proxy_model.cpp create mode 100644 ui/qt/models/expert_info_proxy_model.h create mode 100644 ui/qt/models/export_objects_model.cpp create mode 100644 ui/qt/models/export_objects_model.h create mode 100644 ui/qt/models/fileset_entry_model.cpp create mode 100644 ui/qt/models/fileset_entry_model.h create mode 100644 ui/qt/models/filter_list_model.cpp create mode 100644 ui/qt/models/filter_list_model.h create mode 100644 ui/qt/models/info_proxy_model.cpp create mode 100644 ui/qt/models/info_proxy_model.h create mode 100644 ui/qt/models/interface_sort_filter_model.cpp create mode 100644 ui/qt/models/interface_sort_filter_model.h create mode 100644 ui/qt/models/interface_tree_cache_model.cpp create mode 100644 ui/qt/models/interface_tree_cache_model.h create mode 100644 ui/qt/models/interface_tree_model.cpp create mode 100644 ui/qt/models/interface_tree_model.h create mode 100644 ui/qt/models/manuf_table_model.cpp create mode 100644 ui/qt/models/manuf_table_model.h create mode 100644 ui/qt/models/numeric_value_chooser_delegate.cpp create mode 100644 ui/qt/models/numeric_value_chooser_delegate.h create mode 100644 ui/qt/models/packet_list_model.cpp create mode 100644 ui/qt/models/packet_list_model.h create mode 100644 ui/qt/models/packet_list_record.cpp create mode 100644 ui/qt/models/packet_list_record.h create mode 100644 ui/qt/models/path_selection_delegate.cpp create mode 100644 ui/qt/models/path_selection_delegate.h create mode 100644 ui/qt/models/percent_bar_delegate.cpp create mode 100644 ui/qt/models/percent_bar_delegate.h create mode 100644 ui/qt/models/pref_delegate.cpp create mode 100644 ui/qt/models/pref_delegate.h create mode 100644 ui/qt/models/pref_models.cpp create mode 100644 ui/qt/models/pref_models.h create mode 100644 ui/qt/models/profile_model.cpp create mode 100644 ui/qt/models/profile_model.h create mode 100644 ui/qt/models/proto_tree_model.cpp create mode 100644 ui/qt/models/proto_tree_model.h create mode 100644 ui/qt/models/related_packet_delegate.cpp create mode 100644 ui/qt/models/related_packet_delegate.h create mode 100644 ui/qt/models/resolved_addresses_models.cpp create mode 100644 ui/qt/models/resolved_addresses_models.h create mode 100644 ui/qt/models/sparkline_delegate.cpp create mode 100644 ui/qt/models/sparkline_delegate.h create mode 100644 ui/qt/models/supported_protocols_model.cpp create mode 100644 ui/qt/models/supported_protocols_model.h create mode 100644 ui/qt/models/timeline_delegate.cpp create mode 100644 ui/qt/models/timeline_delegate.h create mode 100644 ui/qt/models/tree_model_helpers.h create mode 100644 ui/qt/models/uat_delegate.cpp create mode 100644 ui/qt/models/uat_delegate.h create mode 100644 ui/qt/models/uat_model.cpp create mode 100644 ui/qt/models/uat_model.h create mode 100644 ui/qt/models/url_link_delegate.cpp create mode 100644 ui/qt/models/url_link_delegate.h create mode 100644 ui/qt/models/voip_calls_info_model.cpp create mode 100644 ui/qt/models/voip_calls_info_model.h create mode 100644 ui/qt/module_preferences_scroll_area.cpp create mode 100644 ui/qt/module_preferences_scroll_area.h create mode 100644 ui/qt/module_preferences_scroll_area.ui create mode 100644 ui/qt/mtp3_summary_dialog.cpp create mode 100644 ui/qt/mtp3_summary_dialog.h create mode 100644 ui/qt/mtp3_summary_dialog.ui create mode 100644 ui/qt/multicast_statistics_dialog.cpp create mode 100644 ui/qt/multicast_statistics_dialog.h create mode 100644 ui/qt/packet_comment_dialog.cpp create mode 100644 ui/qt/packet_comment_dialog.h create mode 100644 ui/qt/packet_comment_dialog.ui create mode 100644 ui/qt/packet_diagram.cpp create mode 100644 ui/qt/packet_diagram.h create mode 100644 ui/qt/packet_dialog.cpp create mode 100644 ui/qt/packet_dialog.h create mode 100644 ui/qt/packet_dialog.ui create mode 100644 ui/qt/packet_format_group_box.cpp create mode 100644 ui/qt/packet_format_group_box.h create mode 100644 ui/qt/packet_format_group_box.ui create mode 100644 ui/qt/packet_list.cpp create mode 100644 ui/qt/packet_list.h create mode 100644 ui/qt/packet_range_group_box.cpp create mode 100644 ui/qt/packet_range_group_box.h create mode 100644 ui/qt/packet_range_group_box.ui create mode 100644 ui/qt/preference_editor_frame.cpp create mode 100644 ui/qt/preference_editor_frame.h create mode 100644 ui/qt/preference_editor_frame.ui create mode 100644 ui/qt/preferences_dialog.cpp create mode 100644 ui/qt/preferences_dialog.h create mode 100644 ui/qt/preferences_dialog.ui create mode 100644 ui/qt/print_dialog.cpp create mode 100644 ui/qt/print_dialog.h create mode 100644 ui/qt/print_dialog.ui create mode 100644 ui/qt/profile_dialog.cpp create mode 100644 ui/qt/profile_dialog.h create mode 100644 ui/qt/profile_dialog.ui create mode 100644 ui/qt/progress_frame.cpp create mode 100644 ui/qt/progress_frame.h create mode 100644 ui/qt/progress_frame.ui create mode 100644 ui/qt/proto_tree.cpp create mode 100644 ui/qt/proto_tree.h create mode 100644 ui/qt/protocol_hierarchy_dialog.cpp create mode 100644 ui/qt/protocol_hierarchy_dialog.h create mode 100644 ui/qt/protocol_hierarchy_dialog.ui create mode 100644 ui/qt/protocol_preferences_menu.cpp create mode 100644 ui/qt/protocol_preferences_menu.h create mode 100644 ui/qt/qt6-migration-links.txt create mode 100644 ui/qt/recent_file_status.cpp create mode 100644 ui/qt/recent_file_status.h create mode 100644 ui/qt/remote_capture_dialog.cpp create mode 100644 ui/qt/remote_capture_dialog.h create mode 100644 ui/qt/remote_capture_dialog.ui create mode 100644 ui/qt/remote_settings_dialog.cpp create mode 100644 ui/qt/remote_settings_dialog.h create mode 100644 ui/qt/remote_settings_dialog.ui create mode 100644 ui/qt/resolved_addresses_dialog.cpp create mode 100644 ui/qt/resolved_addresses_dialog.h create mode 100644 ui/qt/resolved_addresses_dialog.ui create mode 100644 ui/qt/response_time_delay_dialog.cpp create mode 100644 ui/qt/response_time_delay_dialog.h create mode 100644 ui/qt/rpc_service_response_time_dialog.cpp create mode 100644 ui/qt/rpc_service_response_time_dialog.h create mode 100644 ui/qt/rsa_keys_frame.cpp create mode 100644 ui/qt/rsa_keys_frame.h create mode 100644 ui/qt/rsa_keys_frame.ui create mode 100644 ui/qt/rtp_analysis_dialog.cpp create mode 100644 ui/qt/rtp_analysis_dialog.h create mode 100644 ui/qt/rtp_analysis_dialog.ui create mode 100644 ui/qt/rtp_audio_stream.cpp create mode 100644 ui/qt/rtp_audio_stream.h create mode 100644 ui/qt/rtp_player_dialog.cpp create mode 100644 ui/qt/rtp_player_dialog.h create mode 100644 ui/qt/rtp_player_dialog.ui create mode 100644 ui/qt/rtp_stream_dialog.cpp create mode 100644 ui/qt/rtp_stream_dialog.h create mode 100644 ui/qt/rtp_stream_dialog.ui create mode 100644 ui/qt/scsi_service_response_time_dialog.cpp create mode 100644 ui/qt/scsi_service_response_time_dialog.h create mode 100644 ui/qt/sctp_all_assocs_dialog.cpp create mode 100644 ui/qt/sctp_all_assocs_dialog.h create mode 100644 ui/qt/sctp_all_assocs_dialog.ui create mode 100644 ui/qt/sctp_assoc_analyse_dialog.cpp create mode 100644 ui/qt/sctp_assoc_analyse_dialog.h create mode 100644 ui/qt/sctp_assoc_analyse_dialog.ui create mode 100644 ui/qt/sctp_chunk_statistics_dialog.cpp create mode 100644 ui/qt/sctp_chunk_statistics_dialog.h create mode 100644 ui/qt/sctp_chunk_statistics_dialog.ui create mode 100644 ui/qt/sctp_graph_arwnd_dialog.cpp create mode 100644 ui/qt/sctp_graph_arwnd_dialog.h create mode 100644 ui/qt/sctp_graph_arwnd_dialog.ui create mode 100644 ui/qt/sctp_graph_byte_dialog.cpp create mode 100644 ui/qt/sctp_graph_byte_dialog.h create mode 100644 ui/qt/sctp_graph_byte_dialog.ui create mode 100644 ui/qt/sctp_graph_dialog.cpp create mode 100644 ui/qt/sctp_graph_dialog.h create mode 100644 ui/qt/sctp_graph_dialog.ui create mode 100644 ui/qt/search_frame.cpp create mode 100644 ui/qt/search_frame.h create mode 100644 ui/qt/search_frame.ui create mode 100644 ui/qt/sequence_diagram.cpp create mode 100644 ui/qt/sequence_diagram.h create mode 100644 ui/qt/sequence_dialog.cpp create mode 100644 ui/qt/sequence_dialog.h create mode 100644 ui/qt/sequence_dialog.ui create mode 100644 ui/qt/service_response_time_dialog.cpp create mode 100644 ui/qt/service_response_time_dialog.h create mode 100644 ui/qt/show_packet_bytes_dialog.cpp create mode 100644 ui/qt/show_packet_bytes_dialog.h create mode 100644 ui/qt/show_packet_bytes_dialog.ui create mode 100644 ui/qt/simple_dialog.cpp create mode 100644 ui/qt/simple_dialog.h create mode 100644 ui/qt/simple_statistics_dialog.cpp create mode 100644 ui/qt/simple_statistics_dialog.h create mode 100644 ui/qt/stats_tree_dialog.cpp create mode 100644 ui/qt/stats_tree_dialog.h create mode 100644 ui/qt/strip_headers_dialog.cpp create mode 100644 ui/qt/strip_headers_dialog.h create mode 100644 ui/qt/strip_headers_dialog.ui create mode 100644 ui/qt/supported_protocols_dialog.cpp create mode 100644 ui/qt/supported_protocols_dialog.h create mode 100644 ui/qt/supported_protocols_dialog.ui create mode 100644 ui/qt/tabnav_tree_widget.cpp create mode 100644 ui/qt/tabnav_tree_widget.h create mode 100644 ui/qt/tap_parameter_dialog.cpp create mode 100644 ui/qt/tap_parameter_dialog.h create mode 100644 ui/qt/tap_parameter_dialog.ui create mode 100644 ui/qt/tcp_stream_dialog.cpp create mode 100644 ui/qt/tcp_stream_dialog.h create mode 100644 ui/qt/tcp_stream_dialog.ui create mode 100644 ui/qt/time_shift_dialog.cpp create mode 100644 ui/qt/time_shift_dialog.h create mode 100644 ui/qt/time_shift_dialog.ui create mode 100644 ui/qt/tlskeylog_launcher_dialog.cpp create mode 100644 ui/qt/tlskeylog_launcher_dialog.h create mode 100644 ui/qt/tlskeylog_launcher_dialog.ui create mode 100644 ui/qt/traffic_table_dialog.cpp create mode 100644 ui/qt/traffic_table_dialog.h create mode 100644 ui/qt/traffic_table_dialog.ui create mode 100644 ui/qt/uat_dialog.cpp create mode 100644 ui/qt/uat_dialog.h create mode 100644 ui/qt/uat_dialog.ui create mode 100644 ui/qt/uat_frame.cpp create mode 100644 ui/qt/uat_frame.h create mode 100644 ui/qt/uat_frame.ui create mode 100644 ui/qt/utils/color_utils.cpp create mode 100644 ui/qt/utils/color_utils.h create mode 100644 ui/qt/utils/data_printer.cpp create mode 100644 ui/qt/utils/data_printer.h create mode 100644 ui/qt/utils/field_information.cpp create mode 100644 ui/qt/utils/field_information.h create mode 100644 ui/qt/utils/frame_information.cpp create mode 100644 ui/qt/utils/frame_information.h create mode 100644 ui/qt/utils/idata_printable.h create mode 100644 ui/qt/utils/proto_node.cpp create mode 100644 ui/qt/utils/proto_node.h create mode 100644 ui/qt/utils/qt_ui_utils.cpp create mode 100644 ui/qt/utils/qt_ui_utils.h create mode 100644 ui/qt/utils/rtp_audio_file.cpp create mode 100644 ui/qt/utils/rtp_audio_file.h create mode 100644 ui/qt/utils/rtp_audio_routing.cpp create mode 100644 ui/qt/utils/rtp_audio_routing.h create mode 100644 ui/qt/utils/rtp_audio_routing_filter.cpp create mode 100644 ui/qt/utils/rtp_audio_routing_filter.h create mode 100644 ui/qt/utils/rtp_audio_silence_generator.cpp create mode 100644 ui/qt/utils/rtp_audio_silence_generator.h create mode 100644 ui/qt/utils/stock_icon.cpp create mode 100644 ui/qt/utils/stock_icon.h create mode 100644 ui/qt/utils/tango_colors.h create mode 100644 ui/qt/utils/variant_pointer.h create mode 100644 ui/qt/utils/wireshark_mime_data.cpp create mode 100644 ui/qt/utils/wireshark_mime_data.h create mode 100644 ui/qt/utils/wireshark_zip_helper.cpp create mode 100644 ui/qt/utils/wireshark_zip_helper.h create mode 100644 ui/qt/voip_calls_dialog.cpp create mode 100644 ui/qt/voip_calls_dialog.h create mode 100644 ui/qt/voip_calls_dialog.ui create mode 100644 ui/qt/welcome_page.cpp create mode 100644 ui/qt/welcome_page.h create mode 100644 ui/qt/welcome_page.ui create mode 100644 ui/qt/widgets/additional_toolbar.cpp create mode 100644 ui/qt/widgets/additional_toolbar.h create mode 100644 ui/qt/widgets/apply_line_edit.cpp create mode 100644 ui/qt/widgets/apply_line_edit.h create mode 100644 ui/qt/widgets/byte_view_text.cpp create mode 100644 ui/qt/widgets/byte_view_text.h create mode 100644 ui/qt/widgets/capture_filter_combo.cpp create mode 100644 ui/qt/widgets/capture_filter_combo.h create mode 100644 ui/qt/widgets/capture_filter_edit.cpp create mode 100644 ui/qt/widgets/capture_filter_edit.h create mode 100644 ui/qt/widgets/clickable_label.cpp create mode 100644 ui/qt/widgets/clickable_label.h create mode 100644 ui/qt/widgets/copy_from_profile_button.cpp create mode 100644 ui/qt/widgets/copy_from_profile_button.h create mode 100644 ui/qt/widgets/detachable_tabwidget.cpp create mode 100644 ui/qt/widgets/detachable_tabwidget.h create mode 100644 ui/qt/widgets/display_filter_combo.cpp create mode 100644 ui/qt/widgets/display_filter_combo.h create mode 100644 ui/qt/widgets/display_filter_edit.cpp create mode 100644 ui/qt/widgets/display_filter_edit.h create mode 100644 ui/qt/widgets/dissector_syntax_line_edit.cpp create mode 100644 ui/qt/widgets/dissector_syntax_line_edit.h create mode 100644 ui/qt/widgets/dissector_tables_view.cpp create mode 100644 ui/qt/widgets/dissector_tables_view.h create mode 100644 ui/qt/widgets/drag_drop_toolbar.cpp create mode 100644 ui/qt/widgets/drag_drop_toolbar.h create mode 100644 ui/qt/widgets/drag_label.cpp create mode 100644 ui/qt/widgets/drag_label.h create mode 100644 ui/qt/widgets/editor_file_dialog.cpp create mode 100644 ui/qt/widgets/editor_file_dialog.h create mode 100644 ui/qt/widgets/elided_label.cpp create mode 100644 ui/qt/widgets/elided_label.h create mode 100644 ui/qt/widgets/expert_info_view.cpp create mode 100644 ui/qt/widgets/expert_info_view.h create mode 100644 ui/qt/widgets/export_objects_view.cpp create mode 100644 ui/qt/widgets/export_objects_view.h create mode 100644 ui/qt/widgets/field_filter_edit.cpp create mode 100644 ui/qt/widgets/field_filter_edit.h create mode 100644 ui/qt/widgets/filter_expression_toolbar.cpp create mode 100644 ui/qt/widgets/filter_expression_toolbar.h create mode 100644 ui/qt/widgets/find_line_edit.cpp create mode 100644 ui/qt/widgets/find_line_edit.h create mode 100644 ui/qt/widgets/follow_stream_text.cpp create mode 100644 ui/qt/widgets/follow_stream_text.h create mode 100644 ui/qt/widgets/interface_toolbar_lineedit.cpp create mode 100644 ui/qt/widgets/interface_toolbar_lineedit.h create mode 100644 ui/qt/widgets/label_stack.cpp create mode 100644 ui/qt/widgets/label_stack.h create mode 100644 ui/qt/widgets/overlay_scroll_bar.cpp create mode 100644 ui/qt/widgets/overlay_scroll_bar.h create mode 100644 ui/qt/widgets/packet_list_header.cpp create mode 100644 ui/qt/widgets/packet_list_header.h create mode 100644 ui/qt/widgets/path_selection_edit.cpp create mode 100644 ui/qt/widgets/path_selection_edit.h create mode 100644 ui/qt/widgets/pref_module_view.cpp create mode 100644 ui/qt/widgets/pref_module_view.h create mode 100644 ui/qt/widgets/profile_tree_view.cpp create mode 100644 ui/qt/widgets/profile_tree_view.h create mode 100644 ui/qt/widgets/qcustomplot.cpp create mode 100644 ui/qt/widgets/qcustomplot.h create mode 100644 ui/qt/widgets/range_syntax_lineedit.cpp create mode 100644 ui/qt/widgets/range_syntax_lineedit.h create mode 100644 ui/qt/widgets/rtp_audio_graph.cpp create mode 100644 ui/qt/widgets/rtp_audio_graph.h create mode 100644 ui/qt/widgets/splash_overlay.cpp create mode 100644 ui/qt/widgets/splash_overlay.h create mode 100644 ui/qt/widgets/splash_overlay.ui create mode 100644 ui/qt/widgets/stock_icon_tool_button.cpp create mode 100644 ui/qt/widgets/stock_icon_tool_button.h create mode 100644 ui/qt/widgets/syntax_line_edit.cpp create mode 100644 ui/qt/widgets/syntax_line_edit.h create mode 100644 ui/qt/widgets/tabnav_tree_view.cpp create mode 100644 ui/qt/widgets/tabnav_tree_view.h create mode 100644 ui/qt/widgets/traffic_tab.cpp create mode 100644 ui/qt/widgets/traffic_tab.h create mode 100644 ui/qt/widgets/traffic_tree.cpp create mode 100644 ui/qt/widgets/traffic_tree.h create mode 100644 ui/qt/widgets/traffic_types_list.cpp create mode 100644 ui/qt/widgets/traffic_types_list.h create mode 100644 ui/qt/widgets/wireless_timeline.cpp create mode 100644 ui/qt/widgets/wireless_timeline.h create mode 100644 ui/qt/widgets/wireshark_file_dialog.cpp create mode 100644 ui/qt/widgets/wireshark_file_dialog.h create mode 100644 ui/qt/wireless_frame.cpp create mode 100644 ui/qt/wireless_frame.h create mode 100644 ui/qt/wireless_frame.ui create mode 100644 ui/qt/wireshark_application.cpp create mode 100644 ui/qt/wireshark_application.h create mode 100644 ui/qt/wireshark_de.ts create mode 100644 ui/qt/wireshark_dialog.cpp create mode 100644 ui/qt/wireshark_dialog.h create mode 100644 ui/qt/wireshark_en.ts create mode 100644 ui/qt/wireshark_es.ts create mode 100644 ui/qt/wireshark_fr.ts create mode 100644 ui/qt/wireshark_it.ts create mode 100644 ui/qt/wireshark_ja_JP.ts create mode 100644 ui/qt/wireshark_ko.ts create mode 100644 ui/qt/wireshark_main_window.cpp create mode 100644 ui/qt/wireshark_main_window.h create mode 100644 ui/qt/wireshark_main_window.ui create mode 100644 ui/qt/wireshark_main_window_slots.cpp create mode 100644 ui/qt/wireshark_pl.ts create mode 100644 ui/qt/wireshark_ru.ts create mode 100644 ui/qt/wireshark_sv.ts create mode 100644 ui/qt/wireshark_tr_TR.ts create mode 100644 ui/qt/wireshark_uk.ts create mode 100644 ui/qt/wireshark_zh_CN.ts create mode 100644 ui/qt/wlan_statistics_dialog.cpp create mode 100644 ui/qt/wlan_statistics_dialog.h (limited to 'ui/qt') diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt new file mode 100644 index 00000000..6a1aab9a --- /dev/null +++ b/ui/qt/CMakeLists.txt @@ -0,0 +1,873 @@ +# CMakeLists.txt +# +# Wireshark - Network traffic analyzer +# By Gerald Combs +# Copyright 1998 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + +if(USE_qt6) + set(qtver "6") +else() + set(qtver "5") +endif() + +ADD_CUSTOM_CMAKE_INCLUDE() + +set(WIRESHARK_WIDGET_HEADERS + widgets/additional_toolbar.h + widgets/apply_line_edit.h + widgets/byte_view_text.h + widgets/capture_filter_combo.h + widgets/capture_filter_edit.h + widgets/clickable_label.h + widgets/copy_from_profile_button.h + widgets/detachable_tabwidget.h + widgets/display_filter_combo.h + widgets/display_filter_edit.h + widgets/dissector_syntax_line_edit.h + widgets/dissector_tables_view.h + widgets/drag_drop_toolbar.h + widgets/drag_label.h + widgets/editor_file_dialog.h + widgets/elided_label.h + widgets/expert_info_view.h + widgets/export_objects_view.h + widgets/field_filter_edit.h + widgets/filter_expression_toolbar.h + widgets/find_line_edit.h + widgets/follow_stream_text.h + widgets/interface_toolbar_lineedit.h + widgets/label_stack.h + widgets/overlay_scroll_bar.h + widgets/packet_list_header.h + widgets/path_selection_edit.h + widgets/pref_module_view.h + widgets/profile_tree_view.h + widgets/range_syntax_lineedit.h + widgets/rtp_audio_graph.h + widgets/splash_overlay.h + widgets/stock_icon_tool_button.h + widgets/syntax_line_edit.h + widgets/tabnav_tree_view.h + widgets/traffic_tab.h + widgets/traffic_tree.h + widgets/traffic_types_list.h + widgets/wireless_timeline.h + widgets/wireshark_file_dialog.h +) + +set(WIRESHARK_3RD_PARTY_WIDGET_HEADERS + widgets/qcustomplot.h +) + +set(WIRESHARK_MANAGER_HEADERS + manager/preference_manager.h + manager/wireshark_preference.h +) + +set(WIRESHARK_UTILS_HEADERS + utils/color_utils.h + utils/data_printer.h + utils/field_information.h + utils/frame_information.h + utils/idata_printable.h + utils/proto_node.h + utils/qt_ui_utils.h + utils/rtp_audio_file.h + utils/rtp_audio_routing_filter.h + utils/rtp_audio_routing.h + utils/rtp_audio_silence_generator.h + utils/stock_icon.h + utils/tango_colors.h + utils/variant_pointer.h + utils/wireshark_mime_data.h + utils/wireshark_zip_helper.h +) + +set(WIRESHARK_MODEL_HEADERS + models/astringlist_list_model.h + models/atap_data_model.h + models/cache_proxy_model.h + models/coloring_rules_delegate.h + models/coloring_rules_model.h + models/column_list_model.h + models/credentials_model.h + models/decode_as_delegate.h + models/decode_as_model.h + models/dissector_tables_model.h + models/enabled_protocols_model.h + models/expert_info_model.h + models/expert_info_proxy_model.h + models/export_objects_model.h + models/fileset_entry_model.h + models/filter_list_model.h + models/info_proxy_model.h + models/interface_sort_filter_model.h + models/interface_tree_cache_model.h + models/interface_tree_model.h + models/manuf_table_model.h + models/numeric_value_chooser_delegate.h + models/packet_list_model.h + models/packet_list_record.h + models/path_selection_delegate.h + models/percent_bar_delegate.h + models/pref_delegate.h + models/pref_models.h + models/profile_model.h + models/proto_tree_model.h + models/related_packet_delegate.h + models/resolved_addresses_models.h + models/sparkline_delegate.h + models/supported_protocols_model.h + models/timeline_delegate.h + models/tree_model_helpers.h + models/uat_delegate.h + models/uat_model.h + models/url_link_delegate.h + models/voip_calls_info_model.h +) + +# All .h files which inherit from QObject aka which use the Q_OBJECT macro +# need to go here. +set(WIRESHARK_QT_HEADERS + about_dialog.h + accordion_frame.h + address_editor_frame.h + bluetooth_att_server_attributes_dialog.h + bluetooth_device_dialog.h + bluetooth_devices_dialog.h + bluetooth_hci_summary_dialog.h + byte_view_tab.h + capture_file_dialog.h + capture_file_properties_dialog.h + capture_file.h + capture_filter_syntax_worker.h + capture_options_dialog.h + capture_preferences_frame.h + coloring_rules_dialog.h + column_editor_frame.h + column_preferences_frame.h + compiled_filter_output.h + conversation_colorize_action.h + conversation_dialog.h + conversation_hash_tables_dialog.h + credentials_dialog.h + decode_as_dialog.h + display_filter_expression_dialog.h + dissector_tables_dialog.h + enabled_protocols_dialog.h + endpoint_dialog.h + expert_info_dialog.h + export_dissection_dialog.h + export_object_action.h + export_object_dialog.h + export_pdu_dialog.h + extcap_argument_file.h + extcap_argument_multiselect.h + extcap_argument.h + extcap_options_dialog.h + file_set_dialog.h + filter_action.h + filter_dialog.h + filter_dialog.h + filter_expression_frame.h + firewall_rules_dialog.h + follow_stream_action.h + follow_stream_dialog.h + font_color_preferences_frame.h + funnel_statistics.h + funnel_string_dialog.h + funnel_text_dialog.h + geometry_state_dialog.h + glib_mainloop_on_qeventloop.h + gsm_map_summary_dialog.h + iax2_analysis_dialog.h + import_text_dialog.h + interface_frame.h + interface_toolbar_reader.h + interface_toolbar.h + io_console_dialog.h + io_graph_dialog.h + layout_preferences_frame.h + lbm_lbtrm_transport_dialog.h + lbm_lbtru_transport_dialog.h + lbm_stream_dialog.h + lte_mac_statistics_dialog.h + lte_rlc_graph_dialog.h + lte_rlc_statistics_dialog.h + main_application.h + main_status_bar.h + main_window_preferences_frame.h + main_window.h + manage_interfaces_dialog.h + manuf_dialog.h + module_preferences_scroll_area.h + mtp3_summary_dialog.h + multicast_statistics_dialog.h + packet_comment_dialog.h + packet_diagram.h + packet_dialog.h + packet_format_group_box.h + packet_list.h + packet_range_group_box.h + preference_editor_frame.h + preferences_dialog.h + print_dialog.h + profile_dialog.h + progress_frame.h + proto_tree.h + protocol_hierarchy_dialog.h + protocol_preferences_menu.h + recent_file_status.h + resolved_addresses_dialog.h + response_time_delay_dialog.h + rpc_service_response_time_dialog.h + rsa_keys_frame.h + rtp_analysis_dialog.h + rtp_audio_stream.h + rtp_player_dialog.h + rtp_stream_dialog.h + scsi_service_response_time_dialog.h + sctp_all_assocs_dialog.h + sctp_assoc_analyse_dialog.h + sctp_chunk_statistics_dialog.h + sctp_graph_arwnd_dialog.h + sctp_graph_byte_dialog.h + sctp_graph_dialog.h + search_frame.h + sequence_diagram.h + sequence_dialog.h + service_response_time_dialog.h + show_packet_bytes_dialog.h + simple_statistics_dialog.h + tlskeylog_launcher_dialog.h + stats_tree_dialog.h + strip_headers_dialog.h + supported_protocols_dialog.h + tabnav_tree_widget.h + tap_parameter_dialog.h + tcp_stream_dialog.h + time_shift_dialog.h + traffic_table_dialog.h + uat_dialog.h + uat_frame.h + voip_calls_dialog.h + welcome_page.h + wireless_frame.h + wireshark_application.h + wireshark_dialog.h + wireshark_main_window.h + wlan_statistics_dialog.h + ${WIRESHARK_CUSTOM_QT_HEADERS} +) + +if(ENABLE_PCAP) + list(APPEND WIRESHARK_QT_HEADERS + capture_info_dialog.h + ) + if(HAVE_PCAP_REMOTE) + list(APPEND WIRESHARK_QT_HEADERS + remote_capture_dialog.h + remote_settings_dialog.h + ) + endif() +endif() + +set(WIRESHARK_WIDGET_SRCS + widgets/additional_toolbar.cpp + widgets/apply_line_edit.cpp + widgets/byte_view_text.cpp + widgets/capture_filter_combo.cpp + widgets/capture_filter_edit.cpp + widgets/clickable_label.cpp + widgets/copy_from_profile_button.cpp + widgets/detachable_tabwidget.cpp + widgets/display_filter_combo.cpp + widgets/display_filter_edit.cpp + widgets/dissector_syntax_line_edit.cpp + widgets/dissector_tables_view.cpp + widgets/drag_drop_toolbar.cpp + widgets/drag_label.cpp + widgets/editor_file_dialog.cpp + widgets/elided_label.cpp + widgets/expert_info_view.cpp + widgets/export_objects_view.cpp + widgets/field_filter_edit.cpp + widgets/filter_expression_toolbar.cpp + widgets/find_line_edit.cpp + widgets/follow_stream_text.cpp + widgets/interface_toolbar_lineedit.cpp + widgets/label_stack.cpp + widgets/overlay_scroll_bar.cpp + widgets/packet_list_header.cpp + widgets/path_selection_edit.cpp + widgets/pref_module_view.cpp + widgets/profile_tree_view.cpp + widgets/range_syntax_lineedit.cpp + widgets/rtp_audio_graph.cpp + widgets/splash_overlay.cpp + widgets/stock_icon_tool_button.cpp + widgets/syntax_line_edit.cpp + widgets/tabnav_tree_view.cpp + widgets/traffic_tab.cpp + widgets/traffic_tree.cpp + widgets/traffic_types_list.cpp + widgets/wireless_timeline.cpp + widgets/wireshark_file_dialog.cpp +) + +set(WIRESHARK_3RD_PARTY_WIDGET_SRCS + widgets/qcustomplot.cpp +) + +set(WIRESHARK_MANAGER_SRCS + manager/preference_manager.cpp + manager/wireshark_preference.cpp +) + +set(WIRESHARK_UTILS_SRCS + utils/color_utils.cpp + utils/data_printer.cpp + utils/field_information.cpp + utils/frame_information.cpp + utils/proto_node.cpp + utils/qt_ui_utils.cpp + utils/rtp_audio_file.cpp + utils/rtp_audio_routing_filter.cpp + utils/rtp_audio_routing.cpp + utils/rtp_audio_silence_generator.cpp + utils/stock_icon.cpp + utils/wireshark_mime_data.cpp + utils/wireshark_zip_helper.cpp +) + +set(WIRESHARK_MODEL_SRCS + models/astringlist_list_model.cpp + models/atap_data_model.cpp + models/cache_proxy_model.cpp + models/coloring_rules_delegate.cpp + models/coloring_rules_model.cpp + models/column_list_model.cpp + models/credentials_model.cpp + models/decode_as_delegate.cpp + models/decode_as_model.cpp + models/dissector_tables_model.cpp + models/enabled_protocols_model.cpp + models/expert_info_model.cpp + models/expert_info_proxy_model.cpp + models/export_objects_model.cpp + models/fileset_entry_model.cpp + models/filter_list_model.cpp + models/info_proxy_model.cpp + models/interface_sort_filter_model.cpp + models/interface_tree_cache_model.cpp + models/interface_tree_model.cpp + models/manuf_table_model.cpp + models/numeric_value_chooser_delegate.cpp + models/packet_list_model.cpp + models/packet_list_record.cpp + models/path_selection_delegate.cpp + models/percent_bar_delegate.cpp + models/pref_delegate.cpp + models/pref_models.cpp + models/profile_model.cpp + models/proto_tree_model.cpp + models/related_packet_delegate.cpp + models/resolved_addresses_models.cpp + models/sparkline_delegate.cpp + models/supported_protocols_model.cpp + models/timeline_delegate.cpp + models/uat_delegate.cpp + models/uat_model.cpp + models/url_link_delegate.cpp + models/voip_calls_info_model.cpp +) + +set(WIRESHARK_QT_SRC + about_dialog.cpp + accordion_frame.cpp + address_editor_frame.cpp + bluetooth_att_server_attributes_dialog.cpp + bluetooth_device_dialog.cpp + bluetooth_devices_dialog.cpp + bluetooth_hci_summary_dialog.cpp + byte_view_tab.cpp + capture_file_dialog.cpp + capture_file_properties_dialog.cpp + capture_file.cpp + capture_filter_syntax_worker.cpp + capture_options_dialog.cpp + capture_preferences_frame.cpp + coloring_rules_dialog.cpp + column_editor_frame.cpp + column_preferences_frame.cpp + compiled_filter_output.cpp + conversation_colorize_action.cpp + conversation_dialog.cpp + conversation_hash_tables_dialog.cpp + credentials_dialog.cpp + decode_as_dialog.cpp + display_filter_expression_dialog.cpp + dissector_tables_dialog.cpp + enabled_protocols_dialog.cpp + endpoint_dialog.cpp + export_dissection_dialog.cpp + export_object_action.cpp + export_object_dialog.cpp + export_pdu_dialog.cpp + extcap_argument_file.cpp + extcap_argument_multiselect.cpp + extcap_argument.cpp + extcap_options_dialog.cpp + file_set_dialog.cpp + filter_action.cpp + filter_dialog.cpp + filter_expression_frame.cpp + firewall_rules_dialog.cpp + follow_stream_action.cpp + follow_stream_dialog.cpp + font_color_preferences_frame.cpp + funnel_string_dialog.cpp + funnel_text_dialog.cpp + geometry_state_dialog.cpp + glib_mainloop_on_qeventloop.cpp + iax2_analysis_dialog.cpp + import_text_dialog.cpp + interface_frame.cpp + interface_toolbar_reader.cpp + interface_toolbar.cpp + io_console_dialog.cpp + layout_preferences_frame.cpp + lbm_lbtrm_transport_dialog.cpp + lbm_lbtru_transport_dialog.cpp + lbm_stream_dialog.cpp + lte_mac_statistics_dialog.cpp + lte_rlc_graph_dialog.cpp + lte_rlc_statistics_dialog.cpp + main_application.cpp + main_status_bar.cpp + main_window_layout.cpp + main_window_preferences_frame.cpp + main_window.cpp + main.cpp + manage_interfaces_dialog.cpp + manuf_dialog.cpp + module_preferences_scroll_area.cpp + packet_comment_dialog.cpp + packet_diagram.cpp + packet_dialog.cpp + packet_format_group_box.cpp + packet_list.cpp + packet_range_group_box.cpp + preference_editor_frame.cpp + preferences_dialog.cpp + print_dialog.cpp + profile_dialog.cpp + progress_frame.cpp + proto_tree.cpp + protocol_hierarchy_dialog.cpp + protocol_preferences_menu.cpp + recent_file_status.cpp + resolved_addresses_dialog.cpp + response_time_delay_dialog.cpp + rpc_service_response_time_dialog.cpp + rsa_keys_frame.cpp + rtp_analysis_dialog.cpp + rtp_audio_stream.cpp + rtp_player_dialog.cpp + rtp_stream_dialog.cpp + scsi_service_response_time_dialog.cpp + sctp_all_assocs_dialog.cpp + sctp_assoc_analyse_dialog.cpp + sctp_chunk_statistics_dialog.cpp + sctp_graph_arwnd_dialog.cpp + sctp_graph_byte_dialog.cpp + sctp_graph_dialog.cpp + search_frame.cpp + sequence_diagram.cpp + sequence_dialog.cpp + service_response_time_dialog.cpp + show_packet_bytes_dialog.cpp + simple_dialog.cpp + simple_statistics_dialog.cpp + tlskeylog_launcher_dialog.cpp + strip_headers_dialog.cpp + supported_protocols_dialog.cpp + tabnav_tree_widget.cpp + tap_parameter_dialog.cpp + tcp_stream_dialog.cpp + time_shift_dialog.cpp + traffic_table_dialog.cpp + uat_dialog.cpp + uat_frame.cpp + voip_calls_dialog.cpp + welcome_page.cpp + wireless_frame.cpp + wireshark_application.cpp + wireshark_dialog.cpp + wireshark_main_window.cpp + wireshark_main_window_slots.cpp + ${WIRESHARK_CUSTOM_QT_SRCS} +) + +if(ENABLE_PCAP) + list(APPEND WIRESHARK_QT_SRC + capture_info_dialog.cpp + ) + if(HAVE_PCAP_REMOTE) + list(APPEND WIRESHARK_QT_SRC + remote_capture_dialog.cpp + remote_settings_dialog.cpp + ) + endif() +endif() + +set(WIRESHARK_QT_TAP_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/expert_info_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/funnel_statistics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/gsm_map_summary_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/io_graph_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/lte_mac_statistics_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/lte_rlc_statistics_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mtp3_summary_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/multicast_statistics_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rtp_stream_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sctp_all_assocs_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sctp_assoc_analyse_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/stats_tree_dialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/wlan_statistics_dialog.cpp + ${WIRESHARK_CUSTOM_TAP_SRC} +) + +set(WIRESHARK_QT_NONGENERATED_SRC + ${WIRESHARK_QT_SRC} + ${WIRESHARK_QT_TAP_SRC} + ${WIRESHARK_MODEL_SRCS} + ${WIRESHARK_UTILS_SRCS} + ${WIRESHARK_MANAGER_SRCS} + ${WIRESHARK_3RD_PARTY_WIDGET_SRCS} + ${WIRESHARK_WIDGET_SRCS} +) + +set(WIRESHARK_QT_UI + about_dialog.ui + address_editor_frame.ui + bluetooth_att_server_attributes_dialog.ui + bluetooth_device_dialog.ui + bluetooth_devices_dialog.ui + bluetooth_hci_summary_dialog.ui + capture_file_properties_dialog.ui + capture_info_dialog.ui + capture_options_dialog.ui + capture_preferences_frame.ui + coloring_rules_dialog.ui + column_editor_frame.ui + column_preferences_frame.ui + compiled_filter_output.ui + conversation_hash_tables_dialog.ui + credentials_dialog.ui + decode_as_dialog.ui + display_filter_expression_dialog.ui + dissector_tables_dialog.ui + enabled_protocols_dialog.ui + expert_info_dialog.ui + export_object_dialog.ui + export_pdu_dialog.ui + extcap_options_dialog.ui + file_set_dialog.ui + filter_dialog.ui + filter_expression_frame.ui + firewall_rules_dialog.ui + follow_stream_dialog.ui + font_color_preferences_frame.ui + funnel_string_dialog.ui + funnel_text_dialog.ui + gsm_map_summary_dialog.ui + iax2_analysis_dialog.ui + import_text_dialog.ui + interface_frame.ui + interface_toolbar.ui + io_console_dialog.ui + io_graph_dialog.ui + layout_preferences_frame.ui + lbm_lbtrm_transport_dialog.ui + lbm_lbtru_transport_dialog.ui + lbm_stream_dialog.ui + lte_rlc_graph_dialog.ui + main_window_preferences_frame.ui + manage_interfaces_dialog.ui + manuf_dialog.ui + module_preferences_scroll_area.ui + mtp3_summary_dialog.ui + packet_comment_dialog.ui + packet_dialog.ui + packet_format_group_box.ui + packet_range_group_box.ui + preference_editor_frame.ui + preferences_dialog.ui + print_dialog.ui + profile_dialog.ui + progress_frame.ui + protocol_hierarchy_dialog.ui + resolved_addresses_dialog.ui + rsa_keys_frame.ui + rtp_analysis_dialog.ui + rtp_player_dialog.ui + rtp_stream_dialog.ui + sctp_all_assocs_dialog.ui + sctp_assoc_analyse_dialog.ui + sctp_chunk_statistics_dialog.ui + sctp_graph_arwnd_dialog.ui + sctp_graph_byte_dialog.ui + sctp_graph_dialog.ui + search_frame.ui + sequence_dialog.ui + show_packet_bytes_dialog.ui + tlskeylog_launcher_dialog.ui + strip_headers_dialog.ui + supported_protocols_dialog.ui + tap_parameter_dialog.ui + tcp_stream_dialog.ui + time_shift_dialog.ui + traffic_table_dialog.ui + uat_dialog.ui + uat_frame.ui + voip_calls_dialog.ui + welcome_page.ui + widgets/splash_overlay.ui + wireless_frame.ui + wireshark_main_window.ui +) + +if(HAVE_PCAP_REMOTE) + list(APPEND WIRESHARK_QT_UI + remote_capture_dialog.ui + remote_settings_dialog.ui + ) +endif() + +set(WIRESHARK_QT_TS + wireshark_de.ts + wireshark_en.ts # lupdate -pluralonly + wireshark_es.ts + wireshark_fr.ts + wireshark_it.ts + wireshark_ja_JP.ts + wireshark_ko.ts + wireshark_pl.ts + wireshark_ru.ts + wireshark_sv.ts + wireshark_tr_TR.ts + wireshark_uk.ts + wireshark_zh_CN.ts +) + +foreach(_file ${WIRESHARK_QT_TS}) + get_filename_component(_qresource ${_file} NAME_WE) + set(_qresource_qm "${_qresource}.qm") + set(i18n_qresource "${i18n_qresource}\n ${_qresource_qm}") +endforeach() +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/i18n.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/i18n.qrc) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/authors.qrc" +"\n" +" \n" +" authors.csv\n" +" \n" +"\n" +) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/authors.csv + COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/make-authors-csv.py + < ${CMAKE_SOURCE_DIR}/AUTHORS + > ${CMAKE_CURRENT_BINARY_DIR}/authors.csv + DEPENDS + ${CMAKE_SOURCE_DIR}/tools/make-authors-csv.py + ${CMAKE_SOURCE_DIR}/AUTHORS +) + +set(WIRESHARK_QT_QRC + ../../resources/about.qrc + ../../resources/languages/languages.qrc + ../../resources/layout.qrc + ../../resources/stock_icons.qrc + ../../resources/wsicon.qrc + ${CMAKE_CURRENT_BINARY_DIR}/i18n.qrc + ${CMAKE_CURRENT_BINARY_DIR}/authors.qrc + ${CMAKE_CURRENT_BINARY_DIR}/authors.csv +) + +if(NOT Qt${qtver}Widgets_VERSION VERSION_LESS "5.9") + # Drop the file modification time of source files from generated files + # to help with reproducible builds. We do not use QFileInfo.lastModified + # so this has no unwanted side effects. This mtime started appearing in + # Qt 5.8. The option to force the old file format without mtime was + # added in Qt 5.9. See https://bugreports.qt.io/browse/QTBUG-58769. + # Force the compression algorithm to zlib, since zstd requires format + # version 3. See https://gitlab.com/wireshark/wireshark/-/issues/18100. + # Use the number of dashes for each argument as documented at + # https://doc.qt.io/qt-6/rcc.html. + set(CMAKE_AUTORCC_OPTIONS --format-version 1) + if(Qt${qtver}Widgets_VERSION VERSION_GREATER_EQUAL "5.13") + list(APPEND CMAKE_AUTORCC_OPTIONS -compress-algo zlib) + endif() +endif() + +if (USE_qt6) + QT6_ADD_TRANSLATION(WIRESHARK_QT_QM ${WIRESHARK_QT_TS} OPTIONS -silent) +elseif(NOT Qt${qtver}Widgets_VERSION VERSION_LESS "5.11") + QT5_ADD_TRANSLATION(WIRESHARK_QT_QM ${WIRESHARK_QT_TS} OPTIONS -silent) +else() + QT5_ADD_TRANSLATION(WIRESHARK_QT_QM ${WIRESHARK_QT_TS}) +endif() + +if (WIN32) + add_custom_target(copy_qt_translations ALL) + add_custom_command(TARGET copy_qt_translations + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory $/translations + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${WIRESHARK_QT_QM} $/translations + ) +endif() + +add_custom_target( + translations + DEPENDS + ${WIRESHARK_QT_QM} +) +set_target_properties(translations PROPERTIES FOLDER "UI") + +set_source_files_properties( + ${WIRESHARK_QT_NONGENERATED_SRC} + PROPERTIES + COMPILE_FLAGS "${WERROR_COMMON_FLAGS}" +) + +set_source_files_properties( + wireshark-tap-register.c + PROPERTIES + SKIP_AUTOGEN ON +) + +add_definitions(${QT5_DEFINITIONS}) + +register_tap_files(wireshark-tap-register.c + ${WIRESHARK_QT_TAP_SRC} +) + +source_group("ui\\UIC Files" FILES ${WIRESHARK_QT_UI}) +source_group("ui\\qrc" FILES ${WIRESHARK_QT_QRC}) +source_group("ui\\Header" FILES ${WIRESHARK_QT_HEADERS}) +source_group("ui\\Widget Header" FILES ${WIRESHARK_WIDGET_HEADERS}) +source_group("ui\\Widget Source" FILES ${WIRESHARK_WIDGET_SRCS}) +source_group("ui\\Utils Headers Files" FILES ${WIRESHARK_UTILS_HEADERS}) +source_group("ui\\Utils Source" FILES ${WIRESHARK_UTILS_SRCS}) +source_group("ui\\Models Headers" FILES ${WIRESHARK_MODEL_HEADERS}) +source_group("ui\\Models Source" FILES ${WIRESHARK_MODEL_SRCS}) +source_group("ui\\Manager Headers" FILES ${WIRESHARK_MANAGER_HEADERS}) +source_group("ui\\Manager Source" FILES ${WIRESHARK_MANAGER_SRCS}) + +add_library(qtui OBJECT + #Included so that Visual Studio can properly put header files in solution + ${WIRESHARK_QT_HEADERS} + ${WIRESHARK_WIDGET_HEADERS} + ${WIRESHARK_3RD_PARTY_WIDGET_HEADERS} + ${WIRESHARK_MANAGER_HEADERS} + ${WIRESHARK_UTILS_HEADERS} + ${WIRESHARK_MODEL_HEADERS} + + ${WIRESHARK_QT_NONGENERATED_SRC} + + # For AUTOUIC and AUTORCC. + ${WIRESHARK_QT_UI} + ${WIRESHARK_QT_QRC} + + ${WIRESHARK_QT_TAP_SRC} + wireshark-tap-register.c +) + +if(USE_qt6) + target_link_libraries(qtui PUBLIC + Qt6::Widgets + Qt6::Core5Compat + Qt6::Concurrent + Qt6::PrintSupport + ) + if(Qt6Multimedia_FOUND) + target_link_libraries(qtui PUBLIC Qt6::Multimedia) + endif() +endif() + +target_include_directories(qtui + SYSTEM PRIVATE + # Include Qt before anything else, see the comment about + # QT5_INCLUDE_DIRS in the top-level CMakeLists.txt + # Basically, qt@5 headers should be prioritized over qt@6 which + # would be found due to GCRYPT_INCLUDE_DIRS=/usr/local/include + ${QT5_INCLUDE_DIRS} + ${GCRYPT_INCLUDE_DIRS} + ${MINIZIP_INCLUDE_DIRS} + ${PCAP_INCLUDE_DIRS} + ${SPEEXDSP_INCLUDE_DIRS} + ${WINSPARKLE_INCLUDE_DIRS} + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_compile_definitions(qtui + PUBLIC + ${QT5_COMPILE_DEFINITIONS} +) + +set_target_properties(qtui PROPERTIES + LINK_FLAGS "${WS_LINK_FLAGS}" + FOLDER "UI" + AUTOMOC ON + AUTOUIC ON + AUTORCC ON + # Ensure .qm files are generated before autogenerating i18n.qrc + AUTOGEN_TARGET_DEPENDS "${WIRESHARK_QT_QM}" +) + +if(MSVC) + set_target_properties(qtui PROPERTIES LINK_FLAGS_DEBUG "${WS_MSVC_DEBUG_LINK_FLAGS}") +endif() + +CHECKAPI( + NAME + ui-qt + SWITCHES + --nocheck-shadow + SOURCES + # QCustomPlot (WIRESHARK_3RD_PARTY_WIDGET_{HEADERS,SRCS}) uses + # prohibited APIs. + ${WIRESHARK_QT_HEADERS} + ${WIRESHARK_WIDGET_HEADERS} + ${WIRESHARK_MANAGER_HEADERS} + ${WIRESHARK_UTILS_HEADERS} + ${WIRESHARK_MODEL_HEADERS} + + ${WIRESHARK_QT_SRC} + ${WIRESHARK_WIDGET_SRCS} + ${WIRESHARK_MANAGER_SRCS} + ${WIRESHARK_UTILS_SRCS} + ${WIRESHARK_MODEL_SRCS} + ${WIRESHARK_QT_TAP_SRC} +) + +# +# Editor modelines - https://www.wireshark.org/tools/modelines.html +# +# Local variables: +# c-basic-offset: 8 +# tab-width: 8 +# indent-tabs-mode: t +# End: +# +# vi: set shiftwidth=8 tabstop=8 noexpandtab: +# :indentSize=8:tabSize=8:noTabs=false: +# diff --git a/ui/qt/CMakeListsCustom.txt.example b/ui/qt/CMakeListsCustom.txt.example new file mode 100644 index 00000000..d020b1b1 --- /dev/null +++ b/ui/qt/CMakeListsCustom.txt.example @@ -0,0 +1,23 @@ +# CMakeListsCustom.txt +# +# Wireshark - Network traffic analyzer +# By Gerald Combs +# Copyright 1998 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# You can add custom GUI files here by replacing the commented out file foo with your file. + +#Add GUI source files here +set(WIRESHARK_CUSTOM_QT_HEADERS +# foo.h +) + +set(WIRESHARK_CUSTOM_QT_SRCS +# foo.c +) + +#Add your tap source files here: +set(WIRESHARK_CUSTOM_TAP_SRC +# tap_foo.c +) diff --git a/ui/qt/about_dialog.cpp b/ui/qt/about_dialog.cpp new file mode 100644 index 00000000..01513030 --- /dev/null +++ b/ui/qt/about_dialog.cpp @@ -0,0 +1,646 @@ +/* about_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "about_dialog.h" +#include + +#include "main_application.h" +#include + +#include +#include + +#ifdef HAVE_LIBSMI +#include +#endif + +#include +#include + +#ifdef HAVE_LUA +#include +#endif + +#include "ui/alert_box.h" +#include "ui/util.h" +#include "ui/help_url.h" +#include + +#include "file.h" +#include "wsutil/file_util.h" +#include "wsutil/tempfile.h" +#include "wsutil/plugins.h" +#include "wsutil/version_info.h" +#include "ui/capture_globals.h" + +#include "extcap.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +AuthorListModel::AuthorListModel(QObject * parent) : +AStringListListModel(parent) +{ + QFile f_authors; + + f_authors.setFileName(":/about/authors.csv"); + f_authors.open(QFile::ReadOnly | QFile::Text); + QTextStream ReadFile_authors(&f_authors); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + ReadFile_authors.setEncoding(QStringConverter::Utf8); +#else + ReadFile_authors.setCodec("UTF-8"); +#endif + + while (!ReadFile_authors.atEnd()) { + QString line = ReadFile_authors.readLine(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QStringList entry = line.split(",", Qt::SkipEmptyParts); +#else + QStringList entry = QStringList() << line.section(',', 0, 0) << line.section(',', 1, 1); +#endif + if (entry.size() == 2) { + appendRow(entry); + } + } + f_authors.close(); + +} + +AuthorListModel::~AuthorListModel() { } + +QStringList AuthorListModel::headerColumns() const +{ + return QStringList() << tr("Name") << tr("Email"); +} + +static void plugins_add_description(const char *name, const char *version, + const char *types, const char *filename, + void *user_data) +{ + QList *plugin_data = (QList *)user_data; + QStringList plugin_row = QStringList() << name << version << types << filename; + *plugin_data << plugin_row; +} + +PluginListModel::PluginListModel(QObject * parent) : AStringListListModel(parent) +{ + QList plugin_data; +#ifdef HAVE_PLUGINS + plugins_get_descriptions(plugins_add_description, &plugin_data); +#endif + +#ifdef HAVE_LUA + wslua_plugins_get_descriptions(plugins_add_description, &plugin_data); +#endif + + extcap_get_descriptions(plugins_add_description, &plugin_data); + + typeNames_ << QString(""); + foreach(QStringList row, plugin_data) + { + QString type_name = row.at(2); + typeNames_ << type_name; + appendRow(row); + } + + typeNames_.sort(); + typeNames_.removeDuplicates(); +} + +QStringList PluginListModel::typeNames() const +{ + return typeNames_; +} + +QStringList PluginListModel::headerColumns() const +{ + return QStringList() << tr("Name") << tr("Version") << tr("Type") << tr("Path"); +} + +ShortcutListModel::ShortcutListModel(QObject * parent): + AStringListListModel(parent) +{ + QMap > shortcuts; // name -> (shortcut, description) + foreach (const QWidget *child, mainApp->mainWindow()->findChildren()) { + // Recent items look funny here. + if (child->objectName().compare("menuOpenRecentCaptureFile") == 0) continue; + foreach (const QAction *action, child->actions()) { + + if (!action->shortcut().isEmpty()) { + QString name = action->text(); + name.replace('&', ""); + shortcuts[name] = QPair(action->shortcut().toString(QKeySequence::NativeText), action->toolTip()); + } + } + } + + QStringList names = shortcuts.keys(); + names.sort(); + foreach (const QString &name, names) { + QStringList row; + row << shortcuts[name].first << name << shortcuts[name].second; + appendRow(row); + } +} + +QStringList ShortcutListModel::headerColumns() const +{ + return QStringList() << tr("Shortcut") << tr("Name") << tr("Description"); +} + +FolderListModel::FolderListModel(QObject * parent): + AStringListListModel(parent) +{ + /* "file open" */ + appendRow(QStringList() << tr("\"File\" dialogs") << get_open_dialog_initial_dir() << tr("capture files")); + + /* temp */ + appendRow(QStringList() << tr("Temp") << (global_capture_opts.temp_dir && global_capture_opts.temp_dir[0] ? global_capture_opts.temp_dir : g_get_tmp_dir()) << tr("untitled capture files")); + + /* pers conf */ + appendRow(QStringList() << tr("Personal configuration") + << gchar_free_to_qstring(get_persconffile_path("", FALSE)) + << tr("dfilters, preferences, ethers, …")); + + /* global conf */ + QString dirPath = get_datafile_dir(); + if (! dirPath.isEmpty()) { + appendRow (QStringList() << tr("Global configuration") << dirPath + << tr("dfilters, preferences, manuf, …")); + } + + /* system */ + appendRow(QStringList() << tr("System") << get_systemfile_dir() << tr("ethers, ipxnets")); + + /* program */ + appendRow(QStringList() << tr("Program") << get_progfile_dir() << tr("program files")); + +#ifdef HAVE_PLUGINS + /* pers plugins */ + appendRow(QStringList() << tr("Personal Plugins") << get_plugins_pers_dir_with_version() << tr("binary plugins")); + + /* global plugins */ + appendRow(QStringList() << tr("Global Plugins") << get_plugins_dir_with_version() << tr("binary plugins")); +#endif + +#ifdef HAVE_LUA + /* pers plugins */ + appendRow(QStringList() << tr("Personal Lua Plugins") << get_plugins_pers_dir() << tr("Lua scripts")); + + /* global plugins */ + appendRow(QStringList() << tr("Global Lua Plugins") << get_plugins_dir() << tr("Lua scripts")); +#endif + + /* Extcap */ + appendRow(QStringList() << tr("Personal Extcap path") << QString(get_extcap_pers_dir()) << tr("external capture (extcap) plugins")); + appendRow(QStringList() << tr("Global Extcap path") << QString(get_extcap_dir()) << tr("external capture (extcap) plugins")); + +#ifdef HAVE_MAXMINDDB + /* MaxMind DB */ + QStringList maxMindDbPaths = QString(maxmind_db_get_paths()).split(G_SEARCHPATH_SEPARATOR_S); + foreach(QString path, maxMindDbPaths) + appendRow(QStringList() << tr("MaxMind DB path") << path.trimmed() << tr("MaxMind DB database search path")); +#endif + +#ifdef HAVE_LIBSMI + /* SMI MIBs/PIBs */ + char *default_mib_path = oid_get_default_mib_path(); + QStringList smiPaths = QString(default_mib_path).split(G_SEARCHPATH_SEPARATOR_S); + g_free(default_mib_path); + foreach(QString path, smiPaths) + appendRow(QStringList() << tr("MIB/PIB path") << path.trimmed() << tr("SMI MIB/PIB search path")); +#endif + +#ifdef Q_OS_MAC + /* Mac Extras */ + QString extras_path = mainApp->applicationDirPath() + "/../Resources/Extras"; + appendRow(QStringList() << tr("macOS Extras") << QDir::cleanPath(extras_path) << tr("Extra macOS packages")); + +#endif +} + +QStringList FolderListModel::headerColumns() const +{ + return QStringList() << tr("Name") << tr("Location") << tr("Typical Files"); +} + +// To do: +// - Tweak and enhance ui... + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + QFile f_acknowledgements; + QFile f_license; + + AuthorListModel * authorModel = new AuthorListModel(this); + AStringListListSortFilterProxyModel * proxyAuthorModel = new AStringListListSortFilterProxyModel(this); + proxyAuthorModel->setSourceModel(authorModel); + proxyAuthorModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + proxyAuthorModel->setColumnToFilter(0); + proxyAuthorModel->setColumnToFilter(1); + ui->tblAuthors->setModel(proxyAuthorModel); + ui->tblAuthors->setRootIsDecorated(false); + ui->tblAuthors->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->tblAuthors, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu); + connect(ui->searchAuthors, &QLineEdit::textChanged, proxyAuthorModel, &AStringListListSortFilterProxyModel::setFilter); + + /* Wireshark tab */ + updateWiresharkText(); + + ui->pte_wireshark->setFrameStyle(QFrame::NoFrame); + ui->pte_wireshark->viewport()->setAutoFillBackground(false); + +/* Check if it is a dev release... (VERSION_MINOR is odd in dev release) */ +#if VERSION_MINOR & 1 + ui->label_logo->setPixmap(QPixmap(":/about/wssplash_dev.png")); +#endif + + /* Folders */ + FolderListModel * folderModel = new FolderListModel(this); + AStringListListSortFilterProxyModel * folderProxyModel = new AStringListListSortFilterProxyModel(this); + folderProxyModel->setSourceModel(folderModel); + folderProxyModel->setColumnToFilter(1); + folderProxyModel->setFilterType(AStringListListSortFilterProxyModel::FilterByStart); + AStringListListUrlProxyModel * folderDisplayModel = new AStringListListUrlProxyModel(this); + folderDisplayModel->setSourceModel(folderProxyModel); + folderDisplayModel->setUrlColumn(1); + ui->tblFolders->setModel(folderDisplayModel); + ui->tblFolders->setRootIsDecorated(false); + ui->tblFolders->setItemDelegateForColumn(1, new UrlLinkDelegate(this)); + ui->tblFolders->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tblFolders->setTextElideMode(Qt::ElideMiddle); + ui->tblFolders->setSortingEnabled(true); + ui->tblFolders->sortByColumn(0, Qt::AscendingOrder); + connect(ui->tblFolders, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu); + connect(ui->searchFolders, &QLineEdit::textChanged, folderProxyModel, &AStringListListSortFilterProxyModel::setFilter); + connect(ui->tblFolders, &QTreeView::doubleClicked, this, &AboutDialog::urlDoubleClicked); + + + /* Plugins */ + ui->label_no_plugins->hide(); + PluginListModel * pluginModel = new PluginListModel(this); + AStringListListSortFilterProxyModel * pluginFilterModel = new AStringListListSortFilterProxyModel(this); + pluginFilterModel->setSourceModel(pluginModel); + pluginFilterModel->setColumnToFilter(0); + AStringListListSortFilterProxyModel * pluginTypeModel = new AStringListListSortFilterProxyModel(this); + pluginTypeModel->setSourceModel(pluginFilterModel); + pluginTypeModel->setColumnToFilter(2); + ui->tblPlugins->setModel(pluginTypeModel); + ui->tblPlugins->setRootIsDecorated(false); + UrlLinkDelegate *plugin_delegate = new UrlLinkDelegate(this); + script_pattern = QString("\\.(lua|py)$"); + plugin_delegate->setColCheck(3, script_pattern); + ui->tblPlugins->setItemDelegateForColumn(3, plugin_delegate); + ui->cmbType->addItems(pluginModel->typeNames()); + ui->tblPlugins->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tblPlugins->setTextElideMode(Qt::ElideMiddle); + ui->tblPlugins->setSortingEnabled(true); + ui->tblPlugins->sortByColumn(0, Qt::AscendingOrder); + connect(ui->tblPlugins, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu); + connect(ui->searchPlugins, &QLineEdit::textChanged, pluginFilterModel, &AStringListListSortFilterProxyModel::setFilter); + connect(ui->cmbType, &QComboBox::currentTextChanged, pluginTypeModel, &AStringListListSortFilterProxyModel::setFilter); + if (ui->tblPlugins->model()->rowCount() < 1) { + foreach (QWidget *w, ui->tab_plugins->findChildren()) { + w->hide(); + } + ui->label_no_plugins->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + ui->label_no_plugins->setEnabled(false); + ui->label_no_plugins->show(); + } + + /* Shortcuts */ + ShortcutListModel * shortcutModel = new ShortcutListModel(this); + AStringListListSortFilterProxyModel * shortcutProxyModel = new AStringListListSortFilterProxyModel(this); + shortcutProxyModel->setSourceModel(shortcutModel); + shortcutProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + shortcutProxyModel->setColumnToFilter(1); + shortcutProxyModel->setColumnToFilter(2); + ui->tblShortcuts->setModel(shortcutProxyModel); + ui->tblShortcuts->setRootIsDecorated(false); + ui->tblShortcuts->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tblShortcuts->setSortingEnabled(true); + ui->tblShortcuts->sortByColumn(1, Qt::AscendingOrder); + connect(ui->tblShortcuts, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu); + connect(ui->searchShortcuts, &QLineEdit::textChanged, shortcutProxyModel, &AStringListListSortFilterProxyModel::setFilter); + + /* Acknowledgements */ + f_acknowledgements.setFileName(":/about/Acknowledgements.md"); + + f_acknowledgements.open(QFile::ReadOnly | QFile::Text); + QTextStream ReadFile_acks(&f_acknowledgements); + + /* QTextBrowser markdown support added in 5.14. */ +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QTextBrowser *textBrowserAcks = new QTextBrowser(); + textBrowserAcks->setMarkdown(ReadFile_acks.readAll()); + textBrowserAcks->setReadOnly(true); + textBrowserAcks->setOpenExternalLinks(true); + textBrowserAcks->moveCursor(QTextCursor::Start); + ui->ackVerticalLayout->addWidget(textBrowserAcks); +#else + QPlainTextEdit *pte = new QPlainTextEdit(); + pte->setPlainText(ReadFile_acks.readAll()); + pte->setReadOnly(true); + pte->moveCursor(QTextCursor::Start); + ui->ackVerticalLayout->addWidget(pte); +#endif + + /* License */ + f_license.setFileName(":/about/gpl-2.0-standalone.html"); + + f_license.open(QFile::ReadOnly | QFile::Text); + QTextStream ReadFile_license(&f_license); + + ui->textBrowserLicense->setHtml(ReadFile_license.readAll()); + ui->textBrowserLicense->moveCursor(QTextCursor::Start); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +bool AboutDialog::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + updateWiresharkText(); + break; + default: + break; + + } + return QDialog::event(event); +} + +void AboutDialog::showEvent(QShowEvent * event) +{ + int one_em = fontMetrics().height(); + + // Authors: Names slightly narrower than emails. + QAbstractItemModel *model = ui->tblAuthors->model(); + int column_count = model->columnCount(); + if (column_count) { + ui->tblAuthors->setColumnWidth(0, (ui->tblAuthors->parentWidget()->width() / column_count) - one_em); + } + + // Folders: First and last to contents. + ui->tblFolders->resizeColumnToContents(0); + ui->tblFolders->resizeColumnToContents(2); + ui->tblFolders->setColumnWidth(1, ui->tblFolders->parentWidget()->width() - + (ui->tblFolders->columnWidth(0) + ui->tblFolders->columnWidth(2))); + + // Plugins: All but the last to contents. + model = ui->tblPlugins->model(); + for (int col = 0; model && col < model->columnCount() - 1; col++) { + ui->tblPlugins->resizeColumnToContents(col); + } + + // Contents + 2 em-widths + ui->tblShortcuts->resizeColumnToContents(0); + ui->tblShortcuts->setColumnWidth(0, ui->tblShortcuts->columnWidth(0) + (one_em * 2)); + ui->tblShortcuts->setColumnWidth(1, one_em * 12); + ui->tblShortcuts->resizeColumnToContents(2); + + QDialog::showEvent(event); +} + +void AboutDialog::updateWiresharkText() +{ + QString vcs_version_info_str = get_ws_vcs_version_info(); + QString copyright_info_str = get_copyright_info(); + QString license_info_str = get_license_info(); + QString comp_info_str = gstring_free_to_qbytearray(get_compiled_version_info(gather_wireshark_qt_compiled_info)); + QString runtime_info_str = gstring_free_to_qbytearray(get_runtime_version_info(gather_wireshark_runtime_info)); + + QString message = ColorUtils::themeLinkStyle(); + + /* Construct the message string */ + message += "

Version " + html_escape(vcs_version_info_str) + ".

\n"; + message += "

" + html_escape(copyright_info_str) + "

\n"; + message += "

" + html_escape(license_info_str) + "

\n"; + message += "

" + html_escape(comp_info_str) + "

\n"; + message += "

" + html_escape(runtime_info_str) + "

\n"; + message += "

Check the man page and www.wireshark.org " + "for more information.

\n"; + ui->pte_wireshark->setHtml(message); + + /* Save the info for the clipboard copy */ + clipboardInfo = ""; + clipboardInfo += "Version " + vcs_version_info_str + ".\n\n"; + /* XXX: GCC 12.1 has a bogus stringop-overread warning using the Qt + * conversions from QByteArray to QString at -O2 and higher due to + * computing a branch that will never be taken. + */ +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_OFF(stringop-overread) +#endif + clipboardInfo += gstring_free_to_qbytearray(get_compiled_version_info(gather_wireshark_qt_compiled_info)) + "\n"; + clipboardInfo += gstring_free_to_qbytearray(get_runtime_version_info(gather_wireshark_runtime_info)) + "\n"; +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_ON(stringop-overread) +#endif +} + +void AboutDialog::on_copyToClipboard_clicked() +{ + QClipboard * clipBoard = QApplication::clipboard(); + clipBoard->setText(clipboardInfo); +} + +void AboutDialog::urlDoubleClicked(const QModelIndex &idx) +{ + if (idx.column() != 1) { + return; + } + QTreeView * table = qobject_cast(sender()); + if (! table) + return; + + QString urlText = table->model()->data(idx).toString(); + if (urlText.isEmpty()) + return; + + if (! QDir(urlText).exists()) + { + if (QMessageBox::question(this, tr("The directory does not exist"), + QString(tr("Should the directory %1 be created?").arg(urlText))) == QMessageBox::Yes) + { + if (! QDir().mkpath(urlText)) + { + QMessageBox::warning(this, tr("The directory could not be created"), + QString(tr("The directory %1 could not be created.").arg(urlText))); + } + } + } + + if (QDir(urlText).exists()) + { + QUrl url = QUrl::fromLocalFile(urlText); + if (url.isValid()) + QDesktopServices::openUrl(url); + } +} + +void AboutDialog::handleCopyMenu(QPoint pos) +{ + QTreeView * tree = qobject_cast(sender()); + if (! tree) + return; + + QModelIndex index = tree->indexAt(pos); + if (! index.isValid()) + return; + + QMenu * menu = new QMenu(this); + menu->setAttribute(Qt::WA_DeleteOnClose); + + if (ui->tabWidget->currentWidget() == ui->tab_plugins) + { +#ifdef Q_OS_MAC + QString show_in_str = tr("Show in Finder"); +#else + QString show_in_str = tr("Show in Folder"); +#endif + QAction * showInFolderAction = menu->addAction(show_in_str); + showInFolderAction->setData(VariantPointer::asQVariant(tree)); + connect(showInFolderAction, &QAction::triggered, this, &AboutDialog::showInFolderActionTriggered); + } + + QAction * copyColumnAction = menu->addAction(tr("Copy")); + copyColumnAction->setData(VariantPointer::asQVariant(tree)); + connect(copyColumnAction, &QAction::triggered, this, &AboutDialog::copyActionTriggered); + + QModelIndexList selectedRows = tree->selectionModel()->selectedRows(); + QAction * copyRowAction = menu->addAction(tr("Copy Row(s)", "", static_cast(selectedRows.count()))); + copyRowAction->setData(VariantPointer::asQVariant(tree)); + connect(copyRowAction, &QAction::triggered, this, &AboutDialog::copyRowActionTriggered); + + menu->popup(tree->viewport()->mapToGlobal(pos)); +} + +void AboutDialog::showInFolderActionTriggered() +{ + QAction * sendingAction = qobject_cast(sender()); + if (!sendingAction) + return; + + QTreeView * tree = VariantPointer::asPtr(sendingAction->data()); + QModelIndexList selectedRows = tree->selectionModel()->selectedRows(); + + foreach (QModelIndex index, selectedRows) + { + QString cf_path = tree->model()->index(index.row(), 3).data().toString(); + desktop_show_in_folder(cf_path); + } +} + +void AboutDialog::copyRowActionTriggered() +{ + copyActionTriggered(true); +} + +void AboutDialog::copyActionTriggered(bool copyRow) +{ + QAction * sendingAction = qobject_cast(sender()); + if (! sendingAction) + return; + + QTreeView * tree = VariantPointer::asPtr(sendingAction->data()); + + QModelIndexList selIndeces = tree->selectionModel()->selectedIndexes(); + + int copyColumn = -1; + if (! copyRow) + { + QMenu * menu = qobject_cast(sendingAction->parent()); + if (menu) + { + QPoint menuPosOnTable = tree->mapFromGlobal(menu->pos()); + QModelIndex clickedIndex = tree->indexAt(menuPosOnTable); + if (clickedIndex.isValid()) + copyColumn = clickedIndex.column(); + } + } + + QString clipdata; + if (selIndeces.count() > 0) + { + int columnCount = tree->model()->columnCount(); + QList visitedRows; + + foreach(QModelIndex index, selIndeces) + { + if (visitedRows.contains(index.row())) + continue; + + QStringList row; + if (copyRow) + { + for (int cnt = 0; cnt < columnCount; cnt++) + { + QModelIndex dataIdx = tree->model()->index(index.row(), cnt); + row << tree->model()->data(dataIdx).toString(); + } + } + else + { + if (copyColumn < 0) + copyColumn = index.column(); + + QModelIndex dataIdx = tree->model()->index(index.row(), copyColumn); + row << tree->model()->data(dataIdx).toString(); + } + + clipdata.append(row.join("\t\t").append("\n")); + + visitedRows << index.row(); + } + } + QClipboard * clipBoard = QApplication::clipboard(); + clipBoard->setText(clipdata); +} + +void AboutDialog::on_tblPlugins_doubleClicked(const QModelIndex &index) +{ + const int path_col = 3; + if (index.column() != path_col) { + return; + } + const int row = index.row(); + const QAbstractItemModel *model = index.model(); + if (model->index(row, path_col).data().toString().contains(QRegularExpression(script_pattern))) { + QDesktopServices::openUrl(QUrl::fromLocalFile(model->index(row, path_col).data().toString())); + } +} diff --git a/ui/qt/about_dialog.h b/ui/qt/about_dialog.h new file mode 100644 index 00000000..ea5cfdd2 --- /dev/null +++ b/ui/qt/about_dialog.h @@ -0,0 +1,105 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ABOUT_DIALOG_H +#define ABOUT_DIALOG_H + +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include + +namespace Ui { +class AboutDialog; +} + +class AuthorListModel : public AStringListListModel +{ +Q_OBJECT + +public: + explicit AuthorListModel(QObject * parent = Q_NULLPTR); + virtual ~AuthorListModel(); + +protected: + virtual QStringList headerColumns() const; + +}; + +class PluginListModel : public AStringListListModel +{ + Q_OBJECT +public: + explicit PluginListModel(QObject * parent = Q_NULLPTR); + + QStringList typeNames() const; + +protected: + virtual QStringList headerColumns() const; + +private: + QStringList typeNames_; +}; + +class ShortcutListModel : public AStringListListModel +{ + Q_OBJECT +public: + explicit ShortcutListModel(QObject * parent = Q_NULLPTR); + +protected: + virtual QStringList headerColumns() const; +}; + +class FolderListModel : public AStringListListModel +{ + Q_OBJECT +public: + explicit FolderListModel(QObject * parent = Q_NULLPTR); + +protected: + virtual QStringList headerColumns() const; +}; + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + +protected: + virtual bool event(QEvent *event); + virtual void showEvent(QShowEvent *); + +private: + void updateWiresharkText(); + + Ui::AboutDialog *ui; + QString script_pattern; + QString clipboardInfo; + +private slots: + void urlDoubleClicked(const QModelIndex &); + void handleCopyMenu(QPoint); + void showInFolderActionTriggered(); + void copyActionTriggered(bool row = false); + void copyRowActionTriggered(); + void on_tblPlugins_doubleClicked(const QModelIndex &index); + void on_copyToClipboard_clicked(); +}; + +#endif // ABOUT_DIALOG_H diff --git a/ui/qt/about_dialog.ui b/ui/qt/about_dialog.ui new file mode 100644 index 00000000..4b515de0 --- /dev/null +++ b/ui/qt/about_dialog.ui @@ -0,0 +1,334 @@ + + + AboutDialog + + + + 0 + 0 + 740 + 650 + + + + About Wireshark + + + + + + + 371 + 231 + + + + 0 + + + false + + + + true + + + Wireshark + + + + + + + + + :/about/wssplash.png + + + false + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + + + 16777215 + 20 + + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + + + Qt::AlignCenter + + + + + + + true + + + Qt::TextBrowserInteraction + + + true + + + + + + + + + + 0 + 0 + + + + Copy the version information to the clipboard + + + Copy to Clipboard + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Authors + + + + + + Search Authors + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ContiguousSelection + + + QAbstractItemView::SelectRows + + + 100 + + + true + + + + + + + + Folders + + + + + + Filter by path + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ContiguousSelection + + + QAbstractItemView::SelectRows + + + + + + + + Plugins + + + + + + No plugins found. + + + + + + + + + Search Plugins + + + + + + + Filter by type: + + + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ContiguousSelection + + + QAbstractItemView::SelectRows + + + + + + + + Keyboard Shortcuts + + + + + + Search Shortcuts + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ContiguousSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideRight + + + true + + + + + + + + Acknowledgments + + + + + + License + + + + + + true + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 254 + 595 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 322 + 595 + + + 286 + 274 + + + + + diff --git a/ui/qt/accordion_frame.cpp b/ui/qt/accordion_frame.cpp new file mode 100644 index 00000000..8f8cc430 --- /dev/null +++ b/ui/qt/accordion_frame.cpp @@ -0,0 +1,107 @@ +/* accordion_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" +#include + +#include "accordion_frame.h" + +#include "ui/util.h" +#include + +#include +#include + +const int duration_ = 150; + +AccordionFrame::AccordionFrame(QWidget *parent) : + QFrame(parent), + frame_height_(0) +{ + updateStyleSheet(); + + animation_ = new QPropertyAnimation(this, "maximumHeight", this); + animation_->setDuration(duration_); + animation_->setEasingCurve(QEasingCurve::InOutQuad); + connect(animation_, &QPropertyAnimation::finished, this, &AccordionFrame::animationFinished); +} + +void AccordionFrame::animatedShow() +{ + if (isVisible()) { + show(); + return; + } + + if (!display_is_remote()) { + QWidget *parent = parentWidget(); + + if (parent && parent->layout()) { + // Force our parent layout to update its geometry. There are a number + // of ways of doing this. Calling invalidate + activate seems to + // be the best. + show(); + parent->layout()->invalidate(); // Calls parent->layout()->update() + parent->layout()->activate(); // Calculates sizes then calls parent->updateGeometry() + frame_height_ = height(); + hide(); + } + if (frame_height_ > 0) { + animation_->setStartValue(0); + animation_->setEndValue(frame_height_); + animation_->start(); + } + } + show(); +} + +void AccordionFrame::animatedHide() +{ + if (!isVisible()) { + hide(); + return; + } + + if (!display_is_remote()) { + animation_->setStartValue(frame_height_); + animation_->setEndValue(0); + animation_->start(); + } else { + hide(); + } +} + +void AccordionFrame::animationFinished() +{ + if (animation_->currentValue().toInt() < 1) { + hide(); + setMaximumHeight(frame_height_); + } +} + +void AccordionFrame::updateStyleSheet() +{ + QString style_sheet( + "QLineEdit#goToLineEdit {" + " max-width: 5em;" + "}" + ); + +#ifdef Q_OS_MAC + style_sheet += QString( + "QLineEdit {" + " border: 1px solid palette(%1);" + " border-radius: 3px;" + " padding: 1px;" + "}" + ).arg(ColorUtils::themeIsDark() ? QString("light") : QString("dark")); +#endif + + setStyleSheet(style_sheet); +} \ No newline at end of file diff --git a/ui/qt/accordion_frame.h b/ui/qt/accordion_frame.h new file mode 100644 index 00000000..bed42149 --- /dev/null +++ b/ui/qt/accordion_frame.h @@ -0,0 +1,42 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ACCORDION_FRAME_H +#define ACCORDION_FRAME_H + +#include + +class QPropertyAnimation; + +class AccordionFrame : public QFrame +{ + Q_OBJECT +public: + explicit AccordionFrame(QWidget *parent = 0); + void animatedShow(); + void animatedHide(); + void updateStyleSheet(); + +signals: + void visibilityChanged(bool visible); + +protected: + virtual void hideEvent(QHideEvent *) { emit visibilityChanged(false); } + virtual void showEvent(QShowEvent *) { emit visibilityChanged(true); } + +private: + int frame_height_; + QPropertyAnimation *animation_; + +private slots: + void animationFinished(); + +}; + +#endif // ACCORDION_FRAME_H diff --git a/ui/qt/address_editor_frame.cpp b/ui/qt/address_editor_frame.cpp new file mode 100644 index 00000000..fb044418 --- /dev/null +++ b/ui/qt/address_editor_frame.cpp @@ -0,0 +1,300 @@ +/* address_editor_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include "file.h" +#include "frame_tvbuff.h" + +#include "epan/addr_resolv.h" +#include "epan/epan_dissect.h" +#include "epan/frame_data.h" + +#include "main_application.h" + +#include "address_editor_frame.h" +#include + +#include +#include + +#include + +// To do: +// - Fill in currently resolved address. +// - Allow editing other kinds of addresses. + +AddressEditorFrame::AddressEditorFrame(QWidget *parent) : + AccordionFrame(parent), + ui(new Ui::AddressEditorFrame), + cap_file_(NULL) +{ + ui->setupUi(this); + ui->addressComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + +#ifdef Q_OS_MAC + foreach (QWidget *w, findChildren()) { + w->setAttribute(Qt::WA_MacSmallSize, true); + } +#endif +} + +AddressEditorFrame::~AddressEditorFrame() +{ + delete ui; +} + +QString AddressEditorFrame::addressToString(const FieldInformation& finfo) +{ + address addr; + ws_in4_addr ipv4; + const ws_in6_addr* ipv6; + + if (!finfo.isValid()) { + return QString(); + } + + switch (finfo.headerInfo().type) { + + case FT_IPv4: + // FieldInformation.toString gives us the result of + // proto_item_fill_display_label, but that gives us + // the currently resolved version, if resolution is + // available and enabled. We want the unresolved string. + ipv4 = fvalue_get_uinteger(finfo.fieldInfo()->value); + set_address(&addr, AT_IPv4, 4, &ipv4); + return gchar_free_to_qstring(address_to_str(NULL, &addr)); + case FT_IPv6: + ipv6 = fvalue_get_ipv6(finfo.fieldInfo()->value); + set_address(&addr, AT_IPv6, sizeof(ws_in6_addr), ipv6); + return gchar_free_to_qstring(address_to_str(NULL, &addr)); + default: + return QString(); + } +} + +void AddressEditorFrame::addAddresses(const ProtoNode& node, QStringList& addresses) +{ + QString addrString = addressToString(FieldInformation(&node)); + if (!addrString.isEmpty()) { + addresses << addrString; + } + ProtoNode::ChildIterator kids = node.children(); + while (kids.element().isValid()) { + addAddresses(kids.element(), addresses); + kids.next(); + } +} + +void AddressEditorFrame::editAddresses(CaptureFile &cf, int column) +{ + cap_file_ = cf.capFile(); + + if (!cap_file_->current_frame) { + on_buttonBox_rejected(); + return; + } + + if (!cf_read_current_record(cap_file_)) { + on_buttonBox_rejected(); + return; // error reading the frame + } + + epan_dissect_t edt; + QStringList addresses; + QString selectedString; + + ui->addressComboBox->clear(); + + // Dissect the record with a visible tree and fill in the custom + // columns. We don't really need to have a visible tree (we should + // have one in cap_file_->edt->tree as we have a current frame), but + // this is only a single frame that's previously been dissected so + // the performance hit is slight anyway. + epan_dissect_init(&edt, cap_file_->epan, TRUE, TRUE); + col_custom_prime_edt(&edt, &cap_file_->cinfo); + + epan_dissect_run(&edt, cap_file_->cd_t, &cap_file_->rec, + frame_tvbuff_new_buffer(&cap_file_->provider, cap_file_->current_frame, &cap_file_->buf), + cap_file_->current_frame, &cap_file_->cinfo); + epan_dissect_fill_in_columns(&edt, TRUE, TRUE); + + addAddresses(ProtoNode(edt.tree), addresses); + + if (column >= 0) { + // Check selected column + if (isAddressColumn(&cap_file_->cinfo, column)) { + // This always gets the unresolved value. + // XXX: For multifield custom columns, we don't have a good + // function to return each string separately before joining + // them. Since we know that IP addresses don't include commas, + // we could split on commas here, and check each field value + // to find the first one that is an IP address in our list. + selectedString = cap_file_->cinfo.col_expr.col_expr_val[column]; + } + } else if (cap_file_->finfo_selected) { + selectedString = addressToString(FieldInformation(cap_file_->finfo_selected)); + } + + epan_dissect_cleanup(&edt); + + displayPreviousUserDefinedHostname(); + + addresses.removeDuplicates(); + ui->addressComboBox->addItems(addresses); + int index = ui->addressComboBox->findText(selectedString); + if (index != -1) { + ui->addressComboBox->setCurrentIndex(index); + } + ui->nameLineEdit->setFocus(); + updateWidgets(); +} + +void AddressEditorFrame::showEvent(QShowEvent *event) +{ + ui->nameLineEdit->setFocus(); + ui->nameLineEdit->selectAll(); + + AccordionFrame::showEvent(event); +} + +void AddressEditorFrame::keyPressEvent(QKeyEvent *event) +{ + if (event->modifiers() == Qt::NoModifier) { + if (event->key() == Qt::Key_Escape) { + on_buttonBox_rejected(); + } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + if (ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) { + on_buttonBox_accepted(); + } + } + } + + AccordionFrame::keyPressEvent(event); +} + +void AddressEditorFrame::displayPreviousUserDefinedHostname() +{ + QString addr = ui->addressComboBox->currentText(); + // XXX: If there's a resolved name that wasn't manually entered, + // we should probably display that too. Possibly even if network + // name resolution is off globally, as get_edited_resolved_name() does. + // It's possible to have such names from DNS lookups if the global is + // turned on then turned back off, from NRBs, or from DNS packets. + // There's no clean API call to always get the resolved name, but + // we could access the hash tables directly the way that + // models/resolved_addresses_models.cpp does. + resolved_name_t* previous_entry = get_edited_resolved_name(addr.toUtf8().constData()); + if (previous_entry) + { + ui->nameLineEdit->setText(previous_entry->name); + } + else + { + ui->nameLineEdit->setText(""); + } +} + +void AddressEditorFrame::updateWidgets() +{ + bool ok_enable = false; + if (ui->addressComboBox->count() > 0) { + ok_enable = true; + } + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok_enable); +} + +void AddressEditorFrame::on_nameResolutionPreferencesToolButton_clicked() +{ + on_buttonBox_rejected(); + emit showNameResolutionPreferences("nameres"); +} + +void AddressEditorFrame::on_addressComboBox_currentIndexChanged(int) +{ + displayPreviousUserDefinedHostname(); + updateWidgets(); +} + +void AddressEditorFrame::on_nameLineEdit_textEdited(const QString &) +{ + updateWidgets(); +} + +void AddressEditorFrame::on_buttonBox_accepted() +{ + if (ui->addressComboBox->count() < 1) { + return; + } + QString addr = ui->addressComboBox->currentText(); + QString name = ui->nameLineEdit->text(); + if (!cf_add_ip_name_from_string(cap_file_, addr.toUtf8().constData(), name.toUtf8().constData())) { + QString error_msg = tr("Can't assign %1 to %2.").arg(name).arg(addr); + mainApp->pushStatus(MainApplication::TemporaryStatus, error_msg); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + return; + } + on_buttonBox_rejected(); + // There's no point in redissecting packets if the network resolution + // global is off. There is a use case for editing several names before + // turning on the preference to avoid a lot of expensive redissects. + // (Statistics->Resolved Addresses still displays them even when + // resolution is disabled, so the user can check what has been input.) + // + // XXX: Can entering a new name but having nothing happen because + // network name resolution is off be confusing to the user? The GTK + // dialog had a simple checkbox, the "Name Resolution Preferences..." + // is a little more complicated but hopefully obvious enough. + if (gbl_resolv_flags.network_name) { + emit redissectPackets(); + } +} + +void AddressEditorFrame::on_buttonBox_rejected() +{ + ui->addressComboBox->clear(); + ui->nameLineEdit->clear(); + animatedHide(); +} + +bool AddressEditorFrame::isAddressColumn(epan_column_info *cinfo, int column) +{ + if (!cinfo || column < 0 || column >= cinfo->num_cols) return false; + + if (((cinfo->columns[column].col_fmt == COL_DEF_SRC) || + (cinfo->columns[column].col_fmt == COL_RES_SRC) || + (cinfo->columns[column].col_fmt == COL_UNRES_SRC) || + (cinfo->columns[column].col_fmt == COL_DEF_DST) || + (cinfo->columns[column].col_fmt == COL_RES_DST) || + (cinfo->columns[column].col_fmt == COL_UNRES_DST) || + (cinfo->columns[column].col_fmt == COL_DEF_NET_SRC) || + (cinfo->columns[column].col_fmt == COL_RES_NET_SRC) || + (cinfo->columns[column].col_fmt == COL_UNRES_NET_SRC) || + (cinfo->columns[column].col_fmt == COL_DEF_NET_DST) || + (cinfo->columns[column].col_fmt == COL_RES_NET_DST) || + (cinfo->columns[column].col_fmt == COL_UNRES_NET_DST)) && + strlen(cinfo->col_expr.col_expr_val[column])) + { + return true; + } + + if ((cinfo->columns[column].col_fmt == COL_CUSTOM) && + cinfo->columns[column].col_custom_fields) { + // We could cycle through all the col_custom_fields_ids and + // see if proto_registrar_get_ftype() says that any of them + // are FT_IPv4 or FT_IPv6, but let's just check the string + // against all the addresses we found from the tree. + return true; + } + + return false; +} diff --git a/ui/qt/address_editor_frame.h b/ui/qt/address_editor_frame.h new file mode 100644 index 00000000..a4981e35 --- /dev/null +++ b/ui/qt/address_editor_frame.h @@ -0,0 +1,63 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ADDRESS_EDITOR_FRAME_H +#define ADDRESS_EDITOR_FRAME_H + +#include "accordion_frame.h" + +#include "capture_file.h" + +#include +#include + +namespace Ui { +class AddressEditorFrame; +} + +struct epan_column_info; + +class AddressEditorFrame : public AccordionFrame +{ + Q_OBJECT + +public: + explicit AddressEditorFrame(QWidget *parent = 0); + ~AddressEditorFrame(); + +public slots: + void editAddresses(CaptureFile &cf, int column = -1); + +signals: + void showNameResolutionPreferences(const QString module_name); + void redissectPackets(); + +protected: + virtual void showEvent(QShowEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + +private slots: + void displayPreviousUserDefinedHostname(); + void updateWidgets(); + void on_nameResolutionPreferencesToolButton_clicked(); + void on_addressComboBox_currentIndexChanged(int idx); + void on_nameLineEdit_textEdited(const QString &); + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + +private: + Ui::AddressEditorFrame *ui; + capture_file *cap_file_; + + static QString addressToString(const FieldInformation& finfo); + static void addAddresses(const ProtoNode& node, QStringList& addresses); + bool isAddressColumn(struct epan_column_info *cinfo, int column); +}; + +#endif // ADDRESS_EDITOR_FRAME_H diff --git a/ui/qt/address_editor_frame.ui b/ui/qt/address_editor_frame.ui new file mode 100644 index 00000000..fdf49ccb --- /dev/null +++ b/ui/qt/address_editor_frame.ui @@ -0,0 +1,138 @@ + + + AddressEditorFrame + + + + 0 + 0 + 833 + 34 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + + + Name Resolution Preferences… + + + + + + + Qt::Horizontal + + + + 81 + 5 + + + + + + + + Address: + + + + + + + + + + Qt::Horizontal + + + + 10 + 5 + + + + + + + + Name: + + + + + + + + 1 + 0 + + + + + 80 + 0 + + + + + + + + Qt::Horizontal + + + + 20 + 13 + + + + + + + + + 16777215 + 27 + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + AccordionFrame + QFrame +
accordion_frame.h
+ 1 +
+ + SyntaxLineEdit + QLineEdit +
widgets/syntax_line_edit.h
+
+
+ + +
diff --git a/ui/qt/bluetooth_att_server_attributes_dialog.cpp b/ui/qt/bluetooth_att_server_attributes_dialog.cpp new file mode 100644 index 00000000..00330b3c --- /dev/null +++ b/ui/qt/bluetooth_att_server_attributes_dialog.cpp @@ -0,0 +1,385 @@ +/* bluetooth_att_server_attributes_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "bluetooth_att_server_attributes_dialog.h" +#include + +#include + +#include "epan/epan.h" +#include "epan/to_str.h" +#include "epan/epan_dissect.h" +#include "epan/prefs.h" +#include "epan/dissectors/packet-bluetooth.h" +#include "epan/dissectors/packet-btatt.h" + +#include "ui/simple_dialog.h" + +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include + +static const int column_number_handle = 0; +static const int column_number_uuid = 1; +static const int column_number_uuid_name = 2; + +static tap_packet_status +btatt_handle_tap_packet(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *edt, const void* data, tap_flags_t flags) +{ + tapinfo_t *tapinfo = (tapinfo_t *) tapinfo_ptr; + + if (tapinfo->tap_packet) + tapinfo->tap_packet(tapinfo, pinfo, edt, data, flags); + + return TAP_PACKET_REDRAW; +} + +static void +btatt_handle_tap_reset(void *tapinfo_ptr) +{ + tapinfo_t *tapinfo = (tapinfo_t *) tapinfo_ptr; + + if (tapinfo->tap_reset) + tapinfo->tap_reset(tapinfo); +} + +BluetoothAttServerAttributesDialog::BluetoothAttServerAttributesDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::BluetoothAttServerAttributesDialog) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 2 / 3); + + connect(ui->tableTreeWidget, &QTreeWidget::customContextMenuRequested, this, &BluetoothAttServerAttributesDialog::tableContextMenu); + connect(ui->interfaceComboBox, static_cast(&QComboBox::currentIndexChanged), this, &BluetoothAttServerAttributesDialog::interfaceCurrentIndexChanged); + connect(ui->deviceComboBox, static_cast(&QComboBox::currentIndexChanged), this, &BluetoothAttServerAttributesDialog::deviceCurrentIndexChanged); + connect(ui->removeDuplicatesCheckBox, &QCheckBox::stateChanged, this, &BluetoothAttServerAttributesDialog::removeDuplicatesStateChanged); + + ui->tableTreeWidget->sortByColumn(column_number_handle, Qt::AscendingOrder); + + ui->tableTreeWidget->setStyleSheet("QTreeView::item:hover{background-color:lightyellow; color:black;}"); + + context_menu_.addActions(QList() << ui->actionMark_Unmark_Cell); + context_menu_.addActions(QList() << ui->actionMark_Unmark_Row); + context_menu_.addActions(QList() << ui->actionCopy_Cell); + context_menu_.addActions(QList() << ui->actionCopy_Rows); + context_menu_.addActions(QList() << ui->actionCopy_All); + context_menu_.addActions(QList() << ui->actionSave_as_image); + + tapinfo_.tap_packet = tapPacket; + tapinfo_.tap_reset = tapReset; + tapinfo_.ui = this; + + registerTapListener("btatt.handles", &tapinfo_, NULL, + 0, + btatt_handle_tap_reset, + btatt_handle_tap_packet, + NULL + ); + + cap_file_.retapPackets(); +} + + +BluetoothAttServerAttributesDialog::~BluetoothAttServerAttributesDialog() +{ + delete ui; +} + + +void BluetoothAttServerAttributesDialog::captureFileClosed() +{ + ui->interfaceComboBox->setEnabled(FALSE); + ui->deviceComboBox->setEnabled(FALSE); + ui->removeDuplicatesCheckBox->setEnabled(FALSE); + + WiresharkDialog::captureFileClosed(); +} + + +void BluetoothAttServerAttributesDialog::changeEvent(QEvent *event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + + +void BluetoothAttServerAttributesDialog::keyPressEvent(QKeyEvent *event) +{ +/* NOTE: Do nothing*, but in real it "takes focus" from button_box so allow user + * to use Enter button to jump to frame from tree widget */ +/* * - reimplement shortcuts from contex menu */ + + if (event->modifiers() & Qt::ControlModifier && event->key()== Qt::Key_M) + on_actionMark_Unmark_Row_triggered(); +} + + +void BluetoothAttServerAttributesDialog::tableContextMenu(const QPoint &pos) +{ + context_menu_.popup(ui->tableTreeWidget->viewport()->mapToGlobal(pos)); +} + + +void BluetoothAttServerAttributesDialog::on_actionMark_Unmark_Cell_triggered() +{ + QTreeWidgetItem *current_item = ui->tableTreeWidget->currentItem(); + if (!current_item) + return; + + QBrush fg; + QBrush bg; + + if (current_item->background(ui->tableTreeWidget->currentColumn()) == QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) { + fg = QBrush(); + bg = QBrush(); + } else { + fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg)); + bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)); + } + + current_item->setForeground(ui->tableTreeWidget->currentColumn(), fg); + current_item->setBackground(ui->tableTreeWidget->currentColumn(), bg); +} + + +void BluetoothAttServerAttributesDialog::on_actionMark_Unmark_Row_triggered() +{ + QTreeWidgetItem *current_item = ui->tableTreeWidget->currentItem(); + if (!current_item) + return; + + QBrush fg; + QBrush bg; + bool is_marked = TRUE; + + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i += 1) { + if (current_item->background(i) != QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) + is_marked = FALSE; + } + + if (is_marked) { + fg = QBrush(); + bg = QBrush(); + } else { + fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg)); + bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)); + } + + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i += 1) { + current_item->setForeground(i, fg); + current_item->setBackground(i, bg); + } +} + + +void BluetoothAttServerAttributesDialog::on_actionCopy_Cell_triggered() +{ + QTreeWidgetItem *current_item = ui->tableTreeWidget->currentItem(); + if (!current_item) + return; + + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + + copy = QString(current_item->text(ui->tableTreeWidget->currentColumn())); + + clipboard->setText(copy); +} + + +void BluetoothAttServerAttributesDialog::on_actionCopy_Rows_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + QList items; + QList::iterator i_item; + + items = ui->tableTreeWidget->selectedItems(); + + for (i_item = items.begin(); i_item != items.end(); ++i_item) { + copy += QString("%1 %2 %3\n") + .arg((*i_item)->text(column_number_handle), -6) + .arg((*i_item)->text(column_number_uuid), -32) + .arg((*i_item)->text(column_number_uuid_name)); + + } + + clipboard->setText(copy); +} + +void BluetoothAttServerAttributesDialog::tapReset(void *tapinfo_ptr) +{ + tapinfo_t *tapinfo = (tapinfo_t *) tapinfo_ptr; + BluetoothAttServerAttributesDialog *bluetooth_att_server_attributes_dialog = static_cast(tapinfo->ui); + + + bluetooth_att_server_attributes_dialog->ui->tableTreeWidget->clear(); +} + + +tap_packet_status BluetoothAttServerAttributesDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t) +{ + tapinfo_t *tapinfo = static_cast(tapinfo_ptr); + BluetoothAttServerAttributesDialog *dialog = static_cast(tapinfo->ui); + tap_handles_t *tap_handles = static_cast(const_cast(data)); + QString handle; + QString uuid; + QString uuid_name; + gchar *addr = NULL; + + if (dialog->file_closed_) + return TAP_PACKET_DONT_REDRAW; + + if (pinfo->rec->rec_type != REC_TYPE_PACKET) + return TAP_PACKET_DONT_REDRAW; + + if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID) { + gchar *interface; + const char *interface_name; + + interface_name = epan_get_interface_name(pinfo->epan, pinfo->rec->rec_header.packet_header.interface_id); + interface = wmem_strdup_printf(pinfo->pool, "%u: %s", pinfo->rec->rec_header.packet_header.interface_id, interface_name); + + if (dialog->ui->interfaceComboBox->findText(interface) == -1) + dialog->ui->interfaceComboBox->addItem(interface); + + if (interface && dialog->ui->interfaceComboBox->currentIndex() > 0) { + if (dialog->ui->interfaceComboBox->currentText() != interface) + return TAP_PACKET_REDRAW; + } + } + + if (pinfo->p2p_dir == P2P_DIR_SENT || pinfo->p2p_dir == P2P_DIR_RECV) + addr = address_to_str(pinfo->pool, &pinfo->src); + + if (addr && dialog->ui->deviceComboBox->findText(addr) == -1) { + dialog->ui->deviceComboBox->addItem(addr); + } + + if (addr && dialog->ui->deviceComboBox->currentIndex() > 0) { + if (dialog->ui->deviceComboBox->currentText() != addr) + return TAP_PACKET_REDRAW; + } + + handle = QString("0x%1").arg(tap_handles->handle, 4, 16, QChar('0')); + uuid = QString(print_numeric_bluetooth_uuid(pinfo->pool, &tap_handles->uuid)); + uuid_name = QString(print_bluetooth_uuid(pinfo->pool, &tap_handles->uuid)); + + if (dialog->ui->removeDuplicatesCheckBox->checkState() == Qt::Checked) { + QTreeWidgetItemIterator i_item(dialog->ui->tableTreeWidget); + + while (*i_item) { + QTreeWidgetItem *item = static_cast(*i_item); + + if (item->text(column_number_handle) == handle && + item->text(column_number_uuid) == uuid && + item->text(column_number_uuid_name) == uuid_name) + return TAP_PACKET_REDRAW; + ++i_item; + } + } + + QTreeWidgetItem *item = new QTreeWidgetItem(dialog->ui->tableTreeWidget); + item->setText(column_number_handle, handle); + item->setText(column_number_uuid, uuid); + item->setText(column_number_uuid_name, uuid_name); + item->setData(0, Qt::UserRole, QVariant::fromValue(pinfo->num)); + + for (int i = 0; i < dialog->ui->tableTreeWidget->columnCount(); i++) { + dialog->ui->tableTreeWidget->resizeColumnToContents(i); + } + + return TAP_PACKET_REDRAW; +} + +void BluetoothAttServerAttributesDialog::interfaceCurrentIndexChanged(int) +{ + cap_file_.retapPackets(); +} + + +void BluetoothAttServerAttributesDialog::deviceCurrentIndexChanged(int) +{ + cap_file_.retapPackets(); +} + + +void BluetoothAttServerAttributesDialog::removeDuplicatesStateChanged(int) +{ + cap_file_.retapPackets(); +} + + + +void BluetoothAttServerAttributesDialog::on_tableTreeWidget_itemActivated(QTreeWidgetItem *item, int) +{ + if (file_closed_) + return; + + guint32 frame_number = item->data(0, Qt::UserRole).value(); + + emit goToPacket(frame_number); +} + + +void BluetoothAttServerAttributesDialog::on_actionCopy_All_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + QTreeWidgetItemIterator i_item(ui->tableTreeWidget); + + copy = QString("%1 %2 %3\n") + .arg(ui->tableTreeWidget->headerItem()->text(column_number_handle), -6) + .arg(ui->tableTreeWidget->headerItem()->text(column_number_uuid), -32) + .arg(ui->tableTreeWidget->headerItem()->text(column_number_uuid_name)); + + while (*i_item) { + QTreeWidgetItem *item = static_cast(*i_item); + copy += QString("%1 %2 %3\n") + .arg(item->text(column_number_handle), -6) + .arg(item->text(column_number_uuid), -32) + .arg(item->text(column_number_uuid_name)); + ++i_item; + } + + clipboard->setText(copy); +} + +void BluetoothAttServerAttributesDialog::on_actionSave_as_image_triggered() +{ + QPixmap image; + + QString fileName = WiresharkFileDialog::getSaveFileName(this, tr("Save Table Image"), + "att_server_attributes_table.png", + tr("PNG Image (*.png)")); + + if (fileName.isEmpty()) return; + + image = ui->tableTreeWidget->grab(); + image.save(fileName, "PNG"); +} + +void BluetoothAttServerAttributesDialog::on_buttonBox_clicked(QAbstractButton *) +{ +/* if (button == foo_button_) */ +} diff --git a/ui/qt/bluetooth_att_server_attributes_dialog.h b/ui/qt/bluetooth_att_server_attributes_dialog.h new file mode 100644 index 00000000..2d2a4f88 --- /dev/null +++ b/ui/qt/bluetooth_att_server_attributes_dialog.h @@ -0,0 +1,85 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BLUETOOTH_ATT_SERVER_ATTRIBUTES_DIALOG_H +#define BLUETOOTH_ATT_SERVER_ATTRIBUTES_DIALOG_H + +#include + +#include + +#include "wireshark_dialog.h" +#include "cfile.h" + +#include "epan/tap.h" + +#include + +class QAbstractButton; +class QPushButton; +class QTreeWidgetItem; + +typedef struct _tapinfo_t { + tap_reset_cb tap_reset; + tap_packet_cb tap_packet; + void *ui; +} tapinfo_t; + +namespace Ui { +class BluetoothAttServerAttributesDialog; +} + +class QTreeWidgetItem; +class BluetoothAttServerAttributesDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit BluetoothAttServerAttributesDialog(QWidget &parent, CaptureFile &cf); + ~BluetoothAttServerAttributesDialog(); + +public slots: + +signals: + void updateFilter(QString filter, bool force = false); + void captureFileChanged(capture_file *cf); + void goToPacket(int packet_num); + +protected: + void keyPressEvent(QKeyEvent *event); + void captureFileClosed(); + +protected slots: + void changeEvent(QEvent* event); + +private: + Ui::BluetoothAttServerAttributesDialog *ui; + + tapinfo_t tapinfo_; + QMenu context_menu_; + + static void tapReset(void *tapinfo_ptr); + static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t flags); + +private slots: + void on_tableTreeWidget_itemActivated(QTreeWidgetItem *item, int); + void on_buttonBox_clicked(QAbstractButton *button); + void on_actionMark_Unmark_Cell_triggered(); + void on_actionMark_Unmark_Row_triggered(); + void on_actionCopy_Cell_triggered(); + void on_actionCopy_Rows_triggered(); + void on_actionCopy_All_triggered(); + void on_actionSave_as_image_triggered(); + void tableContextMenu(const QPoint &pos); + void interfaceCurrentIndexChanged(int index); + void deviceCurrentIndexChanged(int index); + void removeDuplicatesStateChanged(int state); +}; + +#endif // BLUETOOTH_ATT_SERVER_ATTRIBUTES_DIALOG_H diff --git a/ui/qt/bluetooth_att_server_attributes_dialog.ui b/ui/qt/bluetooth_att_server_attributes_dialog.ui new file mode 100644 index 00000000..ba67973f --- /dev/null +++ b/ui/qt/bluetooth_att_server_attributes_dialog.ui @@ -0,0 +1,250 @@ + + + BluetoothAttServerAttributesDialog + + + + 0 + 0 + 880 + 477 + + + + + 0 + 0 + + + + Bluetooth ATT Server Attributes + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::ExtendedSelection + + + Qt::ElideMiddle + + + false + + + false + + + true + + + false + + + false + + + true + + + + Handle + + + + + UUID + + + + + UUID Name + + + + + + + + -1 + + + QLayout::SetDefaultConstraint + + + 0 + + + + + + 0 + 0 + + + + + 350 + 0 + + + + + All Interfaces + + + + + + + + true + + + + 0 + 0 + + + + + 325 + 0 + + + + + 0 + 0 + + + + false + + + Qt::WheelFocus + + + false + + + Qt::LeftToRight + + + 0 + + + false + + + true + + + 0 + + + + All Devices + + + + + + + + Remove duplicates + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + Copy Cell + + + + + Copy Rows + + + + + Copy All + + + + + Save as image + + + + + Mark/Unmark Row + + + Mark/Unmark Row + + + Ctrl-M + + + + + Mark/Unmark Cell + + + + + + + buttonBox + accepted() + BluetoothAttServerAttributesDialog + accept() + + + 374 + 407 + + + 374 + 214 + + + + + buttonBox + rejected() + BluetoothAttServerAttributesDialog + reject() + + + 374 + 407 + + + 374 + 214 + + + + + diff --git a/ui/qt/bluetooth_device_dialog.cpp b/ui/qt/bluetooth_device_dialog.cpp new file mode 100644 index 00000000..6e679b8a --- /dev/null +++ b/ui/qt/bluetooth_device_dialog.cpp @@ -0,0 +1,688 @@ +/* bluetooth_device_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "bluetooth_device_dialog.h" +#include + +#include + +#include "epan/epan.h" +#include "epan/addr_resolv.h" +#include "epan/to_str.h" +#include "epan/epan_dissect.h" +#include "epan/prefs.h" +#include "epan/dissectors/packet-bthci_cmd.h" +#include "epan/dissectors/packet-bthci_evt.h" + +#include "ui/simple_dialog.h" + +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include + +static const int column_number_value = 0; +static const int column_number_changes = 1; + +static const int row_number_bd_addr = 0; +static const int row_number_bd_addr_oui = 1; +static const int row_number_name = 2; +static const int row_number_class_of_device = 3; +static const int row_number_lmp_version = 4; +static const int row_number_lmp_subversion = 5; +static const int row_number_manufacturer = 6; +static const int row_number_hci_version = 7; +static const int row_number_hci_revision = 8; +static const int row_number_scan = 9; +static const int row_number_authentication = 10; +static const int row_number_encryption = 11; +static const int row_number_acl_mtu = 12; +static const int row_number_acl_packets = 13; +static const int row_number_sco_mtu = 14; +static const int row_number_sco_packets = 15; +static const int row_number_le_acl_mtu = 16; +static const int row_number_le_acl_packets = 17; +static const int row_number_le_iso_mtu = 18; +static const int row_number_le_iso_packets = 19; +static const int row_number_inquiry_mode = 20; +static const int row_number_page_timeout = 21; +static const int row_number_simple_pairing_mode = 22; +static const int row_number_voice_setting = 23; + +static tap_packet_status +bluetooth_device_tap_packet(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *edt, const void* data, tap_flags_t flags) +{ + bluetooth_device_tapinfo_t *tapinfo = (bluetooth_device_tapinfo_t *) tapinfo_ptr; + + if (tapinfo->tap_packet) + tapinfo->tap_packet(tapinfo, pinfo, edt, data, flags); + + return TAP_PACKET_REDRAW; +} + +static void +bluetooth_device_tap_reset(void *tapinfo_ptr) +{ + bluetooth_device_tapinfo_t *tapinfo = (bluetooth_device_tapinfo_t *) tapinfo_ptr; + + if (tapinfo->tap_reset) + tapinfo->tap_reset(tapinfo); +} + + +static void +bluetooth_devices_tap(void *data) +{ + GString *error_string; + + error_string = register_tap_listener("bluetooth.device", data, NULL, + 0, + bluetooth_device_tap_reset, + bluetooth_device_tap_packet, + NULL, + NULL + ); + + if (error_string != NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "%s", error_string->str); + g_string_free(error_string, TRUE); + } +} + + +BluetoothDeviceDialog::BluetoothDeviceDialog(QWidget &parent, CaptureFile &cf, QString bdAddr, QString name, guint32 interface_id, guint32 adapter_id, gboolean is_local) : + WiresharkDialog(parent, cf), + ui(new Ui::BluetoothDeviceDialog) +{ + ui->setupUi(this); + resize(parent.width() * 4 / 10, parent.height() * 2 / 2); + + setTitle(bdAddr, name); + + connect(ui->tableWidget, &QTableWidget::customContextMenuRequested, this, &BluetoothDeviceDialog::tableContextMenu); + + ui->tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + ui->tableWidget->setStyleSheet("QTableView::item:hover{background-color:lightyellow; color:black;}"); + + context_menu_.addActions(QList() << ui->actionMark_Unmark_Cell); + context_menu_.addActions(QList() << ui->actionMark_Unmark_Row); + context_menu_.addActions(QList() << ui->actionCopy_Cell); + context_menu_.addActions(QList() << ui->actionCopy_Rows); + context_menu_.addActions(QList() << ui->actionCopy_All); + context_menu_.addActions(QList() << ui->actionSave_as_image); + + changes_ = 0; + + tapinfo_.tap_packet = tapPacket; + tapinfo_.tap_reset = tapReset; + tapinfo_.ui = this; + tapinfo_.is_local = is_local; + tapinfo_.bdAddr = bdAddr; + tapinfo_.interface_id = interface_id; + tapinfo_.adapter_id = adapter_id; + tapinfo_.changes = &changes_; + + ui->hintLabel->setText(ui->hintLabel->text().arg(changes_)); + + for (int i_row = 0; i_row < ui->tableWidget->rowCount(); i_row += 1) { + for (int i_column = 0; i_column < ui->tableWidget->columnCount(); i_column += 1) { + QTableWidgetItem *item = new QTableWidgetItem(); + ui->tableWidget->setItem(i_row, i_column, item); + } + } + + bluetooth_devices_tap(&tapinfo_); + + cap_file_.retapPackets(); +} + + +BluetoothDeviceDialog::~BluetoothDeviceDialog() +{ + delete ui; + + remove_tap_listener(&tapinfo_); +} + +void BluetoothDeviceDialog::setTitle(QString bdAddr, QString name) +{ + QString titleBdAddr; + QString titleName; + + if (bdAddr.isEmpty()) + titleBdAddr = QString(tr("Unknown")); + else + titleBdAddr = bdAddr; + + if (name.isEmpty()) + titleName = ""; + else + titleName = " ("+name+")"; + + setWindowTitle(tr("Bluetooth Device - %1%2").arg(titleBdAddr).arg(titleName)); +} + +void BluetoothDeviceDialog::captureFileClosing() +{ + remove_tap_listener(&tapinfo_); + + WiresharkDialog::captureFileClosing(); +} + + +void BluetoothDeviceDialog::changeEvent(QEvent *event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + + +void BluetoothDeviceDialog::keyPressEvent(QKeyEvent *event) +{ +/* NOTE: Do nothing*, but in real it "takes focus" from button_box so allow user + * to use Enter button to jump to frame from tree widget */ +/* * - reimplement shortcuts from contex menu */ + + if (event->modifiers() & Qt::ControlModifier && event->key()== Qt::Key_M) + on_actionMark_Unmark_Row_triggered(); +} + +void BluetoothDeviceDialog::on_actionMark_Unmark_Cell_triggered() +{ + QTableWidgetItem *current_item = ui->tableWidget->currentItem(); + if (!current_item) + return; + + QBrush fg; + QBrush bg; + + if (current_item->background() == QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) { + fg = QBrush(); + bg = QBrush(); + } else { + fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg)); + bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)); + } + + current_item->setForeground(fg); + current_item->setBackground(bg); +} + + +void BluetoothDeviceDialog::on_actionMark_Unmark_Row_triggered() +{ + QBrush fg; + QBrush bg; + bool is_marked = TRUE; + + QTableWidgetItem *current_item = ui->tableWidget->currentItem(); + if (!current_item) + return; + + for (int i = 0; i < ui->tableWidget->columnCount(); i += 1) { + QTableWidgetItem *item = ui->tableWidget->item(current_item->row(), i); + if (item->background() != QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) + is_marked = FALSE; + } + + if (is_marked) { + fg = QBrush(); + bg = QBrush(); + } else { + fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg)); + bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)); + } + + for (int i = 0; i < ui->tableWidget->columnCount(); i += 1) { + QTableWidgetItem *item = ui->tableWidget->item(current_item->row(), i); + item->setForeground(fg); + item->setBackground(bg); + } +} + + +void BluetoothDeviceDialog::tableContextMenu(const QPoint &pos) +{ + context_menu_.popup(ui->tableWidget->viewport()->mapToGlobal(pos)); +} + +void BluetoothDeviceDialog::on_actionCopy_Cell_triggered() +{ + QTableWidgetItem *current_item = ui->tableWidget->currentItem(); + if (!current_item) + return; + + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + + copy = QString(current_item->text()); + + clipboard->setText(copy); +} + +void BluetoothDeviceDialog::on_actionCopy_Rows_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + QList items; + QList::iterator i_item; + + items = ui->tableWidget->selectedItems(); + + for (i_item = items.begin(); i_item != items.end(); ++i_item) { + copy += QString("%1 %2 %3\n") + .arg(ui->tableWidget->verticalHeaderItem((*i_item)->row())->text(), -40) + .arg(ui->tableWidget->item((*i_item)->row(), column_number_value)->text(), -50) + .arg(ui->tableWidget->item((*i_item)->row(), column_number_changes)->text(), -10); + } + + clipboard->setText(copy); +} + +void BluetoothDeviceDialog::on_actionCopy_All_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + + copy += QString("%1 %2 %3\n") + .arg("Headers", -40) + .arg(ui->tableWidget->horizontalHeaderItem(column_number_value)->text(), -50) + .arg(ui->tableWidget->horizontalHeaderItem(column_number_changes)->text(), -10); + + for (int i_row = 0; i_row < ui->tableWidget->rowCount(); i_row += 1) { + for (int i_column = 0; i_column < ui->tableWidget->columnCount(); i_column += 1) { + + copy += QString("%1 %2 %3\n") + .arg(ui->tableWidget->verticalHeaderItem(i_row)->text(), -40) + .arg(ui->tableWidget->item(i_row, column_number_value)->text(), -50) + .arg(ui->tableWidget->item(i_row, column_number_changes)->text(), -10); + } + } + + clipboard->setText(copy); +} + + + +void BluetoothDeviceDialog::tapReset(void *tapinfo_ptr) +{ + bluetooth_device_tapinfo_t *tapinfo = (bluetooth_device_tapinfo_t *) tapinfo_ptr; + BluetoothDeviceDialog *dialog = static_cast(tapinfo->ui); + + for (int i_row = 0; i_row < dialog->ui->tableWidget->rowCount(); i_row += 1) { + for (int i_column = 0; i_column < dialog->ui->tableWidget->columnCount(); i_column += 1) { + QTableWidgetItem *item = new QTableWidgetItem(); + dialog->ui->tableWidget->setItem(i_row, i_column, item); + } + } + *tapinfo->changes = 0; +} + +void BluetoothDeviceDialog::updateChanges(QTableWidget *tableWidget, QString value, const int row, guint *changes, packet_info *pinfo) +{ + QTableWidgetItem *item = tableWidget->item(row, column_number_value); + bluetooth_item_data_t *item_data = VariantPointer::asPtr(item->data(Qt::UserRole)); + + if (item->text() == value) + return; + + if (item_data->changes == -1) { + item_data->changes = 0; + } else { + *changes += 1; + item_data->changes += 1; + item_data->frame_number = pinfo->fd->num; + tableWidget->item(row, column_number_changes)->setText(QString::number(item_data->changes)); + } +} + +void BluetoothDeviceDialog::saveItemData(QTableWidgetItem *item, + bluetooth_device_tap_t *tap_device, packet_info *pinfo) +{ + if (item->data(Qt::UserRole).isValid()) + return; + + bluetooth_item_data_t *item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_device->interface_id; + item_data->adapter_id = tap_device->adapter_id; + item_data->changes = -1; + item_data->frame_number = pinfo->fd->num; + item->setData(Qt::UserRole, VariantPointer::asQVariant(item_data)); + +} + +tap_packet_status BluetoothDeviceDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t) +{ + bluetooth_device_tapinfo_t *tapinfo = static_cast(tapinfo_ptr); + BluetoothDeviceDialog *dialog = static_cast(tapinfo->ui); + bluetooth_device_tap_t *tap_device = static_cast(const_cast(data)); + QString bd_addr; + QString bd_addr_oui; + const gchar *manuf; + QTableWidget *tableWidget; + QTableWidgetItem *item; + QString field; + + tableWidget = dialog->ui->tableWidget; + + if (!((!tap_device->is_local && tap_device->has_bd_addr) || (tap_device->is_local && tapinfo->is_local && tap_device->interface_id == tapinfo->interface_id && tap_device->adapter_id == tapinfo->adapter_id))) { + return TAP_PACKET_REDRAW; + } + + if (tap_device->has_bd_addr) { + for (int i = 0; i < 6; ++i) { + bd_addr += QString("%1:").arg(tap_device->bd_addr[i], 2, 16, QChar('0')); + } + bd_addr.chop(1); // remove extra character ":" from the end of the string + if (!tap_device->is_local && bd_addr != tapinfo->bdAddr) + return TAP_PACKET_REDRAW; + + manuf = get_ether_name(tap_device->bd_addr); + if (manuf) { + int pos; + + bd_addr_oui = QString(manuf); + pos = static_cast(bd_addr_oui.indexOf('_')); + if (pos < 0) { + manuf = NULL; + } else { + bd_addr_oui.remove(pos, bd_addr_oui.size()); + } + } + + if (!manuf) + bd_addr_oui = ""; + + item = tableWidget->item(row_number_bd_addr, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, bd_addr, row_number_bd_addr, tapinfo->changes, pinfo); + item->setText(bd_addr); + + item = tableWidget->item(row_number_bd_addr_oui, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, bd_addr_oui, row_number_bd_addr_oui, tapinfo->changes, pinfo); + item->setText(bd_addr_oui); + + dialog->setTitle(bd_addr, tableWidget->item(row_number_name, column_number_value)->text()); + } + + switch (tap_device->type) { + case BLUETOOTH_DEVICE_LOCAL_ADAPTER: + case BLUETOOTH_DEVICE_BD_ADDR: + break; + case BLUETOOTH_DEVICE_NAME: + item = tableWidget->item(row_number_name, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, QString(tap_device->data.name), row_number_name, tapinfo->changes, pinfo); + + item->setText(tap_device->data.name); + + dialog->setTitle(tableWidget->item(row_number_bd_addr, column_number_value)->text(), tap_device->data.name); + + break; + case BLUETOOTH_DEVICE_RESET: + for (int i_row = 0; i_row < dialog->ui->tableWidget->rowCount(); i_row += 1) { + bluetooth_item_data_t *item_data; + + item = dialog->ui->tableWidget->item(i_row, column_number_value); + saveItemData(item, tap_device, pinfo); + + item_data = VariantPointer::asPtr(item->data(Qt::UserRole)); + + if (item_data->changes > -1) { + item_data->changes += 1; + item_data->frame_number = pinfo->fd->num; + dialog->ui->tableWidget->item(i_row, column_number_changes)->setText(QString::number(item_data->changes)); + } else { + item_data->changes = 0; + } + dialog->ui->tableWidget->item(i_row, column_number_value)->setText(""); + } + *tapinfo->changes += 1; + + break; + case BLUETOOTH_DEVICE_SCAN: + field = QString(val_to_str_const(tap_device->data.scan, bthci_cmd_scan_enable_values, "Unknown 0x%02x")); + item = tableWidget->item(row_number_scan, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_scan, tapinfo->changes, pinfo); + item->setText(field); + break; + case BLUETOOTH_DEVICE_LOCAL_VERSION: + field = QString(val_to_str_const(tap_device->data.local_version.hci_version, bthci_evt_hci_version, "Unknown 0x%02x")); + item = tableWidget->item(row_number_hci_version, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_hci_version, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.local_version.hci_revision); + item = tableWidget->item(row_number_hci_revision, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_hci_revision, tapinfo->changes, pinfo); + item->setText(field); + + field = QString(val_to_str_const(tap_device->data.local_version.lmp_version, bthci_evt_lmp_version, "Unknown 0x%02x")); + item = tableWidget->item(row_number_lmp_version, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_lmp_version, tapinfo->changes, pinfo); + item->setText(field); + + field = QString(val_to_str_const(tap_device->data.local_version.lmp_version, bthci_evt_lmp_version, "Unknown 0x%02x")); + item = tableWidget->item(row_number_lmp_version, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_lmp_version, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.local_version.lmp_subversion); + item = tableWidget->item(row_number_lmp_subversion, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_lmp_subversion, tapinfo->changes, pinfo); + item->setText(field); + + field = QString(val_to_str_ext_const(tap_device->data.local_version.manufacturer, &bluetooth_company_id_vals_ext, "Unknown 0x%04x")); + item = tableWidget->item(row_number_manufacturer, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_manufacturer, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_REMOTE_VERSION: + field = QString(val_to_str_const(tap_device->data.remote_version.lmp_version, bthci_evt_lmp_version, "Unknown 0x%02x")); + item = tableWidget->item(row_number_lmp_version, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_lmp_version, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.remote_version.lmp_subversion); + item = tableWidget->item(row_number_lmp_subversion, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_lmp_subversion, tapinfo->changes, pinfo); + item->setText(field); + + field = QString(val_to_str_ext_const(tap_device->data.remote_version.manufacturer, &bluetooth_company_id_vals_ext, "Unknown 0x%04x")); + item = tableWidget->item(row_number_manufacturer, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_manufacturer, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_VOICE_SETTING: + field = QString("%1").arg(tap_device->data.voice_setting, 4, 16, QChar('0')); + item = tableWidget->item(row_number_voice_setting, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_voice_setting, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_CLASS_OF_DEVICE: + field = QString("%1").arg(tap_device->data.class_of_device, 6, 16, QChar('0')); + item = tableWidget->item(row_number_class_of_device, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_class_of_device, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_AUTHENTICATION: + field = QString(val_to_str_const(tap_device->data.authentication, bthci_cmd_authentication_enable_values, "Unknown 0x%02x")); + item = tableWidget->item(row_number_authentication, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_authentication, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_ENCRYPTION: + field = QString(val_to_str_const(tap_device->data.encryption, bthci_cmd_encrypt_mode_vals, "Unknown 0x%02x")); + item = tableWidget->item(row_number_encryption, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_encryption, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_SIMPLE_PAIRING_MODE: + field = QString(tap_device->data.encryption ? tr("enabled") : tr("disabled")); + item = tableWidget->item(row_number_simple_pairing_mode, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_simple_pairing_mode, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_PAGE_TIMEOUT: + field = QString(tr("%1 ms (%2 slots)")).arg(tap_device->data.page_timeout * 0.625).arg(tap_device->data.page_timeout); + item = tableWidget->item(row_number_page_timeout, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_page_timeout, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_INQUIRY_MODE: + field = QString(val_to_str_const(tap_device->data.inquiry_mode, bthci_cmd_inq_modes, "Unknown 0x%02x")); + item = tableWidget->item(row_number_inquiry_mode, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_inquiry_mode, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_MTUS: + field = QString::number(tap_device->data.mtus.acl_mtu); + item = tableWidget->item(row_number_acl_mtu, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_acl_mtu, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.mtus.acl_packets); + item = tableWidget->item(row_number_acl_packets, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_acl_packets, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.mtus.sco_mtu); + item = tableWidget->item(row_number_sco_mtu, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_sco_mtu, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.mtus.sco_packets); + item = tableWidget->item(row_number_sco_packets, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_sco_packets, tapinfo->changes, pinfo); + item->setText(field); + + break; + case BLUETOOTH_DEVICE_LE_MTU: + field = QString::number(tap_device->data.le_mtus.acl_mtu); + item = tableWidget->item(row_number_le_acl_mtu, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_le_acl_mtu, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.le_mtus.acl_packets); + item = tableWidget->item(row_number_le_acl_packets, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_le_acl_packets, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.le_mtus.iso_mtu); + item = tableWidget->item(row_number_le_iso_mtu, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_le_iso_mtu, tapinfo->changes, pinfo); + item->setText(field); + + field = QString::number(tap_device->data.le_mtus.iso_packets); + item = tableWidget->item(row_number_le_iso_packets, column_number_value); + saveItemData(item, tap_device, pinfo); + updateChanges(tableWidget, field, row_number_le_iso_packets, tapinfo->changes, pinfo); + item->setText(field); + + break; + } + + dialog->ui->hintLabel->setText(QString(tr("%1 changes")).arg(*tapinfo->changes)); + + return TAP_PACKET_REDRAW; +} + +void BluetoothDeviceDialog::interfaceCurrentIndexChanged(int) +{ + cap_file_.retapPackets(); +} + +void BluetoothDeviceDialog::showInformationStepsChanged(int) +{ + cap_file_.retapPackets(); +} + + +void BluetoothDeviceDialog::on_tableWidget_itemActivated(QTableWidgetItem *item) +{ + if (!cap_file_.isValid()) + return; + + if (!item->data(Qt::UserRole).isValid()) + return; + + bluetooth_item_data_t *item_data = VariantPointer::asPtr(item->data(Qt::UserRole)); + + emit goToPacket(item_data->frame_number); + +} + +void BluetoothDeviceDialog::on_actionSave_as_image_triggered() +{ + QPixmap image; + + QString fileName = WiresharkFileDialog::getSaveFileName(this, + tr("Save Table Image"), + "bluetooth_device_table.png", + tr("PNG Image (*.png)")); + + if (fileName.isEmpty()) return; + + image = ui->tableWidget->grab(); + image.save(fileName, "PNG"); +} + +void BluetoothDeviceDialog::on_buttonBox_clicked(QAbstractButton *) +{ + +} diff --git a/ui/qt/bluetooth_device_dialog.h b/ui/qt/bluetooth_device_dialog.h new file mode 100644 index 00000000..7ae37bce --- /dev/null +++ b/ui/qt/bluetooth_device_dialog.h @@ -0,0 +1,102 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BLUETOOTH_DEVICE_DIALOG_H +#define BLUETOOTH_DEVICE_DIALOG_H + +#include "config.h" + +#include + +#include "wireshark_dialog.h" +#include "cfile.h" + +#include "epan/tap.h" + +#include "epan/dissectors/packet-bluetooth.h" + +#include +#include + +class QAbstractButton; +class QPushButton; +class QTreeWidgetItem; + +typedef struct _bluetooth_device_tapinfo_t { + tap_reset_cb tap_reset; + tap_packet_cb tap_packet; + QString bdAddr; + guint32 interface_id; + guint32 adapter_id; + gboolean is_local; + void *ui; + guint *changes; +} bluetooth_device_tapinfo_t; + +typedef struct _bluetooth_item_data_t { + guint32 interface_id; + guint32 adapter_id; + guint32 frame_number; + gint changes; +} bluetooth_item_data_t; + +namespace Ui { +class BluetoothDeviceDialog; +} + +class BluetoothDeviceDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit BluetoothDeviceDialog(QWidget &parent, CaptureFile &cf, QString bdAddr, QString name, guint32 interface_id, guint32 adapter_id, gboolean is_local); + ~BluetoothDeviceDialog(); + +public slots: + +signals: + void updateFilter(QString &filter, bool force = false); + void captureFileChanged(capture_file *cf); + void goToPacket(int packet_num); + +protected: + void keyPressEvent(QKeyEvent *event); + void captureFileClosing(); + +protected slots: + void changeEvent(QEvent* event); + +private: + Ui::BluetoothDeviceDialog *ui; + + bluetooth_device_tapinfo_t tapinfo_; + QMenu context_menu_; + guint changes_; + + static void tapReset(void *tapinfo_ptr); + static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t flags); + static void updateChanges(QTableWidget *tableWidget, QString value, const int row, guint *changes, packet_info *pinfo); + static void saveItemData(QTableWidgetItem *item, bluetooth_device_tap_t *tap_device, packet_info *pinfo); + +private slots: + void setTitle(QString bdAddr, QString name); + void on_tableWidget_itemActivated(QTableWidgetItem *item); + void on_buttonBox_clicked(QAbstractButton *button); + void on_actionMark_Unmark_Cell_triggered(); + void on_actionMark_Unmark_Row_triggered(); + void on_actionCopy_Cell_triggered(); + void on_actionCopy_Rows_triggered(); + void on_actionCopy_All_triggered(); + void on_actionSave_as_image_triggered(); + void tableContextMenu(const QPoint &pos); + void interfaceCurrentIndexChanged(int index); + void showInformationStepsChanged(int state); +}; + +#endif // BLUETOOTH_DEVICE_DIALOG_H diff --git a/ui/qt/bluetooth_device_dialog.ui b/ui/qt/bluetooth_device_dialog.ui new file mode 100644 index 00000000..fcc7d093 --- /dev/null +++ b/ui/qt/bluetooth_device_dialog.ui @@ -0,0 +1,300 @@ + + + BluetoothDeviceDialog + + + + 0 + 0 + 544 + 679 + + + + + 0 + 0 + + + + Qt::CustomContextMenu + + + Bluetooth Device + + + + + + Qt::CustomContextMenu + + + Qt::ScrollBarAsNeeded + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + Qt::SolidLine + + + false + + + 100 + + + true + + + false + + + false + + + false + + + false + + + + BD_ADDR + + + + + OUI + + + + + Name + + + + + Class of Device + + + + + LMP Version + + + + + LMP Subversion + + + + + Manufacturer + + + + + HCI Version + + + + + HCI Revision + + + + + Scan + + + + + Authentication + + + + + Encryption + + + + + ACL MTU + + + + + ACL Total Packets + + + + + SCO MTU + + + + + SCO Total Packets + + + + + LE ACL MTU + + + + + LE ACL Total Packets + + + + + LE ISO MTU + + + + + LE ISO Total Packets + + + + + Inquiry Mode + + + + + Page Timeout + + + + + Simple Pairing Mode + + + + + Voice Setting + + + + + Value + + + + + Changes + + + + + + + + + + + + + %1 changes + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + Copy Cell + + + + + Copy Rows + + + + + Copy All + + + + + Save as image + + + + + Mark/Unmark Row + + + Mark/Unmark Row + + + Ctrl+M + + + + + Mark/Unmark Cell + + + + + + + buttonBox + accepted() + BluetoothDeviceDialog + accept() + + + 374 + 407 + + + 374 + 214 + + + + + buttonBox + rejected() + BluetoothDeviceDialog + reject() + + + 374 + 407 + + + 374 + 214 + + + + + diff --git a/ui/qt/bluetooth_devices_dialog.cpp b/ui/qt/bluetooth_devices_dialog.cpp new file mode 100644 index 00000000..87d77772 --- /dev/null +++ b/ui/qt/bluetooth_devices_dialog.cpp @@ -0,0 +1,455 @@ +/* bluetooth_devices_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "bluetooth_devices_dialog.h" +#include + +#include "bluetooth_device_dialog.h" + +#include + +#include "epan/epan.h" +#include "epan/addr_resolv.h" +#include "epan/to_str.h" +#include "epan/epan_dissect.h" +#include "epan/prefs.h" +#include "epan/dissectors/packet-bluetooth.h" +#include "epan/dissectors/packet-bthci_evt.h" + +#include + +#include "ui/simple_dialog.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include + +static const int column_number_bd_addr = 0; +static const int column_number_bd_addr_oui = 1; +static const int column_number_name = 2; +static const int column_number_lmp_version = 3; +static const int column_number_lmp_subversion = 4; +static const int column_number_manufacturer = 5; +static const int column_number_hci_version = 6; +static const int column_number_hci_revision = 7; +static const int column_number_is_local_adapter = 8; + + +static tap_packet_status +bluetooth_device_tap_packet(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *edt, const void* data, tap_flags_t flags) +{ + bluetooth_devices_tapinfo_t *tapinfo = (bluetooth_devices_tapinfo_t *) tapinfo_ptr; + + if (tapinfo->tap_packet) + tapinfo->tap_packet(tapinfo, pinfo, edt, data, flags); + + return TAP_PACKET_REDRAW; +} + +static void +bluetooth_device_tap_reset(void *tapinfo_ptr) +{ + bluetooth_devices_tapinfo_t *tapinfo = (bluetooth_devices_tapinfo_t *) tapinfo_ptr; + + if (tapinfo->tap_reset) + tapinfo->tap_reset(tapinfo); +} + +BluetoothDevicesDialog::BluetoothDevicesDialog(QWidget &parent, CaptureFile &cf, PacketList *packet_list) : + WiresharkDialog(parent, cf), + ui(new Ui::BluetoothDevicesDialog) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 2 / 3); + + packet_list_ = packet_list; + + connect(ui->tableTreeWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(tableContextMenu(const QPoint &))); + connect(ui->tableTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(tableItemDoubleClicked(QTreeWidgetItem *, int))); + connect(ui->interfaceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(interfaceCurrentIndexChanged(int))); + connect(ui->showInformationStepsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(showInformationStepsChanged(int))); + + ui->tableTreeWidget->sortByColumn(column_number_bd_addr, Qt::AscendingOrder); + + ui->tableTreeWidget->setStyleSheet("QTreeView::item:hover{background-color:lightyellow; color:black;}"); + + context_menu_.addActions(QList() << ui->actionMark_Unmark_Cell); + context_menu_.addActions(QList() << ui->actionMark_Unmark_Row); + context_menu_.addActions(QList() << ui->actionCopy_Cell); + context_menu_.addActions(QList() << ui->actionCopy_Rows); + context_menu_.addActions(QList() << ui->actionCopy_All); + context_menu_.addActions(QList() << ui->actionSave_as_image); + + tapinfo_.tap_packet = tapPacket; + tapinfo_.tap_reset = tapReset; + tapinfo_.ui = this; + + registerTapListener("bluetooth.device", &tapinfo_, NULL, + 0, + bluetooth_device_tap_reset, + bluetooth_device_tap_packet, + NULL + ); + ui->hintLabel->setText(ui->hintLabel->text().arg(0)); + + cap_file_.retapPackets(); +} + + +BluetoothDevicesDialog::~BluetoothDevicesDialog() +{ + delete ui; +} + + +void BluetoothDevicesDialog::captureFileClosed() +{ + ui->interfaceComboBox->setEnabled(FALSE); + ui->showInformationStepsCheckBox->setEnabled(FALSE); + + WiresharkDialog::captureFileClosed(); +} + + +void BluetoothDevicesDialog::changeEvent(QEvent *event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + + +void BluetoothDevicesDialog::keyPressEvent(QKeyEvent *event) +{ +/* NOTE: Do nothing*, but in real it "takes focus" from button_box so allow user + * to use Enter button to jump to frame from tree widget */ +/* * - reimplement shortcuts from contex menu */ + + if (event->modifiers() & Qt::ControlModifier && event->key()== Qt::Key_M) + on_actionMark_Unmark_Row_triggered(); +} + + +void BluetoothDevicesDialog::tableContextMenu(const QPoint &pos) +{ + context_menu_.popup(ui->tableTreeWidget->viewport()->mapToGlobal(pos)); +} + +void BluetoothDevicesDialog::tableItemDoubleClicked(QTreeWidgetItem *item, int) +{ + bluetooth_item_data_t *item_data; + BluetoothDeviceDialog *bluetooth_device_dialog; + + item_data = VariantPointer::asPtr(item->data(0, Qt::UserRole)); + bluetooth_device_dialog = new BluetoothDeviceDialog(*this, cap_file_, item->text(column_number_bd_addr), item->text(column_number_name), item_data->interface_id, item_data->adapter_id, !item->text(column_number_is_local_adapter).isEmpty()); + connect(bluetooth_device_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); + bluetooth_device_dialog->show(); +} + + +void BluetoothDevicesDialog::on_actionMark_Unmark_Cell_triggered() +{ + QBrush fg; + QBrush bg; + + if (ui->tableTreeWidget->currentItem()->background(ui->tableTreeWidget->currentColumn()) == QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) { + fg = QBrush(); + bg = QBrush(); + } else { + fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg)); + bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)); + } + + ui->tableTreeWidget->currentItem()->setForeground(ui->tableTreeWidget->currentColumn(), fg); + ui->tableTreeWidget->currentItem()->setBackground(ui->tableTreeWidget->currentColumn(), bg); +} + + +void BluetoothDevicesDialog::on_actionMark_Unmark_Row_triggered() +{ + QBrush fg; + QBrush bg; + bool is_marked = TRUE; + + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i += 1) { + if (ui->tableTreeWidget->currentItem()->background(i) != QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) + is_marked = FALSE; + } + + if (is_marked) { + fg = QBrush(); + bg = QBrush(); + } else { + fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg)); + bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)); + } + + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i += 1) { + ui->tableTreeWidget->currentItem()->setForeground(i, fg); + ui->tableTreeWidget->currentItem()->setBackground(i, bg); + } +} + + +void BluetoothDevicesDialog::on_actionCopy_Cell_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + + copy = QString(ui->tableTreeWidget->currentItem()->text(ui->tableTreeWidget->currentColumn())); + + clipboard->setText(copy); +} + + +void BluetoothDevicesDialog::on_actionCopy_Rows_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + QList items; + QList::iterator i_item; + + items = ui->tableTreeWidget->selectedItems(); + + for (i_item = items.begin(); i_item != items.end(); ++i_item) { + copy += QString("%1 %2 %3 %4 %5 %6 %7 %8 %9\n") + .arg((*i_item)->text(column_number_bd_addr), -20) + .arg((*i_item)->text(column_number_bd_addr_oui), -20) + .arg((*i_item)->text(column_number_name), -30) + .arg((*i_item)->text(column_number_lmp_version), -20) + .arg((*i_item)->text(column_number_lmp_subversion), -20) + .arg((*i_item)->text(column_number_manufacturer), -30) + .arg((*i_item)->text(column_number_hci_version), -20) + .arg((*i_item)->text(column_number_hci_revision), -20) + .arg((*i_item)->text(column_number_is_local_adapter), -20); + } + + clipboard->setText(copy); +} + +void BluetoothDevicesDialog::tapReset(void *tapinfo_ptr) +{ + bluetooth_devices_tapinfo_t *tapinfo = (bluetooth_devices_tapinfo_t *) tapinfo_ptr; + BluetoothDevicesDialog *bluetooth_devices_dialog = static_cast(tapinfo->ui); + + bluetooth_devices_dialog->ui->tableTreeWidget->clear(); +} + +tap_packet_status BluetoothDevicesDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t) +{ + bluetooth_devices_tapinfo_t *tapinfo = static_cast(tapinfo_ptr); + BluetoothDevicesDialog *dialog = static_cast(tapinfo->ui); + bluetooth_device_tap_t *tap_device = static_cast(const_cast(data)); + QString bd_addr; + QString bd_addr_oui; + const gchar *manuf; + QTreeWidgetItem *item = NULL; + + if (dialog->file_closed_) + return TAP_PACKET_DONT_REDRAW; + + if (pinfo->rec->rec_type != REC_TYPE_PACKET) + return TAP_PACKET_DONT_REDRAW; + + if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID) { + gchar *interface; + const char *interface_name; + + interface_name = epan_get_interface_name(pinfo->epan, pinfo->rec->rec_header.packet_header.interface_id); + interface = wmem_strdup_printf(pinfo->pool, "%u: %s", pinfo->rec->rec_header.packet_header.interface_id, interface_name); + + if (dialog->ui->interfaceComboBox->findText(interface) == -1) + dialog->ui->interfaceComboBox->addItem(interface); + + if (interface && dialog->ui->interfaceComboBox->currentIndex() > 0) { + if (dialog->ui->interfaceComboBox->currentText() != interface) + return TAP_PACKET_REDRAW; + } + } + + if (tap_device->has_bd_addr) { + for (int i = 0; i < 6; ++i) { + bd_addr += QString("%1:").arg(tap_device->bd_addr[i], 2, 16, QChar('0')); + } + bd_addr.chop(1); // remove extra character ":" from the end of the string + manuf = get_ether_name(tap_device->bd_addr); + if (manuf) { + int pos; + + bd_addr_oui = QString(manuf); + pos = static_cast(bd_addr_oui.indexOf('_')); + if (pos < 0) { + manuf = NULL; + } else { + bd_addr_oui.remove(pos, bd_addr_oui.size()); + } + } + + if (!manuf) + bd_addr_oui = ""; + } + + if (dialog->ui->showInformationStepsCheckBox->checkState() != Qt::Checked) { + QTreeWidgetItemIterator i_item(dialog->ui->tableTreeWidget); + + while (*i_item) { + QTreeWidgetItem *current_item = static_cast(*i_item); + bluetooth_item_data_t *item_data = VariantPointer::asPtr(current_item->data(0, Qt::UserRole)); + + if ((tap_device->has_bd_addr && current_item->text(column_number_bd_addr) == bd_addr) || + (tap_device->is_local && + item_data->interface_id == tap_device->interface_id && + item_data->adapter_id == tap_device->adapter_id && + !current_item->text(column_number_is_local_adapter).isEmpty())) { + item = current_item; + break; + } + ++i_item; + } + } + + if (!item) { + item = new QTreeWidgetItem(dialog->ui->tableTreeWidget); + item->setText(column_number_bd_addr, bd_addr); + item->setText(column_number_bd_addr_oui, bd_addr_oui); + if (tap_device->is_local) { + item->setText(column_number_is_local_adapter, tr("true")); + } + + bluetooth_item_data_t *item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_device->interface_id; + item_data->adapter_id = tap_device->adapter_id; + item_data->frame_number = pinfo->num; + item->setData(0, Qt::UserRole, VariantPointer::asQVariant(item_data)); + } + + if (tap_device->type == BLUETOOTH_DEVICE_BD_ADDR) { + item->setText(column_number_bd_addr, bd_addr); + item->setText(column_number_bd_addr_oui, bd_addr_oui); + } + + if (tap_device->type == BLUETOOTH_DEVICE_NAME) { + item->setText(column_number_name, tap_device->data.name); + } + + if (tap_device->type == BLUETOOTH_DEVICE_LOCAL_ADAPTER) + item->setText(column_number_is_local_adapter, tr("true")); + + if (tap_device->type == BLUETOOTH_DEVICE_LOCAL_VERSION) { + item->setText(column_number_hci_version, val_to_str_const(tap_device->data.local_version.hci_version, bthci_evt_hci_version, "Unknown 0x%02x")); + item->setText(column_number_hci_revision, QString::number(tap_device->data.local_version.hci_revision)); + item->setText(column_number_lmp_version, val_to_str_const(tap_device->data.local_version.lmp_version, bthci_evt_lmp_version, "Unknown 0x%02x")); + item->setText(column_number_lmp_subversion, QString::number(tap_device->data.local_version.lmp_subversion)); + item->setText(column_number_manufacturer, val_to_str_ext_const(tap_device->data.local_version.manufacturer, &bluetooth_company_id_vals_ext, "Unknown 0x%04x")); + } + if (tap_device->type == BLUETOOTH_DEVICE_REMOTE_VERSION) { + item->setText(column_number_lmp_version, val_to_str_const(tap_device->data.remote_version.lmp_version, bthci_evt_lmp_version, "Unknown 0x%02x")); + item->setText(column_number_lmp_subversion, QString::number(tap_device->data.remote_version.lmp_subversion)); + item->setText(column_number_manufacturer, val_to_str_ext_const(tap_device->data.remote_version.manufacturer, &bluetooth_company_id_vals_ext, "Unknown 0x%04x")); + } + + for (int i = 0; i < dialog->ui->tableTreeWidget->columnCount(); i++) { + dialog->ui->tableTreeWidget->resizeColumnToContents(i); + } + + dialog->ui->hintLabel->setText(QString(tr("%1 items; Right click for more option; Double click for device details")).arg(dialog->ui->tableTreeWidget->topLevelItemCount())); + + return TAP_PACKET_REDRAW; +} + +void BluetoothDevicesDialog::interfaceCurrentIndexChanged(int) +{ + cap_file_.retapPackets(); +} + +void BluetoothDevicesDialog::showInformationStepsChanged(int) +{ + cap_file_.retapPackets(); +} + +void BluetoothDevicesDialog::on_tableTreeWidget_itemActivated(QTreeWidgetItem *item, int) +{ + if (file_closed_) + return; + + bluetooth_item_data_t *item_data = VariantPointer::asPtr(item->data(0, Qt::UserRole)); + + emit goToPacket(item_data->frame_number); + +} + +void BluetoothDevicesDialog::on_actionCopy_All_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + QTreeWidgetItemIterator i_item(ui->tableTreeWidget); + QTreeWidgetItem *item; + + item = ui->tableTreeWidget->headerItem(); + + copy += QString("%1 %2 %3 %4 %5 %6 %7 %8 %9\n") + .arg(item->text(column_number_bd_addr), -20) + .arg(item->text(column_number_bd_addr_oui), -20) + .arg(item->text(column_number_name), -30) + .arg(item->text(column_number_lmp_version), -20) + .arg(item->text(column_number_lmp_subversion), -20) + .arg(item->text(column_number_manufacturer), -30) + .arg(item->text(column_number_hci_version), -20) + .arg(item->text(column_number_hci_revision), -20) + .arg(item->text(column_number_is_local_adapter), -20); + + while (*i_item) { + item = static_cast(*i_item); + copy += QString("%1 %2 %3 %4 %5 %6 %7 %8 %9\n") + .arg(item->text(column_number_bd_addr), -20) + .arg(item->text(column_number_bd_addr_oui), -20) + .arg(item->text(column_number_name), -30) + .arg(item->text(column_number_lmp_version), -20) + .arg(item->text(column_number_lmp_subversion), -20) + .arg(item->text(column_number_manufacturer), -30) + .arg(item->text(column_number_hci_version), -20) + .arg(item->text(column_number_hci_revision), -20) + .arg(item->text(column_number_is_local_adapter), -20); + ++i_item; + } + + clipboard->setText(copy); +} + +void BluetoothDevicesDialog::on_actionSave_as_image_triggered() +{ + QPixmap image; + + QString fileName = WiresharkFileDialog::getSaveFileName(this, + tr("Save Table Image"), + "bluetooth_devices_table.png", + tr("PNG Image (*.png)")); + + if (fileName.isEmpty()) return; + + image = ui->tableTreeWidget->grab(); + image.save(fileName, "PNG"); +} + +void BluetoothDevicesDialog::on_buttonBox_clicked(QAbstractButton *) +{ +/* if (button == foo_button_) */ +} diff --git a/ui/qt/bluetooth_devices_dialog.h b/ui/qt/bluetooth_devices_dialog.h new file mode 100644 index 00000000..cf78d9a0 --- /dev/null +++ b/ui/qt/bluetooth_devices_dialog.h @@ -0,0 +1,86 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BLUETOOTH_DEVICES_DIALOG_H +#define BLUETOOTH_DEVICES_DIALOG_H + +#include "config.h" + +#include + +#include "wireshark_dialog.h" +#include "cfile.h" +#include "packet_list.h" + +#include "epan/tap.h" + +#include + +class QAbstractButton; +class QPushButton; +class QTreeWidgetItem; + +typedef struct _bluetooth_devices_tapinfo_t { + tap_reset_cb tap_reset; + tap_packet_cb tap_packet; + void *ui; +} bluetooth_devices_tapinfo_t; + +namespace Ui { +class BluetoothDevicesDialog; +} + +class BluetoothDevicesDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit BluetoothDevicesDialog(QWidget &parent, CaptureFile &cf, PacketList *packet_list); + ~BluetoothDevicesDialog(); + +public slots: + +signals: + void updateFilter(QString filter, bool force = false); + void captureFileChanged(capture_file *cf); + void goToPacket(int packet_num); + +protected: + void keyPressEvent(QKeyEvent *event); + void captureFileClosed(); + +protected slots: + void changeEvent(QEvent* event); + +private: + Ui::BluetoothDevicesDialog *ui; + PacketList *packet_list_; + + bluetooth_devices_tapinfo_t tapinfo_; + QMenu context_menu_; + + static void tapReset(void *tapinfo_ptr); + static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t flags); + +private slots: + void on_tableTreeWidget_itemActivated(QTreeWidgetItem *item, int); + void on_buttonBox_clicked(QAbstractButton *button); + void on_actionMark_Unmark_Cell_triggered(); + void on_actionMark_Unmark_Row_triggered(); + void on_actionCopy_Cell_triggered(); + void on_actionCopy_Rows_triggered(); + void on_actionCopy_All_triggered(); + void on_actionSave_as_image_triggered(); + void tableContextMenu(const QPoint &pos); + void tableItemDoubleClicked(QTreeWidgetItem *item, int column); + void interfaceCurrentIndexChanged(int index); + void showInformationStepsChanged(int state); +}; + +#endif // BLUETOOTH_DEVICES_DIALOG_H diff --git a/ui/qt/bluetooth_devices_dialog.ui b/ui/qt/bluetooth_devices_dialog.ui new file mode 100644 index 00000000..a4cfd91b --- /dev/null +++ b/ui/qt/bluetooth_devices_dialog.ui @@ -0,0 +1,233 @@ + + + BluetoothDevicesDialog + + + + 0 + 0 + 880 + 477 + + + + + 0 + 0 + + + + Bluetooth Devices + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::ExtendedSelection + + + Qt::ElideMiddle + + + false + + + false + + + true + + + false + + + false + + + true + + + + BD_ADDR + + + + + OUI + + + + + Name + + + + + LMP Version + + + + + LMP Subversion + + + + + Manufacturer + + + + + HCI Version + + + + + HCI Revision + + + + + Is Local Adapter + + + + + + + + -1 + + + QLayout::SetDefaultConstraint + + + 0 + + + + + + 0 + 0 + + + + + 350 + 0 + + + + + All Interfaces + + + + + + + + Show information steps + + + false + + + + + + + + + %1 items; Right click for more option; Double click for device details + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + Copy Cell + + + + + Copy Rows + + + + + Copy All + + + + + Save as image + + + + + Mark/Unmark Row + + + Mark/Unmark Row + + + Ctrl-M + + + + + Mark/Unmark Cell + + + + + + + buttonBox + accepted() + BluetoothDevicesDialog + accept() + + + 374 + 407 + + + 374 + 214 + + + + + buttonBox + rejected() + BluetoothDevicesDialog + reject() + + + 374 + 407 + + + 374 + 214 + + + + + diff --git a/ui/qt/bluetooth_hci_summary_dialog.cpp b/ui/qt/bluetooth_hci_summary_dialog.cpp new file mode 100644 index 00000000..15d2df5e --- /dev/null +++ b/ui/qt/bluetooth_hci_summary_dialog.cpp @@ -0,0 +1,935 @@ +/* bluetooth_hci_summary_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "bluetooth_hci_summary_dialog.h" +#include + +#include "bluetooth_device_dialog.h" + +#include + +#include "epan/epan.h" +#include "epan/addr_resolv.h" +#include "epan/to_str.h" +#include "epan/epan_dissect.h" +#include "epan/prefs.h" +#include "epan/dissectors/packet-bluetooth.h" +#include "epan/dissectors/packet-bthci_cmd.h" +#include "epan/dissectors/packet-bthci_evt.h" + +#include + +#include "ui/simple_dialog.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include + +static const int column_number_name = 0; +static const int column_number_ogf = 1; +static const int column_number_ocf = 2; +static const int column_number_opcode = 3; +static const int column_number_event = 4; +static const int column_number_subevent = 5; +static const int column_number_status = 6; +static const int column_number_reason = 7; +static const int column_number_hardware_error = 8; +static const int column_number_occurrence = 9; + +static tap_packet_status +bluetooth_hci_summary_tap_packet(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *edt, const void* data, tap_flags_t flags) +{ + bluetooth_hci_summary_tapinfo_t *tapinfo = (bluetooth_hci_summary_tapinfo_t *) tapinfo_ptr; + + if (tapinfo->tap_packet) + tapinfo->tap_packet(tapinfo, pinfo, edt, data, flags); + + return TAP_PACKET_REDRAW; +} + +static void +bluetooth_hci_summary_tap_reset(void *tapinfo_ptr) +{ + bluetooth_hci_summary_tapinfo_t *tapinfo = (bluetooth_hci_summary_tapinfo_t *) tapinfo_ptr; + + if (tapinfo->tap_reset) + tapinfo->tap_reset(tapinfo); +} + +static void +bluetooth_hci_summary_tap_init(void *data) +{ + GString *error_string; + + error_string = register_tap_listener("bluetooth.hci_summary", data, NULL, + 0, + bluetooth_hci_summary_tap_reset, + bluetooth_hci_summary_tap_packet, + NULL, + NULL + ); + + if (error_string != NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "%s", error_string->str); + g_string_free(error_string, TRUE); + } +} + + +BluetoothHciSummaryDialog::BluetoothHciSummaryDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::BluetoothHciSummaryDialog) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 2 / 3); + + connect(ui->tableTreeWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(tableContextMenu(const QPoint &))); + connect(ui->tableTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(tableItemExpanded(QTreeWidgetItem *))); + connect(ui->tableTreeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(tableItemCollapsed(QTreeWidgetItem *))); + + connect(ui->interfaceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(interfaceCurrentIndexChanged(int))); + connect(ui->adapterComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(adapterCurrentIndexChanged(int))); + connect(ui->displayFilterLineEdit, SIGNAL(returnPressed()), this, SLOT(displayFilterLineEditAccepted())); + connect(ui->resultsFilterLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(resultsFilterLineEditChanged(const QString &))); + + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i++) { + ui->tableTreeWidget->resizeColumnToContents(i); + } + + ui->tableTreeWidget->setStyleSheet("QTreeView::item:hover{background-color:lightyellow; color:black;}"); + + context_menu_.addActions(QList() << ui->actionMark_Unmark_Cell); + context_menu_.addActions(QList() << ui->actionMark_Unmark_Row); + context_menu_.addActions(QList() << ui->actionCopy_Cell); + context_menu_.addActions(QList() << ui->actionCopy_Rows); + context_menu_.addActions(QList() << ui->actionCopy_All); + context_menu_.addActions(QList() << ui->actionSave_as_image); + + tapinfo_.tap_packet = tapPacket; + tapinfo_.tap_reset = tapReset; + tapinfo_.ui = this; + + item_link_control_ = ui->tableTreeWidget->topLevelItem(0); + item_link_policy_ = ui->tableTreeWidget->topLevelItem(1); + item_controller_and_baseband_ = ui->tableTreeWidget->topLevelItem(2); + item_informational_ = ui->tableTreeWidget->topLevelItem(3); + item_status_parameters_ = ui->tableTreeWidget->topLevelItem(4); + item_testing_ = ui->tableTreeWidget->topLevelItem(5); + item_low_energy_ = ui->tableTreeWidget->topLevelItem(6); + item_logo_testing_ = ui->tableTreeWidget->topLevelItem(7); + item_vendor_ = ui->tableTreeWidget->topLevelItem(8); + item_unknown_ogf_ = ui->tableTreeWidget->topLevelItem(9); + item_events_ = ui->tableTreeWidget->topLevelItem(10); + item_status_ = ui->tableTreeWidget->topLevelItem(11); + item_reason_ = ui->tableTreeWidget->topLevelItem(12); + item_hardware_errors_ = ui->tableTreeWidget->topLevelItem(13); + + bluetooth_hci_summary_tap_init(&tapinfo_); + + cap_file_.retapPackets(); +} + + +BluetoothHciSummaryDialog::~BluetoothHciSummaryDialog() +{ + delete ui; + + remove_tap_listener(&tapinfo_); +} + + +void BluetoothHciSummaryDialog::captureFileClosing() +{ + remove_tap_listener(&tapinfo_); + + WiresharkDialog::captureFileClosing(); +} + + +void BluetoothHciSummaryDialog::captureFileClosed() +{ + ui->interfaceComboBox->setEnabled(FALSE); + ui->adapterComboBox->setEnabled(FALSE); + + WiresharkDialog::captureFileClosed(); +} + + +void BluetoothHciSummaryDialog::changeEvent(QEvent *event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + + +void BluetoothHciSummaryDialog::keyPressEvent(QKeyEvent *event) +{ +/* NOTE: Do nothing*, but in real it "takes focus" from button_box so allow user + * to use Enter button to jump to frame from tree widget */ +/* * - reimplement shortcuts from contex menu */ + + if (event->modifiers() & Qt::ControlModifier && event->key()== Qt::Key_M) + on_actionMark_Unmark_Row_triggered(); +} + + +void BluetoothHciSummaryDialog::tableContextMenu(const QPoint &pos) +{ + context_menu_.popup(ui->tableTreeWidget->viewport()->mapToGlobal(pos)); +} + +void BluetoothHciSummaryDialog::tableItemExpanded(QTreeWidgetItem *) +{ + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i++) { + ui->tableTreeWidget->resizeColumnToContents(i); + } +} + +void BluetoothHciSummaryDialog::tableItemCollapsed(QTreeWidgetItem *) +{ + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i++) { + ui->tableTreeWidget->resizeColumnToContents(i); + } +} + +void BluetoothHciSummaryDialog::on_actionMark_Unmark_Cell_triggered() +{ + QBrush fg; + QBrush bg; + + if (ui->tableTreeWidget->currentItem()->background(ui->tableTreeWidget->currentColumn()) == QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) { + fg = QBrush(); + bg = QBrush(); + } else { + fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg)); + bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)); + } + + ui->tableTreeWidget->currentItem()->setForeground(ui->tableTreeWidget->currentColumn(), fg); + ui->tableTreeWidget->currentItem()->setBackground(ui->tableTreeWidget->currentColumn(), bg); +} + +void BluetoothHciSummaryDialog::on_actionMark_Unmark_Row_triggered() +{ + QBrush fg; + QBrush bg; + bool is_marked = TRUE; + + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i += 1) { + if (ui->tableTreeWidget->currentItem()->background(i) != QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) + is_marked = FALSE; + } + + if (is_marked) { + fg = QBrush(); + bg = QBrush(); + } else { + fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg)); + bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)); + } + + for (int i = 0; i < ui->tableTreeWidget->columnCount(); i += 1) { + ui->tableTreeWidget->currentItem()->setForeground(i, fg); + ui->tableTreeWidget->currentItem()->setBackground(i, bg); + } +} + +void BluetoothHciSummaryDialog::on_actionCopy_Cell_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + + copy = QString(ui->tableTreeWidget->currentItem()->text(ui->tableTreeWidget->currentColumn())); + + clipboard->setText(copy); +} + + +void BluetoothHciSummaryDialog::on_actionCopy_Rows_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + QList items; + QList::iterator i_item; + + items = ui->tableTreeWidget->selectedItems(); + + for (i_item = items.begin(); i_item != items.end(); ++i_item) { + copy += QString("%1 %2 %3 %4 %5 %6 %7 %8 %9\n") + .arg((*i_item)->text(column_number_name), -60) + .arg((*i_item)->text(column_number_ogf), -10) + .arg((*i_item)->text(column_number_ocf), -10) + .arg((*i_item)->text(column_number_opcode), -10) + .arg((*i_item)->text(column_number_event), -10) + .arg((*i_item)->text(column_number_subevent), -10) + .arg((*i_item)->text(column_number_status), -10) + .arg((*i_item)->text(column_number_reason), -10) + .arg((*i_item)->text(column_number_hardware_error), -15) + .arg((*i_item)->text(column_number_occurrence), -10); + } + + clipboard->setText(copy); +} + +void BluetoothHciSummaryDialog::tapReset(void *tapinfo_ptr) +{ + bluetooth_hci_summary_tapinfo_t *tapinfo = (bluetooth_hci_summary_tapinfo_t *) tapinfo_ptr; + BluetoothHciSummaryDialog *dialog = static_cast(tapinfo->ui); + + dialog->item_link_control_->takeChildren(); + dialog->item_link_control_->setText(column_number_occurrence, "0"); + + dialog->item_link_policy_->takeChildren(); + dialog->item_link_policy_->setText(column_number_occurrence, "0"); + + dialog->item_controller_and_baseband_->takeChildren(); + dialog->item_controller_and_baseband_->setText(column_number_occurrence, "0"); + + dialog->item_informational_->takeChildren(); + dialog->item_informational_->setText(column_number_occurrence, "0"); + + dialog->item_status_parameters_->takeChildren(); + dialog->item_status_parameters_->setText(column_number_occurrence, "0"); + + dialog->item_testing_->takeChildren(); + dialog->item_testing_->setText(column_number_occurrence, "0"); + + dialog->item_low_energy_->takeChildren(); + dialog->item_low_energy_->setText(column_number_occurrence, "0"); + + dialog->item_logo_testing_->takeChildren(); + dialog->item_logo_testing_->setText(column_number_occurrence, "0"); + + dialog->item_vendor_->takeChildren(); + dialog->item_vendor_->setText(column_number_occurrence, "0"); + + dialog->item_unknown_ogf_->takeChildren(); + dialog->item_unknown_ogf_->setText(column_number_occurrence, "0"); + + dialog->item_events_->takeChildren(); + dialog->item_events_->setText(column_number_occurrence, "0"); + + dialog->item_status_->takeChildren(); + dialog->item_status_->setText(column_number_occurrence, "0"); + + dialog->item_reason_->takeChildren(); + dialog->item_reason_->setText(column_number_occurrence, "0"); + + dialog->item_hardware_errors_->takeChildren(); + dialog->item_hardware_errors_->setText(column_number_occurrence, "0"); +} + +tap_packet_status BluetoothHciSummaryDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t) +{ + bluetooth_hci_summary_tapinfo_t *tapinfo = static_cast(tapinfo_ptr); + BluetoothHciSummaryDialog *dialog = static_cast(tapinfo->ui); + bluetooth_hci_summary_tap_t *tap_hci = static_cast(const_cast(data)); + QTreeWidgetItem *main_item = NULL; + QTreeWidgetItem *item = NULL; + QTreeWidgetItem *frame_item = NULL; + QTreeWidgetItem *meta_item = NULL; + bluetooth_item_data_t *item_data = NULL; + QString adapter; + QString name; + + if (dialog->file_closed_) + return TAP_PACKET_DONT_REDRAW; + + if (pinfo->rec->rec_type != REC_TYPE_PACKET) + return TAP_PACKET_DONT_REDRAW; + + name = tr("Unknown"); + + if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID) { + gchar *interface; + const char *interface_name; + + interface_name = epan_get_interface_name(pinfo->epan, pinfo->rec->rec_header.packet_header.interface_id); + interface = wmem_strdup_printf(pinfo->pool, "%u: %s", pinfo->rec->rec_header.packet_header.interface_id, interface_name); + + if (dialog->ui->interfaceComboBox->findText(interface) == -1) + dialog->ui->interfaceComboBox->addItem(interface); + + if (interface && dialog->ui->interfaceComboBox->currentIndex() > 0) { + if (dialog->ui->interfaceComboBox->currentText() != interface) + return TAP_PACKET_REDRAW; + } + } + + adapter = QString(tr("Adapter %1")).arg(tap_hci->adapter_id); + + if (dialog->ui->adapterComboBox->findText(adapter) == -1) { + dialog->ui->adapterComboBox->addItem(adapter); + } + + if (dialog->ui->adapterComboBox->currentIndex() > 0) { + if (dialog->ui->adapterComboBox->currentText() != adapter) + return TAP_PACKET_REDRAW; + } + + switch (tap_hci->type) { + case BLUETOOTH_HCI_SUMMARY_OPCODE: + case BLUETOOTH_HCI_SUMMARY_EVENT_OPCODE: + case BLUETOOTH_HCI_SUMMARY_VENDOR_OPCODE: + case BLUETOOTH_HCI_SUMMARY_VENDOR_EVENT_OPCODE: + switch (tap_hci->ogf) { + case HCI_OGF_LINK_CONTROL: + main_item = dialog->item_link_control_; + break; + case HCI_OGF_LINK_POLICY: + main_item = dialog->item_link_policy_; + break; + case HCI_OGF_HOST_CONTROLLER: + main_item = dialog->item_controller_and_baseband_; + break; + case HCI_OGF_INFORMATIONAL: + main_item = dialog->item_informational_; + break; + case HCI_OGF_STATUS: + main_item = dialog->item_status_parameters_; + break; + case HCI_OGF_TESTING: + main_item = dialog->item_testing_; + break; + case HCI_OGF_LOW_ENERGY: + main_item = dialog->item_low_energy_; + break; + case HCI_OGF_LOGO_TESTING: + main_item = dialog->item_logo_testing_; + break; + case HCI_OGF_VENDOR_SPECIFIC: + main_item = dialog->item_vendor_; + break; + default: + main_item = dialog->item_unknown_ogf_; + } + + for (int i_item = 0; i_item < main_item->childCount(); i_item +=1) { + if (main_item->child(i_item)->text(column_number_opcode) == + QString("0x%1").arg(tap_hci->ogf << 10 | tap_hci->ocf, 4, 16, QChar('0'))) { + item = main_item->child(i_item); + if (tap_hci->type == BLUETOOTH_HCI_SUMMARY_VENDOR_OPCODE && tap_hci->name) { + item->setText(column_number_name, tap_hci->name); + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() - 1)); + } + break; + } + } + + if (!item) { + item = new QTreeWidgetItem(); + if (tap_hci->name) + name = tap_hci->name; + + item->setText(column_number_name, name); + item->setText(column_number_ogf, QString("0x%1").arg(tap_hci->ogf, 2, 16, QChar('0'))); + item->setText(column_number_ocf, QString("0x%1").arg(tap_hci->ocf, 4, 16, QChar('0'))); + item->setText(column_number_opcode, + QString("0x%1").arg(tap_hci->ogf << 10 | tap_hci->ocf, 4, 16, QChar('0'))); + if (tap_hci->type == BLUETOOTH_HCI_SUMMARY_OPCODE) + item->setText(column_number_occurrence, "0"); + else + item->setText(column_number_occurrence, "1"); + + main_item->addChild(item); + item->setHidden(!name.contains(dialog->ui->resultsFilterLineEdit->text(), Qt::CaseInsensitive)); + main_item->sortChildren(column_number_opcode, Qt::AscendingOrder); + + main_item->setText(column_number_occurrence, QString::number(main_item->text(column_number_occurrence).toInt() + 1)); + } + + if (tap_hci->type != BLUETOOTH_HCI_SUMMARY_EVENT_OPCODE && tap_hci->type != BLUETOOTH_HCI_SUMMARY_VENDOR_EVENT_OPCODE) + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + + /* I believe bthci_cmd/bthci_evt already add frame item */ + if (tap_hci->type == BLUETOOTH_HCI_SUMMARY_VENDOR_OPCODE || + tap_hci->type == BLUETOOTH_HCI_SUMMARY_VENDOR_EVENT_OPCODE) + break; + + frame_item = new QTreeWidgetItem(); + frame_item->setText(column_number_name, QString(tr("Frame %1")).arg(pinfo->num)); + frame_item->setText(column_number_ogf, QString("0x%1").arg(tap_hci->ogf, 2, 16, QChar('0'))); + frame_item->setText(column_number_ocf, QString("0x%1").arg(tap_hci->ocf, 4, 16, QChar('0'))); + frame_item->setText(column_number_opcode, QString("0x%1") + .arg(tap_hci->ogf << 10 | tap_hci->ocf, 4, 16, QChar('0'))); + if (tap_hci->type == BLUETOOTH_HCI_SUMMARY_EVENT_OPCODE) + frame_item->setText(column_number_event, QString("0x%1").arg(tap_hci->event, 2, 16, QChar('0'))); + item->addChild(frame_item); + + item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_hci->interface_id; + item_data->adapter_id = tap_hci->adapter_id; + item_data->frame_number = pinfo->num; + frame_item->setData(0, Qt::UserRole, VariantPointer::asQVariant(item_data)); + + break; + case BLUETOOTH_HCI_SUMMARY_EVENT: + case BLUETOOTH_HCI_SUMMARY_VENDOR_EVENT: + main_item = dialog->item_events_; + + for (int i_item = 0; i_item < main_item->childCount(); i_item +=1) { + if (main_item->child(i_item)->text(column_number_event) == + QString("0x%1").arg(tap_hci->event, 2, 16, QChar('0'))) { + item = main_item->child(i_item); + if (tap_hci->type == BLUETOOTH_HCI_SUMMARY_VENDOR_EVENT && tap_hci->name) + item->setText(column_number_name, tap_hci->name); + break; + } + } + + if (!item) { + item = new QTreeWidgetItem(); + if (tap_hci->name) + name = tap_hci->name; + + item->setText(column_number_name, name); + item->setText(column_number_event, QString("0x%1").arg(tap_hci->event, 2, 16, QChar('0'))); + item->setText(column_number_occurrence, QString::number(0)); + + main_item->addChild(item); + item->setHidden(!name.contains(dialog->ui->resultsFilterLineEdit->text(), Qt::CaseInsensitive)); + main_item->sortChildren(column_number_event, Qt::AscendingOrder); + main_item->setText(column_number_occurrence, QString::number(main_item->text(column_number_occurrence).toInt() + 1)); + } + + /* I believe bthci_cmd/bthci_evt already add frame item */ + if (tap_hci->type == BLUETOOTH_HCI_SUMMARY_VENDOR_EVENT) + break; + + if (tap_hci->event == 0x3E) { /* LE Meta */ + int i_item; + for (i_item = 0; i_item < item->childCount(); i_item +=1) { + if (item->child(i_item)->text(column_number_name) != QString(tr("Unknown"))) + continue; + } + + if (i_item >= item->childCount()) { + frame_item = new QTreeWidgetItem(); + frame_item->setText(column_number_name, QString(tr("Unknown"))); + frame_item->setText(column_number_occurrence, QString::number(1)); + item->addChild(frame_item); + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + + item = frame_item; + } else { + item = item->child(i_item); + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + } + } else { + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + } + + frame_item = new QTreeWidgetItem(); + frame_item->setText(column_number_name, QString(tr("Frame %1")).arg(pinfo->num)); + frame_item->setText(column_number_event, QString("0x%1").arg(tap_hci->event, 2, 16, QChar('0'))); + item->addChild(frame_item); + + item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_hci->interface_id; + item_data->adapter_id = tap_hci->adapter_id; + item_data->frame_number = pinfo->num; + frame_item->setData(0, Qt::UserRole, VariantPointer::asQVariant(item_data)); + + break; + case BLUETOOTH_HCI_SUMMARY_SUBEVENT: + main_item = dialog->item_events_; + + meta_item = NULL; + + for (int i_item = 0; i_item < main_item->childCount(); i_item +=1) { + if (main_item->child(i_item)->text(column_number_event) != + QString("0x%1").arg(tap_hci->event, 2, 16, QChar('0'))) { + continue; + } + + meta_item = main_item->child(i_item); + break; + } + + if (meta_item == NULL) + break; + + item = NULL; + + for (int i_item = 0; i_item < meta_item->childCount(); i_item +=1) { + if (meta_item->child(i_item)->text(column_number_subevent) != + QString("0x%1").arg(tap_hci->subevent, 2, 16, QChar('0'))) { + continue; + } + + item = meta_item->child(i_item); + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + + break; + } + + if (item == NULL) { + item = new QTreeWidgetItem(); + item->setText(column_number_name, tap_hci->name); + item->setText(column_number_subevent, QString("0x%1").arg(tap_hci->subevent, 2, 16, QChar('0'))); + item->setText(column_number_occurrence, QString::number(1)); + + meta_item->addChild(item); + meta_item->setText(column_number_occurrence, QString::number(meta_item->text(column_number_occurrence).toInt() + 1)); + meta_item->sortChildren(column_number_subevent, Qt::AscendingOrder); + } + + frame_item = new QTreeWidgetItem(); + frame_item->setText(column_number_name, QString(tr("Frame %1")).arg(pinfo->num)); + frame_item->setText(column_number_event, QString("0x%1").arg(tap_hci->event, 2, 16, QChar('0'))); + frame_item->setText(column_number_subevent, QString("0x%1").arg(tap_hci->subevent, 2, 16, QChar('0'))); + + item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_hci->interface_id; + item_data->adapter_id = tap_hci->adapter_id; + item_data->frame_number = pinfo->num; + + frame_item->setData(0, Qt::UserRole, VariantPointer::asQVariant(item_data)); + + item->addChild(frame_item); + + /* Remove item that is known now */ + for (int i_item = 0; i_item < meta_item->childCount(); i_item +=1) { + if (meta_item->child(i_item)->text(column_number_name) != QString(tr("Unknown"))) + continue; + + item = meta_item->child(i_item); + for (int ii_item = 0; ii_item < item->childCount(); ii_item +=1) { + if (item->child(ii_item)->text(column_number_name) != QString(tr("Frame %1")).arg(pinfo->num)) + continue; + + delete item->child(ii_item); + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() - 1)); + if (item->childCount() == 0) { + delete item; + meta_item->setText(column_number_occurrence, QString::number(meta_item->text(column_number_occurrence).toInt() - 1)); + } + + break; + } + break; + } + + break; + case BLUETOOTH_HCI_SUMMARY_STATUS: + main_item = dialog->item_status_; + + for (int i_item = 0; i_item < main_item->childCount(); i_item +=1) { + if (main_item->child(i_item)->text(column_number_status) == + QString("0x%1").arg(tap_hci->status, 2, 16, QChar('0'))) { + item = main_item->child(i_item); + break; + } + } + + if (!item) { + if (tap_hci->name) + name = tap_hci->name; + + item = new QTreeWidgetItem(); + item->setText(column_number_name, name); + item->setText(column_number_status, QString("0x%1").arg(tap_hci->status, 2, 16, QChar('0'))); + + main_item->addChild(item); + item->setHidden(!name.contains(dialog->ui->resultsFilterLineEdit->text(), Qt::CaseInsensitive)); + main_item->sortChildren(column_number_event, Qt::AscendingOrder); + main_item->setText(column_number_occurrence, QString::number(main_item->text(column_number_occurrence).toInt() + 1)); + } + + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + + frame_item = new QTreeWidgetItem(); + frame_item->setText(column_number_name, QString(tr("Frame %1")).arg(pinfo->num)); + frame_item->setText(column_number_status, QString("0x%1").arg(tap_hci->status, 2, 16, QChar('0'))); + item->addChild(frame_item); + + item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_hci->interface_id; + item_data->adapter_id = tap_hci->adapter_id; + item_data->frame_number = pinfo->num; + frame_item->setData(0, Qt::UserRole, VariantPointer::asQVariant(item_data)); + + break; + case BLUETOOTH_HCI_SUMMARY_STATUS_PENDING: + main_item = dialog->item_status_; + + for (int i_item = 0; i_item < main_item->childCount(); i_item +=1) { + if (main_item->child(i_item)->text(column_number_status) == QString::number(tap_hci->status)) { + item = main_item->child(i_item); + break; + } + } + + if (!item) { + item = new QTreeWidgetItem(); + item->setText(column_number_name, tr("Pending")); + item->setText(column_number_status, QString::number(tap_hci->status)); + + main_item->addChild(item); + item->setHidden(!name.contains(dialog->ui->resultsFilterLineEdit->text(), Qt::CaseInsensitive)); + main_item->sortChildren(column_number_event, Qt::AscendingOrder); + main_item->setText(column_number_occurrence, QString::number(main_item->text(column_number_occurrence).toInt() + 1)); + } + + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + + frame_item = new QTreeWidgetItem(); + frame_item->setText(column_number_name, QString(tr("Frame %1")).arg(pinfo->num)); + frame_item->setText(column_number_status, QString::number(tap_hci->status)); + item->addChild(frame_item); + + item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_hci->interface_id; + item_data->adapter_id = tap_hci->adapter_id; + item_data->frame_number = pinfo->num; + frame_item->setData(0, Qt::UserRole, VariantPointer::asQVariant(item_data)); + + break; + case BLUETOOTH_HCI_SUMMARY_REASON: + main_item = dialog->item_reason_; + + for (int i_item = 0; i_item < main_item->childCount(); i_item +=1) { + if (main_item->child(i_item)->text(column_number_reason) == + QString("0x%1").arg(tap_hci->reason, 2, 16, QChar('0'))) { + item = main_item->child(i_item); + break; + } + } + + if (!item) { + if (tap_hci->name) + name = tap_hci->name; + + item = new QTreeWidgetItem(); + item->setText(column_number_name, name); + item->setText(column_number_reason, QString("0x%1").arg(tap_hci->reason, 2, 16, QChar('0'))); + + main_item->addChild(item); + item->setHidden(!name.contains(dialog->ui->resultsFilterLineEdit->text(), Qt::CaseInsensitive)); + main_item->sortChildren(column_number_event, Qt::AscendingOrder); + main_item->setText(column_number_occurrence, QString::number(main_item->text(column_number_occurrence).toInt() + 1)); + } + + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + + frame_item = new QTreeWidgetItem(); + frame_item->setText(column_number_name, QString(tr("Frame %1")).arg(pinfo->num)); + frame_item->setText(column_number_reason, QString("0x%1").arg(tap_hci->reason, 2, 16, QChar('0'))); + item->addChild(frame_item); + + item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_hci->interface_id; + item_data->adapter_id = tap_hci->adapter_id; + item_data->frame_number = pinfo->num; + frame_item->setData(0, Qt::UserRole, VariantPointer::asQVariant(item_data)); + + break; + case BLUETOOTH_HCI_SUMMARY_HARDWARE_ERROR: + main_item = dialog->item_hardware_errors_; + + for (int i_item = 0; i_item < main_item->childCount(); i_item +=1) { + if (main_item->child(i_item)->text(column_number_hardware_error) == + QString("0x%1").arg(tap_hci->hardware_error, 2, 16, QChar('0'))) { + item = main_item->child(i_item); + break; + } + } + + if (!item) { + item = new QTreeWidgetItem(); + const QString error = QString("0x%1").arg(tap_hci->hardware_error, 2, 16, QChar('0')); + item->setText(column_number_name, QString("Hardware error %1").arg(error)); + item->setText(column_number_hardware_error, error); + + main_item->addChild(item); + item->setHidden(!name.contains(dialog->ui->resultsFilterLineEdit->text(), Qt::CaseInsensitive)); + main_item->sortChildren(column_number_event, Qt::AscendingOrder); + main_item->setText(column_number_occurrence, QString::number(main_item->text(column_number_occurrence).toInt() + 1)); + } + + item->setText(column_number_occurrence, QString::number(item->text(column_number_occurrence).toInt() + 1)); + + frame_item = new QTreeWidgetItem(); + frame_item->setText(column_number_name, QString(tr("Frame %1")).arg(pinfo->num)); + frame_item->setText(column_number_hardware_error, QString("0x%1").arg(tap_hci->hardware_error, 2, 16, QChar('0'))); + item->addChild(frame_item); + + item_data = wmem_new(wmem_file_scope(), bluetooth_item_data_t); + item_data->interface_id = tap_hci->interface_id; + item_data->adapter_id = tap_hci->adapter_id; + item_data->frame_number = pinfo->num; + frame_item->setData(0, Qt::UserRole, VariantPointer::asQVariant(item_data)); + + break; + } + + for (int i = 0; i < dialog->ui->tableTreeWidget->columnCount(); i++) { + dialog->ui->tableTreeWidget->resizeColumnToContents(i); + } + + return TAP_PACKET_REDRAW; +} + +void BluetoothHciSummaryDialog::interfaceCurrentIndexChanged(int) +{ + cap_file_.retapPackets(); +} + +void BluetoothHciSummaryDialog::adapterCurrentIndexChanged(int) +{ + cap_file_.retapPackets(); +} + +void BluetoothHciSummaryDialog::on_tableTreeWidget_itemActivated(QTreeWidgetItem *item, int) +{ + if (file_closed_) + return; + + bluetooth_item_data_t *item_data = VariantPointer::asPtr(item->data(0, Qt::UserRole)); + + if (item_data) + emit goToPacket(item_data->frame_number); +} + + +void BluetoothHciSummaryDialog::recursiveCopyTreeItems(QTreeWidgetItem *item, QString ©, int ident_level) +{ + QTreeWidgetItem *child_item; + + if (!item->isExpanded()) return; + + for (int i_item = 0; i_item < item->childCount(); i_item += 1) { + child_item = item->child(i_item); + + copy.append(QString(" ").repeated(ident_level)); + copy += QString("%1 %2 %3 %4 %5 %6 %7 %8 %9\n") + .arg(child_item->text(column_number_name), -60 + 4 * ident_level) + .arg(child_item->text(column_number_ogf), -10) + .arg(child_item->text(column_number_ocf), -10) + .arg(child_item->text(column_number_opcode), -10) + .arg(child_item->text(column_number_event), -10) + .arg(child_item->text(column_number_subevent), -10) + .arg(child_item->text(column_number_status), -10) + .arg(child_item->text(column_number_reason), -10) + .arg(child_item->text(column_number_hardware_error), -15) + .arg(child_item->text(column_number_occurrence), -10); + + recursiveCopyTreeItems(child_item, copy, ident_level + 1); + } +} + +void BluetoothHciSummaryDialog::on_actionCopy_All_triggered() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString copy; + QTreeWidgetItem *item; + + item = ui->tableTreeWidget->headerItem(); + + copy += QString("%1 %2 %3 %4 %5 %6 %7 %8 %9 %10\n") + .arg(item->text(column_number_name), -60) + .arg(item->text(column_number_ogf), -10) + .arg(item->text(column_number_ocf), -10) + .arg(item->text(column_number_opcode), -10) + .arg(item->text(column_number_event), -10) + .arg(item->text(column_number_subevent), -10) + .arg(item->text(column_number_status), -10) + .arg(item->text(column_number_reason), -10) + .arg(item->text(column_number_hardware_error), -15) + .arg(item->text(column_number_occurrence), -10); + + for (int i_item = 0; i_item < ui->tableTreeWidget->topLevelItemCount(); ++i_item) { + item = ui->tableTreeWidget->topLevelItem(i_item); + + copy += QString("%1 %2 %3 %4 %5 %6 %7 %8 %9 %10\n") + .arg(item->text(column_number_name), -60) + .arg(item->text(column_number_ogf), -10) + .arg(item->text(column_number_ocf), -10) + .arg(item->text(column_number_opcode), -10) + .arg(item->text(column_number_event), -10) + .arg(item->text(column_number_subevent), -10) + .arg(item->text(column_number_status), -10) + .arg(item->text(column_number_reason), -10) + .arg(item->text(column_number_hardware_error), -15) + .arg(item->text(column_number_occurrence), -10); + + recursiveCopyTreeItems(ui->tableTreeWidget->topLevelItem(i_item), copy, 1); + } + + clipboard->setText(copy); +} + +void BluetoothHciSummaryDialog::on_actionSave_as_image_triggered() +{ + QPixmap image; + + QString fileName = WiresharkFileDialog::getSaveFileName(this, + tr("Save Table Image"), + "bluetooth_hci_summary.png", + tr("PNG Image (*.png)")); + + if (fileName.isEmpty()) return; + + image = ui->tableTreeWidget->grab(); + image.save(fileName, "PNG"); +} + +void BluetoothHciSummaryDialog::on_buttonBox_clicked(QAbstractButton *) +{ +/* if (button == foo_button_) */ +} + +void BluetoothHciSummaryDialog::displayFilterLineEditAccepted() +{ + GString *error_string; + + remove_tap_listener(&tapinfo_); + error_string = register_tap_listener("bluetooth.hci_summary", &tapinfo_, + ui->displayFilterLineEdit->text().toUtf8().constData(), + 0, + bluetooth_hci_summary_tap_reset, + bluetooth_hci_summary_tap_packet, + NULL, + NULL + ); + + if (error_string != NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "%s", error_string->str); + g_string_free(error_string, TRUE); + } + + cap_file_.retapPackets(); +} + +void BluetoothHciSummaryDialog::resultsFilterLineEditChanged(const QString &text) +{ + for (int i_item = 0; i_item < ui->tableTreeWidget->topLevelItemCount(); ++i_item) { + QTreeWidgetItem *item = ui->tableTreeWidget->topLevelItem(i_item); + + for (int i_child = 0; i_child < item->childCount(); i_child += 1) { + QTreeWidgetItem *child_item = item->child(i_child); + QString name = child_item->text(column_number_name); + child_item->setHidden(!name.contains(text, Qt::CaseInsensitive)); + } + } +} diff --git a/ui/qt/bluetooth_hci_summary_dialog.h b/ui/qt/bluetooth_hci_summary_dialog.h new file mode 100644 index 00000000..8bba405e --- /dev/null +++ b/ui/qt/bluetooth_hci_summary_dialog.h @@ -0,0 +1,105 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BLUETOOTH_HCI_SUMMARY_DIALOG_H +#define BLUETOOTH_HCI_SUMMARY_DIALOG_H + +#include "config.h" + +#include + +#include "wireshark_dialog.h" +#include "cfile.h" +#include "packet_list.h" + +#include "epan/tap.h" + +#include + +class QAbstractButton; +class QPushButton; +class QTreeWidgetItem; + +typedef struct _bluetooth_hci_summary_tapinfo_t { + tap_reset_cb tap_reset; + tap_packet_cb tap_packet; + void *ui; +} bluetooth_hci_summary_tapinfo_t; + +namespace Ui { +class BluetoothHciSummaryDialog; +} + +class BluetoothHciSummaryDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit BluetoothHciSummaryDialog(QWidget &parent, CaptureFile &cf); + ~BluetoothHciSummaryDialog(); + +public slots: + +signals: + void updateFilter(QString filter, bool force = false); + void captureFileChanged(capture_file *cf); + void goToPacket(int packet_num); + +protected: + void keyPressEvent(QKeyEvent *event); + void captureFileClosing(); + void captureFileClosed(); + +protected slots: + void changeEvent(QEvent* event); + +private: + Ui::BluetoothHciSummaryDialog *ui; + + bluetooth_hci_summary_tapinfo_t tapinfo_; + QMenu context_menu_; + + QTreeWidgetItem *item_link_control_; + QTreeWidgetItem *item_link_policy_; + QTreeWidgetItem *item_controller_and_baseband_; + QTreeWidgetItem *item_informational_; + QTreeWidgetItem *item_status_parameters_; + QTreeWidgetItem *item_testing_; + QTreeWidgetItem *item_low_energy_; + QTreeWidgetItem *item_logo_testing_; + QTreeWidgetItem *item_vendor_; + QTreeWidgetItem *item_unknown_ogf_; + QTreeWidgetItem *item_events_; + QTreeWidgetItem *item_status_; + QTreeWidgetItem *item_reason_; + QTreeWidgetItem *item_hardware_errors_; + + static void tapReset(void *tapinfo_ptr); + static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t flags); + +private slots: + void recursiveCopyTreeItems(QTreeWidgetItem *item, QString ©, int ident_level); + void on_tableTreeWidget_itemActivated(QTreeWidgetItem *item, int); + void on_buttonBox_clicked(QAbstractButton *button); + void on_actionMark_Unmark_Cell_triggered(); + void on_actionMark_Unmark_Row_triggered(); + void on_actionCopy_Cell_triggered(); + void on_actionCopy_Rows_triggered(); + void on_actionCopy_All_triggered(); + void on_actionSave_as_image_triggered(); + void tableContextMenu(const QPoint &pos); + void tableItemExpanded(QTreeWidgetItem *item); + void tableItemCollapsed(QTreeWidgetItem *item); + void interfaceCurrentIndexChanged(int index); + void adapterCurrentIndexChanged(int index); + void displayFilterLineEditAccepted(); + void resultsFilterLineEditChanged(const QString &text); +}; + +#endif // BLUETOOTH_HCI_SUMMARY_DIALOG_H diff --git a/ui/qt/bluetooth_hci_summary_dialog.ui b/ui/qt/bluetooth_hci_summary_dialog.ui new file mode 100644 index 00000000..c4775008 --- /dev/null +++ b/ui/qt/bluetooth_hci_summary_dialog.ui @@ -0,0 +1,755 @@ + + + BluetoothHciSummaryDialog + + + + 0 + 0 + 880 + 477 + + + + + 0 + 0 + + + + Bluetooth HCI Summary + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::ExtendedSelection + + + Qt::ElideMiddle + + + 30 + + + true + + + false + + + true + + + false + + + false + + + false + + + false + + + false + + + 100 + + + 100 + + + false + + + false + + + true + + + + Name + + + + + OGF + + + + + OCF + + + + + Opcode + + + + + Event + + + + + Subevent + + + + + Status + + + + + Reason + + + + + Hardware Error + + + + + Occurrence + + + + + Link Control Commands + + + 0x01 + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Link Policy Commands + + + 0x02 + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Controller & Baseband Commands + + + 0x03 + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Informational Parameters + + + 0x04 + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Status Parameters + + + 0x05 + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Testing Commands + + + 0x06 + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + LE Controller Commands + + + 0x08 + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Bluetooth Logo Testing Commands + + + 0x3E + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Vendor-Specific Commands + + + 0x3F + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Unknown OGF + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Events + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Status + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Reason + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + Hardware Errors + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + 0 + + + + + Results filter: + + + + + + + + + + + + 6 + + + QLayout::SetDefaultConstraint + + + 0 + + + + + Display filter: + + + + + + + + + + + 0 + 0 + + + + + 350 + 0 + + + + + All Interfaces + + + + + + + + + 320 + 0 + + + + + All Adapters + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + Copy Cell + + + + + Copy Rows + + + + + Copy All + + + + + Save as image + + + + + Mark/Unmark Row + + + Mark/Unmark Row + + + Ctrl+M + + + + + Mark/Unmark Cell + + + + + + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + + + buttonBox + accepted() + BluetoothHciSummaryDialog + accept() + + + 374 + 407 + + + 374 + 214 + + + + + buttonBox + rejected() + BluetoothHciSummaryDialog + reject() + + + 374 + 407 + + + 374 + 214 + + + + +
diff --git a/ui/qt/byte_view_tab.cpp b/ui/qt/byte_view_tab.cpp new file mode 100644 index 00000000..cf23c24c --- /dev/null +++ b/ui/qt/byte_view_tab.cpp @@ -0,0 +1,373 @@ +/* byte_view_tab.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "byte_view_tab.h" + +#include +#include +#include +#include + +#include "cfile.h" +#include "epan/epan_dissect.h" +#include "epan/tvbuff-int.h" + +#include + +#include +#include + +#define tvb_data_property "tvb_data_property" + +// To do: +// - We might want to add a callback to free_data_sources in so that we +// don't have to blindly call clear(). + +ByteViewTab::ByteViewTab(QWidget *parent, epan_dissect_t *edt_fixed) : + QTabWidget(parent), + cap_file_(0), + is_fixed_packet_(edt_fixed != NULL), + edt_(edt_fixed), + disable_hover_(false) +{ + setAccessibleName(tr("Packet bytes")); + setTabPosition(QTabWidget::South); + setDocumentMode(true); + + // Shrink down to a small but nonzero size in the main splitter. + int one_em = fontMetrics().height(); + setMinimumSize(one_em, one_em); + + if (!edt_fixed) { + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(connectToMainWindow())); + } +} + +// Connects the byte view with the main window, acting on changes to the packet +// list selection. It MUST NOT be used with the packet dialog as that is +// independent of the selection in the packet list. +void ByteViewTab::connectToMainWindow() +{ + connect(this, SIGNAL(fieldSelected(FieldInformation *)), + mainApp->mainWindow(), SIGNAL(fieldSelected(FieldInformation *))); + connect(this, SIGNAL(fieldHighlight(FieldInformation *)), + mainApp->mainWindow(), SIGNAL(fieldHighlight(FieldInformation *))); + + /* Connect change of packet selection */ + connect(mainApp->mainWindow(), SIGNAL(framesSelected(QList)), this, SLOT(selectedFrameChanged(QList))); + connect(mainApp->mainWindow(), SIGNAL(setCaptureFile(capture_file*)), this, SLOT(setCaptureFile(capture_file*))); + connect(mainApp->mainWindow(), SIGNAL(fieldSelected(FieldInformation *)), this, SLOT(selectedFieldChanged(FieldInformation *))); + + connect(mainApp->mainWindow(), SIGNAL(captureActive(int)), this, SLOT(captureActive(int))); +} + +void ByteViewTab::captureActive(int cap) +{ + if (cap == 0) + { + QList allBVTs = findChildren(); + if (allBVTs.count() > 0) + { + ByteViewText * bvt = allBVTs.at(0); + tvbuff_t * stored = VariantPointer::asPtr(bvt->property(tvb_data_property)); + + if (! stored) + selectedFrameChanged(QList()); + } + } +} + +void ByteViewTab::addTab(const char *name, tvbuff_t *tvb) { + if (count() == 1) { // Remove empty placeholder. + ByteViewText *cur_text = qobject_cast(currentWidget()); + if (cur_text && cur_text->isEmpty()) delete currentWidget(); + } + + packet_char_enc encoding = PACKET_CHAR_ENC_CHAR_ASCII; + if (cap_file_ && cap_file_->current_frame) + encoding = (packet_char_enc)cap_file_->current_frame->encoding; + + QByteArray data; + if (tvb) { + int data_len = (int) tvb_captured_length(tvb); + if (data_len > 0) { + // Note: this does not copy the data and will be invalidated + // when the tvbuff's real data becomes invalid (which is not + // necessarily when the tvb itself becomes invalid.) + data = QByteArray::fromRawData((const char *) tvb_get_ptr(tvb, 0, data_len), data_len); + } + } + + ByteViewText * byte_view_text = new ByteViewText(data, encoding, this); + byte_view_text->setAccessibleName(name); + byte_view_text->setMonospaceFont(mainApp->monospaceFont(true)); + + if (tvb) + { + // There are some secondary data source tvbuffs whose datais not freed + // when the epan_dissect_t is freed, but at some other point expected + // to outlive the packet, generally when the capture file is closed. + // If this is a PacketDialog, it can break that assumption. + // To get around this, we deep copy their data when the file is closed. + // + // XXX: We could add a function to the tvbuff API and only do this if + // there is no free_cb (a free_cb implies the data is freed at the + // same time as the tvb, i.e. when leaving the packet.) + if (is_fixed_packet_ && count() > 0) { + connect(this, &ByteViewTab::detachData, byte_view_text, &ByteViewText::detachData); + } + // See above - this tvb is (expected to be) scoped to the packet, but + // the real data is not necessarily so. If this is a PacketDialog + // and such a secondary data source, then we MUST NOT use any tvb + // function that accesses the real data after the capture file closes. + // That includes via the ds_tvb item of a field_info in the tree. + // proto_find_field_from_offset() is OK. See #14363. + // + // XXX: It sounds appealing to clone the secondary data source tvbs + // and set them to be freed when the byte_view_text is freed, perhaps + // even doing so only when the capture file is closing. However, while + // relatively simple for the few number of secondary data sources, it + // would be a pain to change the pointers for every field_info. + byte_view_text->setProperty(tvb_data_property, VariantPointer::asQVariant(tvb)); + + connect(mainApp, SIGNAL(zoomMonospaceFont(QFont)), byte_view_text, SLOT(setMonospaceFont(QFont))); + + connect(byte_view_text, SIGNAL(byteHovered(int)), this, SLOT(byteViewTextHovered(int))); + connect(byte_view_text, SIGNAL(byteSelected(int)), this, SLOT(byteViewTextMarked(int))); + connect(byte_view_text, SIGNAL(byteViewSettingsChanged()), this, SIGNAL(byteViewSettingsChanged())); + connect(this, SIGNAL(byteViewSettingsChanged()), byte_view_text, SLOT(updateByteViewSettings())); + connect(this, SIGNAL(byteViewUnmarkField()), byte_view_text, SLOT(unmarkField())); + } + + int idx = QTabWidget::addTab(byte_view_text, name); + byte_view_text->setProperty("tab_index", QVariant::fromValue(idx)); + + QTabWidget::setTabToolTip(idx, name); +} + +void ByteViewTab::byteViewTextHovered(int idx) +{ + if (idx >= 0 && edt_) + { + tvbuff_t * tvb = VariantPointer::asPtr(sender()->property(tvb_data_property)); + proto_tree * tree = edt_->tree; + + if (tvb && tree) + { + field_info * fi = proto_find_field_from_offset(tree, idx, tvb); + if (fi) + { + FieldInformation finfo(fi, this); + highlightedFieldChanged(&finfo); + emit fieldHighlight(&finfo); + return; + } + } + } + + emit fieldHighlight((FieldInformation *)0); +} + +void ByteViewTab::byteViewTextMarked(int idx) +{ + if (idx >= 0 && edt_) + { + tvbuff_t * tvb = VariantPointer::asPtr(sender()->property(tvb_data_property)); + proto_tree * tree = edt_->tree; + + if (tvb && tree) + { + field_info * fi = proto_find_field_from_offset(tree, idx, tvb); + if (fi) + { + FieldInformation finfo(fi, this); + emit fieldSelected(&finfo); + return; + } + } + } + + emit fieldSelected((FieldInformation *)0); +} + +ByteViewText * ByteViewTab::findByteViewTextForTvb(tvbuff_t * search_tvb, int * idx) +{ + + ByteViewText * item = 0; + if (! search_tvb) + return item; + + bool found = false; + + QList allBVTs = findChildren(); + for (int i = 0; i < allBVTs.size() && ! found; ++i) + { + ByteViewText * bvt = allBVTs.at(i); + tvbuff_t * stored = VariantPointer::asPtr(bvt->property(tvb_data_property)); + if (stored == search_tvb) + { + found = true; + int wdgIdx = bvt->property("tab_index").toInt(); + if (idx) + { + *idx = wdgIdx; + } + item = (ByteViewText *)widget(wdgIdx); + } + } + + return item; +} + +void ByteViewTab::tabInserted(int tab_index) { + setTabsVisible(); + QTabWidget::tabInserted(tab_index); +} + +void ByteViewTab::tabRemoved(int tab_index) { + setTabsVisible(); + QTabWidget::tabRemoved(tab_index); +} + +void ByteViewTab::setTabsVisible() { + if (count() > 1) + tabBar()->show(); + else + tabBar()->hide(); +} + +void ByteViewTab::selectedFrameChanged(QList frames) +{ + clear(); + qDeleteAll(findChildren()); + + if (!is_fixed_packet_) { + /* If this is not a fixed packet (not the packet dialog), it must be the + * byte view associated with the packet list. */ + if (cap_file_ && cap_file_->edt) { + /* Assumes that this function is called as a result of selecting a + * packet in the packet list (PacketList::selectionChanged). That + * invokes "cf_select_packet" which will update "cap_file_->edt". */ + edt_ = cap_file_->edt; + } else { + /* capture file is closing or packet is deselected. */ + edt_ = NULL; + } + } + + /* only show the bytes for single selections */ + if (frames.count() == 1) + { + if (! cap_file_ || ! cap_file_->edt) + return; + + /* This code relies on a dissection, which had happened somewhere else. It also does not + * really check, if the dissection happened for the correct frame. In the future we might + * rewrite this for directly calling the dissection engine here. */ + GSList *src_le; + for (src_le = edt_->pi.data_src; src_le != NULL; src_le = src_le->next) { + struct data_source *source; + char* source_name; + source = (struct data_source *)src_le->data; + source_name = get_data_source_name(source); + addTab(source_name, get_data_source_tvb(source)); + wmem_free(NULL, source_name); + } + } + else + addTab("PlaceHolder", 0); + + setCurrentIndex(0); +} + +void ByteViewTab::selectedFieldChanged(FieldInformation *selected) +{ + // We need to handle both selection and deselection. + ByteViewText * byte_view_text = qobject_cast(currentWidget()); + int f_start = -1, f_length = -1; + int p_start = -1, p_length = -1; + int fa_start = -1, fa_length = -1; + + if (selected) { + if (selected->parent() == this) { + // We only want inbound signals. + return; + } + const field_info *fi = selected->fieldInfo(); + + int idx = 0; + if (fi) + byte_view_text = findByteViewTextForTvb(fi->ds_tvb, &idx); + + if (cap_file_->search_in_progress && (cap_file_->hex || (cap_file_->string && cap_file_->packet_data))) { + // In the hex view, only highlight the target bytes or string. The entire + // field can then be displayed by clicking on any of the bytes in the field. + f_start = cap_file_->search_pos - cap_file_->search_len + 1; + f_length = (int) cap_file_->search_len; + } else { + f_start = selected->position().start; + f_length = selected->position().length; + } + + setCurrentIndex(idx); + + FieldInformation *parentField = selected->parentField(); + + p_start = parentField->position().start; + p_length = parentField->position().length; + fa_start = selected->appendix().start; + fa_length = selected->appendix().length; + + delete parentField; + } + + if (byte_view_text) + { + byte_view_text->markField(f_start, f_length); + byte_view_text->markProtocol(p_start, p_length); + byte_view_text->markAppendix(fa_start, fa_length); + } else { + emit byteViewUnmarkField(); + } +} +void ByteViewTab::highlightedFieldChanged(FieldInformation *highlighted) +{ + ByteViewText * byte_view_text = qobject_cast(currentWidget()); + if (!highlighted || !byte_view_text) { + return; + } + + int f_start = -1, f_length = -1; + + if (cap_file_->search_in_progress && (cap_file_->hex || (cap_file_->string && cap_file_->packet_data))) { + // In the hex view, only highlight the target bytes or string. The entire + // field can then be displayed by clicking on any of the bytes in the field. + f_start = cap_file_->search_pos - cap_file_->search_len + 1; + f_length = (int) cap_file_->search_len; + } else { + f_start = highlighted->position().start; + f_length = highlighted->position().length; + } + + byte_view_text->markField(f_start, f_length, false); + byte_view_text->markProtocol(-1, -1); + byte_view_text->markAppendix(-1, -1); +} + +void ByteViewTab::setCaptureFile(capture_file *cf) +{ + selectedFrameChanged(QList()); + + cap_file_ = cf; +} + +void ByteViewTab::captureFileClosing() +{ + emit detachData(); +} diff --git a/ui/qt/byte_view_tab.h b/ui/qt/byte_view_tab.h new file mode 100644 index 00000000..dbf96c13 --- /dev/null +++ b/ui/qt/byte_view_tab.h @@ -0,0 +1,78 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BYTE_VIEW_TAB_H +#define BYTE_VIEW_TAB_H + +#include + +#include +#include +#include + +#include + +#include "cfile.h" + +#include + + +#include + +class ByteViewTab : public QTabWidget +{ + Q_OBJECT + +public: + explicit ByteViewTab(QWidget *parent = 0, epan_dissect_t *edt_fixed = 0); + +public slots: + /* Set the capture file */ + void setCaptureFile(capture_file *cf); + /* Creates the tabs and data, depends on an dissection which has already run */ + void selectedFrameChanged(QList); + /* Selects or marks a field */ + void selectedFieldChanged(FieldInformation *); + /* Highlights field */ + void highlightedFieldChanged(FieldInformation *); + void captureFileClosing(void); + +signals: + void fieldSelected(FieldInformation *); + void fieldHighlight(FieldInformation *); + void byteViewSettingsChanged(void); + void byteViewUnmarkField(void); + void detachData(void); + +private: + capture_file *cap_file_; + bool is_fixed_packet_; /* true if this byte view is related to a single + packet in the packet dialog and false if the + packet dissection context can change. */ + epan_dissect_t *edt_; /* Packet dissection result for the currently selected packet. */ + bool disable_hover_; + + void setTabsVisible(); + ByteViewText * findByteViewTextForTvb(tvbuff_t * search, int * idx = 0); + void addTab(const char *name = "", tvbuff_t *tvb = NULL); + +protected: + void tabInserted(int); + void tabRemoved(int); + +private slots: + void byteViewTextHovered(int); + void byteViewTextMarked(int); + + void connectToMainWindow(); + + void captureActive(int); +}; + +#endif // BYTE_VIEW_TAB_H diff --git a/ui/qt/capture_event.h b/ui/qt/capture_event.h new file mode 100644 index 00000000..b4db282b --- /dev/null +++ b/ui/qt/capture_event.h @@ -0,0 +1,68 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_EVENT_H +#define CAPTURE_EVENT_H + +#include + +typedef struct _capture_session capture_session; + +struct _packet_info; + +class CaptureEvent +{ +public: + enum Context { +#ifdef HAVE_LIBPCAP + Capture = 0x0001, + Update = 0x0100 | Capture, + Fixed = 0x0200 | Capture, +#endif + File = 0x0002, + Reload = 0x0100 | File, + Rescan = 0x0200 | File, + Save = 0x0400 | File, + Retap = 0x0800 | File, + Merge = 0x1000 | File + }; + + enum EventType { + Opened = 0x0001, + Started = 0x0002, + Finished = 0x0004, + Closing = 0x0008, + Closed = 0x0010, + Failed = 0x0020, + Stopped = 0x0040, + Flushed = 0x0080, + Prepared = 0x0100, + Continued = 0x0200, + Stopping = 0x0400 + }; + + CaptureEvent(Context ctx, EventType evt); + CaptureEvent(Context ctx, EventType evt, QString file); + CaptureEvent(Context ctx, EventType evt, capture_session * session); + + CaptureEvent(const CaptureEvent &ce); + + Context captureContext() const; + EventType eventType() const; + QString filePath() const; + capture_session * capSession() const; + +private: + Context _ctx; + EventType _evt; + QString _filePath; + capture_session * _session; +}; + +#endif // CAPTURE_EVENT_H diff --git a/ui/qt/capture_file.cpp b/ui/qt/capture_file.cpp new file mode 100644 index 00000000..0fd088b9 --- /dev/null +++ b/ui/qt/capture_file.cpp @@ -0,0 +1,386 @@ +/* capture_file.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "capture_file.h" + +/* + * @file Capture file class + * + * Wraps the capture_file struct, cfile global, and callbacks. + */ + +#include "globals.h" +capture_file cfile; + +#include "file.h" + +#include "epan/epan_dissect.h" + +#include "ui/capture.h" + +#include +#include +#include + +CaptureEvent::CaptureEvent(Context ctx, EventType evt) : + _ctx(ctx), + _evt(evt), + _session(Q_NULLPTR) +{ +} + +CaptureEvent::CaptureEvent(Context ctx, EventType evt, QString file) : + _ctx(ctx), + _evt(evt), + _filePath(file), + _session(Q_NULLPTR) +{ +} + +CaptureEvent::CaptureEvent(Context ctx, EventType evt, capture_session * session) : + _ctx(ctx), + _evt(evt), + _session(session) +{ +} + +CaptureEvent::CaptureEvent(const CaptureEvent &ce) : + _ctx(ce._ctx), + _evt(ce._evt), + _filePath(ce._filePath), + _session(ce._session) +{ +} + +CaptureEvent::Context CaptureEvent::captureContext() const +{ return _ctx; } + +CaptureEvent::EventType CaptureEvent::eventType() const +{ return _evt; } + +QString CaptureEvent::filePath() const +{ return _filePath; } + +capture_session * CaptureEvent::capSession() const +{ return _session; } + +// To do: +// - Add getters and (if needed) setters: +// - Full filename +// - Capture state (stopped, prepared, running). +// - Call common_create_progress_dlg. This would let us manage the stop +// flag here as well as emit progress signals. + +QString CaptureFile::no_capture_file_ = QObject::tr("[no capture file]"); + +CaptureFile::CaptureFile(QObject *parent, capture_file *cap_file) : + QObject(parent), + cap_file_(cap_file), + file_state_(QString()) +{ +#ifdef HAVE_LIBPCAP + capture_callback_add(captureCallback, (gpointer) this); +#endif + cf_callback_add(captureFileCallback, (gpointer) this); +} + +CaptureFile::~CaptureFile() +{ + cf_callback_remove(captureFileCallback, this); +} + +bool CaptureFile::isValid() const +{ + if (cap_file_ && cap_file_->state != FILE_CLOSED && cap_file_->state != FILE_READ_PENDING) { // XXX FILE_READ_IN_PROGRESS as well? + return true; + } + return false; +} + +const QString CaptureFile::filePath() +{ + QString path; + + if (isValid()) { + // + // Sadly, some UN*Xes don't necessarily use UTF-8 + // for their file names, so we have to map the + // file path to UTF-8. If that fails, we're somewhat + // stuck. + // + char *utf8_filename = g_filename_to_utf8(cap_file_->filename, + -1, + NULL, + NULL, + NULL); + if (utf8_filename) { + path = QString::fromUtf8(utf8_filename); + g_free(utf8_filename); + } else { + // So what the heck else can we do here? + path = QString(); + } + } else { + path = QString(); + } + return path; +} + +const QString CaptureFile::fileName() +{ + QString path, name; + + path = filePath(); + if (!path.isEmpty()) { + QFileInfo cfi(path); + name = cfi.fileName(); + } else { + name = QString(); + } + + return name; +} + +const QString CaptureFile::fileBaseName() +{ + QString baseName; + + if (isValid()) { + char *basename = cf_get_basename(cap_file_); + baseName = basename; + g_free(basename); + } else { + baseName = QString(); + } + return baseName; +} + +const QString CaptureFile::fileDisplayName() +{ + QString displayName; + + if (isValid()) { + char *display_name = cf_get_display_name(cap_file_); + displayName = display_name; + g_free(display_name); + } else { + displayName = QString(); + } + return displayName; +} + +const QString CaptureFile::fileTitle() +{ + QString title; + + if (isValid()) { + title = fileDisplayName() + file_state_; + } else { + title = no_capture_file_; + } + return title; +} + +struct _packet_info *CaptureFile::packetInfo() +{ + if (capFile() && capFile()->edt) { + return &(capFile()->edt->pi); + } + return NULL; +} + +int CaptureFile::timestampPrecision() +{ + if (capFile() && capFile()->provider.wth) { + return wtap_file_tsprec(capFile()->provider.wth); + } + return WTAP_TSPREC_UNKNOWN; +} + +void CaptureFile::retapPackets() +{ + if (cap_file_) { + cf_retap_packets(cap_file_); + } +} + +void CaptureFile::delayedRetapPackets() +{ + QTimer::singleShot(0, this, SLOT(retapPackets())); +} + +void CaptureFile::reload() +{ + if (cap_file_ && cap_file_->state == FILE_READ_DONE) { + cf_reload(cap_file_); + } +} + +void CaptureFile::stopLoading() +{ + setCaptureStopFlag(true); +} + +QString CaptureFile::displayFilter() const +{ + if (isValid()) + return QString(cap_file_->dfilter); + return QString(); +} + +capture_file *CaptureFile::globalCapFile() +{ + return &cfile; +} + +gpointer CaptureFile::window() +{ + if (cap_file_) return cap_file_->window; + return NULL; +} + +void CaptureFile::setCaptureStopFlag(bool stop_flag) +{ + if (cap_file_) cap_file_->stop_flag = stop_flag; +} + +void CaptureFile::captureFileCallback(gint event, gpointer data, gpointer user_data) +{ + CaptureFile *capture_file = static_cast(user_data); + if (!capture_file) return; + + capture_file->captureFileEvent(event, data); +} + +#ifdef HAVE_LIBPCAP +void CaptureFile::captureCallback(gint event, capture_session *cap_session, gpointer user_data) +{ + CaptureFile *capture_file = static_cast(user_data); + if (!capture_file) return; + + capture_file->captureSessionEvent(event, cap_session); +} +#endif + +void CaptureFile::captureFileEvent(int event, gpointer data) +{ + switch(event) { + case(cf_cb_file_opened): + cap_file_ = (capture_file *) data; + emit captureEvent(CaptureEvent(CaptureEvent::File, CaptureEvent::Opened)); + break; + case(cf_cb_file_closing): + file_state_ = tr(" [closing]"); + emit captureEvent(CaptureEvent(CaptureEvent::File, CaptureEvent::Closing)); + break; + case(cf_cb_file_closed): + file_state_ = tr(" [closed]"); + emit captureEvent(CaptureEvent(CaptureEvent::File, CaptureEvent::Closed)); + cap_file_ = NULL; + file_state_ = QString(); + break; + case(cf_cb_file_read_started): + emit captureEvent(CaptureEvent(CaptureEvent::File, CaptureEvent::Started)); + break; + case(cf_cb_file_read_finished): + emit captureEvent(CaptureEvent(CaptureEvent::File, CaptureEvent::Finished)); + break; + case(cf_cb_file_reload_started): + emit captureEvent(CaptureEvent(CaptureEvent::Reload, CaptureEvent::Started)); + break; + case(cf_cb_file_reload_finished): + emit captureEvent(CaptureEvent(CaptureEvent::Reload, CaptureEvent::Finished)); + break; + case(cf_cb_file_rescan_started): + emit captureEvent(CaptureEvent(CaptureEvent::Rescan, CaptureEvent::Started)); + break; + case(cf_cb_file_rescan_finished): + emit captureEvent(CaptureEvent(CaptureEvent::Rescan, CaptureEvent::Finished)); + break; + case(cf_cb_file_retap_started): + emit captureEvent(CaptureEvent(CaptureEvent::Retap, CaptureEvent::Started)); + break; + case(cf_cb_file_retap_finished): + /* Flush any pending tapped packet before emitting captureFileRetapFinished() */ + emit captureEvent(CaptureEvent(CaptureEvent::Retap, CaptureEvent::Finished)); + emit captureEvent(CaptureEvent(CaptureEvent::Retap, CaptureEvent::Flushed)); + break; + case(cf_cb_file_merge_started): + emit captureEvent(CaptureEvent(CaptureEvent::Merge, CaptureEvent::Started)); + break; + case(cf_cb_file_merge_finished): + emit captureEvent(CaptureEvent(CaptureEvent::Merge, CaptureEvent::Finished)); + break; + + case(cf_cb_file_fast_save_finished): + // gtk/main.c calls main_cf_cb_file_rescan_finished. Should we do + // the equivalent? + break; + + case(cf_cb_file_save_started): + { + emit captureEvent(CaptureEvent(CaptureEvent::Save, CaptureEvent::Started, QString((const char *)data))); + break; + } + case(cf_cb_file_save_finished): + emit captureEvent(CaptureEvent(CaptureEvent::Save, CaptureEvent::Finished)); + break; + case(cf_cb_file_save_failed): + emit captureEvent(CaptureEvent(CaptureEvent::Save, CaptureEvent::Failed)); + break; + case(cf_cb_file_save_stopped): + emit captureEvent(CaptureEvent(CaptureEvent::Save, CaptureEvent::Stopped)); + break; + + default: + qWarning() << "CaptureFile::captureFileCallback: event " << event << " unknown"; + Q_ASSERT(false); + break; + } +} + +#ifdef HAVE_LIBPCAP +void CaptureFile::captureSessionEvent(int event, capture_session *cap_session) +{ + switch(event) { + case(capture_cb_capture_prepared): + emit captureEvent(CaptureEvent(CaptureEvent::Capture, CaptureEvent::Prepared, cap_session)); + cap_file_ = cap_session->cf; + break; + case(capture_cb_capture_update_started): + emit captureEvent(CaptureEvent(CaptureEvent::Update, CaptureEvent::Started, cap_session)); + break; + case(capture_cb_capture_update_continue): + emit captureEvent(CaptureEvent(CaptureEvent::Update, CaptureEvent::Continued, cap_session)); + break; + case(capture_cb_capture_update_finished): + emit captureEvent(CaptureEvent(CaptureEvent::Update, CaptureEvent::Finished, cap_session)); + break; + case(capture_cb_capture_fixed_started): + emit captureEvent(CaptureEvent(CaptureEvent::Fixed, CaptureEvent::Started, cap_session)); + break; + case(capture_cb_capture_fixed_continue): + emit captureEvent(CaptureEvent(CaptureEvent::Fixed, CaptureEvent::Continued, cap_session)); + break; + case(capture_cb_capture_fixed_finished): + emit captureEvent(CaptureEvent(CaptureEvent::Fixed, CaptureEvent::Finished, cap_session)); + break; + case(capture_cb_capture_stopping): + /* Beware: this state won't be called, if the capture child + * closes the capturing on it's own! */ + emit captureEvent(CaptureEvent(CaptureEvent::Capture, CaptureEvent::Stopping, cap_session)); + break; + case(capture_cb_capture_failed): + emit captureEvent(CaptureEvent(CaptureEvent::Capture, CaptureEvent::Failed, cap_session)); + break; + default: + qWarning() << "main_capture_callback: event " << event << " unknown"; + } +} +#endif // HAVE_LIBPCAP diff --git a/ui/qt/capture_file.h b/ui/qt/capture_file.h new file mode 100644 index 00000000..2249f8e0 --- /dev/null +++ b/ui/qt/capture_file.h @@ -0,0 +1,164 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_FILE_H +#define CAPTURE_FILE_H + +#include + +#include + +#include + +#include "cfile.h" +#include "capture_event.h" + +class CaptureFile : public QObject +{ + Q_OBJECT +public: + explicit CaptureFile(QObject *parent = 0, capture_file *cap_file = NULL); + ~CaptureFile(); + + capture_file *capFile() const { return isValid() ? cap_file_ : NULL; } + void setCapFile(capture_file *cap_file) { cap_file_ = cap_file; } + + /** Check capture file validity + * + * @return true if the file is open, readable, and tappable. false if the file + * is closed. + */ + bool isValid() const; + + /** Return the full pathname. + * + * @return The entire pathname, converted from the native OS's encoding + * to Unicode if necessary, or a null string if the conversion can't + * be done. + */ + const QString filePath(); + + /** Return the plain filename. + * + * @return The last component of the pathname, including the extension, + * converted from the native OS's encoding to Unicode if necessary, or + * a null string if the conversion can't be done. + */ + const QString fileName(); + + /** Return the plain filename without an extension. + * + * @return The last component of the pathname, without the extension, + * converted from the native OS's encoding to Unicode if necessary, or + * a null string if the conversion can't be done. + */ + const QString fileBaseName(); + + /** Return a string representing the file suitable for use for + * display in the UI in places such as a main window title. + * + * @return One of: + * + * the devices on which the capture was done, if the file is a + * temporary file for a capture; + * + * the last component of the capture file's name, converted + * from the native OS's encoding to Unicode if necessary (and + * with REPLACEMENT CHARACTER inserted if the string can't + * be converted). + * + * a null string, if there is no capture file. + */ + const QString fileDisplayName(); + + /** Return a string representing the file suitable for use in an + * auxiliary window title. + * + * @return One of: + * + * the result of fileDisplayName(), if the file is open; + * + * the result of fileDisplayName() followed by [closing], if + * the file is being closed; + * + * the result of fileDisplayName() followed by [closed], if + * the file has been closed; + * + * [no capture file], if there is no capture file. + */ + const QString fileTitle(); + + /** Return the current packet information. + * + * @return A pointer to the current packet_info struct or NULL. + */ + struct _packet_info *packetInfo(); + + /** Timestamp precision for the current file. + * @return One of the WTAP_TSPREC_x values defined in wiretap/wtap.h, + * or WTAP_TSPREC_UNKNOWN if no file is open. + */ + int timestampPrecision(); + + /** Reload the capture file + */ + void reload(); + + /** Return any set display filter + */ + QString displayFilter() const; + + // XXX This shouldn't be needed. + static capture_file *globalCapFile(); + + gpointer window(); + +signals: + void captureEvent(CaptureEvent); + +public slots: + /** Retap the capture file. Convenience wrapper for cf_retap_packets. + * Application events are processed periodically via update_progress_dlg. + */ + void retapPackets(); + + /** Retap the capture file after the current batch of application events + * is processed. If you call this instead of retapPackets or + * cf_retap_packets in a dialog's constructor it will be displayed before + * tapping starts. + */ + void delayedRetapPackets(); + + /** Cancel any tapping that might be in progress. + */ + void stopLoading(); + + /** Sets the capture file's "stop_flag" member. + * + * @param stop_flag If true, stops the current capture file operation. + */ + void setCaptureStopFlag(bool stop_flag = true); + +private: + static void captureFileCallback(gint event, gpointer data, gpointer user_data); +#ifdef HAVE_LIBPCAP + static void captureCallback(gint event, capture_session *cap_session, gpointer user_data); +#endif + + void captureFileEvent(int event, gpointer data); + void captureSessionEvent(int event, capture_session *cap_session); + const QString &getFileBasename(); + + static QString no_capture_file_; + + capture_file *cap_file_; + QString file_state_; +}; + +#endif // CAPTURE_FILE_H diff --git a/ui/qt/capture_file_dialog.cpp b/ui/qt/capture_file_dialog.cpp new file mode 100644 index 00000000..3e1e6230 --- /dev/null +++ b/ui/qt/capture_file_dialog.cpp @@ -0,0 +1,1036 @@ +/* capture_file_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "file.h" + +#include + +#include "packet_range_group_box.h" +#include "capture_file_dialog.h" + + +#ifdef Q_OS_WIN +#include +#include "ui/packet_range.h" +#include "ui/win32/file_dlg_win32.h" +#else // Q_OS_WIN + +#include +#include "wsutil/filesystem.h" +#include "wsutil/nstime.h" +#include "wsutil/str_util.h" +#include "wsutil/utf8_entities.h" + +#include "ui/all_files_wildcard.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // ! Q_OS_WIN + +#include +#include + +#include "epan/prefs.h" +#include +#include + +static const double WIDTH_SCALE_FACTOR = 1.4; +static const double HEIGHT_SCALE_FACTOR = 1.4; + +CaptureFileDialog::CaptureFileDialog(QWidget *parent, capture_file *cf) : + WiresharkFileDialog(parent), + cap_file_(cf), +#if !defined(Q_OS_WIN) + display_filter_edit_(NULL), + default_ft_(-1), + save_bt_(NULL), + help_topic_(TOPIC_ACTION_NONE) +#else + file_type_(-1) +#endif +{ + switch (prefs.gui_fileopen_style) { + case FO_STYLE_LAST_OPENED: + /* The user has specified that we should start out in the last + * directory in which we opened a file. + * + * The open dialog initial directory will be that directory + * unless we've never opened a file, in which case it will + * be the user's personal data file directory. + */ + setDirectory(mainApp->openDialogInitialDir()); + break; + + case FO_STYLE_SPECIFIED: + /* The user has specified that we should always start out in a + * specified directory; if they've specified that directory, + * start out by showing the files in that dir. + */ + if (prefs.gui_fileopen_dir[0] != '\0') + setDirectory(prefs.gui_fileopen_dir); + break; + } + +#if !defined(Q_OS_WIN) + // Add extra widgets + // https://wiki.qt.io/Qt_project_org_faq#How_can_I_add_widgets_to_my_QFileDialog_instance.3F + setOption(QFileDialog::DontUseNativeDialog, true); + setOption(QFileDialog::HideNameFilterDetails, true); + QGridLayout *fd_grid = qobject_cast(layout()); + QHBoxLayout *h_box = new QHBoxLayout(); + + last_row_ = fd_grid->rowCount(); + + fd_grid->addItem(new QSpacerItem(1, 1), last_row_, 0); + fd_grid->addLayout(h_box, last_row_, 0, 1, 2); + last_row_++; + + // Left and right boxes for controls and preview + h_box->addLayout(&left_v_box_); + h_box->addLayout(&right_v_box_); + +#else // Q_OS_WIN + merge_type_ = 0; +#endif // Q_OS_WIN +} + +check_savability_t CaptureFileDialog::checkSaveAsWithComments(QWidget *parent, capture_file *cf, int file_type) { + guint32 comment_types; + bool all_comment_types_supported = true; + + /* What types of comments do we have? */ + comment_types = cf_comment_types(cf); + + /* Does the file's format support all the comments we have? */ + if (comment_types & WTAP_COMMENT_PER_SECTION) { + if (wtap_file_type_subtype_supports_option(file_type, + WTAP_BLOCK_SECTION, + OPT_COMMENT) == OPTION_NOT_SUPPORTED) + all_comment_types_supported = false; + } + if (comment_types & WTAP_COMMENT_PER_INTERFACE) { + if (wtap_file_type_subtype_supports_option(file_type, + WTAP_BLOCK_IF_ID_AND_INFO, + OPT_COMMENT) == OPTION_NOT_SUPPORTED) + all_comment_types_supported = false; + } + if (comment_types & WTAP_COMMENT_PER_PACKET) { + if (wtap_file_type_subtype_supports_option(file_type, + WTAP_BLOCK_PACKET, + OPT_COMMENT) == OPTION_NOT_SUPPORTED) + all_comment_types_supported = false; + } + if (all_comment_types_supported) { + /* Yes. Let the save happen; we can save all the comments, so + there's no need to delete them. */ + return SAVE; + } + + QMessageBox msg_dialog(parent); + QPushButton *save_button; + QPushButton *discard_button; + + msg_dialog.setIcon(QMessageBox::Question); + msg_dialog.setText(tr("This capture file contains comments.")); + msg_dialog.setStandardButtons(QMessageBox::Cancel); + + /* No. Are there formats in which we can write this file that + supports all the comments in this file? */ + if (wtap_dump_can_write(cf->linktypes, comment_types)) { + /* Yes. Offer the user a choice of "Save in a format that + supports comments", "Discard comments and save in the + format you selected", or "Cancel", meaning "don't bother + saving the file at all". */ + msg_dialog.setInformativeText(tr("The file format you chose doesn't support comments. " + "Do you want to save the capture in a format that supports comments " + "or discard the comments and save in the format you chose?")); + // The predefined roles don't really match the tasks at hand... + discard_button = msg_dialog.addButton(tr("Discard comments and save"), QMessageBox::DestructiveRole); + save_button = msg_dialog.addButton(tr("Save in another format"), QMessageBox::AcceptRole); + msg_dialog.setDefaultButton(save_button); + } else { + /* No. Offer the user a choice of "Discard comments and + save in the format you selected" or "Cancel". */ + msg_dialog.setInformativeText(tr("No file format in which it can be saved supports comments. " + "Do you want to discard the comments and save in the format you chose?")); + save_button = NULL; + discard_button = msg_dialog.addButton(tr("Discard comments and save"), QMessageBox::DestructiveRole); + msg_dialog.setDefaultButton(QMessageBox::Cancel); + } + +#if defined(Q_OS_MAC) + /* + * In macOS, the "default button" is not necessarily the button that + * has the input focus; Enter/Return activates the default button, and + * the spacebar activates the button that has the input focus, and + * they might be different buttons. + * + * In a "do you want to save" dialog, for example, the "save" button + * is the default button, and the "don't save" button has the input + * focus, so you can press Enter/Return to save or space not to save + * (or Escape to dismiss the dialog). + * + * In Qt terms, this means "no auto-default", as auto-default makes the + * button with the input focus the default button, so that Enter/Return + * will activate it. + */ + QList buttons = msg_dialog.buttons(); + for (int i = 0; i < buttons.size(); ++i) { + QPushButton *button = static_cast(buttons.at(i));; + button->setAutoDefault(false); + } + + /* + * It also means that the "don't save" button should be the one + * initially given the focus. + */ + discard_button->setFocus(); +#endif + + msg_dialog.exec(); + /* According to the Qt doc: + * when using QMessageBox with custom buttons, exec() function returns an opaque value. + * + * Therefore we should use clickedButton() to determine which button was clicked. */ + + if (msg_dialog.clickedButton() == save_button) { + /* OK, the only other format we support is pcapng. Make that + the one and only format in the combo box, and return to + let the user continue with the dialog. + + XXX - removing all the formats from the combo box will clear + the compressed checkbox; get the current value and restore + it. + + XXX - we know pcapng can be compressed; if we ever end up + supporting saving comments in a format that *can't* be + compressed, such as NetMon format, we must check this. */ + /* XXX - need a compressed checkbox here! */ + return SAVE_IN_ANOTHER_FORMAT; + + } else if (msg_dialog.clickedButton() == discard_button) { + /* Save without the comments and, if that succeeds, delete the + comments. */ + return SAVE_WITHOUT_COMMENTS; + } + + /* Just give up. */ + return CANCELLED; +} + + +#ifndef Q_OS_WIN +void CaptureFileDialog::accept() +{ + // + // If this is a dialog for writing files, we want to ensure that + // the filename has a valid extension before performing file + // existence checks and before closing the dialog. + // This isn't necessary for dialogs for reading files; the name + // has to exactly match the name of the file you want to open, + // and doesn't need to be, and shouldn't be, modified. + // + // XXX also useful for Windows, but that uses a different dialog... + // + if (acceptMode() == QFileDialog::AcceptSave) { + // HACK: ensure that the filename field does not have the focus, + // otherwise selectFile will not change the filename. + setFocus(); + fixFilenameExtension(); + } + WiresharkFileDialog::accept(); +} +#endif // ! Q_OS_WIN + + +// You have to use open, merge, saveAs, or exportPackets. We should +// probably just make each type a subclass. +int CaptureFileDialog::exec() { + return QDialog::Rejected; +} + + + +// Windows +// We use native file dialogs here, rather than the Qt dialog +#ifdef Q_OS_WIN +int CaptureFileDialog::selectedFileType() { + return file_type_; +} + +wtap_compression_type CaptureFileDialog::compressionType() { + return compression_type_; +} + +int CaptureFileDialog::open(QString &file_name, unsigned int &type, QString &display_filter) { + QString title_str = mainApp->windowTitleString(tr("Open Capture File")); + GString *fname = g_string_new(file_name.toUtf8().constData()); + GString *dfilter = g_string_new(display_filter.toUtf8().constData()); + gboolean wof_status; + + // XXX Add a widget->HWND routine to qt_ui_utils and use it instead. + wof_status = win32_open_file((HWND)parentWidget()->effectiveWinId(), title_str.toStdWString().c_str(), fname, &type, dfilter); + file_name = fname->str; + display_filter = dfilter->str; + + g_string_free(fname, TRUE); + g_string_free(dfilter, TRUE); + + return (int) wof_status; +} + +check_savability_t CaptureFileDialog::saveAs(QString &file_name, bool must_support_all_comments) { + QString title_str = mainApp->windowTitleString(tr("Save Capture File As")); + GString *fname = g_string_new(file_name.toUtf8().constData()); + gboolean wsf_status; + + wsf_status = win32_save_as_file((HWND)parentWidget()->effectiveWinId(), title_str.toStdWString().c_str(), cap_file_, fname, &file_type_, &compression_type_, must_support_all_comments); + file_name = fname->str; + + g_string_free(fname, TRUE); + + if (wsf_status) { + return checkSaveAsWithComments(parentWidget(), cap_file_, file_type_); + } + + return CANCELLED; +} + +check_savability_t CaptureFileDialog::exportSelectedPackets(QString &file_name, packet_range_t *range, QString selRange) { + QString title_str = mainApp->windowTitleString(tr("Export Specified Packets")); + GString *fname = g_string_new(file_name.toUtf8().constData()); + gboolean wespf_status; + + if (selRange.length() > 0) + { + packet_range_convert_selection_str(range, selRange.toUtf8().constData()); + } + + wespf_status = win32_export_specified_packets_file((HWND)parentWidget()->effectiveWinId(), title_str.toStdWString().c_str(), cap_file_, fname, &file_type_, &compression_type_, range); + file_name = fname->str; + + g_string_free(fname, TRUE); + + if (wespf_status) { + return checkSaveAsWithComments(parentWidget(), cap_file_, file_type_); + } + + return CANCELLED; +} + +int CaptureFileDialog::merge(QString &file_name, QString &display_filter) { + QString title_str = mainApp->windowTitleString(tr("Merge Capture File")); + GString *fname = g_string_new(file_name.toUtf8().constData()); + GString *dfilter = g_string_new(display_filter.toUtf8().constData()); + gboolean wmf_status; + + + wmf_status = win32_merge_file((HWND)parentWidget()->effectiveWinId(), title_str.toStdWString().c_str(), fname, dfilter, &merge_type_); + file_name = fname->str; + display_filter = dfilter->str; + + g_string_free(fname, TRUE); + g_string_free(dfilter, TRUE); + + return (int) wmf_status; +} + +int CaptureFileDialog::mergeType() { + return merge_type_; +} + +#else // ! Q_OS_WIN +// Not Windows +// We use the Qt dialogs here +QString CaptureFileDialog::fileExtensionType(int et, bool extension_globs) +{ + QString extension_type_name; + QStringList all_wildcards; + QStringList no_compression_suffix_wildcards; + GSList *extensions_list; + GSList *extension; + + extension_type_name = wtap_get_file_extension_type_name(et); + + if (!extension_globs) { + return extension_type_name; + } + + extensions_list = wtap_get_file_extension_type_extensions(et); + + // Get the list of compression-type extensions. + GSList *compression_type_extensions = wtap_get_all_compression_type_extensions_list(); + + /* Construct the list of patterns. */ + for (extension = extensions_list; extension != NULL; + extension = g_slist_next(extension)) { + QString bare_wc = QString("*.%1").arg((char *)extension->data); + all_wildcards << bare_wc; + + // Does this end with a compression suffix? + bool ends_with_compression_suffix = false; + for (GSList *compression_type_extension = compression_type_extensions; + compression_type_extension != NULL; + compression_type_extension = g_slist_next(compression_type_extension)) { + QString suffix = QString(".") + (char *)compression_type_extension->data; + if (bare_wc.endsWith(suffix)) { + ends_with_compression_suffix = true; + break; + } + } + + // If it doesn't, add it to the list of wildcards-without- + // compression-suffixes. + if (!ends_with_compression_suffix) + no_compression_suffix_wildcards << bare_wc; + } + g_slist_free(compression_type_extensions); + wtap_free_extensions_list(extensions_list); + + // We set HideNameFilterDetails so that "All Files" and "All Capture + // Files" don't show a wildcard list. We want to show the associated + // wildcards for individual file types so we add them twice. + return QString("%1 (%2) (%3)") + .arg(extension_type_name) + .arg(no_compression_suffix_wildcards.join(" ")) + .arg(all_wildcards.join(" ")); +} + +// Returns " (...)", containing the suffix list suitable for setNameFilters. +// All extensions ("pcap", "pcap.gz", etc.) are also returned in "suffixes". +QString CaptureFileDialog::fileType(int ft, QStringList &suffixes) +{ + QString filter; + GSList *extensions_list; + + filter = " ("; + + extensions_list = wtap_get_file_extensions_list(ft, TRUE); + if (extensions_list == NULL) { + /* This file type doesn't have any particular extension + conventionally used for it, so we'll just use a + wildcard that matches all file names - even those + with no extension, so we don't need to worry about + compressed file extensions. */ + filter += ALL_FILES_WILDCARD; + } else { + // HACK: at least for Qt 5.10 and before, if the first extension is + // empty ("."), it will prevent the default (broken) extension + // replacement from being applied in the non-native Save file dialog. + filter += '.'; + + /* Construct the list of patterns. */ + for (GSList *extension = extensions_list; extension != NULL; + extension = g_slist_next(extension)) { + QString suffix((char *)extension->data); + filter += " *." + suffix;; + suffixes << suffix; + } + wtap_free_extensions_list(extensions_list); + } + filter += ')'; + return filter; +} + +QStringList CaptureFileDialog::buildFileOpenTypeList() { + QStringList filters; + QString filter, sep; + GSList *extensions_list; + GSList *extension; + int et; + + /* + * Microsoft's UI guidelines say, of the file filters in open and + * save dialogs: + * + * For meta-filters, remove the file extension list to eliminate + * clutter. Examples: "All files," "All pictures," "All music," + * and "All videos." + * + * On both Windows XP and Windows 7, Wordpad doesn't do that, but + * Paint does. + * + * XXX - on Windows, does Qt do that here? For "All Capture Files", + * the filter will be a bit long, so it *really* shouldn't be shown. + * What about other platforms? + */ + filters << QString(tr("All Files (" ALL_FILES_WILDCARD ")")); + + /* + * Add an "All Capture Files" entry, with all the capture file + * extensions we know about. + */ + filter = tr("All Capture Files"); + + /* + * Construct its list of patterns. + */ + extensions_list = wtap_get_all_capture_file_extensions_list(); + sep = " ("; + for (extension = extensions_list; extension != NULL; + extension = g_slist_next(extension)) { + filter += sep; + filter += "*."; + filter += (char *)extension->data; + sep = " "; + } + wtap_free_extensions_list(extensions_list); + filter += ")"; + filters << filter; + + /* Include all the file types Wireshark supports. */ + for (et = 0; et < wtap_get_num_file_type_extensions(); et++) { + filters << fileExtensionType(et); + } + + return filters; +} + +// Replaces or appends an extension based on the current file filter +// and compression setting. +// Used in dialogs that select a file to write. +void CaptureFileDialog::fixFilenameExtension() +{ + QFileInfo fi(selectedFiles()[0]); + QString filename = fi.fileName(); + if (fi.isDir() || filename.isEmpty()) { + // no file selected, or a directory was selected. Ignore. + return; + } + + QString old_suffix; + QString new_suffix(wtap_default_file_extension(selectedFileType())); + QStringList valid_extensions = type_suffixes_.value(selectedNameFilter()); + // Find suffixes such as "pcap" or "pcap.gz" if any + if (!fi.suffix().isEmpty()) { + QStringList current_suffixes(fi.suffix()); + int pos = static_cast(filename.lastIndexOf('.', -2 - current_suffixes.at(0).size())); + if (pos > 0) { + current_suffixes.prepend(filename.right(filename.size() - (pos + 1))); + } + + // If the current suffix is valid for the current file type, try to + // preserve it. Otherwise use the default file extension (if available). + foreach (const QString ¤t_suffix, current_suffixes) { + if (valid_extensions.contains(current_suffix)) { + old_suffix = current_suffix; + new_suffix = current_suffix; + break; + } + } + if (old_suffix.isEmpty()) { + foreach (const QString ¤t_suffix, current_suffixes) { + foreach (const QStringList &suffixes, type_suffixes_.values()) { + if (suffixes.contains(current_suffix)) { + old_suffix = current_suffix; + break; + } + } + if (!old_suffix.isEmpty()) { + break; + } + } + } + } + + // Fixup the new suffix based on whether we're compressing or not. + if (compressionType() == WTAP_UNCOMPRESSED) { + // Not compressing; strip off any compression suffix + GSList *compression_type_extensions = wtap_get_all_compression_type_extensions_list(); + for (GSList *compression_type_extension = compression_type_extensions; + compression_type_extension != NULL; + compression_type_extension = g_slist_next(compression_type_extension)) { + QString suffix = QString(".") + (char *)compression_type_extension->data; + if (new_suffix.endsWith(suffix)) { + // + // It ends with this compression suffix; chop it off. + // + new_suffix.chop(suffix.size()); + break; + } + } + g_slist_free(compression_type_extensions); + } else { + // Compressing; append the appropriate compression suffix. + QString compressed_file_extension = QString(".") + wtap_compression_type_extension(compressionType()); + if (valid_extensions.contains(new_suffix + compressed_file_extension)) { + new_suffix += compressed_file_extension; + } + } + + if (!new_suffix.isEmpty() && old_suffix != new_suffix) { + filename.chop(old_suffix.size()); + if (old_suffix.isEmpty()) { + filename += '.'; + } + filename += new_suffix; + selectFile(filename); + } +} + +void CaptureFileDialog::addPreview(QVBoxLayout &v_box) { + QGridLayout *preview_grid = new QGridLayout(); + QLabel *lbl; + + preview_labels_.clear(); + v_box.addLayout(preview_grid); + + preview_grid->setColumnStretch(0, 0); + preview_grid->setColumnStretch(1, 10); + + lbl = new QLabel(tr("Format:")); + preview_grid->addWidget(lbl, 0, 0); + preview_grid->addWidget(&preview_format_, 0, 1); + preview_labels_ << lbl << &preview_format_; + + lbl = new QLabel(tr("Size:")); + preview_grid->addWidget(lbl, 1, 0); + preview_grid->addWidget(&preview_size_, 1, 1); + preview_labels_ << lbl << &preview_size_; + + lbl = new QLabel(tr("Start / elapsed:")); + preview_grid->addWidget(lbl, 3, 0); + preview_grid->addWidget(&preview_first_elapsed_, 3, 1); + preview_labels_ << lbl << &preview_first_elapsed_; + + connect(this, &CaptureFileDialog::currentChanged, this, &CaptureFileDialog::preview); + + preview(""); +} + +void CaptureFileDialog::addMergeControls(QVBoxLayout &v_box) { + + merge_prepend_.setText(tr("Prepend packets")); + merge_prepend_.setToolTip(tr("Insert packets from the selected file before the current file. Packet timestamps will be ignored.")); + v_box.addWidget(&merge_prepend_, 0, Qt::AlignTop); + + merge_chrono_.setText(tr("Merge chronologically")); + merge_chrono_.setToolTip(tr("Insert packets in chronological order.")); + merge_chrono_.setChecked(true); + v_box.addWidget(&merge_chrono_, 0, Qt::AlignTop); + + merge_append_.setText(tr("Append packets")); + merge_append_.setToolTip(tr("Insert packets from the selected file after the current file. Packet timestamps will be ignored.")); + v_box.addWidget(&merge_append_, 0, Qt::AlignTop); +} + +int CaptureFileDialog::selectedFileType() { + return type_hash_.value(selectedNameFilter(), WTAP_FILE_TYPE_SUBTYPE_UNKNOWN); +} + +wtap_compression_type CaptureFileDialog::compressionType() { + return compress_.isChecked() ? WTAP_GZIP_COMPRESSED : WTAP_UNCOMPRESSED; +} + +void CaptureFileDialog::addDisplayFilterEdit(QString &display_filter) { + QGridLayout *fd_grid = qobject_cast(layout()); + + fd_grid->addWidget(new QLabel(tr("Read filter:")), last_row_, 0); + + display_filter_edit_ = new DisplayFilterEdit(this, ReadFilterToApply); + display_filter_edit_->setText(display_filter); + fd_grid->addWidget(display_filter_edit_, last_row_, 1); + last_row_++; +} + +void CaptureFileDialog::addFormatTypeSelector(QVBoxLayout &v_box) { + int i; + /* Put Auto, as well as pcap and pcapng (which are the first two entries in + open_routines), at the top of the file type list. */ + format_type_.addItem(tr("Automatically detect file type")); + for (i = 0; i < 2; i += 1) { + format_type_.addItem(open_routines[i].name); + } + /* Generate a sorted list of the remaining file types. */ + QStringList routine_names; + for ( /* keep using i */ ; open_routines[i].name != NULL; i += 1) { + routine_names += QString(open_routines[i].name); + } + routine_names.sort(Qt::CaseInsensitive); + for (i = 0; i < routine_names.size(); i += 1) { + format_type_.addItem(routine_names.at(i)); + } + + v_box.addWidget(&format_type_, 0, Qt::AlignTop); +} + +void CaptureFileDialog::addGzipControls(QVBoxLayout &v_box) { + compress_.setText(tr("Compress with g&zip")); + if (cap_file_->compression_type == WTAP_GZIP_COMPRESSED && + wtap_dump_can_compress(default_ft_)) { + compress_.setChecked(true); + } else { + compress_.setChecked(false); + } + v_box.addWidget(&compress_, 0, Qt::AlignTop); + connect(&compress_, &QCheckBox::stateChanged, this, &CaptureFileDialog::fixFilenameExtension); + +} + +void CaptureFileDialog::addRangeControls(QVBoxLayout &v_box, packet_range_t *range, QString selRange) { + packet_range_group_box_.initRange(range, selRange); + v_box.addWidget(&packet_range_group_box_, 0, Qt::AlignTop); +} + +QDialogButtonBox *CaptureFileDialog::addHelpButton(topic_action_e help_topic) +{ + // This doesn't appear to be documented anywhere but it seems pretty obvious + // and it works. + QDialogButtonBox *button_box = findChild(); + + help_topic_ = help_topic; + + if (button_box) { + button_box->addButton(QDialogButtonBox::Help); + connect(button_box, &QDialogButtonBox::helpRequested, this, &CaptureFileDialog::on_buttonBox_helpRequested); + } + return button_box; +} + +int CaptureFileDialog::open(QString &file_name, unsigned int &type, QString &display_filter) { + setWindowTitle(mainApp->windowTitleString(tr("Open Capture File"))); + QStringList open_type_filters(buildFileOpenTypeList()); + setNameFilters(open_type_filters); + selectNameFilter(open_type_filters.at(1)); + setFileMode(QFileDialog::ExistingFile); + + addFormatTypeSelector(left_v_box_); + addDisplayFilterEdit(display_filter); + addPreview(right_v_box_); + addHelpButton(HELP_OPEN_DIALOG); + + // Grow the dialog to account for the extra widgets. + resize(width() * WIDTH_SCALE_FACTOR, height() * HEIGHT_SCALE_FACTOR + left_v_box_.minimumSize().height() + display_filter_edit_->minimumSize().height()); + + display_filter.clear(); + + if (!file_name.isEmpty()) { + selectFile(file_name); + } + + if (WiresharkFileDialog::exec() && selectedFiles().length() > 0) { + file_name = selectedFiles()[0]; + type = open_info_name_to_type(qPrintable(format_type_.currentText())); + display_filter.append(display_filter_edit_->text()); + + return QDialog::Accepted; + } else { + return QDialog::Rejected; + } +} + +check_savability_t CaptureFileDialog::saveAs(QString &file_name, bool must_support_all_comments) { + setWindowTitle(mainApp->windowTitleString(tr("Save Capture File As"))); + // XXX There doesn't appear to be a way to use setNameFilters without restricting + // what the user can select. We might want to use our own combobox instead and + // let the user select anything. + setNameFilters(buildFileSaveAsTypeList(must_support_all_comments)); + setAcceptMode(QFileDialog::AcceptSave); + setLabelText(FileType, tr("Save as:")); + + addGzipControls(left_v_box_); + addHelpButton(HELP_SAVE_DIALOG); + + // Grow the dialog to account for the extra widgets. + resize(width() * WIDTH_SCALE_FACTOR, height() * HEIGHT_SCALE_FACTOR + left_v_box_.minimumSize().height()); + + if (!file_name.isEmpty()) { + selectFile(file_name); + } + connect(this, &QFileDialog::filterSelected, this, &CaptureFileDialog::fixFilenameExtension); + + if (WiresharkFileDialog::exec() && selectedFiles().length() > 0) { + int file_type; + + file_name = selectedFiles()[0]; + file_type = selectedFileType(); + /* Is the file type bogus? */ + if (file_type == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN) { + /* This "should not happen". */ + QMessageBox msg_dialog; + + msg_dialog.setIcon(QMessageBox::Critical); + msg_dialog.setText(tr("Unknown file type returned by save as dialog.")); + msg_dialog.setInformativeText(tr("Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues.")); + msg_dialog.exec(); + return CANCELLED; + } + return checkSaveAsWithComments(this, cap_file_, file_type); + } + return CANCELLED; +} + +check_savability_t CaptureFileDialog::exportSelectedPackets(QString &file_name, packet_range_t *range, QString selRange) { + QDialogButtonBox *button_box; + + setWindowTitle(mainApp->windowTitleString(tr("Export Specified Packets"))); + // XXX See comment in ::saveAs regarding setNameFilters + setNameFilters(buildFileSaveAsTypeList(false)); + setAcceptMode(QFileDialog::AcceptSave); + setLabelText(FileType, tr("Export as:")); + + addRangeControls(left_v_box_, range, selRange); + addGzipControls(right_v_box_); + button_box = addHelpButton(HELP_EXPORT_FILE_DIALOG); + + if (button_box) { + save_bt_ = button_box->button(QDialogButtonBox::Save); + if (save_bt_) { + connect(&packet_range_group_box_, &PacketRangeGroupBox::validityChanged, + save_bt_, &QPushButton::setEnabled); + } + } + + // Grow the dialog to account for the extra widgets. + resize(width() * WIDTH_SCALE_FACTOR, height() * HEIGHT_SCALE_FACTOR + (packet_range_group_box_.height() * 2 / 3)); + + if (!file_name.isEmpty()) { + selectFile(file_name); + } + connect(this, &QFileDialog::filterSelected, this, &CaptureFileDialog::fixFilenameExtension); + + if (WiresharkFileDialog::exec() && selectedFiles().length() > 0) { + int file_type; + + file_name = selectedFiles()[0]; + file_type = selectedFileType(); + /* Is the file type bogus? */ + if (file_type == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN) { + /* This "should not happen". */ + QMessageBox msg_dialog; + + msg_dialog.setIcon(QMessageBox::Critical); + msg_dialog.setText(tr("Unknown file type returned by save as dialog.")); + msg_dialog.setInformativeText(tr("Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues.")); + msg_dialog.exec(); + return CANCELLED; + } + return checkSaveAsWithComments(this, cap_file_, file_type); + } + return CANCELLED; +} + +int CaptureFileDialog::merge(QString &file_name, QString &display_filter) { + setWindowTitle(mainApp->windowTitleString(tr("Merge Capture File"))); + setNameFilters(buildFileOpenTypeList()); + setFileMode(QFileDialog::ExistingFile); + + addDisplayFilterEdit(display_filter); + addMergeControls(left_v_box_); + addPreview(right_v_box_); + addHelpButton(HELP_MERGE_DIALOG); + + file_name.clear(); + display_filter.clear(); + + // Grow the dialog to account for the extra widgets. + resize(width() * WIDTH_SCALE_FACTOR, height() * HEIGHT_SCALE_FACTOR + right_v_box_.minimumSize().height() + display_filter_edit_->minimumSize().height()); + + if (WiresharkFileDialog::exec() && selectedFiles().length() > 0) { + file_name.append(selectedFiles()[0]); + display_filter.append(display_filter_edit_->text()); + + return QDialog::Accepted; + } else { + return QDialog::Rejected; + } +} + +QStringList CaptureFileDialog::buildFileSaveAsTypeList(bool must_support_all_comments) { + QStringList filters; + guint32 required_comment_types; + GArray *savable_file_types_subtypes; + guint i; + + type_hash_.clear(); + type_suffixes_.clear(); + + /* What types of comments do we have to support? */ + if (must_support_all_comments) + required_comment_types = cf_comment_types(cap_file_); /* all the ones the file has */ + else + required_comment_types = 0; /* none of them */ + + /* What types of file can we save this file as? */ + savable_file_types_subtypes = wtap_get_savable_file_types_subtypes_for_file(cap_file_->cd_t, + cap_file_->linktypes, + required_comment_types, + FT_SORT_BY_DESCRIPTION); + + if (savable_file_types_subtypes != NULL) { + int ft; + /* OK, we have at least one file type we can save this file as. + (If we didn't, we shouldn't have gotten here in the first + place.) Add them all to the combo box. */ + for (i = 0; i < savable_file_types_subtypes->len; i++) { + ft = g_array_index(savable_file_types_subtypes, int, i); + if (default_ft_ < 1) + default_ft_ = ft; /* first file type is the default */ + QString type_name(wtap_file_type_subtype_description(ft)); + filters << type_name + fileType(ft, type_suffixes_[type_name]); + type_hash_[type_name] = ft; + } + g_array_free(savable_file_types_subtypes, TRUE); + } + + return filters; +} + +int CaptureFileDialog::mergeType() { + if (merge_prepend_.isChecked()) + return -1; + else if (merge_append_.isChecked()) + return 1; + + return 0; +} + +// Slots + + + +/* do a preview run on the currently selected capture file */ +void CaptureFileDialog::preview(const QString & path) +{ + wtap *wth; + int err; + gchar *err_info; + ws_file_preview_stats stats; + ws_file_preview_stats_status status; + time_t ti_time; + struct tm *ti_tm; + unsigned int elapsed_time; + + foreach (QLabel *lbl, preview_labels_) { + lbl->setEnabled(false); + } + + preview_format_.setText(tr(UTF8_EM_DASH)); + preview_size_.setText(tr(UTF8_EM_DASH)); + preview_first_elapsed_.setText(tr(UTF8_EM_DASH)); + + if (path.length() < 1) { + return; + } + + if (test_for_directory(path.toUtf8().data()) == EISDIR) { + preview_format_.setText(tr("directory")); + return; + } + + wth = wtap_open_offline(path.toUtf8().data(), WTAP_TYPE_AUTO, &err, &err_info, TRUE); + if (wth == NULL) { + if (err == WTAP_ERR_FILE_UNKNOWN_FORMAT) { + preview_format_.setText(tr("unknown file format")); + } else { + preview_format_.setText(tr("error opening file")); + } + return; + } + + // Success! + foreach (QLabel *lbl, preview_labels_) { + lbl->setEnabled(true); + } + + // Format + preview_format_.setText(QString::fromUtf8(wtap_file_type_subtype_description(wtap_file_type_subtype(wth)))); + + // Size + gint64 filesize = wtap_file_size(wth, &err); + // Finder and Windows Explorer use IEC. What do the various Linux file managers use? + QString size_str(gchar_free_to_qstring(format_size(filesize, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_IEC))); + + status = get_stats_for_preview(wth, &stats, &err, &err_info); + + if (status == PREVIEW_READ_ERROR) { + // XXX - give error details? + g_free(err_info); + preview_size_.setText(tr("%1, error after %Ln data record(s)", "", stats.records) + .arg(size_str)); + return; + } + + // Packet count + if (status == PREVIEW_TIMED_OUT) { + preview_size_.setText(tr("%1, timed out at %Ln data record(s)", "", stats.data_records) + .arg(size_str)); + } else { + preview_size_.setText(tr("%1, %Ln data record(s)", "", stats.data_records) + .arg(size_str)); + } + + // First packet + elapsed time + QString first_elapsed; + if (stats.have_times) { + // + // We saw at least one record with a time stamp, so we can give + // a start time (if we have a mix of records with and without + // time stamps, and there were records without time stamps + // before the first one with a time stamp, this may be inaccurate). + // + ti_time = (long)stats.start_time; + ti_tm = localtime(&ti_time); + first_elapsed = "?"; + if (ti_tm) { + first_elapsed = QString("%1-%2-%3 %4:%5:%6") + .arg(ti_tm->tm_year + 1900, 4, 10, QChar('0')) + .arg(ti_tm->tm_mon + 1, 2, 10, QChar('0')) + .arg(ti_tm->tm_mday, 2, 10, QChar('0')) + .arg(ti_tm->tm_hour, 2, 10, QChar('0')) + .arg(ti_tm->tm_min, 2, 10, QChar('0')) + .arg(ti_tm->tm_sec, 2, 10, QChar('0')); + } + } else { + first_elapsed = tr("unknown"); + } + + // Elapsed time + first_elapsed += " / "; + if (status == PREVIEW_SUCCEEDED && stats.have_times) { + // + // We didn't time out, so we looked at all packets, and we got + // at least one packet with a time stamp, so we can calculate + // an elapsed time from the time stamp of the last packet with + // with a time stamp (if we have a mix of records with and without + // time stamps, and there were records without time stamps after + // the last one with a time stamp, this may be inaccurate). + // + elapsed_time = (unsigned int)(stats.stop_time-stats.start_time); + if (elapsed_time/86400) { + first_elapsed += QString("%1 days ").arg(elapsed_time/86400, 2, 10, QChar('0')); + elapsed_time = elapsed_time % 86400; + } + first_elapsed += QString("%2:%3:%4") + .arg(elapsed_time%86400/3600, 2, 10, QChar('0')) + .arg(elapsed_time%3600/60, 2, 10, QChar('0')) + .arg(elapsed_time%60, 2, 10, QChar('0')); + } else { + first_elapsed += tr("unknown"); + } + preview_first_elapsed_.setText(first_elapsed); + + wtap_close(wth); +} + +void CaptureFileDialog::on_buttonBox_helpRequested() +{ + if (help_topic_ != TOPIC_ACTION_NONE) mainApp->helpTopicAction(help_topic_); +} + +#endif // ! Q_OS_WIN diff --git a/ui/qt/capture_file_dialog.h b/ui/qt/capture_file_dialog.h new file mode 100644 index 00000000..1e22ad8b --- /dev/null +++ b/ui/qt/capture_file_dialog.h @@ -0,0 +1,146 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_FILE_DIALOG_H +#define CAPTURE_FILE_DIALOG_H + +#include + +#ifndef Q_OS_WIN +#include +#include "packet_range_group_box.h" +#include "ui/help_url.h" +#endif // Q_OS_WIN + +#include + +#include +#include "cfile.h" + +#include "ui/file_dialog.h" + +#include +#include +#include +#include +#include +#include + +class CaptureFileDialog : public WiresharkFileDialog +{ + // The GTK+ Open Capture File dialog has the following elements and features: + // - The ability to select a capture file from a list of known extensions + // - A display filter entry + // - Name resolution checkboxes + // - Capture file preview information + // Ideally we should provide similar functionality here. + // + // You can subclass QFileDialog (which we've done here) and add widgets as + // described at + // https://web.archive.org/web/20100528190736/http://developer.qt.nokia.com/faq/answer/how_can_i_add_widgets_to_my_qfiledialog_instance + // However, Qt's idea of what a file dialog looks like isn't what Microsoft + // and Apple think a file dialog looks like. + // + // On Windows Vista and later we should probably use IFileOpenDialog. On earlier + // versions of Windows (including XP) we should use GetOpenFileName, which is + // what we do in ui/win32/file_dlg_win32.c. macOS we should use NSOpenPanel. On + // other platforms we should fall back to QFileDialog. + // + // Yes, that's four implementations of the same window. + // + // If a plain native open file dialog is good enough we can just the static + // version of QFileDialog::getOpenFileName. (Commenting out Q_OBJECT and + // "explicit" below has the same effect.) + + Q_OBJECT +public: + explicit CaptureFileDialog(QWidget *parent = NULL, capture_file *cf = NULL); + static check_savability_t checkSaveAsWithComments(QWidget * +#if defined(Q_OS_WIN) + parent +#endif // Q_OS_WIN + , capture_file *cf, int file_type); + + int mergeType(); + int selectedFileType(); + wtap_compression_type compressionType(); + +private: + capture_file *cap_file_; + +#if !defined(Q_OS_WIN) + void addMergeControls(QVBoxLayout &v_box); + void addFormatTypeSelector(QVBoxLayout &v_box); + void addDisplayFilterEdit(QString &display_filter); + void addPreview(QVBoxLayout &v_box); + QString fileExtensionType(int et, bool extension_globs = true); + QString fileType(int ft, QStringList &suffixes); + QStringList buildFileOpenTypeList(void); + + QVBoxLayout left_v_box_; + QVBoxLayout right_v_box_; + + DisplayFilterEdit* display_filter_edit_; + int last_row_; + + QLabel preview_format_; + QLabel preview_size_; + QLabel preview_first_elapsed_; + QList preview_labels_; + + QRadioButton merge_prepend_; + QRadioButton merge_chrono_; + QRadioButton merge_append_; + + QComboBox format_type_; + QHash type_hash_; + QHash type_suffixes_; + + void addGzipControls(QVBoxLayout &v_box); + void addRangeControls(QVBoxLayout &v_box, packet_range_t *range, QString selRange = QString()); + QDialogButtonBox *addHelpButton(topic_action_e help_topic); + + QStringList buildFileSaveAsTypeList(bool must_support_comments); + + int default_ft_; + + QCheckBox compress_; + + PacketRangeGroupBox packet_range_group_box_; + QPushButton *save_bt_; + topic_action_e help_topic_; + +#else // Q_OS_WIN + int file_type_; + int merge_type_; + wtap_compression_type compression_type_; +#endif // Q_OS_WIN + +signals: + +public slots: + +#ifndef Q_OS_WIN + void accept() Q_DECL_OVERRIDE; +#endif + int exec() Q_DECL_OVERRIDE; + int open(QString &file_name, unsigned int &type, QString &display_filter); + check_savability_t saveAs(QString &file_name, bool must_support_comments); + check_savability_t exportSelectedPackets(QString &file_name, packet_range_t *range, QString selRange = QString()); + int merge(QString &file_name, QString &display_filter); + +private slots: +#if !defined(Q_OS_WIN) + void fixFilenameExtension(); + void preview(const QString & path); + void on_buttonBox_helpRequested(); +#endif // Q_OS_WIN +}; + +#endif // CAPTURE_FILE_DIALOG_H diff --git a/ui/qt/capture_file_properties_dialog.cpp b/ui/qt/capture_file_properties_dialog.cpp new file mode 100644 index 00000000..c7705681 --- /dev/null +++ b/ui/qt/capture_file_properties_dialog.cpp @@ -0,0 +1,650 @@ +/* capture_file_properties_dialog.cpp + * + * GSoC 2013 - QtShark + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "capture_file_properties_dialog.h" +#include + +#include "ui/simple_dialog.h" +#include "ui/summary.h" + +#include "wsutil/str_util.h" +#include "wsutil/utf8_entities.h" +#include "wsutil/version_info.h" + +#include +#include "main_application.h" + +#include +#include +#include + +// To do: +// - Add file hashes +// - Add formats (HTML, plain text, YAML)? + +CaptureFilePropertiesDialog::CaptureFilePropertiesDialog(QWidget &parent, CaptureFile &capture_file) : + WiresharkDialog(parent, capture_file), + ui(new Ui::CaptureFilePropertiesDialog) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 2 / 3, parent.height()); + + ui->detailsTextEdit->setAcceptRichText(true); + + // make the details box larger than the comments + ui->splitter->setStretchFactor(0, 6); + ui->splitter->setStretchFactor(1, 1); + + QPushButton *button = ui->buttonBox->button(QDialogButtonBox::Reset); + if (button) { + button->setText(tr("Refresh")); + } + + button = ui->buttonBox->button(QDialogButtonBox::Apply); + if (button) { + button->setText(tr("Copy To Clipboard")); + } + + button = ui->buttonBox->button(QDialogButtonBox::Save); + if (button) { + button->setText(tr("Save Comments")); + } + + button = ui->buttonBox->button(QDialogButtonBox::Close); + if (button) { + button->setDefault(true); + } + + setWindowSubtitle(tr("Capture File Properties")); + QTimer::singleShot(0, this, SLOT(updateWidgets())); +} + +/* + * Slots + */ + +CaptureFilePropertiesDialog::~CaptureFilePropertiesDialog() +{ + delete ui; +} + +/**/ + +void CaptureFilePropertiesDialog::updateWidgets() +{ + QPushButton *refresh_bt = ui->buttonBox->button(QDialogButtonBox::Reset); + QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save); + + if (file_closed_ || !cap_file_.isValid()) { + if (refresh_bt) { + refresh_bt->setEnabled(false); + } + ui->commentsTextEdit->setReadOnly(true); + if (save_bt) { + save_bt->setEnabled(false); + } + WiresharkDialog::updateWidgets(); + return; + } + + bool enable = wtap_dump_can_write(cap_file_.capFile()->linktypes, WTAP_COMMENT_PER_SECTION); + save_bt->setEnabled(enable); + ui->commentsTextEdit->setEnabled(enable); + + fillDetails(); + // XXX - this just handles the first comment in the first section; + // add support for multiple sections with multiple comments. + wtap_block_t shb = wtap_file_get_shb(cap_file_.capFile()->provider.wth, 0); + char *shb_comment; + if (wtap_block_get_nth_string_option_value(shb, OPT_COMMENT, 0, + &shb_comment) == WTAP_OPTTYPE_SUCCESS) + ui->commentsTextEdit->setText(shb_comment); + else + ui->commentsTextEdit->setText(NULL); + + WiresharkDialog::updateWidgets(); +} + +static const QString section_tmpl_ = "

%1

\n"; +static const QString para_tmpl_ = "

%1

\n"; + +QString CaptureFilePropertiesDialog::summaryToHtml() +{ + summary_tally summary; + double seconds = 0.0; + double disp_seconds = 0.0; + double marked_seconds = 0.0; + + memset(&summary, 0, sizeof(summary_tally)); + + QString table_begin, table_end; + QString table_row_begin, table_ul_row_begin, table_row_end; + QString table_vheader_tmpl, table_hheader20_tmpl, table_hheader25_tmpl; + QString table_data_tmpl; + + table_begin = "

\n"; + table_end = "

\n"; + table_row_begin = "\n"; + table_ul_row_begin = "\n"; + table_row_end = "\n"; + table_vheader_tmpl = "%1:"; // looked odd + table_hheader20_tmpl = "%1"; + table_hheader25_tmpl = "%1"; + table_data_tmpl = "%1"; + + if (!file_closed_) { + /* initial computations */ + summary_fill_in(cap_file_.capFile(), &summary); +#ifdef HAVE_LIBPCAP + summary_fill_in_capture(cap_file_.capFile(), &global_capture_opts, &summary); +#endif + } + + seconds = summary.stop_time - summary.start_time; + disp_seconds = summary.filtered_stop - summary.filtered_start; + marked_seconds = summary.marked_stop - summary.marked_start; + + QString summary_str; + QTextStream out(&summary_str); + QString unknown = tr("Unknown"); + + // File Section + out << section_tmpl_.arg(tr("File")); + out << table_begin; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Name")) + << table_data_tmpl.arg(summary.filename) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Length")) + << table_data_tmpl.arg(file_size_to_qstring(summary.file_length)) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Hash (SHA256)")) + << table_data_tmpl.arg(summary.file_sha256) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Hash (SHA1)")) + << table_data_tmpl.arg(summary.file_sha1) + << table_row_end; + + QString format_str = wtap_file_type_subtype_description(summary.file_type); + const char *compression_type_description = wtap_compression_type_description(summary.compression_type); + if (compression_type_description != nullptr) { + format_str += QString(" (%1)").arg(compression_type_description); + } + out << table_row_begin + << table_vheader_tmpl.arg(tr("Format")) + << table_data_tmpl.arg(format_str) + << table_row_end; + + QString encaps_str; + if (summary.file_encap_type == WTAP_ENCAP_PER_PACKET) { + for (guint i = 0; i < summary.packet_encap_types->len; i++) + { + encaps_str = QString(wtap_encap_description(g_array_index(summary.packet_encap_types, int, i))); + } + } else { + encaps_str = QString(wtap_encap_description(summary.file_encap_type)); + } + out << table_row_begin + << table_vheader_tmpl.arg(tr("Encapsulation")) + << table_data_tmpl.arg(encaps_str) + << table_row_end; + + if (summary.snap != 0) { + out << table_row_begin + << table_vheader_tmpl.arg(tr("Snapshot length")) + << table_data_tmpl.arg(summary.snap) + << table_row_end; + } + + out << table_end; + + // Time Section + if (summary.packet_count_ts == summary.packet_count && + summary.packet_count >= 1) + { + out << section_tmpl_.arg(tr("Time")); + out << table_begin; + + // start time + out << table_row_begin + << table_vheader_tmpl.arg(tr("First packet")) + << table_data_tmpl.arg(time_t_to_qstring((time_t)summary.start_time)) + << table_row_end; + + // stop time + out << table_row_begin + << table_vheader_tmpl.arg(tr("Last packet")) + << table_data_tmpl.arg(time_t_to_qstring((time_t)summary.stop_time)) + << table_row_end; + + // elapsed seconds (capture duration) + if (summary.packet_count_ts >= 2) + { + /* elapsed seconds */ + QString elapsed_str; + unsigned int elapsed_time = (unsigned int)summary.elapsed_time; + if (elapsed_time/86400) + { + elapsed_str = QString("%1 days ").arg(elapsed_time / 86400); + } + + elapsed_str += QString("%1:%2:%3") + .arg(elapsed_time % 86400 / 3600, 2, 10, QChar('0')) + .arg(elapsed_time % 3600 / 60, 2, 10, QChar('0')) + .arg(elapsed_time % 60, 2, 10, QChar('0')); + out << table_row_begin + << table_vheader_tmpl.arg(tr("Elapsed")) + << table_data_tmpl.arg(elapsed_str) + << table_row_end; + } + + out << table_end; + } + + // Information from file sections. + for (guint section_number = 0; + section_number < wtap_file_get_num_shbs(cap_file_.capFile()->provider.wth); + section_number++) { + + // If we have more than one section, add headers for each section. + if (wtap_file_get_num_shbs(cap_file_.capFile()->provider.wth) > 1) + out << section_tmpl_.arg(QString(tr("Section %1")) + .arg(section_number)); + + // Capture Section + out << section_tmpl_.arg(tr("Capture")); + out << table_begin; + + wtap_block_t shb_inf = wtap_file_get_shb(cap_file_.capFile()->provider.wth, section_number); + char *str; + + if (shb_inf != nullptr) { + QString capture_hardware(unknown); + if (wtap_block_get_string_option_value(shb_inf, OPT_SHB_HARDWARE, &str) == WTAP_OPTTYPE_SUCCESS) { + if (str[0] != '\0') { + capture_hardware = str; + } + } + // capture HW + out << table_row_begin + << table_vheader_tmpl.arg(tr("Hardware")) + << table_data_tmpl.arg(capture_hardware) + << table_row_end; + + QString capture_os(unknown); + if (wtap_block_get_string_option_value(shb_inf, OPT_SHB_OS, &str) == WTAP_OPTTYPE_SUCCESS) { + if (str[0] != '\0') { + capture_os = str; + } + } + out << table_row_begin + << table_vheader_tmpl.arg(tr("OS")) + << table_data_tmpl.arg(capture_os) + << table_row_end; + + QString capture_app(unknown); + if (wtap_block_get_string_option_value(shb_inf, OPT_SHB_USERAPPL, &str) == WTAP_OPTTYPE_SUCCESS) { + if (str[0] != '\0') { + capture_app = str; + } + } + out << table_row_begin + << table_vheader_tmpl.arg(tr("Application")) + << table_data_tmpl.arg(capture_app) + << table_row_end; + } + + out << table_end; + + // capture interfaces info + if (summary.ifaces->len > 0) { + out << section_tmpl_.arg(tr("Interfaces")); + out << table_begin; + + out << table_ul_row_begin + << table_hheader20_tmpl.arg(tr("Interface")) + << table_hheader20_tmpl.arg(tr("Dropped packets")) + << table_hheader20_tmpl.arg(tr("Capture filter")) + << table_hheader20_tmpl.arg(tr("Link type")) + << table_hheader20_tmpl.arg(tr("Packet size limit (snaplen)")) + << table_row_end; + } + + for (guint i = 0; i < summary.ifaces->len; i++) { + iface_summary_info iface; + iface = g_array_index(summary.ifaces, iface_summary_info, i); + + /* interface */ + QString interface_name(unknown); + if (iface.descr) { + interface_name = iface.descr; + } else if (iface.name) { + interface_name = iface.name; + } + + /* Dropped count */ + QString interface_drops(unknown); + if (iface.drops_known) { + interface_drops = QString("%1 (%2%)").arg(iface.drops).arg(QString::number( + /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */ + summary.packet_count ? (100.0 * (gint64)iface.drops)/summary.packet_count : 0, 'f', 1)); + } + + /* Capture filter */ + QString interface_cfilter(unknown); + if (iface.cfilter && iface.cfilter[0] != '\0') { + interface_cfilter = iface.cfilter; + } else if (iface.name) { + interface_cfilter = QString(tr("none")); + } + + QString interface_snaplen = QString(tr("%1 bytes").arg(iface.snap)); + + out << table_row_begin + << table_data_tmpl.arg(interface_name) + << table_data_tmpl.arg(interface_drops) + << table_data_tmpl.arg(interface_cfilter) + << table_data_tmpl.arg(wtap_encap_description(iface.encap_type)) + << table_data_tmpl.arg(interface_snaplen) + << table_row_end; + } + if (summary.ifaces->len > 0) { + out << table_end; + } + } + + // Statistics Section + out << section_tmpl_.arg(tr("Statistics")); + out << table_begin; + + out << table_ul_row_begin + << table_hheader25_tmpl.arg(tr("Measurement")) + << table_hheader25_tmpl.arg(tr("Captured")) + << table_hheader25_tmpl.arg(tr("Displayed")) + << table_hheader25_tmpl.arg(tr("Marked")) + << table_row_end; + + QString n_a = UTF8_EM_DASH; + QString captured_str, displayed_str, marked_str; + + // Packets + displayed_str = marked_str = n_a; + if (summary.filtered_count > 0 && summary.packet_count > 0) { + displayed_str = QString("%1 (%2%)") + .arg(summary.filtered_count) + .arg(100.0 * summary.filtered_count / summary.packet_count, 1, 'f', 1); + } + if (summary.packet_count > 0 && summary.marked_count > 0) { + marked_str = QString("%1 (%2%)") + .arg(summary.marked_count) + .arg(100.0 * summary.marked_count / summary.packet_count, 1, 'f', 1); + } + + out << table_row_begin + << table_data_tmpl.arg(tr("Packets")) + << table_data_tmpl.arg(summary.packet_count) + << table_data_tmpl.arg(displayed_str) + << table_data_tmpl.arg(marked_str) + << table_row_end; + + // Time between first and last + captured_str = displayed_str = marked_str = n_a; + if (seconds > 0) { + captured_str = QString("%1").arg(seconds, 1, 'f', 3); + } + if (disp_seconds > 0) { + displayed_str = QString("%1").arg(disp_seconds, 1, 'f', 3); + } + if (marked_seconds > 0) { + marked_str = QString("%1").arg(marked_seconds, 1, 'f', 3); + } + out << table_row_begin + << table_data_tmpl.arg(tr("Time span, s")) + << table_data_tmpl.arg(captured_str) + << table_data_tmpl.arg(displayed_str) + << table_data_tmpl.arg(marked_str) + << table_row_end; + + // Average packets per second + captured_str = displayed_str = marked_str = n_a; + if (seconds > 0) { + captured_str = QString("%1").arg(summary.packet_count/seconds, 1, 'f', 1); + } + if (disp_seconds > 0) { + displayed_str = QString("%1").arg(summary.filtered_count/disp_seconds, 1, 'f', 1); + } + if (marked_seconds > 0) { + marked_str = QString("%1").arg(summary.marked_count/marked_seconds, 1, 'f', 1); + } + out << table_row_begin + << table_data_tmpl.arg(tr("Average pps")) + << table_data_tmpl.arg(captured_str) + << table_data_tmpl.arg(displayed_str) + << table_data_tmpl.arg(marked_str) + << table_row_end; + + // Average packet size + captured_str = displayed_str = marked_str = n_a; + if (summary.packet_count > 0) { + captured_str = QString::number((guint64) ((double)summary.bytes/summary.packet_count + 0.5)); + } + if (summary.filtered_count > 0) { + displayed_str = QString::number((guint64) ((double)summary.filtered_bytes/summary.filtered_count + 0.5)); + } + if (summary.marked_count > 0) { + marked_str = QString::number((guint64) ((double)summary.marked_bytes/summary.marked_count + 0.5)); + } + out << table_row_begin + << table_data_tmpl.arg(tr("Average packet size, B")) + << table_data_tmpl.arg(captured_str) + << table_data_tmpl.arg(displayed_str) + << table_data_tmpl.arg(marked_str) + << table_row_end; + + // Byte count + displayed_str = marked_str = "0"; + if (summary.bytes > 0 && summary.filtered_bytes > 0) { + displayed_str = QString("%1 (%2%)") + .arg(summary.filtered_bytes) + .arg(100.0 * summary.filtered_bytes / summary.bytes, 1, 'f', 1); + } + if (summary.bytes > 0 && summary.marked_bytes > 0) { + marked_str = QString("%1 (%2%)") + .arg(summary.marked_bytes) + .arg(100.0 * summary.marked_bytes / summary.bytes, 1, 'f', 1); + } + out << table_row_begin + << table_data_tmpl.arg(tr("Bytes")) + << table_data_tmpl.arg(summary.bytes) + << table_data_tmpl.arg(displayed_str) + << table_data_tmpl.arg(marked_str) + << table_row_end; + + // Bytes per second + captured_str = displayed_str = marked_str = n_a; + if (seconds > 0) { + captured_str = + gchar_free_to_qstring(format_size(summary.bytes / seconds, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)); + } + if (disp_seconds > 0) { + displayed_str = + gchar_free_to_qstring(format_size(summary.filtered_bytes / disp_seconds, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)); + } + if (marked_seconds > 0) { + marked_str = + gchar_free_to_qstring(format_size(summary.marked_bytes / marked_seconds, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)); + } + out << table_row_begin + << table_data_tmpl.arg(tr("Average bytes/s")) + << table_data_tmpl.arg(captured_str) + << table_data_tmpl.arg(displayed_str) + << table_data_tmpl.arg(marked_str) + << table_row_end; + + // Bits per second + captured_str = displayed_str = marked_str = n_a; + if (seconds > 0) { + captured_str = + gchar_free_to_qstring(format_size(summary.bytes * 8 / seconds, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)); + } + if (disp_seconds > 0) { + displayed_str = + gchar_free_to_qstring(format_size(summary.filtered_bytes * 8 / disp_seconds, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)); + } + if (marked_seconds > 0) { + marked_str = + gchar_free_to_qstring(format_size(summary.marked_bytes * 8 / marked_seconds, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)); + } + out << table_row_begin + << table_data_tmpl.arg(tr("Average bits/s")) + << table_data_tmpl.arg(captured_str) + << table_data_tmpl.arg(displayed_str) + << table_data_tmpl.arg(marked_str) + << table_row_end; + + out << table_end; + + return summary_str; +} + +void CaptureFilePropertiesDialog::fillDetails() +{ + if (!cap_file_.isValid()) return; + + ui->detailsTextEdit->clear(); + + QTextCursor cursor = ui->detailsTextEdit->textCursor(); + QString summary = summaryToHtml(); + cursor.insertHtml(summary); + cursor.insertBlock(); // Work around rendering oddity. + + // XXX - this just shows the first comment in the first section; + // add support for multiple sections with multiple comments. + wtap_block_t shb = wtap_file_get_shb(cap_file_.capFile()->provider.wth, 0); + char *shb_comment; + if (wtap_block_get_nth_string_option_value(shb, OPT_COMMENT, 0, + &shb_comment) == WTAP_OPTTYPE_SUCCESS) { + QString section_comment = shb_comment; + QString section_comment_html; + + if (!section_comment.isEmpty()) { + QString comment_escaped = html_escape(section_comment).replace('\n', "
"); + section_comment_html += section_tmpl_.arg(QString(tr("Section Comment"))); + section_comment_html += para_tmpl_.arg(comment_escaped); + + cursor.insertBlock(); + cursor.insertHtml(section_comment_html); + } + } + + if (cap_file_.capFile()->packet_comment_count > 0) { + cursor.insertBlock(); + cursor.insertHtml(section_tmpl_.arg(tr("Packet Comments"))); + + for (guint32 framenum = 1; framenum <= cap_file_.capFile()->count ; framenum++) { + frame_data *fdata = frame_data_sequence_find(cap_file_.capFile()->provider.frames, framenum); + wtap_block_t pkt_block = cf_get_packet_block(cap_file_.capFile(), fdata); + + if (pkt_block) { + guint n_comments = wtap_block_count_option(pkt_block, OPT_COMMENT); + for (guint i = 0; i < n_comments; i++) { + char *comment_text; + if (WTAP_OPTTYPE_SUCCESS == wtap_block_get_nth_string_option_value(pkt_block, OPT_COMMENT, i, &comment_text)) { + QString frame_comment_html = tr("

Frame %1: ").arg(framenum); + QString raw_comment = comment_text; + + frame_comment_html += html_escape(raw_comment).replace('\n', "
"); + frame_comment_html += "

\n"; + cursor.insertBlock(); + cursor.insertHtml(frame_comment_html); + } + } + } + wtap_block_unref(pkt_block); + } + } + ui->detailsTextEdit->verticalScrollBar()->setValue(0); +} + +void CaptureFilePropertiesDialog::changeEvent(QEvent* event) +{ + if (event != nullptr) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + updateWidgets(); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + +void CaptureFilePropertiesDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_STATS_SUMMARY_DIALOG); +} + +void CaptureFilePropertiesDialog::on_buttonBox_accepted() +{ + if (file_closed_ || !cap_file_.capFile()->filename) { + return; + } + + if (wtap_dump_can_write(cap_file_.capFile()->linktypes, WTAP_COMMENT_PER_SECTION)) + { + gchar *str = qstring_strdup(ui->commentsTextEdit->toPlainText()); + + /* + * Make sure this would fit in a pcapng option. + * + * XXX - 65535 is the maximum size for an option in pcapng; + * what if another capture file format supports larger + * comments? + */ + if (strlen(str) > 65535) { + /* It doesn't fit. Tell the user and give up. */ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "That comment is too large to save in a capture file."); + return; + } + cf_update_section_comment(cap_file_.capFile(), str); + emit captureCommentChanged(); + fillDetails(); + } +} + +void CaptureFilePropertiesDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) { + QClipboard *clipboard = QApplication::clipboard(); + QString details = tr("Created by Wireshark %1\n\n").arg(get_ws_vcs_version_info()); + details.append(ui->detailsTextEdit->toPlainText()); + clipboard->setText(details); + } else if (button == ui->buttonBox->button(QDialogButtonBox::Reset)) { + updateWidgets(); + } +} + +void CaptureFilePropertiesDialog::on_buttonBox_rejected() +{ + reject(); +} diff --git a/ui/qt/capture_file_properties_dialog.h b/ui/qt/capture_file_properties_dialog.h new file mode 100644 index 00000000..dd1698a6 --- /dev/null +++ b/ui/qt/capture_file_properties_dialog.h @@ -0,0 +1,71 @@ +/** @file + * + * GSoC 2013 - QtShark + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_FILE_PROPERTIES_DIALOG_H +#define CAPTURE_FILE_PROPERTIES_DIALOG_H + +#include + +#include + +#include +#include + +#include +#include + +#include "file.h" + +#ifdef HAVE_LIBPCAP + #include "ui/capture.h" + #include "ui/capture_globals.h" +#endif + +#include "wireshark_dialog.h" + +#include + +namespace Ui { +class CaptureFilePropertiesDialog; +} + +class QAbstractButton; + +class CaptureFilePropertiesDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit CaptureFilePropertiesDialog(QWidget &parent, CaptureFile& capture_file); + ~CaptureFilePropertiesDialog(); + +signals: + void captureCommentChanged(); + +protected slots: + void changeEvent(QEvent* event); + + +private: + Ui::CaptureFilePropertiesDialog *ui; + + QString summaryToHtml(); + void fillDetails(); + +private slots: + void updateWidgets(); + void on_buttonBox_helpRequested(); + void on_buttonBox_accepted(); + void on_buttonBox_clicked(QAbstractButton *button); + void on_buttonBox_rejected(); +}; + +#endif diff --git a/ui/qt/capture_file_properties_dialog.ui b/ui/qt/capture_file_properties_dialog.ui new file mode 100644 index 00000000..bf5cd634 --- /dev/null +++ b/ui/qt/capture_file_properties_dialog.ui @@ -0,0 +1,86 @@ + + + CaptureFilePropertiesDialog + + + + 0 + 0 + 799 + 585 + + + + + 0 + 0 + + + + + + + + + + Qt::Vertical + + + false + + + false + + + + + + + Details + + + + + + + true + + + + + + + + + + + Capture file comments + + + + + + + + 0 + 10 + + + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Reset|QDialogButtonBox::Save + + + + + + + + diff --git a/ui/qt/capture_filter_syntax_worker.cpp b/ui/qt/capture_filter_syntax_worker.cpp new file mode 100644 index 00000000..89ef981c --- /dev/null +++ b/ui/qt/capture_filter_syntax_worker.cpp @@ -0,0 +1,147 @@ +/* capture_filter_syntax_worker.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#ifdef HAVE_LIBPCAP +#include + +#ifdef __MINGW32__ +#include <_bsd_types.h> +#endif +#include + +#include "capture_opts.h" +#include "ui/capture_globals.h" +#endif +#include "extcap.h" + +#include "capture_filter_syntax_worker.h" +#include + +#include +#include + +// We use a global mutex to protect pcap_compile since it calls gethostbyname. +// This probably isn't needed on Windows (where pcap_comple calls +// EnterCriticalSection + LeaveCriticalSection) or *BSD or macOS where +// gethostbyname(3) claims that it's thread safe. +static QMutex pcap_compile_mtx_; + +#if 0 +#include +#include +#define DEBUG_SYNTAX_CHECK(state1, state2) qDebug() << "CF state" << QThread::currentThreadId() << state1 << "->" << state2 << ":" << filter +#define DEBUG_SLEEP_TIME 5000 // ms +#else +#define DEBUG_SYNTAX_CHECK(state1, state2) +#define DEBUG_SLEEP_TIME 0 // ms +#endif + +#define DUMMY_SNAPLENGTH 65535 +#define DUMMY_NETMASK 0xFF000000 + +void CaptureFilterSyntaxWorker::checkFilter(const QString filter) +{ +#ifdef HAVE_LIBPCAP + QSet active_dlts; + QSet active_extcap; + struct bpf_program fcode; + pcap_t *pd; + int pc_err; + enum SyntaxLineEdit::SyntaxState state = SyntaxLineEdit::Valid; + QString err_str; + + DEBUG_SYNTAX_CHECK("received", "?"); + + if (global_capture_opts.num_selected < 1) { + emit syntaxResult(filter, SyntaxLineEdit::Invalid, QString("No interfaces selected")); + DEBUG_SYNTAX_CHECK("unknown", "no interfaces"); + return; + } + + for (guint if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++) { + interface_t *device; + + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx); + if (device->selected) { + if (device->if_info.extcap == NULL || strlen(device->if_info.extcap) == 0) { + if ((device->active_dlt >= DLT_USER0 && device->active_dlt <= DLT_USER15) || device->active_dlt == -1) { + // Capture filter for DLT_USER is unknown + state = SyntaxLineEdit::Deprecated; + err_str = "Unable to check capture filter"; + } else { + active_dlts.insert(device->active_dlt); + } + } else { + active_extcap.insert(if_idx); + } + } + } + + foreach(gint dlt, active_dlts.values()) { + pcap_compile_mtx_.lock(); + pd = pcap_open_dead(dlt, DUMMY_SNAPLENGTH); + if (pd == NULL) + { + //don't have ability to verify capture filter + break; + } +#ifdef PCAP_NETMASK_UNKNOWN + pc_err = pcap_compile(pd, &fcode, filter.toUtf8().data(), 1 /* Do optimize */, PCAP_NETMASK_UNKNOWN); +#else + pc_err = pcap_compile(pd, &fcode, filter.toUtf8().data(), 1 /* Do optimize */, 0); +#endif + +#if DEBUG_SLEEP_TIME > 0 + QThread::msleep(DEBUG_SLEEP_TIME); +#endif + + if (pc_err) { + DEBUG_SYNTAX_CHECK("unknown", "known bad"); + state = SyntaxLineEdit::Invalid; + err_str = pcap_geterr(pd); + } else { + DEBUG_SYNTAX_CHECK("unknown", "known good"); + } + pcap_close(pd); + + pcap_compile_mtx_.unlock(); + + if (state == SyntaxLineEdit::Invalid) break; + } + // If it's already invalid, don't bother to check extcap + if (state != SyntaxLineEdit::Invalid) { + foreach(guint extcapif, active_extcap.values()) { + interface_t *device; + gchar *error = NULL; + + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, extcapif); + extcap_filter_status status = extcap_verify_capture_filter(device->name, filter.toUtf8().constData(), &error); + if (status == EXTCAP_FILTER_VALID) { + DEBUG_SYNTAX_CHECK("unknown", "known good"); + } else if (status == EXTCAP_FILTER_INVALID) { + DEBUG_SYNTAX_CHECK("unknown", "known bad"); + state = SyntaxLineEdit::Invalid; + err_str = error; + break; + } else { + state = SyntaxLineEdit::Deprecated; + err_str = "Unable to check capture filter"; + } + g_free(error); + } + } + emit syntaxResult(filter, state, err_str); + + DEBUG_SYNTAX_CHECK("known", "idle"); +#else + emit syntaxResult(filter, SyntaxLineEdit::Deprecated, QString("Syntax checking unavailable")); +#endif // HAVE_LIBPCAP +} diff --git a/ui/qt/capture_filter_syntax_worker.h b/ui/qt/capture_filter_syntax_worker.h new file mode 100644 index 00000000..51085124 --- /dev/null +++ b/ui/qt/capture_filter_syntax_worker.h @@ -0,0 +1,31 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_FILTER_SYNTAX_WORKER_H +#define CAPTURE_FILTER_SYNTAX_WORKER_H + +#include +#include +#include + +class CaptureFilterSyntaxWorker : public QObject +{ + Q_OBJECT + +public: + CaptureFilterSyntaxWorker(QObject *parent = 0) : QObject(parent) {} + +public slots: + void checkFilter(const QString filter); + +signals: + void syntaxResult(QString filter, int state, QString err_msg); +}; + +#endif // CAPTURE_FILTER_SYNTAX_WORKER_H diff --git a/ui/qt/capture_info_dialog.cpp b/ui/qt/capture_info_dialog.cpp new file mode 100644 index 00000000..5a29652c --- /dev/null +++ b/ui/qt/capture_info_dialog.cpp @@ -0,0 +1,205 @@ +/* capture_info_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "wireshark.h" + +#include "ui/capture_info.h" + +#include "epan/capture_dissectors.h" +#include "epan/proto.h" + +#include "ui/capture.h" + +#include "capture_info_dialog.h" +#include "ui_capture_info_dialog.h" + +#include "main_application.h" + +#include "ui/qt/models/sparkline_delegate.h" + +#include "utils/qt_ui_utils.h" + +#include +#include + +// The GTK+ version of this dialog showed a list of protocols and a simple bar graph +// (progress bars) showing their portion of the total number of packets. We show a +// a time series for each protocol using a sparkline. If we wanted to show bar graphs +// instead we could do so using QProgressBars or using PercentBarDelegates. + +extern "C" { + +// Callbacks defined in ui/capture_info.h. + +/* create the capture info dialog */ +/* will keep pointers to the fields in the counts parameter */ +void capture_info_ui_create( +capture_info *cinfo, +capture_session *cap_session) +{ + // cinfo->ui should have three values: + // - The main window, set in MainWindow::startCapture. + // - This dialog, set below. + // - NULL, set in our destructor. + + if (!cinfo || !cinfo->ui) return; + if (!cap_session) return; + QMainWindow *main_window = qobject_cast((QObject *)cinfo->ui); + if (!main_window) return; + + // ...and we take it over from here. + CaptureInfoDialog *ci_dlg = new CaptureInfoDialog(cinfo, cap_session, main_window); + cinfo->ui = ci_dlg; + ci_dlg->show(); +} + +/* update the capture info dialog */ +/* As this function is a bit time critical while capturing, */ +/* prepare everything possible in the capture_info_ui_create() function above! */ +void capture_info_ui_update( +capture_info *cinfo) +{ + CaptureInfoDialog *ci_dlg = qobject_cast((QObject *)cinfo->ui); + if (!ci_dlg) return; + ci_dlg->updateInfo(); +} + +/* destroy the capture info dialog again */ +void capture_info_ui_destroy( +capture_info *cinfo) +{ + CaptureInfoDialog *ci_dlg = qobject_cast((QObject *)cinfo->ui); + if (!ci_dlg) return; + cinfo->ui = NULL; + delete ci_dlg; +} + +} // extern "C" + +CaptureInfoDialog::CaptureInfoDialog(struct _capture_info *cap_info, struct _capture_session *cap_session, QWidget *parent) : + GeometryStateDialog(parent), + ui(new Ui::CaptureInfoDialog), + cap_info_(cap_info), + cap_session_(cap_session) +{ + ui->setupUi(this); + loadGeometry(); + setWindowTitle(mainApp->windowTitleString(tr("Capture Information"))); + + QPushButton *button = ui->buttonBox->button(QDialogButtonBox::Abort); + button->setText(tr("Stop Capture")); + connect(button, &QPushButton::clicked, this, &CaptureInfoDialog::stopCapture); + + ci_model_ = new CaptureInfoModel(cap_info, this); + ui->treeView->setModel(ci_model_); + + ui->treeView->setItemDelegateForColumn(1, new SparkLineDelegate(this)); + + duration_.start(); +} + +CaptureInfoDialog::~CaptureInfoDialog() +{ + delete ui; + cap_info_->ui = NULL; +} + +void CaptureInfoDialog::updateInfo() +{ + int secs = int(duration_.elapsed() / 1000); + QString duration = tr("%1 packets, %2:%3:%4") + .arg(cap_info_->counts->total) + .arg(secs / 3600, 2, 10, QChar('0')) + .arg(secs % 3600 / 60, 2, 10, QChar('0')) + .arg(secs % 60, 2, 10, QChar('0')); + ui->infoLabel->setText(duration); + + ci_model_->updateInfo(); + ui->treeView->resizeColumnToContents(0); +} + +void CaptureInfoDialog::stopCapture() +{ +#ifdef HAVE_LIBPCAP + capture_stop(cap_session_); // ...or we could connect to MainWindow::stopCapture. +#endif // HAVE_LIBPCAP +} + +CaptureInfoModel::CaptureInfoModel(struct _capture_info *cap_info, QObject *parent) : + QAbstractTableModel(parent), + cap_info_(cap_info), + samples_(0), + last_other_(0) +{ +} + +void CaptureInfoModel::updateInfo() +{ + if (!cap_info_) return; + + GHashTableIter iter; + gpointer key, value; + + samples_++; + other_points_.append(cap_info_->counts->other - last_other_); + last_other_ = cap_info_->counts->other; + + g_hash_table_iter_init (&iter, cap_info_->counts->counts_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + int proto_id = GPOINTER_TO_INT(key); + int cur_count = (int) capture_dissector_get_count(cap_info_->counts, proto_id); + if (!points_.contains(proto_id)) { + emit beginInsertRows(QModelIndex(), rowCount(), rowCount()); + QVector zeroes = QVector(samples_, 0); + points_[proto_id] = zeroes.toList(); + last_count_[proto_id] = 0; + emit endInsertRows(); + } else { + points_[proto_id].append(cur_count - last_count_[proto_id]); + last_count_[proto_id] = cur_count; + } + } + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); +} + +int CaptureInfoModel::rowCount(const QModelIndex &) const +{ + if (!cap_info_) return 0; + return static_cast(points_.keys().size()) + 1; +} + +int CaptureInfoModel::columnCount(const QModelIndex &) const +{ + return 2; +} + +QVariant CaptureInfoModel::data(const QModelIndex &index, int role) const +{ + QList proto_ids = points_.keys(); + int row = index.row(); + + if (role == Qt::DisplayRole && index.column() == 0) { + if (row < proto_ids.size()) { + int proto_id = proto_ids.at(row); + return QString(proto_get_protocol_short_name(find_protocol_by_id(proto_id))); + } else { + return tr("Other"); + } + } else if (role == Qt::UserRole && index.column() == 1) { + if (row < proto_ids.size()) { + int proto_id = proto_ids.at(row); + return QVariant::fromValue(points_[proto_id]); + } else { + return QVariant::fromValue(other_points_); + } + } + return QVariant(); +} diff --git a/ui/qt/capture_info_dialog.h b/ui/qt/capture_info_dialog.h new file mode 100644 index 00000000..da30d04e --- /dev/null +++ b/ui/qt/capture_info_dialog.h @@ -0,0 +1,72 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_INFO_DIALOG_H +#define CAPTURE_INFO_DIALOG_H + +#include "geometry_state_dialog.h" + +#include +#include + +struct _capture_info; +struct _capture_session; + +namespace Ui { +class CaptureInfoDialog; +} + +class CaptureInfoModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit CaptureInfoModel(struct _capture_info *cap_info, QObject * parent = Q_NULLPTR); + virtual ~CaptureInfoModel() {} + void updateInfo(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; +// virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + +private: + struct _capture_info *cap_info_; + int samples_; + QMap last_count_; + QMap > points_; + int last_other_; + QList other_points_; +}; + +class CaptureInfoDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit CaptureInfoDialog(struct _capture_info *cap_info, struct _capture_session *cap_session, QWidget *parent = 0); + ~CaptureInfoDialog(); + void updateInfo(void); + +signals: + +public slots: + +private slots: + void stopCapture(); + +private: + Ui::CaptureInfoDialog *ui; + struct _capture_info *cap_info_; + struct _capture_session *cap_session_; + CaptureInfoModel *ci_model_; + QElapsedTimer duration_; +}; + +#endif // CAPTURE_INFO_DIALOG_H diff --git a/ui/qt/capture_info_dialog.ui b/ui/qt/capture_info_dialog.ui new file mode 100644 index 00000000..1765feff --- /dev/null +++ b/ui/qt/capture_info_dialog.ui @@ -0,0 +1,90 @@ + + + CaptureInfoDialog + + + + 0 + 0 + 400 + 275 + + + + true + + + + + + false + + + true + + + false + + + false + + + false + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Abort|QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + CaptureInfoDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CaptureInfoDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/capture_options_dialog.cpp b/ui/qt/capture_options_dialog.cpp new file mode 100644 index 00000000..a0aed791 --- /dev/null +++ b/ui/qt/capture_options_dialog.cpp @@ -0,0 +1,1534 @@ +/* capture_options_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include "capture_options_dialog.h" +#include +#include +#include "compiled_filter_output.h" +#include "manage_interfaces_dialog.h" + +#include "main_application.h" + +#include "extcap.h" + +#ifdef HAVE_LIBPCAP + +#include +#include +#include + +#include "ringbuffer.h" +#include "ui/capture_ui_utils.h" +#include "ui/capture_globals.h" +#include "ui/iface_lists.h" +#include "ui/file_dialog.h" + +#include "ui/ws_ui_util.h" +#include "ui/util.h" +#include +#include "ui/preference_utils.h" +#include "ui/recent.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" + +// To do: +// - Set a size hint for item delegates. +// - Make promiscuous and monitor mode checkboxes. +// - Fix InterfaceTreeDelegate method names. +// - You can edit filters via the main CaptureFilterCombo and via each +// individual interface row. We should probably do one or the other. +// - There might be a point in having the separate combo boxes in the +// individual interface row, if their CaptureFilterCombos actually +// called recent_get_cfilter_list with the interface name to get the +// separate list of recent capture filters for that interface, but +// they don't. + +const int stat_update_interval_ = 1000; // ms + +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE +#define SHOW_BUFFER_COLUMN 1 +#endif + +#if defined(HAVE_PCAP_CREATE) +#define SHOW_MONITOR_COLUMN 1 +#endif + +/* + * Symbolic names for column indices. + */ +enum +{ + col_extcap_ = 0, + col_interface_, + col_traffic_, + col_link_, + col_pmode_, + col_snaplen_, + col_buffer_, + col_monitor_, + col_filter_, + col_num_columns_ +}; + +static interface_t *find_device_by_if_name(const QString &interface_name) +{ + interface_t *device; + guint i; + for (i = 0; i < global_capture_opts.all_ifaces->len; i++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (!interface_name.compare(device->display_name) && !device->hidden && device->type != IF_PIPE) { + return device; + } + } + return NULL; +} + +class InterfaceTreeWidgetItem : public QTreeWidgetItem +{ +public: + InterfaceTreeWidgetItem(QTreeWidget *tree) : QTreeWidgetItem(tree) {} + bool operator< (const QTreeWidgetItem &other) const; + QVariant data(int column, int role) const; + void setData(int column, int role, const QVariant &value); + QList points; + + void updateInterfaceColumns(interface_t *device) + { + if (!device) return; + + // Prevent infinite recursive signal loop + // itemChanged->interfaceItemChanged->updateInterfaceColumns + treeWidget()->blockSignals(true); + QString default_str = QObject::tr("default"); + + // XXX - this is duplicated in InterfaceTreeModel::data; + // it should be done in common code somewhere. + QString linkname; + if (device->active_dlt == -1) + linkname = "Unknown"; + else { + linkname = QObject::tr("DLT %1").arg(device->active_dlt); + for (GList *list = device->links; list != NULL; list = gxx_list_next(list)) { + link_row *linkr = gxx_list_data(link_row *, list); + if (linkr->dlt == device->active_dlt) { + linkname = linkr->name; + break; + } + } + } + setText(col_link_, linkname); + + if (device->if_info.type == IF_EXTCAP) { + /* extcap interfaces does not have this settings */ + setApplicable(col_pmode_, false); + + setApplicable(col_snaplen_, false); +#ifdef SHOW_BUFFER_COLUMN + setApplicable(col_buffer_, false); +#endif + } else { + setApplicable(col_pmode_, true); + setCheckState(col_pmode_, device->pmode ? Qt::Checked : Qt::Unchecked); + + QString snaplen_string = device->has_snaplen ? QString::number(device->snaplen) : default_str; + setText(col_snaplen_, snaplen_string); +#ifdef SHOW_BUFFER_COLUMN + setText(col_buffer_, QString::number(device->buffer)); +#endif + } + setText(col_filter_, device->cfilter); + +#ifdef SHOW_MONITOR_COLUMN + if (device->monitor_mode_supported) { + setApplicable(col_monitor_, true); + setCheckState(col_monitor_, device->monitor_mode_enabled ? Qt::Checked : Qt::Unchecked); + } else { + setApplicable(col_monitor_, false); + } +#endif + treeWidget()->blockSignals(false); + } + + void setApplicable(int column, bool applicable = false) { + QPalette palette = mainApp->palette(); + + if (applicable) { + setText(column, QString()); + } else { + setData(column, Qt::CheckStateRole, QVariant()); + palette.setCurrentColorGroup(QPalette::Disabled); + setText(column, UTF8_EM_DASH); + } + setForeground(column, palette.text().color()); + } + +}; + +CaptureOptionsDialog::CaptureOptionsDialog(QWidget *parent) : + GeometryStateDialog(parent), + ui(new Ui::CaptureOptionsDialog) +{ + ui->setupUi(this); + loadGeometry(); + setWindowTitle(mainApp->windowTitleString(tr("Capture Options"))); + + stat_timer_ = NULL; + stat_cache_ = NULL; + + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start")); + + // Start out with the list *not* sorted, so they show up in the order + // in which they were provided + ui->interfaceTree->sortByColumn(-1, Qt::AscendingOrder); + ui->interfaceTree->setItemDelegateForColumn(col_extcap_, &interface_item_delegate_); + ui->interfaceTree->setItemDelegateForColumn(col_interface_, &interface_item_delegate_); + ui->interfaceTree->setItemDelegateForColumn(col_traffic_, new SparkLineDelegate(this)); + ui->interfaceTree->setItemDelegateForColumn(col_link_, &interface_item_delegate_); + + ui->interfaceTree->setItemDelegateForColumn(col_snaplen_, &interface_item_delegate_); +#ifdef SHOW_BUFFER_COLUMN + ui->interfaceTree->setItemDelegateForColumn(col_buffer_, &interface_item_delegate_); +#else + ui->interfaceTree->setColumnHidden(col_buffer_, true); +#endif +#ifndef SHOW_MONITOR_COLUMN + ui->interfaceTree->setColumnHidden(col_monitor_, true); +#endif + ui->interfaceTree->setItemDelegateForColumn(col_filter_, &interface_item_delegate_); + + interface_item_delegate_.setTree(ui->interfaceTree); + + ui->filenameLineEdit->setPlaceholderText(tr("Leave blank to use a temporary file")); + + ui->rbCompressionNone->setChecked(true); + + ui->tempDirLineEdit->setPlaceholderText(g_get_tmp_dir()); + ui->tempDirLineEdit->setText(global_capture_opts.temp_dir); + + // Changes in interface selections or capture filters should be propagated + // to the main welcome screen where they will be applied to the global + // capture options. + connect(this, SIGNAL(interfacesChanged()), ui->captureFilterComboBox, SIGNAL(interfacesChanged())); + connect(ui->captureFilterComboBox, SIGNAL(captureFilterSyntaxChanged(bool)), this, SLOT(updateWidgets())); + connect(ui->captureFilterComboBox->lineEdit(), SIGNAL(textEdited(QString)), + this, SLOT(filterEdited())); + connect(ui->captureFilterComboBox->lineEdit(), SIGNAL(textEdited(QString)), + this, SIGNAL(captureFilterTextEdited(QString))); + connect(&interface_item_delegate_, SIGNAL(filterChanged(QString)), + ui->captureFilterComboBox->lineEdit(), SLOT(setText(QString))); + connect(&interface_item_delegate_, SIGNAL(filterChanged(QString)), + this, SIGNAL(captureFilterTextEdited(QString))); + connect(this, SIGNAL(ifsChanged()), this, SLOT(refreshInterfaceList())); + connect(mainApp, SIGNAL(localInterfaceListChanged()), this, SLOT(updateLocalInterfaces())); + connect(ui->browseButton, SIGNAL(clicked()), this, SLOT(browseButtonClicked())); + connect(ui->interfaceTree, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(itemClicked(QTreeWidgetItem*,int))); + connect(ui->interfaceTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem*,int))); + connect(ui->tempDirBrowseButton, SIGNAL(clicked()), this, SLOT(tempDirBrowseButtonClicked())); + + ui->tabWidget->setCurrentIndex(0); + + updateWidgets(); +} + +CaptureOptionsDialog::~CaptureOptionsDialog() +{ + delete ui; +} + +/* Update global device selections based on the TreeWidget selection. */ +void CaptureOptionsDialog::updateGlobalDeviceSelections() +{ +#ifdef HAVE_LIBPCAP + QTreeWidgetItemIterator iter(ui->interfaceTree); + + global_capture_opts.num_selected = 0; + + while (*iter) { + QString device_name = (*iter)->data(col_interface_, Qt::UserRole).value(); + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (device_name.compare(QString().fromUtf8(device->name)) == 0) { + if ((*iter)->isSelected()) { + device->selected = TRUE; + global_capture_opts.num_selected++; + } else { + device->selected = FALSE; + } + break; + } + } + ++iter; + } +#endif +} + +/* Update TreeWidget selection based on global device selections. */ +void CaptureOptionsDialog::updateFromGlobalDeviceSelections() +{ +#ifdef HAVE_LIBPCAP + QTreeWidgetItemIterator iter(ui->interfaceTree); + + // Prevent recursive interface interfaceSelected signals + ui->interfaceTree->blockSignals(true); + + while (*iter) { + QString device_name = (*iter)->data(col_interface_, Qt::UserRole).value(); + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (device_name.compare(QString().fromUtf8(device->name)) == 0) { + if ((bool)device->selected != (*iter)->isSelected()) { + (*iter)->setSelected(device->selected); + } + break; + } + } + ++iter; + } + + ui->interfaceTree->blockSignals(false); +#endif +} + +void CaptureOptionsDialog::interfaceSelected() +{ + if (sender() == ui->interfaceTree) { + // Local changes, propagate our changes + updateGlobalDeviceSelections(); + emit interfacesChanged(); + } else { + // Changes from the welcome screen, adjust to its state. + updateFromGlobalDeviceSelections(); + } + + updateSelectedFilter(); + + updateWidgets(); +} + +void CaptureOptionsDialog::filterEdited() +{ + QList si = ui->interfaceTree->selectedItems(); + + foreach (QTreeWidgetItem *ti, si) { + ti->setText(col_filter_, ui->captureFilterComboBox->lineEdit()->text()); + } + + if (si.count() > 0) { + QModelIndex col_filter_idx = ui->interfaceTree->model()->index(ui->interfaceTree->indexOfTopLevelItem(si[0]), col_filter_); + ui->interfaceTree->scrollTo(col_filter_idx); + } +} + +void CaptureOptionsDialog::updateWidgets() +{ + SyntaxLineEdit *sle = qobject_cast(ui->captureFilterComboBox->lineEdit()); + if (!sle) { + return; + } + + bool can_capture = false; + + if (ui->interfaceTree->selectedItems().count() > 0 && sle->syntaxState() != SyntaxLineEdit::Invalid) { + can_capture = true; + } + + ui->compileBPF->setEnabled(can_capture); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(can_capture); +} + +void CaptureOptionsDialog::on_capturePromModeCheckBox_toggled(bool checked) +{ + interface_t *device; + prefs.capture_prom_mode = checked; + for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) { + InterfaceTreeWidgetItem *ti = dynamic_cast(ui->interfaceTree->topLevelItem(row)); + if (!ti) continue; + + QString device_name = ti->data(col_interface_, Qt::UserRole).toString(); + device = getDeviceByName(device_name); + if (!device) continue; + device->pmode = checked; + ti->updateInterfaceColumns(device); + } +} + +void CaptureOptionsDialog::browseButtonClicked() +{ + const char *open_dir = NULL; + + switch (prefs.gui_fileopen_style) { + + case FO_STYLE_LAST_OPENED: + open_dir = get_open_dialog_initial_dir(); + break; + + case FO_STYLE_SPECIFIED: + if (prefs.gui_fileopen_dir[0] != '\0') + open_dir = prefs.gui_fileopen_dir; + break; + } + QString file_name = WiresharkFileDialog::getSaveFileName(this, tr("Specify a Capture File"), open_dir); + ui->filenameLineEdit->setText(file_name); +} + +void CaptureOptionsDialog::tempDirBrowseButtonClicked() +{ + QString specified_dir = WiresharkFileDialog::getExistingDirectory(this, tr("Specify temporary directory")); + ui->tempDirLineEdit->setText(specified_dir); +} + +void CaptureOptionsDialog::interfaceItemChanged(QTreeWidgetItem *item, int column) +{ + QWidget* editor = ui->interfaceTree->indexWidget(ui->interfaceTree->currentIndex()); + if (editor) { + ui->interfaceTree->closePersistentEditor(item, ui->interfaceTree->currentColumn()); + } + + InterfaceTreeWidgetItem *ti = dynamic_cast(item); + if (!ti) return; + + interface_t *device; + QString interface_name = ti->text(col_interface_); + device = find_device_by_if_name(interface_name); + if (!device) return; + + switch(column) { + + case col_pmode_: + device->pmode = item->checkState(col_pmode_) == Qt::Checked ? TRUE : FALSE; + ti->updateInterfaceColumns(device); + break; + +#ifdef SHOW_MONITOR_COLUMN + case col_monitor_: + { + gboolean monitor_mode = FALSE; + if (ti->checkState(col_monitor_) == Qt::Checked) monitor_mode = TRUE; + + if_capabilities_t *caps; + char *auth_str = NULL; + QString active_dlt_name; + + set_active_dlt(device, global_capture_opts.default_options.linktype); + + #ifdef HAVE_PCAP_REMOTE + if (device->remote_opts.remote_host_opts.auth_type == CAPTURE_AUTH_PWD) { + auth_str = ws_strdup_printf("%s:%s", device->remote_opts.remote_host_opts.auth_username, + device->remote_opts.remote_host_opts.auth_password); + } + #endif + caps = capture_get_if_capabilities(device->name, monitor_mode, auth_str, NULL, NULL, main_window_update); + g_free(auth_str); + + if (caps != Q_NULLPTR) { + + for (int i = static_cast(g_list_length(device->links)) - 1; i >= 0; i--) { + GList* rem = g_list_nth(device->links, static_cast(i)); + device->links = g_list_remove_link(device->links, rem); + g_list_free_1(rem); + } + device->active_dlt = -1; + device->monitor_mode_supported = caps->can_set_rfmon; + device->monitor_mode_enabled = monitor_mode; + + for (GList *lt_entry = caps->data_link_types; lt_entry != Q_NULLPTR; lt_entry = gxx_list_next(lt_entry)) { + link_row *linkr = new link_row(); + data_link_info_t *data_link_info = gxx_list_data(data_link_info_t *, lt_entry); + /* + * For link-layer types libpcap/WinPcap/Npcap doesn't know + * about, the name will be "DLT n", and the description will + * be null. + * We mark those as unsupported, and don't allow them to be + * used - capture filters won't work on them, for example. + */ + if (data_link_info->description != Q_NULLPTR) { + linkr->dlt = data_link_info->dlt; + if (active_dlt_name.isEmpty()) { + device->active_dlt = data_link_info->dlt; + active_dlt_name = data_link_info->description; + } + linkr->name = g_strdup(data_link_info->description); + } else { + gchar *str; + /* XXX - should we just omit them? */ + str = ws_strdup_printf("%s (not supported)", data_link_info->name); + linkr->dlt = -1; + linkr->name = g_strdup(str); + g_free(str); + } + device->links = g_list_append(device->links, linkr); + } + free_if_capabilities(caps); + } else { + /* We don't know whether this supports monitor mode or not; + don't ask for monitor mode. */ + device->monitor_mode_enabled = FALSE; + device->monitor_mode_supported = FALSE; + } + + ti->updateInterfaceColumns(device); + + break; + } +#endif // SHOW_MONITOR_COLUMN + default: + break; + } +} + +void CaptureOptionsDialog::itemClicked(QTreeWidgetItem *item, int column) +{ + InterfaceTreeWidgetItem *ti = dynamic_cast(item); + if (!ti) return; + +#ifdef HAVE_LIBPCAP + interface_t *device; + QString interface_name = ti->text(col_interface_); + device = find_device_by_if_name(interface_name); + if (!device) return; + + switch(column) { + + case col_extcap_: + if (device->if_info.type == IF_EXTCAP) { + /* this checks if configuration is required and not yet provided or saved via prefs */ + QString device_name = ti->data(col_extcap_, Qt::UserRole).value(); + if (extcap_has_configuration((const char *)(device_name.toStdString().c_str()))) + { + emit showExtcapOptions(device_name, false); + return; + } + } + break; + + default: + break; + } +#endif /* HAVE_LIBPCAP */ +} + +void CaptureOptionsDialog::itemDoubleClicked(QTreeWidgetItem *item, int column) +{ + InterfaceTreeWidgetItem *ti = dynamic_cast(item); + if (!ti) return; + + switch(column) { + + // Double click starts capture just on columns which are not editable + case col_interface_: + case col_traffic_: + { +#ifdef HAVE_LIBPCAP + interface_t *device; + QString interface_name = ti->text(col_interface_); + device = find_device_by_if_name(interface_name); + if (!device) return; + + if (device->if_info.type == IF_EXTCAP) { + /* this checks if configuration is required and not yet provided or saved via prefs */ + QString device_name = ti->data(col_extcap_, Qt::UserRole).value(); + if (extcap_requires_configuration((const char *)(device_name.toStdString().c_str()))) + { + emit showExtcapOptions(device_name, true); + return; + } + } +#endif /* HAVE_LIBPCAP */ + emit startCapture(); + close(); + break; + } + + default: + break; + } +} + +void CaptureOptionsDialog::on_gbStopCaptureAuto_toggled(bool checked) +{ + global_capture_opts.has_file_interval = checked; +} + +void CaptureOptionsDialog::on_gbNewFileAuto_toggled(bool checked) +{ + global_capture_opts.multi_files_on = checked; + ui->stopMBCheckBox->setEnabled(checked?false:true); + ui->stopMBSpinBox->setEnabled(checked?false:true); + ui->stopMBComboBox->setEnabled(checked?false:true); + ui->gbCompression->setEnabled(checked); + ui->rbCompressionNone->setEnabled(checked); +#ifdef HAVE_ZLIB + ui->rbCompressionGzip->setEnabled(checked); +#else + ui->rbCompressionGzip->setEnabled(false); +#endif +} + +void CaptureOptionsDialog::on_cbUpdatePacketsRT_toggled(bool checked) +{ + global_capture_opts.real_time_mode = checked; +} + +void CaptureOptionsDialog::on_cbAutoScroll_toggled(bool checked) +{ + recent.capture_auto_scroll = checked; +} + +void CaptureOptionsDialog::on_cbExtraCaptureInfo_toggled(bool checked) +{ + global_capture_opts.show_info = checked; +} + +void CaptureOptionsDialog::on_cbResolveMacAddresses_toggled(bool checked) +{ + gbl_resolv_flags.mac_name = checked; +} + +void CaptureOptionsDialog::on_cbResolveNetworkNames_toggled(bool checked) +{ + gbl_resolv_flags.network_name = checked; +} + +void CaptureOptionsDialog::on_cbResolveTransportNames_toggled(bool checked) +{ + gbl_resolv_flags.transport_name = checked; +} + +void CaptureOptionsDialog::on_buttonBox_accepted() +{ + if (saveOptionsToPreferences()) { + +#ifdef HAVE_LIBPCAP + InterfaceTreeWidgetItem *ti = dynamic_cast(ui->interfaceTree->currentItem()); + if (ti) { + interface_t *device; + + QString interface_name = ti->text(col_interface_); + device = find_device_by_if_name(interface_name); + if (device && device->if_info.type == IF_EXTCAP) { + /* this checks if configuration is required and not yet provided or saved via prefs */ + QString device_name = ti->data(col_extcap_, Qt::UserRole).value(); + if (extcap_requires_configuration((const char *)(device_name.toStdString().c_str()))) + { + emit showExtcapOptions(device_name, true); + return; + } + } + } +#endif /* HAVE_LIBPCAP */ + + emit setFilterValid(true, ui->captureFilterComboBox->lineEdit()->text()); + accept(); + } +} + +// Not sure why we have to do this manually. +void CaptureOptionsDialog::on_buttonBox_rejected() +{ + if (saveOptionsToPreferences()) { + reject(); + } +} + +void CaptureOptionsDialog::on_buttonBox_helpRequested() +{ + // Probably the wrong URL. + mainApp->helpTopicAction(HELP_CAPTURE_OPTIONS_DIALOG); +} + +void CaptureOptionsDialog::updateInterfaces() +{ + if (prefs.capture_pcap_ng) { + ui->rbPcapng->setChecked(true); + } else { + ui->rbPcap->setChecked(true); + } + ui->capturePromModeCheckBox->setChecked(prefs.capture_prom_mode); + + if (global_capture_opts.saving_to_file) { + ui->filenameLineEdit->setText(QString(global_capture_opts.orig_save_file)); + } + + ui->gbNewFileAuto->setChecked(global_capture_opts.multi_files_on); + ui->PktCheckBox->setChecked(global_capture_opts.has_file_packets); + if (global_capture_opts.has_file_packets) { + ui->PktSpinBox->setValue(global_capture_opts.file_packets); + } + ui->MBCheckBox->setChecked(global_capture_opts.has_autostop_filesize); + if (global_capture_opts.has_autostop_filesize) { + int value = global_capture_opts.autostop_filesize; + if (value > 1000000) { + if (global_capture_opts.multi_files_on) { + ui->MBSpinBox->setValue(value / 1000000); + ui->MBComboBox->setCurrentIndex(2); + } else { + ui->stopMBCheckBox->setChecked(true); + ui->stopMBSpinBox->setValue(value / 1000000); + ui->stopMBComboBox->setCurrentIndex(2); + } + } else if (value > 1000 && value % 1000 == 0) { + if (global_capture_opts.multi_files_on) { + ui->MBSpinBox->setValue(value / 1000); + ui->MBComboBox->setCurrentIndex(1); + } else { + ui->stopMBCheckBox->setChecked(true); + ui->stopMBSpinBox->setValue(value / 1000); + ui->stopMBComboBox->setCurrentIndex(1); + } + } else { + if (global_capture_opts.multi_files_on) { + ui->MBSpinBox->setValue(value); + ui->MBComboBox->setCurrentIndex(0); + } else { + ui->stopMBCheckBox->setChecked(true); + ui->stopMBSpinBox->setValue(value); + ui->stopMBComboBox->setCurrentIndex(0); + } + } + } + + ui->SecsCheckBox->setChecked(global_capture_opts.has_file_duration); + if (global_capture_opts.has_file_duration) { + int value = global_capture_opts.file_duration; + if (value > 3600 && value % 3600 == 0) { + ui->SecsSpinBox->setValue(value / 3600); + ui->SecsComboBox->setCurrentIndex(2); + } else if (value > 60 && value % 60 == 0) { + ui->SecsSpinBox->setValue(value / 60); + ui->SecsComboBox->setCurrentIndex(1); + } else { + ui->SecsSpinBox->setValue(value); + ui->SecsComboBox->setCurrentIndex(0); + } + } + + ui->IntervalSecsCheckBox->setChecked(global_capture_opts.has_file_interval); + if (global_capture_opts.has_file_interval) { + int value = global_capture_opts.file_interval; + if (value > 3600 && value % 3600 == 0) { + ui->IntervalSecsSpinBox->setValue(value / 3600); + ui->IntervalSecsComboBox->setCurrentIndex(2); + } else if (value > 60 && value % 60 == 0) { + ui->IntervalSecsSpinBox->setValue(value / 60); + ui->IntervalSecsComboBox->setCurrentIndex(1); + } else { + ui->IntervalSecsSpinBox->setValue(value); + ui->IntervalSecsComboBox->setCurrentIndex(0); + } + } + + if (global_capture_opts.has_ring_num_files) { + ui->RbSpinBox->setValue(global_capture_opts.ring_num_files); + ui->RbCheckBox->setCheckState(Qt::Checked); + } + + if (global_capture_opts.has_autostop_duration) { + ui->stopSecsCheckBox->setChecked(true); + int value = global_capture_opts.autostop_duration; + if (value > 3600 && value % 3600 == 0) { + ui->stopSecsSpinBox->setValue(value / 3600); + ui->stopSecsComboBox->setCurrentIndex(2); + } else if (value > 60 && value % 60 == 0) { + ui->stopSecsSpinBox->setValue(value / 60); + ui->stopSecsComboBox->setCurrentIndex(1); + } else { + ui->stopSecsSpinBox->setValue(value); + ui->stopSecsComboBox->setCurrentIndex(0); + } + } + + if (global_capture_opts.has_autostop_packets) { + ui->stopPktCheckBox->setChecked(true); + ui->stopPktSpinBox->setValue(global_capture_opts.autostop_packets); + } + + if (global_capture_opts.has_autostop_files) { + ui->stopFilesCheckBox->setChecked(true); + ui->stopFilesSpinBox->setValue(global_capture_opts.autostop_files); + } + + ui->cbUpdatePacketsRT->setChecked(global_capture_opts.real_time_mode); + ui->cbAutoScroll->setChecked(recent.capture_auto_scroll); + ui->cbExtraCaptureInfo->setChecked(global_capture_opts.show_info); + + ui->cbResolveMacAddresses->setChecked(gbl_resolv_flags.mac_name); + ui->cbResolveNetworkNames->setChecked(gbl_resolv_flags.network_name); + ui->cbResolveTransportNames->setChecked(gbl_resolv_flags.transport_name); + + // Rebuild the interface list without disturbing the main welcome screen. + disconnect(ui->interfaceTree, SIGNAL(itemSelectionChanged()), this, SLOT(interfaceSelected())); + ui->interfaceTree->clear(); + +#ifdef SHOW_BUFFER_COLUMN + gint buffer; +#endif + gint snaplen; + gboolean hassnap, pmode; + QList selected_interfaces; + + disconnect(ui->interfaceTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(interfaceItemChanged(QTreeWidgetItem*,int))); + + if (global_capture_opts.all_ifaces->len > 0) { + interface_t *device; + + for (guint device_idx = 0; device_idx < global_capture_opts.all_ifaces->len; device_idx++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx); + + /* Continue if capture device is hidden */ + if (device->hidden) { + continue; + } + + // Traffic sparklines + InterfaceTreeWidgetItem *ti = new InterfaceTreeWidgetItem(ui->interfaceTree); + ti->setFlags(ti->flags() | Qt::ItemIsEditable); + + if (device->if_info.type == IF_EXTCAP) { + ti->setIcon(col_extcap_, QIcon(StockIcon("x-capture-options"))); + ti->setData(col_extcap_, Qt::UserRole, QString(device->if_info.name)); + ti->setToolTip(col_extcap_, QString("Extcap interface settings")); + } + + ti->setText(col_interface_, device->display_name); + ti->setData(col_interface_, Qt::UserRole, QString(device->name)); + if (device->if_info.type != IF_EXTCAP) + ti->setData(col_traffic_, Qt::UserRole, QVariant::fromValue(ti->points)); + + if (device->no_addresses > 0) { + QString addr_str = tr("%1: %2").arg(device->no_addresses > 1 ? tr("Addresses") : tr("Address")).arg(device->addresses); + QTreeWidgetItem *addr_ti = new QTreeWidgetItem(ti); + + addr_str.replace('\n', ", "); + addr_ti->setText(0, addr_str); + addr_ti->setFlags(addr_ti->flags() ^ Qt::ItemIsSelectable); + addr_ti->setFirstColumnSpanned(true); + addr_ti->setToolTip(col_interface_, QString("%1").arg(addr_str)); + ti->setToolTip(col_interface_, QString("%1").arg(addr_str)); + } else { + ti->setToolTip(col_interface_, tr("no addresses")); + } + + if (capture_dev_user_pmode_find(device->name, &pmode)) { + device->pmode = pmode; + } + if (capture_dev_user_snaplen_find(device->name, &hassnap, &snaplen)) { + /* Default snap length set in preferences */ + device->snaplen = snaplen; + device->has_snaplen = snaplen == WTAP_MAX_PACKET_SIZE_STANDARD ? FALSE : hassnap; + } else { + /* No preferences set yet, use default values */ + device->snaplen = WTAP_MAX_PACKET_SIZE_STANDARD; + device->has_snaplen = FALSE; + } + +#ifdef SHOW_BUFFER_COLUMN + if (capture_dev_user_buffersize_find(device->name) != -1) { + buffer = capture_dev_user_buffersize_find(device->name); + device->buffer = buffer; + } else { + device->buffer = DEFAULT_CAPTURE_BUFFER_SIZE; + } +#endif + ti->updateInterfaceColumns(device); + + if (device->selected) { + selected_interfaces << ti; + } + } + } + + connect(ui->interfaceTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(interfaceItemChanged(QTreeWidgetItem*,int))); + + foreach (QTreeWidgetItem *ti, selected_interfaces) { + ti->setSelected(true); + } + connect(ui->interfaceTree, SIGNAL(itemSelectionChanged()), this, SLOT(interfaceSelected())); + updateSelectedFilter(); + + // Manually or automatically size some columns as needed. + int one_em = fontMetrics().height(); + for (int col = 0; col < ui->interfaceTree->topLevelItemCount(); col++) { + switch (col) { + case col_pmode_: + ui->interfaceTree->setColumnWidth(col, one_em * 3.25); + break; + case col_snaplen_: + ui->interfaceTree->setColumnWidth(col, one_em * 4.25); + break; + case col_buffer_: + ui->interfaceTree->setColumnWidth(col, one_em * 4.25); + break; + case col_monitor_: + ui->interfaceTree->setColumnWidth(col, one_em * 3.25); + break; + default: + ui->interfaceTree->resizeColumnToContents(col); + } + + } + + updateWidgets(); + + if (!stat_timer_) { + updateStatistics(); + stat_timer_ = new QTimer(this); + connect(stat_timer_, SIGNAL(timeout()), this, SLOT(updateStatistics())); + stat_timer_->start(stat_update_interval_); + } +} + +void CaptureOptionsDialog::showEvent(QShowEvent *) +{ + updateInterfaces(); +} + +void CaptureOptionsDialog::refreshInterfaceList() +{ + updateInterfaces(); + emit interfaceListChanged(); +} + +void CaptureOptionsDialog::updateLocalInterfaces() +{ + updateInterfaces(); +} + +void CaptureOptionsDialog::updateStatistics(void) +{ + interface_t *device; + + disconnect(ui->interfaceTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(interfaceItemChanged(QTreeWidgetItem*,int))); + for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) { + + for (guint if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++) { + QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row); + if (!ti) { + continue; + } + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx); + QString device_name = ti->text(col_interface_); + if (device_name.compare(device->display_name) || device->hidden || device->type == IF_PIPE) { + continue; + } + QList points = ti->data(col_traffic_, Qt::UserRole).value >(); + points.append(device->packet_diff); + ti->setData(col_traffic_, Qt::UserRole, QVariant::fromValue(points)); + } + } + connect(ui->interfaceTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(interfaceItemChanged(QTreeWidgetItem*,int))); + ui->interfaceTree->viewport()->update(); +} + +void CaptureOptionsDialog::on_compileBPF_clicked() +{ + QStringList interfaces; + foreach (QTreeWidgetItem *ti, ui->interfaceTree->selectedItems()) { + interfaces.append(ti->text(col_interface_)); + } + + QString filter = ui->captureFilterComboBox->currentText(); + CompiledFilterOutput *cfo = new CompiledFilterOutput(this, interfaces, filter); + + cfo->show(); +} + +bool CaptureOptionsDialog::saveOptionsToPreferences() +{ + if (ui->rbPcapng->isChecked()) { + global_capture_opts.use_pcapng = true; + prefs.capture_pcap_ng = true; + } else { + global_capture_opts.use_pcapng = false; + prefs.capture_pcap_ng = false; + } + + g_free(global_capture_opts.save_file); + g_free(global_capture_opts.orig_save_file); + + QString filename = ui->filenameLineEdit->text(); + if (filename.length() > 0) { + /* User specified a file to which the capture should be written. */ + global_capture_opts.saving_to_file = true; + global_capture_opts.save_file = qstring_strdup(filename); + global_capture_opts.orig_save_file = qstring_strdup(filename); + /* Save the directory name for future file dialogs. */ + set_last_open_dir(get_dirname(filename.toUtf8().data())); + } else { + /* User didn't specify a file; save to a temporary file. */ + global_capture_opts.saving_to_file = false; + global_capture_opts.save_file = NULL; + global_capture_opts.orig_save_file = NULL; + } + + QString tempdir = ui->tempDirLineEdit->text(); + if (tempdir.length() > 0) { + global_capture_opts.temp_dir = qstring_strdup(tempdir); + } + else { + global_capture_opts.temp_dir = NULL; + } + + global_capture_opts.has_ring_num_files = ui->RbCheckBox->isChecked(); + + if (global_capture_opts.has_ring_num_files) { + global_capture_opts.ring_num_files = ui->RbSpinBox->value(); + if (global_capture_opts.ring_num_files > RINGBUFFER_MAX_NUM_FILES) + global_capture_opts.ring_num_files = RINGBUFFER_MAX_NUM_FILES; +#if RINGBUFFER_MIN_NUM_FILES > 0 + else if (global_capture_opts.ring_num_files < RINGBUFFER_MIN_NUM_FILES) + global_capture_opts.ring_num_files = RINGBUFFER_MIN_NUM_FILES; +#endif + } + global_capture_opts.multi_files_on = ui->gbNewFileAuto->isChecked(); + if (global_capture_opts.multi_files_on) { + global_capture_opts.has_file_duration = ui->SecsCheckBox->isChecked(); + if (global_capture_opts.has_file_duration) { + global_capture_opts.file_duration = ui->SecsSpinBox->value(); + int index = ui->SecsComboBox->currentIndex(); + switch (index) { + case 1: global_capture_opts.file_duration *= 60; + break; + case 2: global_capture_opts.file_duration *= 3600; + break; + } + } + global_capture_opts.has_file_interval = ui->IntervalSecsCheckBox->isChecked(); + if (global_capture_opts.has_file_interval) { + global_capture_opts.file_interval = ui->IntervalSecsSpinBox->value(); + int index = ui->IntervalSecsComboBox->currentIndex(); + switch (index) { + case 1: global_capture_opts.file_interval *= 60; + break; + case 2: global_capture_opts.file_interval *= 3600; + break; + } + } + global_capture_opts.has_file_packets = ui->PktCheckBox->isChecked(); + if (global_capture_opts.has_file_packets) { + global_capture_opts.file_packets = ui->PktSpinBox->value(); + } + global_capture_opts.has_autostop_filesize = ui->MBCheckBox->isChecked(); + if (global_capture_opts.has_autostop_filesize) { + global_capture_opts.autostop_filesize = ui->MBSpinBox->value(); + int index = ui->MBComboBox->currentIndex(); + switch (index) { + case 1: if (global_capture_opts.autostop_filesize > 2000) { + QMessageBox::warning(this, tr("Error"), + tr("Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB.")); + return false; + } else { + global_capture_opts.autostop_filesize *= 1000; + } + break; + case 2: if (global_capture_opts.autostop_filesize > 2) { + QMessageBox::warning(this, tr("Error"), + tr("Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB.")); + return false; + } else { + global_capture_opts.autostop_filesize *= 1000000; + } + break; + } + } + /* test if the settings are ok for a ringbuffer */ + if (global_capture_opts.save_file == NULL) { + QMessageBox::warning(this, tr("Error"), + tr("Multiple files: No capture file name given. You must specify a filename if you want to use multiple files.")); + return false; + } else if (!global_capture_opts.has_autostop_filesize && + !global_capture_opts.has_file_interval && + !global_capture_opts.has_file_duration && + !global_capture_opts.has_file_packets) { + QMessageBox::warning(this, tr("Error"), + tr("Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file.")); + g_free(global_capture_opts.save_file); + global_capture_opts.save_file = NULL; + return false; + } + } else { + global_capture_opts.has_autostop_filesize = ui->stopMBCheckBox->isChecked(); + if (global_capture_opts.has_autostop_filesize) { + global_capture_opts.autostop_filesize = ui->stopMBSpinBox->value(); + int index = ui->stopMBComboBox->currentIndex(); + switch (index) { + case 1: if (global_capture_opts.autostop_filesize > 2000) { + QMessageBox::warning(this, tr("Error"), + tr("Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB.")); + return false; + } else { + global_capture_opts.autostop_filesize *= 1000; + } + break; + case 2: if (global_capture_opts.autostop_filesize > 2) { + QMessageBox::warning(this, tr("Error"), + tr("Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB.")); + return false; + } else { + global_capture_opts.autostop_filesize *= 1000000; + } + break; + } + } + } + + global_capture_opts.has_autostop_duration = ui->stopSecsCheckBox->isChecked(); + if (global_capture_opts.has_autostop_duration) { + global_capture_opts.autostop_duration = ui->stopSecsSpinBox->value(); + int index = ui->stopSecsComboBox->currentIndex(); + switch (index) { + case 1: global_capture_opts.autostop_duration *= 60; + break; + case 2: global_capture_opts.autostop_duration *= 3600; + break; + } + } + + global_capture_opts.has_autostop_packets = ui->stopPktCheckBox->isChecked(); + if (global_capture_opts.has_autostop_packets) { + global_capture_opts.autostop_packets = ui->stopPktSpinBox->value(); + } + + global_capture_opts.has_autostop_files = ui->stopFilesCheckBox->isChecked(); + if (global_capture_opts.has_autostop_files) { + global_capture_opts.autostop_files = ui->stopFilesSpinBox->value(); + } + + interface_t *device; + + for (int col = col_link_; col <= col_filter_; col++) { + if (ui->interfaceTree->isColumnHidden(col)) { + continue; + } + /* All entries are separated by comma. There is also one before the first interface to be able to identify + word boundaries. As 'lo' is part of 'nflog' an exact match is necessary. */ + switch (col) { + case col_link_: + { + QStringList link_list; + + for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row); + QString device_name = ti->data(col_interface_, Qt::UserRole).toString(); + device = getDeviceByName(device_name); + if (!device || device->active_dlt == -1) { + continue; + } + link_list << QString("%1(%2)").arg(device->name).arg(device->active_dlt); + } + g_free(prefs.capture_devices_linktypes); + prefs.capture_devices_linktypes = qstring_strdup(link_list.join(",")); + break; + } +#ifdef SHOW_BUFFER_COLUMN + case col_buffer_: + { + QStringList buffer_size_list; + + for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row); + QString device_name = ti->data(col_interface_, Qt::UserRole).toString(); + device = getDeviceByName(device_name); + if (!device || device->buffer == -1) { + continue; + } + buffer_size_list << QString("%1(%2)").arg(device->name).arg(device->buffer); + } + g_free(prefs.capture_devices_buffersize); + prefs.capture_devices_buffersize = qstring_strdup(buffer_size_list.join(",")); + break; + } +#endif // HAVE_BUFFER_SETTING + case col_snaplen_: + { + QStringList snaplen_list; + + for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row); + QString device_name = ti->data(col_interface_, Qt::UserRole).toString(); + device = getDeviceByName(device_name); + if (!device) continue; + snaplen_list << QString("%1:%2(%3)") + .arg(device->name) + .arg(device->has_snaplen) + .arg(device->has_snaplen ? device->snaplen : WTAP_MAX_PACKET_SIZE_STANDARD); + } + g_free(prefs.capture_devices_snaplen); + prefs.capture_devices_snaplen = qstring_strdup(snaplen_list.join(",")); + break; + } + case col_pmode_: + { + QStringList pmode_list; + + for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row); + QString device_name = ti->data(col_interface_, Qt::UserRole).toString(); + device = getDeviceByName(device_name); + if (!device || device->pmode == -1) { + continue; + } + pmode_list << QString("%1(%2)").arg(device->name).arg(device->pmode); + } + g_free(prefs.capture_devices_pmode); + prefs.capture_devices_pmode = qstring_strdup(pmode_list.join(",")); + break; + } + +#ifdef SHOW_MONITOR_COLUMN + case col_monitor_: + { + QStringList monitor_list; + + for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row); + QString device_name = ti->data(col_interface_, Qt::UserRole).toString(); + device = getDeviceByName(device_name); + if (!device || !device->monitor_mode_supported || (device->monitor_mode_supported && !device->monitor_mode_enabled)) { + continue; + } + monitor_list << device->name; + } + g_free(prefs.capture_devices_monitor_mode); + prefs.capture_devices_monitor_mode = qstring_strdup(monitor_list.join(",")); + break; + } +#endif // HAVE_MONITOR_SETTING + +#if 0 + // The device cfilter should have been applied at this point. + // We shouldn't change it here. + case col_filter_: + { + // XXX Update selected interfaces only? + for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row); + QString device_name = ti->data(col_interface_, Qt::UserRole).toString(); + device = getDeviceByName(device_name); + if (!device) continue; + g_free(device->cfilter); + if (ti->text(col_filter_).isEmpty()) { + device->cfilter = NULL; + } else { + device->cfilter = qstring_strdup(ti->text(col_filter_)); + } + } + } +#endif + } + } + + g_free(global_capture_opts.compress_type); + + if (ui->rbCompressionNone->isChecked() ) { + global_capture_opts.compress_type = NULL; + } else if (ui->rbCompressionGzip->isChecked() ) { + global_capture_opts.compress_type = qstring_strdup("gzip"); + } else { + global_capture_opts.compress_type = NULL; + } + + prefs_main_write(); + return true; +} + +void CaptureOptionsDialog::updateSelectedFilter() +{ + // Should match MainWelcome::interfaceSelected. + QPair sf_pair = CaptureFilterEdit::getSelectedFilter(); + const QString user_filter = sf_pair.first; + bool conflict = sf_pair.second; + + if (conflict) { + ui->captureFilterComboBox->lineEdit()->clear(); + ui->captureFilterComboBox->setConflict(true); + } else { + ui->captureFilterComboBox->lineEdit()->setText(user_filter); + } +} + +void CaptureOptionsDialog::on_manageButton_clicked() +{ + if (saveOptionsToPreferences()) { + ManageInterfacesDialog *dlg = new ManageInterfacesDialog(this); + dlg->show(); + } +} + +void CaptureOptionsDialog::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + +interface_t *CaptureOptionsDialog::getDeviceByName(const QString device_name) +{ + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (device_name.compare(QString().fromUtf8(device->name)) == 0) { + return device; + } + } + return NULL; +} + +// +// InterfaceTreeItem +// +bool InterfaceTreeWidgetItem::operator< (const QTreeWidgetItem &other) const { + if (treeWidget()->sortColumn() == col_traffic_) { + QList points = data(col_traffic_, Qt::UserRole).value >(); + QList other_points = other.data(col_traffic_, Qt::UserRole).value >(); + double avg = 0, other_avg = 0; + foreach (int point, points) { + avg += (double) point / points.length(); + } + foreach (int point, other_points) { + other_avg += (double) point / other_points.length(); + } + return avg < other_avg; + } + return QTreeWidgetItem::operator<(other); +} + +QVariant InterfaceTreeWidgetItem::data(int column, int role) const +{ + // See setData for the special col_traffic_ treatment. + if (column == col_traffic_ && role == Qt::UserRole) { + return QVariant::fromValue(points); + } + + return QTreeWidgetItem::data(column, role); +} + +void InterfaceTreeWidgetItem::setData(int column, int role, const QVariant &value) +{ + // Workaround for closing editors on updates to the points list: normally + // QTreeWidgetItem::setData emits dataChanged when the value (list) changes. + // We could store a pointer to the list, or just have this hack that does + // not emit dataChanged. + if (column == col_traffic_ && role == Qt::UserRole) { + points = value.value >(); + return; + } + + QTreeWidgetItem::setData(column, role, value); +} + +// +// InterfaceTreeDelegate +// + +#include + +InterfaceTreeDelegate::InterfaceTreeDelegate(QObject *parent) + : QStyledItemDelegate(parent), tree_(NULL) +{ +} + + +InterfaceTreeDelegate::~InterfaceTreeDelegate() +{ +} + + +QWidget* InterfaceTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &idx) const +{ + QWidget *w = NULL; +#ifdef SHOW_BUFFER_COLUMN + gint buffer = DEFAULT_CAPTURE_BUFFER_SIZE; +#endif + guint snap = WTAP_MAX_PACKET_SIZE_STANDARD; + GList *links = NULL; + + if (idx.column() > 1 && idx.data().toString().compare(UTF8_EM_DASH)) { + QTreeWidgetItem *ti = tree_->topLevelItem(idx.row()); + QString interface_name = ti->text(col_interface_); + interface_t *device = find_device_by_if_name(interface_name); + + if (device) { +#ifdef SHOW_BUFFER_COLUMN + buffer = device->buffer; +#endif + snap = device->snaplen; + links = device->links; + } + switch (idx.column()) { + case col_extcap_: + case col_interface_: + case col_traffic_: + break; + case col_link_: + { + GList *list; + link_row *linkr; + QStringList valid_link_types; + + // XXX The GTK+ UI fills in all link types, valid or not. We add + // only the valid ones. If we *do* wish to include invalid link + // types we'll have to jump through the hoops necessary to disable + // QComboBox items. + + for (list = links; list != Q_NULLPTR; list = gxx_list_next(list)) { + linkr = gxx_list_data(link_row*, list); + if (linkr->dlt >= 0) { + valid_link_types << linkr->name; + } + } + + if (valid_link_types.size() < 2) { + break; + } + QComboBox *cb = new QComboBox(parent); + cb->addItems(valid_link_types); + + connect(cb, &QComboBox::currentTextChanged, this, &InterfaceTreeDelegate::linkTypeChanged); + w = (QWidget*) cb; + break; + } + case col_snaplen_: + { + QSpinBox *sb = new QSpinBox(parent); + sb->setRange(1, WTAP_MAX_PACKET_SIZE_STANDARD); + sb->setValue(snap); + sb->setWrapping(true); + connect(sb, SIGNAL(valueChanged(int)), this, SLOT(snapshotLengthChanged(int))); + w = (QWidget*) sb; + break; + } +#ifdef SHOW_BUFFER_COLUMN + case col_buffer_: + { + QSpinBox *sb = new QSpinBox(parent); + sb->setRange(1, WTAP_MAX_PACKET_SIZE_STANDARD); + sb->setValue(buffer); + sb->setWrapping(true); + connect(sb, SIGNAL(valueChanged(int)), this, SLOT(bufferSizeChanged(int))); + w = (QWidget*) sb; + break; + } +#endif + case col_filter_: + { + // XXX: Should this take the interface name, so that the history + // list is taken from the interface-specific recent cfilter list? + CaptureFilterCombo *cf = new CaptureFilterCombo(parent, true); + connect(cf->lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(filterChanged(QString))); + w = (QWidget*) cf; + } + default: + break; + } + } + if (w) + w->setAutoFillBackground(true); + return w; +} + +bool InterfaceTreeDelegate::eventFilter(QObject *object, QEvent *event) +{ + QComboBox * comboBox = dynamic_cast(object); + if (comboBox) { + if (event->type() == QEvent::MouseButtonRelease) { + comboBox->showPopup(); + return true; + } + } else { + return QStyledItemDelegate::eventFilter(object, event); + } + return false; +} + +void InterfaceTreeDelegate::linkTypeChanged(const QString selected_link_type) +{ + GList *list; + link_row *temp; + interface_t *device; + + QTreeWidgetItem *ti = tree_->currentItem(); + if (!ti) { + return; + } + QString interface_name = ti->text(col_interface_); + device = find_device_by_if_name(interface_name); + if (!device) { + return; + } + for (list = device->links; list != Q_NULLPTR; list = gxx_list_next(list)) { + temp = gxx_list_data(link_row*, list); + if (!selected_link_type.compare(temp->name)) { + device->active_dlt = temp->dlt; + } + } + // XXX We might want to verify that active_dlt is valid at this point. +} + +void InterfaceTreeDelegate::snapshotLengthChanged(int value) +{ + interface_t *device; + QTreeWidgetItem *ti = tree_->currentItem(); + if (!ti) { + return; + } + QString interface_name = ti->text(col_interface_); + device = find_device_by_if_name(interface_name); + if (!device) { + return; + } + if (value != WTAP_MAX_PACKET_SIZE_STANDARD) { + device->has_snaplen = true; + device->snaplen = value; + } else { + device->has_snaplen = false; + device->snaplen = WTAP_MAX_PACKET_SIZE_STANDARD; + } +} + +#ifdef SHOW_BUFFER_COLUMN +void InterfaceTreeDelegate::bufferSizeChanged(int value) +{ + interface_t *device; + QTreeWidgetItem *ti = tree_->currentItem(); + if (!ti) { + return; + } + QString interface_name = ti->text(col_interface_); + device = find_device_by_if_name(interface_name); + if (!device) { + return; + } + device->buffer = value; +} +#endif + +#endif /* HAVE_LIBPCAP */ diff --git a/ui/qt/capture_options_dialog.h b/ui/qt/capture_options_dialog.h new file mode 100644 index 00000000..e9eea761 --- /dev/null +++ b/ui/qt/capture_options_dialog.h @@ -0,0 +1,126 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#ifndef CAPTURE_OPTIONS_DIALOG_H +#define CAPTURE_OPTIONS_DIALOG_H + +#include + +#ifdef HAVE_LIBPCAP + +#include + +#include "geometry_state_dialog.h" +#include +#include + +typedef struct if_stat_cache_s if_stat_cache_t; + +namespace Ui { +class CaptureOptionsDialog; +} + +#include + +class InterfaceTreeDelegate : public QStyledItemDelegate +{ + Q_OBJECT +private: + QTreeWidget* tree_; + +public: + InterfaceTreeDelegate(QObject *parent = 0); + ~InterfaceTreeDelegate(); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &idx) const; + void setTree(QTreeWidget* tree) { tree_ = tree; } + bool eventFilter(QObject *object, QEvent *event); + +signals: + void filterChanged(const QString filter); + +private slots: + void linkTypeChanged(const QString selected_link_type); + void snapshotLengthChanged(int value); + void bufferSizeChanged(int value); +}; + +class CaptureOptionsDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit CaptureOptionsDialog(QWidget *parent = 0); + ~CaptureOptionsDialog(); + + void updateInterfaces(); + +public slots: + void interfaceSelected(); + +protected: + virtual void showEvent(QShowEvent *); + +private slots: + void on_capturePromModeCheckBox_toggled(bool checked); + void on_gbStopCaptureAuto_toggled(bool checked); + void on_cbUpdatePacketsRT_toggled(bool checked); + void on_cbAutoScroll_toggled(bool checked); + void on_gbNewFileAuto_toggled(bool checked); + void on_cbExtraCaptureInfo_toggled(bool checked); + void on_cbResolveMacAddresses_toggled(bool checked); + void on_compileBPF_clicked(); + void on_manageButton_clicked(); + void on_cbResolveNetworkNames_toggled(bool checked); + void on_cbResolveTransportNames_toggled(bool checked); + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + void on_buttonBox_helpRequested(); + void filterEdited(); + void updateWidgets(); + void updateStatistics(void); + void refreshInterfaceList(); + void updateLocalInterfaces(); + void browseButtonClicked(); + void interfaceItemChanged(QTreeWidgetItem *item, int column); + void itemClicked(QTreeWidgetItem *item, int column); + void itemDoubleClicked(QTreeWidgetItem *item, int column); + void changeEvent(QEvent* event); + void tempDirBrowseButtonClicked(); + +signals: + void startCapture(); + void stopCapture(); + void setSelectedInterfaces(); + void setFilterValid(bool valid, const QString capture_filter); + void interfacesChanged(); + void ifsChanged(); + void interfaceListChanged(); + void captureFilterTextEdited(const QString & text); + void showExtcapOptions(QString &device_name, bool startCaptureOnClose); + +private: + Ui::CaptureOptionsDialog *ui; + + if_stat_cache_t *stat_cache_; + QTimer *stat_timer_; + InterfaceTreeDelegate interface_item_delegate_; + + interface_t *getDeviceByName(const QString device_name); + bool saveOptionsToPreferences(); + void updateSelectedFilter(); + + void updateGlobalDeviceSelections(); + void updateFromGlobalDeviceSelections(); +}; + +#endif /* HAVE_LIBPCAP */ + +#endif // CAPTURE_OPTIONS_DIALOG_H diff --git a/ui/qt/capture_options_dialog.ui b/ui/qt/capture_options_dialog.ui new file mode 100644 index 00000000..2ace9a95 --- /dev/null +++ b/ui/qt/capture_options_dialog.ui @@ -0,0 +1,976 @@ + + + CaptureOptionsDialog + + + + 0 + 0 + 950 + 440 + + + + + + + 0 + + + + Input + + + + + + + 0 + 0 + + + + QAbstractItemView::ExtendedSelection + + + Qt::ElideMiddle + + + true + + + + + + + + + Interface + + + + + Traffic + + + + + Link-layer Header + + + + + Promiscuous + + + + + Snaplen (B) + + + + + Buffer (MB) + + + + + Monitor Mode + + + + + Capture Filter + + + + + + + + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + + + Enable promiscuous mode on all interfaces + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + + + Manage Interfaces… + + + + + + + + + + + Capture filter for selected interfaces: + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Compile BPFs + + + + + + + + + + Output + + + + + + true + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + + + false + + + Capture to a permanent file + + + true + + + false + + + + + + File: + + + + + + + + + + Browse… + + + + + + + + + + + + Output format: + + + + + + + pcapng + + + + + + + pcap + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + + + Create a new file automatically… + + + true + + + true + + + + + + Switch to the next file after the specified number of packets have been captured. + + + after + + + + + + + Switch to the next file after the specified number of packets have been captured. + + + QAbstractSpinBox::PlusMinus + + + 2147483647 + + + 100000 + + + + + + + packets + + + + + + + Switch to the next file after the file size exceeds the specified file size. + + + after + + + + + + + Switch to the next file after the file size exceeds the specified file size. + + + true + + + QAbstractSpinBox::PlusMinus + + + 1 + + + 1000000 + + + 1 + + + + + + + Switch to the next file after the file size exceeds the specified file size. + + + + kilobytes + + + + + megabytes + + + + + gigabytes + + + + + + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + + + after + + + + + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + + + true + + + QAbstractSpinBox::PlusMinus + + + 1 + + + 1000000 + + + 1 + + + + + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + + + + seconds + + + + + minutes + + + + + hours + + + + + + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + + + when time is a multiple of + + + + + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + + + true + + + QAbstractSpinBox::PlusMinus + + + 1 + + + 1000000 + + + 1 + + + + + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + + + 2 + + + + seconds + + + + + minutes + + + + + hours + + + + + + + + compression + + + + + + None + + + buttonGroup + + + + + + + gzip + + + buttonGroup + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + + + Use a ring buffer with + + + + + + + true + + + 2 + + + 65535 + + + 2 + + + + + + + files + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + gbNewFileAuto + gbCaptureToFile + + + + Options + + + + + + + + Display Options + + + true + + + + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + + + Update list of packets in real-time + + + + + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + + + Automatically scroll during live capture + + + + + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + + + Show capture information during live capture + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Name Resolution + + + true + + + + + + Perform MAC layer name resolution while capturing. + + + Resolve MAC addresses + + + + + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + + + Resolve network names + + + + + + + Perform transport layer name resolution while capturing. + + + Resolve transport names + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + true + + + Stop capture automatically after… + + + true + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + + + + + + + + + + + 0 + 0 + + + + Stop capturing after the specified number of packets have been captured. + + + QAbstractSpinBox::PlusMinus + + + 2147483647 + + + 1 + + + + + + + packets + + + + + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + + + + + + + + + + + 0 + 0 + + + + Stop capturing after the specified number of packets have been captured. + + + QAbstractSpinBox::PlusMinus + + + 2147483647 + + + 1 + + + + + + + files + + + + + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + + + + + + + + + + Stop capturing after the specified amount of data has been captured. + + + QAbstractSpinBox::PlusMinus + + + 2147483647 + + + 1 + + + + + + + Stop capturing after the specified amount of data has been captured. + + + + kilobytes + + + + + megabytes + + + + + gigabytes + + + + + + + + Stop capturing after the specified amount of time has passed. + + + + + + + + + + Stop capturing after the specified amount of time has passed. + + + QAbstractSpinBox::PlusMinus + + + 2147483647 + + + 1 + + + + + + + Stop capturing after the specified amount of time has passed. + + + + seconds + + + + + minutes + + + + + hours + + + + + + + + + + + true + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + + + false + + + Directory for temporary files + + + true + + + false + + + + + + + + + Browse… + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + CaptureFilterCombo + QComboBox +
widgets/capture_filter_combo.h
+
+
+ + + + + +
diff --git a/ui/qt/capture_preferences_frame.cpp b/ui/qt/capture_preferences_frame.cpp new file mode 100644 index 00000000..361eb656 --- /dev/null +++ b/ui/qt/capture_preferences_frame.cpp @@ -0,0 +1,184 @@ +/* capture_preferences_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#ifdef HAVE_LIBPCAP +#include "ui/capture_globals.h" +#endif + +#include "capture_preferences_frame.h" +#include +#include +#include +#include "main_application.h" + +#include + +#include "ui/capture_ui_utils.h" +#include "ui/ws_ui_util.h" + +#include + +CapturePreferencesFrame::CapturePreferencesFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::CapturePreferencesFrame) +{ + ui->setupUi(this); + + pref_device_ = prefFromPrefPtr(&prefs.capture_device); + pref_prom_mode_ = prefFromPrefPtr(&prefs.capture_prom_mode); + pref_pcap_ng_ = prefFromPrefPtr(&prefs.capture_pcap_ng); + pref_real_time_ = prefFromPrefPtr(&prefs.capture_real_time); + pref_update_interval_ = prefFromPrefPtr(&prefs.capture_update_interval); + pref_no_interface_load_ = prefFromPrefPtr(&prefs.capture_no_interface_load); + pref_no_extcap_ = prefFromPrefPtr(&prefs.capture_no_extcap); + + // Setting the left margin via a style sheet clobbers its + // appearance. + int margin = style()->pixelMetric(QStyle::PM_LayoutLeftMargin); + QRect geom = ui->defaultInterfaceSpacer->geometry(); + geom.setWidth(margin); + ui->defaultInterfaceSpacer->setGeometry(geom); +} + +CapturePreferencesFrame::~CapturePreferencesFrame() +{ + delete ui; +} + +void CapturePreferencesFrame::showEvent(QShowEvent *) +{ + updateWidgets(); +} + +void CapturePreferencesFrame::updateWidgets() +{ +#ifdef HAVE_LIBPCAP + interface_t *device; + QString default_device_string; + + if (prefs_get_string_value(pref_device_, pref_stashed)) { + default_device_string = prefs_get_string_value(pref_device_, pref_stashed); + } + ui->defaultInterfaceComboBox->clear(); + if ((global_capture_opts.all_ifaces->len == 0) && + (prefs_get_bool_value(pref_no_interface_load_, pref_stashed) == FALSE)) { + /* + * No interfaces - try refreshing the local interfaces, to + * see whether any have showed up (or privileges have changed + * to allow us to access them). + */ + mainApp->refreshLocalInterfaces(); + } + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + + /* Continue if capture device is hidden */ + if (device->hidden) { + continue; + } + // InterfaceTree matches against device->name (the device name) + // when selecting the default interface, so add it here if needed. + // + // On UN*Xes, the display name includes the device name, as + // interface names are generally short simple names that + // are somewhat human-recognizable; if there's a description, + // it precedes the device name, which is followed by a colon + // and a space, e.g. "Wi-Fi: en0". This means that we do not + // need to add the device name. + // + // On Windows, the display name does not include the device + // name, as it begins with \\Device and ends with a GUID, + // with nothing much human-recognizable. Therefore, the + // display name is just the "friendly name" that Windows + // provides. This means that we *do* need to add the device + // name, which means that, in the drop-down list, we show + // the user a big ugly UUID-laden device path. + // + // We might be able to work around that by passing device->name as + // the userData argument to addItem instead. + // + // This also means that the capture.device + QString item_text = device->display_name; + if (!item_text.contains(device->name)) { + item_text.append(QString(" (%1)").arg(device->name)); + } + ui->defaultInterfaceComboBox->addItem(item_text); + } + + if (!default_device_string.isEmpty()) { + ui->defaultInterfaceComboBox->setEditText(default_device_string); + } else { + ui->defaultInterfaceComboBox->clearEditText(); + } + + ui->capturePromModeCheckBox->setChecked(prefs_get_bool_value(pref_prom_mode_, pref_stashed)); + ui->capturePcapNgCheckBox->setChecked(prefs_get_bool_value(pref_pcap_ng_, pref_stashed)); + ui->captureRealTimeCheckBox->setChecked(prefs_get_bool_value(pref_real_time_, pref_stashed)); + ui->captureUpdateIntervalLineEdit->setText(QString::number(prefs_get_uint_value_real(pref_update_interval_, pref_stashed))); + ui->captureUpdateIntervalLineEdit->setPlaceholderText(QString::number(prefs_get_uint_value_real(pref_update_interval_, pref_default))); + ui->captureUpdateIntervalLineEdit->setSyntaxState(SyntaxLineEdit::Empty); +#endif // HAVE_LIBPCAP + ui->captureNoInterfaceLoad->setChecked(prefs_get_bool_value(pref_no_interface_load_, pref_stashed)); + ui->captureNoExtcapCheckBox->setChecked(prefs_get_bool_value(pref_no_extcap_, pref_stashed)); +} + +void CapturePreferencesFrame::on_defaultInterfaceComboBox_editTextChanged(const QString &new_iface) +{ + prefs_set_string_value(pref_device_, new_iface.toUtf8().constData(), pref_stashed); +} + +void CapturePreferencesFrame::on_capturePromModeCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_prom_mode_, checked, pref_stashed); +} + +void CapturePreferencesFrame::on_capturePcapNgCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_pcap_ng_, checked, pref_stashed); +} + +void CapturePreferencesFrame::on_captureRealTimeCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_real_time_, checked, pref_stashed); +} + +void CapturePreferencesFrame::on_captureUpdateIntervalLineEdit_textChanged(const QString &new_str) +{ + uint new_uint; + if (new_str.isEmpty()) { + new_uint = prefs_get_uint_value_real(pref_update_interval_, pref_default); + prefs_set_uint_value(pref_update_interval_, new_uint, pref_stashed); + ui->captureUpdateIntervalLineEdit->setSyntaxState(SyntaxLineEdit::Empty); + return; + } + + bool ok; + new_uint = new_str.toUInt(&ok, 0); + if (ok) { + ui->captureUpdateIntervalLineEdit->setSyntaxState(SyntaxLineEdit::Valid); + } else { + new_uint = prefs_get_uint_value_real(pref_update_interval_, pref_current); + ui->captureUpdateIntervalLineEdit->setSyntaxState(SyntaxLineEdit::Invalid); + } + prefs_set_uint_value(pref_update_interval_, new_uint, pref_stashed); +} + +void CapturePreferencesFrame::on_captureNoInterfaceLoad_toggled(bool checked) +{ + prefs_set_bool_value(pref_no_interface_load_, checked, pref_stashed); +} + +void CapturePreferencesFrame::on_captureNoExtcapCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_no_extcap_, checked, pref_stashed); +} diff --git a/ui/qt/capture_preferences_frame.h b/ui/qt/capture_preferences_frame.h new file mode 100644 index 00000000..33cf29b8 --- /dev/null +++ b/ui/qt/capture_preferences_frame.h @@ -0,0 +1,55 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_PREFERENCES_FRAME_H +#define CAPTURE_PREFERENCES_FRAME_H + +#include + +#include + +namespace Ui { +class CapturePreferencesFrame; +} + +class CapturePreferencesFrame : public QFrame +{ + Q_OBJECT + +public: + explicit CapturePreferencesFrame(QWidget *parent = 0); + ~CapturePreferencesFrame(); + +protected: + void showEvent(QShowEvent *evt); + +private slots: + void on_defaultInterfaceComboBox_editTextChanged(const QString &new_iface); + void on_capturePromModeCheckBox_toggled(bool checked); + void on_capturePcapNgCheckBox_toggled(bool checked); + void on_captureRealTimeCheckBox_toggled(bool checked); + void on_captureUpdateIntervalLineEdit_textChanged(const QString &new_str); + void on_captureNoInterfaceLoad_toggled(bool checked); + void on_captureNoExtcapCheckBox_toggled(bool checked); + +private: + Ui::CapturePreferencesFrame *ui; + + pref_t *pref_device_; + pref_t *pref_prom_mode_; + pref_t *pref_pcap_ng_; + pref_t *pref_real_time_; + pref_t *pref_update_interval_; + pref_t *pref_no_interface_load_; + pref_t *pref_no_extcap_; + + void updateWidgets(); +}; + +#endif // CAPTURE_PREFERENCES_FRAME_H diff --git a/ui/qt/capture_preferences_frame.ui b/ui/qt/capture_preferences_frame.ui new file mode 100644 index 00000000..b5a0b4b8 --- /dev/null +++ b/ui/qt/capture_preferences_frame.ui @@ -0,0 +1,156 @@ + + + CapturePreferencesFrame + + + + 0 + 0 + 354 + 220 + + + + + 0 + 191 + + + + Frame + + + 0 + + + + + + Default interface + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 18 + 20 + + + + + + + + true + + + + + + + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + + + Capture packets in promiscuous mode + + + + + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + + + Capture packets in pcapng format + + + + + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + + + Update list of packets in real time + + + + + + + + + Interval between updates (ms) + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + + + + Qt:Horizontal + + + + + + + + + Don't load interfaces on startup + + + + + + + Disable external capture interfaces + + + + + + + Qt::Vertical + + + + 20 + 3 + + + + + + + + + SyntaxLineEdit + QLineEdit +
widgets/syntax_line_edit.h
+
+
+ + +
diff --git a/ui/qt/coloring_rules_dialog.cpp b/ui/qt/coloring_rules_dialog.cpp new file mode 100644 index 00000000..20eb72b2 --- /dev/null +++ b/ui/qt/coloring_rules_dialog.cpp @@ -0,0 +1,459 @@ +/* coloring_rules_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "coloring_rules_dialog.h" +#include + +#include "ui/simple_dialog.h" +#include "epan/prefs.h" + +#include + +#include "wsutil/filesystem.h" +#include "epan/dfilter/dfilter.h" + +#include "main_application.h" + +#include "ui/qt/utils/qt_ui_utils.h" +#include "ui/qt/widgets/copy_from_profile_button.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include +#include + +/* + * @file Coloring Rules dialog + * + * Coloring rule editor for the current profile. + */ + +// To do: +// - Make the filter column narrower? It's easy to run into Qt's annoying +// habit of horizontally scrolling QTreeWidgets here. + +ColoringRulesDialog::ColoringRulesDialog(QWidget *parent, QString add_filter) : + GeometryStateDialog(parent), + ui(new Ui::ColoringRulesDialog), + colorRuleModel_(palette().color(QPalette::Text), palette().color(QPalette::Base), this), + colorRuleDelegate_(this) +{ + ui->setupUi(this); + if (parent) loadGeometry(parent->width() * 2 / 3, parent->height() * 4 / 5); + + setWindowTitle(mainApp->windowTitleString(tr("Coloring Rules %1").arg(get_profile_name()))); + + ui->coloringRulesTreeView->setModel(&colorRuleModel_); + ui->coloringRulesTreeView->setItemDelegate(&colorRuleDelegate_); + + ui->coloringRulesTreeView->viewport()->setAcceptDrops(true); + + for (int i = 0; i < colorRuleModel_.columnCount(); i++) { + ui->coloringRulesTreeView->resizeColumnToContents(i); + } + + ui->newToolButton->setStockIcon("list-add"); + ui->deleteToolButton->setStockIcon("list-remove"); + ui->copyToolButton->setStockIcon("list-copy"); + ui->clearToolButton->setStockIcon("list-clear"); + +#ifdef Q_OS_MAC + ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->clearToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + connect(ui->coloringRulesTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(colorRuleSelectionChanged(const QItemSelection &, const QItemSelection &))); + connect(&colorRuleDelegate_, SIGNAL(invalidField(const QModelIndex&, const QString&)), + this, SLOT(invalidField(const QModelIndex&, const QString&))); + connect(&colorRuleDelegate_, SIGNAL(validField(const QModelIndex&)), + this, SLOT(validField(const QModelIndex&))); + connect(ui->coloringRulesTreeView, &QTreeView::clicked, this, &ColoringRulesDialog::treeItemClicked); + connect(&colorRuleModel_, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(rowCountChanged())); + connect(&colorRuleModel_, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(rowCountChanged())); + + rowCountChanged(); + + import_button_ = ui->buttonBox->addButton(tr("Import…"), QDialogButtonBox::ApplyRole); + import_button_->setToolTip(tr("Select a file and add its filters to the end of the list.")); + export_button_ = ui->buttonBox->addButton(tr("Export…"), QDialogButtonBox::ApplyRole); + export_button_->setToolTip(tr("Save filters in a file.")); + + CopyFromProfileButton * copy_button = new CopyFromProfileButton(this, COLORFILTERS_FILE_NAME, tr("Copy coloring rules from another profile.")); + ui->buttonBox->addButton(copy_button, QDialogButtonBox::ActionRole); + connect(copy_button, &CopyFromProfileButton::copyProfile, this, &ColoringRulesDialog::copyFromProfile); + + QString abs_path = gchar_free_to_qstring(get_persconffile_path(COLORFILTERS_FILE_NAME, TRUE)); + if (file_exists(abs_path.toUtf8().constData())) { + ui->pathLabel->setText(abs_path); + ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString()); + ui->pathLabel->setToolTip(tr("Open ") + COLORFILTERS_FILE_NAME); + ui->pathLabel->setEnabled(true); + } + + if (!add_filter.isEmpty()) { + colorRuleModel_.addColor(false, add_filter, palette().color(QPalette::Text), palette().color(QPalette::Base)); + + //setup the buttons appropriately + ui->coloringRulesTreeView->setCurrentIndex(colorRuleModel_.index(0, 0)); + + //set edit on display filter + ui->coloringRulesTreeView->edit(colorRuleModel_.index(0, 1)); + }else { + ui->coloringRulesTreeView->setCurrentIndex(QModelIndex()); + } + + updateHint(); +} + +ColoringRulesDialog::~ColoringRulesDialog() +{ + delete ui; +} + +void ColoringRulesDialog::copyFromProfile(QString filename) +{ + QString err; + + if (!colorRuleModel_.importColors(filename, err)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err.toUtf8().constData()); + } + + for (int i = 0; i < colorRuleModel_.columnCount(); i++) { + ui->coloringRulesTreeView->resizeColumnToContents(i); + } +} + +void ColoringRulesDialog::showEvent(QShowEvent *) +{ + ui->fGPushButton->setFixedHeight(ui->copyToolButton->geometry().height()); + ui->bGPushButton->setFixedHeight(ui->copyToolButton->geometry().height()); +#ifndef Q_OS_MAC + ui->displayFilterPushButton->setFixedHeight(ui->copyToolButton->geometry().height()); +#endif +} + +void ColoringRulesDialog::rowCountChanged() +{ + ui->clearToolButton->setEnabled(colorRuleModel_.rowCount() > 0); +} + +bool ColoringRulesDialog::isValidFilter(QString filter, QString * error) +{ + dfilter_t *dfp = NULL; + df_error_t *df_err = NULL; + + if (dfilter_compile(filter.toUtf8().constData(), &dfp, &df_err)) { + dfilter_free(dfp); + return true; + } + + if (df_err) + { + error->append(df_err->msg); + df_error_free(&df_err); + } + + return false; +} + +void ColoringRulesDialog::treeItemClicked(const QModelIndex &index) +{ + QModelIndex idx = ui->coloringRulesTreeView->model()->index(index.row(), ColoringRulesModel::colFilter); + QString filter = idx.data(Qt::DisplayRole).toString(); + QString err; + if (! isValidFilter(filter, &err) && index.data(Qt::CheckStateRole).toInt() == Qt::Checked) + { + errors_.insert(index, err); + updateHint(index); + } + else + { + QList keys = errors_.keys(); + bool update = false; + foreach (QModelIndex key, keys) + { + if (key.row() == index.row()) + { + errors_.remove(key); + update = true; + } + } + + if (update) + updateHint(index); + } +} + +void ColoringRulesDialog::invalidField(const QModelIndex &index, const QString& errMessage) +{ + errors_.insert(index, errMessage); + updateHint(index); +} + +void ColoringRulesDialog::validField(const QModelIndex &index) +{ + QList keys = errors_.keys(); + bool update = false; + foreach (QModelIndex key, keys) + { + if (key.row() == index.row()) + { + errors_.remove(key); + update = true; + } + } + + if (update) + updateHint(index); +} + +void ColoringRulesDialog::updateHint(QModelIndex idx) +{ + QString hint = ""; + QString error_text; + bool enable_save = true; + + if (errors_.count() > 0) { + //take the list of QModelIndexes and sort them so first color rule error is displayed + //This isn't the most efficent algorithm, but the list shouldn't be large to matter + QList keys = errors_.keys(); + + //list is not guaranteed to be sorted, so force it + std::sort(keys.begin(), keys.end()); + const QModelIndex& error_key = keys[0]; + error_text = QString("%1: %2") + .arg(colorRuleModel_.data(colorRuleModel_.index(error_key.row(), ColoringRulesModel::colName), Qt::DisplayRole).toString()) + .arg(errors_[error_key]); + } + + if (error_text.isEmpty()) { + hint += tr("Double click to edit. Drag to move. Rules are processed in order until a match is found."); + } else { + hint += error_text; + if (idx.isValid()) + { + QModelIndex fiIdx = ui->coloringRulesTreeView->model()->index(idx.row(), ColoringRulesModel::colName); + if (fiIdx.data(Qt::CheckStateRole).toInt() == Qt::Checked) + enable_save = false; + } + else + enable_save = false; + } + + hint += ""; + ui->hintLabel->setText(hint); + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable_save); +} + +void ColoringRulesDialog::setColorButtons(QModelIndex &index) +{ + QString color_button_ss = + "QPushButton {" + " border: 1px solid palette(Dark);" + " padding-left: %1px;" + " padding-right: %1px;" + " color: %2;" + " background-color: %3;" + "}"; + + int one_em = fontMetrics().height(); + QVariant fg = colorRuleModel_.data(index, Qt::ForegroundRole); + QVariant bg = colorRuleModel_.data(index, Qt::BackgroundRole); + if (fg.isNull() || bg.isNull()) { + //should never happen + ui->fGPushButton->setVisible(false); + ui->bGPushButton->setVisible(false); + } else { + QString fg_color = fg.toString(); + QString bg_color = bg.toString(); + + ui->fGPushButton->setStyleSheet(color_button_ss.arg(one_em).arg(bg_color).arg(fg_color)); + ui->bGPushButton->setStyleSheet(color_button_ss.arg(one_em).arg(fg_color).arg(bg_color)); + } +} + +void ColoringRulesDialog::colorRuleSelectionChanged(const QItemSelection&, const QItemSelection&) +{ + QModelIndexList selectedList = ui->coloringRulesTreeView->selectionModel()->selectedIndexes(); + + //determine the number of unique rows + QHash selectedRows; + foreach (const QModelIndex &index, selectedList) { + selectedRows.insert(index.row(), index); + } + + qsizetype num_selected = selectedRows.count(); + if (num_selected == 1) { + setColorButtons(selectedList[0]); + } + + ui->copyToolButton->setEnabled(num_selected == 1); + ui->deleteToolButton->setEnabled(num_selected > 0); + ui->fGPushButton->setVisible(num_selected == 1); + ui->bGPushButton->setVisible(num_selected == 1); + ui->displayFilterPushButton->setVisible(num_selected == 1); +} + +void ColoringRulesDialog::changeColor(bool foreground) +{ + QModelIndex current = ui->coloringRulesTreeView->currentIndex(); + if (!current.isValid()) + return; + + QColorDialog *color_dlg = new QColorDialog(); + color_dlg->setCurrentColor(colorRuleModel_.data(current, foreground ? Qt::ForegroundRole : Qt::BackgroundRole).toString()); + + connect(color_dlg, &QColorDialog::colorSelected, std::bind(&ColoringRulesDialog::colorChanged, this, foreground, std::placeholders::_1)); + color_dlg->setWindowModality(Qt::ApplicationModal); + color_dlg->setAttribute(Qt::WA_DeleteOnClose); + color_dlg->show(); +} + +void ColoringRulesDialog::colorChanged(bool foreground, const QColor &cc) +{ + QModelIndex current = ui->coloringRulesTreeView->currentIndex(); + if (!current.isValid()) + return; + + colorRuleModel_.setData(current, cc, foreground ? Qt::ForegroundRole : Qt::BackgroundRole); + setColorButtons(current); +} + +void ColoringRulesDialog::on_fGPushButton_clicked() +{ + changeColor(); +} + +void ColoringRulesDialog::on_bGPushButton_clicked() +{ + changeColor(false); +} + +void ColoringRulesDialog::on_displayFilterPushButton_clicked() +{ + QModelIndex current = ui->coloringRulesTreeView->currentIndex(); + if (!current.isValid()) + return; + + QString filter = colorRuleModel_.data(colorRuleModel_.index(current.row(), ColoringRulesModel::colFilter), Qt::DisplayRole).toString(); + emit filterAction(filter, FilterAction::ActionApply, FilterAction::ActionTypePlain); +} + +void ColoringRulesDialog::addRule(bool copy_from_current) +{ + const QModelIndex ¤t = ui->coloringRulesTreeView->currentIndex(); + if (copy_from_current && !current.isValid()) + return; + + //always add rules at the top of the list + if (copy_from_current) { + colorRuleModel_.copyRow(colorRuleModel_.index(0, 0).row(), current.row()); + } else { + if (!colorRuleModel_.insertRows(0, 1)) { + return; + } + } + + //set edit on display filter + ui->coloringRulesTreeView->edit(colorRuleModel_.index(0, 1)); +} + +void ColoringRulesDialog::on_newToolButton_clicked() +{ + addRule(); +} + +void ColoringRulesDialog::on_deleteToolButton_clicked() +{ + QModelIndexList selectedList = ui->coloringRulesTreeView->selectionModel()->selectedIndexes(); + qsizetype num_selected = selectedList.count() / colorRuleModel_.columnCount(); + if (num_selected > 0) { + //list is not guaranteed to be sorted, so force it + std::sort(selectedList.begin(), selectedList.end()); + + //walk the list from the back because deleting a value in + //the middle will leave the selectedList out of sync and + //delete the wrong elements + for (int i = static_cast(selectedList.count()) - 1; i >= 0; i--) { + QModelIndex deleteIndex = selectedList[i]; + //selectedList includes all cells, use first column as key to remove row + if (deleteIndex.isValid() && (deleteIndex.column() == 0)) { + colorRuleModel_.removeRows(deleteIndex.row(), 1); + } + } + } +} + +void ColoringRulesDialog::on_copyToolButton_clicked() +{ + addRule(true); +} + +void ColoringRulesDialog::on_clearToolButton_clicked() +{ + colorRuleModel_.removeRows(0, colorRuleModel_.rowCount()); +} + +void ColoringRulesDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + QString err; + + if (button == import_button_) { + QString file_name = WiresharkFileDialog::getOpenFileName(this, mainApp->windowTitleString(tr("Import Coloring Rules")), + mainApp->openDialogInitialDir().path()); + if (!file_name.isEmpty()) { + if (!colorRuleModel_.importColors(file_name, err)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err.toUtf8().constData()); + } + } + } else if (button == export_button_) { + int num_items = static_cast(ui->coloringRulesTreeView->selectionModel()->selectedIndexes().count()) / colorRuleModel_.columnCount(); + + if (num_items < 1) { + num_items = colorRuleModel_.rowCount(); + } + + if (num_items < 1) + return; + + QString caption = mainApp->windowTitleString(tr("Export %1 Coloring Rules").arg(num_items)); + QString file_name = WiresharkFileDialog::getSaveFileName(this, caption, + mainApp->openDialogInitialDir().path()); + if (!file_name.isEmpty()) { + if (!colorRuleModel_.exportColors(file_name, err)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err.toUtf8().constData()); + } + } + } +} + +void ColoringRulesDialog::on_buttonBox_accepted() +{ + QString err; + int ret = QDialog::Accepted; + if (!colorRuleModel_.writeColors(err)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err.toUtf8().constData()); + ret = QDialog::Rejected; + } + done(ret); +} + +void ColoringRulesDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_COLORING_RULES_DIALOG); +} diff --git a/ui/qt/coloring_rules_dialog.h b/ui/qt/coloring_rules_dialog.h new file mode 100644 index 00000000..5581e106 --- /dev/null +++ b/ui/qt/coloring_rules_dialog.h @@ -0,0 +1,79 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COLORING_RULES_DIALOG_H +#define COLORING_RULES_DIALOG_H + +#include "geometry_state_dialog.h" +#include "filter_action.h" + +#include +#include + +#include + +class QAbstractButton; + +namespace Ui { +class ColoringRulesDialog; +} + +class ColoringRulesDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit ColoringRulesDialog(QWidget *parent = 0, QString add_filter = QString()); + ~ColoringRulesDialog(); + +signals: + void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + +protected: + void showEvent(QShowEvent *); + +private slots: + void copyFromProfile(QString fileName); + void colorRuleSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void colorChanged(bool foreground, const QColor &cc); + void on_fGPushButton_clicked(); + void on_bGPushButton_clicked(); + void on_displayFilterPushButton_clicked(); + void on_newToolButton_clicked(); + void on_deleteToolButton_clicked(); + void on_copyToolButton_clicked(); + void on_clearToolButton_clicked(); + void on_buttonBox_clicked(QAbstractButton *button); + void on_buttonBox_accepted(); + void on_buttonBox_helpRequested(); + void rowCountChanged(); + void invalidField(const QModelIndex &index, const QString& errMessage); + void validField(const QModelIndex &index); + void treeItemClicked(const QModelIndex &index); + +private: + Ui::ColoringRulesDialog *ui; + QPushButton *import_button_; + QPushButton *export_button_; + ColoringRulesModel colorRuleModel_; + ColoringRulesDelegate colorRuleDelegate_; + + QMap errors_; + + void checkUnknownColorfilters(); + void setColorButtons(QModelIndex &index); + void updateHint(QModelIndex idx = QModelIndex()); + + void addRule(bool copy_from_current = false); + void changeColor(bool foreground = true); + + bool isValidFilter(QString filter, QString *error); +}; + +#endif // COLORING_RULES_DIALOG_H diff --git a/ui/qt/coloring_rules_dialog.ui b/ui/qt/coloring_rules_dialog.ui new file mode 100644 index 00000000..16935af8 --- /dev/null +++ b/ui/qt/coloring_rules_dialog.ui @@ -0,0 +1,245 @@ + + + ColoringRulesDialog + + + + 0 + 0 + 650 + 480 + + + + Dialog + + + + + + true + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + Qt::ElideMiddle + + + false + + + true + + + false + + + false + + + true + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + + + Add a new coloring rule. + + + + + + + + + + false + + + Delete this coloring rule. + + + + + + + + + + false + + + Duplicate this coloring rule. + + + + + + + false + + + Clear all coloring rules. + + + + + + + false + + + Set the foreground color for this rule. + + + QPushButton { border: 1px solid palette(Dark); } + + + Foreground + + + false + + + true + + + + + + + false + + + Set the background color for this rule. + + + QPushButton { border: 1px solid palette(Dark); } + + + Background + + + false + + + true + + + + + + + false + + + Set the display filter using this rule. + + + Apply as filter + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+ + TabnavTreeView + QTreeView +
widgets/tabnav_tree_view.h
+
+ + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+
+ + + + buttonBox + rejected() + ColoringRulesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/column_editor_frame.cpp b/ui/qt/column_editor_frame.cpp new file mode 100644 index 00000000..ea527b9a --- /dev/null +++ b/ui/qt/column_editor_frame.cpp @@ -0,0 +1,198 @@ +/* column_editor_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include + +#include "main_application.h" + +#include "column_editor_frame.h" +#include + +#include +#include +#include + +ColumnEditorFrame::ColumnEditorFrame(QWidget *parent) : + AccordionFrame(parent), + ui(new Ui::ColumnEditorFrame), + cur_column_(-1) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC + foreach (QWidget *w, findChildren()) { + w->setAttribute(Qt::WA_MacSmallSize, true); + } +#endif + + for (int i = 0; i < NUM_COL_FMTS; i++) { + ui->typeComboBox->addItem(col_format_desc(i), QVariant(i)); + } + + connect(ui->fieldsNameLineEdit, &FieldFilterEdit::textChanged, + ui->fieldsNameLineEdit, &FieldFilterEdit::checkCustomColumn); + connect(ui->fieldsNameLineEdit, &FieldFilterEdit::textChanged, + this, &ColumnEditorFrame::checkCanResolve); +} + +ColumnEditorFrame::~ColumnEditorFrame() +{ + delete ui; +} + +bool ColumnEditorFrame::syntaxIsValid(void) +{ + // Fields must be a valid filter. + // Occurrence must be empty or valid. + return ((ui->fieldsNameLineEdit->syntaxState() == SyntaxLineEdit::Valid) && + (ui->occurrenceLineEdit->syntaxState() != SyntaxLineEdit::Invalid)); +} + +void ColumnEditorFrame::setFields(int index) +{ + bool ok = true; + + if (index == COL_CUSTOM) { + ui->fieldsNameLineEdit->setText(saved_fields_); + ui->fieldsNameLineEdit->checkCustomColumn(saved_fields_); + ui->occurrenceLineEdit->setText(saved_occurrence_); + ui->occurrenceLineEdit->checkInteger(saved_occurrence_); + ok = syntaxIsValid(); + } else { + ui->fieldsNameLineEdit->clear(); + ui->fieldsNameLineEdit->setSyntaxState(SyntaxLineEdit::Empty); + ui->occurrenceLineEdit->clear(); + ui->occurrenceLineEdit->setSyntaxState(SyntaxLineEdit::Empty); + ui->resolvedCheckBox->setEnabled(false); + } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok); +} + +void ColumnEditorFrame::editColumn(int column) +{ + cur_column_ = column; + ui->titleLineEdit->setText(get_column_title(column)); + saved_fields_ = get_column_custom_fields(column); + saved_occurrence_ = QString::number(get_column_custom_occurrence(column)); + ui->typeComboBox->setCurrentIndex(get_column_format(column)); + ui->resolvedCheckBox->setChecked(get_column_resolved(column)); + setFields(ui->typeComboBox->currentIndex()); +} + +void ColumnEditorFrame::showEvent(QShowEvent *event) +{ + ui->titleLineEdit->setFocus(); + ui->titleLineEdit->selectAll(); + + AccordionFrame::showEvent(event); +} + +void ColumnEditorFrame::on_typeComboBox_activated(int index) +{ + setFields(index); +} + +void ColumnEditorFrame::on_fieldsNameLineEdit_textEdited(const QString &fields) +{ + ui->fieldsNameLineEdit->checkCustomColumn(fields); + if (ui->typeComboBox->currentIndex() != COL_CUSTOM) { + ui->typeComboBox->setCurrentIndex(COL_CUSTOM); + ui->occurrenceLineEdit->setText(saved_occurrence_); + } + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(syntaxIsValid()); + + saved_fields_ = fields; +} + +void ColumnEditorFrame::on_occurrenceLineEdit_textEdited(const QString &occurrence) +{ + ui->occurrenceLineEdit->checkInteger(occurrence); + if (ui->typeComboBox->currentIndex() != COL_CUSTOM) { + ui->typeComboBox->setCurrentIndex(COL_CUSTOM); + ui->fieldsNameLineEdit->setText(saved_fields_); + } + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(syntaxIsValid()); + + saved_occurrence_ = occurrence; +} + +void ColumnEditorFrame::on_buttonBox_rejected() +{ + cur_column_ = -1; + animatedHide(); +} + +void ColumnEditorFrame::on_buttonBox_accepted() +{ + QByteArray col_str; + if (cur_column_ >= 0) { + col_str = ui->titleLineEdit->text().toUtf8(); + set_column_title(cur_column_, col_str.constData()); + set_column_format(cur_column_, ui->typeComboBox->currentIndex()); + if (ui->typeComboBox->currentIndex() == COL_CUSTOM) { + gint width = recent_get_column_width(cur_column_); + gchar xalign = recent_get_column_xalign(cur_column_); + col_str = ui->fieldsNameLineEdit->text().toUtf8(); + set_column_custom_fields(cur_column_, col_str.constData()); + recent_set_column_width(cur_column_, width); + recent_set_column_xalign(cur_column_, xalign); + if (!ui->occurrenceLineEdit->text().isEmpty()) { + set_column_custom_occurrence(cur_column_, ui->occurrenceLineEdit->text().toInt()); + } + if (ui->resolvedCheckBox->isEnabled()) { + set_column_resolved(cur_column_, ui->resolvedCheckBox->isChecked()); + } + } + prefs_main_write(); + emit columnEdited(); + } + + on_buttonBox_rejected(); +} + +void ColumnEditorFrame::keyPressEvent(QKeyEvent *event) +{ + if (event->modifiers() == Qt::NoModifier) { + if (event->key() == Qt::Key_Escape) { + on_buttonBox_rejected(); + } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + if (ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) { + on_buttonBox_accepted(); + } else if (ui->fieldsNameLineEdit->syntaxState() == SyntaxLineEdit::Empty) { + mainApp->pushStatus(MainApplication::FilterSyntax, tr("Missing fields.")); + } else if (ui->fieldsNameLineEdit->syntaxState() != SyntaxLineEdit::Valid) { + mainApp->pushStatus(MainApplication::FilterSyntax, tr("Invalid fields.")); + } else if (ui->occurrenceLineEdit->syntaxState() == SyntaxLineEdit::Invalid) { + mainApp->pushStatus(MainApplication::FilterSyntax, tr("Invalid occurrence value.")); + } + } + } + + AccordionFrame::keyPressEvent(event); +} + +void ColumnEditorFrame::checkCanResolve() +{ + if (ui->fieldsNameLineEdit->syntaxState() == SyntaxLineEdit::Valid && column_prefs_custom_resolve(ui->fieldsNameLineEdit->text().toUtf8().constData())) { + ui->resolvedCheckBox->setEnabled(true); + } else { + ui->resolvedCheckBox->setEnabled(false); + ui->resolvedCheckBox->setChecked(false); + } +} diff --git a/ui/qt/column_editor_frame.h b/ui/qt/column_editor_frame.h new file mode 100644 index 00000000..3778fb0d --- /dev/null +++ b/ui/qt/column_editor_frame.h @@ -0,0 +1,52 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COLUMN_EDITOR_FRAME_H +#define COLUMN_EDITOR_FRAME_H + +#include "accordion_frame.h" + +namespace Ui { +class ColumnEditorFrame; +} + +class ColumnEditorFrame : public AccordionFrame +{ + Q_OBJECT + +public: + explicit ColumnEditorFrame(QWidget *parent = nullptr); + ~ColumnEditorFrame(); + void editColumn(int column); + +signals: + void columnEdited(); + +protected: + virtual void showEvent(QShowEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + +private slots: + void on_typeComboBox_activated(int index); + void on_fieldsNameLineEdit_textEdited(const QString &fields); + void on_occurrenceLineEdit_textEdited(const QString &occurrence); + void on_buttonBox_rejected(); + void on_buttonBox_accepted(); + void checkCanResolve(void); + +private: + bool syntaxIsValid(void); + Ui::ColumnEditorFrame *ui; + int cur_column_; + QString saved_fields_; + QString saved_occurrence_; + void setFields(int index); +}; + +#endif // COLUMN_EDITOR_FRAME_H diff --git a/ui/qt/column_editor_frame.ui b/ui/qt/column_editor_frame.ui new file mode 100644 index 00000000..682be2bc --- /dev/null +++ b/ui/qt/column_editor_frame.ui @@ -0,0 +1,169 @@ + + + ColumnEditorFrame + + + + 0 + 0 + 1018 + 34 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + + + Title: + + + + + + + + + + Qt::Horizontal + + + + 20 + 5 + + + + + + + + Type: + + + + + + + + + + Qt::Horizontal + + + + 20 + 5 + + + + + + + + Fields: + + + + + + + + + + Qt::Horizontal + + + + 20 + 5 + + + + + + + + Occurrence: + + + + + + + + + + Resolve Names: + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + + + Qt::RightToLeft + + + + + + + Qt::Horizontal + + + + 20 + 5 + + + + + + + + + 16777215 + 27 + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + AccordionFrame + QFrame +
accordion_frame.h
+ 1 +
+ + FieldFilterEdit + QLineEdit +
widgets/field_filter_edit.h
+
+ + SyntaxLineEdit + QLineEdit +
widgets/syntax_line_edit.h
+
+
+ + +
diff --git a/ui/qt/column_preferences_frame.cpp b/ui/qt/column_preferences_frame.cpp new file mode 100644 index 00000000..961e2737 --- /dev/null +++ b/ui/qt/column_preferences_frame.cpp @@ -0,0 +1,128 @@ +/* column_preferences_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include + +#include +#include "column_preferences_frame.h" +#include +#include +#include +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include + +ColumnPreferencesFrame::ColumnPreferencesFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::ColumnPreferencesFrame) +{ + ui->setupUi(this); + + model_ = new ColumnListModel(); + proxyModel_ = new ColumnProxyModel(); + proxyModel_->setSourceModel(model_); + + int one_em = ui->columnTreeView->fontMetrics().height(); + ui->columnTreeView->setColumnWidth(ColumnListModel::COL_FIELDS, one_em * 10); + ui->columnTreeView->setColumnWidth(ColumnListModel::COL_OCCURRENCE, one_em * 5); + + ui->columnTreeView->setMinimumWidth(one_em * 20); + ui->columnTreeView->setMinimumHeight(one_em * 12); + + ui->columnTreeView->setSelectionMode(QAbstractItemView::SingleSelection); + ui->columnTreeView->setDragEnabled(true); + ui->columnTreeView->viewport()->setAcceptDrops(true); + ui->columnTreeView->setDropIndicatorShown(true); + ui->columnTreeView->setDragDropMode(QAbstractItemView::InternalMove); + ui->columnTreeView->setContextMenuPolicy(Qt::CustomContextMenu); + + ui->newToolButton->setStockIcon("list-add"); + ui->deleteToolButton->setStockIcon("list-remove"); + + ui->columnTreeView->setModel(proxyModel_); + delegate_ = new ColumnTypeDelegate(); + ui->columnTreeView->setItemDelegate(delegate_); + ui->columnTreeView->setSortingEnabled(false); + + ui->columnTreeView->resizeColumnToContents(ColumnListModel::COL_DISPLAYED); + ui->columnTreeView->resizeColumnToContents(ColumnListModel::COL_TITLE); + ui->columnTreeView->resizeColumnToContents(ColumnListModel::COL_TYPE); + ui->columnTreeView->resizeColumnToContents(ColumnListModel::COL_OCCURRENCE); + ui->columnTreeView->resizeColumnToContents(ColumnListModel::COL_RESOLVED); + + connect(ui->columnTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &ColumnPreferencesFrame::selectionChanged); +} + +ColumnPreferencesFrame::~ColumnPreferencesFrame() +{ + delete delegate_; + delete proxyModel_; + delete model_; + delete ui; +} + +void ColumnPreferencesFrame::unstash() +{ + model_->saveColumns(); + mainApp->emitAppSignal(MainApplication::ColumnsChanged); +} + +void ColumnPreferencesFrame::on_newToolButton_clicked() +{ + model_->addEntry(); +} + +void ColumnPreferencesFrame::on_deleteToolButton_clicked() +{ + if (ui->columnTreeView->selectionModel()->selectedIndexes().count() > 0) + { + QModelIndex selIndex = ui->columnTreeView->selectionModel()->selectedIndexes().at(0); + model_->deleteEntry(proxyModel_->mapToSource(selIndex).row()); + } +} + +void ColumnPreferencesFrame::selectionChanged(const QItemSelection &/*selected*/, + const QItemSelection &/*deselected*/) +{ + ui->deleteToolButton->setEnabled(ui->columnTreeView->selectionModel()->selectedIndexes().count() > 0); +} + +void ColumnPreferencesFrame::on_chkShowDisplayedOnly_stateChanged(int /*state*/) +{ + proxyModel_->setShowDisplayedOnly(ui->chkShowDisplayedOnly->checkState() == Qt::Checked ? true : false); +} + +void ColumnPreferencesFrame::on_columnTreeView_customContextMenuRequested(const QPoint &pos) +{ + QMenu * contextMenu = new QMenu(this); + contextMenu->setAttribute(Qt::WA_DeleteOnClose); + QAction * action = contextMenu->addAction(tr("Reset all changes")); + connect(action, &QAction::triggered, this, &ColumnPreferencesFrame::resetAction); + contextMenu->popup(mapToGlobal(pos)); +} + +void ColumnPreferencesFrame::resetAction(bool /*checked*/) +{ + model_->reset(); +} diff --git a/ui/qt/column_preferences_frame.h b/ui/qt/column_preferences_frame.h new file mode 100644 index 00000000..baab9418 --- /dev/null +++ b/ui/qt/column_preferences_frame.h @@ -0,0 +1,48 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COLUMN_PREFERENCES_FRAME_H +#define COLUMN_PREFERENCES_FRAME_H + +#include + +#include +#include + +namespace Ui { +class ColumnPreferencesFrame; +} + +class ColumnPreferencesFrame : public QFrame +{ + Q_OBJECT + +public: + explicit ColumnPreferencesFrame(QWidget *parent = Q_NULLPTR); + ~ColumnPreferencesFrame(); + + void unstash(); + +private: + Ui::ColumnPreferencesFrame *ui; + ColumnListModel * model_; + ColumnProxyModel * proxyModel_; + ColumnTypeDelegate * delegate_; + +private slots: + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void on_newToolButton_clicked(); + void on_deleteToolButton_clicked(); + void on_chkShowDisplayedOnly_stateChanged(int); + + void on_columnTreeView_customContextMenuRequested(const QPoint &pos); + void resetAction(bool checked = false); +}; + +#endif // COLUMN_PREFERENCES_FRAME_H diff --git a/ui/qt/column_preferences_frame.ui b/ui/qt/column_preferences_frame.ui new file mode 100644 index 00000000..38a7e3a3 --- /dev/null +++ b/ui/qt/column_preferences_frame.ui @@ -0,0 +1,100 @@ + + + ColumnPreferencesFrame + + + + 0 + 0 + 550 + 456 + + + + + 0 + 0 + + + + Frame + + + 0 + + + + + + true + + + QAbstractItemView::InternalMove + + + false + + + false + + + false + + + + + + + + + Add a new column + + + + + + + + + + Delete selected column + + + + + + + + + + Show displayed columns only + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+
+ + +
diff --git a/ui/qt/compiled_filter_output.cpp b/ui/qt/compiled_filter_output.cpp new file mode 100644 index 00000000..1db9c899 --- /dev/null +++ b/ui/qt/compiled_filter_output.cpp @@ -0,0 +1,120 @@ +/* compiled_filter_output.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include +#include "compiled_filter_output.h" + +#ifdef HAVE_LIBPCAP +#ifdef __MINGW32__ +#include <_bsd_types.h> +#endif +#include +#endif + +#include "capture_opts.h" +#include +#include "ui/capture_globals.h" + +#include "main_application.h" + +#include +#include + +CompiledFilterOutput::CompiledFilterOutput(QWidget *parent, QStringList &intList, QString &compile_filter) : + GeometryStateDialog(parent), + intList_(intList), + compile_filter_(compile_filter), + ui(new Ui::CompiledFilterOutput) +{ + ui->setupUi(this); + loadGeometry(); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->filterList->setCurrentFont(mainApp->monospaceFont()); + + copy_bt_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); + copy_bt_->setToolTip(tr("Copy filter text to the clipboard.")); + connect(copy_bt_, &QPushButton::clicked, this, &CompiledFilterOutput::copyFilterText); + + QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close); + close_bt->setDefault(true); + + interface_list_ = ui->interfaceList; + pcap_compile_mtx = g_new(GMutex,1); + g_mutex_init(pcap_compile_mtx); +#ifdef HAVE_LIBPCAP + compileFilter(); +#endif +} + +CompiledFilterOutput::~CompiledFilterOutput() +{ + // For some reason closing this dialog either lowers the Capture Options dialog + // or raises the main window. Work around the problem for now by manually raising + // and activating our parent (presumably the Capture Options dialog). + if (parentWidget()) { + parentWidget()->raise(); + parentWidget()->activateWindow(); + } + delete ui; +} + +#ifdef HAVE_LIBPCAP +void CompiledFilterOutput::compileFilter() +{ + struct bpf_program fcode; + + foreach (QString interfaces, intList_) { + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + + if (interfaces.compare(device->display_name)) { + continue; + } else { + pcap_t *pd = pcap_open_dead(device->active_dlt, WTAP_MAX_PACKET_SIZE_STANDARD); + if (pd == NULL) + break; + g_mutex_lock(pcap_compile_mtx); + if (pcap_compile(pd, &fcode, compile_filter_.toUtf8().data(), 1, 0) < 0) { + compile_results.insert(interfaces, QString(pcap_geterr(pd))); + g_mutex_unlock(pcap_compile_mtx); + ui->interfaceList->addItem(new QListWidgetItem(QIcon(":expert/expert_error.png"),interfaces)); + } else { + GString *bpf_code_dump = g_string_new(""); + struct bpf_insn *insn = fcode.bf_insns; + int ii, n = fcode.bf_len; + for (ii = 0; ii < n; ++insn, ++ii) { + g_string_append(bpf_code_dump, bpf_image(insn, ii)); + g_string_append(bpf_code_dump, "\n"); + } + g_mutex_unlock(pcap_compile_mtx); + compile_results.insert(interfaces, QString(bpf_code_dump->str)); + g_string_free(bpf_code_dump, TRUE); + ui->interfaceList->addItem(new QListWidgetItem(interfaces)); + } + break; + } + } + } +} +#endif + +void CompiledFilterOutput::on_interfaceList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *) +{ + QString interface = current->text(); + QHash::const_iterator iter = compile_results.find(interface); + ui->filterList->clear(); + ui->filterList->setPlainText(iter.value()); +} + +void CompiledFilterOutput::copyFilterText() +{ + mainApp->clipboard()->setText(ui->filterList->toPlainText()); +} diff --git a/ui/qt/compiled_filter_output.h b/ui/qt/compiled_filter_output.h new file mode 100644 index 00000000..a94fc4f0 --- /dev/null +++ b/ui/qt/compiled_filter_output.h @@ -0,0 +1,52 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COMPILEDFILTEROUTPUT_H +#define COMPILEDFILTEROUTPUT_H + +#include "geometry_state_dialog.h" + +#include +#include +#include +#include + +#include + +namespace Ui { +class CompiledFilterOutput; +} + +class CompiledFilterOutput : public GeometryStateDialog +{ + Q_OBJECT + +private: + QStringList intList_; + QString &compile_filter_; + Ui::CompiledFilterOutput *ui; + GMutex *pcap_compile_mtx; + QHash compile_results; + QListWidget *interface_list_; + QPushButton *copy_bt_; +#ifdef HAVE_LIBPCAP + void compileFilter(); +#endif + +public: + explicit CompiledFilterOutput(QWidget *parent = 0, QStringList &intList = *new QStringList(), QString &filter = *new QString()); + + ~CompiledFilterOutput(); + +private slots: + void on_interfaceList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void copyFilterText(); +}; + +#endif // COMPILEDFILTEROUTPUT_H diff --git a/ui/qt/compiled_filter_output.ui b/ui/qt/compiled_filter_output.ui new file mode 100644 index 00000000..0554e822 --- /dev/null +++ b/ui/qt/compiled_filter_output.ui @@ -0,0 +1,80 @@ + + + CompiledFilterOutput + + + + 0 + 0 + 654 + 380 + + + + Compiled Filter Output + + + + + + + + QAbstractItemView::NoEditTriggers + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + false + + + + + + + + buttonBox + accepted() + CompiledFilterOutput + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CompiledFilterOutput + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/conversation_colorize_action.cpp b/ui/qt/conversation_colorize_action.cpp new file mode 100644 index 00000000..9518e1ee --- /dev/null +++ b/ui/qt/conversation_colorize_action.cpp @@ -0,0 +1,62 @@ +/* conversation_colorize_action.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "conversation_colorize_action.h" + +#include + +#include + +#include "epan/conversation_filter.h" + +#include + +#include + +ConversationAction::ConversationAction(QObject *parent, conversation_filter_s *conv_filter) : + QAction(parent), + color_number_(-1) +{ + conv_filter_ = conv_filter; + if (conv_filter_) { + setText(conv_filter_->display_name); + } +} + +void ConversationAction::setPacketInfo(struct _packet_info *pinfo) +{ + bool enable = false; + if (conv_filter_ && pinfo) { + enable = conv_filter_->is_filter_valid(pinfo, conv_filter_->user_data); + if (enable) { + filter_ba_ = gchar_free_to_qbytearray(conv_filter_->build_filter_string(pinfo, conv_filter_->user_data)); + } + } + setEnabled(enable); + + // If we're the "New Coloring Rule" item, enable or disable our parent menu. + QMenu *parent_submenu = qobject_cast(parent()); + if (color_number_ < 0 || !parent_submenu) return; + parent_submenu->setEnabled(enable); +} + +void ConversationAction::setFieldFilter(const QByteArray field_filter) +{ + filter_ba_ = field_filter; + setEnabled(!filter_ba_.isEmpty()); +} + +bool ConversationAction::isFilterValid(struct _packet_info *pinfo) +{ + bool valid = false; + if (conv_filter_ && pinfo) { + valid = conv_filter_->is_filter_valid(pinfo, conv_filter_->user_data); + } + return valid; +} diff --git a/ui/qt/conversation_colorize_action.h b/ui/qt/conversation_colorize_action.h new file mode 100644 index 00000000..47f9ea50 --- /dev/null +++ b/ui/qt/conversation_colorize_action.h @@ -0,0 +1,65 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CONVERSATIONCOLORIZEACTION_H +#define CONVERSATIONCOLORIZEACTION_H + +#include + +struct conversation_filter_s; +struct _packet_info; + +// Actions for "Conversation Filter" and "Colorize with Filter" menu items. + +class ConversationAction : public QAction +{ + Q_OBJECT +public: + ConversationAction(QObject *parent, struct conversation_filter_s *conv_filter = NULL); + + bool isFilterValid(struct _packet_info *pinfo); + + const QByteArray filter() { return filter_ba_; } + + void setColorNumber(int color_number) { color_number_ = color_number; } + int colorNumber() { return color_number_; } + +public slots: + // Exactly one of these should be connected. + void setPacketInfo(struct _packet_info *pinfo); + void setFieldFilter(const QByteArray field_filter); + +private: + struct conversation_filter_s *conv_filter_; + QByteArray filter_ba_; + int color_number_; +}; + +class ColorizeAction : public QAction +{ + Q_OBJECT +public: + ColorizeAction(QObject *parent) : QAction(parent), + color_number_(-1) + {} + + const QByteArray filter() { return filter_ba_; } + + void setColorNumber(int color_number) { color_number_ = color_number; } + int colorNumber() { return color_number_; } + +public slots: + void setFieldFilter(const QByteArray field_filter) { filter_ba_ = field_filter; } + +private: + QByteArray filter_ba_; + int color_number_; +}; + +#endif // CONVERSATIONCOLORIZEACTION_H diff --git a/ui/qt/conversation_dialog.cpp b/ui/qt/conversation_dialog.cpp new file mode 100644 index 00000000..bad3d6d2 --- /dev/null +++ b/ui/qt/conversation_dialog.cpp @@ -0,0 +1,193 @@ +/* conversation_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "conversation_dialog.h" + +#include +#include +#include + +#include "ui/recent.h" +#include "ui/tap-tcp-stream.h" + +#include "wsutil/str_util.h" + +#include +#include +#include +#include +#include +#include "main_application.h" + +#include +#include +#include +#include +#include + +// To do: +// - https://gitlab.com/wireshark/wireshark/-/issues/6727 +// - Wide last column? +// - Improper wildcard handling https://gitlab.com/wireshark/wireshark/-/issues/8010 +// - TShark consolidation https://gitlab.com/wireshark/wireshark/-/issues/6310 +// - Display filter entry? +// - Add follow, copy & graph actions to context menu. + +// Bugs: +// - Slow for large numbers of items. +// - Name resolution doesn't do anything if its preference is disabled. + +// Fixed bugs: +// - Friendly unit displays https://gitlab.com/wireshark/wireshark/-/issues/9231 +// - Misleading bps calculation https://gitlab.com/wireshark/wireshark/-/issues/8703 +// - Show Absolute time in conversation tables https://gitlab.com/wireshark/wireshark/-/issues/11618 +// - The value of 'Rel start' and 'Duration' in "Conversations" no need too precise https://gitlab.com/wireshark/wireshark/-/issues/12803 + + +static const QString table_name_ = QObject::tr("Conversation"); + +static ATapDataModel * createModel(int protoId, QString filter) +{ + return new ConversationDataModel(protoId, filter); +} + +static QAbstractItemDelegate * createDelegate(QWidget * parent) +{ + TimelineDelegate * delegate = new TimelineDelegate(parent); + delegate->setDataRole(ATapDataModel::TIMELINE_DATA); + + return delegate; +} + +ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf) : + TrafficTableDialog(parent, cf, table_name_), + tcp_graph_requested_(false) +{ + trafficList()->setProtocolInfo(table_name_, &(recent.conversation_tabs)); + + trafficTab()->setProtocolInfo(table_name_, trafficList(), &(recent.conversation_tabs_columns), &createModel); + trafficTab()->setDelegate(&createDelegate); + trafficTab()->setDelegate(&createDelegate); + trafficTab()->setFilter(cf.displayFilter()); + + connect(trafficTab(), &TrafficTab::filterAction, this, &ConversationDialog::filterAction); + connect(trafficTab()->tabBar(), &QTabBar::currentChanged, this, &ConversationDialog::tabChanged); + connect(trafficTab(), &TrafficTab::tabDataChanged, this, &ConversationDialog::tabChanged); + + follow_bt_ = buttonBox()->addButton(tr("Follow Stream…"), QDialogButtonBox::ActionRole); + follow_bt_->setToolTip(tr("Follow a TCP or UDP stream.")); + connect(follow_bt_, SIGNAL(clicked()), this, SLOT(followStream())); + + graph_bt_ = buttonBox()->addButton(tr("Graph…"), QDialogButtonBox::ActionRole); + graph_bt_->setToolTip(tr("Graph a TCP conversation.")); + connect(graph_bt_, SIGNAL(clicked()), this, SLOT(graphTcp())); + + connect(mainApp->mainWindow(), SIGNAL(displayFilterSuccess(bool)), + this, SLOT(displayFilterSuccess(bool))); + + absoluteTimeCheckBox()->show(); + + updateWidgets(); +} + +void ConversationDialog::captureFileClosing() +{ + trafficTab()->disableTap(); + displayFilterCheckBox()->setEnabled(false); + follow_bt_->setEnabled(false); + graph_bt_->setEnabled(false); + TrafficTableDialog::captureFileClosing(); +} + +void ConversationDialog::followStream() +{ + if (file_closed_) + return; + + QVariant protoIdData = trafficTab()->currentItemData(ATapDataModel::PROTO_ID); + if (protoIdData.isNull()) + return; + + int protoId = protoIdData.toInt(); + if (get_follow_by_proto_id(protoId) == nullptr) + return; + + int convId = trafficTab()->currentItemData(ATapDataModel::CONVERSATION_ID).toInt(); + + // ATapDataModel doesn't support a substream ID (XXX: yet), so set it to a + // dummy value. + emit openFollowStreamDialog(protoId, convId, 0); +} + +void ConversationDialog::graphTcp() +{ + if (file_closed_) + return; + + int endpointType = trafficTab()->currentItemData(ATapDataModel::ENDPOINT_DATATYPE).toInt(); + if (endpointType != CONVERSATION_TCP) + return; + + int convId = trafficTab()->currentItemData(ATapDataModel::CONVERSATION_ID).toInt(); + + // XXX The GTK+ code opens the TCP Stream dialog. We might want + // to open the I/O Graphs dialog instead. + QString filter = QString("tcp.stream eq %1").arg(convId); + + tcp_graph_requested_ = true; + // Apply the filter for this conversation. When the filter is active, we + // can draw the TCP graph. + emit filterAction(filter, FilterAction::ActionApply, FilterAction::ActionTypePlain); +} + +void ConversationDialog::tabChanged(int) +{ + bool follow = false; + bool graph = false; + + if (!file_closed_) { + QVariant proto_id = trafficTab()->currentItemData(ATapDataModel::PROTO_ID); + if (!proto_id.isNull()) { + follow = (get_follow_by_proto_id(proto_id.toInt()) != nullptr); + } + int endpointType = trafficTab()->currentItemData(ATapDataModel::ENDPOINT_DATATYPE).toInt(); + switch(endpointType) { + case CONVERSATION_TCP: + graph = true; + break; + } + } + + follow_bt_->setEnabled(follow); + graph_bt_->setEnabled(graph); + + TrafficTableDialog::currentTabChanged(); +} + +void ConversationDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_STATS_CONVERSATIONS_DIALOG); +} + +void ConversationDialog::displayFilterSuccess(bool success) +{ + if (tcp_graph_requested_) { + if (success) { + // The display filter was applied successfully, i.e. the current + // packet is now part of our selected tcp conversation. + openTcpStreamGraph(GRAPH_TSEQ_TCPTRACE); + } + tcp_graph_requested_ = false; + } +} + +void init_conversation_table(struct register_ct* ct, const char *filter) +{ + mainApp->emitStatCommandSignal("Conversations", filter, GINT_TO_POINTER(get_conversation_proto_id(ct))); +} diff --git a/ui/qt/conversation_dialog.h b/ui/qt/conversation_dialog.h new file mode 100644 index 00000000..5ad058ac --- /dev/null +++ b/ui/qt/conversation_dialog.h @@ -0,0 +1,49 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CONVERSATION_DIALOG_H +#define CONVERSATION_DIALOG_H + +#include "traffic_table_dialog.h" + +class ConversationDialog : public TrafficTableDialog +{ + Q_OBJECT + +public: + /** Create a new conversation window. + * + * @param parent Parent widget. + * @param cf Capture file. No statistics will be calculated if this is NULL. + */ + explicit ConversationDialog(QWidget &parent, CaptureFile &cf); + +protected: + void captureFileClosing(); + +signals: + void openFollowStreamDialog(int proto_id, guint stream_num, guint sub_stream_num); + +private: + QPushButton *follow_bt_; + QPushButton *graph_bt_; + + bool tcp_graph_requested_; + +private slots: + void followStream(); + void graphTcp(); + void on_buttonBox_helpRequested(); + void displayFilterSuccess(bool success); + void tabChanged(int idx); +}; + +void init_conversation_table(struct register_ct* ct, const char *filter); + +#endif // CONVERSATION_DIALOG_H diff --git a/ui/qt/conversation_hash_tables_dialog.cpp b/ui/qt/conversation_hash_tables_dialog.cpp new file mode 100644 index 00000000..cc699dae --- /dev/null +++ b/ui/qt/conversation_hash_tables_dialog.cpp @@ -0,0 +1,147 @@ +/* conversation_hash_tables_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "conversation_hash_tables_dialog.h" +#include + +#include "config.h" + +#include + +#include +#include + +#include +#include "main_application.h" + +static void +fill_named_table(gpointer key, gpointer value _U_, gpointer user_data) +{ + const conversation_element_t *elements = static_cast(key); + QString* html_table = static_cast(user_data); + + if (!elements || !html_table) { + return; + } + + if (html_table->isEmpty()) { + html_table->append(""); + int addr_count = 1; + int port_count = 1; + int string_count = 1; + int uint_count = 1; + int uint64_count = 1; + int int_count = 1; + for (const conversation_element_t *cur_el = elements; ; cur_el++) { + QString title; + switch (cur_el->type) { + case CE_ADDRESS: + title = QString("Address %1").arg(addr_count++); + break; + case CE_PORT: + title = QString("Port %1").arg(port_count++); + break; + case CE_STRING: + title = QString("String %1").arg(string_count++); + break; + case CE_UINT: + title = QString("UInt %1").arg(uint_count++); + break; + case CE_UINT64: + title = QString("UInt64 %1").arg(uint64_count++); + break; + case CE_INT: + title = QString("Int %1").arg(int_count++); + break; + case CE_CONVERSATION_TYPE: + html_table->append(QString("Endpoint")); + goto title_done; + break; + } + html_table->append(QString("%1").arg(title)); + } +title_done: + html_table->append("\n"); + } + + html_table->append(""); + + for (const conversation_element_t *cur_el = elements; ; cur_el++) { + QString val; + switch (cur_el->type) { + case CE_ADDRESS: + val = address_to_qstring(&cur_el->addr_val); + break; + case CE_PORT: + val = QString::number(cur_el->port_val); + break; + case CE_STRING: + val = cur_el->str_val; + break; + case CE_UINT: + val = QString::number(cur_el->uint_val); + break; + case CE_UINT64: + val = QString::number(cur_el->uint64_val); + break; + case CE_INT: + val = QString::number(cur_el->int_val); + break; + case CE_CONVERSATION_TYPE: + html_table->append(QString("%1").arg(QString::number(cur_el->conversation_type_val))); + goto val_done; + break; + } + html_table->append(QString("%1").arg(val)); + } +val_done: + + html_table->append("\n"); +} + +ConversationHashTablesDialog::ConversationHashTablesDialog(QWidget *parent) : + GeometryStateDialog(parent), + ui(new Ui::ConversationHashTablesDialog) +{ + ui->setupUi(this); + if (parent) loadGeometry(parent->width() * 3 / 4, parent->height() * 3 / 4); + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowTitle(mainApp->windowTitleString(tr("Conversation Hash Tables"))); + + QString html; + + html += "

Conversation Hash Tables

\n"; + + wmem_map_t *conversation_tables = get_conversation_hashtables(); + wmem_list_t *table_names = wmem_map_get_keys(NULL, conversation_tables); + for (wmem_list_frame_t *cur_frame = wmem_list_head(table_names); cur_frame; cur_frame = wmem_list_frame_next(cur_frame)) + { + const char *table_name = static_cast(wmem_list_frame_data(cur_frame)); + wmem_map_t *table = static_cast(wmem_map_lookup(conversation_tables, table_name)); + + if (!table) { + html += QString("

%1, Error: table not found

\n").arg(table_name); + continue; + } + + html += QString("

%1, %2 entries

\n").arg(table_name).arg(wmem_map_size(table)); + QString html_table; + html += "\n"; + wmem_map_foreach(table, fill_named_table, &html_table); + html += html_table; + html += "
\n"; + } + wmem_destroy_list(table_names); + ui->conversationTextEdit->setHtml(html); +} + +ConversationHashTablesDialog::~ConversationHashTablesDialog() +{ + delete ui; +} diff --git a/ui/qt/conversation_hash_tables_dialog.h b/ui/qt/conversation_hash_tables_dialog.h new file mode 100644 index 00000000..56927240 --- /dev/null +++ b/ui/qt/conversation_hash_tables_dialog.h @@ -0,0 +1,34 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CONVERSATION_HASH_TABLES_DIALOG_H +#define CONVERSATION_HASH_TABLES_DIALOG_H + +#include "geometry_state_dialog.h" +#include + +namespace Ui { +class ConversationHashTablesDialog; +} + +class ConversationHashTablesDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit ConversationHashTablesDialog(QWidget *parent = 0); + ~ConversationHashTablesDialog(); + +private: + Ui::ConversationHashTablesDialog *ui; + + const QString hashTableToHtmlTable(const QString table_name, wmem_map_t *hash_table); +}; + +#endif // CONVERSATION_HASH_TABLES_DIALOG_H diff --git a/ui/qt/conversation_hash_tables_dialog.ui b/ui/qt/conversation_hash_tables_dialog.ui new file mode 100644 index 00000000..ee58791f --- /dev/null +++ b/ui/qt/conversation_hash_tables_dialog.ui @@ -0,0 +1,67 @@ + + + ConversationHashTablesDialog + + + + 0 + 0 + 640 + 450 + + + + Dialog + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + ConversationHashTablesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ConversationHashTablesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/credentials_dialog.cpp b/ui/qt/credentials_dialog.cpp new file mode 100644 index 00000000..810e0c81 --- /dev/null +++ b/ui/qt/credentials_dialog.cpp @@ -0,0 +1,120 @@ +/* + * credentials_dialog.c + * + * Copyright 2019 - Dario Lombardo + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "file.h" + +#include "credentials_dialog.h" +#include +#include +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include "ui/qt/models/credentials_model.h" +#include + +#include +#include +#include +#include +#include + +class CredentialsUrlDelegate : public UrlLinkDelegate +{ +public: + + CredentialsUrlDelegate(QObject * parent) : UrlLinkDelegate(parent) {} + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const + { + bool ok = false; + int val = index.data(Qt::UserRole).toInt(&ok); + if (!ok || val <= 0) + QStyledItemDelegate::paint(painter, option, index); + else + UrlLinkDelegate::paint(painter, option, index); + } + +}; + +CredentialsDialog::CredentialsDialog(QWidget &parent, CaptureFile &cf, PacketList *packet_list) : + WiresharkDialog(parent, cf), + ui(new Ui::CredentialsDialog) +{ + ui->setupUi(this); + loadGeometry(); + packet_list_ = packet_list; + + model_ = new CredentialsModel(this); + QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this); + + proxyModel->setSourceModel(model_); + ui->auths->setModel(proxyModel); + + setWindowSubtitle(tr("Credentials")); + + ui->auths->setRootIsDecorated(false); + ui->auths->setItemDelegateForColumn(CredentialsModel::COL_NUM, new CredentialsUrlDelegate(this)); + ui->auths->setItemDelegateForColumn(CredentialsModel::COL_USERNAME, new CredentialsUrlDelegate(this)); + + ui->auths->resizeColumnToContents(CredentialsModel::COL_NUM); + ui->auths->resizeColumnToContents(CredentialsModel::COL_PROTO); + ui->auths->resizeColumnToContents(CredentialsModel::COL_USERNAME); + + ui->auths->setSortingEnabled(true); + ui->auths->sortByColumn(CredentialsModel::COL_NUM, Qt::AscendingOrder); + + connect(ui->auths, &QTreeView::clicked, this, &CredentialsDialog::actionGoToPacket); + + registerTapListener("credentials", this, "", 0, tapReset, tapPacket, Q_NULLPTR); + cf.retapPackets(); +} + +CredentialsDialog::~CredentialsDialog() +{ + delete ui; +} + +void CredentialsDialog::tapReset(void *tapdata) +{ + CredentialsDialog * d = (CredentialsDialog*) tapdata; + d->model_->clear(); +} + +tap_packet_status CredentialsDialog::tapPacket(void *tapdata, _packet_info *, epan_dissect *, const void *data, tap_flags_t) +{ + CredentialsDialog * d = (CredentialsDialog*) tapdata; + d->model_->addRecord((const tap_credential_t*)data); + return TAP_PACKET_REDRAW; +} + +void CredentialsDialog::actionGoToPacket(const QModelIndex& idx) +{ + if (!idx.isValid()) + return; + + QVariant packet_data = idx.data(Qt::UserRole); + QVariant hf_id = idx.data(CredentialsModel::ColumnHFID); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (!hf_id.canConvert()) + hf_id = QVariant::fromValue(0); + + if (packet_data.canConvert()) + packet_list_->goToPacket(packet_data.toInt(), hf_id.toInt()); +#else + if (!hf_id.canConvert(QVariant::Int)) + hf_id = QVariant::fromValue(0); + + if (packet_data.canConvert(QVariant::Int)) + packet_list_->goToPacket(packet_data.toInt(), hf_id.toInt()); +#endif +} diff --git a/ui/qt/credentials_dialog.h b/ui/qt/credentials_dialog.h new file mode 100644 index 00000000..61f836ad --- /dev/null +++ b/ui/qt/credentials_dialog.h @@ -0,0 +1,47 @@ +/** @file + * + * Copyright 2019 - Dario Lombardo + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CREDENTIALS_DIALOG_H +#define CREDENTIALS_DIALOG_H + +#include "config.h" + +#include +#include "packet_list.h" +#include + +class CredentialsModel; + +namespace Ui { +class CredentialsDialog; +} + +class CredentialsDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit CredentialsDialog(QWidget &parent, CaptureFile &cf, PacketList *packet_list); + ~CredentialsDialog(); + +private slots: + void actionGoToPacket(const QModelIndex&); + +private: + Ui::CredentialsDialog *ui; + PacketList *packet_list_; + CredentialsModel * model_; + + static void tapReset(void *tapdata); + static tap_packet_status tapPacket(void *tapdata, struct _packet_info *pinfo, struct epan_dissect *edt, const void *data, tap_flags_t flags); +}; + +#endif // CREDENTIALS_DIALOG_H diff --git a/ui/qt/credentials_dialog.ui b/ui/qt/credentials_dialog.ui new file mode 100644 index 00000000..fc41414b --- /dev/null +++ b/ui/qt/credentials_dialog.ui @@ -0,0 +1,51 @@ + + + CredentialsDialog + + + + 0 + 0 + 634 + 454 + + + + Wireshark - Credentials + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + clicked(QAbstractButton*) + CredentialsDialog + close() + + + 248 + 254 + + + 157 + 274 + + + + + diff --git a/ui/qt/decode_as_dialog.cpp b/ui/qt/decode_as_dialog.cpp new file mode 100644 index 00000000..edc6ccc7 --- /dev/null +++ b/ui/qt/decode_as_dialog.cpp @@ -0,0 +1,231 @@ +/* decode_as_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "decode_as_dialog.h" +#include + +#include "epan/decode_as.h" +#include "epan/epan_dissect.h" + +#include "ui/decode_as_utils.h" +#include "ui/simple_dialog.h" +#include "wsutil/filesystem.h" +#include + +#include +#include +#include "main_application.h" + +#include + +#include +#include +#include +#include +#include + +#include + +// To do: +// - Ranges +// - Add DCERPC support (or make DCERPC use a regular dissector table?) + +DecodeAsDialog::DecodeAsDialog(QWidget *parent, capture_file *cf, bool create_new) : + GeometryStateDialog(parent), + ui(new Ui::DecodeAsDialog), + model_(new DecodeAsModel(this, cf)), + delegate_(NULL) +{ + ui->setupUi(this); + loadGeometry(); + + delegate_ = new DecodeAsDelegate(ui->decodeAsTreeView, cf); + + ui->decodeAsTreeView->setModel(model_); + ui->decodeAsTreeView->setItemDelegate(delegate_); + + ui->newToolButton->setStockIcon("list-add"); + ui->deleteToolButton->setStockIcon("list-remove"); + ui->copyToolButton->setStockIcon("list-copy"); + ui->clearToolButton->setStockIcon("list-clear"); + +#ifdef Q_OS_MAC + ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->clearToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + setWindowTitle(mainApp->windowTitleString(tr("Decode As…"))); + + QString abs_path = gchar_free_to_qstring(get_persconffile_path(DECODE_AS_ENTRIES_FILE_NAME, TRUE)); + if (file_exists(abs_path.toUtf8().constData())) { + ui->pathLabel->setText(abs_path); + ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString()); + ui->pathLabel->setToolTip(tr("Open ") + DECODE_AS_ENTRIES_FILE_NAME); + ui->pathLabel->setEnabled(true); + } + + CopyFromProfileButton *copy_button = new CopyFromProfileButton(this, DECODE_AS_ENTRIES_FILE_NAME); + ui->buttonBox->addButton(copy_button, QDialogButtonBox::ActionRole); + connect(copy_button, &CopyFromProfileButton::copyProfile, this, &DecodeAsDialog::copyFromProfile); + + fillTable(); + + connect(model_, &DecodeAsModel::modelReset, this, &DecodeAsDialog::modelRowsReset); + ui->clearToolButton->setEnabled(model_->rowCount() > 0); + + if (create_new) + on_newToolButton_clicked(); +} + +DecodeAsDialog::~DecodeAsDialog() +{ + delete ui; + delete model_; + delete delegate_; +} + +void DecodeAsDialog::fillTable() +{ + model_->fillTable(); + + resizeColumns(); + + //set selection as first row + if (model_->rowCount() > 0) { + const QModelIndex &new_index = model_->index(0, 0); + ui->decodeAsTreeView->setCurrentIndex(new_index); + } +} + +void DecodeAsDialog::resizeColumns() +{ + if (model_->rowCount() > 0) { + for (int i = 0; i < model_->columnCount(); i++) { + ui->decodeAsTreeView->resizeColumnToContents(i); + } + } +} + +void DecodeAsDialog::modelRowsReset() +{ + ui->deleteToolButton->setEnabled(false); + ui->copyToolButton->setEnabled(false); + ui->clearToolButton->setEnabled(false); +} + +void DecodeAsDialog::on_decodeAsTreeView_currentItemChanged(const QModelIndex ¤t, const QModelIndex&) +{ + if (current.isValid()) { + ui->deleteToolButton->setEnabled(true); + ui->copyToolButton->setEnabled(true); + ui->clearToolButton->setEnabled(true); + } else { + ui->deleteToolButton->setEnabled(false); + ui->copyToolButton->setEnabled(false); + ui->clearToolButton->setEnabled(false); + } +} + +void DecodeAsDialog::copyFromProfile(QString filename) +{ + const gchar *err = NULL; + + if (!model_->copyFromProfile(filename, &err)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Error while loading %s: %s", filename.toUtf8().constData(), err); + } + + resizeColumns(); + + ui->clearToolButton->setEnabled(model_->rowCount() > 0); +} + +void DecodeAsDialog::addRecord(bool copy_from_current) +{ + const QModelIndex ¤t = ui->decodeAsTreeView->currentIndex(); + if (copy_from_current && !current.isValid()) return; + +// XXX - This doesn't appear to work as intended to give "edit triggers on demand" + ui->decodeAsTreeView->setEditTriggers(ui->decodeAsTreeView->editTriggers() | QAbstractItemView::CurrentChanged | QAbstractItemView::AnyKeyPressed); + + // should not fail, but you never know. + if (!model_->insertRows(model_->rowCount(), 1)) { + qDebug() << "Failed to add a new record"; + return; + } + const QModelIndex &new_index = model_->index(model_->rowCount() - 1, 0); + if (copy_from_current) { + model_->copyRow(new_index.row(), current.row()); + } + + resizeColumns(); + + // due to an EditTrigger, this will also start editing. + ui->decodeAsTreeView->setCurrentIndex(new_index); +} + +void DecodeAsDialog::on_newToolButton_clicked() +{ + addRecord(); +} + +void DecodeAsDialog::on_deleteToolButton_clicked() +{ + const QModelIndex ¤t = ui->decodeAsTreeView->currentIndex(); + if (model_ && current.isValid()) { + if (!model_->removeRows(current.row(), 1)) { + qDebug() << "Failed to remove row"; + } + } +} + +void DecodeAsDialog::on_copyToolButton_clicked() +{ + addRecord(true); +} + +void DecodeAsDialog::on_clearToolButton_clicked() +{ + model_->clearAll(); +} + +void DecodeAsDialog::applyChanges() +{ + model_->applyChanges(); + mainApp->queueAppSignal(MainApplication::PacketDissectionChanged); +} + +void DecodeAsDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + ui->buttonBox->setFocus(); + + switch (ui->buttonBox->standardButton(button)) { + case QDialogButtonBox::Ok: + applyChanges(); + break; + case QDialogButtonBox::Save: + { + gchar* err = NULL; + + applyChanges(); + if (save_decode_as_entries(&err) < 0) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err); + g_free(err); + } + } + break; + case QDialogButtonBox::Help: + mainApp->helpTopicAction(HELP_DECODE_AS_SHOW_DIALOG); + break; + default: + break; + } +} diff --git a/ui/qt/decode_as_dialog.h b/ui/qt/decode_as_dialog.h new file mode 100644 index 00000000..8765489a --- /dev/null +++ b/ui/qt/decode_as_dialog.h @@ -0,0 +1,65 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DECODE_AS_DIALOG_H +#define DECODE_AS_DIALOG_H + +#include + +#include + +#include "cfile.h" +#include +#include + +#include "geometry_state_dialog.h" +#include +#include + +class QComboBox; + +namespace Ui { +class DecodeAsDialog; +} + +class DecodeAsDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit DecodeAsDialog(QWidget *parent = 0, capture_file *cf = NULL, bool create_new = false); + ~DecodeAsDialog(); + +private: + Ui::DecodeAsDialog *ui; + + DecodeAsModel* model_; + DecodeAsDelegate* delegate_; + + void addRecord(bool copy_from_current = false); + void applyChanges(); + void fillTable(); + void resizeColumns(); + +public slots: + void modelRowsReset(); + +private slots: + void copyFromProfile(QString filename); + void on_decodeAsTreeView_currentItemChanged(const QModelIndex ¤t, const QModelIndex &previous); + + void on_newToolButton_clicked(); + void on_deleteToolButton_clicked(); + void on_copyToolButton_clicked(); + void on_clearToolButton_clicked(); + + void on_buttonBox_clicked(QAbstractButton *button); +}; + +#endif // DECODE_AS_DIALOG_H diff --git a/ui/qt/decode_as_dialog.ui b/ui/qt/decode_as_dialog.ui new file mode 100644 index 00000000..795a3beb --- /dev/null +++ b/ui/qt/decode_as_dialog.ui @@ -0,0 +1,161 @@ + + + DecodeAsDialog + + + + 0 + 0 + 750 + 460 + + + + + + + 0 + + + + + + + + + Change the dissection behavior for a protocol. + + + + + + + + + + false + + + Remove this dissection behavior. + + + + + + + Copy this dissection behavior. + + + + + + + + + + false + + + Clear all dissection behaviors. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Save + + + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+ + TabnavTreeView + QTreeView +
widgets/tabnav_tree_view.h
+
+ + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+
+ + + + buttonBox + accepted() + DecodeAsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DecodeAsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/display_filter_expression_dialog.cpp b/ui/qt/display_filter_expression_dialog.cpp new file mode 100644 index 00000000..e071d0ab --- /dev/null +++ b/ui/qt/display_filter_expression_dialog.cpp @@ -0,0 +1,533 @@ +/* display_filter_expression_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "display_filter_expression_dialog.h" +#include + +#include +#include +#include +#include + +#include + +#include +#include "main_application.h" + +#include + +#include +#include +#include +#include +#include +#include + +// To do: +// - Speed up search. + +enum { + proto_type_ = 1000, + field_type_ +}; + +enum { + present_op_ = 1000, + any_eq_op_, + all_eq_op_, + any_ne_op_, + all_ne_op_, + gt_op_, + lt_op_, + ge_op_, + le_op_, + contains_op_, + matches_op_, + in_op_ +}; + +static inline bool compareTreeWidgetItems(const QTreeWidgetItem *it1, const QTreeWidgetItem *it2) +{ + return *it1 < *it2; +} + +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE +static void generateProtocolTreeItems(QPromise &promise) +{ + QList proto_list; + QList *ptr_proto_list = &proto_list; +#else +static QList *generateProtocolTreeItems() +{ + QList *ptr_proto_list = new QList(); +#endif + + void *proto_cookie; + for (int proto_id = proto_get_first_protocol(&proto_cookie); proto_id != -1; + proto_id = proto_get_next_protocol(&proto_cookie)) { + protocol_t *protocol = find_protocol_by_id(proto_id); + if (!proto_is_protocol_enabled(protocol)) continue; + + QTreeWidgetItem *proto_ti = new QTreeWidgetItem(proto_type_); + QString label = QString("%1 " UTF8_MIDDLE_DOT " %3") + .arg(proto_get_protocol_short_name(protocol)) + .arg(proto_get_protocol_long_name(protocol)); + proto_ti->setText(0, label); + proto_ti->setData(0, Qt::UserRole, QVariant::fromValue(proto_id)); + ptr_proto_list->append(proto_ti); + } + std::stable_sort(ptr_proto_list->begin(), ptr_proto_list->end(), compareTreeWidgetItems); + + foreach (QTreeWidgetItem *proto_ti, *ptr_proto_list) { +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE + if (promise.isCanceled()) { + delete proto_ti; + continue; + } + promise.suspendIfRequested(); +#endif + void *field_cookie; + int proto_id = proto_ti->data(0, Qt::UserRole).toInt(); + + QList field_list; + for (header_field_info *hfinfo = proto_get_first_protocol_field(proto_id, &field_cookie); hfinfo != NULL; + hfinfo = proto_get_next_protocol_field(proto_id, &field_cookie)) { + if (hfinfo->same_name_prev_id != -1) continue; // Ignore duplicate names. + + QTreeWidgetItem *field_ti = new QTreeWidgetItem(field_type_); + QString label = QString("%1 " UTF8_MIDDLE_DOT " %3").arg(hfinfo->abbrev).arg(hfinfo->name); + field_ti->setText(0, label); + field_ti->setData(0, Qt::UserRole, VariantPointer::asQVariant(hfinfo)); + field_list << field_ti; + } + std::stable_sort(field_list.begin(), field_list.end(), compareTreeWidgetItems); + proto_ti->addChildren(field_list); +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE + if (!promise.addResult(proto_ti)) + delete proto_ti; + } +#else + } + return ptr_proto_list; +#endif +} + +DisplayFilterExpressionDialog::DisplayFilterExpressionDialog(QWidget *parent) : + GeometryStateDialog(parent), +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE + watcher(new QFutureWatcher(nullptr)), +#else + watcher(new QFutureWatcher *>(nullptr)), +#endif + ui(new Ui::DisplayFilterExpressionDialog), + ftype_(FT_NONE), + field_(NULL) +{ + ui->setupUi(this); + if (parent) loadGeometry(parent->width() * 2 / 3, parent->height()); + setAttribute(Qt::WA_DeleteOnClose, true); + + setWindowTitle(mainApp->windowTitleString(tr("Display Filter Expression"))); + setWindowIcon(mainApp->normalIcon()); + + proto_initialize_all_prefixes(); + + auto future = QtConcurrent::run(generateProtocolTreeItems); + + ui->fieldTreeWidget->setToolTip(ui->fieldLabel->toolTip()); + ui->searchLineEdit->setToolTip(ui->searchLabel->toolTip()); + ui->relationListWidget->setToolTip(ui->relationLabel->toolTip()); + ui->valueLineEdit->setToolTip(ui->valueLabel->toolTip()); + ui->enumListWidget->setToolTip(ui->enumLabel->toolTip()); + ui->rangeLineEdit->setToolTip(ui->rangeLabel->toolTip()); + + // Relation list + new QListWidgetItem("is present", ui->relationListWidget, present_op_); + new QListWidgetItem("==", ui->relationListWidget, any_eq_op_); + new QListWidgetItem("!=", ui->relationListWidget, all_ne_op_); + new QListWidgetItem("===", ui->relationListWidget, all_eq_op_); + new QListWidgetItem("!==", ui->relationListWidget, any_ne_op_); + new QListWidgetItem(">", ui->relationListWidget, gt_op_); + new QListWidgetItem("<", ui->relationListWidget, lt_op_); + new QListWidgetItem(">=", ui->relationListWidget, ge_op_); + new QListWidgetItem("<=", ui->relationListWidget, le_op_); + new QListWidgetItem("contains", ui->relationListWidget, contains_op_); + new QListWidgetItem("matches", ui->relationListWidget, matches_op_); + new QListWidgetItem("in", ui->relationListWidget, in_op_); + + value_label_pfx_ = ui->valueLabel->text(); + + connect(ui->anyRadioButton, &QAbstractButton::toggled, this, &DisplayFilterExpressionDialog::updateWidgets); + connect(ui->allRadioButton, &QAbstractButton::toggled, this, &DisplayFilterExpressionDialog::updateWidgets); + connect(ui->valueLineEdit, &QLineEdit::textEdited, this, &DisplayFilterExpressionDialog::updateWidgets); + connect(ui->rangeLineEdit, &QLineEdit::textEdited, this, &DisplayFilterExpressionDialog::updateWidgets); + + updateWidgets(); + ui->searchLineEdit->setReadOnly(true); + +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE + connect(watcher, &QFutureWatcher::resultReadyAt, this, &DisplayFilterExpressionDialog::addTreeItem); + connect(watcher, &QFutureWatcher::finished, this, &DisplayFilterExpressionDialog::fillTree); +#else + connect(watcher, &QFutureWatcher *>::finished, this, &DisplayFilterExpressionDialog::fillTree); + // If window is closed before future finishes, DisplayFilterExpressionDialog fillTree slot won't run + // Register lambda to free up the list container and tree entries (if not consumed by fillTree()) + auto captured_watcher = this->watcher; + connect(watcher, &QFutureWatcher *>::finished, [captured_watcher]() { + QList *items = captured_watcher->future().result(); + qDeleteAll(*items); + delete items; + }); +#endif + watcher->setFuture(future); +} + +DisplayFilterExpressionDialog::~DisplayFilterExpressionDialog() +{ + if (watcher) + { +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE + watcher->future().cancel(); + qDeleteAll(watcher->future().results()); +#endif + watcher->waitForFinished(); + watcher->deleteLater(); + } + delete ui; +} + +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE +void DisplayFilterExpressionDialog::addTreeItem(int result) +{ + QTreeWidgetItem *item = watcher->future().resultAt(result); + ui->fieldTreeWidget->invisibleRootItem()->addChild(item); +} +#endif + +void DisplayFilterExpressionDialog::fillTree() +{ +#ifndef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE + QList *items = watcher->future().result(); + ui->fieldTreeWidget->invisibleRootItem()->addChildren(*items); + // fieldTreeWidget now owns all items + items->clear(); +#endif + watcher->deleteLater(); + watcher = nullptr; + + ui->searchLineEdit->setReadOnly(false); +} + +void DisplayFilterExpressionDialog::updateWidgets() +{ + bool rel_enable = field_ != NULL; + + ui->relationLabel->setEnabled(rel_enable); + ui->relationListWidget->setEnabled(rel_enable); + ui->hintLabel->clear(); + + bool quantity_enable = false; + bool value_enable = false; + bool enum_enable = false; + bool enum_multi_enable = false; + bool range_enable = false; + + QString filter; + if (field_) { + filter = field_; + QListWidgetItem *rli = ui->relationListWidget->currentItem(); + if (rli && rli->type() > all_ne_op_) { + quantity_enable = true; + if (ui->anyRadioButton->isChecked()) { + filter.prepend("any "); + } + else if (ui->allRadioButton->isChecked()) { + filter.prepend("all "); + } + else { + ws_assert_not_reached(); + } + } + if (rli && rli->type() != present_op_) { + value_enable = true; + if (ftype_can_slice(ftype_)) { + range_enable = true; + } + enum_enable = ui->enumListWidget->count() > 0; + filter.append(QString(" %1").arg(rli->text())); + } + if (value_enable && !ui->valueLineEdit->text().isEmpty()) { + if (rli && rli->type() == in_op_) { + filter.append(QString(" {%1}").arg(ui->valueLineEdit->text())); + enum_multi_enable = enum_enable; + } else { + if (ftype_ == FT_STRING) { + filter.append(QString(" \"%1\"").arg(ui->valueLineEdit->text())); + } else { + filter.append(QString(" %1").arg(ui->valueLineEdit->text())); + } + } + } + } + + ui->quantityLabel->setEnabled(quantity_enable); + ui->allRadioButton->setEnabled(quantity_enable); + ui->anyRadioButton->setEnabled(quantity_enable); + + ui->valueLabel->setEnabled(value_enable); + ui->valueLineEdit->setEnabled(value_enable); + + ui->enumLabel->setEnabled(enum_enable); + ui->enumListWidget->setEnabled(enum_enable); + ui->enumListWidget->setSelectionMode(enum_multi_enable ? + QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection); + + ui->rangeLabel->setEnabled(range_enable); + ui->rangeLineEdit->setEnabled(range_enable); + + ui->displayFilterLineEdit->setText(filter); + + QString hint = ""; + if (ui->fieldTreeWidget->selectedItems().count() < 1) { + hint.append(tr("Select a field name to get started")); + } else if (ui->displayFilterLineEdit->syntaxState() != SyntaxLineEdit::Valid) { + hint.append(ui->displayFilterLineEdit->syntaxErrorMessage()); + } else { + hint.append(tr("Click OK to insert this filter")); + } + hint.append(""); + ui->hintLabel->setText(hint); + + QPushButton *ok_bt = ui->buttonBox->button(QDialogButtonBox::Ok); + if (ok_bt) { + bool ok_enable = !(ui->displayFilterLineEdit->text().isEmpty() + || (ui->displayFilterLineEdit->syntaxState() == SyntaxLineEdit::Invalid)); + ok_bt->setEnabled(ok_enable); + } +} + +void DisplayFilterExpressionDialog::fillEnumBooleanValues(const true_false_string *tfs) +{ + QListWidgetItem *eli = new QListWidgetItem(tfs_get_string(TRUE, tfs), ui->enumListWidget); + eli->setData(Qt::UserRole, QString("1")); + eli = new QListWidgetItem(tfs_get_string(FALSE, tfs), ui->enumListWidget); + eli->setData(Qt::UserRole, QString("0")); +} + +void DisplayFilterExpressionDialog::fillEnumIntValues(const _value_string *vals, int base) +{ + if (!vals) return; + + for (int i = 0; vals[i].strptr != NULL; i++) { + QListWidgetItem *eli = new QListWidgetItem(vals[i].strptr, ui->enumListWidget); + eli->setData(Qt::UserRole, int_to_qstring(vals[i].value, 0, base)); + } +} + +void DisplayFilterExpressionDialog::fillEnumInt64Values(const _val64_string *vals64, int base) +{ + if (!vals64) return; + + for (int i = 0; vals64[i].strptr != NULL; i++) { + QListWidgetItem *eli = new QListWidgetItem(vals64[i].strptr, ui->enumListWidget); + eli->setData(Qt::UserRole, int_to_qstring(vals64[i].value, 0, base)); + } +} + +void DisplayFilterExpressionDialog::fillEnumRangeValues(const _range_string *rvals) +{ + if (!rvals) return; + + for (int i = 0; rvals[i].strptr != NULL; i++) { + QString range_text = rvals[i].strptr; + + // Tell the user which values are valid here. Default to value_min below. + if (rvals[i].value_min != rvals[i].value_max) { + range_text.append(QString(" (%1 valid)").arg(range_to_qstring(&rvals[i]))); + } + + QListWidgetItem *eli = new QListWidgetItem(range_text, ui->enumListWidget); + eli->setData(Qt::UserRole, QString::number(rvals[i].value_min)); + } +} + +void DisplayFilterExpressionDialog::on_fieldTreeWidget_itemSelectionChanged() +{ + ftype_ = FT_NONE; + field_ = NULL; + QTreeWidgetItem *cur_fti = NULL; + + if (ui->fieldTreeWidget->selectedItems().count() > 0) { + cur_fti = ui->fieldTreeWidget->selectedItems()[0]; + } + ui->valueLineEdit->clear(); + ui->enumListWidget->clear(); + ui->rangeLineEdit->clear(); + + if (cur_fti && cur_fti->type() == proto_type_) { + ftype_ = FT_PROTOCOL; + field_ = proto_get_protocol_filter_name(cur_fti->data(0, Qt::UserRole).toInt()); + } else if (cur_fti && cur_fti->type() == field_type_) { + header_field_info *hfinfo = VariantPointer::asPtr(cur_fti->data(0, Qt::UserRole)); + if (hfinfo) { + ftype_ = hfinfo->type; + field_ = hfinfo->abbrev; + + switch(ftype_) { + case FT_BOOLEAN: + // Let the user select the "True" and "False" values. + fillEnumBooleanValues((const true_false_string *)hfinfo->strings); + break; + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + { + int base; + + switch (hfinfo->display & FIELD_DISPLAY_E_MASK) { + case BASE_HEX: + case BASE_HEX_DEC: + base = 16; + break; + case BASE_OCT: + base = 8; + break; + default: + base = 10; + break; + } + // Let the user select from a list of value_string or range_string values. + if (hfinfo->strings && ! ((hfinfo->display & FIELD_DISPLAY_E_MASK) == BASE_CUSTOM)) { + if (hfinfo->display & BASE_RANGE_STRING) { + fillEnumRangeValues((const range_string *)hfinfo->strings); + } else if (hfinfo->display & BASE_VAL64_STRING) { + const val64_string *vals = (const val64_string *)hfinfo->strings; + fillEnumInt64Values(vals, base); + } else { // Plain old value_string / VALS + const value_string *vals = (const value_string *)hfinfo->strings; + if (hfinfo->display & BASE_EXT_STRING) + vals = VALUE_STRING_EXT_VS_P((const value_string_ext *)vals); + fillEnumIntValues(vals, base); + } + } + break; + } + default: + break; + } + } + } + if (ui->enumListWidget->count() > 0) { + ui->enumListWidget->setCurrentRow(0); + } + + bool all_show = field_ != NULL; + for (int i = 0; i < ui->relationListWidget->count(); i++) { + QListWidgetItem *li = ui->relationListWidget->item(i); + switch (li->type()) { + case any_eq_op_: + case all_eq_op_: + case any_ne_op_: + case all_ne_op_: + li->setHidden(!ftype_can_eq(ftype_) && !(ftype_can_slice(ftype_) && ftype_can_eq(FT_BYTES))); + break; + case gt_op_: + case lt_op_: + case ge_op_: + case le_op_: + case in_op_: + li->setHidden(!ftype_can_cmp(ftype_) && !(ftype_can_slice(ftype_) && ftype_can_cmp(FT_BYTES))); + break; + case contains_op_: + li->setHidden(!ftype_can_contains(ftype_) && !(ftype_can_slice(ftype_) && ftype_can_contains(FT_BYTES))); + break; + case matches_op_: + li->setHidden(!ftype_can_matches(ftype_) && !(ftype_can_slice(ftype_) && ftype_can_matches(FT_BYTES))); + break; + default: + li->setHidden(!all_show); + break; + } + } + if (all_show) { + // Select "==" if it's present and we have a value, "is present" otherwise + int row = ui->relationListWidget->count() > 1 && ui->enumListWidget->count() > 0 ? 1 : 0; + ui->relationListWidget->setCurrentRow(row); + } + + if (ftype_ != FT_NONE) { + ui->valueLabel->setText(QString("%1 (%2)") + .arg(value_label_pfx_) + .arg(ftype_pretty_name(ftype_))); + } else { + ui->valueLabel->setText(value_label_pfx_); + } + + updateWidgets(); +} + +void DisplayFilterExpressionDialog::on_relationListWidget_itemSelectionChanged() +{ + updateWidgets(); +} + +void DisplayFilterExpressionDialog::on_enumListWidget_itemSelectionChanged() +{ + QStringList values; + QList items = ui->enumListWidget->selectedItems(); + QList::const_iterator it = items.constBegin(); + while (it != items.constEnd()) + { + values << (*it)->data(Qt::UserRole).toString(); + ++it; + } + + ui->valueLineEdit->setText(values.join(" ")); + + updateWidgets(); +} + +void DisplayFilterExpressionDialog::on_searchLineEdit_textChanged(const QString &search_re) +{ + ui->fieldTreeWidget->setUpdatesEnabled(false); + QTreeWidgetItemIterator it(ui->fieldTreeWidget); + QRegularExpression regex(search_re, QRegularExpression::CaseInsensitiveOption); + if (! regex.isValid()) + return; + + while (*it) { + bool hidden = true; + if (search_re.isEmpty() || (*it)->text(0).contains(regex)) { + hidden = false; + if ((*it)->type() == field_type_) { + (*it)->parent()->setHidden(false); + } + } + (*it)->setHidden(hidden); + ++it; + } + ui->fieldTreeWidget->setUpdatesEnabled(true); +} + +void DisplayFilterExpressionDialog::on_buttonBox_accepted() +{ + emit insertDisplayFilter(ui->displayFilterLineEdit->text()); +} + +void DisplayFilterExpressionDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_FILTER_EXPRESSION_DIALOG); +} diff --git a/ui/qt/display_filter_expression_dialog.h b/ui/qt/display_filter_expression_dialog.h new file mode 100644 index 00000000..00a248cd --- /dev/null +++ b/ui/qt/display_filter_expression_dialog.h @@ -0,0 +1,79 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISPLAY_FILTER_EXPRESSION_DIALOG_H +#define DISPLAY_FILTER_EXPRESSION_DIALOG_H + +#include "config.h" + +#include + +#include "geometry_state_dialog.h" + +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +/* Qt6 introduces QPromise interface that makes it possible to add tree entries + * protocol by protocol instead of all at once. + */ +#define DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE +#endif + +class QTreeWidgetItem; +struct true_false_string; +struct _value_string; +struct _val64_string; + +namespace Ui { +class DisplayFilterExpressionDialog; +} + +class DisplayFilterExpressionDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit DisplayFilterExpressionDialog(QWidget *parent = 0); + ~DisplayFilterExpressionDialog(); + +signals: + void insertDisplayFilter(const QString &filter); + +private slots: +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE + void addTreeItem(int result); +#endif + void fillTree(); + void updateWidgets(); + + void on_fieldTreeWidget_itemSelectionChanged(); + void on_relationListWidget_itemSelectionChanged(); + void on_enumListWidget_itemSelectionChanged(); + void on_searchLineEdit_textChanged(const QString &search_re); + void on_buttonBox_accepted(); + void on_buttonBox_helpRequested(); + +private: +#ifdef DISPLAY_FILTER_EXPRESSION_DIALOG_USE_QPROMISE + QFutureWatcher *watcher; +#else + QFutureWatcher *> *watcher; +#endif + Ui::DisplayFilterExpressionDialog *ui; + void fillEnumBooleanValues(const struct true_false_string *tfs); + void fillEnumIntValues(const struct _value_string *vals, int base); + void fillEnumInt64Values(const struct _val64_string *vals64, int base); + void fillEnumRangeValues(const struct _range_string *rvals); + + enum ftenum ftype_; + const char *field_; + QString value_label_pfx_; +}; + +#endif // DISPLAY_FILTER_EXPRESSION_DIALOG_H diff --git a/ui/qt/display_filter_expression_dialog.ui b/ui/qt/display_filter_expression_dialog.ui new file mode 100644 index 00000000..dc770bae --- /dev/null +++ b/ui/qt/display_filter_expression_dialog.ui @@ -0,0 +1,292 @@ + + + DisplayFilterExpressionDialog + + + + 0 + 0 + 657 + 588 + + + + Dialog + + + + + + + + + + Select a field to start building a display filter. + + + Field Name + + + + + + + true + + + true + + + + 1 + + + + + + + + + + <html><head/><body><p>Search the list of field names.</p></body></html> + + + Search: + + + + + + + + + + + + + + + + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + + + Relation + + + + + + + + + + + + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + + + Quantifier + + + + + + + + + Any + + + true + + + + + + + All + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 12 + + + + + + + + + + Match against this value. + + + Value + + + + + + + + + + + + + + If the field you have selected has a known set of valid values they will be listed here. + + + Predefined Values + + + + + + + + + + + + Qt::Vertical + + + + 20 + 12 + + + + + + + + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + + + Range (offset:length) + + + + + + + + + + + + + + + + true + + + No display filter + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + + + buttonBox + accepted() + DisplayFilterExpressionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DisplayFilterExpressionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/dissector_tables_dialog.cpp b/ui/qt/dissector_tables_dialog.cpp new file mode 100644 index 00000000..51e5cf7e --- /dev/null +++ b/ui/qt/dissector_tables_dialog.cpp @@ -0,0 +1,54 @@ +/* dissector_tables_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + #include "config.h" + +#include +#include + +#include "main_application.h" + +DissectorTablesDialog::DissectorTablesDialog(QWidget *parent) : + GeometryStateDialog(parent), + ui(new Ui::DissectorTablesDialog) +{ + ui->setupUi(this); + + if (parent) + loadGeometry(parent->width() * 3 / 4, parent->height() * 3 / 4); + + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowTitle(mainApp->windowTitleString(tr("Dissector Tables"))); + + proxyModel_ = new DissectorTablesProxyModel(this); + proxyModel_->setSourceModel(new DissectorTablesModel(this)); + //it's recommended to sort after list is populated + proxyModel_->sort(DissectorTablesModel::colTableName); + + ui->tableTree->setModel(proxyModel_); + + //expand the "type" tables + ui->tableTree->expandToDepth(0); + ui->tableTree->resizeColumnToContents(DissectorTablesModel::colTableName); + + ui->txtSearchLine->setFocus(); +} + +DissectorTablesDialog::~DissectorTablesDialog() +{ + delete ui; +} + +void DissectorTablesDialog::on_txtSearchLine_textChanged(const QString &search_re) +{ + proxyModel_->setFilter(search_re); + /* If items are filtered out, then filtered back in, the tree remains collapsed + Force an expansion */ + ui->tableTree->expandToDepth(0); +} diff --git a/ui/qt/dissector_tables_dialog.h b/ui/qt/dissector_tables_dialog.h new file mode 100644 index 00000000..c785e24e --- /dev/null +++ b/ui/qt/dissector_tables_dialog.h @@ -0,0 +1,37 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISSECTOR_TABLES_DIALOG_H +#define DISSECTOR_TABLES_DIALOG_H + +#include +#include + +namespace Ui { +class DissectorTablesDialog; +} + +class DissectorTablesDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit DissectorTablesDialog(QWidget *parent = 0); + ~DissectorTablesDialog(); + +private slots: + void on_txtSearchLine_textChanged(const QString &search_re); + +private: + Ui::DissectorTablesDialog *ui; + + DissectorTablesProxyModel* proxyModel_; +}; + +#endif // DISSECTOR_TABLES_DIALOG_H diff --git a/ui/qt/dissector_tables_dialog.ui b/ui/qt/dissector_tables_dialog.ui new file mode 100644 index 00000000..5eef9545 --- /dev/null +++ b/ui/qt/dissector_tables_dialog.ui @@ -0,0 +1,92 @@ + + + DissectorTablesDialog + + + + 0 + 0 + 400 + 351 + + + + Dialog + + + + + + + + Search: + + + + + + + + + + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + DissectorTablesTreeView + QTreeView +
widgets/dissector_tables_view.h
+
+
+ + + + buttonBox + accepted() + DissectorTablesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DissectorTablesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/enabled_protocols_dialog.cpp b/ui/qt/enabled_protocols_dialog.cpp new file mode 100644 index 00000000..e503991d --- /dev/null +++ b/ui/qt/enabled_protocols_dialog.cpp @@ -0,0 +1,125 @@ +/* enabled_protocols_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "enabled_protocols_dialog.h" +#include + +#include + +#include + +#include "main_application.h" + +EnabledProtocolsDialog::EnabledProtocolsDialog(QWidget *parent) : + GeometryStateDialog(parent), + ui(new Ui::EnabledProtocolsDialog), + enabled_protocols_model_(new EnabledProtocolsModel()), + proxyModel_(new EnabledProtocolsProxyModel(this)) +{ + ui->setupUi(this); + loadGeometry(); + + proxyModel_->setSourceModel(enabled_protocols_model_); + ui->protocol_tree_->setModel(proxyModel_); + + setWindowTitle(mainApp->windowTitleString(tr("Enabled Protocols"))); + + // Some protocols have excessively long names. Instead of calling + // resizeColumnToContents, pick a reasonable-ish em width and apply it. + int one_em = ui->protocol_tree_->fontMetrics().height(); + ui->protocol_tree_->setColumnWidth(EnabledProtocolsModel::colProtocol, one_em * 18); + + ui->cmbSearchType->addItem(tr("Everywhere"), QVariant::fromValue(EnabledProtocolsProxyModel::EveryWhere)); + ui->cmbSearchType->addItem(tr("Only Protocols"), QVariant::fromValue(EnabledProtocolsProxyModel::OnlyProtocol)); + ui->cmbSearchType->addItem(tr("Only Description"), QVariant::fromValue(EnabledProtocolsProxyModel::OnlyDescription)); + ui->cmbSearchType->addItem(tr("Only enabled protocols"), QVariant::fromValue(EnabledProtocolsProxyModel::EnabledItems)); + ui->cmbSearchType->addItem(tr("Only disabled protocols"), QVariant::fromValue(EnabledProtocolsProxyModel::DisabledItems)); + + ui->cmbProtocolType->addItem(tr("any protocol"), QVariant::fromValue(EnabledProtocolItem::Any)); + ui->cmbProtocolType->addItem(tr("non-heuristic protocols"), QVariant::fromValue(EnabledProtocolItem::Standard)); + ui->cmbProtocolType->addItem(tr("heuristic protocols"), QVariant::fromValue(EnabledProtocolItem::Heuristic)); + + fillTree(); +} + +EnabledProtocolsDialog::~EnabledProtocolsDialog() +{ + delete ui; + delete proxyModel_; + delete enabled_protocols_model_; +} + +void EnabledProtocolsDialog::fillTree() +{ + enabled_protocols_model_->populate(); + //it's recommended to sort after list is populated + proxyModel_->sort(EnabledProtocolsModel::colProtocol); + ui->protocol_tree_->expandAll(); +} + +void EnabledProtocolsDialog::on_invert_button__clicked() +{ + proxyModel_->setItemsEnable(EnabledProtocolsProxyModel::Invert); + ui->protocol_tree_->expandAll(); +} + +void EnabledProtocolsDialog::on_enable_all_button__clicked() +{ + proxyModel_->setItemsEnable(EnabledProtocolsProxyModel::Enable); + ui->protocol_tree_->expandAll(); +} + +void EnabledProtocolsDialog::on_disable_all_button__clicked() +{ + proxyModel_->setItemsEnable(EnabledProtocolsProxyModel::Disable); + ui->protocol_tree_->expandAll(); +} + +void EnabledProtocolsDialog::searchFilterChange() +{ + EnabledProtocolsProxyModel::SearchType type = EnabledProtocolsProxyModel::EveryWhere; + EnabledProtocolItem::EnableProtocolType protocol = EnabledProtocolItem::Any; + QString search_re = ui->search_line_edit_->text(); + + if (ui->cmbSearchType->currentData().canConvert()) + type = ui->cmbSearchType->currentData().value(); + + if (ui->cmbProtocolType->currentData().canConvert()) + protocol = ui->cmbProtocolType->currentData().value(); + + proxyModel_->setFilter(search_re, type, protocol); + /* If items are filtered out, then filtered back in, the tree remains collapsed + Force an expansion */ + ui->protocol_tree_->expandAll(); +} + +void EnabledProtocolsDialog::on_search_line_edit__textChanged(const QString &) +{ + searchFilterChange(); +} + +void EnabledProtocolsDialog::on_cmbSearchType_currentIndexChanged(int) +{ + searchFilterChange(); +} + +void EnabledProtocolsDialog::on_cmbProtocolType_currentIndexChanged(int) +{ + searchFilterChange(); +} + +void EnabledProtocolsDialog::on_buttonBox_accepted() +{ + enabled_protocols_model_->applyChanges(); +} + +void EnabledProtocolsDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_ENABLED_PROTOCOLS_DIALOG); +} diff --git a/ui/qt/enabled_protocols_dialog.h b/ui/qt/enabled_protocols_dialog.h new file mode 100644 index 00000000..eb72ea8f --- /dev/null +++ b/ui/qt/enabled_protocols_dialog.h @@ -0,0 +1,49 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ENABLED_PROTOCOLS_DIALOG_H +#define ENABLED_PROTOCOLS_DIALOG_H + +#include "geometry_state_dialog.h" +#include "wireshark_dialog.h" +#include + +namespace Ui { +class EnabledProtocolsDialog; +} + +class EnabledProtocolsDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit EnabledProtocolsDialog(QWidget *parent); + virtual ~EnabledProtocolsDialog(); + +private slots: + void on_invert_button__clicked(); + void on_enable_all_button__clicked(); + void on_disable_all_button__clicked(); + void on_search_line_edit__textChanged(const QString &); + void on_cmbSearchType_currentIndexChanged(int); + void on_cmbProtocolType_currentIndexChanged(int); + void on_buttonBox_accepted(); + void on_buttonBox_helpRequested(); + void fillTree(); + +private: + Ui::EnabledProtocolsDialog *ui; + + EnabledProtocolsModel* enabled_protocols_model_; + EnabledProtocolsProxyModel* proxyModel_; + + void searchFilterChange(); +}; + +#endif // ENABLED_PROTOCOLS_DIALOG_H diff --git a/ui/qt/enabled_protocols_dialog.ui b/ui/qt/enabled_protocols_dialog.ui new file mode 100644 index 00000000..5c81c3d3 --- /dev/null +++ b/ui/qt/enabled_protocols_dialog.ui @@ -0,0 +1,146 @@ + + + EnabledProtocolsDialog + + + + 0 + 0 + 987 + 595 + + + + Dialog + + + + + + + + Search: + + + + + + + + + + + + + in + + + + + + + + + + + + true + + + + + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + Enable All + + + + + + + Disable All + + + + + + + Invert + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + EnabledProtocolsDialog + accept() + + + 6 + 559 + + + 157 + 274 + + + + + buttonBox + rejected() + EnabledProtocolsDialog + reject() + + + 32 + 559 + + + 286 + 274 + + + + + diff --git a/ui/qt/endpoint_dialog.cpp b/ui/qt/endpoint_dialog.cpp new file mode 100644 index 00000000..38a30be2 --- /dev/null +++ b/ui/qt/endpoint_dialog.cpp @@ -0,0 +1,156 @@ +/* endpoint_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "endpoint_dialog.h" + +#include + +#include +#include + +#include "ui/recent.h" + +#include "wsutil/filesystem.h" +#include "wsutil/file_util.h" +#include "wsutil/pint.h" +#include "wsutil/str_util.h" +#include + +#include +#include +#include +#include +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum +{ + ENDP_COLUMN_ADDR, + ENDP_COLUMN_PORT, + ENDP_COLUMN_PACKETS, + ENDP_COLUMN_BYTES, + ENDP_COLUMN_PKT_AB, + ENDP_COLUMN_BYTES_AB, + ENDP_COLUMN_PKT_BA, + ENDP_COLUMN_BYTES_BA, + ENDP_NUM_COLUMNS, + ENDP_COLUMN_GEO_COUNTRY = ENDP_NUM_COLUMNS, + ENDP_COLUMN_GEO_CITY, + ENDP_COLUMN_GEO_AS_NUM, + ENDP_COLUMN_GEO_AS_ORG, + ENDP_NUM_GEO_COLUMNS +} endpoint_column_type_e; + +static const QString table_name_ = QObject::tr("Endpoint"); + +static ATapDataModel * createModel(int protoId, QString filter) +{ + return new EndpointDataModel(protoId, filter); +} + +EndpointDialog::EndpointDialog(QWidget &parent, CaptureFile &cf) : + TrafficTableDialog(parent, cf, table_name_) +{ + trafficList()->setProtocolInfo(table_name_, &(recent.endpoint_tabs)); + + trafficTab()->setProtocolInfo(table_name_, trafficList(), &(recent.endpoint_tabs_columns), &createModel); + trafficTab()->setFilter(cf.displayFilter()); + + connect(trafficTab(), &TrafficTab::filterAction, this, &EndpointDialog::filterAction); + connect(trafficTab()->tabBar(), &QTabBar::currentChanged, this, &EndpointDialog::tabChanged); + connect(trafficTab(), &TrafficTab::tabDataChanged, this, &EndpointDialog::tabChanged); + +#ifdef HAVE_MAXMINDDB + map_bt_ = buttonBox()->addButton(tr("Map"), QDialogButtonBox::ActionRole); + map_bt_->setToolTip(tr("Draw IPv4 or IPv6 endpoints on a map.")); + + QMenu *map_menu_ = new QMenu(map_bt_); + QAction *action; + action = map_menu_->addAction(tr("Open in browser")); + connect(action, &QAction::triggered, this, &EndpointDialog::openMap); + action = map_menu_->addAction(tr("Save As…")); + connect(action, &QAction::triggered, this, &EndpointDialog::saveMap); + map_bt_->setMenu(map_menu_); +#endif + + updateWidgets(); +} + +void EndpointDialog::captureFileClosing() +{ + trafficTab()->disableTap(); + displayFilterCheckBox()->setEnabled(false); + TrafficTableDialog::captureFileClosing(); +} + +void EndpointDialog::tabChanged(int idx) +{ +#ifdef HAVE_MAXMINDDB + if (idx == trafficTab()->currentIndex()) + { + bool geoIp = trafficTab()->hasGeoIPData(idx); + map_bt_->setEnabled(geoIp); + } +#else + Q_UNUSED(idx); +#endif + + TrafficTableDialog::currentTabChanged(); +} + +#ifdef HAVE_MAXMINDDB +void EndpointDialog::openMap() +{ + QUrl map_file = trafficTab()->createGeoIPMap(false); + if (!map_file.isEmpty()) { + QDesktopServices::openUrl(map_file); + } +} + +void EndpointDialog::saveMap() +{ + QString destination_file = + WiresharkFileDialog::getSaveFileName(this, tr("Save Endpoints Map"), + "ipmap.html", + "HTML files (*.html);;GeoJSON files (*.json)"); + if (destination_file.isEmpty()) { + return; + } + QUrl map_file = trafficTab()->createGeoIPMap(destination_file.endsWith(".json")); + if (!map_file.isEmpty()) { + QString source_file = map_file.toLocalFile(); + QFile::remove(destination_file); + if (!QFile::rename(source_file, destination_file)) { + QMessageBox::warning(this, tr("Map file error"), + tr("Failed to save map file %1.").arg(destination_file)); + QFile::remove(source_file); + } + } +} +#endif + +void EndpointDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_STATS_ENDPOINTS_DIALOG); +} + +void init_endpoint_table(struct register_ct* ct, const char *filter) +{ + mainApp->emitStatCommandSignal("Endpoints", filter, GINT_TO_POINTER(get_conversation_proto_id(ct))); +} diff --git a/ui/qt/endpoint_dialog.h b/ui/qt/endpoint_dialog.h new file mode 100644 index 00000000..682dc389 --- /dev/null +++ b/ui/qt/endpoint_dialog.h @@ -0,0 +1,51 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ENDPOINT_DIALOG_H +#define ENDPOINT_DIALOG_H + +#include + +#include "traffic_table_dialog.h" + +#include + +class EndpointDialog : public TrafficTableDialog +{ + Q_OBJECT +public: + /** Create a new endpoint window. + * + * @param parent Parent widget. + * @param cf Capture file. No statistics will be calculated if this is NULL. + */ + explicit EndpointDialog(QWidget &parent, CaptureFile &cf); + +signals: + +protected: + void captureFileClosing(); + +private: +#ifdef HAVE_MAXMINDDB + QPushButton * map_bt_; +#endif + +private slots: +#ifdef HAVE_MAXMINDDB + void openMap(); + void saveMap(); +#endif + void tabChanged(int idx); + void on_buttonBox_helpRequested(); +}; + +void init_endpoint_table(struct register_ct* ct, const char *filter); + +#endif // ENDPOINT_DIALOG_H diff --git a/ui/qt/expert_info_dialog.cpp b/ui/qt/expert_info_dialog.cpp new file mode 100644 index 00000000..38fa4496 --- /dev/null +++ b/ui/qt/expert_info_dialog.cpp @@ -0,0 +1,353 @@ +/* expert_info_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "expert_info_dialog.h" +#include + +#include "file.h" + +#include +#include +#include +#include + +#include "progress_frame.h" +#include "main_application.h" + +#include +#include +#include +#include +#include + +// To do: +// - Test with custom expert levels (Preferences -> Expert). +// - Test with large captures. +// - Promote to a fourth pane in the main window? +// - Make colors configurable? In theory we could condense image/expert_indicators.svg, +// down to one item, make sure it uses a single (or a few) base color(s), and generate +// icons on the fly. + +ExpertInfoDialog::ExpertInfoDialog(QWidget &parent, CaptureFile &capture_file, QString displayFilter) : + WiresharkDialog(parent, capture_file), + ui(new Ui::ExpertInfoDialog), + expert_info_model_(new ExpertInfoModel(capture_file)), + proxyModel_(new ExpertInfoProxyModel(this)), + display_filter_(displayFilter) +{ + ui->setupUi(this); + ui->hintLabel->setSmallText(); + ui->limitCheckBox->setChecked(! display_filter_.isEmpty()); + connect(ui->limitCheckBox, &QCheckBox::toggled, + this, &ExpertInfoDialog::limitCheckBoxToggled); + + proxyModel_->setSourceModel(expert_info_model_); + ui->expertInfoTreeView->setModel(proxyModel_); + + setWindowSubtitle(tr("Expert Information")); + + // Clicking on an item jumps to its associated packet. Make the dialog + // narrow so that we avoid obscuring the packet list. + int dlg_width = parent.width() * 3 / 5; + if (dlg_width < width()) dlg_width = width(); + loadGeometry(dlg_width, parent.height()); + + int one_em = fontMetrics().height(); + ui->expertInfoTreeView->setColumnWidth(ExpertInfoProxyModel::colProxySummary, one_em * 25); // Arbitrary + + //Unfortunately this has to be done manually and not through .ui + ui->severitiesPushButton->setMenu(ui->menuShowExpert); + + ui->expertInfoTreeView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->expertInfoTreeView, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showExpertInfoMenu(QPoint))); + + QMenu *submenu; + + FilterAction::Action cur_action = FilterAction::ActionApply; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + cur_action = FilterAction::ActionPrepare; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + FilterAction *fa; + QList extra_actions = + QList() << FilterAction::ActionFind + << FilterAction::ActionColorize + << FilterAction::ActionWebLookup + << FilterAction::ActionCopy; + + foreach (FilterAction::Action extra_action, extra_actions) { + fa = new FilterAction(&ctx_menu_, extra_action); + ctx_menu_.addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + //Add collapse/expand all menu options + QAction *collapse = new QAction(tr("Collapse All"), this); + ctx_menu_.addAction(collapse); + connect(collapse, SIGNAL(triggered()), this, SLOT(collapseTree())); + + QAction *expand = new QAction(tr("Expand All"), this); + ctx_menu_.addAction(expand); + connect(expand, SIGNAL(triggered()), this, SLOT(expandTree())); + + connect(&cap_file_, SIGNAL(captureEvent(CaptureEvent)), + this, SLOT(captureEvent(CaptureEvent))); + + ProgressFrame::addToButtonBox(ui->buttonBox, &parent); + + updateWidgets(); + QTimer::singleShot(0, this, SLOT(retapPackets())); +} + +ExpertInfoDialog::~ExpertInfoDialog() +{ + delete ui; + delete proxyModel_; + delete expert_info_model_; +} + +void ExpertInfoDialog::clearAllData() +{ + expert_info_model_->clear(); +} + +ExpertInfoTreeView* ExpertInfoDialog::getExpertInfoView() +{ + return ui->expertInfoTreeView; +} + +void ExpertInfoDialog::retapPackets() +{ + if (file_closed_) return; + + clearAllData(); + removeTapListeners(); + + if (!registerTapListener("expert", + expert_info_model_, + ui->limitCheckBox->isChecked() ? display_filter_.toUtf8().constData(): NULL, + TL_REQUIRES_COLUMNS, + ExpertInfoModel::tapReset, + ExpertInfoModel::tapPacket, + ExpertInfoModel::tapDraw)) { + return; + } + + cap_file_.retapPackets(); +} + +void ExpertInfoDialog::captureEvent(CaptureEvent e) +{ + if (e.captureContext() == CaptureEvent::Retap) + { + switch (e.eventType()) + { + case CaptureEvent::Started: + ui->limitCheckBox->setEnabled(false); + ui->groupBySummaryCheckBox->setEnabled(false); + break; + case CaptureEvent::Finished: + updateWidgets(); + break; + default: + break; + } + } +} + +void ExpertInfoDialog::updateWidgets() +{ + ui->limitCheckBox->setEnabled(! file_closed_ && ! display_filter_.isEmpty()); + + ui->actionShowError->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityError) > 0); + ui->actionShowWarning->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityWarn) > 0); + ui->actionShowNote->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityNote) > 0); + ui->actionShowChat->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityChat) > 0); + ui->actionShowComment->setEnabled(expert_info_model_->numEvents(ExpertInfoModel::severityComment) > 0); + + QString tooltip; + QString hint; + + if (file_closed_) { + tooltip = tr("Capture file closed."); + hint = tr("Capture file closed."); + } else if (display_filter_.isEmpty()) { + tooltip = tr("No display filter"); + hint = tr("No display filter set."); + } else { + tooltip = tr("Limit information to \"%1\".").arg(display_filter_); + hint = tr("Display filter: \"%1\"").arg(display_filter_); + } + + ui->limitCheckBox->setToolTip(tooltip); + ui->hintLabel->setText(hint); + + ui->groupBySummaryCheckBox->setEnabled(!file_closed_); +} + +void ExpertInfoDialog::on_actionShowError_toggled(bool checked) +{ + proxyModel_->setSeverityFilter(PI_ERROR, !checked); + updateWidgets(); +} + +void ExpertInfoDialog::on_actionShowWarning_toggled(bool checked) +{ + proxyModel_->setSeverityFilter(PI_WARN, !checked); + updateWidgets(); +} + +void ExpertInfoDialog::on_actionShowNote_toggled(bool checked) +{ + proxyModel_->setSeverityFilter(PI_NOTE, !checked); + updateWidgets(); +} + +void ExpertInfoDialog::on_actionShowChat_toggled(bool checked) +{ + proxyModel_->setSeverityFilter(PI_CHAT, !checked); + updateWidgets(); +} + +void ExpertInfoDialog::on_actionShowComment_toggled(bool checked) +{ + proxyModel_->setSeverityFilter(PI_COMMENT, !checked); + updateWidgets(); +} + + +void ExpertInfoDialog::showExpertInfoMenu(QPoint pos) +{ + bool enable = true; + QModelIndex expertIndex = ui->expertInfoTreeView->indexAt(pos); + if (!expertIndex.isValid()) { + return; + } + + if (proxyModel_->data(expertIndex.sibling(expertIndex.row(), ExpertInfoModel::colHf), Qt::DisplayRole).toInt() < 0) { + enable = false; + } + + foreach (QMenu *submenu, ctx_menu_.findChildren()) { + submenu->setEnabled(enable && !file_closed_); + } + foreach (QAction *action, ctx_menu_.actions()) { + FilterAction *fa = qobject_cast(action); + bool action_enable = enable && !file_closed_; + if (fa && (fa->action() == FilterAction::ActionWebLookup || fa->action() == FilterAction::ActionCopy)) { + action_enable = enable; + } + action->setEnabled(action_enable); + } + + ctx_menu_.popup(ui->expertInfoTreeView->viewport()->mapToGlobal(pos)); +} + +void ExpertInfoDialog::filterActionTriggered() +{ + QModelIndex modelIndex = ui->expertInfoTreeView->currentIndex(); + FilterAction *fa = qobject_cast(QObject::sender()); + + if (!fa || !modelIndex.isValid()) { + return; + } + + int hf_index = proxyModel_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colHf), Qt::DisplayRole).toInt(); + + if (hf_index > -1) { + QString filter_string; + if (fa->action() == FilterAction::ActionWebLookup) { + filter_string = QString("%1 %2") + .arg(proxyModel_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colProtocol), Qt::DisplayRole).toString()) + .arg(proxyModel_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colSummary), Qt::DisplayRole).toString()); + } else if (fa->action() == FilterAction::ActionCopy) { + filter_string = QString("%1 %2: %3") + .arg(proxyModel_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colPacket), Qt::DisplayRole).toUInt()) + .arg(proxyModel_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colProtocol), Qt::DisplayRole).toString()) + .arg(proxyModel_->data(modelIndex.sibling(modelIndex.row(), ExpertInfoModel::colSummary), Qt::DisplayRole).toString()); + } else { + filter_string = proto_registrar_get_abbrev(hf_index); + } + + if (! filter_string.isEmpty()) { + emit filterAction(filter_string, fa->action(), fa->actionType()); + } + } +} + +void ExpertInfoDialog::collapseTree() +{ + ui->expertInfoTreeView->collapseAll(); +} + +void ExpertInfoDialog::expandTree() +{ + ui->expertInfoTreeView->expandAll(); +} + +void ExpertInfoDialog::limitCheckBoxToggled(bool) +{ + retapPackets(); +} + +void ExpertInfoDialog::on_groupBySummaryCheckBox_toggled(bool) +{ + expert_info_model_->setGroupBySummary(ui->groupBySummaryCheckBox->isChecked()); +} + +// Show child (packet list) items that match the contents of searchLineEdit. +void ExpertInfoDialog::on_searchLineEdit_textChanged(const QString &search_re) +{ + proxyModel_->setSummaryFilter(search_re); +} + +void ExpertInfoDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_EXPERT_INFO_DIALOG); +} + +// Stat command + args + +static void +expert_info_init(const char *, void*) { + mainApp->emitStatCommandSignal("ExpertInfo", NULL, NULL); +} + +static stat_tap_ui expert_info_stat_ui = { + REGISTER_STAT_GROUP_GENERIC, + NULL, + "expert", + expert_info_init, + 0, + NULL +}; + +extern "C" { + +void register_tap_listener_qt_expert_info(void); + +void +register_tap_listener_qt_expert_info(void) +{ + register_stat_tap_ui(&expert_info_stat_ui, NULL); +} + +} diff --git a/ui/qt/expert_info_dialog.h b/ui/qt/expert_info_dialog.h new file mode 100644 index 00000000..1494ffb4 --- /dev/null +++ b/ui/qt/expert_info_dialog.h @@ -0,0 +1,77 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPERT_INFO_DIALOG_H +#define EXPERT_INFO_DIALOG_H + +#include + +#include + +#include "filter_action.h" +#include "wireshark_dialog.h" +#include +#include +#include + +#include + +namespace Ui { +class ExpertInfoDialog; +} + +class ExpertInfoDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit ExpertInfoDialog(QWidget &parent, CaptureFile& capture_file, QString displayFilter); + ~ExpertInfoDialog(); + + void clearAllData(); + + ExpertInfoTreeView* getExpertInfoView(); + +signals: + void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + +private: + Ui::ExpertInfoDialog *ui; + + ExpertInfoModel* expert_info_model_; + ExpertInfoProxyModel* proxyModel_; + + QMenu ctx_menu_; + + QString display_filter_; + +private slots: + void retapPackets(); + void captureEvent(CaptureEvent e); + + void updateWidgets(); + + void on_actionShowError_toggled(bool checked); + void on_actionShowWarning_toggled(bool checked); + void on_actionShowNote_toggled(bool checked); + void on_actionShowChat_toggled(bool checked); + void on_actionShowComment_toggled(bool checked); + + void showExpertInfoMenu(QPoint pos); + void filterActionTriggered(); + void collapseTree(); + void expandTree(); + + void limitCheckBoxToggled(bool); + void on_groupBySummaryCheckBox_toggled(bool); + void on_searchLineEdit_textChanged(const QString &search_re); + void on_buttonBox_helpRequested(); +}; + +#endif // EXPERT_INFO_DIALOG_H diff --git a/ui/qt/expert_info_dialog.ui b/ui/qt/expert_info_dialog.ui new file mode 100644 index 00000000..ade23694 --- /dev/null +++ b/ui/qt/expert_info_dialog.ui @@ -0,0 +1,254 @@ + + + ExpertInfoDialog + + + + 0 + 0 + 620 + 540 + + + + Dialog + + + + + + true + + + true + + + + + + + <small><i>A hint.</i></small> + + + + + + + + + Limit to Display Filter + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Group by summary + + + true + + + + + + + Qt::Horizontal + + + + 20 + 10 + + + + + + + + Search expert summaries. + + + Search: + + + + + + + Search expert summaries. + + + + + + + Qt::Horizontal + + + + 40 + 10 + + + + + + + + Show… + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + true + + + true + + + Error + + + Show error packets. + + + + + true + + + true + + + Warning + + + Show warning packets. + + + + + true + + + true + + + Note + + + Show note packets. + + + + + true + + + true + + + Chat + + + Show chat packets. + + + + + true + + + true + + + Comment + + + Show comment packets. + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+ + ExpertInfoTreeView + QTreeView +
widgets/expert_info_view.h
+
+
+ + + + buttonBox + accepted() + ExpertInfoDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ExpertInfoDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/export_dissection_dialog.cpp b/ui/qt/export_dissection_dialog.cpp new file mode 100644 index 00000000..7f2664ec --- /dev/null +++ b/ui/qt/export_dissection_dialog.cpp @@ -0,0 +1,287 @@ +/* export_dissection_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "export_dissection_dialog.h" + +#ifdef Q_OS_WIN +#include +#include "ui/packet_range.h" +#include "ui/win32/file_dlg_win32.h" +#else // Q_OS_WIN + +#include "ui/alert_box.h" +#include "ui/help_url.h" +#include "ui/util.h" + +#include +#include + +#include + + +#include +#include +#include +#endif // Q_OS_WIN + +#include +#include "main_application.h" + +#if !defined(Q_OS_WIN) +static const QStringList export_extensions = QStringList() + << "" + << "txt" + << "" + << "csv" + << "psml" + << "pdml" + << "c" + << "json"; + +#endif + +ExportDissectionDialog::ExportDissectionDialog(QWidget *parent, capture_file *cap_file, export_type_e export_type, QString selRange): + WiresharkFileDialog(parent), + export_type_(export_type), + cap_file_(cap_file) +#if !defined(Q_OS_WIN) + , save_bt_(NULL) +#else + , sel_range_(selRange) +#endif /* Q_OS_WIN */ +{ + setWindowTitle(mainApp->windowTitleString(tr("Export Packet Dissections"))); + + switch (prefs.gui_fileopen_style) { + + case FO_STYLE_LAST_OPENED: + /* The user has specified that we should start out in the last directory + * we looked in. If we've already opened a file, use its containing + * directory, if we could determine it, as the directory, otherwise + * use the "last opened" directory saved in the preferences file if + * there was one. + */ + setDirectory(mainApp->openDialogInitialDir()); + break; + + case FO_STYLE_SPECIFIED: + /* The user has specified that we should always start out in a + * specified directory; if they've specified that directory, + * start out by showing the files in that dir. + */ + if (prefs.gui_fileopen_dir[0] != '\0') + setDirectory(prefs.gui_fileopen_dir); + break; + } + +#if !defined(Q_OS_WIN) + // Add extra widgets + // https://wiki.qt.io/Qt_project_org_faq#How_can_I_add_widgets_to_my_QFileDialog_instance.3F + setOption(QFileDialog::DontUseNativeDialog, true); + QDialogButtonBox *button_box = findChild(); + QGridLayout *fd_grid = qobject_cast(layout()); + QHBoxLayout *h_box = new QHBoxLayout(); + QStringList name_filters; + int last_row; + + setAcceptMode(QFileDialog::AcceptSave); + setLabelText(FileType, tr("Export As:")); + + // export_type_map_keys() sorts alphabetically. We don't want that. + name_filters + << tr("Plain text (*.txt)") + << tr("Comma Separated Values - summary (*.csv)") + << tr("PSML - summary (*.psml, *.xml)") + << tr("PDML - details (*.pdml, *.xml)") + << tr("JSON (*.json)") + << tr("C Arrays - bytes (*.c, *.h)"); + export_type_map_[name_filters[0]] = export_type_text; + export_type_map_[name_filters[1]] = export_type_csv; + export_type_map_[name_filters[2]] = export_type_psml; + export_type_map_[name_filters[3]] = export_type_pdml; + export_type_map_[name_filters[4]] = export_type_json; + export_type_map_[name_filters[5]] = export_type_carrays; + setNameFilters(name_filters); + selectNameFilter(export_type_map_.key(export_type)); + exportTypeChanged(export_type_map_.key(export_type)); + + last_row = fd_grid->rowCount(); + fd_grid->addItem(new QSpacerItem(1, 1), last_row, 0); + fd_grid->addLayout(h_box, last_row, 1); + + print_args_.file = NULL; + /* Init the export range */ + packet_range_init(&print_args_.range, cap_file_); + /* Default to displayed packets */ + print_args_.range.process_filtered = TRUE; + + packet_range_group_box_.initRange(&print_args_.range, selRange); + h_box->addWidget(&packet_range_group_box_); + + h_box->addWidget(&packet_format_group_box_, 0, Qt::AlignTop); + + if (button_box) { + button_box->addButton(QDialogButtonBox::Help); + connect(button_box, SIGNAL(helpRequested()), this, SLOT(on_buttonBox_helpRequested())); + + save_bt_ = button_box->button(QDialogButtonBox::Save); + } + + if (save_bt_) { + connect(&packet_range_group_box_, SIGNAL(validityChanged(bool)), + this, SLOT(checkValidity())); + connect(&packet_format_group_box_, SIGNAL(formatChanged()), + this, SLOT(checkValidity())); + } + connect(this, SIGNAL(filterSelected(QString)), this, SLOT(exportTypeChanged(QString))); + + // Grow the dialog to account for the extra widgets. + resize(width(), height() + (packet_range_group_box_.height() * 2 / 3)); + + connect(this, SIGNAL(filesSelected(QStringList)), this, SLOT(dialogAccepted(QStringList))); +#else // Q_OS_WIN +#endif // Q_OS_WIN +} + +ExportDissectionDialog::~ExportDissectionDialog() +{ +#if !defined(Q_OS_WIN) + g_free(print_args_.file); + packet_range_cleanup(&print_args_.range); +#endif +} + +void ExportDissectionDialog::show() +{ +#if !defined(Q_OS_WIN) + if (cap_file_) { + WiresharkFileDialog::show(); + } +#else // Q_OS_WIN + win32_export_file((HWND)parentWidget()->effectiveWinId(), windowTitle().toStdWString().c_str(), cap_file_, export_type_, sel_range_.toStdString().c_str()); +#endif // Q_OS_WIN +} + +#ifndef Q_OS_WIN +void ExportDissectionDialog::dialogAccepted(const QStringList &selected) +{ + if (selected.length() > 0) { + cf_print_status_t status; + QString file_name = selected[0]; + + /* Fill in our print (and export) args */ + + print_args_.file = qstring_strdup(file_name); + print_args_.format = PR_FMT_TEXT; + print_args_.to_file = TRUE; + print_args_.cmd = NULL; + print_args_.print_summary = TRUE; + print_args_.print_col_headings = TRUE; + print_args_.print_dissections = print_dissections_as_displayed; + print_args_.print_hex = FALSE; + print_args_.print_formfeed = FALSE; + + switch (export_type_) { + case export_type_text: /* Text */ + print_args_.print_summary = packet_format_group_box_.summaryEnabled(); + print_args_.print_col_headings = packet_format_group_box_.includeColumnHeadingsEnabled(); + print_args_.print_dissections = print_dissections_none; + if (packet_format_group_box_.detailsEnabled()) { + if (packet_format_group_box_.allCollapsedEnabled()) + print_args_.print_dissections = print_dissections_collapsed; + else if (packet_format_group_box_.asDisplayedEnabled()) + print_args_.print_dissections = print_dissections_as_displayed; + else if (packet_format_group_box_.allExpandedEnabled()) + print_args_.print_dissections = print_dissections_expanded; + } + print_args_.print_hex = packet_format_group_box_.bytesEnabled(); + print_args_.hexdump_options = packet_format_group_box_.getHexdumpOptions(); + print_args_.stream = print_stream_text_new(TRUE, print_args_.file); + if (print_args_.stream == NULL) { + open_failure_alert_box(print_args_.file, errno, TRUE); + return; + } + status = cf_print_packets(cap_file_, &print_args_, TRUE); + break; + case export_type_csv: /* CSV */ + status = cf_write_csv_packets(cap_file_, &print_args_); + break; + case export_type_carrays: /* C Arrays */ + status = cf_write_carrays_packets(cap_file_, &print_args_); + break; + case export_type_psml: /* PSML */ + status = cf_write_psml_packets(cap_file_, &print_args_); + break; + case export_type_pdml: /* PDML */ + status = cf_write_pdml_packets(cap_file_, &print_args_); + break; + case export_type_json: /* JSON */ + status = cf_write_json_packets(cap_file_, &print_args_); + break; + default: + return; + } + + switch (status) { + case CF_PRINT_OK: + break; + case CF_PRINT_OPEN_ERROR: + open_failure_alert_box(print_args_.file, errno, TRUE); + break; + case CF_PRINT_WRITE_ERROR: + write_failure_alert_box(print_args_.file, errno); + break; + } + + gchar *dirname; + /* Save the directory name for future file dialogs. */ + dirname = get_dirname(print_args_.file); /* Overwrites file_name data */ + set_last_open_dir(dirname); + } +} + +void ExportDissectionDialog::exportTypeChanged(QString name_filter) +{ + export_type_ = export_type_map_.value(name_filter); + if (export_type_ == export_type_text) { + packet_format_group_box_.setEnabled(true); + print_args_.format = PR_FMT_TEXT; + } else { + packet_format_group_box_.setEnabled(false); + } + + checkValidity(); + setDefaultSuffix(export_extensions[export_type_]); +} + +void ExportDissectionDialog::checkValidity() +{ + bool enable = true; + + if (!save_bt_) return; + + if (!packet_range_group_box_.isValid()) enable = false; + + if (export_type_ == export_type_text) { + if (! packet_format_group_box_.summaryEnabled() && + ! packet_format_group_box_.detailsEnabled() && + ! packet_format_group_box_.bytesEnabled()) + { + enable = false; + } + } + + save_bt_->setEnabled(enable); +} + +void ExportDissectionDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_EXPORT_FILE_DIALOG); +} +#endif // Q_OS_WIN diff --git a/ui/qt/export_dissection_dialog.h b/ui/qt/export_dissection_dialog.h new file mode 100644 index 00000000..6d794918 --- /dev/null +++ b/ui/qt/export_dissection_dialog.h @@ -0,0 +1,66 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPORT_DISSECTION_DIALOG_H +#define EXPORT_DISSECTION_DIALOG_H + +#include + +#include + +#include "file.h" +#include "epan/print.h" + +#include "ui/file_dialog.h" +#include + +#ifndef Q_OS_WIN +#include "packet_range_group_box.h" +#include "packet_format_group_box.h" +#endif // Q_OS_WIN + +#include + +class ExportDissectionDialog : public WiresharkFileDialog +{ + Q_OBJECT + +public: + explicit ExportDissectionDialog(QWidget *parent, capture_file *cap_file, export_type_e export_type, QString selRange = QString()); + ~ExportDissectionDialog(); + +public slots: + void show(); + +private slots: +#ifndef Q_OS_WIN + void dialogAccepted(const QStringList &selected); + void exportTypeChanged(QString name_filter); + void checkValidity(); + void on_buttonBox_helpRequested(); +#endif // Q_OS_WIN + +private: + export_type_e export_type_; + capture_file *cap_file_; +#ifndef Q_OS_WIN + print_args_t print_args_; + + QMap export_type_map_; + PacketRangeGroupBox packet_range_group_box_; + + PacketFormatGroupBox packet_format_group_box_; + + QPushButton *save_bt_; +#else + QString sel_range_; +#endif // Q_OS_WIN +}; + +#endif // EXPORT_DISSECTION_DIALOG_H diff --git a/ui/qt/export_object_action.cpp b/ui/qt/export_object_action.cpp new file mode 100644 index 00000000..bccf84ff --- /dev/null +++ b/ui/qt/export_object_action.cpp @@ -0,0 +1,41 @@ +/* conversation_colorize_action.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include +#include +#include "export_object_action.h" + +#include + +#include + +ExportObjectAction::ExportObjectAction(QObject *parent, register_eo_t *eo) : + QAction(parent), + eo_(eo) +{ + if (eo_) { + setText(QString("%1%2").arg(proto_get_protocol_short_name(find_protocol_by_id(get_eo_proto_id(eo)))).arg(UTF8_HORIZONTAL_ELLIPSIS)); + } +} + +void ExportObjectAction::captureFileEvent(CaptureEvent e) +{ + if (e.captureContext() == CaptureEvent::File) + { + if (e.eventType() == CaptureEvent::Opened) + setEnabled(true); + else if (e.eventType() == CaptureEvent::Closed) + setEnabled(false); + } +} diff --git a/ui/qt/export_object_action.h b/ui/qt/export_object_action.h new file mode 100644 index 00000000..e4cc4cc5 --- /dev/null +++ b/ui/qt/export_object_action.h @@ -0,0 +1,40 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPORTOBJECTACTION_H +#define EXPORTOBJECTACTION_H + +#include "config.h" + +#include +#include +#include + +#include + +#include + +// Actions for "Export Objects" menu items. + +class ExportObjectAction : public QAction +{ + Q_OBJECT +public: + ExportObjectAction(QObject *parent, register_eo_t *eo = NULL); + + register_eo_t* exportObject() {return eo_;} + +public slots: + void captureFileEvent(CaptureEvent e); + +private: + register_eo_t *eo_; +}; + +#endif // EXPORTOBJECTACTION_H diff --git a/ui/qt/export_object_dialog.cpp b/ui/qt/export_object_dialog.cpp new file mode 100644 index 00000000..79ad427e --- /dev/null +++ b/ui/qt/export_object_dialog.cpp @@ -0,0 +1,303 @@ +/* export_object_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "export_object_dialog.h" +#include + +#include +#include + +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ExportObjectDialog::ExportObjectDialog(QWidget &parent, CaptureFile &cf, register_eo_t* eo) : + WiresharkDialog(parent, cf), + eo_ui_(new Ui::ExportObjectDialog), + save_bt_(NULL), + save_all_bt_(NULL), + model_(eo, this), + proxyModel_(this) +{ + QPushButton *close_bt; + + eo_ui_->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + + proxyModel_.setSourceModel(&model_); + eo_ui_->objectTree->setModel(&proxyModel_); + + proxyModel_.setFilterFixedString(""); + proxyModel_.setFilterCaseSensitivity(Qt::CaseInsensitive); + proxyModel_.setFilterKeyColumn(-1); + +#if defined(Q_OS_MAC) + eo_ui_->progressLabel->setAttribute(Qt::WA_MacSmallSize, true); + eo_ui_->progressBar->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + connect(&model_, &ExportObjectModel::rowsInserted, + this, &ExportObjectDialog::modelDataChanged); + connect(&model_, &ExportObjectModel::modelReset, this, &ExportObjectDialog::modelRowsReset); + connect(eo_ui_->filterLine, &QLineEdit::textChanged, &proxyModel_, &ExportObjectProxyModel::setTextFilterString); + connect(eo_ui_->objectTree, &ExportObjectsTreeView::currentIndexChanged, this, &ExportObjectDialog::currentHasChanged); + + save_bt_ = eo_ui_->buttonBox->button(QDialogButtonBox::Save); + save_all_bt_ = eo_ui_->buttonBox->button(QDialogButtonBox::SaveAll); + close_bt = eo_ui_->buttonBox->button(QDialogButtonBox::Close); + if (eo_ui_->buttonBox->button(QDialogButtonBox::Open)) + { + QPushButton * open = eo_ui_->buttonBox->button(QDialogButtonBox::Open); + open->setText(tr("Preview")); + open->setEnabled(false); + } + + contentTypes << tr("All Content-Types"); + eo_ui_->cmbContentType->addItems(contentTypes); + + setWindowTitle(mainApp->windowTitleString(QStringList() << tr("Export") << tr("%1 object list").arg(proto_get_protocol_short_name(find_protocol_by_id(get_eo_proto_id(eo)))))); + + if (save_bt_) save_bt_->setEnabled(false); + if (save_all_bt_) save_all_bt_->setEnabled(false); + if (close_bt) close_bt->setDefault(true); + + connect(&cap_file_, &CaptureFile::captureEvent, + this, &ExportObjectDialog::captureEvent); +} + +ExportObjectDialog::~ExportObjectDialog() +{ + delete eo_ui_; + model_.removeTap(); + removeTapListeners(); +} + +void ExportObjectDialog::currentHasChanged(QModelIndex current) +{ + if (current.isValid()) + { + QModelIndex sibl = current.sibling(current.row(), ExportObjectModel::colPacket); + if (eo_ui_->buttonBox->button(QDialogButtonBox::Open)) + { + QString mime_type = sibl.sibling(current.row(), ExportObjectModel::colContent).data().toString(); + eo_ui_->buttonBox->button(QDialogButtonBox::Open)->setEnabled(mimeTypeIsPreviewable(mime_type)); + } + mainApp->gotoFrame(sibl.data().toInt()); + } +} + +bool ExportObjectDialog::mimeTypeIsPreviewable(QString mime_type) +{ + // XXX This excludes everything except HTTP. Maybe that's a good thing? + // Take care when adding to this, e.g. text/html or image/svg might contain JavaScript. + QStringList previewable_mime_types = QStringList() + << "text/plain" + << "image/gif" << "image/jpeg" << "image/png"; + + if (previewable_mime_types.contains(mime_type)) { + return true; + } + return false; +} + +void ExportObjectDialog::modelDataChanged(const QModelIndex&, int from, int to) +{ + bool contentTypes_changed = false; + bool enabled = (model_.rowCount() > 0); + if (save_bt_) save_bt_->setEnabled(enabled); + if (save_all_bt_) save_all_bt_->setEnabled(enabled); + + for (int row = from; row <= to; row++) + { + QModelIndex idx = model_.index(row, ExportObjectModel::colContent); + if (idx.isValid()) + { + QString dataType = idx.data().toString(); + if (dataType.length() > 0 && ! contentTypes.contains(dataType)) + { + contentTypes << dataType; + contentTypes_changed = true; + } + } + } + if (contentTypes_changed) { + contentTypes.sort(Qt::CaseInsensitive); + QString selType = eo_ui_->cmbContentType->currentText(); + eo_ui_->cmbContentType->clear(); + eo_ui_->cmbContentType->addItem(tr("All Content-Types")); + eo_ui_->cmbContentType->addItems(contentTypes); + if (contentTypes.contains(selType) ) + eo_ui_->cmbContentType->setCurrentText(selType); + } +} + +void ExportObjectDialog::modelRowsReset() +{ + contentTypes.clear(); + eo_ui_->cmbContentType->clear(); + eo_ui_->cmbContentType->addItem(tr("All Content-Types")); + + if (save_bt_) save_bt_->setEnabled(false); + if (save_all_bt_) save_all_bt_->setEnabled(false); +} + +void ExportObjectDialog::show() +{ + /* Data will be gathered via a tap callback */ + if (!registerTapListener(model_.getTapListenerName(), model_.getTapData(), NULL, 0, + ExportObjectModel::resetTap, + model_.getTapPacketFunc(), + NULL)) { + return; + } + + QDialog::show(); + cap_file_.retapPackets(); + eo_ui_->progressFrame->hide(); + for (int i = 0; i < eo_ui_->objectTree->model()->columnCount(); i++) + eo_ui_->objectTree->resizeColumnToContents(i); + + eo_ui_->objectTree->sortByColumn(ExportObjectModel::colPacket, Qt::AscendingOrder); +} + +void ExportObjectDialog::keyPressEvent(QKeyEvent *evt) +{ + if(evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) + return; + QDialog::keyPressEvent(evt); +} + +void ExportObjectDialog::accept() +{ + // Don't close the dialog. +} + +void ExportObjectDialog::captureEvent(CaptureEvent e) +{ + if ((e.captureContext() == CaptureEvent::File) && + (e.eventType() == CaptureEvent::Closing)) + { + close(); + } +} + +void ExportObjectDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_EXPORT_OBJECT_LIST); +} + +void ExportObjectDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + switch (eo_ui_->buttonBox->standardButton(button)) { + case QDialogButtonBox::Save: + saveCurrentEntry(); + break; + case QDialogButtonBox::SaveAll: + saveAllEntries(); + break; + case QDialogButtonBox::Open: + { + QString temp; + saveCurrentEntry(&temp); + + if (temp.length() > 0) { + QMimeDatabase mime_db; + QMimeType mime_type = mime_db.mimeTypeForFile(temp, QMimeDatabase::MatchContent); + if (mimeTypeIsPreviewable(mime_type.name())) { + QDesktopServices::openUrl(QUrl(QString("file:///").append(temp), QUrl::TolerantMode)); + } else { + desktop_show_in_folder(temp); + } + + } + break; + } + default: // Help, Cancel + break; + } +} + +void ExportObjectDialog::on_cmbContentType_currentIndexChanged(int index) +{ + QString filterString = index <= 0 ? "" : eo_ui_->cmbContentType->currentText(); + proxyModel_.setContentFilterString(filterString); + +} + +void ExportObjectDialog::saveCurrentEntry(QString *tempFile) +{ + QDir path(mainApp->openDialogInitialDir()); + + QModelIndex proxyIndex = eo_ui_->objectTree->currentIndex(); + if (!proxyIndex.isValid()) + return; + + QModelIndex current = proxyModel_.mapToSource(proxyIndex); + if (!current.isValid()) + return; + + QString entry_filename = current.sibling(current.row(), ExportObjectModel::colFilename).data().toString(); + if (entry_filename.isEmpty()) + return; + + QString file_name; + if (!tempFile) + { + GString *safe_filename = eo_massage_str(entry_filename.toUtf8().constData(), EXPORT_OBJECT_MAXFILELEN, 0); + file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Object As…")), + safe_filename->str); + g_string_free(safe_filename, TRUE); + } else { + QString path = QDir::tempPath().append("/").append(entry_filename); + /* This means, the system must remove the file! */ + file_name = path; + if (QFileInfo::exists(path)) + QFile::remove(path); + *tempFile = path; + } + + model_.saveEntry(current, file_name); +} + +void ExportObjectDialog::saveAllEntries() +{ + QDir save_in_dir(mainApp->openDialogInitialDir()); + QString save_in_path; + + // + // We want the user to be able to specify a directory in which + // to drop files for all the objects, not a file name. + // + // XXX - what we *really* want is something that asks the user + // for an existing directory *but* lets them create a new + // directory in the process. That's what we get on macOS, + // as the native dialog is used, and it supports that; does + // that also work on Windows and with Qt's own dialog? + // + save_in_path = WiresharkFileDialog::getExistingDirectory(this, mainApp->windowTitleString(tr("Save All Objects In…")), + save_in_dir.canonicalPath(), + QFileDialog::ShowDirsOnly); + + if (save_in_path.length() < 1) + return; + + model_.saveAllEntries(save_in_path); +} diff --git a/ui/qt/export_object_dialog.h b/ui/qt/export_object_dialog.h new file mode 100644 index 00000000..1dc12644 --- /dev/null +++ b/ui/qt/export_object_dialog.h @@ -0,0 +1,74 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPORT_OBJECT_DIALOG_H +#define EXPORT_OBJECT_DIALOG_H + +#include + +#include + +#include +#include + +#include "wireshark_dialog.h" + +#include + +class QTreeWidgetItem; +class QAbstractButton; + +namespace Ui { +class ExportObjectDialog; +} + +class ExportObjectDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit ExportObjectDialog(QWidget &parent, CaptureFile &cf, register_eo_t* eo); + ~ExportObjectDialog(); + +public slots: + void show(); + +protected: + virtual void keyPressEvent(QKeyEvent *evt); + +private slots: + void accept(); + void captureEvent(CaptureEvent e); + void on_buttonBox_helpRequested(); + void on_buttonBox_clicked(QAbstractButton *button); + void on_cmbContentType_currentIndexChanged(int index); + + void modelDataChanged(const QModelIndex &topLeft, int from, int to); + void modelRowsReset(); + + void currentHasChanged(QModelIndex current); + +private: + bool mimeTypeIsPreviewable(QString mime_type); + void saveCurrentEntry(QString *tempFile = Q_NULLPTR); + void saveAllEntries(); + + Ui::ExportObjectDialog *eo_ui_; + + QPushButton *save_bt_; + QPushButton *save_all_bt_; + ExportObjectModel model_; + ExportObjectProxyModel proxyModel_; + + QStringList contentTypes; + + void updateContentTypes(); +}; + +#endif // EXPORT_OBJECT_DIALOG_H diff --git a/ui/qt/export_object_dialog.ui b/ui/qt/export_object_dialog.ui new file mode 100644 index 00000000..81ea0da0 --- /dev/null +++ b/ui/qt/export_object_dialog.ui @@ -0,0 +1,181 @@ + + + ExportObjectDialog + + + + 0 + 0 + 750 + 520 + + + + Dialog + + + true + + + + + + + + Text Filter: + + + + + + + Only display entries containing this string + + + + + + + Content Type: + + + + + + + + + + + + false + + + true + + + false + + + true + + + false + + + + + + + true + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + + + Searching for objects + + + + + + + QProgressBar { width: 10em; } + + + 0 + + + -1 + + + false + + + + + + + Qt::Horizontal + + + + 387 + 20 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Open|QDialogButtonBox::Save|QDialogButtonBox::SaveAll + + + + + + + + ExportObjectsTreeView + QTreeView +
widgets/export_objects_view.h
+
+
+ + + + buttonBox + accepted() + ExportObjectDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ExportObjectDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/export_pdu_dialog.cpp b/ui/qt/export_pdu_dialog.cpp new file mode 100644 index 00000000..09709e18 --- /dev/null +++ b/ui/qt/export_pdu_dialog.cpp @@ -0,0 +1,46 @@ +/* export_pdu_dialog.cpp + * Dialog for exporting PDUs to file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "export_pdu_dialog.h" +#include + +#include +#include + +#include "ui/export_pdu_ui_utils.h" +#include "ui/capture_globals.h" + +ExportPDUDialog::ExportPDUDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ExportPDUDialog) +{ + GSList *tap_name_list; + + ui->setupUi(this); + + for (tap_name_list = get_export_pdu_tap_list(); tap_name_list; tap_name_list = g_slist_next(tap_name_list)) { + if (export_pdu_tap_get_encap((const char*)tap_name_list->data) == WTAP_ENCAP_WIRESHARK_UPPER_PDU) { + ui->comboBox->addItem((const char*)(tap_name_list->data)); + } + } +} +void ExportPDUDialog::on_buttonBox_accepted() +{ + const QByteArray& filter = ui->displayFilterLineEdit->text().toUtf8(); + const QByteArray& tap_name = ui->comboBox->currentText().toUtf8(); + + do_export_pdu(filter.constData(), global_capture_opts.temp_dir, tap_name.constData()); +} +ExportPDUDialog::~ExportPDUDialog() +{ + delete ui; +} diff --git a/ui/qt/export_pdu_dialog.h b/ui/qt/export_pdu_dialog.h new file mode 100644 index 00000000..e76399a9 --- /dev/null +++ b/ui/qt/export_pdu_dialog.h @@ -0,0 +1,35 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPORT_PDU_DIALOG_H +#define EXPORT_PDU_DIALOG_H + +#include +#include + +namespace Ui { +class ExportPDUDialog; +} + +class ExportPDUDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ExportPDUDialog(QWidget *parent = 0); + ~ExportPDUDialog(); + +private: + Ui::ExportPDUDialog *ui; + +private slots: + void on_buttonBox_accepted(); +}; + +#endif // EXPORT_PDU_DIALOG_H diff --git a/ui/qt/export_pdu_dialog.ui b/ui/qt/export_pdu_dialog.ui new file mode 100644 index 00000000..2a0fae81 --- /dev/null +++ b/ui/qt/export_pdu_dialog.ui @@ -0,0 +1,106 @@ + + + ExportPDUDialog + + + + 0 + 0 + 393 + 158 + + + + Dialog + + + + + 30 + 100 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 16 + 20 + 361 + 29 + + + + + + + Display filter: + + + + + + + + + + + + 10 + 60 + 120 + 30 + + + + + + + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + + buttonBox + accepted() + ExportPDUDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ExportPDUDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/extcap_argument.cpp b/ui/qt/extcap_argument.cpp new file mode 100644 index 00000000..0a965a79 --- /dev/null +++ b/ui/qt/extcap_argument.cpp @@ -0,0 +1,1036 @@ +/* extcap_argument.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +ExtArgTimestamp::ExtArgTimestamp(extcap_arg * argument, QObject * parent) : + ExtcapArgument(argument, parent), tsBox(0) {} + +QWidget * ExtArgTimestamp::createEditor(QWidget * parent) +{ + QString text = defaultValue(); + + if (_argument->pref_valptr && strlen(*_argument->pref_valptr)) + { + QString storeValue(*_argument->pref_valptr); + text = storeValue.trimmed(); + } + + ts = QDateTime::fromSecsSinceEpoch(text.toInt()); + tsBox = new QDateTimeEdit(ts, parent); + tsBox->setDisplayFormat(QLocale::system().dateTimeFormat()); + tsBox->setCalendarPopup(true); + tsBox->setAutoFillBackground(true); + + if (_argument->tooltip != NULL) + tsBox->setToolTip(QString().fromUtf8(_argument->tooltip)); + + connect(tsBox, SIGNAL(dateTimeChanged(QDateTime)), SLOT(onDateTimeChanged(QDateTime))); + + return tsBox; +} + +void ExtArgTimestamp::onDateTimeChanged(QDateTime t) +{ + ts = t; + emit valueChanged(); +} + +QString ExtArgTimestamp::defaultValue() +{ + return QString::number(QDateTime::currentDateTime().toSecsSinceEpoch()); +} + +bool ExtArgTimestamp::isValid() +{ + bool valid = true; + + if (value().length() == 0 && isRequired()) + valid = false; + + return valid; +} + +QString ExtArgTimestamp::value() +{ + return QString::number(ts.toSecsSinceEpoch()); +} + +QString ExtArgTimestamp::prefValue() +{ + return value(); +} + +bool ExtArgTimestamp::isSetDefaultValueSupported() +{ + return TRUE; +} + +void ExtArgTimestamp::setDefaultValue() +{ + QDateTime t; + + t = QDateTime::fromSecsSinceEpoch(defaultValue().toInt()); + tsBox->setDateTime(t); +} + + + +ExtArgSelector::ExtArgSelector(extcap_arg * argument, QObject * parent) : + ExtcapArgument(argument, parent), boxSelection(0) {} + +QWidget * ExtArgSelector::createEditor(QWidget * parent) +{ + QWidget * editor = new QWidget(parent); + QHBoxLayout * layout = new QHBoxLayout(); + QMargins margins = layout->contentsMargins(); + layout->setContentsMargins(0, margins.top(), 0, margins.bottom()); + + boxSelection = new QComboBox(parent); + boxSelection->setToolTip(QString().fromUtf8(_argument->tooltip)); + layout->addWidget(boxSelection); + + if (values.length() > 0) + { + ExtcapValueList::const_iterator iter = values.constBegin(); + + while (iter != values.constEnd()) + { + boxSelection->addItem((*iter).value(), (*iter).call()); + ++iter; + } + } + + setDefaultValue(); + + if (reload()) + { + QString btnText(tr("Reload data")); + if (_argument->placeholder) + btnText = QString(_argument->placeholder); + + QPushButton * reloadButton = new QPushButton(btnText, editor); + layout->addWidget(reloadButton); + reloadButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + boxSelection->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + + connect(reloadButton, SIGNAL(clicked()), this, SLOT(onReloadTriggered())); + } + + connect (boxSelection, SIGNAL(currentIndexChanged(int)), SLOT(onIntChanged(int))); + + editor->setLayout(layout); + + return editor; +} + +void ExtArgSelector::onReloadTriggered() +{ + int counter = 0; + int selected = -1; + + QString call = boxSelection->currentData().toString(); + const char *prefval = (_argument->pref_valptr && strlen(*_argument->pref_valptr)) ? *_argument->pref_valptr : NULL; + QString stored(prefval ? prefval : ""); + if (call != stored) + stored = call; + + if (reloadValues() && values.length() > 0) + { + boxSelection->clear(); + + ExtcapValueList::const_iterator iter = values.constBegin(); + + while (iter != values.constEnd()) + { + boxSelection->addItem((*iter).value(), (*iter).call()); + + if (stored.compare((*iter).call()) == 0) + selected = counter; + else if ((*iter).isDefault() && selected == -1) + selected = counter; + + counter++; + ++iter; + } + + if (selected > -1 && selected < boxSelection->count()) + boxSelection->setCurrentIndex(selected); + } +} + +bool ExtArgSelector::isValid() +{ + bool valid = true; + + if (value().length() == 0 && isRequired()) + valid = false; + + if (boxSelection) + { + QString lblInvalidColor = ColorUtils::fromColorT(prefs.gui_text_invalid).name(); + QString cmbBoxStyle("QComboBox { background-color: %1; } "); + boxSelection->setStyleSheet(cmbBoxStyle.arg(valid ? QString("") : lblInvalidColor)); + } + + return valid; +} + +QString ExtArgSelector::value() +{ + if (boxSelection == 0) + return QString(); + + QVariant data = boxSelection->currentData(); + + return data.toString(); +} + +bool ExtArgSelector::isSetDefaultValueSupported() +{ + return TRUE; +} + +void ExtArgSelector::setDefaultValue() +{ + int counter = 0; + int selected = -1; + + const char *prefval = (_argument->pref_valptr && strlen(*_argument->pref_valptr)) ? *_argument->pref_valptr : NULL; + QString stored(prefval ? prefval : ""); + + if (values.length() > 0) + { + ExtcapValueList::const_iterator iter = values.constBegin(); + + while (iter != values.constEnd()) + { + if (!prefval && (*iter).isDefault()) + selected = counter; + else if (prefval && stored.compare((*iter).call()) == 0) + selected = counter; + + counter++; + ++iter; + } + + if (selected > -1 && selected < boxSelection->count()) + boxSelection->setCurrentIndex(selected); + } + +} + + +ExtArgEditSelector::ExtArgEditSelector(extcap_arg * argument, QObject * parent) : + ExtArgSelector(argument, parent) {} + +QWidget * ExtArgEditSelector::createEditor(QWidget * parent) +{ + QWidget *editor = ExtArgSelector::createEditor(parent); + + boxSelection->setEditable(true); + boxSelection->setInsertPolicy(QComboBox::NoInsert); + + return editor; +} + +QString ExtArgEditSelector::value() +{ + if (boxSelection == nullptr) { + return QString(); + } + + if (boxSelection->currentIndex() > -1) { + return ExtArgSelector::value(); + } + + return boxSelection->currentText(); +} + +void ExtArgEditSelector::setDefaultValue() +{ + ExtArgSelector::setDefaultValue(); + + if (boxSelection == nullptr) { + return; + } + + const char *prefval = (_argument->pref_valptr && strlen(*_argument->pref_valptr)) ? *_argument->pref_valptr : NULL; + QString stored(prefval ? prefval : ""); + QVariant data = boxSelection->currentData(); + + if (data.toString() != stored) { + // createEditor may not have been called at this point? + boxSelection->setEditable(true); + boxSelection->setInsertPolicy(QComboBox::NoInsert); + boxSelection->setEditText(stored); + } +} + + +ExtArgRadio::ExtArgRadio(extcap_arg * argument, QObject * parent) : + ExtcapArgument(argument, parent), selectorGroup(0), callStrings(0) {} + +QWidget * ExtArgRadio::createEditor(QWidget * parent) +{ + int count = 0; + + selectorGroup = new QButtonGroup(parent); + QWidget * radioButtons = new QWidget; + QVBoxLayout * vrLayout = new QVBoxLayout(); + QMargins margins = vrLayout->contentsMargins(); + vrLayout->setContentsMargins(0, 0, 0, margins.bottom()); + if (callStrings != 0) + delete callStrings; + + callStrings = new QList(); + + if (values.length() > 0) + { + ExtcapValueList::const_iterator iter = values.constBegin(); + + while (iter != values.constEnd()) + { + QRadioButton * radio = new QRadioButton((*iter).value()); + QString callString = (*iter).call(); + callStrings->append(callString); + + connect(radio, SIGNAL(clicked(bool)), SLOT(onBoolChanged(bool))); + selectorGroup->addButton(radio, count); + + vrLayout->addWidget(radio); + count++; + + ++iter; + } + } + + setDefaultValue(); + + radioButtons->setLayout(vrLayout); + + return radioButtons; +} + +QString ExtArgRadio::value() +{ + int idx = 0; + if (selectorGroup == 0 || callStrings == 0) + return QString(); + + idx = selectorGroup->checkedId(); + if (idx > -1 && callStrings->length() > idx) + return callStrings->at(idx); + + return QString(); +} + +bool ExtArgRadio::isValid() +{ + bool valid = true; + int idx = 0; + + if (isRequired()) + { + if (selectorGroup == 0 || callStrings == 0) + valid = false; + else + { + idx = selectorGroup->checkedId(); + if (idx == -1 || callStrings->length() <= idx) + valid = false; + } + } + + /* If nothing is selected, but a selection is required, the only thing that + * can be marked is the label */ + QString lblInvalidColor = ColorUtils::fromColorT(prefs.gui_text_invalid).name(); + _label->setStyleSheet (label_style.arg(valid ? QString("") : lblInvalidColor)); + + return valid; +} + +bool ExtArgRadio::isSetDefaultValueSupported() +{ + return TRUE; +} + +void ExtArgRadio::setDefaultValue() +{ + int counter = 0; + int selected = 0; + + const char *prefval = (_argument->pref_valptr && strlen(*_argument->pref_valptr)) ? *_argument->pref_valptr : NULL; + QString stored(prefval ? prefval : ""); + + if (values.length() > 0) + { + ExtcapValueList::const_iterator iter = values.constBegin(); + + while (iter != values.constEnd()) + { + if (!prefval && (*iter).isDefault()) + selected = counter; + else if (prefval && stored.compare((*iter).call()) == 0) + selected = counter; + + counter++; + ++iter; + } + + ((QRadioButton*)(selectorGroup->button(selected)))->setChecked(true); + } +} + + + +ExtArgBool::ExtArgBool(extcap_arg * argument, QObject * parent) : + ExtcapArgument(argument, parent), boolBox(0) {} + +QWidget * ExtArgBool::createLabel(QWidget * parent) +{ + return new QWidget(parent); +} + +QWidget * ExtArgBool::createEditor(QWidget * parent) +{ + bool state = defaultBool(); + + boolBox = new QCheckBox(QString().fromUtf8(_argument->display), parent); + if (_argument->tooltip != NULL) + boolBox->setToolTip(QString().fromUtf8(_argument->tooltip)); + + const char *prefval = (_argument->pref_valptr && strlen(*_argument->pref_valptr)) ? *_argument->pref_valptr : NULL; + if (prefval) + { + QRegularExpression regexp(EXTCAP_BOOLEAN_REGEX); + QRegularExpressionMatch match = regexp.match(QString(prefval[0])); + bool savedstate = match.hasMatch(); + if (savedstate != state) + state = savedstate; + } + + boolBox->setCheckState(state ? Qt::Checked : Qt::Unchecked); + + connect (boolBox, SIGNAL(stateChanged(int)), SLOT(onIntChanged(int))); + + return boolBox; +} + +QString ExtArgBool::call() +{ + if (boolBox == NULL) + return QString(""); + + if (_argument->arg_type == EXTCAP_ARG_BOOLEAN) + return ExtcapArgument::call(); + + return QString(boolBox->checkState() == Qt::Checked ? _argument->call : ""); +} + +QString ExtArgBool::value() +{ + if (boolBox == NULL || _argument->arg_type == EXTCAP_ARG_BOOLFLAG) + return QString(); + return QString(boolBox->checkState() == Qt::Checked ? "true" : "false"); +} + +QString ExtArgBool::prefValue() +{ + if (boolBox == NULL) + return QString("false"); + return QString(boolBox->checkState() == Qt::Checked ? "true" : "false"); +} + +bool ExtArgBool::isValid() +{ + /* A bool is always valid, but the base function checks on string length, + * which will fail with boolflags */ + return true; +} + +bool ExtArgBool::defaultBool() +{ + bool result = false; + + if (_argument) + { + if (extcap_complex_get_bool(_argument->default_complex) == (gboolean)TRUE) + result = true; + } + + return result; +} + +QString ExtArgBool::defaultValue() +{ + return defaultBool() ? QString("true") : QString("false"); +} + +bool ExtArgBool::isSetDefaultValueSupported() +{ + return TRUE; +} + +void ExtArgBool::setDefaultValue() +{ + boolBox->setCheckState(defaultBool() ? Qt::Checked : Qt::Unchecked); +} + + + +ExtArgText::ExtArgText(extcap_arg * argument, QObject * parent) : + ExtcapArgument(argument, parent), textBox(0) +{ +} + +QWidget * ExtArgText::createEditor(QWidget * parent) +{ + QString text = defaultValue(); + + /* Prefs can contain empty string. We accept it. */ + if (_argument->pref_valptr && (*_argument->pref_valptr)) + { + QString storeValue(*_argument->pref_valptr); + text = storeValue.trimmed(); + } + + textBox = new QLineEdit(text, parent); + + if (_argument->tooltip != NULL) + textBox->setToolTip(QString().fromUtf8(_argument->tooltip)); + + if (_argument->placeholder != NULL) + textBox->setPlaceholderText(QString().fromUtf8(_argument->placeholder)); + + if (_argument->arg_type == EXTCAP_ARG_PASSWORD) + textBox->setEchoMode(QLineEdit::PasswordEchoOnEdit); + + connect(textBox , SIGNAL(textChanged(QString)), SLOT(onStringChanged(QString))); + + return textBox; +} + +QString ExtArgText::value() +{ + if (textBox == 0) + return QString(); + + return textBox->text(); +} + +bool ExtArgText::isValid() +{ + bool valid = true; + + if (isRequired() && value().length() == 0) + valid = false; + + /* Does the validator, if any, consider the value valid? + * + * If it considers it an "intermediate" value, rather than an "invalid" + * value, the user will be able to transfer the input focus to another + * widget, and, as long as all widgets have values for which isValid() + * is true, they wil be able to click the "Start" button. + * + * For QIntValidator(), used for integral fields with minimum and + * maximum values, a value that's larger than the maximum but has + * the same number of digits as the maximum is "intermediate" rather + * than "invalid", so the user will be able to cause that value to + * be passed to the extcap module; see bug 16510. + * + * So we explicitly call the hasAcceptableInput() method on the + * text box; that returns false if the value is not "valid", and + * that includes "intermediate" values. + * + * This way, 1) non-colorblind users are informed that the value + * is invalid by the text box background being red (perhaps the + * user isn't fully colorblind, or perhaps the background is a + * noticeably different grayscale), and 2) the user won't be able + * to start the capture until they fix the problem. + * + * XXX - it might be nice to have some way of indicating to the + * user what the problem is with the value - alert box? Tooltip? */ + if (!textBox->hasAcceptableInput()) + valid = false; + + /* validation should only be checked if there is a value. if the argument + * must be present (isRequired) the check above will handle that */ + if (valid && _argument->regexp != NULL && value().length() > 0) + { + QString regexp = QString().fromUtf8(_argument->regexp); + if (regexp.length() > 0) + { + QRegularExpression expr(regexp, QRegularExpression::UseUnicodePropertiesOption); + if (! expr.isValid() || ! expr.match(value()).hasMatch()) + valid = false; + } + } + + QString lblInvalidColor = ColorUtils::fromColorT(prefs.gui_text_invalid).name(); + QString txtStyle("QLineEdit { background-color: %1; } "); + textBox->setStyleSheet(txtStyle.arg(valid ? QString("") : lblInvalidColor)); + + return valid; +} + +bool ExtArgText::isSetDefaultValueSupported() +{ + return TRUE; +} + +void ExtArgText::setDefaultValue() +{ + textBox->setText(defaultValue()); +} + + + +ExtArgNumber::ExtArgNumber(extcap_arg * argument, QObject * parent) : + ExtArgText(argument, parent) {} + +QWidget * ExtArgNumber::createEditor(QWidget * parent) +{ + QString text = defaultValue(); + + if (_argument->pref_valptr && strlen(*_argument->pref_valptr)) + { + QString storeValue(*_argument->pref_valptr); + text = storeValue; + } + + textBox = (QLineEdit *)ExtArgText::createEditor(parent); + textBox->disconnect(SIGNAL(textChanged(QString))); + + if (_argument->arg_type == EXTCAP_ARG_INTEGER || _argument->arg_type == EXTCAP_ARG_UNSIGNED) + { + QIntValidator * textValidator = new QIntValidator(parent); + if (_argument->range_start != NULL) + { + int val = 0; + if (_argument->arg_type == EXTCAP_ARG_INTEGER) + val = extcap_complex_get_int(_argument->range_start); + else if (_argument->arg_type == EXTCAP_ARG_UNSIGNED) + { + guint tmp = extcap_complex_get_uint(_argument->range_start); + if (tmp > G_MAXINT) + { + ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Defined value for range_start of %s exceeds valid integer range", _argument->call); + val = G_MAXINT; + } + else + val = (gint)tmp; + } + + textValidator->setBottom(val); + } + if (_argument->arg_type == EXTCAP_ARG_UNSIGNED && textValidator->bottom() < 0) + { + ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "%s sets negative bottom range for unsigned value, setting to 0", _argument->call); + textValidator->setBottom(0); + } + + if (_argument->range_end != NULL) + { + int val = 0; + if (_argument->arg_type == EXTCAP_ARG_INTEGER) + val = extcap_complex_get_int(_argument->range_end); + else if (_argument->arg_type == EXTCAP_ARG_UNSIGNED) + { + guint tmp = extcap_complex_get_uint(_argument->range_end); + if (tmp > G_MAXINT) + { + ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Defined value for range_end of %s exceeds valid integer range", _argument->call); + val = G_MAXINT; + } + else + val = (gint)tmp; + } + + textValidator->setTop(val); + } + textBox->setValidator(textValidator); + } + else if (_argument->arg_type == EXTCAP_ARG_DOUBLE) + { + QDoubleValidator * textValidator = new QDoubleValidator(parent); + if (_argument->range_start != NULL) + textValidator->setBottom(extcap_complex_get_double(_argument->range_start)); + if (_argument->range_end != NULL) + textValidator->setTop(extcap_complex_get_double(_argument->range_end)); + + textBox->setValidator(textValidator); + } + + textBox->setText(text.trimmed()); + + connect(textBox, SIGNAL(textChanged(QString)), SLOT(onStringChanged(QString))); + + return textBox; +} + +QString ExtArgNumber::defaultValue() +{ + QString result; + + if (_argument != 0) + { + if (_argument->arg_type == EXTCAP_ARG_DOUBLE) + result = QString::number(extcap_complex_get_double(_argument->default_complex)); + else if (_argument->arg_type == EXTCAP_ARG_INTEGER) + result = QString::number(extcap_complex_get_int(_argument->default_complex)); + else if (_argument->arg_type == EXTCAP_ARG_UNSIGNED) + result = QString::number(extcap_complex_get_uint(_argument->default_complex)); + else if (_argument->arg_type == EXTCAP_ARG_LONG) + result = QString::number(extcap_complex_get_long(_argument->default_complex)); + else + { + QString defValue = ExtcapArgument::defaultValue(); + result = defValue.length() > 0 ? defValue : QString(); + } + } + + return result; +} + +ExtcapValue::~ExtcapValue() {} + +void ExtcapValue::setChildren(ExtcapValueList children) +{ + ExtcapValueList::iterator iter = children.begin(); + while (iter != children.end()) + { + (*iter)._depth = _depth + 1; + ++iter; + } + + _children.append(children); +} + +ExtcapArgument::ExtcapArgument(QObject *parent) : + QObject(parent), _argument(0), _label(0), _number(0), + label_style(QString("QLabel { color: %1; }")) +{ +} + +ExtcapArgument::ExtcapArgument(extcap_arg * argument, QObject *parent) : + QObject(parent), _argument(argument), _label(0), + label_style(QString("QLabel { color: %1; }")) +{ + _number = argument->arg_num; + + if (_argument->values != 0) + { + ExtcapValueList elements = loadValues(QString("")); + if (elements.length() > 0) + values.append(elements); + } +} + +ExtcapArgument::ExtcapArgument(const ExtcapArgument &obj) : + QObject(obj.parent()), _argument(obj._argument), _label(0), + label_style(QString("QLabel { color: %1; }")) +{ + _number = obj._argument->arg_num; + + if (_argument->values != 0) + { + ExtcapValueList elements = loadValues(QString("")); + if (elements.length() > 0) + values.append(elements); + } +} + +ExtcapValueList ExtcapArgument::loadValues(QString parent) +{ + if (_argument == 0 || _argument->values == 0) + return ExtcapValueList(); + + GList * walker = 0; + extcap_value * v; + ExtcapValueList elements; + + for (walker = g_list_first((GList *)(_argument->values)); walker != NULL ; walker = walker->next) + { + v = (extcap_value *) walker->data; + if (v == NULL || v->display == NULL || v->call == NULL) + break; + + QString valParent = QString().fromUtf8(v->parent); + + if (parent.compare(valParent) == 0) + { + + QString display = QString().fromUtf8(v->display); + QString call = QString().fromUtf8(v->call); + + ExtcapValue element = ExtcapValue(display, call, + v->enabled == (gboolean)TRUE, v->is_default == (gboolean)TRUE); + + if (!call.isEmpty()) + element.setChildren(this->loadValues(call)); + + elements.append(element); + } + } + + return elements; +} + +bool ExtcapArgument::reloadValues() +{ + if (! qobject_cast (parent()) ) + return false; + + ExtcapOptionsDialog * dialog = qobject_cast(parent()); + ExtcapValueList list = dialog->loadValuesFor(_argument->arg_num, _argument->call); + + if (list.size() > 0) + { + values.clear(); + values << list; + + return true; + } + + return false; +} + +ExtcapArgument::~ExtcapArgument() { +} + +QWidget * ExtcapArgument::createLabel(QWidget * parent) +{ + if (_argument == 0 || _argument->display == 0) + return 0; + + QString lblInvalidColor = ColorUtils::fromColorT(prefs.gui_text_invalid).name(); + + QString text = QString().fromUtf8(_argument->display); + + if (_label == 0) + _label = new QLabel(text, parent); + else + _label->setText(text); + + _label->setProperty("isRequired", QString(isRequired() ? "true" : "false")); + + _label->setStyleSheet (label_style.arg(QString(""))); + + if (_argument->tooltip != 0) + _label->setToolTip(QString().fromUtf8(_argument->tooltip)); + + return (QWidget *)_label; +} + +QWidget * ExtcapArgument::createEditor(QWidget *) +{ + return 0; +} + +QString ExtcapArgument::call() +{ + return QString(_argument->call); +} + +QString ExtcapArgument::value() +{ + return QString(); +} + +QString ExtcapArgument::prefValue() +{ + return value(); +} + +void ExtcapArgument::resetValue() +{ + if (_argument->pref_valptr) { + g_free(*_argument->pref_valptr); + *_argument->pref_valptr = g_strdup(""); + } +} + +bool ExtcapArgument::isValid() +{ + /* Unrequired arguments are always valid, except if validity checks fail, + * which must be checked in an derived class, not here */ + if (! isRequired()) + return true; + + return value().length() > 0; +} + +QString ExtcapArgument::defaultValue() +{ + if (_argument != 0 && _argument->default_complex != 0) + { + gchar * str = extcap_get_complex_as_string(_argument->default_complex); + if (str != 0) + return QString(str); + } + return QString(); +} + +QString ExtcapArgument::group() const +{ + if (_argument != 0 && _argument->group != 0) + return QString(_argument->group); + + return QString(); +} + +int ExtcapArgument::argNr() const +{ + return _number; +} + +QString ExtcapArgument::prefKey(const QString & device_name) +{ + struct preference * pref = NULL; + + if (_argument == 0 || ! _argument->save) + return QString(); + + pref = extcap_pref_for_argument(device_name.toStdString().c_str(), _argument); + if (pref != NULL) + return QString(prefs_get_name(pref)); + + return QString(); +} + +bool ExtcapArgument::isRequired() +{ + if (_argument != NULL) + return _argument->is_required; + + return FALSE; +} + +bool ExtcapArgument::reload() +{ + if (_argument != NULL) + return _argument->reload; + + return false; +} + +bool ExtcapArgument::fileExists() +{ + if (_argument != NULL) + return _argument->fileexists; + + return FALSE; +} + +bool ExtcapArgument::isDefault() +{ + if (value().compare(defaultValue()) == 0) + return true; + + return false; +} + +ExtcapArgument * ExtcapArgument::create(extcap_arg * argument, QObject *parent) +{ + if (argument == 0 || argument->display == 0) + return 0; + + ExtcapArgument * result = 0; + + if (argument->arg_type == EXTCAP_ARG_STRING || argument->arg_type == EXTCAP_ARG_PASSWORD) + result = new ExtArgText(argument, parent); + else if (argument->arg_type == EXTCAP_ARG_INTEGER || argument->arg_type == EXTCAP_ARG_LONG || + argument->arg_type == EXTCAP_ARG_UNSIGNED || argument->arg_type == EXTCAP_ARG_DOUBLE) + result = new ExtArgNumber(argument, parent); + else if (argument->arg_type == EXTCAP_ARG_BOOLEAN || argument->arg_type == EXTCAP_ARG_BOOLFLAG) + result = new ExtArgBool(argument, parent); + else if (argument->arg_type == EXTCAP_ARG_SELECTOR) + result = new ExtArgSelector(argument, parent); + else if (argument->arg_type == EXTCAP_ARG_EDIT_SELECTOR) + result = new ExtArgEditSelector(argument, parent); + else if (argument->arg_type == EXTCAP_ARG_RADIO) + result = new ExtArgRadio(argument, parent); + else if (argument->arg_type == EXTCAP_ARG_FILESELECT) + result = new ExtcapArgumentFileSelection(argument, parent); + else if (argument->arg_type == EXTCAP_ARG_MULTICHECK) + result = new ExtArgMultiSelect(argument, parent); + else if (argument->arg_type == EXTCAP_ARG_TIMESTAMP) + result = new ExtArgTimestamp(argument, parent); + else + { + /* For everything else, we just print the label */ + result = new ExtcapArgument(argument, parent); + } + + return result; +} + +/* The following is a necessity, because Q_Object does not do well with multiple inheritances */ +void ExtcapArgument::onStringChanged(QString) +{ + emit valueChanged(); +} + +void ExtcapArgument::onIntChanged(int) +{ + if (isValid()) + emit valueChanged(); +} + +void ExtcapArgument::onBoolChanged(bool) +{ + emit valueChanged(); +} + +bool ExtcapArgument::isSetDefaultValueSupported() +{ + return FALSE; +} + +void ExtcapArgument::setDefaultValue() +{ +} + diff --git a/ui/qt/extcap_argument.h b/ui/qt/extcap_argument.h new file mode 100644 index 00000000..2efa4e42 --- /dev/null +++ b/ui/qt/extcap_argument.h @@ -0,0 +1,285 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_EXTCAP_ARGUMENT_H_ +#define UI_QT_EXTCAP_ARGUMENT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define EXTCAP_GUI_BLANK_LABEL "QLabel { color : ; }" +#define EXTCAP_GUI_ERROR_LABEL "QLabel { color : red; }" + +class ExtcapValue; + +typedef QList ExtcapValueList; + +class ExtcapValue +{ +public: + ExtcapValue(QString value, QString call, bool enabled, bool isDefault) : + _value(value), _call(call), _enabled(enabled), + _isDefault(isDefault), _depth(0) {} + virtual ~ExtcapValue(); + + void setChildren(ExtcapValueList children); + ExtcapValueList children() + { + if (_children.length() == 0) + return ExtcapValueList(); + return _children; + } + + QString value() const { return _value; } + const QString call() const { return _call; } + bool enabled() const { return _enabled; } + bool isDefault() const { return _isDefault; } + + int depth() { return _depth; } + +private: + QString _value; + QString _call; + + bool _enabled; + bool _isDefault; + + int _depth; + + ExtcapValueList _children; +}; + + + +class ExtcapArgument: public QObject +{ + Q_OBJECT + +public: + ExtcapArgument(QObject *parent = Q_NULLPTR); + ExtcapArgument(extcap_arg * argument, QObject *parent = Q_NULLPTR); + ExtcapArgument(const ExtcapArgument &obj); + virtual ~ExtcapArgument(); + + virtual QWidget * createLabel(QWidget * parent = 0); + virtual QWidget * createEditor(QWidget * parent = 0); + + virtual extcap_arg * argument() { return _argument; } + virtual QString call(); + virtual QString value(); + virtual QString defaultValue(); + + bool isDefault(); + virtual bool isValid(); + bool isRequired(); + bool reload(); + + QString prefKey(const QString & device_name); + virtual QString prefValue(); + + void resetValue(); + + virtual QString group() const; + virtual int argNr() const; + + static ExtcapArgument * create(extcap_arg * argument = Q_NULLPTR, QObject * parent = Q_NULLPTR); + virtual bool isSetDefaultValueSupported(); + +public Q_SLOTS: + virtual void setDefaultValue(); + +Q_SIGNALS: + void valueChanged(); + +protected: + + bool fileExists(); + + ExtcapValueList loadValues(QString parent); + bool reloadValues(); + + ExtcapValueList values; + + extcap_arg * _argument; + QLabel * _label; + int _number; + + const QString label_style; + +private Q_SLOTS: + + void onStringChanged(QString); + void onIntChanged(int); + void onBoolChanged(bool); + +}; + + + +class ExtArgText : public ExtcapArgument +{ + Q_OBJECT + +public: + ExtArgText(extcap_arg * argument, QObject *parent = Q_NULLPTR); + + virtual QWidget * createEditor(QWidget * parent); + virtual QString value(); + virtual bool isValid(); + virtual bool isSetDefaultValueSupported(); + +public Q_SLOTS: + virtual void setDefaultValue(); + +protected: + + QLineEdit * textBox; +}; + + + +class ExtArgNumber : public ExtArgText +{ + Q_OBJECT + +public: + ExtArgNumber(extcap_arg * argument, QObject *parent = Q_NULLPTR); + + virtual QWidget * createEditor(QWidget * parent); + virtual QString defaultValue(); +}; + + + +class ExtArgSelector : public ExtcapArgument +{ + Q_OBJECT + +public: + ExtArgSelector(extcap_arg * argument, QObject *parent = Q_NULLPTR); + + virtual QWidget * createEditor(QWidget * parent); + virtual QString value(); + virtual bool isValid(); + virtual bool isSetDefaultValueSupported(); + +public Q_SLOTS: + virtual void setDefaultValue(); + +protected: + QComboBox * boxSelection; + +private Q_SLOTS: + void onReloadTriggered(); + +}; + + +class ExtArgEditSelector : public ExtArgSelector +{ + Q_OBJECT + +public: + ExtArgEditSelector(extcap_arg * argument, QObject *parent = Q_NULLPTR); + virtual QWidget * createEditor(QWidget * parent); + virtual QString value(); + +public Q_SLOTS: + virtual void setDefaultValue(); +}; + + +class ExtArgRadio : public ExtcapArgument +{ + Q_OBJECT + +public: + ExtArgRadio(extcap_arg * argument, QObject *parent = Q_NULLPTR); + + virtual QWidget * createEditor(QWidget * parent); + virtual QString value(); + virtual bool isValid(); + virtual bool isSetDefaultValueSupported(); + +public Q_SLOTS: + virtual void setDefaultValue(); + +private: + + QButtonGroup * selectorGroup; + QList * callStrings; +}; + + + +class ExtArgBool : public ExtcapArgument +{ + Q_OBJECT + +public: + ExtArgBool(extcap_arg * argument, QObject *parent = Q_NULLPTR); + + virtual QWidget * createLabel(QWidget * parent); + virtual QWidget * createEditor(QWidget * parent); + + virtual QString call(); + virtual QString value(); + virtual bool isValid(); + virtual QString defaultValue(); + virtual QString prefValue(); + virtual bool isSetDefaultValueSupported(); + +public Q_SLOTS: + virtual void setDefaultValue(); + +private: + + QCheckBox * boolBox; + + bool defaultBool(); +}; + + + +class ExtArgTimestamp : public ExtcapArgument +{ + Q_OBJECT + +public: + ExtArgTimestamp(extcap_arg * argument, QObject *parent = Q_NULLPTR); + virtual QWidget * createEditor(QWidget * parent); + + virtual bool isValid(); + virtual QString defaultValue(); + virtual QString value(); + virtual QString prefValue(); + virtual bool isSetDefaultValueSupported(); + +public Q_SLOTS: + virtual void setDefaultValue(); + +private Q_SLOTS: + void onDateTimeChanged(QDateTime); + +private: + QDateTime ts; + QDateTimeEdit *tsBox; +}; + +#endif /* UI_QT_EXTCAP_ARGUMENT_H_ */ diff --git a/ui/qt/extcap_argument_file.cpp b/ui/qt/extcap_argument_file.cpp new file mode 100644 index 00000000..bac7a5b9 --- /dev/null +++ b/ui/qt/extcap_argument_file.cpp @@ -0,0 +1,170 @@ +/* extcap_argument_file.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include + +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +ExtcapArgumentFileSelection::ExtcapArgumentFileSelection (extcap_arg * argument, QObject *parent) : + ExtcapArgument(argument, parent), textBox(0) +{ +} + +ExtcapArgumentFileSelection::~ExtcapArgumentFileSelection() +{ + if (textBox != NULL) + delete textBox; +} + +QWidget * ExtcapArgumentFileSelection::createEditor(QWidget * parent) +{ + QString text = defaultValue(); + QString buttonText(UTF8_HORIZONTAL_ELLIPSIS); + QString buttonClearText(tr("Clear")); + + QWidget * fileWidget = new QWidget(parent); + QHBoxLayout * editLayout = new QHBoxLayout(); + QMargins margins = editLayout->contentsMargins(); + editLayout->setContentsMargins(0, 0, 0, margins.bottom()); + fileWidget->setContentsMargins(margins.left(), margins.right(), 0, margins.bottom()); + QPushButton * buttonSelect = new QPushButton(buttonText, fileWidget); + QPushButton * buttonClear = new QPushButton(buttonClearText, fileWidget); + + textBox = new QLineEdit(text, parent); + textBox->setReadOnly(true); + + /* Value is empty if no file is selected */ + const char *prefval = (_argument->pref_valptr && (*_argument->pref_valptr)) ? *_argument->pref_valptr : NULL; + if (prefval) + { + QString storeValue(prefval); + + if (storeValue.length() > 0 && storeValue.compare(text) != 0) + text = storeValue.trimmed(); + } + textBox->setText(text); + + if (_argument->tooltip != NULL) + { + textBox->setToolTip(QString().fromUtf8(_argument->tooltip)); + buttonSelect->setToolTip(QString().fromUtf8(_argument->tooltip)); + } + + connect(buttonSelect, &QPushButton::clicked, this, &ExtcapArgumentFileSelection::openFileDialog); + connect(buttonClear, &QPushButton::clicked, this, &ExtcapArgumentFileSelection::clearFilename); + + editLayout->addWidget(textBox); + editLayout->addWidget(buttonSelect); + editLayout->addWidget(buttonClear); + + fileWidget->setLayout(editLayout); + + return fileWidget; +} + +QString ExtcapArgumentFileSelection::value() +{ + if (textBox == 0) + return QString(); + return textBox->text(); +} + +/* opens the file dialog */ +void ExtcapArgumentFileSelection::openFileDialog() +{ + QString filename = textBox->text(); + + QDir workingDir = QDir::currentPath(); + if (QFileInfo(filename).exists()) + workingDir = QFileInfo(filename).dir(); + + QString fileExt(tr("All Files (" ALL_FILES_WILDCARD ")")); + if (_argument->fileextension != NULL) + { + QString givenExt = QString().fromUtf8(_argument->fileextension); + if (givenExt.length() != 0) + fileExt.prepend(";;").prepend(givenExt); + } + + if (fileExists()) + { + /* UI should check that the file exists */ + filename = WiresharkFileDialog::getOpenFileName((QWidget*)(textBox->parent()), + QString().fromUtf8(_argument->display) + " " + tr("Open File"), + workingDir.absolutePath(), fileExt); + } + else + { + /* File might or might not exist. Actual overwrite handling is extcap specific + * (e.g. boolflag argument if user wants to always overwrite the file) + */ + filename = WiresharkFileDialog::getSaveFileName((QWidget*)(textBox->parent()), + QString().fromUtf8(_argument->display) + " " + tr("Select File"), + workingDir.absolutePath(), fileExt, nullptr, QFileDialog::Option::DontConfirmOverwrite); + } + + if (! filename.isEmpty() && (! fileExists() || QFileInfo(filename).exists())) + { + textBox->setText(filename); + emit valueChanged(); + } +} + +void ExtcapArgumentFileSelection::clearFilename() +{ + textBox->clear(); + emit valueChanged(); +} + +bool ExtcapArgumentFileSelection::isValid() +{ + bool valid = false; + + if (textBox->text().length() > 0) + { + if (_argument->fileexists) + valid = QFileInfo(textBox->text()).exists(); + else + valid = true; + } + else if (! isRequired()) + valid = true; + + QString lblInvalidColor = ColorUtils::fromColorT(prefs.gui_text_invalid).name(); + QString txtStyle("QLineEdit { background-color: %1; } "); + textBox->setStyleSheet(txtStyle.arg(valid ? QString("") : lblInvalidColor)); + + return valid; +} + +void ExtcapArgumentFileSelection::setDefaultValue() +{ + clearFilename(); +} + diff --git a/ui/qt/extcap_argument_file.h b/ui/qt/extcap_argument_file.h new file mode 100644 index 00000000..9dac908d --- /dev/null +++ b/ui/qt/extcap_argument_file.h @@ -0,0 +1,46 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_EXTCAP_ARGUMENT_FILE_H_ +#define UI_QT_EXTCAP_ARGUMENT_FILE_H_ + +#include +#include +#include + +#include +#include + +class ExtcapArgumentFileSelection : public ExtcapArgument +{ + Q_OBJECT + +public: + ExtcapArgumentFileSelection(extcap_arg * argument, QObject * parent = Q_NULLPTR); + virtual ~ExtcapArgumentFileSelection(); + + virtual QWidget * createEditor(QWidget * parent); + + virtual QString value(); + + virtual bool isValid(); + + virtual void setDefaultValue(); + +protected: + QLineEdit * textBox; + +private slots: + /* opens the file dialog */ + void openFileDialog(); + /* clears previously entered filename */ + void clearFilename(); +}; + +#endif /* UI_QT_EXTCAP_ARGUMENT_FILE_H_ */ diff --git a/ui/qt/extcap_argument_multiselect.cpp b/ui/qt/extcap_argument_multiselect.cpp new file mode 100644 index 00000000..6f816b98 --- /dev/null +++ b/ui/qt/extcap_argument_multiselect.cpp @@ -0,0 +1,230 @@ +/* extcap_argument_multiselect.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +ExtArgMultiSelect::ExtArgMultiSelect(extcap_arg * argument, QObject *parent) : + ExtcapArgument(argument, parent), treeView(0), viewModel(0) {} + +ExtArgMultiSelect::~ExtArgMultiSelect() +{ + if (treeView != 0) + delete treeView; + if (viewModel != 0) + delete viewModel; +} + +QList ExtArgMultiSelect::valueWalker(ExtcapValueList list, QStringList &defaults) +{ + ExtcapValueList::iterator iter = list.begin(); + QList items; + + while (iter != list.end()) + { + QStandardItem * item = new QStandardItem((*iter).value()); + if ((*iter).enabled() == false) + { + item->setCheckable(false); + } + else + { + item->setCheckable(true); + } + + item->setData((*iter).call(), Qt::UserRole); + if ((*iter).isDefault()) + defaults << (*iter).call(); + + item->setSelectable(false); + item->setEditable(false); + QList childs = valueWalker((*iter).children(), defaults); + if (childs.length() > 0) + item->appendRows(childs); + + items << item; + ++iter; + } + + return items; +} + +void ExtArgMultiSelect::checkItemsWalker(QStandardItem * item, QStringList defaults) +{ + QModelIndexList results; + QModelIndex index; + + if (item->hasChildren()) + { + for (int row = 0; row < item->rowCount(); row++) + { + QStandardItem * child = item->child(row); + if (child != 0) + { + checkItemsWalker(child, defaults); + } + } + } + + QString data = item->data(Qt::UserRole).toString(); + + if (defaults.contains(data)) + { + item->setCheckState(Qt::Checked); + index = item->index(); + while (index.isValid()) + { + treeView->setExpanded(index, true); + index = index.parent(); + } + } else if (item->isCheckable()) { + item->setCheckState(Qt::Unchecked); + } +} + +QWidget * ExtArgMultiSelect::createEditor(QWidget * parent) +{ + QStringList checked; + + QList items = valueWalker(values, checked); + if (items.length() == 0) + return new QWidget(); + + /* Value can be empty if no items are checked */ + if (_argument->pref_valptr && (*_argument->pref_valptr)) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + checked = QString(*_argument->pref_valptr).split(",", Qt::SkipEmptyParts); +#else + checked = QString(*_argument->pref_valptr).split(",", QString::SkipEmptyParts); +#endif + } + + viewModel = new QStandardItemModel(); + QList::const_iterator iter = items.constBegin(); + while (iter != items.constEnd()) + { + ((QStandardItemModel *)viewModel)->appendRow((*iter)); + ++iter; + } + + treeView = new QTreeView(parent); + treeView->setModel(viewModel); + + /* Shows at minimum 6 entries at most desktops */ + treeView->setMinimumHeight(100); + treeView->setHeaderHidden(true); + treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); + treeView->setEditTriggers(QAbstractItemView::NoEditTriggers); + + for (int row = 0; row < viewModel->rowCount(); row++) + checkItemsWalker(((QStandardItemModel*)viewModel)->item(row), checked); + + connect (viewModel, + SIGNAL(itemChanged(QStandardItem *)), + SLOT(itemChanged(QStandardItem *))); + + return treeView; +} + +QString ExtArgMultiSelect::value() +{ + if (viewModel == 0) + return QString(); + + QStringList result; + QModelIndexList checked = viewModel->match(viewModel->index(0, 0), Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly | Qt::MatchRecursive); + if (checked.size() <= 0) + return QString(); + + QModelIndexList::const_iterator iter = checked.constBegin(); + while (iter != checked.constEnd()) + { + QModelIndex index = (QModelIndex)(*iter); + + result << viewModel->data(index, Qt::UserRole).toString(); + + ++iter; + } + + return result.join(QString(',')); +} + +void ExtArgMultiSelect::itemChanged(QStandardItem *) +{ + emit valueChanged(); +} + +bool ExtArgMultiSelect::isValid() +{ + bool valid = true; + + if (isRequired()) + { + if (viewModel == 0) + valid = false; + else + { + QModelIndexList checked = viewModel->match(viewModel->index(0, 0), Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly | Qt::MatchRecursive); + if (checked.size() <= 0) + valid = false; + } + } + + QString lblInvalidColor = ColorUtils::fromColorT(prefs.gui_text_invalid).name(); + QString txtStyle("QTreeView { background-color: %1; } "); + if (viewModel != 0) + treeView->setStyleSheet(txtStyle.arg(valid ? QString("") : lblInvalidColor)); + + return valid; +} + +QString ExtArgMultiSelect::defaultValue() +{ + QStringList checked; + + QList items = valueWalker(values, checked); + + return checked.join(QString(',')); +} + +bool ExtArgMultiSelect::isSetDefaultValueSupported() +{ + return TRUE; +} + +void ExtArgMultiSelect::setDefaultValue() +{ + QStringList checked; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + checked = defaultValue().split(",", Qt::SkipEmptyParts); +#else + checked = defaultValue().split(",", QString::SkipEmptyParts); +#endif + for (int row = 0; row < viewModel->rowCount(); row++) + checkItemsWalker(((QStandardItemModel*)viewModel)->item(row), checked); +} + diff --git a/ui/qt/extcap_argument_multiselect.h b/ui/qt/extcap_argument_multiselect.h new file mode 100644 index 00000000..b64573df --- /dev/null +++ b/ui/qt/extcap_argument_multiselect.h @@ -0,0 +1,54 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_EXTCAP_ARGUMENT_MULTISELECT_H_ +#define UI_QT_EXTCAP_ARGUMENT_MULTISELECT_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +class ExtArgMultiSelect : public ExtcapArgument +{ + Q_OBJECT +public: + ExtArgMultiSelect(extcap_arg * argument, QObject *parent = Q_NULLPTR); + virtual ~ExtArgMultiSelect(); + + virtual QString value(); + virtual bool isValid(); + virtual QString defaultValue(); + virtual bool isSetDefaultValueSupported(); + +public Q_SLOTS: + virtual void setDefaultValue(); + +protected: + virtual QList valueWalker(ExtcapValueList list, QStringList &defaults); + void checkItemsWalker(QStandardItem * item, QStringList defaults); + virtual QWidget * createEditor(QWidget * parent); + +private Q_SLOTS: + + void itemChanged(QStandardItem *); + +private: + + QTreeView * treeView; + QAbstractItemModel * viewModel; + +}; + +#endif /* UI_QT_EXTCAP_ARGUMENT_MULTISELECT_H_ */ diff --git a/ui/qt/extcap_options_dialog.cpp b/ui/qt/extcap_options_dialog.cpp new file mode 100644 index 00000000..cbac4db7 --- /dev/null +++ b/ui/qt/extcap_options_dialog.cpp @@ -0,0 +1,701 @@ +/* extcap_options_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ringbuffer.h" +#include "ui/capture_ui_utils.h" +#include "ui/capture_globals.h" +#include "ui/iface_lists.h" + +#include "ui/ws_ui_util.h" +#include "ui/util.h" +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +ExtcapOptionsDialog::ExtcapOptionsDialog(bool startCaptureOnClose, QWidget *parent) : + QDialog(parent), + ui(new Ui::ExtcapOptionsDialog), + device_name(""), + device_idx(0), + defaultValueIcon_(StockIcon("x-reset")) +{ + ui->setupUi(this); + + setWindowTitle(mainApp->windowTitleString(tr("Interface Options"))); + + ui->checkSaveOnStart->setCheckState(prefs.extcap_save_on_start ? Qt::Checked : Qt::Unchecked); + + if (startCaptureOnClose) { + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start")); + } else { + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Save")); + } +} + +ExtcapOptionsDialog * ExtcapOptionsDialog::createForDevice(QString &dev_name, bool startCaptureOnClose, QWidget *parent) +{ + interface_t *device; + ExtcapOptionsDialog * resultDialog = NULL; + bool dev_found = false; + guint if_idx; + + if (dev_name.length() == 0) + return NULL; + + for (if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++) + { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx); + if (dev_name.compare(QString(device->name)) == 0 && device->if_info.type == IF_EXTCAP) + { + dev_found = true; + break; + } + } + + if (! dev_found) + return NULL; + + resultDialog = new ExtcapOptionsDialog(startCaptureOnClose, parent); + resultDialog->device_name = QString(dev_name); + resultDialog->device_idx = if_idx; + + resultDialog->setWindowTitle(mainApp->windowTitleString(tr("Interface Options") + ": " + device->display_name)); + + resultDialog->updateWidgets(); + + /* mark required fields */ + resultDialog->anyValueChanged(); + + return resultDialog; +} + + +ExtcapOptionsDialog::~ExtcapOptionsDialog() +{ + delete ui; +} + +void ExtcapOptionsDialog::on_buttonBox_accepted() +{ + if (saveOptionToCaptureInfo()) { + /* Starting a new capture with those values */ + prefs.extcap_save_on_start = ui->checkSaveOnStart->checkState() == Qt::Checked; + + if (prefs.extcap_save_on_start) + storeValues(); + + accept(); + } +} + +void ExtcapOptionsDialog::anyValueChanged() +{ + bool allowStart = true; + + ExtcapArgumentList::const_iterator iter; + + /* All arguments are being iterated, to ensure, that any error handling catches all arguments */ + for (iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter) + { + /* The dynamic casts are necessary, because we come here using the Signal/Slot system + * of Qt, and -in short- Q_OBJECT classes cannot be multiple inherited. Another possibility + * would be to use Q_INTERFACE, but this causes way more nightmares, and we really just + * need here an explicit cast for the check functionality */ + if (dynamic_cast((*iter)) != NULL) + { + if (! ((ExtArgBool *)*iter)->isValid()) + allowStart = false; + } + else if (dynamic_cast((*iter)) != NULL) + { + if (! ((ExtArgRadio *)*iter)->isValid()) + allowStart = false; + } + else if (dynamic_cast((*iter)) != NULL) + { + if (! ((ExtArgSelector *)*iter)->isValid()) + allowStart = false; + } + else if (dynamic_cast((*iter)) != NULL) + { + if (! ((ExtArgMultiSelect *)*iter)->isValid()) + allowStart = false; + } + else if (dynamic_cast((*iter)) != NULL) + { + if (! ((ExtcapArgumentFileSelection *)*iter)->isValid()) + allowStart = false; + } + else if (dynamic_cast((*iter)) != NULL) + { + if (! ((ExtArgNumber *)*iter)->isValid()) + allowStart = false; + } + else if (dynamic_cast((*iter)) != NULL) + { + if (! ((ExtArgText *)*iter)->isValid()) + allowStart = false; + } + else if (dynamic_cast((*iter)) != NULL) + { + if (! ((ExtArgTimestamp *)*iter)->isValid()) + allowStart = false; + } + else + if (! (*iter)->isValid()) + allowStart = false; + } + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowStart); +} + +void ExtcapOptionsDialog::loadArguments() +{ + GList * arguments = Q_NULLPTR, * walker = Q_NULLPTR, * item = Q_NULLPTR; + ExtcapArgument * argument = Q_NULLPTR; + + if (device_name.length() == 0 ) + return; + + extcapArguments.clear(); + + arguments = g_list_first(extcap_get_if_configuration(device_name.toUtf8().constData())); + + ExtcapArgumentList required; + ExtcapArgumentList optional; + + walker = arguments; + while (walker != Q_NULLPTR) + { + item = g_list_first(gxx_list_data(GList *, walker)); + while (item != Q_NULLPTR) + { + argument = ExtcapArgument::create(gxx_list_data(extcap_arg *, item), this); + if (argument != Q_NULLPTR) + { + if (argument->isRequired()) + required << argument; + else + optional << argument; + + } + item = item->next; + } + walker = gxx_list_next(walker); + } + + if (required.length() > 0) + extcapArguments << required; + + if (optional.length() > 0) + extcapArguments << optional; + + /* argument items are now owned by ExtcapArgument. Only free the lists */ + extcap_free_if_configuration(arguments, FALSE); +} + +void ExtcapOptionsDialog::updateWidgets() +{ + QWidget * lblWidget = NULL, *editWidget = NULL; + ExtcapArgument * argument = NULL; + bool allowStart = true; + + unsigned int counter = 0; + + if (device_name.length() == 0 ) + return; + + /* find existing layout */ + if (ui->verticalLayout->children().count() > 0) + { + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + QWidget * item = ui->verticalLayout->itemAt(0)->widget(); + if (item) + { + ui->verticalLayout->removeItem(ui->verticalLayout->itemAt(0)); + delete item; + } + } + + QHash layouts; + + /* Load all extcap arguments */ + loadArguments(); + + /* exit if no arguments have been found. This is a precaution, it should + * never happen, that this dialog get's called without any arguments */ + if (extcapArguments.count() == 0) + { + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + return; + } + ui->checkSaveOnStart->setText(tr("Save parameter(s) on capture start", "", static_cast(extcapArguments.count()))); + + QStringList groupKeys; + QString defaultKeyName(tr("Default")); + /* QMap sorts keys, therefore the groups are sorted by appearance */ + QMap groups; + + /* Look for all necessary tabs */ + ExtcapArgumentList::iterator iter = extcapArguments.begin(); + while (iter != extcapArguments.end()) + { + argument = (ExtcapArgument *)(*iter); + QString groupKey = argument->group(); + if (groupKey.length() > 0) + { + if (! groups.values().contains(groupKey)) + groups.insert(argument->argNr(), groupKey); + } + else if (! groups.keys().contains(0)) + { + groups.insert(0, defaultKeyName); + groupKey = defaultKeyName; + } + + if (! layouts.keys().contains(groupKey)) + { + QWidget * tabWidget = new QWidget(this); + QGridLayout * tabLayout = new QGridLayout(tabWidget); + tabWidget->setLayout(tabLayout); + + layouts.insert(groupKey, tabWidget); + } + + ++iter; + } + groupKeys << groups.values(); + + /* Iterate over all arguments and do the following: + * 1. create the label for each element + * 2. create an editor for each element + * 3. add both to the layout for the tab widget + */ + iter = extcapArguments.begin(); + while (iter != extcapArguments.end()) + { + argument = (ExtcapArgument *)(*iter); + QString groupKey = defaultKeyName; + if (argument->group().length() > 0) + groupKey = argument->group(); + + /* Skip non-assigned group keys, this happens if the configuration of the extcap is faulty */ + if (! layouts.keys().contains(groupKey)) + { + ++iter; + continue; + } + + QGridLayout * layout = ((QGridLayout *)layouts[groupKey]->layout()); + lblWidget = argument->createLabel((QWidget *)this); + if (lblWidget != NULL) + { + layout->addWidget(lblWidget, counter, 0, Qt::AlignVCenter); + editWidget = argument->createEditor((QWidget *) this); + if (editWidget != NULL) + { + editWidget->setProperty("extcap", VariantPointer::asQVariant(argument)); + layout->addWidget(editWidget, counter, 1, Qt::AlignVCenter); + + if (argument->isSetDefaultValueSupported()) + { + QPushButton *button = new QPushButton(defaultValueIcon_,""); + button->setToolTip(tr("Restore default value of the item")); + layout->addWidget(button, counter, 2, Qt::AlignVCenter); + connect(button, SIGNAL(clicked()), argument, SLOT(setDefaultValue())); + } + } + + if (argument->isRequired() && ! argument->isValid()) + allowStart = false; + + connect(argument, &ExtcapArgument::valueChanged, this, &ExtcapOptionsDialog::anyValueChanged); + + counter++; + } + ++iter; + } + + if (counter > 0) + { + setStyleSheet ("QLabel[isRequired=\"true\"] { font-weight: bold; } "); + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowStart); + + QWidget * mainWidget = Q_NULLPTR; + + /* We should never display the dialog, if no settings are present */ + Q_ASSERT(layouts.count() > 0); + + if (layouts.count() > 1) + { + QTabWidget * tabs = new QTabWidget(this); + foreach (QString key, groupKeys) + { + layouts[key]->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding)); + tabs->addTab(layouts[key], key); + } + + tabs->setCurrentIndex(0); + mainWidget = tabs; + } + else if (layouts.count() == 1) + mainWidget = layouts[layouts.keys().at(0)]; + + ui->verticalLayout->addWidget(mainWidget); + ui->verticalLayout->addSpacerItem(new QSpacerItem(20, 100, QSizePolicy::Minimum, QSizePolicy::Expanding)); + } + else + { + QList keys = layouts.keys(); + foreach (QString key, keys) + delete(layouts[key]); + } +} + +// Not sure why we have to do this manually. +void ExtcapOptionsDialog::on_buttonBox_rejected() +{ + reject(); +} + +void ExtcapOptionsDialog::on_buttonBox_helpRequested() +{ + interface_t *device; + QString interface_help = NULL; + + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx); + interface_help = QString(extcap_get_help_for_ifname(device->name)); + /* The extcap interface didn't provide an help. Let's go with the default */ + if (interface_help.isEmpty()) { + mainApp->helpTopicAction(HELP_EXTCAP_OPTIONS_DIALOG); + return; + } + + QUrl help_url(interface_help); + + /* Check the existence for a local file */ + if (help_url.isLocalFile()) { + QString local_path = help_url.toLocalFile(); + QFileInfo help_file(local_path); + if (!help_file.exists()) { + QMessageBox::warning(this, tr("Extcap Help cannot be found"), + QString(tr("The help for the extcap interface %1 cannot be found. Given file: %2")) + .arg(device->name).arg(QDir::toNativeSeparators(local_path)), + QMessageBox::Ok); + return; + } + } + + /* We have an actual url or an existing local file. Let's open it. */ + QDesktopServices::openUrl(help_url); +} + +bool ExtcapOptionsDialog::saveOptionToCaptureInfo() +{ + GHashTable * ret_args; + interface_t *device; + + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx); + ret_args = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + ExtcapArgumentList::const_iterator iter; + + for (iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter) + { + QString call = (*iter)->call(); + QString value = (*iter)->value(); + QString prefValue = (*iter)->prefValue(); + + if ((*iter)->argument()->arg_type != EXTCAP_ARG_BOOLFLAG && value.length() == 0) + continue; + + if (call.length() <= 0) { + /* BOOLFLAG was cleared, make its value empty */ + if ((*iter)->argument()->arg_type == EXTCAP_ARG_BOOLFLAG) { + *(*iter)->argument()->pref_valptr[0] = 0; + } + continue; + } + + if (value.compare((*iter)->defaultValue()) == 0) { + extcap_arg *arg = (*iter)->argument(); + + // If previous value is not default, set it to default value + if (arg->default_complex != NULL && arg->default_complex->_val != NULL) { + g_free(*arg->pref_valptr); + *arg->pref_valptr = g_strdup(arg->default_complex->_val); + } else { + // Set empty value if there is no default value + *arg->pref_valptr[0] = 0; + } + continue; + } + + gchar * call_string = qstring_strdup(call); + gchar * value_string = NULL; + if (value.length() > 0) + value_string = qstring_strdup(value); + + g_hash_table_insert(ret_args, call_string, value_string); + + // For current value we need strdup even it is empty + value_string = qstring_strdup(prefValue); + // Update current value with new value + // We use prefValue because for bool/boolflag it returns value + // even it is false + g_free(*(*iter)->argument()->pref_valptr); + *(*iter)->argument()->pref_valptr = value_string; + } + + if (device->external_cap_args_settings != NULL) + g_hash_table_unref(device->external_cap_args_settings); + device->external_cap_args_settings = ret_args; + return true; +} + +void ExtcapOptionsDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + /* Only the save button has the ActionRole */ + if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole) + resetValues(); +} + +void ExtcapOptionsDialog::resetValues() +{ + int count = ui->verticalLayout->count(); + if (count > 0) + { + QList layouts; + + /* Find all layouts */ + if (qobject_cast(ui->verticalLayout->itemAt(0)->widget())) + { + QTabWidget * tabs = qobject_cast(ui->verticalLayout->itemAt(0)->widget()); + for (int cnt = 0; cnt < tabs->count(); cnt++) + { + layouts.append(tabs->widget(cnt)->layout()); + } + } + else + layouts.append(ui->verticalLayout->itemAt(0)->layout()); + + /* Loop over all layouts */ + for (int cnt = 0; cnt < layouts.count(); cnt++) + { + QGridLayout * layout = qobject_cast(layouts.at(cnt)); + if (! layout) + continue; + + /* Loop over all widgets in column 1 on layout */ + for (int row = 0; row < layout->rowCount(); row++) + { + QWidget * child = Q_NULLPTR; + if (layout->itemAtPosition(row, 1)) + child = qobject_cast(layout->itemAtPosition(row, 1)->widget()); + + if (child) + { + /* Don't need labels, the edit widget contains the extcapargument property value */ + ExtcapArgument * arg = 0; + QVariant prop = child->property("extcap"); + + if (prop.isValid()) + { + arg = VariantPointer::asPtr(prop); + + /* value<> can fail */ + if (arg) + { + arg->setDefaultValue(); + } + } + } + } + + } + + /* Values are stored when dialog is commited, just check validity*/ + anyValueChanged(); + } +} + +GHashTable *ExtcapOptionsDialog::getArgumentSettings(bool useCallsAsKey, bool includeEmptyValues) +{ + GHashTable * entries = g_hash_table_new(g_str_hash, g_str_equal); + ExtcapArgumentList::const_iterator iter; + + QString value; + + /* All arguments are being iterated, to ensure, that any error handling catches all arguments */ + for (iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter) + { + ExtcapArgument * argument = (ExtcapArgument *)(*iter); + bool isBoolflag = false; + + /* The dynamic casts are necessary, because we come here using the Signal/Slot system + * of Qt, and -in short- Q_OBJECT classes cannot be multiple inherited. Another possibility + * would be to use Q_INTERFACE, but this causes way more nightmares, and we really just + * need here an explicit cast for the check functionality */ + if (dynamic_cast((*iter)) != NULL) + { + value = ((ExtArgBool *)*iter)->prefValue(); + // For boolflag there should be no value + if ((*iter)->argument()->arg_type != EXTCAP_ARG_BOOLFLAG) + isBoolflag = true; + } + else if (dynamic_cast((*iter)) != NULL) + { + value = ((ExtArgRadio *)*iter)->prefValue(); + } + else if (dynamic_cast((*iter)) != NULL) + { + value = ((ExtArgSelector *)*iter)->prefValue(); + } + else if (dynamic_cast((*iter)) != NULL) + { + value = ((ExtArgMultiSelect *)*iter)->prefValue(); + } + else if (dynamic_cast((*iter)) != NULL) + { + value = ((ExtcapArgumentFileSelection *)*iter)->prefValue(); + } + else if (dynamic_cast((*iter)) != NULL) + { + value = ((ExtArgNumber *)*iter)->prefValue(); + } + else if (dynamic_cast((*iter)) != NULL) + { + value = ((ExtArgText *)*iter)->prefValue(); + } + else if (dynamic_cast((*iter)) != NULL) + { + value = ((ExtArgTimestamp *)*iter)->prefValue(); + } + else + value = (*iter)->prefValue(); + + QString key = argument->prefKey(device_name); + if (useCallsAsKey) + key = argument->call(); + + if ((key.length() > 0) && (includeEmptyValues || isBoolflag || value.length() > 0) ) + { + gchar * val = qstring_strdup(value); + + g_hash_table_insert(entries, qstring_strdup(key), val); + } + } + + return entries; +} + +void ExtcapOptionsDialog::storeValues() +{ + GHashTable * entries = getArgumentSettings(); + + if (g_hash_table_size(entries) > 0) + { + if (prefs_store_ext_multiple("extcap", entries)) + mainApp->emitAppSignal(MainApplication::PreferencesChanged); + + } +} + +ExtcapValueList ExtcapOptionsDialog::loadValuesFor(int argNum, QString argumentName, QString parent) +{ + ExtcapValueList elements; + GList * walker = 0, * values = 0; + extcap_value * v; + + QList children = findChildren(); + foreach (QWidget * child, children) + child->setEnabled(false); + + QString argcall = argumentName; + if (argcall.startsWith("--")) + argcall = argcall.right(argcall.size()-2); + + GHashTable * entries = getArgumentSettings(true, false); + + values = extcap_get_if_configuration_values(this->device_name.toStdString().c_str(), argcall.toStdString().c_str(), entries); + + for (walker = g_list_first((GList *)(values)); walker != NULL ; walker = walker->next) + { + v = (extcap_value *) walker->data; + if (v == NULL || v->display == NULL || v->call == NULL) + break; + + /* Only accept values for this argument */ + if (v->arg_num != argNum) + break; + + QString valParent = QString().fromUtf8(v->parent); + + if (parent.compare(valParent) == 0) + { + + QString display = QString().fromUtf8(v->display); + QString call = QString().fromUtf8(v->call); + + ExtcapValue element = ExtcapValue(display, call, + v->enabled == (gboolean)TRUE, v->is_default == (gboolean)TRUE); + +#if 0 + /* TODO: Disabled due to wrong parent handling. It leads to an infinite loop for now. To implement this properly, other things + will be needed, like new arguments for setting the parent in the call to the extcap utility*/ + if (!call.isEmpty()) + element.setChildren(this->loadValuesFor(argumentName, call)); +#endif + + elements.append(element); + } + } + + foreach (QWidget * child, children) + child->setEnabled(true); + + return elements; +} diff --git a/ui/qt/extcap_options_dialog.h b/ui/qt/extcap_options_dialog.h new file mode 100644 index 00000000..230e2d32 --- /dev/null +++ b/ui/qt/extcap_options_dialog.h @@ -0,0 +1,69 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#ifndef EXTCAP_OPTIONS_DIALOG_H +#define EXTCAP_OPTIONS_DIALOG_H + +#include + +#include +#include +#include +#include + +#include "ui/qt/extcap_argument.h" + +#include +#include + +namespace Ui { +class ExtcapOptionsDialog; +} + +typedef QList ExtcapArgumentList; + +class ExtcapOptionsDialog : public QDialog +{ + Q_OBJECT + +public: + ~ExtcapOptionsDialog(); + static ExtcapOptionsDialog * createForDevice(QString &device_name, bool startCaptureOnClose, QWidget *parent = 0); + + ExtcapValueList loadValuesFor(int argNum, QString call, QString parent = ""); + +private Q_SLOTS: + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + void on_buttonBox_clicked(QAbstractButton *button); + void on_buttonBox_helpRequested(); + void updateWidgets(); + void anyValueChanged(); + +private: + explicit ExtcapOptionsDialog(bool startCaptureOnClose, QWidget *parent = 0); + + Ui::ExtcapOptionsDialog *ui; + QString device_name; + guint device_idx; + QIcon defaultValueIcon_; + + ExtcapArgumentList extcapArguments; + + void loadArguments(); + + bool saveOptionToCaptureInfo(); + GHashTable * getArgumentSettings(bool useCallsAsKey = false, bool includeEmptyValues = true); + void storeValues(); + void resetValues(); + +}; + +#endif // EXTCAP_OPTIONS_DIALOG_H diff --git a/ui/qt/extcap_options_dialog.ui b/ui/qt/extcap_options_dialog.ui new file mode 100644 index 00000000..337a0c9d --- /dev/null +++ b/ui/qt/extcap_options_dialog.ui @@ -0,0 +1,51 @@ + + + ExtcapOptionsDialog + + + + 0 + 0 + 600 + 92 + + + + + 600 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + Save parameter(s) on capture start + + + true + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults + + + + + + + + diff --git a/ui/qt/file_set_dialog.cpp b/ui/qt/file_set_dialog.cpp new file mode 100644 index 00000000..70f38914 --- /dev/null +++ b/ui/qt/file_set_dialog.cpp @@ -0,0 +1,157 @@ +/* fileset_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include "file.h" +#include "fileset.h" + +#include "ui/help_url.h" + +#include + +#include "file_set_dialog.h" +#include +#include "models/fileset_entry_model.h" +#include "main_application.h" + +#include +#include +#include +#include +#include +#include + +// To do: +// - We might want to rename this to FilesetDialog / fileset_dialog.{cpp,h}. + +void +fileset_dlg_begin_add_file(void *window) { + FileSetDialog *fs_dlg = static_cast(window); + + if (fs_dlg) fs_dlg->beginAddFile(); +} + +/* This file is a part of the current file set. Add it to our model. */ +void +fileset_dlg_add_file(fileset_entry *entry, void *window) { + FileSetDialog *fs_dlg = static_cast(window); + + if (fs_dlg) fs_dlg->addFile(entry); +} + +void +fileset_dlg_end_add_file(void *window) { + FileSetDialog *fs_dlg = static_cast(window); + + if (fs_dlg) fs_dlg->endAddFile(); +} + +FileSetDialog::FileSetDialog(QWidget *parent) : + GeometryStateDialog(parent), + fs_ui_(new Ui::FileSetDialog), + fileset_entry_model_(new FilesetEntryModel(this)), + close_button_(NULL) +{ + fs_ui_->setupUi(this); + loadGeometry (); + + fs_ui_->fileSetTree->setModel(fileset_entry_model_); + + fs_ui_->fileSetTree->setFocus(); + + close_button_ = fs_ui_->buttonBox->button(QDialogButtonBox::Close); + + connect(fs_ui_->fileSetTree->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &FileSetDialog::selectionChanged); + + beginAddFile(); + addFile(); + endAddFile(); +} + +FileSetDialog::~FileSetDialog() +{ + fileset_entry_model_->clear(); + delete fs_ui_; +} + +/* a new capture file was opened, browse the dir and look for files matching the given file set */ +void FileSetDialog::fileOpened(const capture_file *cf) { + if (!cf) return; + fileset_entry_model_->clear(); + fileset_add_dir(cf->filename, this); +} + +/* the capture file was closed */ +void FileSetDialog::fileClosed() { + fileset_entry_model_->clear(); +} + +void FileSetDialog::addFile(fileset_entry *entry) { + if (!entry) return; + + if (entry->current) { + cur_idx_ = fileset_entry_model_->entryCount(); + } + fileset_entry_model_->appendEntry(entry); +} + +void FileSetDialog::beginAddFile() +{ + cur_idx_ = -1; + setWindowTitle(mainApp->windowTitleString(tr("No files in Set"))); + fs_ui_->directoryLabel->setText(tr("No capture loaded")); + fs_ui_->directoryLabel->setEnabled(false); +} + +void FileSetDialog::endAddFile() +{ + if (fileset_entry_model_->entryCount() > 0) { + setWindowTitle(mainApp->windowTitleString(tr("%Ln File(s) in Set", "", + fileset_entry_model_->entryCount()))); + } + + QString dir_name = fileset_get_dirname(); + fs_ui_->directoryLabel->setText(dir_name); + fs_ui_->directoryLabel->setUrl(QUrl::fromLocalFile(dir_name).toString()); + fs_ui_->directoryLabel->setEnabled(true); + + if (cur_idx_ >= 0) { + fs_ui_->fileSetTree->setCurrentIndex(fileset_entry_model_->index(cur_idx_, 0)); + } + + for (int col = 0; col < 4; col++) { + fs_ui_->fileSetTree->resizeColumnToContents(col); + } + + if (close_button_) + close_button_->setEnabled(true); +} + +void FileSetDialog::selectionChanged(const QItemSelection &selected, const QItemSelection &) +{ + const fileset_entry *entry = fileset_entry_model_->getRowEntry(selected.first().top()); + + if (!entry || entry->current) + return; + + QString new_cf_path = entry->fullname; + + if (new_cf_path.length() > 0) { + emit fileSetOpenCaptureFile(new_cf_path); + } +} + +void FileSetDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_FILESET_DIALOG); +} diff --git a/ui/qt/file_set_dialog.h b/ui/qt/file_set_dialog.h new file mode 100644 index 00000000..d4158898 --- /dev/null +++ b/ui/qt/file_set_dialog.h @@ -0,0 +1,58 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FILE_SET_DIALOG_H +#define FILE_SET_DIALOG_H + +#include + +#include + +#include "file.h" +#include "fileset.h" + +#include "geometry_state_dialog.h" + +#include + +namespace Ui { +class FileSetDialog; +} + +class FilesetEntryModel; + +class FileSetDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit FileSetDialog(QWidget *parent = 0); + ~FileSetDialog(); + + void fileOpened(const capture_file *cf); + void fileClosed(); + void addFile(fileset_entry *entry = NULL); + void beginAddFile(); + void endAddFile(); + +signals: + void fileSetOpenCaptureFile(QString); + +private slots: + void selectionChanged(const QItemSelection &selected, const QItemSelection &); + void on_buttonBox_helpRequested(); + +private: + Ui::FileSetDialog *fs_ui_; + FilesetEntryModel *fileset_entry_model_; + QPushButton *close_button_; + int cur_idx_; +}; + +#endif // FILE_SET_DIALOG_H diff --git a/ui/qt/file_set_dialog.ui b/ui/qt/file_set_dialog.ui new file mode 100644 index 00000000..3c614332 --- /dev/null +++ b/ui/qt/file_set_dialog.ui @@ -0,0 +1,136 @@ + + + FileSetDialog + + + + 0 + 0 + 750 + 450 + + + + Dialog + + + true + + + + + + QFormLayout::FieldsStayAtSizeHint + + + + + + + Directory: + + + + + + + + 1 + 0 + + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + + 0 + 1 + + + + Qt::ElideLeft + + + false + + + true + + + false + + + false + + + + + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+
+ + + + buttonBox + accepted() + FileSetDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FileSetDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/filter_action.cpp b/ui/qt/filter_action.cpp new file mode 100644 index 00000000..f8b7732f --- /dev/null +++ b/ui/qt/filter_action.cpp @@ -0,0 +1,299 @@ +/* filter_action.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "filter_action.h" + +#include +#include + +#include +#include + +FilterAction::FilterAction(QObject *parent, FilterAction::Action action, FilterAction::ActionType type, QString actionName) : + QAction(parent), + action_(action), + type_(type), + actionName_(actionName) +{ + setText(actionName); +} + +FilterAction::FilterAction(QObject *parent, FilterAction::Action action, FilterAction::ActionType type, FilterAction::ActionDirection direction) : + QAction(parent), + action_(action), + type_(type), + direction_(direction) +{ + setText(actionDirectionName(direction)); +} + +FilterAction::FilterAction(QObject *parent, FilterAction::Action action, FilterAction::ActionType type) : + QAction(parent), + action_(action), + type_(type), + direction_(ActionDirectionAToAny) +{ + setText(actionTypeName(type)); +} + +FilterAction::FilterAction(QObject *parent, FilterAction::Action action) : + QAction(parent), + action_(action), + type_(ActionTypePlain), + direction_(ActionDirectionAToAny) +{ + setText(actionName(action)); +} + +const QList FilterAction::actions() { + static const QList actions_ = QList() + << ActionApply + << ActionPrepare + << ActionFind + << ActionColorize + << ActionWebLookup + << ActionCopy; + return actions_; +} + +const QString FilterAction::actionName(Action action) { + switch (action) { + case ActionApply: + return QObject::tr("Apply as Filter"); + break; + case ActionPrepare: + return QObject::tr("Prepare as Filter"); + break; + case ActionFind: + return QObject::tr("Find"); + break; + case ActionColorize: + return QObject::tr("Colorize"); + break; + case ActionWebLookup: + return QObject::tr("Look Up"); + break; + case ActionCopy: + return QObject::tr("Copy"); + break; + default: + return QObject::tr("UNKNOWN"); + break; + } +} + + +const QList FilterAction::actionTypes(Action filter_action) +{ + static const QList action_types_ = QList() + << ActionTypePlain + << ActionTypeNot + << ActionTypeAnd + << ActionTypeOr + << ActionTypeAndNot + << ActionTypeOrNot; + + static const QList simple_action_types_ = QList() + << ActionTypePlain + << ActionTypeNot; + + switch (filter_action) { + case ActionFind: + case ActionColorize: + return simple_action_types_; + default: + break; + } + + return action_types_; +} + +const QString FilterAction::actionTypeName(ActionType type) { + switch (type) { + case ActionTypePlain: + return QObject::tr("Selected"); + break; + case ActionTypeNot: + return QObject::tr("Not Selected"); + break; + case ActionTypeAnd: + return QObject::tr("…and Selected"); + break; + case ActionTypeOr: + return QObject::tr("…or Selected"); + break; + case ActionTypeAndNot: + return QObject::tr("…and not Selected"); + break; + case ActionTypeOrNot: + return QObject::tr("…or not Selected"); + break; + default: + return QObject::tr("UNKNOWN"); + break; + } +} + + +const QList FilterAction::actionDirections() +{ + static const QList action_directions_ = QList() + << ActionDirectionAToFromB + << ActionDirectionAToB + << ActionDirectionAFromB + << ActionDirectionAToFromAny + << ActionDirectionAToAny + << ActionDirectionAFromAny + << ActionDirectionAnyToFromB + << ActionDirectionAnyToB + << ActionDirectionAnyFromB; + return action_directions_; +} + +const QString FilterAction::actionDirectionName(ActionDirection direction) { + switch (direction) { + case ActionDirectionAToFromB: + return QObject::tr("A " UTF8_LEFT_RIGHT_ARROW " B"); + break; + case ActionDirectionAToB: + return QObject::tr("A " UTF8_RIGHTWARDS_ARROW " B"); + break; + case ActionDirectionAFromB: + return QObject::tr("B " UTF8_RIGHTWARDS_ARROW " A"); + break; + case ActionDirectionAToFromAny: + return QObject::tr("A " UTF8_LEFT_RIGHT_ARROW " Any"); + break; + case ActionDirectionAToAny: + return QObject::tr("A " UTF8_RIGHTWARDS_ARROW " Any"); + break; + case ActionDirectionAFromAny: + return QObject::tr("Any " UTF8_RIGHTWARDS_ARROW " A"); + break; + case ActionDirectionAnyToFromB: + return QObject::tr("Any " UTF8_LEFT_RIGHT_ARROW " B"); + break; + case ActionDirectionAnyToB: + return QObject::tr("Any " UTF8_RIGHTWARDS_ARROW " B"); + break; + case ActionDirectionAnyFromB: + return QObject::tr("B " UTF8_RIGHTWARDS_ARROW " Any"); + break; + default: + return QObject::tr("UNKNOWN"); + break; + } +} + +QActionGroup * FilterAction::createFilterGroup(QString filter, bool prepare, bool enabled, QWidget * parent) +{ + if (filter.isEmpty()) + enabled = false; + + bool filterEmpty = false; + if (mainApp) + { + QWidget * mainWin = mainApp->mainWindow(); + if (qobject_cast(mainWin)) + filterEmpty = qobject_cast(mainWin)->getFilter().isEmpty(); + } + + FilterAction * filterAction = new FilterAction(parent, prepare ? FilterAction::ActionPrepare : FilterAction::ActionApply); + + QActionGroup * group = new QActionGroup(parent); + group->setProperty("filter", filter); + group->setProperty("filterAction", prepare ? FilterAction::ActionPrepare : FilterAction::ActionApply); + QAction * action = group->addAction(tr("Selected")); + action->setProperty("filterType", FilterAction::ActionTypePlain); + action = group->addAction(tr("Not Selected")); + action->setProperty("filterType", FilterAction::ActionTypeNot); + action = group->addAction(tr("…and Selected")); + action->setProperty("filterType", FilterAction::ActionTypeAnd); + action->setEnabled(!filterEmpty); + action = group->addAction(tr("…or Selected")); + action->setProperty("filterType", FilterAction::ActionTypeOr); + action->setEnabled(!filterEmpty); + action = group->addAction(tr("…and not Selected")); + action->setProperty("filterType", FilterAction::ActionTypeAndNot); + action->setEnabled(!filterEmpty); + action = group->addAction(tr("…or not Selected")); + action->setProperty("filterType", FilterAction::ActionTypeOrNot); + action->setEnabled(!filterEmpty); + group->setEnabled(enabled); + if (! filter.isEmpty()) + connect(group, &QActionGroup::triggered, filterAction, &FilterAction::groupTriggered); + + return group; +} + +QMenu * FilterAction::createFilterMenu(FilterAction::Action act, QString filter, bool enabled, QWidget * par) +{ + QString title = (act == FilterAction::ActionApply) ? QObject::tr("Apply as Filter") : QObject::tr("Prepare as Filter"); + bool prepare = (act == FilterAction::ActionApply) ? false : true; + + QMenu * submenu = new QMenu(title, par); + if (filter.length() > 0) + { + int one_em = submenu->fontMetrics().height(); + QString prep_text = QString("%1: %2").arg(title).arg(filter); + prep_text = submenu->fontMetrics().elidedText(prep_text, Qt::ElideRight, one_em * 40); + QAction * comment = submenu->addAction(prep_text); + comment->setEnabled(false); + submenu->addSeparator(); + } + QActionGroup * group = FilterAction::createFilterGroup(filter, prepare, enabled, par); + submenu->addActions(group->actions()); + + return submenu; +} + +void FilterAction::groupTriggered(QAction * action) +{ + if (action && mainApp) + { + if (action->property("filterType").canConvert() && + sender()->property("filterAction").canConvert()) + { + FilterAction::Action act = sender()->property("filterAction").value(); + FilterAction::ActionType type = action->property("filterType").value(); + QString filter = sender()->property("filter").toString(); + + QWidget * mainWin = mainApp->mainWindow(); + if (qobject_cast(mainWin)) + { + MainWindow * mw = qobject_cast(mainWin); + mw->setDisplayFilter(filter, act, type); + } + } + } +} + +QAction * FilterAction::copyFilterAction(QString filter, QWidget *par) +{ + FilterAction * filterAction = new FilterAction(par, ActionCopy); + QAction * action = new QAction(QObject::tr("Copy"), par); + action->setProperty("filter", QVariant::fromValue(filter)); + connect(action, &QAction::triggered, filterAction, &FilterAction::copyActionTriggered); + + if (filter.isEmpty()) + action->setEnabled(false); + + return action; +} + +void FilterAction::copyActionTriggered() +{ + QAction * sendAction = qobject_cast(sender()); + if (! sendAction) + return; + + QString filter = sendAction->property("filter").toString(); + if (filter.length() > 0) + mainApp->clipboard()->setText(filter); +} diff --git a/ui/qt/filter_action.h b/ui/qt/filter_action.h new file mode 100644 index 00000000..82ff29cf --- /dev/null +++ b/ui/qt/filter_action.h @@ -0,0 +1,97 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* Derived from gtk/filter_utils.h */ + +#ifndef FILTER_ACTION_H +#define FILTER_ACTION_H + +#include + +#include +#include + +class FilterAction : public QAction +{ + Q_OBJECT +public: + /* Filter actions */ + enum Action { + ActionApply, + ActionColorize, + ActionCopy, + ActionFind, + ActionPrepare, + ActionWebLookup + }; + Q_ENUM(Action) + + /* Action type - says what to do with the filter */ + enum ActionType { + ActionTypePlain, + ActionTypeNot, + ActionTypeAnd, + ActionTypeOr, + ActionTypeAndNot, + ActionTypeOrNot + }; + Q_ENUM(ActionType) + + /* Action direction */ + enum ActionDirection { + ActionDirectionAToFromB, + ActionDirectionAToB, + ActionDirectionAFromB, + ActionDirectionAToFromAny, + ActionDirectionAToAny, + ActionDirectionAFromAny, + ActionDirectionAnyToFromB, + ActionDirectionAnyToB, + ActionDirectionAnyFromB + }; + + explicit FilterAction(QObject *parent, Action action, ActionType type, QString actionName); + explicit FilterAction(QObject *parent, Action action, ActionType type, ActionDirection direction); + explicit FilterAction(QObject *parent, Action action, ActionType type); + explicit FilterAction(QObject *parent, Action action); + + Action action() { return action_; } + static const QList actions(); + static const QString actionName(Action action); + + ActionType actionType() { return type_; } + static const QList actionTypes(Action filter_action = ActionApply); + static const QString actionTypeName(ActionType type); + + ActionDirection actionDirection() { return direction_; } + static const QList actionDirections(); + static const QString actionDirectionName(ActionDirection direction); + + static QActionGroup * createFilterGroup(QString filter, bool prepare, bool enabled, QWidget * parent); + static QMenu * createFilterMenu(FilterAction::Action act, QString filter, bool enabled, QWidget * parent); + static QAction * copyFilterAction(QString filter, QWidget *par); + +signals: + +public slots: + +private: + Action action_; + ActionType type_; + ActionDirection direction_; + + QString actionName_; + +private slots: + void groupTriggered(QAction *); + void copyActionTriggered(); + +}; + +#endif // FILTER_ACTION_H diff --git a/ui/qt/filter_dialog.cpp b/ui/qt/filter_dialog.cpp new file mode 100644 index 00000000..1ed86b87 --- /dev/null +++ b/ui/qt/filter_dialog.cpp @@ -0,0 +1,246 @@ +/* filter_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include + +#include "filter_dialog.h" +#include + +#include +#include +#include +#include + +#include +#include +#include +#include "main_application.h" + +FilterDialog::FilterDialog(QWidget *parent, FilterType filter_type, QString new_filter_) : + GeometryStateDialog(parent), + ui(new Ui::FilterDialog), + filter_type_(filter_type), + filter_tree_delegate_(new FilterTreeDelegate(this, filter_type)) +{ + ui->setupUi(this); + + if (parent) loadGeometry(parent->width() * 2 / 3, parent->height() * 2 / 3); + setWindowIcon(mainApp->normalIcon()); + + ui->newToolButton->setStockIcon("list-add"); + ui->deleteToolButton->setStockIcon("list-remove"); + ui->copyToolButton->setStockIcon("list-copy"); + +#ifdef Q_OS_MAC + ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true); +#endif + +#if 0 + ui->filterTreeWidget->setDragEnabled(true); + ui->filterTreeWidget->viewport()->setAcceptDrops(true); + ui->filterTreeWidget->setDropIndicatorShown(true); + ui->filterTreeWidget->setDragDropMode(QAbstractItemView::InternalMove); +#endif + ui->filterTreeView->setDragEnabled(true); + ui->filterTreeView->setAcceptDrops(true); + ui->filterTreeView->setDropIndicatorShown(true); + + const gchar * filename = NULL; + QString newFilterText; + if (filter_type == CaptureFilter) { + setWindowTitle(mainApp->windowTitleString(tr("Capture Filters"))); + filename = CFILTER_FILE_NAME; + newFilterText = tr("New capture filter"); + model_ = new FilterListModel(FilterListModel::Capture, this); + } else { + setWindowTitle(mainApp->windowTitleString(tr("Display Filters"))); + filename = DFILTER_FILE_NAME; + newFilterText = tr("New display filter"); + model_ = new FilterListModel(FilterListModel::Display, this); + } + + if (new_filter_.length() > 0) + model_->addFilter(newFilterText, new_filter_); + + ui->filterTreeView->setModel(model_); + + ui->filterTreeView->setItemDelegate(new FilterTreeDelegate(this, filter_type)); + + ui->filterTreeView->resizeColumnToContents(FilterListModel::ColumnName); + + connect(ui->filterTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FilterDialog::selectionChanged); + + QString abs_path = gchar_free_to_qstring(get_persconffile_path(filename, TRUE)); + if (file_exists(abs_path.toUtf8().constData())) { + ui->pathLabel->setText(abs_path); + ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString()); + ui->pathLabel->setToolTip(tr("Open ") + filename); + ui->pathLabel->setEnabled(true); + } +} + +FilterDialog::~FilterDialog() +{ + delete ui; +} + +void FilterDialog::addFilter(QString name, QString filter, bool start_editing) +{ + if (model_) + { + QModelIndex idx = model_->addFilter(name, filter); + if (start_editing) + ui->filterTreeView->edit(idx); + } +} + +void FilterDialog::updateWidgets() +{ + if (! ui->filterTreeView->selectionModel()) + return; + + qsizetype num_selected = ui->filterTreeView->selectionModel()->selectedRows().count(); + + ui->copyToolButton->setEnabled(num_selected == 1); + ui->deleteToolButton->setEnabled(num_selected > 0); +} + +void FilterDialog::selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + updateWidgets(); +} + +void FilterDialog::on_newToolButton_clicked() +{ + QString name; + QString filter; + + if (filter_type_ == CaptureFilter) { + //: This text is automatically filled in when a new filter is created + name = tr("New capture filter"); + filter = "ip host host.example.com"; + } else { + //: This text is automatically filled in when a new filter is created + name = tr("New display filter"); + filter = "ip.host == host.example.com"; + } + + addFilter(name, filter, true); +} + +void FilterDialog::on_deleteToolButton_clicked() +{ + QModelIndexList selected = ui->filterTreeView->selectionModel()->selectedRows(); + QList rows; + foreach (QModelIndex idx, selected) + { + if (idx.isValid() && ! rows.contains(idx.row())) + { + rows << idx.row(); + model_->removeFilter(idx); + } + } +} + +void FilterDialog::on_copyToolButton_clicked() +{ + QModelIndexList selected = ui->filterTreeView->selectionModel()->selectedRows(); + if (selected.count() <= 0) + return; + + int rowNr = selected.at(0).row(); + QModelIndex row = selected.at(0).sibling(rowNr, FilterListModel::ColumnName); + + addFilter(row.data().toString(), row.sibling(rowNr, FilterListModel::ColumnExpression).data().toString(), true); +} + +void FilterDialog::on_buttonBox_accepted() +{ + model_->saveList(); + + if (filter_type_ == CaptureFilter) { + mainApp->emitAppSignal(MainApplication::CaptureFilterListChanged); + } else { + mainApp->emitAppSignal(MainApplication::DisplayFilterListChanged); + } +} + +void FilterDialog::on_buttonBox_helpRequested() +{ + if (filter_type_ == CaptureFilter) { + mainApp->helpTopicAction(HELP_CAPTURE_FILTERS_DIALOG); + } else { + mainApp->helpTopicAction(HELP_DISPLAY_FILTERS_DIALOG); + } +} + +// +// FilterTreeDelegate +// Delegate for editing capture and display filters. +// + +FilterTreeDelegate::FilterTreeDelegate(QObject *parent, FilterDialog::FilterType filter_type) : + QStyledItemDelegate(parent), + filter_type_(filter_type) +{} + +QWidget *FilterTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QWidget * w = Q_NULLPTR; + if (index.column() != FilterListModel::ColumnExpression) { + w = QStyledItemDelegate::createEditor(parent, option, index); + } + else + { + if (filter_type_ == FilterDialog::CaptureFilter) { + w = new CaptureFilterEdit(parent, true); + } else { + w = new DisplayFilterEdit(parent, DisplayFilterToEnter); + } + } + + if (qobject_cast(w) && index.column() == FilterListModel::ColumnName) + qobject_cast(w)->setValidator(new FilterValidator()); + + return w; +} + +void FilterTreeDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + if (! editor || ! index.isValid()) + return; + + QStyledItemDelegate::setEditorData(editor, index); + + if (qobject_cast(editor)) + qobject_cast(editor)->setText(index.data().toString()); +} + +QValidator::State FilterValidator::validate(QString & input, int & /*pos*/) const +{ + /* Making this a list to be able to easily add additional values in the future */ + QStringList invalidKeys = QStringList() << "\""; + + if (input.length() <= 0) + return QValidator::Intermediate; + + foreach (QString key, invalidKeys) + if (input.indexOf(key) >= 0) + return QValidator::Invalid; + + return QValidator::Acceptable; +} diff --git a/ui/qt/filter_dialog.h b/ui/qt/filter_dialog.h new file mode 100644 index 00000000..34b3a156 --- /dev/null +++ b/ui/qt/filter_dialog.h @@ -0,0 +1,84 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FILTER_DIALOG_H +#define FILTER_DIALOG_H + +#include "geometry_state_dialog.h" + +#include + +#include +#include + +class QItemSelection; +class FilterTreeDelegate; + +namespace Ui { +class FilterDialog; +} + +class FilterDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + enum FilterType { CaptureFilter, DisplayFilter }; + explicit FilterDialog(QWidget *parent = 0, FilterType filter_type = CaptureFilter, const QString new_filter = QString()); + ~FilterDialog(); + +private: + Ui::FilterDialog *ui; + + FilterListModel * model_; + + enum FilterType filter_type_; + FilterTreeDelegate *filter_tree_delegate_; + + void addFilter(QString name, QString filter, bool start_editing = false); + +private slots: + void updateWidgets(); + + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + + void on_newToolButton_clicked(); + void on_deleteToolButton_clicked(); + void on_copyToolButton_clicked(); + void on_buttonBox_accepted(); + void on_buttonBox_helpRequested(); +}; + + +// +// FilterTreeDelegate +// Delegate for editing capture and display filters. +// + +class FilterTreeDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + FilterTreeDelegate(QObject *parent, FilterDialog::FilterType filter_type); + + virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override; + +private: + FilterDialog::FilterType filter_type_; +}; + +class FilterValidator : public QValidator +{ +public: + virtual QValidator::State validate(QString & input, int & pos) const override; +}; + +#endif // FILTER_DIALOG_H diff --git a/ui/qt/filter_dialog.ui b/ui/qt/filter_dialog.ui new file mode 100644 index 00000000..dbd0d5fc --- /dev/null +++ b/ui/qt/filter_dialog.ui @@ -0,0 +1,158 @@ + + + FilterDialog + + + + 0 + 0 + 584 + 390 + + + + Dialog + + + + + + QAbstractItemView::DragDrop + + + true + + + true + + + + + + + + + Create a new filter. + + + + + + + + + + false + + + Remove this filter. + + + + + + + false + + + Copy this filter. + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+ + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+
+ + + + buttonBox + accepted() + FilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/filter_expression_frame.cpp b/ui/qt/filter_expression_frame.cpp new file mode 100644 index 00000000..38975977 --- /dev/null +++ b/ui/qt/filter_expression_frame.cpp @@ -0,0 +1,183 @@ +/* filter_expression_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "filter_expression_frame.h" +#include + +#include +#include + +#include +#include +#include + +#include +#include + +// To do: +// - Add the ability to edit current expressions. + +FilterExpressionFrame::FilterExpressionFrame(QWidget *parent) : + AccordionFrame(parent), + ui(new Ui::FilterExpressionFrame) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC + foreach (QWidget *w, findChildren()) { + w->setAttribute(Qt::WA_MacSmallSize, true); + } +#endif + + editExpression_ = -1; + updateWidgets(); +} + +FilterExpressionFrame::~FilterExpressionFrame() +{ + delete ui; +} + +void FilterExpressionFrame::addExpression(const QString filter_text) +{ + if (isVisible()) { + on_buttonBox_rejected(); + return; + } + + editExpression_ = -1; + ui->displayFilterLineEdit->setText(filter_text); + + if (! isVisible()) + animatedShow(); +} + +void FilterExpressionFrame::editExpression(int exprIdx) +{ + if (isVisible()) + { + ui->labelLineEdit->clear(); + ui->displayFilterLineEdit->clear(); + ui->commentLineEdit->clear(); + editExpression_ = -1; + } + + UatModel * uatModel = new UatModel(this, "Display expressions"); + if (! uatModel->index(exprIdx, 1).isValid()) + return; + + editExpression_ = exprIdx; + + ui->labelLineEdit->setText(uatModel->data(uatModel->index(exprIdx, 1), Qt::DisplayRole).toString()); + ui->displayFilterLineEdit->setText(uatModel->data(uatModel->index(exprIdx, 2), Qt::DisplayRole).toString()); + ui->commentLineEdit->setText(uatModel->data(uatModel->index(exprIdx, 3), Qt::DisplayRole).toString()); + + delete(uatModel); + + if (! isVisible()) + animatedShow(); +} + +void FilterExpressionFrame::showEvent(QShowEvent *event) +{ + ui->labelLineEdit->setFocus(); + ui->labelLineEdit->selectAll(); + + AccordionFrame::showEvent(event); +} + +void FilterExpressionFrame::updateWidgets() +{ + bool ok_enable = true; + + if (ui->labelLineEdit->text().isEmpty() || + ((ui->displayFilterLineEdit->syntaxState() != SyntaxLineEdit::Valid) && + (ui->displayFilterLineEdit->syntaxState() != SyntaxLineEdit::Deprecated))) + ok_enable = false; + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok_enable); +} + +void FilterExpressionFrame::on_filterExpressionPreferencesPushButton_clicked() +{ + on_buttonBox_rejected(); + emit showPreferencesDialog(PrefsModel::typeToString(PrefsModel::FilterButtons)); +} + +void FilterExpressionFrame::on_labelLineEdit_textChanged(const QString) +{ + updateWidgets(); +} + +void FilterExpressionFrame::on_displayFilterLineEdit_textChanged(const QString) +{ + updateWidgets(); +} + +void FilterExpressionFrame::on_buttonBox_accepted() +{ + QByteArray label_ba = ui->labelLineEdit->text().toUtf8(); + QByteArray expr_ba = ui->displayFilterLineEdit->text().toUtf8(); + QByteArray comment_ba = ui->commentLineEdit->text().toUtf8(); + + if (ui->labelLineEdit->text().length() == 0 || ui->displayFilterLineEdit->text().length() == 0) + return; + + if (! ui->displayFilterLineEdit->checkFilter()) + return; + + if (editExpression_ >= 0) + { + UatModel * uatModel = new UatModel(this, "Display expressions"); + if (! uatModel->index(editExpression_, 1).isValid()) + return; + + uatModel->setData(uatModel->index(editExpression_, 1), QVariant::fromValue(label_ba)); + uatModel->setData(uatModel->index(editExpression_, 2), QVariant::fromValue(expr_ba)); + uatModel->setData(uatModel->index(editExpression_, 3), QVariant::fromValue(comment_ba)); + } + else + { + filter_expression_new(label_ba.constData(), expr_ba.constData(), comment_ba.constData(), TRUE); + } + + save_migrated_uat("Display expressions", &prefs.filter_expressions_old); + on_buttonBox_rejected(); + emit filterExpressionsChanged(); +} + +void FilterExpressionFrame::on_buttonBox_rejected() +{ + ui->labelLineEdit->clear(); + ui->displayFilterLineEdit->clear(); + ui->commentLineEdit->clear(); + editExpression_ = -1; + animatedHide(); +} + +void FilterExpressionFrame::keyPressEvent(QKeyEvent *event) +{ + if (event->modifiers() == Qt::NoModifier) { + if (event->key() == Qt::Key_Escape) { + on_buttonBox_rejected(); + } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + if (ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) { + on_buttonBox_accepted(); + } else if (ui->labelLineEdit->text().length() == 0) { + mainApp->pushStatus(MainApplication::FilterSyntax, tr("Missing label.")); + } else if (ui->displayFilterLineEdit->syntaxState() == SyntaxLineEdit::Empty) { + mainApp->pushStatus(MainApplication::FilterSyntax, tr("Missing filter expression.")); + } else if (ui->displayFilterLineEdit->syntaxState() != SyntaxLineEdit::Valid) { + mainApp->pushStatus(MainApplication::FilterSyntax, tr("Invalid filter expression.")); + } + } + } + + AccordionFrame::keyPressEvent(event); +} diff --git a/ui/qt/filter_expression_frame.h b/ui/qt/filter_expression_frame.h new file mode 100644 index 00000000..4a5c21f9 --- /dev/null +++ b/ui/qt/filter_expression_frame.h @@ -0,0 +1,52 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FILTER_EXPRESSION_FRAME_H +#define FILTER_EXPRESSION_FRAME_H + +#include "accordion_frame.h" + +namespace Ui { +class FilterExpressionFrame; +} + +class FilterExpressionFrame : public AccordionFrame +{ + Q_OBJECT + +public: + explicit FilterExpressionFrame(QWidget *parent = 0); + ~FilterExpressionFrame(); + + void addExpression(const QString filter_text); + void editExpression(int exprIdx); + +signals: + void showPreferencesDialog(QString pane_name); + void filterExpressionsChanged(); + +protected: + virtual void showEvent(QShowEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + +private: + Ui::FilterExpressionFrame *ui; + + int editExpression_; + +private slots: + void updateWidgets(); + void on_filterExpressionPreferencesPushButton_clicked(); + void on_labelLineEdit_textChanged(const QString); + void on_displayFilterLineEdit_textChanged(const QString); + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); +}; + +#endif // FILTER_EXPRESSION_FRAME_H diff --git a/ui/qt/filter_expression_frame.ui b/ui/qt/filter_expression_frame.ui new file mode 100644 index 00000000..0762c7c6 --- /dev/null +++ b/ui/qt/filter_expression_frame.ui @@ -0,0 +1,198 @@ + + + FilterExpressionFrame + + + + 0 + 0 + 796 + 82 + + + + + 0 + 0 + + + + + 16777215 + 82 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + + Filter Buttons Preferences… + + + + + + + Qt::Vertical + + + + 20 + 5 + + + + + + + + + + + + + + + + Label: + + + + + + + + 1 + 0 + + + + + 80 + 0 + + + + Enter a description for the filter button + + + + + + + + + + + Filter: + + + + + + + + 1 + 0 + + + + + 80 + 0 + + + + Enter a filter expression to be applied + + + + + + + + + + + + + Comment: + + + + + + + + 1 + 0 + + + + + 80 + 0 + + + + Enter a comment for the filter button + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 27 + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + AccordionFrame + QFrame +
accordion_frame.h
+ 1 +
+ + SyntaxLineEdit + QLineEdit +
widgets/syntax_line_edit.h
+
+ + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + +
diff --git a/ui/qt/firewall_rules_dialog.cpp b/ui/qt/firewall_rules_dialog.cpp new file mode 100644 index 00000000..a74355e1 --- /dev/null +++ b/ui/qt/firewall_rules_dialog.cpp @@ -0,0 +1,206 @@ +/* firewall_rules_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "firewall_rules_dialog.h" +#include + +#include "epan/packet_info.h" +#include "epan/to_str.h" + +#include "ui/all_files_wildcard.h" +#include "ui/firewall_rules.h" +#include "ui/help_url.h" + +#include "wsutil/file_util.h" +#include "wsutil/utf8_entities.h" + +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include + +// XXX As described in bug 2482, some of the generated rules don't +// make sense. We could generate rules for every conceivable use case, +// but that would add complexity. We could also add controls to let +// users fine-tune rule output, but that would also add complexity. + +FirewallRulesDialog::FirewallRulesDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::FirewallRulesDialog), + prod_(0) +{ + ui->setupUi(this); + + setWindowSubtitle(tr("Firewall ACL Rules")); + + ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Copy")); + + file_name_ = cf.fileName(); // XXX Add extension? + packet_num_ = cf.packetInfo()->num; + + packet_info *pinfo = cf.packetInfo(); + copy_address(&dl_src_, &(pinfo->dl_src)); + copy_address(&dl_dst_, &(pinfo->dl_dst)); + copy_address(&net_src_, &(pinfo->net_src)); + copy_address(&net_dst_, &(pinfo->net_dst)); + ptype_ = pinfo->ptype; + src_port_ = pinfo->srcport; + dst_port_ = pinfo->destport; + int nf_item = 0; + + for (size_t prod = 0; prod < firewall_product_count(); prod++) { + QString prod_name = firewall_product_name(prod); + + // Default to Netfilter since it's likely the most popular. + if (prod_name.contains("Netfilter")) nf_item = ui->productComboBox->count(); + ui->productComboBox->addItem(prod_name); + } + ui->productComboBox->setCurrentIndex(nf_item); + + ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true); +} + +FirewallRulesDialog::~FirewallRulesDialog() +{ + delete ui; +} + +void FirewallRulesDialog::updateWidgets() +{ + WiresharkDialog::updateWidgets(); + + QString comment_pfx = firewall_product_comment_prefix(prod_); + QString rule_hint = firewall_product_rule_hint(prod_); + QString rule_line; + + rule_line = QString("%1 %2 rules for %3, packet %4.") + .arg(comment_pfx) + .arg(firewall_product_name(prod_)) + .arg(file_name_) + .arg(packet_num_); + + if (!rule_hint.isEmpty()) rule_line += " " + rule_hint; + + ui->textBrowser->clear(); + ui->textBrowser->append(rule_line); + + syntax_func v4_func = firewall_product_ipv4_func(prod_); + syntax_func port_func = firewall_product_port_func(prod_); + syntax_func v4_port_func = firewall_product_ipv4_port_func(prod_); + syntax_func mac_func = firewall_product_mac_func(prod_); + + if (v4_func && net_src_.type == AT_IPv4) { + addRule(tr("IPv4 source address."), v4_func, &net_src_, src_port_); + addRule(tr("IPv4 destination address."), v4_func, &net_dst_, dst_port_); + } + + if (port_func && (ptype_ == PT_TCP || ptype_ == PT_UDP)) { + addRule(tr("Source port."), port_func, &net_src_, src_port_); + addRule(tr("Destination port."), port_func, &net_dst_, dst_port_); + } + + if (v4_port_func && net_src_.type == AT_IPv4 && + (ptype_ == PT_TCP || ptype_ == PT_UDP)) { + addRule(tr("IPv4 source address and port."), v4_port_func, &net_src_, src_port_); + addRule(tr("IPv4 destination address and port."), v4_port_func, &net_dst_, dst_port_); + } + + if (mac_func && dl_src_.type == AT_ETHER) { + addRule(tr("MAC source address."), mac_func, &dl_src_, src_port_); + addRule(tr("MAC destination address."), mac_func, &dl_dst_, dst_port_); + } + + ui->textBrowser->moveCursor(QTextCursor::Start); + + ui->inboundCheckBox->setEnabled(firewall_product_does_inbound(prod_)); +} + +#define ADDR_BUF_LEN 200 +void FirewallRulesDialog::addRule(QString description, syntax_func rule_func, address *addr, guint32 port) +{ + if (!rule_func) return; + + char addr_buf[ADDR_BUF_LEN]; + QString comment_pfx = firewall_product_comment_prefix(prod_); + GString *rule_str = g_string_new(""); + gboolean inbound = ui->inboundCheckBox->isChecked(); + gboolean deny = ui->denyCheckBox->isChecked(); + + address_to_str_buf(addr, addr_buf, ADDR_BUF_LEN); + rule_func(rule_str, addr_buf, port, ptype_, inbound, deny); + ui->textBrowser->append(QString()); + + QString comment_line = comment_pfx + " " + description; + ui->textBrowser->append(comment_line); + ui->textBrowser->append(rule_str->str); + + g_string_free(rule_str, TRUE); +} + + +void FirewallRulesDialog::on_productComboBox_currentIndexChanged(int new_idx) +{ + prod_ = (size_t) new_idx; + updateWidgets(); +} + +void FirewallRulesDialog::on_inboundCheckBox_toggled(bool) +{ + updateWidgets(); +} + +void FirewallRulesDialog::on_denyCheckBox_toggled(bool) +{ + updateWidgets(); +} + +void FirewallRulesDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + if (button == ui->buttonBox->button(QDialogButtonBox::Save)) { + QString save_title = QString("Save %1 rules as" UTF8_HORIZONTAL_ELLIPSIS) + .arg(firewall_product_name(prod_)); + QByteArray file_name = WiresharkFileDialog::getSaveFileName(this, + save_title, + mainApp->openDialogInitialDir().canonicalPath(), + tr("Text file (*.txt);;All Files (" ALL_FILES_WILDCARD ")") + ).toUtf8(); + if (file_name.length() > 0) { + QFile save_file(file_name); + QByteArray rule_text = ui->textBrowser->toPlainText().toUtf8(); + + save_file.open(QIODevice::WriteOnly); + save_file.write(rule_text); + save_file.close(); + + if (save_file.error() != QFile::NoError) { + QMessageBox::warning(this, tr("Warning"), tr("Unable to save %1").arg(save_file.fileName())); + return; + } + + /* Save the directory name for future file dialogs. */ + mainApp->setLastOpenDirFromFilename(file_name); + } + } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) { + if (ui->textBrowser->textCursor().hasSelection()) { + ui->textBrowser->copy(); + } else { + mainApp->clipboard()->setText(ui->textBrowser->toPlainText()); + } + } +} + +void FirewallRulesDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_FIREWALL_DIALOG); +} diff --git a/ui/qt/firewall_rules_dialog.h b/ui/qt/firewall_rules_dialog.h new file mode 100644 index 00000000..e14456f4 --- /dev/null +++ b/ui/qt/firewall_rules_dialog.h @@ -0,0 +1,60 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FIREWALL_RULES_DIALOG_H +#define FIREWALL_RULES_DIALOG_H + +#include "epan/address.h" + +#include + +namespace Ui { +class FirewallRulesDialog; +} + +class QAbstractButton; + +typedef void (*syntax_func)(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny); + +class FirewallRulesDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit FirewallRulesDialog(QWidget &parent, CaptureFile &cf); + ~FirewallRulesDialog(); + +private slots: + void on_productComboBox_currentIndexChanged(int new_idx); + void on_inboundCheckBox_toggled(bool); + void on_denyCheckBox_toggled(bool); + void on_buttonBox_helpRequested(); + + void on_buttonBox_clicked(QAbstractButton *button); + +private: + Ui::FirewallRulesDialog *ui; + + QString file_name_; + int packet_num_; + + size_t prod_; + address dl_src_; + address dl_dst_; + address net_src_; + address net_dst_; + port_type ptype_; + guint32 src_port_; + guint32 dst_port_; + + void updateWidgets(); + void addRule(QString description, syntax_func rule_func, address *addr, guint32 port); +}; + +#endif // FIREWALL_RULES_DIALOG_H diff --git a/ui/qt/firewall_rules_dialog.ui b/ui/qt/firewall_rules_dialog.ui new file mode 100644 index 00000000..6e9d4b50 --- /dev/null +++ b/ui/qt/firewall_rules_dialog.ui @@ -0,0 +1,124 @@ + + + FirewallRulesDialog + + + + 0 + 0 + 650 + 450 + + + + + + + + + + + + Create rules for + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Inbound + + + true + + + + + + + Qt::Horizontal + + + + 20 + 5 + + + + + + + + Deny + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save + + + + + + + + + buttonBox + accepted() + FirewallRulesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FirewallRulesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/follow_stream_action.cpp b/ui/qt/follow_stream_action.cpp new file mode 100644 index 00000000..c9a946a1 --- /dev/null +++ b/ui/qt/follow_stream_action.cpp @@ -0,0 +1,29 @@ +/* follow_stream_action.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include +#include "follow_stream_action.h" + +#include + +#include + +FollowStreamAction::FollowStreamAction(QObject *parent, register_follow_t *follow) : + QAction(parent), + follow_(follow) +{ + if (follow_) { + setText(QString(tr("%1 Stream").arg(proto_get_protocol_short_name(find_protocol_by_id(get_follow_proto_id(follow)))))); + } +} diff --git a/ui/qt/follow_stream_action.h b/ui/qt/follow_stream_action.h new file mode 100644 index 00000000..8c7c58e3 --- /dev/null +++ b/ui/qt/follow_stream_action.h @@ -0,0 +1,39 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FOLLOWSTREAMACTION_H +#define FOLLOWSTREAMACTION_H + +#include "config.h" + +#include +#include +#include + +#include + +#include + +// Actions for "Follow Stream" menu items. + +class FollowStreamAction : public QAction +{ + Q_OBJECT +public: + FollowStreamAction(QObject *parent, register_follow_t *follow = NULL); + + register_follow_t* follow() const {return follow_;} + int protoId() const {return get_follow_proto_id(follow_);} + const char* filterName() const {return proto_get_protocol_filter_name(get_follow_proto_id(follow_));} + +private: + register_follow_t *follow_; +}; + +#endif // FOLLOWSTREAMACTION_H diff --git a/ui/qt/follow_stream_dialog.cpp b/ui/qt/follow_stream_dialog.cpp new file mode 100644 index 00000000..dbae1604 --- /dev/null +++ b/ui/qt/follow_stream_dialog.cpp @@ -0,0 +1,1202 @@ +/* follow_stream_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "follow_stream_dialog.h" +#include + +#include "main_application.h" +#include "main_window.h" + +#include "frame_tvbuff.h" +#include "epan/follow.h" +#include "epan/prefs.h" +#include "epan/addr_resolv.h" +#include "epan/charsets.h" +#include "epan/epan_dissect.h" +#include "epan/tap.h" + +#include "ui/alert_box.h" +#include "ui/simple_dialog.h" +#include +#include +#include + +#include "wsutil/file_util.h" +#include "wsutil/str_util.h" + +#include "ws_symbol_export.h" + +#include +#include + +#include "progress_frame.h" + +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// To do: +// - Show text while tapping. +// - Instead of calling QMessageBox, display the error message in the text +// box and disable the appropriate controls. +// - Add a progress bar and connect captureCaptureUpdateContinue to it +// - User's Guide documents the "Raw" type as "same as ASCII, but saving binary +// data". However it currently displays hex-encoded data. + +// Matches SplashOverlay. +static int info_update_freq_ = 100; + +// Handle the loop breaking notification properly +static QMutex loop_break_mutex; + +// Indicates that a Follow Stream is currently running +static gboolean isReadRunning; + +Q_DECLARE_METATYPE(bytes_show_type) + +FollowStreamDialog::FollowStreamDialog(QWidget &parent, CaptureFile &cf, int proto_id) : + WiresharkDialog(parent, cf), + ui(new Ui::FollowStreamDialog), + b_find_(NULL), + follower_(NULL), + truncated_(false), + client_buffer_count_(0), + server_buffer_count_(0), + client_packet_count_(0), + server_packet_count_(0), + last_packet_(0), + last_from_server_(0), + turns_(0), + use_regex_find_(false), + terminating_(false), + previous_sub_stream_num_(0) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 2 / 3, parent.height()); + + ui->streamNumberSpinBox->setStyleSheet("QSpinBox { min-width: 2em; }"); + ui->subStreamNumberSpinBox->setStyleSheet("QSpinBox { min-width: 2em; }"); + ui->streamNumberSpinBox->setKeyboardTracking(false); + ui->subStreamNumberSpinBox->setKeyboardTracking(false); + + follower_ = get_follow_by_proto_id(proto_id); + if (follower_ == NULL) { + ws_assert_not_reached(); + } + + memset(&follow_info_, 0, sizeof(follow_info_)); + follow_info_.show_stream = BOTH_HOSTS; + follow_info_.substream_id = SUBSTREAM_UNUSED; + + ui->teStreamContent->installEventFilter(this); + + connect(ui->leFind, SIGNAL(useRegexFind(bool)), this, SLOT(useRegexFind(bool))); + + QComboBox *cbcs = ui->cbCharset; + cbcs->blockSignals(true); + cbcs->addItem(tr("ASCII"), SHOW_ASCII); + cbcs->addItem(tr("C Arrays"), SHOW_CARRAY); + cbcs->addItem(tr("EBCDIC"), SHOW_EBCDIC); + cbcs->addItem(tr("Hex Dump"), SHOW_HEXDUMP); + cbcs->addItem(tr("Raw"), SHOW_RAW); + // UTF-8 is guaranteed to exist as a QTextCodec + cbcs->addItem(tr("UTF-8"), SHOW_CODEC); + cbcs->addItem(tr("YAML"), SHOW_YAML); + cbcs->setCurrentIndex(cbcs->findData(recent.gui_follow_show)); + cbcs->blockSignals(false); + + b_filter_out_ = ui->buttonBox->addButton(tr("Filter Out This Stream"), QDialogButtonBox::ActionRole); + connect(b_filter_out_, SIGNAL(clicked()), this, SLOT(filterOut())); + + b_print_ = ui->buttonBox->addButton(tr("Print"), QDialogButtonBox::ActionRole); + connect(b_print_, SIGNAL(clicked()), this, SLOT(printStream())); + + b_save_ = ui->buttonBox->addButton(tr("Save as…"), QDialogButtonBox::ActionRole); + connect(b_save_, SIGNAL(clicked()), this, SLOT(saveAs())); + + b_back_ = ui->buttonBox->addButton(tr("Back"), QDialogButtonBox::ActionRole); + connect(b_back_, SIGNAL(clicked()), this, SLOT(backButton())); + + ProgressFrame::addToButtonBox(ui->buttonBox, &parent); + + connect(ui->buttonBox, SIGNAL(helpRequested()), this, SLOT(helpButton())); + connect(ui->teStreamContent, SIGNAL(mouseMovedToTextCursorPosition(int)), + this, SLOT(fillHintLabel(int))); + connect(ui->teStreamContent, SIGNAL(mouseClickedOnTextCursorPosition(int)), + this, SLOT(goToPacketForTextPos(int))); + + fillHintLabel(-1); +} + +FollowStreamDialog::~FollowStreamDialog() +{ + delete ui; + resetStream(); // Frees payload +} + +void FollowStreamDialog::addCodecs(const QMap &codecMap) +{ + // Make the combobox respect max visible items? + //ui->cbCharset->setStyleSheet("QComboBox { combobox-popup: 0;}"); + ui->cbCharset->insertSeparator(ui->cbCharset->count()); + for (const auto &codec : codecMap) { + // This is already in the menu and handled separately + if (codec->name() != "US-ASCII" && codec->name() != "UTF-8") + ui->cbCharset->addItem(tr(codec->name()), SHOW_CODEC); + } +} + +void FollowStreamDialog::printStream() +{ +#ifndef QT_NO_PRINTER + QPrinter printer(QPrinter::HighResolution); + QPrintDialog dialog(&printer, this); + if (dialog.exec() == QDialog::Accepted) + ui->teStreamContent->print(&printer); +#endif +} + +void FollowStreamDialog::fillHintLabel(int text_pos) +{ + QString hint; + int pkt = -1; + + if (text_pos >= 0) { + QMap::iterator it = text_pos_to_packet_.upperBound(text_pos); + if (it != text_pos_to_packet_.end()) { + pkt = it.value(); + } + } + + if (pkt > 0) { + hint = QString(tr("Packet %1. ")).arg(pkt); + } + + hint += tr("%Ln client pkt(s), ", "", client_packet_count_) + .arg(ColorUtils::fromColorT(prefs.st_client_fg).name()) + .arg(ColorUtils::fromColorT(prefs.st_client_bg).name()) + + tr("%Ln server pkt(s), ", "", server_packet_count_) + .arg(ColorUtils::fromColorT(prefs.st_server_fg).name()) + .arg(ColorUtils::fromColorT(prefs.st_server_bg).name()) + + tr("%Ln turn(s).", "", turns_); + + if (pkt > 0) { + hint.append(QString(tr(" Click to select."))); + } + + hint.prepend(""); + hint.append(""); + ui->hintLabel->setText(hint); +} + +void FollowStreamDialog::goToPacketForTextPos(int text_pos) +{ + int pkt = -1; + if (file_closed_) { + return; + } + + if (text_pos >= 0) { + QMap::iterator it = text_pos_to_packet_.upperBound(text_pos); + if (it != text_pos_to_packet_.end()) { + pkt = it.value(); + } + } + + if (pkt > 0) { + emit goToPacket(pkt); + } +} + +void FollowStreamDialog::updateWidgets(bool follow_in_progress) +{ + // XXX: If follow_in_progress set cursor to Qt::BusyCursor or WaitCursor, + // otherwise unset cursor? + bool enable = !follow_in_progress; + if (file_closed_) { + ui->teStreamContent->setEnabled(true); + enable = false; + } + + ui->cbDirections->setEnabled(enable); + ui->cbCharset->setEnabled(enable); + ui->streamNumberSpinBox->setReadOnly(!enable); + if (get_follow_sub_stream_id_func(follower_) != NULL) { + ui->subStreamNumberSpinBox->setReadOnly(!enable); + } + ui->leFind->setEnabled(enable); + ui->bFind->setEnabled(enable); + b_filter_out_->setEnabled(enable); + b_print_->setEnabled(enable); + b_save_->setEnabled(enable); + + WiresharkDialog::updateWidgets(); +} + +void FollowStreamDialog::useRegexFind(bool use_regex) +{ + use_regex_find_ = use_regex; + if (use_regex_find_) + ui->lFind->setText(tr("Regex Find:")); + else + ui->lFind->setText(tr("Find:")); +} + +void FollowStreamDialog::findText(bool go_back) +{ + if (ui->leFind->text().isEmpty()) return; + + bool found; + if (use_regex_find_) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + QRegularExpression regex(ui->leFind->text(), QRegularExpression::UseUnicodePropertiesOption); +#else + QRegExp regex(ui->leFind->text()); +#endif + found = ui->teStreamContent->find(regex); + } else { + found = ui->teStreamContent->find(ui->leFind->text()); + } + + if (found) { + ui->teStreamContent->setFocus(); + } else if (go_back) { + ui->teStreamContent->moveCursor(QTextCursor::Start); + findText(false); + } +} + +void FollowStreamDialog::saveAs() +{ + QString file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Stream Content As…"))); + if (file_name.isEmpty()) { + return; + } + + QFile file(file_name); + if (!file.open(QIODevice::WriteOnly)) { + open_failure_alert_box(file_name.toUtf8().constData(), errno, TRUE); + return; + } + + // Unconditionally save data as UTF-8 (even if data is decoded otherwise). + QByteArray bytes = ui->teStreamContent->toPlainText().toUtf8(); + if (recent.gui_follow_show == SHOW_RAW) { + // The "Raw" format is currently displayed as hex data and needs to be + // converted to binary data. + bytes = QByteArray::fromHex(bytes); + } + + QDataStream out(&file); + out.writeRawData(bytes.constData(), static_cast(bytes.size())); +} + +void FollowStreamDialog::helpButton() +{ + mainApp->helpTopicAction(HELP_FOLLOW_STREAM_DIALOG); +} + +void FollowStreamDialog::backButton() +{ + if (terminating_) + return; + + output_filter_ = previous_filter_; + + close(); +} + +void FollowStreamDialog::filterOut() +{ + if (terminating_) + return; + + output_filter_ = filter_out_filter_; + + close(); +} + +void FollowStreamDialog::close() +{ + terminating_ = true; + + // Update filter - Use: + // previous_filter if 'Close' (passed in follow() method) + // filter_out_filter_ if 'Filter Out This Stream' (built by appending !current_stream to previous_filter) + // leave filter alone if window closed. (current stream) + emit updateFilter(output_filter_, TRUE); + + WiresharkDialog::close(); +} + +void FollowStreamDialog::on_cbDirections_currentIndexChanged(int idx) +{ + switch(idx) + { + case 0 : + follow_info_.show_stream = BOTH_HOSTS; + break; + case 1 : + follow_info_.show_stream = FROM_SERVER; + break; + case 2 : + follow_info_.show_stream = FROM_CLIENT; + break; + default: + return; + } + + readStream(); +} + +void FollowStreamDialog::on_cbCharset_currentIndexChanged(int idx) +{ + if (idx < 0) return; + recent.gui_follow_show = ui->cbCharset->currentData().value(); + readStream(); +} + +void FollowStreamDialog::on_bFind_clicked() +{ + findText(); +} + +void FollowStreamDialog::on_leFind_returnPressed() +{ + findText(); +} + +void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num) +{ + if (file_closed_) return; + + int sub_stream_num = 0; + ui->subStreamNumberSpinBox->blockSignals(true); + sub_stream_num = ui->subStreamNumberSpinBox->value(); + ui->subStreamNumberSpinBox->blockSignals(false); + + gboolean ok; + if (ui->subStreamNumberSpinBox->isVisible()) { + /* We need to find a suitable sub stream for the new stream */ + follow_sub_stream_id_func sub_stream_func; + sub_stream_func = get_follow_sub_stream_id_func(follower_); + + if (sub_stream_func == NULL) { + // Should not happen, this field is only visible for suitable protocols. + return; + } + + guint sub_stream_num_new = static_cast(sub_stream_num); + if (sub_stream_num < 0) { + // Stream ID 0 should always exist as it is used for control messages. + // XXX: That is only guaranteed for HTTP2. For example, in QUIC, + // stream ID 0 is a normal stream used by the first standard client- + // initiated bidirectional stream (if it exists, and it might not) + // and we might have a stream (connection) but only the CRYPTO + // stream, which does not have a (sub) stream ID. + // What should we do if there is a stream with no substream to + // follow? Right now the substream spinbox is left active and + // the user can change the value to no effect. + sub_stream_num_new = 0; + ok = TRUE; + } else { + ok = sub_stream_func(static_cast(stream_num), sub_stream_num_new, FALSE, &sub_stream_num_new); + if (!ok) { + ok = sub_stream_func(static_cast(stream_num), sub_stream_num_new, TRUE, &sub_stream_num_new); + } + } + sub_stream_num = static_cast(sub_stream_num_new); + } else { + /* XXX: For HTTP and TLS, we use the TCP stream index, and really should + * return false if the TCP stream doesn't have HTTP or TLS. (Or we could + * switch to having separate HTTP and TLS stream numbers.) + */ + ok = true; + } + + if (stream_num >= 0 && ok) { + follow(previous_filter_, true, stream_num, sub_stream_num); + previous_sub_stream_num_ = sub_stream_num; + } +} + + +void FollowStreamDialog::on_subStreamNumberSpinBox_valueChanged(int sub_stream_num) +{ + if (file_closed_) return; + + int stream_num = 0; + ui->streamNumberSpinBox->blockSignals(true); + stream_num = ui->streamNumberSpinBox->value(); + ui->streamNumberSpinBox->blockSignals(false); + + follow_sub_stream_id_func sub_stream_func; + sub_stream_func = get_follow_sub_stream_id_func(follower_); + + if (sub_stream_func == NULL) { + // Should not happen, this field is only visible for suitable protocols. + return; + } + + guint sub_stream_num_new = static_cast(sub_stream_num); + gboolean ok; + /* previous_sub_stream_num_ is a hack to track which buttons was pressed without event handling */ + if (sub_stream_num < 0) { + // Stream ID 0 should always exist as it is used for control messages. + // XXX: That is only guaranteed for HTTP2, see above. + sub_stream_num_new = 0; + ok = TRUE; + } else if (previous_sub_stream_num_ < sub_stream_num) { + ok = sub_stream_func(static_cast(stream_num), sub_stream_num_new, FALSE, &sub_stream_num_new); + } else { + ok = sub_stream_func(static_cast(stream_num), sub_stream_num_new, TRUE, &sub_stream_num_new); + } + sub_stream_num = static_cast(sub_stream_num_new); + + if (ok) { + follow(previous_filter_, true, stream_num, sub_stream_num); + previous_sub_stream_num_ = sub_stream_num; + } +} + +void FollowStreamDialog::on_buttonBox_rejected() +{ + // Ignore the close button if FollowStreamDialog::close() is running. + if (terminating_) + return; + + WiresharkDialog::reject(); +} + +void FollowStreamDialog::removeStreamControls() +{ + ui->horizontalLayout->removeItem(ui->streamNumberSpacer); + ui->streamNumberLabel->setVisible(false); + ui->streamNumberSpinBox->setVisible(false); + ui->subStreamNumberLabel->setVisible(false); + ui->subStreamNumberSpinBox->setVisible(false); +} + +void FollowStreamDialog::resetStream() +{ + GList *cur; + follow_record_t *follow_record; + + filter_out_filter_.clear(); + text_pos_to_packet_.clear(); + if (!data_out_filename_.isEmpty()) { + ws_unlink(data_out_filename_.toUtf8().constData()); + } + for (cur = follow_info_.payload; cur; cur = gxx_list_next(cur)) { + follow_record = gxx_list_data(follow_record_t *, cur); + if (follow_record->data) { + g_byte_array_free(follow_record->data, TRUE); + } + g_free(follow_record); + } + g_list_free(follow_info_.payload); + + //Only TCP stream uses fragments + for (cur = follow_info_.fragments[0]; cur; cur = gxx_list_next(cur)) { + follow_record = gxx_list_data(follow_record_t *, cur); + if (follow_record->data) { + g_byte_array_free(follow_record->data, TRUE); + } + g_free(follow_record); + } + follow_info_.fragments[0] = Q_NULLPTR; + for (cur = follow_info_.fragments[1]; cur; cur = gxx_list_next(cur)) { + follow_record = gxx_list_data(follow_record_t *, cur); + if (follow_record->data) { + g_byte_array_free(follow_record->data, TRUE); + } + g_free(follow_record); + } + follow_info_.fragments[1] = Q_NULLPTR; + + free_address(&follow_info_.client_ip); + free_address(&follow_info_.server_ip); + follow_info_.payload = Q_NULLPTR; + follow_info_.client_port = 0; +} + +frs_return_t +FollowStreamDialog::readStream() +{ + + // interrupt any reading already running + loop_break_mutex.lock(); + isReadRunning = FALSE; + loop_break_mutex.unlock(); + + ui->teStreamContent->clear(); + text_pos_to_packet_.clear(); + switch (recent.gui_follow_show) { + + case SHOW_CARRAY: + case SHOW_HEXDUMP: + case SHOW_YAML: + /* We control the width and insert line breaks in these formats. */ + ui->teStreamContent->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + break; + + default: + /* Everything else might have extremely long lines without whitespace, + * (SHOW_RAW almost surely so), and QTextEdit is O(N^2) trying + * to search for word boundaries on long lines when adding text. + */ + ui->teStreamContent->setWordWrapMode(QTextOption::WrapAnywhere); + } + + truncated_ = false; + frs_return_t ret; + + client_buffer_count_ = 0; + server_buffer_count_ = 0; + client_packet_count_ = 0; + server_packet_count_ = 0; + last_packet_ = 0; + turns_ = 0; + + if (follower_) { + ret = readFollowStream(); + } else { + ret = (frs_return_t)0; + ws_assert_not_reached(); + } + + ui->teStreamContent->moveCursor(QTextCursor::Start); + + return ret; +} + +void +FollowStreamDialog::followStream() +{ + readStream(); +} + +const int FollowStreamDialog::max_document_length_ = 500 * 1000 * 1000; // Just a guess +void FollowStreamDialog::addText(QString text, gboolean is_from_server, guint32 packet_num, gboolean colorize) +{ + if (truncated_) { + return; + } + + int char_count = ui->teStreamContent->document()->characterCount(); + if (char_count + text.length() > max_document_length_) { + text.truncate(max_document_length_ - char_count); + truncated_ = true; + } + + setUpdatesEnabled(false); + int cur_pos = ui->teStreamContent->verticalScrollBar()->value(); + ui->teStreamContent->moveCursor(QTextCursor::End); + + QTextCharFormat tcf = ui->teStreamContent->currentCharFormat(); + if (!colorize) { + tcf.setBackground(palette().window().color()); + tcf.setForeground(palette().windowText().color()); + } else if (is_from_server) { + tcf.setForeground(ColorUtils::fromColorT(prefs.st_server_fg)); + tcf.setBackground(ColorUtils::fromColorT(prefs.st_server_bg)); + } else { + tcf.setForeground(ColorUtils::fromColorT(prefs.st_client_fg)); + tcf.setBackground(ColorUtils::fromColorT(prefs.st_client_bg)); + } + ui->teStreamContent->setCurrentCharFormat(tcf); + + ui->teStreamContent->insertPlainText(text); + text_pos_to_packet_[ui->teStreamContent->textCursor().anchor()] = packet_num; + + if (truncated_) { + tcf = ui->teStreamContent->currentCharFormat(); + tcf.setBackground(palette().window().color()); + tcf.setForeground(palette().windowText().color()); + ui->teStreamContent->insertPlainText("\n" + tr("[Stream output truncated]")); + ui->teStreamContent->moveCursor(QTextCursor::End); + } else { + ui->teStreamContent->verticalScrollBar()->setValue(cur_pos); + } + setUpdatesEnabled(true); +} + +// The following keyboard shortcuts should work (although +// they may not work consistently depending on focus): +// / (slash), Ctrl-F - Focus and highlight the search box +// Ctrl-G, Ctrl-N, F3 - Find next +// Should we make it so that typing any text starts searching? +bool FollowStreamDialog::eventFilter(QObject *, QEvent *event) +{ + if (ui->teStreamContent->hasFocus() && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->matches(QKeySequence::SelectAll) || keyEvent->matches(QKeySequence::Copy) + || keyEvent->text().isEmpty()) { + return false; + } + ui->leFind->setFocus(); + if (keyEvent->matches(QKeySequence::Find)) { + return true; + } else if (keyEvent->matches(QKeySequence::FindNext)) { + findText(); + return true; + } + } + + return false; +} + +void FollowStreamDialog::keyPressEvent(QKeyEvent *event) +{ + if (ui->leFind->hasFocus()) { + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + findText(); + return; + } + } else { + if (event->key() == Qt::Key_Slash || event->matches(QKeySequence::Find)) { + ui->leFind->setFocus(); + ui->leFind->selectAll(); + } + return; + } + + if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_N && event->modifiers() & Qt::ControlModifier)) { + findText(); + return; + } + + QDialog::keyPressEvent(event); +} + +static inline void sanitize_buffer(char *buffer, size_t nchars) { + for (size_t i = 0; i < nchars; i++) { + if (buffer[i] == '\n' || buffer[i] == '\r' || buffer[i] == '\t') + continue; + if (! g_ascii_isprint((guchar)buffer[i])) { + buffer[i] = '.'; + } + } +} + +frs_return_t +FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_server, guint32 packet_num, + nstime_t abs_ts, guint32 *global_pos) +{ + gchar initbuf[256]; + guint32 current_pos; + static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + switch (recent.gui_follow_show) { + + case SHOW_EBCDIC: + { + /* If our native arch is ASCII, call: */ + EBCDIC_to_ASCII((guint8*)buffer, (guint) nchars); + sanitize_buffer(buffer, nchars); + QByteArray ba = QByteArray(buffer, (int)nchars); + addText(ba, is_from_server, packet_num); + break; + } + + case SHOW_ASCII: + { + /* If our native arch is EBCDIC, call: + * ASCII_TO_EBCDIC(buffer, nchars); + */ + sanitize_buffer(buffer, nchars); + QByteArray ba = QByteArray(buffer, (int)nchars); + addText(ba, is_from_server, packet_num); + break; + } + + case SHOW_CODEC: + { + // This assumes that multibyte characters don't span packets in the + // stream. To handle that case properly (which might occur with fixed + // block sizes, e.g. transferring over TFTP, we would need to create + // two stateful QTextDecoders, one for each direction, presumably in + // on_cbCharset_currentIndexChanged() + QTextCodec *codec = QTextCodec::codecForName(ui->cbCharset->currentText().toUtf8()); + QByteArray ba = QByteArray(buffer, (int)nchars); + QString decoded = codec->toUnicode(ba); + addText(decoded, is_from_server, packet_num); + break; + } + + case SHOW_HEXDUMP: + current_pos = 0; + while (current_pos < nchars) { + gchar hexbuf[256]; + int i; + gchar *cur = hexbuf, *ascii_start; + + /* is_from_server indentation : put 4 spaces at the + * beginning of the string */ + /* XXX - We might want to prepend each line with "C" or "S" instead. */ + if (is_from_server && follow_info_.show_stream == BOTH_HOSTS) { + memset(cur, ' ', 4); + cur += 4; + } + cur += snprintf(cur, 20, "%08X ", *global_pos); + /* 49 is space consumed by hex chars */ + ascii_start = cur + 49 + 2; + for (i = 0; i < 16 && current_pos + i < nchars; i++) { + *cur++ = + hexchars[(buffer[current_pos + i] & 0xf0) >> 4]; + *cur++ = + hexchars[buffer[current_pos + i] & 0x0f]; + *cur++ = ' '; + if (i == 7) + *cur++ = ' '; + } + /* Fill it up if column isn't complete */ + while (cur < ascii_start) + *cur++ = ' '; + + /* Now dump bytes as text */ + for (i = 0; i < 16 && current_pos + i < nchars; i++) { + *cur++ = + (g_ascii_isprint((guchar)buffer[current_pos + i]) ? + buffer[current_pos + i] : '.'); + if (i == 7) { + *cur++ = ' '; + } + } + current_pos += i; + (*global_pos) += i; + *cur++ = '\n'; + *cur = 0; + + addText(hexbuf, is_from_server, packet_num); + } + break; + + case SHOW_CARRAY: + current_pos = 0; + snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = { /* Packet %u */\n", + is_from_server ? 1 : 0, + is_from_server ? server_buffer_count_++ : client_buffer_count_++, + packet_num); + addText(initbuf, is_from_server, packet_num); + + while (current_pos < nchars) { + gchar hexbuf[256]; + int i, cur; + + cur = 0; + for (i = 0; i < 8 && current_pos + i < nchars; i++) { + /* Prepend entries with "0x" */ + hexbuf[cur++] = '0'; + hexbuf[cur++] = 'x'; + hexbuf[cur++] = + hexchars[(buffer[current_pos + i] & 0xf0) >> 4]; + hexbuf[cur++] = + hexchars[buffer[current_pos + i] & 0x0f]; + + /* Delimit array entries with a comma */ + if (current_pos + i + 1 < nchars) + hexbuf[cur++] = ','; + + hexbuf[cur++] = ' '; + } + + /* Terminate the array if we are at the end */ + if (current_pos + i == nchars) { + hexbuf[cur++] = '}'; + hexbuf[cur++] = ';'; + } + + current_pos += i; + (*global_pos) += i; + hexbuf[cur++] = '\n'; + hexbuf[cur] = 0; + addText(hexbuf, is_from_server, packet_num); + } + break; + + case SHOW_YAML: + { + QString yaml_text; + + const int base64_raw_len = 57; // Encodes to 76 bytes, common in RFCs + current_pos = 0; + + if (last_packet_ == 0) { + // Header with general info about peers + const char *hostname0 = address_to_name(&follow_info_.client_ip); + const char *hostname1 = address_to_name(&follow_info_.server_ip); + + char *port0 = get_follow_port_to_display(follower_)(NULL, follow_info_.client_port); + char *port1 = get_follow_port_to_display(follower_)(NULL, follow_info_.server_port); + + addText("peers:\n", false, 0, false); + + addText(QString( + " - peer: 0\n" + " host: %1\n" + " port: %2\n") + .arg(hostname0) + .arg(port0), false, 0); + + addText(QString( + " - peer: 1\n" + " host: %1\n" + " port: %2\n") + .arg(hostname1) + .arg(port1), true, 0); + + wmem_free(NULL, port0); + wmem_free(NULL, port1); + + addText("packets:\n", false, 0, false); + } + + if (packet_num != last_packet_) { + yaml_text.append(QString(" - packet: %1\n") + .arg(packet_num)); + yaml_text.append(QString(" peer: %1\n") + .arg(is_from_server ? 1 : 0)); + yaml_text.append(QString(" index: %1\n") + .arg(is_from_server ? server_buffer_count_++ : client_buffer_count_++)); + yaml_text.append(QString(" timestamp: %1.%2\n") + .arg(abs_ts.secs) + .arg(abs_ts.nsecs, 9, 10, QChar('0'))); + yaml_text.append(QString(" data: !!binary |\n")); + } + while (current_pos < nchars) { + int len = current_pos + base64_raw_len < nchars ? base64_raw_len : (int) nchars - current_pos; + QByteArray base64_data(&buffer[current_pos], len); + + /* XXX: GCC 12.1 has a bogus stringop-overread warning using the Qt + * conversions from QByteArray to QString at -O2 and higher due to + * computing a branch that will never be taken. + */ +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_OFF(stringop-overread) +#endif + yaml_text += " " + base64_data.toBase64() + "\n"; +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_ON(stringop-overread) +#endif + + current_pos += len; + (*global_pos) += len; + } + addText(yaml_text, is_from_server, packet_num); + break; + } + + case SHOW_RAW: + { + QByteArray ba = QByteArray(buffer, (int)nchars).toHex(); + ba += '\n'; + addText(ba, is_from_server, packet_num); + break; + } + + default: + /* The other Show types are supported in Show Packet Bytes but + * not here in Follow. (XXX: Maybe some could be added?) + */ + ws_assert_not_reached(); + } + + if (last_packet_ == 0) { + last_from_server_ = is_from_server; + } + + if (packet_num != last_packet_) { + last_packet_ = packet_num; + if (is_from_server) { + server_packet_count_++; + } else { + client_packet_count_++; + } + if (last_from_server_ != is_from_server) { + last_from_server_ = is_from_server; + turns_++; + } + } + + return FRS_OK; +} + +bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, guint stream_num, guint sub_stream_num) +{ + QString follow_filter; + const char *hostname0 = NULL, *hostname1 = NULL; + char *port0 = NULL, *port1 = NULL; + QString server_to_client_string; + QString client_to_server_string; + QString both_directions_string; + gboolean is_follower = FALSE; + int stream_count; + follow_stream_count_func stream_count_func = NULL; + + resetStream(); + + if (file_closed_) + { + QMessageBox::warning(this, tr("No capture file."), tr("Please make sure you have a capture file opened.")); + return false; + } + + if (!use_stream_index) { + if (cap_file_.capFile()->edt == NULL) + { + QMessageBox::warning(this, tr("Error following stream."), tr("Capture file invalid.")); + return false; + } + is_follower = proto_is_frame_protocol(cap_file_.capFile()->edt->pi.layers, proto_get_protocol_filter_name(get_follow_proto_id(follower_))); + if (!is_follower) { + QMessageBox::warning(this, tr("Error following stream."), tr("Please make sure you have a %1 packet selected.").arg + (proto_get_protocol_short_name(find_protocol_by_id(get_follow_proto_id(follower_))))); + return false; + } + } + + follow_reset_stream(&follow_info_); + + /* Create a new filter that matches all packets in the TCP stream, + and set the display filter entry accordingly */ + if (use_stream_index) { + follow_filter = gchar_free_to_qstring(get_follow_index_func(follower_)(stream_num, sub_stream_num)); + } else { + follow_filter = gchar_free_to_qstring(get_follow_conv_func(follower_)(cap_file_.capFile()->edt, &cap_file_.capFile()->edt->pi, &stream_num, &sub_stream_num)); + } + if (follow_filter.isEmpty()) { + // XXX: This error probably has to do with tunneling (#18231), where + // the addresses or ports changed after the TCP or UDP layer. + // (The appropriate layer must be present, or else the GUI + // doesn't allow the option to be selected.) + QMessageBox::warning(this, + tr("Error creating filter for this stream."), + tr("%1 stream not found on the selected packet.").arg(proto_get_protocol_short_name(find_protocol_by_id(get_follow_proto_id(follower_))))); + return false; + } + + previous_filter_ = previous_filter; + /* append the negation */ + if (!previous_filter.isEmpty()) { + filter_out_filter_ = QString("%1 and !(%2)") + .arg(previous_filter).arg(follow_filter); + } + else + { + filter_out_filter_ = QString("!(%1)").arg(follow_filter); + } + + follow_info_.substream_id = sub_stream_num; + + /* data will be passed via tap callback*/ + if (!registerTapListener(get_follow_tap_string(follower_), &follow_info_, + follow_filter.toUtf8().constData(), + 0, NULL, get_follow_tap_handler(follower_), NULL)) { + return false; + } + + stream_count_func = get_follow_stream_count_func(follower_); + + if (stream_count_func == NULL) { + removeStreamControls(); + } else { + stream_count = stream_count_func(); + ui->streamNumberSpinBox->blockSignals(true); + ui->streamNumberSpinBox->setMaximum(stream_count-1); + ui->streamNumberSpinBox->setValue(stream_num); + ui->streamNumberSpinBox->blockSignals(false); + ui->streamNumberSpinBox->setToolTip(tr("%Ln total stream(s).", "", stream_count)); + ui->streamNumberLabel->setToolTip(ui->streamNumberSpinBox->toolTip()); + } + + follow_sub_stream_id_func sub_stream_func; + sub_stream_func = get_follow_sub_stream_id_func(follower_); + if (sub_stream_func != NULL) { + guint substream_max_id = 0; + sub_stream_func(static_cast(stream_num), G_MAXINT32, TRUE, &substream_max_id); + stream_count = static_cast(substream_max_id); + ui->subStreamNumberSpinBox->blockSignals(true); + ui->subStreamNumberSpinBox->setEnabled(true); + ui->subStreamNumberSpinBox->setMaximum(stream_count); + ui->subStreamNumberSpinBox->setValue(sub_stream_num); + ui->subStreamNumberSpinBox->blockSignals(false); + ui->subStreamNumberSpinBox->setToolTip(tr("Max sub stream ID for the selected stream: %Ln", "", stream_count)); + ui->subStreamNumberSpinBox->setToolTip(ui->subStreamNumberSpinBox->toolTip()); + ui->subStreamNumberSpinBox->setVisible(true); + ui->subStreamNumberLabel->setVisible(true); + } else { + /* disable substream spin box for protocols without substreams */ + ui->subStreamNumberSpinBox->blockSignals(true); + ui->subStreamNumberSpinBox->setEnabled(false); + ui->subStreamNumberSpinBox->setValue(0); + ui->subStreamNumberSpinBox->setKeyboardTracking(false); + ui->subStreamNumberSpinBox->blockSignals(false); + ui->subStreamNumberSpinBox->setVisible(false); + ui->subStreamNumberLabel->setVisible(false); + } + + beginRetapPackets(); + updateWidgets(true); + + /* Run the display filter so it goes in effect - even if it's the + same as the previous display filter. */ + emit updateFilter(follow_filter, TRUE); + + removeTapListeners(); + + hostname0 = address_to_name(&follow_info_.client_ip); + hostname1 = address_to_name(&follow_info_.server_ip); + + port0 = get_follow_port_to_display(follower_)(NULL, follow_info_.client_port); + port1 = get_follow_port_to_display(follower_)(NULL, follow_info_.server_port); + + server_to_client_string = + QString("%1:%2 %3 %4:%5 (%6)") + .arg(hostname0).arg(port0) + .arg(UTF8_RIGHTWARDS_ARROW) + .arg(hostname1).arg(port1) + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[0], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + + client_to_server_string = + QString("%1:%2 %3 %4:%5 (%6)") + .arg(hostname1).arg(port1) + .arg(UTF8_RIGHTWARDS_ARROW) + .arg(hostname0).arg(port0) + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[1], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + + wmem_free(NULL, port0); + wmem_free(NULL, port1); + + both_directions_string = tr("Entire conversation (%1)") + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[0] + follow_info_.bytes_written[1], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + setWindowSubtitle(tr("Follow %1 Stream (%2)").arg(proto_get_protocol_short_name(find_protocol_by_id(get_follow_proto_id(follower_)))) + .arg(follow_filter)); + + ui->cbDirections->blockSignals(true); + ui->cbDirections->clear(); + ui->cbDirections->addItem(both_directions_string); + ui->cbDirections->addItem(client_to_server_string); + ui->cbDirections->addItem(server_to_client_string); + ui->cbDirections->blockSignals(false); + + followStream(); + fillHintLabel(-1); + + updateWidgets(false); + endRetapPackets(); + + if (prefs.restore_filter_after_following_stream) { + emit updateFilter(previous_filter_, TRUE); + } + + return true; +} + +void FollowStreamDialog::captureFileClosed() +{ + QString tooltip = tr("File closed."); + ui->streamNumberSpinBox->setToolTip(tooltip); + ui->streamNumberLabel->setToolTip(tooltip); + WiresharkDialog::captureFileClosed(); +} + +/* + * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines, + * it gets handed bufferfuls. That's fine for "follow_write_raw()" + * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls + * the "print_line()" routine from "print.c", and as that routine might + * genuinely expect to be handed a line (if, for example, it's using + * some OS or desktop environment's printing API, and that API expects + * to be handed lines), "follow_print_text()" should probably accumulate + * lines in a buffer and hand them "print_line()". (If there's a + * complete line in a buffer - i.e., there's nothing of the line in + * the previous buffer or the next buffer - it can just hand that to + * "print_line()" after filtering out non-printables, as an + * optimization.) + * + * This might or might not be the reason why C arrays display + * correctly but get extra blank lines very other line when printed. + */ +frs_return_t +FollowStreamDialog::readFollowStream() +{ + guint32 global_client_pos = 0, global_server_pos = 0; + guint32 *global_pos; + gboolean skip; + GList* cur; + frs_return_t frs_return; + follow_record_t *follow_record; + QElapsedTimer elapsed_timer; + + elapsed_timer.start(); + + loop_break_mutex.lock(); + isReadRunning = TRUE; + loop_break_mutex.unlock(); + + for (cur = g_list_last(follow_info_.payload); cur; cur = g_list_previous(cur)) { + if (dialogClosed() || !isReadRunning) break; + + follow_record = (follow_record_t *)cur->data; + skip = FALSE; + if (!follow_record->is_server) { + global_pos = &global_client_pos; + if (follow_info_.show_stream == FROM_SERVER) { + skip = TRUE; + } + } else { + global_pos = &global_server_pos; + if (follow_info_.show_stream == FROM_CLIENT) { + skip = TRUE; + } + } + + QByteArray buffer; + if (!skip) { + // We want a deep copy. + buffer.clear(); + buffer.append((const char *) follow_record->data->data, + follow_record->data->len); + frs_return = showBuffer( + buffer.data(), + follow_record->data->len, + follow_record->is_server, + follow_record->packet_num, + follow_record->abs_ts, + global_pos); + if (frs_return == FRS_PRINT_ERROR) + return frs_return; + if (elapsed_timer.elapsed() > info_update_freq_) { + fillHintLabel(ui->teStreamContent->textCursor().position()); + mainApp->processEvents(); + elapsed_timer.start(); + } + } + } + + loop_break_mutex.lock(); + isReadRunning = FALSE; + loop_break_mutex.unlock(); + + return FRS_OK; +} diff --git a/ui/qt/follow_stream_dialog.h b/ui/qt/follow_stream_dialog.h new file mode 100644 index 00000000..e56023de --- /dev/null +++ b/ui/qt/follow_stream_dialog.h @@ -0,0 +1,128 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FOLLOW_STREAM_DIALOG_H +#define FOLLOW_STREAM_DIALOG_H + +#include + +#include + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "file.h" + +#include "epan/follow.h" + +#include "wireshark_dialog.h" + +#include +#include +#include +#include + +namespace Ui { +class FollowStreamDialog; +} + +class FollowStreamDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit FollowStreamDialog(QWidget &parent, CaptureFile &cf, int proto_id); + ~FollowStreamDialog(); + + void addCodecs(const QMap &codecMap); + bool follow(QString previous_filter = QString(), bool use_stream_index = false, guint stream_num = 0, guint sub_stream_num = 0); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + void keyPressEvent(QKeyEvent *event); + void captureFileClosed(); + +private slots: + void on_cbCharset_currentIndexChanged(int idx); + void on_cbDirections_currentIndexChanged(int idx); + void on_bFind_clicked(); + void on_leFind_returnPressed(); + + void helpButton(); + void backButton(); + void close(); + void filterOut(); + void useRegexFind(bool use_regex); + void findText(bool go_back = true); + void saveAs(); + void printStream(); + void fillHintLabel(int text_pos); + void goToPacketForTextPos(int text_pos); + + void on_streamNumberSpinBox_valueChanged(int stream_num); + void on_subStreamNumberSpinBox_valueChanged(int sub_stream_num); + + void on_buttonBox_rejected(); + +signals: + void updateFilter(QString filter, bool force); + void goToPacket(int packet_num); + +private: + void removeStreamControls(); + void resetStream(void); + void updateWidgets(bool follow_in_progress); + void updateWidgets() { updateWidgets(false); } // Needed for WiresharkDialog? + frs_return_t + showBuffer(char *buffer, size_t nchars, gboolean is_from_server, + guint32 packet_num, nstime_t abs_ts, guint32 *global_pos); + + frs_return_t readStream(); + frs_return_t readFollowStream(); + frs_return_t readSslStream(); + + void followStream(); + void addText(QString text, gboolean is_from_server, guint32 packet_num, gboolean colorize = true); + + Ui::FollowStreamDialog *ui; + + QPushButton *b_filter_out_; + QPushButton *b_find_; + QPushButton *b_print_; + QPushButton *b_save_; + QPushButton *b_back_; + + follow_info_t follow_info_; + register_follow_t* follower_; + QString data_out_filename_; + static const int max_document_length_; + bool truncated_; + QString previous_filter_; + QString filter_out_filter_; + QString output_filter_; + int client_buffer_count_; + int server_buffer_count_; + int client_packet_count_; + int server_packet_count_; + guint32 last_packet_; + gboolean last_from_server_; + int turns_; + QMap text_pos_to_packet_; + + bool use_regex_find_; + + bool terminating_; + + int previous_sub_stream_num_; +}; + +#endif // FOLLOW_STREAM_DIALOG_H diff --git a/ui/qt/follow_stream_dialog.ui b/ui/qt/follow_stream_dialog.ui new file mode 100644 index 00000000..3b4a76a4 --- /dev/null +++ b/ui/qt/follow_stream_dialog.ui @@ -0,0 +1,158 @@ + + + FollowStreamDialog + + + + 0 + 0 + 609 + 600 + + + + + 0 + 0 + + + + Follow Stream + + + true + + + + + + true + + + + + + + Hint. + + + true + + + + + + + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show data as + + + + + + + -1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Stream + + + + + + + + + + Substream + + + + + + + + + + + + + + Find: + + + + + + + + + + Find &Next + + + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + + FollowStreamText + QTextEdit +
widgets/follow_stream_text.h
+
+ + FindLineEdit + QLineEdit +
widgets/find_line_edit.h
+
+
+ + +
diff --git a/ui/qt/font_color_preferences_frame.cpp b/ui/qt/font_color_preferences_frame.cpp new file mode 100644 index 00000000..0a09e0f4 --- /dev/null +++ b/ui/qt/font_color_preferences_frame.cpp @@ -0,0 +1,414 @@ +/* font_color_preferences_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include + +#include + +#include "font_color_preferences_frame.h" +#include +#include +#include +#include "main_application.h" + +#include +#include +#include + +#include + +//: These are pangrams. Feel free to replace with nonsense text that spans your alphabet. +//: https://en.wikipedia.org/wiki/Pangram +static const char *font_pangrams_[] = { + QT_TRANSLATE_NOOP("FontColorPreferencesFrame", "Example GIF query packets have jumbo window sizes"), + QT_TRANSLATE_NOOP("FontColorPreferencesFrame", "Lazy badgers move unique waxy jellyfish packets") +}; +const int num_font_pangrams_ = (sizeof font_pangrams_ / sizeof font_pangrams_[0]); + +FontColorPreferencesFrame::FontColorPreferencesFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::FontColorPreferencesFrame) +{ + ui->setupUi(this); + + pref_qt_gui_font_name_ = prefFromPrefPtr(&prefs.gui_font_name); + pref_active_fg_ = prefFromPrefPtr(&prefs.gui_active_fg); + pref_active_bg_ = prefFromPrefPtr(&prefs.gui_active_bg); + pref_active_style_ = prefFromPrefPtr(&prefs.gui_active_style); + pref_inactive_fg_ = prefFromPrefPtr(&prefs.gui_inactive_fg); + pref_inactive_bg_ = prefFromPrefPtr(&prefs.gui_inactive_bg); + pref_inactive_style_ = prefFromPrefPtr(&prefs.gui_inactive_style); + pref_marked_fg_ = prefFromPrefPtr(&prefs.gui_marked_fg); + pref_marked_bg_ = prefFromPrefPtr(&prefs.gui_marked_bg); + pref_ignored_fg_ = prefFromPrefPtr(&prefs.gui_ignored_fg); + pref_ignored_bg_ = prefFromPrefPtr(&prefs.gui_ignored_bg); + pref_client_fg_ = prefFromPrefPtr(&prefs.st_client_fg); + pref_client_bg_ = prefFromPrefPtr(&prefs.st_client_bg); + pref_server_fg_ = prefFromPrefPtr(&prefs.st_server_fg); + pref_server_bg_ = prefFromPrefPtr(&prefs.st_server_bg); + pref_valid_bg_ = prefFromPrefPtr(&prefs.gui_text_valid); + pref_invalid_bg_ = prefFromPrefPtr(&prefs.gui_text_invalid); + pref_deprecated_bg_ = prefFromPrefPtr(&prefs.gui_text_deprecated); + + cur_font_.fromString(prefs_get_string_value(pref_qt_gui_font_name_, pref_stashed)); + +} + +FontColorPreferencesFrame::~FontColorPreferencesFrame() +{ + delete ui; +} + +void FontColorPreferencesFrame::showEvent(QShowEvent *) +{ + GRand *rand_state = g_rand_new(); + QString pangram = QString(font_pangrams_[g_rand_int_range(rand_state, 0, num_font_pangrams_)]) + " 0123456789"; + ui->fontSampleLineEdit->setText(pangram); + ui->fontSampleLineEdit->setCursorPosition(0); + ui->fontSampleLineEdit->setMinimumWidth(mainApp->monospaceTextSize(pangram.toUtf8().constData()) + mainApp->monospaceTextSize(" ")); + g_rand_free(rand_state); + + updateWidgets(); +} + +void FontColorPreferencesFrame::updateWidgets() +{ + gint colorstyle; + QColor foreground; + QColor background1; + QColor background2; + QPalette default_pal; + + int margin = style()->pixelMetric(QStyle::PM_LayoutLeftMargin); + + ui->fontPushButton->setText( + cur_font_.family() + " " + cur_font_.styleName() + " " + + QString::number(cur_font_.pointSizeF(), 'f', 1)); + ui->fontSampleLineEdit->setFont(cur_font_); + + QString line_edit_ss = QString("QLineEdit { margin-left: %1px; }").arg(margin); + ui->fontSampleLineEdit->setStyleSheet(line_edit_ss); + + QString color_button_ss = + "QPushButton {" + " border: 1px solid palette(Dark);" + " background-color: %1;" + " margin-left: %2px;" + "}"; + QString sample_text_ss = + "QLineEdit {" + " color: %1;" + " background-color: %2;" + "}"; + QString sample_text_ex_ss = + "QLineEdit {" + " color: %1;" + " background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1 stop: 0 %3, stop: 0.5 %2, stop: 1 %3);" + "}"; + + // + // Sample active selected item + // + colorstyle = prefs_get_enum_value(pref_active_style_, pref_stashed); + + // Make foreground and background colors + switch (colorstyle) + { + case COLOR_STYLE_DEFAULT: + default_pal = QApplication::palette(); + default_pal.setCurrentColorGroup(QPalette::Active); + + foreground = default_pal.highlightedText().color(); + background1 = default_pal.highlight().color(); + background2 = default_pal.highlight().color(); + break; + + case COLOR_STYLE_FLAT: + foreground = ColorUtils::fromColorT(prefs_get_color_value(pref_active_fg_, pref_stashed)); + background1 = ColorUtils::fromColorT(prefs_get_color_value(pref_active_bg_, pref_stashed)); + background2 = ColorUtils::fromColorT(prefs_get_color_value(pref_active_bg_, pref_stashed)); + break; + + case COLOR_STYLE_GRADIENT: + foreground = ColorUtils::fromColorT(prefs_get_color_value(pref_active_fg_, pref_stashed)); + background1 = ColorUtils::fromColorT(prefs_get_color_value(pref_active_bg_, pref_stashed)); + background2 = QColor::fromRgb(ColorUtils::alphaBlend(foreground, background1, COLOR_STYLE_ALPHA)); + break; + } + + ui->activeFGPushButton->setStyleSheet(color_button_ss.arg(foreground.name()).arg(margin)); + ui->activeBGPushButton->setStyleSheet(color_button_ss.arg(background1.name()).arg(0)); + ui->activeSampleLineEdit->setStyleSheet(sample_text_ex_ss.arg( + foreground.name(), + background1.name(), + background2.name())); + ui->activeSampleLineEdit->setFont(cur_font_); + ui->activeStyleComboBox->setCurrentIndex(prefs_get_enum_value(pref_active_style_, pref_stashed)); + + // Show or hide the widgets + ui->activeFGPushButton->setVisible(colorstyle != COLOR_STYLE_DEFAULT); + ui->activeBGPushButton->setVisible(colorstyle != COLOR_STYLE_DEFAULT); + + // + // Sample inactive selected item + // + colorstyle = prefs_get_enum_value(pref_inactive_style_, pref_stashed); + + // Make foreground and background colors + switch (colorstyle) + { + case COLOR_STYLE_DEFAULT: + default_pal = QApplication::palette(); + default_pal.setCurrentColorGroup(QPalette::Inactive); + + foreground = default_pal.highlightedText().color(); + background1 = default_pal.highlight().color(); + background2 = default_pal.highlight().color(); + break; + + case COLOR_STYLE_FLAT: + foreground = ColorUtils::fromColorT(prefs_get_color_value(pref_inactive_fg_, pref_stashed)); + background1 = ColorUtils::fromColorT(prefs_get_color_value(pref_inactive_bg_, pref_stashed)); + background2 = ColorUtils::fromColorT(prefs_get_color_value(pref_inactive_bg_, pref_stashed)); + break; + + case COLOR_STYLE_GRADIENT: + foreground = ColorUtils::fromColorT(prefs_get_color_value(pref_inactive_fg_, pref_stashed)); + background1 = ColorUtils::fromColorT(prefs_get_color_value(pref_inactive_bg_, pref_stashed)); + background2 = QColor::fromRgb(ColorUtils::alphaBlend(foreground, background1, COLOR_STYLE_ALPHA)); + break; + } + + ui->inactiveFGPushButton->setStyleSheet(color_button_ss.arg(foreground.name()).arg(margin)); + ui->inactiveBGPushButton->setStyleSheet(color_button_ss.arg(background1.name()).arg(0)); + ui->inactiveSampleLineEdit->setStyleSheet(sample_text_ex_ss.arg( + foreground.name(), + background1.name(), + background2.name())); + ui->inactiveSampleLineEdit->setFont(cur_font_); + ui->inactiveStyleComboBox->setCurrentIndex(prefs_get_enum_value(pref_inactive_style_, pref_stashed)); + + // Show or hide the widgets + ui->inactiveFGPushButton->setVisible(colorstyle != COLOR_STYLE_DEFAULT); + ui->inactiveBGPushButton->setVisible(colorstyle != COLOR_STYLE_DEFAULT); + + // + // Sample marked packet text + // + ui->markedFGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_marked_fg_, pref_stashed)).name()) + .arg(margin)); + ui->markedBGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_marked_bg_, pref_stashed)).name()) + .arg(0)); + ui->markedSampleLineEdit->setStyleSheet(sample_text_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_marked_fg_, pref_stashed)).name(), + ColorUtils::fromColorT(prefs_get_color_value(pref_marked_bg_, pref_stashed)).name())); + ui->markedSampleLineEdit->setFont(cur_font_); + + // + // Sample ignored packet text + // + ui->ignoredFGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_ignored_fg_, pref_stashed)).name()) + .arg(margin)); + ui->ignoredBGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_ignored_bg_, pref_stashed)).name()) + .arg(0)); + ui->ignoredSampleLineEdit->setStyleSheet(sample_text_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_ignored_fg_, pref_stashed)).name(), + ColorUtils::fromColorT(prefs_get_color_value(pref_ignored_bg_, pref_stashed)).name())); + ui->ignoredSampleLineEdit->setFont(cur_font_); + + // + // Sample "Follow Stream" client text + // + ui->clientFGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_client_fg_, pref_stashed)).name()) + .arg(margin)); + ui->clientBGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_client_bg_, pref_stashed)).name()) + .arg(0)); + ui->clientSampleLineEdit->setStyleSheet(sample_text_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_client_fg_, pref_stashed)).name(), + ColorUtils::fromColorT(prefs_get_color_value(pref_client_bg_, pref_stashed)).name())); + ui->clientSampleLineEdit->setFont(cur_font_); + + // + // Sample "Follow Stream" server text + // + ui->serverFGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_server_fg_, pref_stashed)).name()) + .arg(margin)); + ui->serverBGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_server_bg_, pref_stashed)).name()) + .arg(0)); + ui->serverSampleLineEdit->setStyleSheet(sample_text_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_server_fg_, pref_stashed)).name(), + ColorUtils::fromColorT(prefs_get_color_value(pref_server_bg_, pref_stashed)).name())); + ui->serverSampleLineEdit->setFont(cur_font_); + + // + // Sample valid filter + // + QColor ss_bg = ColorUtils::fromColorT(prefs_get_color_value(pref_valid_bg_, pref_stashed)); + ui->validFilterBGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_valid_bg_, pref_stashed)).name()) + .arg(0)); + ui->validFilterSampleLineEdit->setStyleSheet(sample_text_ss.arg( + ColorUtils::contrastingTextColor(ss_bg).name(), + ss_bg.name())); + + // + // Sample invalid filter + // + ss_bg = ColorUtils::fromColorT(prefs_get_color_value(pref_invalid_bg_, pref_stashed)); + ui->invalidFilterBGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_invalid_bg_, pref_stashed)).name()) + .arg(0)); + ui->invalidFilterSampleLineEdit->setStyleSheet(sample_text_ss.arg( + ColorUtils::contrastingTextColor(ss_bg).name(), + ss_bg.name())); + + // + // Sample warning filter + // + ss_bg = ColorUtils::fromColorT(prefs_get_color_value(pref_deprecated_bg_, pref_stashed)); + ui->deprecatedFilterBGPushButton->setStyleSheet(color_button_ss.arg( + ColorUtils::fromColorT(prefs_get_color_value(pref_deprecated_bg_, pref_stashed)).name()) + .arg(0)); + ui->deprecatedFilterSampleLineEdit->setStyleSheet(sample_text_ss.arg( + ColorUtils::contrastingTextColor(ss_bg).name(), + ss_bg.name())); +} + +void FontColorPreferencesFrame::changeColor(pref_t *pref) +{ + QColorDialog *color_dlg = new QColorDialog(); + color_t* color = prefs_get_color_value(pref, pref_stashed); + + color_dlg->setCurrentColor(QColor( + color->red >> 8, + color->green >> 8, + color->blue >> 8 + )); + + connect(color_dlg, &QColorDialog::colorSelected, std::bind(&FontColorPreferencesFrame::colorChanged, this, pref, std::placeholders::_1)); + color_dlg->setWindowModality(Qt::ApplicationModal); + color_dlg->setAttribute(Qt::WA_DeleteOnClose); + color_dlg->show(); +} + +void FontColorPreferencesFrame::colorChanged(pref_t *pref, const QColor &cc) +{ + color_t new_color; + new_color.red = cc.red() << 8 | cc.red(); + new_color.green = cc.green() << 8 | cc.green(); + new_color.blue = cc.blue() << 8 | cc.blue(); + prefs_set_color_value(pref, new_color, pref_stashed); + updateWidgets(); +} + +void FontColorPreferencesFrame::on_fontPushButton_clicked() +{ + bool ok; + QFont new_font = QFontDialog::getFont(&ok, cur_font_, this, mainApp->windowTitleString(tr("Font"))); + if (ok) { + prefs_set_string_value(pref_qt_gui_font_name_, new_font.toString().toStdString().c_str(), pref_stashed); + cur_font_ = new_font; + updateWidgets(); + } +} + +void FontColorPreferencesFrame::on_activeFGPushButton_clicked() +{ + changeColor(pref_active_fg_); +} + +void FontColorPreferencesFrame::on_activeBGPushButton_clicked() +{ + changeColor(pref_active_bg_); +} + +void FontColorPreferencesFrame::on_activeStyleComboBox_currentIndexChanged(int index) +{ + prefs_set_enum_value(pref_active_style_, index, pref_stashed); + updateWidgets(); +} + + +void FontColorPreferencesFrame::on_inactiveFGPushButton_clicked() +{ + changeColor(pref_inactive_fg_); +} + +void FontColorPreferencesFrame::on_inactiveBGPushButton_clicked() +{ + changeColor(pref_inactive_bg_); +} + +void FontColorPreferencesFrame::on_inactiveStyleComboBox_currentIndexChanged(int index) +{ + prefs_set_enum_value(pref_inactive_style_, index, pref_stashed); + updateWidgets(); +} + +void FontColorPreferencesFrame::on_markedFGPushButton_clicked() +{ + changeColor(pref_marked_fg_); +} + +void FontColorPreferencesFrame::on_markedBGPushButton_clicked() +{ + changeColor(pref_marked_bg_); +} + +void FontColorPreferencesFrame::on_ignoredFGPushButton_clicked() +{ + changeColor(pref_ignored_fg_); +} + +void FontColorPreferencesFrame::on_ignoredBGPushButton_clicked() +{ + changeColor(pref_ignored_bg_); +} + +void FontColorPreferencesFrame::on_clientFGPushButton_clicked() +{ + changeColor(pref_client_fg_); +} + +void FontColorPreferencesFrame::on_clientBGPushButton_clicked() +{ + changeColor(pref_client_bg_); +} + +void FontColorPreferencesFrame::on_serverFGPushButton_clicked() +{ + changeColor(pref_server_fg_); +} + +void FontColorPreferencesFrame::on_serverBGPushButton_clicked() +{ + changeColor(pref_server_bg_); +} + +void FontColorPreferencesFrame::on_validFilterBGPushButton_clicked() +{ + changeColor(pref_valid_bg_); +} + +void FontColorPreferencesFrame::on_invalidFilterBGPushButton_clicked() +{ + changeColor(pref_invalid_bg_); +} + +void FontColorPreferencesFrame::on_deprecatedFilterBGPushButton_clicked() +{ + changeColor(pref_deprecated_bg_); +} diff --git a/ui/qt/font_color_preferences_frame.h b/ui/qt/font_color_preferences_frame.h new file mode 100644 index 00000000..052df9ab --- /dev/null +++ b/ui/qt/font_color_preferences_frame.h @@ -0,0 +1,82 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FONT_COLOR_PREFERENCES_FRAME_H +#define FONT_COLOR_PREFERENCES_FRAME_H + +#include +#include + +#include + +namespace Ui { +class FontColorPreferencesFrame; +} + +class FontColorPreferencesFrame : public QFrame +{ + Q_OBJECT + +public: + explicit FontColorPreferencesFrame(QWidget *parent = 0); + ~FontColorPreferencesFrame(); + +protected: + void showEvent(QShowEvent *evt); + +private: + Ui::FontColorPreferencesFrame *ui; + + pref_t *pref_qt_gui_font_name_; + pref_t *pref_active_fg_; + pref_t *pref_active_bg_; + pref_t *pref_active_style_; + pref_t *pref_inactive_fg_; + pref_t *pref_inactive_bg_; + pref_t *pref_inactive_style_; + pref_t *pref_marked_fg_; + pref_t *pref_marked_bg_; + pref_t *pref_ignored_fg_; + pref_t *pref_ignored_bg_; + pref_t *pref_client_fg_; + pref_t *pref_client_bg_; + pref_t *pref_server_fg_; + pref_t *pref_server_bg_; + pref_t *pref_valid_bg_; + pref_t *pref_invalid_bg_; + pref_t *pref_deprecated_bg_; + QFont cur_font_; + + void updateWidgets(); + void changeColor(pref_t *pref); + +private slots: + void colorChanged(pref_t *pref, const QColor &cc); + void on_fontPushButton_clicked(); + + void on_activeFGPushButton_clicked(); + void on_activeBGPushButton_clicked(); + void on_activeStyleComboBox_currentIndexChanged(int index); + void on_inactiveFGPushButton_clicked(); + void on_inactiveBGPushButton_clicked(); + void on_inactiveStyleComboBox_currentIndexChanged(int index); + void on_markedFGPushButton_clicked(); + void on_markedBGPushButton_clicked(); + void on_ignoredFGPushButton_clicked(); + void on_ignoredBGPushButton_clicked(); + void on_clientFGPushButton_clicked(); + void on_clientBGPushButton_clicked(); + void on_serverFGPushButton_clicked(); + void on_serverBGPushButton_clicked(); + void on_validFilterBGPushButton_clicked(); + void on_invalidFilterBGPushButton_clicked(); + void on_deprecatedFilterBGPushButton_clicked(); +}; + +#endif // FONT_COLOR_PREFERENCES_FRAME_H diff --git a/ui/qt/font_color_preferences_frame.ui b/ui/qt/font_color_preferences_frame.ui new file mode 100644 index 00000000..f1a5e833 --- /dev/null +++ b/ui/qt/font_color_preferences_frame.ui @@ -0,0 +1,401 @@ + + + FontColorPreferencesFrame + + + + 0 + 0 + 540 + 390 + + + + + 540 + 390 + + + + Frame + + + 0 + + + + + + + + Main window font: + + + + + + + Select Font + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + true + + + + + + + Colors: + + + + + + + + + true + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + true + + + QPushButton { border: 1px solid palette(Dark); } + + + + + + + + + true + + + Sample active selected item + + + true + + + + + + + Style: + + + + + + + + System Default + + + + + Solid + + + + + Gradient + + + + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + + + + + + + + + + Sample inactive selected item + + + true + + + + + + + Style: + + + + + + + + System Default + + + + + Solid + + + + + Gradient + + + + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + Sample marked packet text + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + Sample ignored packet text + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + Sample "Follow Stream" client text + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + Sample "Follow Stream" server text + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + Sample valid filter + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + Sample invalid filter + + + true + + + + + + + QPushButton { border: 1px solid palette(Dark); } + + + true + + + + + + + Sample warning filter + + + true + + + + + + + + + Qt::Vertical + + + + 178 + 13 + + + + + + + + + diff --git a/ui/qt/funnel_statistics.cpp b/ui/qt/funnel_statistics.cpp new file mode 100644 index 00000000..f5ed6745 --- /dev/null +++ b/ui/qt/funnel_statistics.cpp @@ -0,0 +1,619 @@ +/* funnel_statistics.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include "epan/color_filters.h" +#include "file.h" + +#include "epan/funnel.h" +#include "epan/prefs.h" + +#include + +#include "ui/progress_dlg.h" +#include "ui/simple_dialog.h" +#include +#include + +#include "funnel_statistics.h" +#include "funnel_string_dialog.h" +#include "funnel_text_dialog.h" + +#include +#include +#include +#include +#include +#include + +#include "main_application.h" + +// To do: +// - Handle menu paths. Do we create a new path (GTK+) or use the base element? +// - Add a FunnelGraphDialog class? + +extern "C" { +static struct _funnel_text_window_t* text_window_new(funnel_ops_id_t *ops_id, const char* title); +static void string_dialog_new(funnel_ops_id_t *ops_id, const gchar* title, const gchar** field_names, const gchar** field_values, funnel_dlg_cb_t dialog_cb, void* dialog_cb_data, funnel_dlg_cb_data_free_t dialog_cb_data_free); + +static void funnel_statistics_retap_packets(funnel_ops_id_t *ops_id); +static void funnel_statistics_copy_to_clipboard(GString *text); +static const gchar *funnel_statistics_get_filter(funnel_ops_id_t *ops_id); +static void funnel_statistics_set_filter(funnel_ops_id_t *ops_id, const char* filter_string); +static gchar* funnel_statistics_get_color_filter_slot(guint8 filter_num); +static void funnel_statistics_set_color_filter_slot(guint8 filter_num, const gchar* filter_string); +static gboolean funnel_statistics_open_file(funnel_ops_id_t *ops_id, const char* fname, const char* filter, char**); +static void funnel_statistics_reload_packets(funnel_ops_id_t *ops_id); +static void funnel_statistics_redissect_packets(funnel_ops_id_t *ops_id); +static void funnel_statistics_reload_lua_plugins(funnel_ops_id_t *ops_id); +static void funnel_statistics_apply_filter(funnel_ops_id_t *ops_id); +static gboolean browser_open_url(const gchar *url); +static void browser_open_data_file(const gchar *filename); +static struct progdlg *progress_window_new(funnel_ops_id_t *ops_id, const gchar* title, const gchar* task, gboolean terminate_is_stop, gboolean *stop_flag); +static void progress_window_update(struct progdlg *progress_dialog, float percentage, const gchar* status); +static void progress_window_destroy(struct progdlg *progress_dialog); +} + +FunnelAction::FunnelAction(QObject *parent) : + QAction(parent), + callback_(nullptr), + callback_data_(NULL), + retap_(FALSE), + packetCallback_(nullptr), + packetData_(NULL) +{ + +} + +FunnelAction::FunnelAction(QString title, funnel_menu_callback callback, gpointer callback_data, gboolean retap, QObject *parent = nullptr) : + QAction(parent), + title_(title), + callback_(callback), + callback_data_(callback_data), + retap_(retap) +{ + // Use "&&" to get a real ampersand in the menu item. + title.replace('&', "&&"); + + setText(title); + setObjectName(FunnelStatistics::actionName()); + packetRequiredFields_ = QSet(); +} + +FunnelAction::FunnelAction(QString title, funnel_packet_menu_callback callback, gpointer callback_data, gboolean retap, const char *packet_required_fields, QObject *parent = nullptr) : + QAction(parent), + title_(title), + callback_data_(callback_data), + retap_(retap), + packetCallback_(callback), + packetRequiredFields_(QSet()) +{ + // Use "&&" to get a real ampersand in the menu item. + title.replace('&', "&&"); + + QStringList menuComponents = title.split(QString("/")); + // Set the menu's text to the rightmost component, set the path to being everything to the left: + setText("(empty)"); + packetSubmenu_ = ""; + if (!menuComponents.isEmpty()) + { + setText(menuComponents.last()); + menuComponents.removeLast(); + packetSubmenu_ = menuComponents.join("/"); + } + + setObjectName(FunnelStatistics::actionName()); + setPacketRequiredFields(packet_required_fields); +} + +FunnelAction::~FunnelAction(){ +} + +funnel_menu_callback FunnelAction::callback() const { + return callback_; +} + +QString FunnelAction::title() const { + return title_; +} + +void FunnelAction::triggerCallback() { + if (callback_) { + callback_(callback_data_); + } +} + +void FunnelAction::setPacketCallback(funnel_packet_menu_callback packet_callback) { + packetCallback_ = packet_callback; +} + +void FunnelAction::setPacketRequiredFields(const char *required_fields_str) { + packetRequiredFields_.clear(); + // If multiple fields are required to be present, they're split by commas + // Also remove leading and trailing spaces, in case someone writes + // "http, dns" instead of "http,dns" + QString requiredFieldsJoined = QString(required_fields_str); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList requiredFieldsSplit = requiredFieldsJoined.split(",", Qt::SkipEmptyParts); +#else + QStringList requiredFieldsSplit = requiredFieldsJoined.split(",", QString::SkipEmptyParts); +#endif + foreach (QString requiredField, requiredFieldsSplit) { + QString trimmedFieldName = requiredField.trimmed(); + if (! trimmedFieldName.isEmpty()) { + packetRequiredFields_.insert(trimmedFieldName); + } + } +} + +const QSet FunnelAction::getPacketRequiredFields() { + return packetRequiredFields_; +} + + +void FunnelAction::setPacketData(GPtrArray* finfos) { + packetData_ = finfos; +} + +void FunnelAction::addToMenu(QMenu * ctx_menu, QHash &menuTextToMenus) { + QString submenusText = this->getPacketSubmenus(); + if (submenusText.isEmpty()) { + ctx_menu->addAction(this); + } else { + // If the action has a submenu, ensure that the + // the full submenu chain exists: + QStringList menuComponents = submenusText.split("/"); + QString menuSubComponentsStringPrior = NULL; + for (int menuIndex=0; menuIndex < menuComponents.size(); menuIndex++) { + QStringList menuSubComponents = menuComponents.mid(0, menuIndex+1); + QString menuSubComponentsString = menuSubComponents.join("/"); + if (!menuTextToMenus.contains(menuSubComponentsString)) { + // Create a new menu object under the prior object + QMenu *previousSubmenu = menuTextToMenus.value(menuSubComponentsStringPrior); + QMenu *submenu = previousSubmenu->addMenu(menuComponents.at(menuIndex)); + menuTextToMenus.insert(menuSubComponentsString, submenu); + } + menuSubComponentsStringPrior = menuSubComponentsString; + } + // Then add the action to the relevant submenu + QMenu *parentMenu = menuTextToMenus.value(submenusText); + parentMenu->addAction(this); + } + +} + +void FunnelAction::triggerPacketCallback() { + if (packetCallback_) { + packetCallback_(callback_data_, packetData_); + } +} + +bool FunnelAction::retap() { + if (retap_) return true; + return false; +} + +QString FunnelAction::getPacketSubmenus() { + return packetSubmenu_; +} + +FunnelConsoleAction::FunnelConsoleAction(QString name, + funnel_console_eval_cb_t eval_cb, + funnel_console_open_cb_t open_cb, + funnel_console_close_cb_t close_cb, + void *callback_data, QObject *parent = nullptr) : + FunnelAction(parent), + eval_cb_(eval_cb), + open_cb_(open_cb), + close_cb_(close_cb), + callback_data_(callback_data) +{ + // Use "&&" to get a real ampersand in the menu item. + QString title = QString("%1 Console").arg(name).replace('&', "&&"); + + setText(title); + setObjectName(FunnelStatistics::actionName()); +} + +FunnelConsoleAction::~FunnelConsoleAction() +{ +} + +void FunnelConsoleAction::triggerCallback() { + if (!dialog_) { + dialog_ = new IOConsoleDialog(*qobject_cast(parent()), + this->text(), + eval_cb_, open_cb_, close_cb_, callback_data_); + dialog_->setAttribute(Qt::WA_DeleteOnClose); + } + + if (dialog_->isMinimized()) { + dialog_->showNormal(); + } + else { + dialog_->show(); + } + dialog_->raise(); + dialog_->activateWindow(); +} + +static QHash > funnel_actions_; +const QString FunnelStatistics::action_name_ = "FunnelStatisticsAction"; +static gboolean menus_registered = FALSE; + +struct _funnel_ops_id_t { + FunnelStatistics *funnel_statistics; +}; + +FunnelStatistics::FunnelStatistics(QObject *parent, CaptureFile &cf) : + QObject(parent), + capture_file_(cf), + prepared_filter_(QString()) +{ + funnel_ops_ = new(struct _funnel_ops_t); + memset(funnel_ops_, 0, sizeof(struct _funnel_ops_t)); + funnel_ops_id_ = new(struct _funnel_ops_id_t); + + funnel_ops_id_->funnel_statistics = this; + funnel_ops_->ops_id = funnel_ops_id_; + funnel_ops_->new_text_window = text_window_new; + funnel_ops_->set_text = text_window_set_text; + funnel_ops_->append_text = text_window_append; + funnel_ops_->prepend_text = text_window_prepend; + funnel_ops_->clear_text = text_window_clear; + funnel_ops_->get_text = text_window_get_text; + funnel_ops_->set_close_cb = text_window_set_close_cb; + funnel_ops_->set_editable = text_window_set_editable; + funnel_ops_->destroy_text_window = text_window_destroy; + funnel_ops_->add_button = text_window_add_button; + funnel_ops_->new_dialog = string_dialog_new; + funnel_ops_->close_dialogs = string_dialogs_close; + funnel_ops_->retap_packets = funnel_statistics_retap_packets; + funnel_ops_->copy_to_clipboard = funnel_statistics_copy_to_clipboard; + funnel_ops_->get_filter = funnel_statistics_get_filter; + funnel_ops_->set_filter = funnel_statistics_set_filter; + funnel_ops_->get_color_filter_slot = funnel_statistics_get_color_filter_slot; + funnel_ops_->set_color_filter_slot = funnel_statistics_set_color_filter_slot; + funnel_ops_->open_file = funnel_statistics_open_file; + funnel_ops_->reload_packets = funnel_statistics_reload_packets; + funnel_ops_->redissect_packets = funnel_statistics_redissect_packets; + funnel_ops_->reload_lua_plugins = funnel_statistics_reload_lua_plugins; + funnel_ops_->apply_filter = funnel_statistics_apply_filter; + funnel_ops_->browser_open_url = browser_open_url; + funnel_ops_->browser_open_data_file = browser_open_data_file; + funnel_ops_->new_progress_window = progress_window_new; + funnel_ops_->update_progress = progress_window_update; + funnel_ops_->destroy_progress_window = progress_window_destroy; + + funnel_set_funnel_ops(funnel_ops_); +} + +FunnelStatistics::~FunnelStatistics() +{ + // At this point we're probably closing the program and will shortly + // call epan_cleanup, which calls ProgDlg__gc and TextWindow__gc. + // They in turn depend on funnel_ops_ being valid. + memset(funnel_ops_id_, 0, sizeof(struct _funnel_ops_id_t)); + memset(funnel_ops_, 0, sizeof(struct _funnel_ops_t)); + // delete(funnel_ops_id_); + // delete(funnel_ops_); +} + +void FunnelStatistics::retapPackets() +{ + capture_file_.retapPackets(); +} + +struct progdlg *FunnelStatistics::progressDialogNew(const gchar *task_title, const gchar *item_title, gboolean terminate_is_stop, gboolean *stop_flag) +{ + return create_progress_dlg(parent(), task_title, item_title, terminate_is_stop, stop_flag); +} + +const char *FunnelStatistics::displayFilter() +{ + return display_filter_.constData(); +} + +void FunnelStatistics::emitSetDisplayFilter(const QString filter) +{ + prepared_filter_ = filter; + emit setDisplayFilter(filter, FilterAction::ActionPrepare, FilterAction::ActionTypePlain); +} + +void FunnelStatistics::reloadPackets() +{ + capture_file_.reload(); +} + +void FunnelStatistics::redissectPackets() +{ + // This will trigger a packet redissection. + mainApp->emitAppSignal(MainApplication::PacketDissectionChanged); +} + +void FunnelStatistics::reloadLuaPlugins() +{ + mainApp->reloadLuaPluginsDelayed(); +} + +void FunnelStatistics::emitApplyDisplayFilter() +{ + emit setDisplayFilter(prepared_filter_, FilterAction::ActionApply, FilterAction::ActionTypePlain); +} + +void FunnelStatistics::emitOpenCaptureFile(QString cf_path, QString filter) +{ + emit openCaptureFile(cf_path, filter); +} + +void FunnelStatistics::funnelActionTriggered() +{ + FunnelAction *funnel_action = dynamic_cast(sender()); + if (!funnel_action) return; + + funnel_action->triggerCallback(); +} + +void FunnelStatistics::displayFilterTextChanged(const QString &filter) +{ + display_filter_ = filter.toUtf8(); +} + +struct _funnel_text_window_t* text_window_new(funnel_ops_id_t *ops_id, const char* title) +{ + return FunnelTextDialog::textWindowNew(qobject_cast(ops_id->funnel_statistics->parent()), title); +} + +void string_dialog_new(funnel_ops_id_t *ops_id, const gchar* title, const gchar** field_names, const gchar** field_values, funnel_dlg_cb_t dialog_cb, void* dialog_cb_data, funnel_dlg_cb_data_free_t dialog_cb_data_free) +{ + QList> field_list; + for (int i = 0; field_names[i]; i++) { + QPair field = QPair(QString(field_names[i]), QString("")); + if (field_values != NULL && field_values[i]) + { + field.second = QString(field_values[i]); + } + + field_list << field; + } + FunnelStringDialog::stringDialogNew(qobject_cast(ops_id->funnel_statistics->parent()), title, field_list, dialog_cb, dialog_cb_data, dialog_cb_data_free); +} + +void funnel_statistics_retap_packets(funnel_ops_id_t *ops_id) { + if (!ops_id || !ops_id->funnel_statistics) return; + + ops_id->funnel_statistics->retapPackets(); +} + +void funnel_statistics_copy_to_clipboard(GString *text) { + mainApp->clipboard()->setText(text->str); +} + +const gchar *funnel_statistics_get_filter(funnel_ops_id_t *ops_id) { + if (!ops_id || !ops_id->funnel_statistics) return nullptr; + + return ops_id->funnel_statistics->displayFilter(); +} + +void funnel_statistics_set_filter(funnel_ops_id_t *ops_id, const char* filter_string) { + if (!ops_id || !ops_id->funnel_statistics) return; + + ops_id->funnel_statistics->emitSetDisplayFilter(filter_string); +} + +gchar* funnel_statistics_get_color_filter_slot(guint8 filter_num) { + return color_filters_get_tmp(filter_num); +} + +void funnel_statistics_set_color_filter_slot(guint8 filter_num, const gchar* filter_string) { + gchar *err_msg = nullptr; + if (!color_filters_set_tmp(filter_num, filter_string, FALSE, &err_msg)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); + g_free(err_msg); + } +} + +gboolean funnel_statistics_open_file(funnel_ops_id_t *ops_id, const char* fname, const char* filter, char**) { + // XXX We need to return a proper error value. We should probably move + // MainWindow::openCaptureFile to CaptureFile and add error handling + // there. + if (!ops_id || !ops_id->funnel_statistics) return FALSE; + + QString cf_name(fname); + QString cf_filter(filter); + ops_id->funnel_statistics->emitOpenCaptureFile(cf_name, cf_filter); + return TRUE; +} + +void funnel_statistics_reload_packets(funnel_ops_id_t *ops_id) { + if (!ops_id || !ops_id->funnel_statistics) return; + + ops_id->funnel_statistics->reloadPackets(); +} + +void funnel_statistics_redissect_packets(funnel_ops_id_t *ops_id) { + if (!ops_id || !ops_id->funnel_statistics) return; + + ops_id->funnel_statistics->redissectPackets(); +} + +void funnel_statistics_reload_lua_plugins(funnel_ops_id_t *ops_id) { + if (!ops_id || !ops_id->funnel_statistics) return; + + ops_id->funnel_statistics->reloadLuaPlugins(); +} + +void funnel_statistics_apply_filter(funnel_ops_id_t *ops_id) { + if (!ops_id || !ops_id->funnel_statistics) return; + + ops_id->funnel_statistics->emitApplyDisplayFilter(); +} + +gboolean browser_open_url(const gchar *url) { + return QDesktopServices::openUrl(QUrl(url)) ? TRUE : FALSE; +} + +void browser_open_data_file(const gchar *filename) { + QDesktopServices::openUrl(QUrl::fromLocalFile(filename)); +} + +struct progdlg *progress_window_new(funnel_ops_id_t *ops_id, const gchar* task_title, const gchar* item_title, gboolean terminate_is_stop, gboolean *stop_flag) { + if (!ops_id || !ops_id->funnel_statistics) return nullptr; + + return ops_id->funnel_statistics->progressDialogNew(task_title, item_title, terminate_is_stop, stop_flag); +} + +void progress_window_update(struct progdlg *progress_dialog, float percentage, const gchar* status) { + update_progress_dlg(progress_dialog, percentage, status); +} + +void progress_window_destroy(progdlg *progress_dialog) { + destroy_progress_dlg(progress_dialog); +} + +extern "C" { + +void register_tap_listener_qt_funnel(void); + +static void register_menu_cb(const char *name, + register_stat_group_t group, + funnel_menu_callback callback, + gpointer callback_data, + gboolean retap) +{ + FunnelAction *funnel_action = new FunnelAction(name, callback, callback_data, retap, mainApp); + if (menus_registered) { + mainApp->appendDynamicMenuGroupItem(group, funnel_action); + } else { + mainApp->addDynamicMenuGroupItem(group, funnel_action); + } + if (!funnel_actions_.contains(group)) { + funnel_actions_[group] = QList(); + } + funnel_actions_[group] << funnel_action; +} + +/* + * Callback used to register packet menus in the GUI. + * + * Creates a new FunnelAction with the Lua + * callback and stores it in the Wireshark GUI with + * appendPacketMenu() so it can be retrieved when + * the packet's context menu is open. + * + * @param name packet menu item's name + * @param required_fields fields required to be present for the packet menu to be displayed + * @param callback function called when the menu item is invoked. The function must take one argument and return nothing. + * @param callback_data Lua state for the callback function + * @param retap whether or not to rescan all packets + */ +static void register_packet_menu_cb(const char *name, + const char *required_fields, + funnel_packet_menu_callback callback, + gpointer callback_data, + gboolean retap) +{ + FunnelAction *funnel_action = new FunnelAction(name, callback, callback_data, retap, required_fields, mainApp); + MainWindow * mainwindow = qobject_cast(mainApp->mainWindow()); + if (mainwindow) { + mainwindow->appendPacketMenu(funnel_action); + } +} + +static void deregister_menu_cb(funnel_menu_callback callback) +{ + foreach (int group, funnel_actions_.keys()) { + QList::iterator it = funnel_actions_[group].begin(); + while (it != funnel_actions_[group].end()) { + FunnelAction *funnel_action = *it; + if (funnel_action->callback() == callback) { + // Must set back to title to find the correct sub-menu in Tools + funnel_action->setText(funnel_action->title()); + mainApp->removeDynamicMenuGroupItem(group, funnel_action); + it = funnel_actions_[group].erase(it); + } else { + ++it; + } + } + } +} + +void +register_tap_listener_qt_funnel(void) +{ + funnel_register_all_menus(register_menu_cb); + funnel_statistics_load_console_menus(); + menus_registered = TRUE; +} + +void +funnel_statistics_reload_menus(void) +{ + funnel_reload_menus(deregister_menu_cb, register_menu_cb); + funnel_statistics_load_packet_menus(); +} + + +/** + * Returns whether the packet menus have been modified since they were last registered + * + * @return TRUE if the packet menus were modified since the last registration + */ +gboolean +funnel_statistics_packet_menus_modified(void) +{ + return funnel_packet_menus_modified(); +} + +/* + * Loads all registered_packet_menus into the + * Wireshark GUI. + */ +void +funnel_statistics_load_packet_menus(void) +{ + funnel_register_all_packet_menus(register_packet_menu_cb); +} + +static void register_console_menu_cb(const char *name, + funnel_console_eval_cb_t eval_cb, + funnel_console_open_cb_t open_cb, + funnel_console_close_cb_t close_cb, + void *callback_data) +{ + FunnelConsoleAction *funnel_action = new FunnelConsoleAction(name, eval_cb, + open_cb, + close_cb, + callback_data, + mainApp); + if (menus_registered) { + mainApp->appendDynamicMenuGroupItem(REGISTER_TOOLS_GROUP_UNSORTED, funnel_action); + } else { + mainApp->addDynamicMenuGroupItem(REGISTER_TOOLS_GROUP_UNSORTED, funnel_action); + } + if (!funnel_actions_.contains(REGISTER_TOOLS_GROUP_UNSORTED)) { + funnel_actions_[REGISTER_TOOLS_GROUP_UNSORTED] = QList(); + } + funnel_actions_[REGISTER_TOOLS_GROUP_UNSORTED] << funnel_action; +} + +/* + * Loads all registered console menus into the + * Wireshark GUI. + */ +void +funnel_statistics_load_console_menus(void) +{ + funnel_register_all_console_menus(register_console_menu_cb); +} + +} // extern "C" diff --git a/ui/qt/funnel_statistics.h b/ui/qt/funnel_statistics.h new file mode 100644 index 00000000..65b36ab8 --- /dev/null +++ b/ui/qt/funnel_statistics.h @@ -0,0 +1,126 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FUNNELSTATISTICS_H +#define FUNNELSTATISTICS_H + +#include +#include +#include +#include + +#include +#include "io_console_dialog.h" +#include "capture_file.h" +#include + +struct _funnel_ops_t; +struct progdlg; + +/** + * Signature of function that can be called from a custom packet menu entry + */ +typedef void (* funnel_packet_menu_callback)(gpointer, GPtrArray*); + +class FunnelStatistics : public QObject +{ + Q_OBJECT +public: + explicit FunnelStatistics(QObject *parent, CaptureFile &cf); + ~FunnelStatistics(); + void retapPackets(); + struct progdlg *progressDialogNew(const gchar *task_title, const gchar *item_title, gboolean terminate_is_stop, gboolean *stop_flag); + const char *displayFilter(); + void emitSetDisplayFilter(const QString filter); + void reloadPackets(); + void redissectPackets(); + void reloadLuaPlugins(); + void emitApplyDisplayFilter(); + void emitOpenCaptureFile(QString cf_path, QString filter); + static const QString &actionName() { return action_name_; } + +signals: + void openCaptureFile(QString cf_path, QString filter); + void setDisplayFilter(QString filter, FilterAction::Action action, FilterAction::ActionType filterType); + +public slots: + void funnelActionTriggered(); + void displayFilterTextChanged(const QString &filter); + +private: + static const QString action_name_; + struct _funnel_ops_t *funnel_ops_; + struct _funnel_ops_id_t *funnel_ops_id_; + + CaptureFile &capture_file_; + QByteArray display_filter_; + QString prepared_filter_; +}; + +class FunnelAction : public QAction +{ + Q_OBJECT +public: + FunnelAction(QObject *parent = nullptr); + FunnelAction(QString title, funnel_menu_callback callback, gpointer callback_data, gboolean retap, QObject *parent); + FunnelAction(QString title, funnel_packet_menu_callback callback, gpointer callback_data, gboolean retap, const char *packet_required_fields, QObject *parent); + ~FunnelAction(); + funnel_menu_callback callback() const; + QString title() const; + virtual void triggerCallback(); + void setPacketCallback(funnel_packet_menu_callback packet_callback); + void setPacketData(GPtrArray* finfos); + void addToMenu(QMenu * ctx_menu, QHash &menuTextToMenus); + void setPacketRequiredFields(const char *required_fields_str); + const QSet getPacketRequiredFields(); + bool retap(); + QString getPacketSubmenus(); + +public slots: + void triggerPacketCallback(); + +private: + QString title_; + QString packetSubmenu_; + funnel_menu_callback callback_; + gpointer callback_data_; + gboolean retap_; + funnel_packet_menu_callback packetCallback_; + GPtrArray* packetData_; + QSet packetRequiredFields_; +}; + +class FunnelConsoleAction : public FunnelAction +{ + Q_OBJECT +public: + FunnelConsoleAction(QString name, funnel_console_eval_cb_t eval_cb, + funnel_console_open_cb_t open_cb, + funnel_console_close_cb_t close_cb, + void *callback_data, QObject *parent); + ~FunnelConsoleAction(); + virtual void triggerCallback(); + +private: + QString title_; + funnel_console_eval_cb_t eval_cb_; + funnel_console_open_cb_t open_cb_; + funnel_console_close_cb_t close_cb_; + void *callback_data_; + QPointer dialog_; +}; + +extern "C" { + void funnel_statistics_reload_menus(void); + void funnel_statistics_load_packet_menus(void); + void funnel_statistics_load_console_menus(void); + gboolean funnel_statistics_packet_menus_modified(void); +} // extern "C" + +#endif // FUNNELSTATISTICS_H diff --git a/ui/qt/funnel_string_dialog.cpp b/ui/qt/funnel_string_dialog.cpp new file mode 100644 index 00000000..de52e73f --- /dev/null +++ b/ui/qt/funnel_string_dialog.cpp @@ -0,0 +1,103 @@ +/* funnel_string_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "funnel_string_dialog.h" +#include + +#include +#include + +#include +#include "main_application.h" + +// Helper object used for sending close signal to open dialogs from a C function +static FunnelStringDialogHelper dialog_helper_; + +const int min_edit_width_ = 20; // em widths +FunnelStringDialog::FunnelStringDialog(QWidget *parent, const QString title, const QList> field_list, funnel_dlg_cb_t dialog_cb, void* dialog_cb_data, funnel_dlg_cb_data_free_t dialog_data_free_cb) : + QDialog(parent), + ui(new Ui::FunnelStringDialog), + dialog_cb_(dialog_cb), + dialog_cb_data_(dialog_cb_data), + dialog_cb_data_free_(dialog_data_free_cb) +{ + ui->setupUi(this); + setWindowTitle(mainApp->windowTitleString(title)); + int one_em = fontMetrics().height(); + + int row = 0; + QPair field; + foreach(field, field_list) { + QLabel* field_label = new QLabel(field.first, this); + ui->stringGridLayout->addWidget(field_label, row, 0); + QLineEdit* field_edit = new QLineEdit(this); + field_edit->setText(field.second); + field_edit->setMinimumWidth(one_em * min_edit_width_); + field_edits_ << field_edit; + ui->stringGridLayout->addWidget(field_edit, row, 1); + row++; + } +} + +FunnelStringDialog::~FunnelStringDialog() +{ + if (dialog_cb_data_free_) { + dialog_cb_data_free_(dialog_cb_data_); + } + + delete ui; +} + +void FunnelStringDialog::accept() +{ + QDialog::accept(); + + disconnect(); + deleteLater(); +} + +void FunnelStringDialog::reject() +{ + QDialog::reject(); + + disconnect(); + deleteLater(); +} + +void FunnelStringDialog::on_buttonBox_accepted() +{ + if (!dialog_cb_) return; + + GPtrArray* returns = g_ptr_array_new(); + + foreach (QLineEdit *field_edit, field_edits_) { + g_ptr_array_add(returns, qstring_strdup(field_edit->text())); + } + g_ptr_array_add(returns, NULL); + + gchar **user_input = (gchar **)g_ptr_array_free(returns, FALSE); + dialog_cb_(user_input, dialog_cb_data_); +} + +void FunnelStringDialog::stringDialogNew(QWidget *parent, const QString title, QList> field_list, funnel_dlg_cb_t dialog_cb, void* dialog_cb_data, funnel_dlg_cb_data_free_t dialog_cb_data_free) +{ + FunnelStringDialog* fsd = new FunnelStringDialog(parent, title, field_list, dialog_cb, dialog_cb_data, dialog_cb_data_free); + connect(&dialog_helper_, &FunnelStringDialogHelper::closeDialogs, fsd, &FunnelStringDialog::close); + fsd->show(); +} + +void FunnelStringDialogHelper::emitCloseDialogs() +{ + emit closeDialogs(); +} + +void string_dialogs_close(void) +{ + dialog_helper_.emitCloseDialogs(); +} diff --git a/ui/qt/funnel_string_dialog.h b/ui/qt/funnel_string_dialog.h new file mode 100644 index 00000000..bd4f3d35 --- /dev/null +++ b/ui/qt/funnel_string_dialog.h @@ -0,0 +1,66 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FUNNEL_STRING_DIALOG_H +#define FUNNEL_STRING_DIALOG_H + +#include + +#include "epan/funnel.h" + +#include + +class QLineEdit; + +namespace Ui { +class FunnelStringDialog; +class FunnelStringDialogHelper; +} + +class FunnelStringDialog : public QDialog +{ + Q_OBJECT + +public: + explicit FunnelStringDialog(QWidget *parent, const QString title, const QList> field_list, funnel_dlg_cb_t dialog_cb, void* dialog_cb_data, funnel_dlg_cb_data_free_t dialog_data_free_cb); + ~FunnelStringDialog(); + + // Funnel ops + static void stringDialogNew(QWidget *parent, const QString title, const QList> field_list, funnel_dlg_cb_t dialog_cb, void* dialog_cb_data, funnel_dlg_cb_data_free_t dialog_cb_data_free); + + void accept(); + void reject(); + +private slots: + void on_buttonBox_accepted(); + +private: + Ui::FunnelStringDialog *ui; + funnel_dlg_cb_t dialog_cb_; + void *dialog_cb_data_; + funnel_dlg_cb_data_free_t dialog_cb_data_free_; + QList field_edits_; +}; + +class FunnelStringDialogHelper : public QObject +{ + Q_OBJECT + +public slots: + void emitCloseDialogs(); + +signals: + void closeDialogs(); +}; + +extern "C" { + void string_dialogs_close(void); +} + +#endif // FUNNEL_STRING_DIALOG_H diff --git a/ui/qt/funnel_string_dialog.ui b/ui/qt/funnel_string_dialog.ui new file mode 100644 index 00000000..c6ba7e2d --- /dev/null +++ b/ui/qt/funnel_string_dialog.ui @@ -0,0 +1,67 @@ + + + FunnelStringDialog + + + + 0 + 0 + 176 + 66 + + + + Dialog + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + FunnelStringDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FunnelStringDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/funnel_text_dialog.cpp b/ui/qt/funnel_text_dialog.cpp new file mode 100644 index 00000000..84bbb941 --- /dev/null +++ b/ui/qt/funnel_text_dialog.cpp @@ -0,0 +1,236 @@ +/* funnel_text_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "funnel_text_dialog.h" +#include + +#include +#include +#include +#include + +#include +#include "main_application.h" + +// To do: +// - Add "Find next" to highlighting. +// - Add rich text support? +// - Zoom text? + +static QHash text_button_to_funnel_button_; + +FunnelTextDialog::FunnelTextDialog(QWidget *parent, const QString &title) : + GeometryStateDialog(parent), + ui(new Ui::FunnelTextDialog), + close_cb_(NULL), + close_cb_data_(NULL) +{ + ui->setupUi(this); + if (!title.isEmpty()) { + loadGeometry(0, 0, QString("Funnel %1").arg(title)); + } + setWindowTitle(mainApp->windowTitleString(title)); + + funnel_text_window_.funnel_text_dialog = this; + + ui->textEdit->setFont(mainApp->monospaceFont()); + ui->textEdit->setReadOnly(true); + ui->textEdit->setAcceptRichText(false); +} + +FunnelTextDialog::~FunnelTextDialog() +{ + delete ui; +} + +void FunnelTextDialog::reject() +{ + QDialog::reject(); + + if (close_cb_) { + close_cb_(close_cb_data_); + } + + QHash::iterator i; + for (i = text_button_to_funnel_button_.begin(); i != text_button_to_funnel_button_.end(); ++i) { + funnel_bt_t *funnel_button = i.value(); + if (funnel_button->free_data_fcn) { + funnel_button->free_data_fcn(funnel_button->data); + } + if (funnel_button->free_fcn) { + funnel_button->free_fcn(funnel_button); + } + } + text_button_to_funnel_button_.clear(); + + disconnect(); + deleteLater(); +} + +struct _funnel_text_window_t *FunnelTextDialog::textWindowNew(QWidget *parent, const QString title) +{ + FunnelTextDialog *ftd = new FunnelTextDialog(parent, title); + ftd->show(); + return &ftd->funnel_text_window_; +} + +void FunnelTextDialog::setText(const QString text) +{ + ui->textEdit->setText(text); +} + +void FunnelTextDialog::appendText(const QString text) +{ + ui->textEdit->moveCursor(QTextCursor::End); + ui->textEdit->insertPlainText(text); +} + +void FunnelTextDialog::prependText(const QString text) +{ + ui->textEdit->moveCursor(QTextCursor::Start); + ui->textEdit->insertPlainText(text); +} + +void FunnelTextDialog::clearText() +{ + ui->textEdit->clear(); +} + +const char *FunnelTextDialog::getText() +{ + return qstring_strdup(ui->textEdit->toPlainText()); +} + +void FunnelTextDialog::setCloseCallback(text_win_close_cb_t close_cb, void *close_cb_data) +{ + close_cb_ = close_cb; + close_cb_data_ = close_cb_data; +} + +void FunnelTextDialog::setTextEditable(gboolean editable) +{ + ui->textEdit->setReadOnly(!editable); +} + +void FunnelTextDialog::addButton(funnel_bt_t *funnel_button, QString label) +{ + // Use "&&" to get a real ampersand in the button. + label.replace('&', "&&"); + + QPushButton *button = new QPushButton(label); + ui->buttonBox->addButton(button, QDialogButtonBox::ActionRole); + text_button_to_funnel_button_[button] = funnel_button; + connect(button, &QPushButton::clicked, this, &FunnelTextDialog::buttonClicked); +} + +void FunnelTextDialog::buttonClicked() +{ + if (text_button_to_funnel_button_.contains(sender())) { + funnel_bt_t *funnel_button = text_button_to_funnel_button_[sender()]; + if (funnel_button) { + funnel_button->func(&funnel_text_window_, funnel_button->data); + } + } +} + +void FunnelTextDialog::on_findLineEdit_textChanged(const QString &pattern) +{ + QRegularExpression re(pattern, QRegularExpression::CaseInsensitiveOption | + QRegularExpression::UseUnicodePropertiesOption); + QTextCharFormat plain_fmt, highlight_fmt; + highlight_fmt.setBackground(Qt::yellow); + QTextCursor csr(ui->textEdit->document()); + int position = csr.position(); + + setUpdatesEnabled(false); + + // Reset highlighting + csr.movePosition(QTextCursor::Start); + csr.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + csr.setCharFormat(plain_fmt); + + // Apply new highlighting + if (!pattern.isEmpty() && re.isValid()) { + QRegularExpressionMatchIterator iter = re.globalMatch(ui->textEdit->toPlainText()); + while (iter.hasNext()) { + QRegularExpressionMatch match = iter.next(); + csr.setPosition(static_cast(match.capturedStart()), QTextCursor::MoveAnchor); + csr.setPosition(static_cast(match.capturedEnd()), QTextCursor::KeepAnchor); + csr.setCharFormat(highlight_fmt); + } + } + + // Restore cursor and anchor + csr.setPosition(position, QTextCursor::MoveAnchor); + setUpdatesEnabled(true); +} + +void text_window_set_text(funnel_text_window_t *ftw, const char* text) +{ + if (ftw) { + ftw->funnel_text_dialog->setText(text); + } +} + +void text_window_append(funnel_text_window_t* ftw, const char* text) +{ + if (ftw) { + ftw->funnel_text_dialog->appendText(text); + } +} + +void text_window_prepend(funnel_text_window_t *ftw, const char* text) +{ + if (ftw) { + ftw->funnel_text_dialog->prependText(text); + } +} + +void text_window_clear(funnel_text_window_t* ftw) +{ + if (ftw) { + ftw->funnel_text_dialog->clearText(); + } +} + +const char *text_window_get_text(funnel_text_window_t *ftw) +{ + if (ftw) { + return ftw->funnel_text_dialog->getText(); + } + return NULL; +} + +void text_window_set_close_cb(funnel_text_window_t *ftw, text_win_close_cb_t close_cb, void *close_cb_data) +{ + if (ftw) { + ftw->funnel_text_dialog->setCloseCallback(close_cb, close_cb_data); + } +} + +void text_window_set_editable(funnel_text_window_t *ftw, gboolean editable) +{ + if (ftw) { + ftw->funnel_text_dialog->setTextEditable(editable); + } +} + +void text_window_destroy(funnel_text_window_t *ftw) +{ + if (ftw) { + ftw->funnel_text_dialog->close(); + } +} + +void text_window_add_button(funnel_text_window_t *ftw, funnel_bt_t *funnel_button, const char *label) +{ + if (ftw) { + ftw->funnel_text_dialog->addButton(funnel_button, label); + } +} diff --git a/ui/qt/funnel_text_dialog.h b/ui/qt/funnel_text_dialog.h new file mode 100644 index 00000000..84a313c9 --- /dev/null +++ b/ui/qt/funnel_text_dialog.h @@ -0,0 +1,75 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FUNNEL_TEXT_DIALOG_H +#define FUNNEL_TEXT_DIALOG_H + +#include + +#include "epan/funnel.h" +#include "geometry_state_dialog.h" + +#include + +namespace Ui { +class FunnelTextDialog; +} + +class FunnelTextDialog; +struct _funnel_text_window_t { + FunnelTextDialog* funnel_text_dialog; +}; + +class FunnelTextDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit FunnelTextDialog(QWidget *parent, const QString &title = QString()); + ~FunnelTextDialog(); + + void reject(); + + // Funnel ops + static struct _funnel_text_window_t *textWindowNew(QWidget *parent, const QString title); + void setText(const QString text); + void appendText(const QString text); + void prependText(const QString text); + void clearText(); + const char *getText(); + void setCloseCallback(text_win_close_cb_t close_cb, void* close_cb_data); + void setTextEditable(gboolean editable); + void addButton(funnel_bt_t *button_cb, QString label); + +private slots: + void buttonClicked(); + void on_findLineEdit_textChanged(const QString &pattern); + +private: + Ui::FunnelTextDialog *ui; + + struct _funnel_text_window_t funnel_text_window_; + text_win_close_cb_t close_cb_; + void *close_cb_data_; +}; + +extern "C" { +void text_window_set_text(funnel_text_window_t* ftw, const char* text); +void text_window_append(funnel_text_window_t *ftw, const char* text); +void text_window_prepend(funnel_text_window_t* ftw, const char* text); +void text_window_clear(funnel_text_window_t *ftw); +const char *text_window_get_text(funnel_text_window_t* ftw); +void text_window_set_close_cb(funnel_text_window_t *ftw, text_win_close_cb_t close_cb, void* close_cb_data); +void text_window_set_editable(funnel_text_window_t* ftw, gboolean editable); +void text_window_destroy(funnel_text_window_t* ftw); +void text_window_add_button(funnel_text_window_t* ftw, funnel_bt_t* funnel_button, const char* label); +} + + +#endif // FUNNEL_TEXT_DIALOG_H diff --git a/ui/qt/funnel_text_dialog.ui b/ui/qt/funnel_text_dialog.ui new file mode 100644 index 00000000..80bb02f3 --- /dev/null +++ b/ui/qt/funnel_text_dialog.ui @@ -0,0 +1,88 @@ + + + FunnelTextDialog + + + + 0 + 0 + 620 + 450 + + + + Dialog + + + + + + + + + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + + + Highlight: + + + + + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + FunnelTextDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FunnelTextDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/geometry_state_dialog.cpp b/ui/qt/geometry_state_dialog.cpp new file mode 100644 index 00000000..ca6d3980 --- /dev/null +++ b/ui/qt/geometry_state_dialog.cpp @@ -0,0 +1,69 @@ +/* geometry_state_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "geometry_state_dialog.h" +#include +#include "ui/recent.h" +#include "ui/ws_ui_util.h" + +GeometryStateDialog::~GeometryStateDialog() +{ + saveGeometry(); +} + +void GeometryStateDialog::loadGeometry(int width, int height, const QString &dialog_name) +{ + window_geometry_t geom; + + dialog_name_ = dialog_name.isEmpty() ? objectName() : dialog_name; + if (!dialog_name_.isEmpty() && window_geom_load(dialog_name_.toUtf8().constData(), &geom)) { + QRect recent_geom(geom.x, geom.y, geom.width, geom.height); + + // Check if the dialog is visible on any screen + if (rect_on_screen(recent_geom)) { + move(recent_geom.topLeft()); + resize(recent_geom.size()); + } else { + // Not visible, move within a reasonable area and try size only + recent_geom.moveTopLeft(QPoint(50, 50)); + if (rect_on_screen(recent_geom)) { + resize(recent_geom.size()); + } else if (width > 0 && height > 0) { + // We're not visible on any screens, use defaults + resize(width, height); + } + } + if (geom.maximized) { + showFullScreen(); + } + } else if (width > 0 && height > 0) { + // No saved geometry found, use defaults + resize(width, height); + } +} + +void GeometryStateDialog::saveGeometry() +{ + if (dialog_name_.isEmpty()) + return; + + window_geometry_t geom; + + geom.key = NULL; + geom.set_pos = TRUE; + geom.x = pos().x(); + geom.y = pos().y(); + geom.set_size = TRUE; + geom.width = size().width(); + geom.height = size().height(); + geom.set_maximized = TRUE; + geom.maximized = isFullScreen(); + + window_geom_save(dialog_name_.toUtf8().constData(), &geom); +} diff --git a/ui/qt/geometry_state_dialog.h b/ui/qt/geometry_state_dialog.h new file mode 100644 index 00000000..613fad8a --- /dev/null +++ b/ui/qt/geometry_state_dialog.h @@ -0,0 +1,67 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GEOMETRY_STATE_DIALOG_H +#define GEOMETRY_STATE_DIALOG_H + +#include + +class GeometryStateDialog : public QDialog +{ +public: + +// As discussed in change 7072, QDialogs have different minimize and "on +// top" behaviors depending on their parents, flags, and platforms. +// +// W = Windows, L = Linux (and other non-macOS UN*Xes), X = macOS +// +// QDialog(parent) +// +// W,L: Always on top, no minimize button. +// X: Independent, no minimize button. +// +// QDialog(parent, Qt::Window) +// +// W: Always on top, minimize button. Minimizes to a small title bar +// attached to the taskbar and not the taskbar itself. (The GTK+ +// UI used to do this.) +// L: Always on top, minimize button. +// X: Independent, minimize button. +// +// QDialog(NULL) +// +// W, L, X: Independent, no minimize button. +// +// QDialog(NULL, Qt::Window) +// +// W, L, X: Independent, minimize button. +// +// Additionally, maximized, parent-less dialogs can close to a black screen +// on macOS: https://gitlab.com/wireshark/wireshark/-/issues/12544 +// +// Pass in the parent on macOS and NULL elsewhere so that we have an +// independent window that un-maximizes correctly. + +#ifdef Q_OS_MAC + explicit GeometryStateDialog(QWidget *parent, Qt::WindowFlags f = Qt::WindowFlags()) : QDialog(parent, f) {} +#else + explicit GeometryStateDialog(QWidget *, Qt::WindowFlags f = Qt::WindowFlags()) : QDialog(NULL, f) {} +#endif + ~GeometryStateDialog(); + +protected: + void loadGeometry(int width = 0, int height = 0, const QString &dialog_name = QString()); + +private: + void saveGeometry(); + + QString dialog_name_; +}; + +#endif // GEOMETRY_STATE_DIALOG_H diff --git a/ui/qt/glib_mainloop_on_qeventloop.cpp b/ui/qt/glib_mainloop_on_qeventloop.cpp new file mode 100644 index 00000000..f715bbc7 --- /dev/null +++ b/ui/qt/glib_mainloop_on_qeventloop.cpp @@ -0,0 +1,125 @@ +/** @file + * + * Copyright 2022 Tomasz Mon + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "glib_mainloop_on_qeventloop.h" + +GLibPoller::GLibPoller(GMainContext *context) : + mutex_(), dispatched_(), + ctx_(context), priority_(0), + fds_(g_new(GPollFD, 1)), allocated_fds_(1), nfds_(0) +{ + g_main_context_ref(ctx_); +} + +GLibPoller::~GLibPoller() +{ + g_main_context_unref(ctx_); + g_free(fds_); +} + +void GLibPoller::run() +{ + gint timeout; + + mutex_.lock(); + while (!isInterruptionRequested()) + { + while (!g_main_context_acquire(ctx_)) + { + /* In normal circumstances context is acquired right away */ + } + g_main_context_prepare(ctx_, &priority_); + while ((nfds_ = g_main_context_query(ctx_, priority_, &timeout, fds_, + allocated_fds_)) > allocated_fds_) + { + g_free(fds_); + fds_ = g_new(GPollFD, nfds_); + allocated_fds_ = nfds_; + } + /* Blocking g_poll() call is the reason for separate polling thread */ + g_poll(fds_, nfds_, timeout); + g_main_context_release(ctx_); + + /* Polling has finished, dispatch events (if any) in main thread so we + * don't have to worry about concurrency issues in GLib callbacks. + */ + emit polled(); + /* Wait for the main thread to finish dispatching before next poll */ + dispatched_.wait(&mutex_); + } + mutex_.unlock(); +} + +GLibMainloopOnQEventLoop::GLibMainloopOnQEventLoop(QObject *parent) : + QObject(parent), + poller_(g_main_context_default()) +{ + connect(&poller_, &GLibPoller::polled, + this, &GLibMainloopOnQEventLoop::checkAndDispatch); + poller_.setObjectName("GLibPoller"); + poller_.start(); +} + +GLibMainloopOnQEventLoop::~GLibMainloopOnQEventLoop() +{ + poller_.requestInterruption(); + /* Wakeup poller thread in case it is blocked on g_poll(). Wakeup does not + * cause any problem if poller thread is already waiting on dispatched wait + * condition. + */ + g_main_context_wakeup(poller_.ctx_); + /* Wakeup poller thread without actually dispatching */ + poller_.mutex_.lock(); + poller_.dispatched_.wakeOne(); + poller_.mutex_.unlock(); + /* Poller thread will quit, wait for it to avoid warning */ + poller_.wait(); +} + +void GLibMainloopOnQEventLoop::checkAndDispatch() +{ + poller_.mutex_.lock(); + while (!g_main_context_acquire(poller_.ctx_)) + { + /* In normal circumstances context is acquired right away */ + } + if (g_main_depth() > 0) + { + /* This should not happen, but if it does warn about nested event loops + * so the issue can be fixed before the harm is done. To identify root + * cause, put breakpoint here and take backtrace when it hits. Look for + * calls to exec() and processEvents() functions. Refactor the code so + * it does not spin additional event loops. + * + * Ignoring this warning will lead to very strange and hard to debug + * problems in the future. + */ + qWarning("Nested GLib event loop detected"); + } + if (g_main_context_check(poller_.ctx_, poller_.priority_, + poller_.fds_, poller_.nfds_)) + { + g_main_context_dispatch(poller_.ctx_); + } + g_main_context_release(poller_.ctx_); + /* Start next iteration in GLibPoller thread */ + poller_.dispatched_.wakeOne(); + poller_.mutex_.unlock(); +} + +void GLibMainloopOnQEventLoop::setup(QObject *parent) +{ + /* Schedule event loop action so we can check if Qt runs GLib mainloop */ + QTimer::singleShot(0, [parent]() { + if (g_main_depth() == 0) + { + /* Not running inside GLib mainloop, actually setup */ + new GLibMainloopOnQEventLoop(parent); + } + }); +} diff --git a/ui/qt/glib_mainloop_on_qeventloop.h b/ui/qt/glib_mainloop_on_qeventloop.h new file mode 100644 index 00000000..9f66c681 --- /dev/null +++ b/ui/qt/glib_mainloop_on_qeventloop.h @@ -0,0 +1,57 @@ +/** @file + * + * Copyright 2022 Tomasz Mon + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GLIB_MAINLOOP_ON_QEVENTLOOP_H +#define GLIB_MAINLOOP_ON_QEVENTLOOP_H + +#include +#include +#include +#include + +class GLibPoller : public QThread +{ + Q_OBJECT + +protected: + explicit GLibPoller(GMainContext *context); + ~GLibPoller(); + + void run() override; + + QMutex mutex_; + QWaitCondition dispatched_; + GMainContext *ctx_; + gint priority_; + GPollFD *fds_; + gint allocated_fds_, nfds_; + +signals: + void polled(void); + + friend class GLibMainloopOnQEventLoop; +}; + +class GLibMainloopOnQEventLoop : public QObject +{ + Q_OBJECT + +protected: + explicit GLibMainloopOnQEventLoop(QObject *parent); + ~GLibMainloopOnQEventLoop(); + +protected slots: + void checkAndDispatch(); + +public: + static void setup(QObject *parent); + +protected: + GLibPoller poller_; +}; + +#endif /* GLIB_MAINLOOP_ON_QEVENTLOOP_H */ diff --git a/ui/qt/gpl-template.txt b/ui/qt/gpl-template.txt new file mode 100644 index 00000000..4d161e04 --- /dev/null +++ b/ui/qt/gpl-template.txt @@ -0,0 +1,8 @@ +/* .c + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ diff --git a/ui/qt/gsm_map_summary_dialog.cpp b/ui/qt/gsm_map_summary_dialog.cpp new file mode 100644 index 00000000..3652c7c7 --- /dev/null +++ b/ui/qt/gsm_map_summary_dialog.cpp @@ -0,0 +1,385 @@ +/* gsm_map_summary_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "gsm_map_summary_dialog.h" +#include + +#include "config.h" + +#include + +#include "ui/summary.h" + +#include +#include +#include +#include + +#include "wsutil/utf8_entities.h" + +#include "ui/capture_globals.h" +#include "ui/simple_dialog.h" + +#include +#include "main_application.h" + +#include + +/** Gsm map statistic data */ +typedef struct _gsm_map_stat_t { + int opr_code[GSM_MAP_MAX_NUM_OPR_CODES]; + int size[GSM_MAP_MAX_NUM_OPR_CODES]; + int opr_code_rr[GSM_MAP_MAX_NUM_OPR_CODES]; + int size_rr[GSM_MAP_MAX_NUM_OPR_CODES]; +} gsm_map_stat_t; + +gsm_map_stat_t gsm_map_stat; + +GsmMapSummaryDialog::GsmMapSummaryDialog(QWidget &parent, CaptureFile &capture_file) : + WiresharkDialog(parent, capture_file), + ui(new Ui::GsmMapSummaryDialog) +{ + ui->setupUi(this); + + setWindowSubtitle(tr("GSM MAP Summary")); + updateWidgets(); +} + +GsmMapSummaryDialog::~GsmMapSummaryDialog() +{ + delete ui; +} + +// Copied from capture_file_properties_dialog.cpp +QString GsmMapSummaryDialog::summaryToHtml() +{ + summary_tally summary; + memset(&summary, 0, sizeof(summary_tally)); + + QString section_tmpl; + QString table_begin, table_end; + QString table_row_begin, table_ul_row_begin, table_row_end; + QString table_vheader_tmpl; + QString table_data_tmpl; + + section_tmpl = "

%1

\n"; + table_begin = "

\n"; + table_end = "

\n"; + table_row_begin = "\n"; + table_ul_row_begin = "\n"; + table_row_end = "\n"; + table_vheader_tmpl = "%1:"; // looked odd + table_data_tmpl = "%1"; + + if (cap_file_.isValid()) { + /* initial computations */ + summary_fill_in(cap_file_.capFile(), &summary); +#ifdef HAVE_LIBPCAP + summary_fill_in_capture(cap_file_.capFile(), &global_capture_opts, &summary); +#endif + } + + QString summary_str; + QTextStream out(&summary_str); + + // File Section + out << section_tmpl.arg(tr("File")); + out << table_begin; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Name")) + << table_data_tmpl.arg(summary.filename) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Length")) + << table_data_tmpl.arg(file_size_to_qstring(summary.file_length)) + << table_row_end; + + QString format_str = wtap_file_type_subtype_description(summary.file_type); + const char *compression_type_description = wtap_compression_type_description(summary.compression_type); + if (compression_type_description != NULL) { + format_str += QString(" (%1)").arg(compression_type_description); + } + out << table_row_begin + << table_vheader_tmpl.arg(tr("Format")) + << table_data_tmpl.arg(format_str) + << table_row_end; + + if (summary.snap != 0) { + out << table_row_begin + << table_vheader_tmpl.arg(tr("Snapshot length")) + << table_data_tmpl.arg(summary.snap) + << table_row_end; + } + + out << table_end; + + // Data Section + out << section_tmpl.arg(tr("Data")); + out << table_begin; + + if (summary.packet_count_ts == summary.packet_count && + summary.packet_count >= 1) + { + // start time + out << table_row_begin + << table_vheader_tmpl.arg(tr("First packet")) + << table_data_tmpl.arg(time_t_to_qstring((time_t)summary.start_time)) + << table_row_end; + + // stop time + out << table_row_begin + << table_vheader_tmpl.arg(tr("Last packet")) + << table_data_tmpl.arg(time_t_to_qstring((time_t)summary.stop_time)) + << table_row_end; + + // elapsed seconds (capture duration) + if (summary.packet_count_ts > 1) + { + /* elapsed seconds */ + QString elapsed_str; + unsigned int elapsed_time = (unsigned int)summary.elapsed_time; + if (elapsed_time/86400) + { + elapsed_str = QString("%1 days ").arg(elapsed_time / 86400); + } + + elapsed_str += QString("%1:%2:%3") + .arg(elapsed_time % 86400 / 3600, 2, 10, QChar('0')) + .arg(elapsed_time % 3600 / 60, 2, 10, QChar('0')) + .arg(elapsed_time % 60, 2, 10, QChar('0')); + out << table_row_begin + << table_vheader_tmpl.arg(tr("Elapsed")) + << table_data_tmpl.arg(elapsed_str) + << table_row_end; + } + } + + // count + out << table_row_begin + << table_vheader_tmpl.arg(tr("Packets")) + << table_data_tmpl.arg(summary.packet_count) + << table_row_end; + + out << table_end; + + QString n_a = UTF8_EM_DASH; + QString invoke_rate_str, result_rate_str, total_rate_str; + QString invoke_avg_size_str, result_avg_size_str, total_avg_size_str; + + // Message averages + invoke_rate_str = result_rate_str = total_rate_str = n_a; + invoke_avg_size_str = result_avg_size_str = total_avg_size_str = n_a; + + double seconds = summary.stop_time - summary.start_time; + int invoke_count = 0, invoke_bytes = 0; + int result_count = 0, result_bytes = 0; + + for (int i = 0; i < GSM_MAP_MAX_NUM_OPR_CODES; i++) { + invoke_count += gsm_map_stat.opr_code[i]; + invoke_bytes += gsm_map_stat.size[i]; + } + + + for (int i = 0; i < GSM_MAP_MAX_NUM_OPR_CODES; i++) { + result_count += gsm_map_stat.opr_code_rr[i]; + result_bytes += gsm_map_stat.size_rr[i]; + } + + int total_count = invoke_count + result_count; + int total_bytes = invoke_bytes + result_bytes; + + /* + * We must have no un-time-stamped packets (i.e., the number of + * time-stamped packets must be the same as the number of packets), + * and at least two time-stamped packets, in order for the elapsed + * time to be valid. + */ + if (summary.packet_count_ts > 1 && seconds > 0.0) { + /* Total number of invokes per second */ + invoke_rate_str = QString("%1").arg(invoke_count / seconds, 1, 'f', 1); + result_rate_str = QString("%1").arg(result_count / seconds, 1, 'f', 1); + total_rate_str = QString("%1").arg((total_count) / seconds, 1, 'f', 1); + } + + /* Average message sizes */ + if (invoke_count > 0) { + invoke_avg_size_str = QString("%1").arg((double) invoke_bytes / invoke_count, 1, 'f', 1); + } + if (result_count > 0) { + result_avg_size_str = QString("%1").arg((double) result_bytes / result_count, 1, 'f', 1); + } + if (total_count > 0) { + total_avg_size_str = QString("%1").arg((double) total_bytes / total_count, 1, 'f', 1); + } + + // Invoke Section + out << section_tmpl.arg(tr("Invokes")); + out << table_begin; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Total number of Invokes")) + << table_data_tmpl.arg(invoke_count) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Average number of Invokes per second")) + << table_data_tmpl.arg(invoke_rate_str) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Total number of bytes for Invokes")) + << table_data_tmpl.arg(invoke_bytes) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Average number of bytes per Invoke")) + << table_data_tmpl.arg(invoke_avg_size_str) + << table_row_end; + + out << table_end; + + // Return Result Section + out << section_tmpl.arg(tr("Return Results")); + out << table_begin; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Total number of Return Results")) + << table_data_tmpl.arg(result_count) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Average number of Return Results per second")) + << table_data_tmpl.arg(result_rate_str) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Total number of bytes for Return Results")) + << table_data_tmpl.arg(result_bytes) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Average number of bytes per Return Result")) + << table_data_tmpl.arg(result_avg_size_str) + << table_row_end; + + out << table_end; + + // Total Section + out << section_tmpl.arg(tr("Totals")); + out << table_begin; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Total number of GSM MAP messages")) + << table_data_tmpl.arg(total_count) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Average number of GSM MAP messages per second")) + << table_data_tmpl.arg(total_rate_str) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Total number of bytes for GSM MAP messages")) + << table_data_tmpl.arg(total_bytes) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Average number of bytes per GSM MAP message")) + << table_data_tmpl.arg(total_avg_size_str) + << table_row_end; + + out << table_end; + + return summary_str; +} + +void GsmMapSummaryDialog::updateWidgets() +{ +// QPushButton *refresh_bt = ui->buttonBox->button(QDialogButtonBox::Reset); +// QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save); + +// if (file_closed_) { +// if (refresh_bt) { +// refresh_bt->setEnabled(false); +// } +// ui->commentsTextEdit->setReadOnly(true); +// if (save_bt) { +// save_bt->setEnabled(false); +// } +// return; +// } + + ui->summaryTextEdit->setHtml(summaryToHtml()); + + WiresharkDialog::updateWidgets(); +} + +extern "C" { + +void register_tap_listener_qt_gsm_map_summary(void); + +static void +gsm_map_summary_reset(void *tapdata) +{ + gsm_map_stat_t *gm_stat = (gsm_map_stat_t *)tapdata; + + memset(gm_stat, 0, sizeof(gsm_map_stat_t)); +} + + +static tap_packet_status +gsm_map_summary_packet(void *tapdata, packet_info *, epan_dissect_t *, const void *gmtr_ptr, tap_flags_t flags _U_) +{ + gsm_map_stat_t *gm_stat = (gsm_map_stat_t *)tapdata; + const gsm_map_tap_rec_t *gm_tap_rec = (const gsm_map_tap_rec_t *)gmtr_ptr; + + if (gm_tap_rec->invoke) + { + gm_stat->opr_code[gm_tap_rec->opcode]++; + gm_stat->size[gm_tap_rec->opcode] += gm_tap_rec->size; + } + else + { + gm_stat->opr_code_rr[gm_tap_rec->opcode]++; + gm_stat->size_rr[gm_tap_rec->opcode] += gm_tap_rec->size; + } + + return(TAP_PACKET_DONT_REDRAW); /* We have no draw callback */ +} + +void +register_tap_listener_qt_gsm_map_summary(void) +{ + GString *err_p; + + memset((void *) &gsm_map_stat, 0, sizeof(gsm_map_stat_t)); + + err_p = + register_tap_listener("gsm_map", &gsm_map_stat, NULL, 0, + gsm_map_summary_reset, + gsm_map_summary_packet, + NULL, + NULL); + + if (err_p != NULL) + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_p->str); + g_string_free(err_p, TRUE); + + exit(1); + } +} + +} // extern "C" diff --git a/ui/qt/gsm_map_summary_dialog.h b/ui/qt/gsm_map_summary_dialog.h new file mode 100644 index 00000000..f7bc89f5 --- /dev/null +++ b/ui/qt/gsm_map_summary_dialog.h @@ -0,0 +1,41 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GSM_MAP_SUMMARY_DIALOG_H +#define GSM_MAP_SUMMARY_DIALOG_H + +#include "wireshark_dialog.h" + +namespace Ui { +class GsmMapSummaryDialog; +} + +class GsmMapSummaryDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit GsmMapSummaryDialog(QWidget &parent, CaptureFile& capture_file); + ~GsmMapSummaryDialog(); + +private: + Ui::GsmMapSummaryDialog *ui; + + QString summaryToHtml(); + +private slots: + void updateWidgets(); + +}; + +#endif // GSM_MAP_SUMMARY_DIALOG_H diff --git a/ui/qt/gsm_map_summary_dialog.ui b/ui/qt/gsm_map_summary_dialog.ui new file mode 100644 index 00000000..c9723dc4 --- /dev/null +++ b/ui/qt/gsm_map_summary_dialog.ui @@ -0,0 +1,71 @@ + + + GsmMapSummaryDialog + + + + 0 + 0 + 640 + 420 + + + + Dialog + + + + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + GsmMapSummaryDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + GsmMapSummaryDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/i18n.qrc.in b/ui/qt/i18n.qrc.in new file mode 100644 index 00000000..7260de04 --- /dev/null +++ b/ui/qt/i18n.qrc.in @@ -0,0 +1,4 @@ + + @i18n_qresource@ + + diff --git a/ui/qt/iax2_analysis_dialog.cpp b/ui/qt/iax2_analysis_dialog.cpp new file mode 100644 index 00000000..7e3fb82e --- /dev/null +++ b/ui/qt/iax2_analysis_dialog.cpp @@ -0,0 +1,1244 @@ +/* iax2_analysis_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "iax2_analysis_dialog.h" +#include + +#include "file.h" +#include "frame_tvbuff.h" + +#include +#include + +#include + +#include + +#include "ui/help_url.h" +#ifdef IAX2_RTP_STREAM_CHECK +#include "ui/rtp_stream.h" +#endif +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +/* + * @file RTP stream analysis dialog + * + * Displays forward and reverse RTP streams and graphs each stream + */ + +// To do: +// - Progress bar for tapping and saving. +// - Add a refresh button and/or action. +// - Fixup output file names. +// - Add a graph title and legend when saving? + +enum { + packet_col_, + delta_col_, + jitter_col_, + bandwidth_col_, + status_col_, + length_col_ +}; + +static const QRgb color_rtp_warn_ = 0xffdbbf; + +enum { iax2_analysis_type_ = 1000 }; +class Iax2AnalysisTreeWidgetItem : public QTreeWidgetItem +{ +public: + Iax2AnalysisTreeWidgetItem(QTreeWidget *tree, tap_iax2_stat_t *statinfo, packet_info *pinfo) : + QTreeWidgetItem(tree, iax2_analysis_type_), + frame_num_(pinfo->num), + pkt_len_(pinfo->fd->pkt_len), + flags_(statinfo->flags), + bandwidth_(statinfo->bandwidth), + ok_(false) + { + if (flags_ & STAT_FLAG_FIRST) { + delta_ = 0.0; + jitter_ = 0.0; + } else { + delta_ = statinfo->delta; + jitter_ = statinfo->jitter; + } + + QColor bg_color = QColor(); + QString status; + + if (statinfo->flags & STAT_FLAG_WRONG_SEQ) { + status = QObject::tr("Wrong sequence number"); + bg_color = ColorUtils::expert_color_error; + } else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) { + status = QObject::tr("Payload changed to PT=%1").arg(statinfo->pt); + bg_color = color_rtp_warn_; + } else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) { + status = QObject::tr("Incorrect timestamp"); + /* color = COLOR_WARNING; */ + bg_color = color_rtp_warn_; + } else if ((statinfo->flags & STAT_FLAG_PT_CHANGE) + && !(statinfo->flags & STAT_FLAG_FIRST) + && !(statinfo->flags & STAT_FLAG_PT_CN) + && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN) + && !(statinfo->flags & STAT_FLAG_MARKER)) { + status = QObject::tr("Marker missing?"); + bg_color = color_rtp_warn_; + } else { + if (statinfo->flags & STAT_FLAG_MARKER) { + bg_color = color_rtp_warn_; + } + } + + if (status.isEmpty()) { + ok_ = true; + status = UTF8_CHECK_MARK; + } + + setText(packet_col_, QString::number(frame_num_)); + setText(delta_col_, QString::number(delta_, 'f', 2)); + setText(jitter_col_, QString::number(jitter_, 'f', 2)); + setText(bandwidth_col_, QString::number(bandwidth_, 'f', 2)); + setText(status_col_, status); + setText(length_col_, QString::number(pkt_len_)); + + setTextAlignment(packet_col_, Qt::AlignRight); + setTextAlignment(delta_col_, Qt::AlignRight); + setTextAlignment(jitter_col_, Qt::AlignRight); + setTextAlignment(bandwidth_col_, Qt::AlignRight); + setTextAlignment(length_col_, Qt::AlignRight); + + if (bg_color.isValid()) { + for (int col = 0; col < columnCount(); col++) { + setBackground(col, bg_color); + setForeground(col, ColorUtils::expert_color_foreground); + } + } + } + + guint32 frameNum() { return frame_num_; } + bool frameStatus() { return ok_; } + + QList rowData() { + QString status_str = ok_ ? "OK" : text(status_col_); + + return QList() + << frame_num_ << delta_ << jitter_ << bandwidth_ + << status_str << pkt_len_; + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != iax2_analysis_type_) return QTreeWidgetItem::operator< (other); + const Iax2AnalysisTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case (packet_col_): + return frame_num_ < other_row->frame_num_; + break; + case (delta_col_): + return delta_ < other_row->delta_; + break; + case (jitter_col_): + return jitter_ < other_row->jitter_; + break; + case (bandwidth_col_): + return bandwidth_ < other_row->bandwidth_; + break; + case (length_col_): + return pkt_len_ < other_row->pkt_len_; + break; + default: + break; + } + + // Fall back to string comparison + return QTreeWidgetItem::operator <(other); + } +private: + guint32 frame_num_; + guint32 pkt_len_; + guint32 flags_; + double delta_; + double jitter_; + double bandwidth_; + bool ok_; +}; + +enum { + fwd_jitter_graph_, + fwd_diff_graph_, + rev_jitter_graph_, + rev_diff_graph_, + num_graphs_ +}; + +Iax2AnalysisDialog::Iax2AnalysisDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::Iax2AnalysisDialog), + save_payload_error_(TAP_IAX2_NO_ERROR) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 4 / 5); + setWindowSubtitle(tr("IAX2 Stream Analysis")); + + ui->progressFrame->hide(); + + stream_ctx_menu_.addAction(ui->actionGoToPacket); + stream_ctx_menu_.addAction(ui->actionNextProblem); + stream_ctx_menu_.addSeparator(); + stream_ctx_menu_.addAction(ui->actionSaveAudio); + stream_ctx_menu_.addAction(ui->actionSaveForwardAudio); + stream_ctx_menu_.addAction(ui->actionSaveReverseAudio); + stream_ctx_menu_.addSeparator(); + stream_ctx_menu_.addAction(ui->actionSaveCsv); + stream_ctx_menu_.addAction(ui->actionSaveForwardCsv); + stream_ctx_menu_.addAction(ui->actionSaveReverseCsv); + stream_ctx_menu_.addSeparator(); + stream_ctx_menu_.addAction(ui->actionSaveGraph); + set_action_shortcuts_visible_in_context_menu(stream_ctx_menu_.actions()); + + ui->forwardTreeWidget->installEventFilter(this); + ui->forwardTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->forwardTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showStreamMenu(QPoint))); + ui->reverseTreeWidget->installEventFilter(this); + ui->reverseTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->reverseTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showStreamMenu(QPoint))); + connect(ui->streamGraph, SIGNAL(mousePress(QMouseEvent*)), + this, SLOT(graphClicked(QMouseEvent*))); + + graph_ctx_menu_.addAction(ui->actionSaveGraph); + + QStringList header_labels; + for (int i = 0; i < ui->forwardTreeWidget->columnCount(); i++) { + header_labels << ui->forwardTreeWidget->headerItem()->text(i); + } + ui->reverseTreeWidget->setHeaderLabels(header_labels); + + memset(&fwd_id_, 0, sizeof(fwd_id_)); + memset(&rev_id_, 0, sizeof(rev_id_)); + + QList graph_cbs = QList() + << ui->fJitterCheckBox << ui->fDiffCheckBox + << ui->rJitterCheckBox << ui->rDiffCheckBox; + + for (int i = 0; i < num_graphs_; i++) { + QCPGraph *graph = ui->streamGraph->addGraph(); + graph->setPen(QPen(ColorUtils::graphColor(i))); + graph->setName(graph_cbs[i]->text()); + graphs_ << graph; + graph_cbs[i]->setChecked(true); + graph_cbs[i]->setIcon(StockIcon::colorIcon(ColorUtils::graphColor(i), QPalette::Text)); + } + ui->streamGraph->xAxis->setLabel("Arrival Time"); + ui->streamGraph->yAxis->setLabel("Value (ms)"); + + // We keep our temp files open for the lifetime of the dialog. The GTK+ + // UI opens and closes at various points. + QString tempname = QString("%1/wireshark_iax2_f").arg(QDir::tempPath()); + fwd_tempfile_ = new QTemporaryFile(tempname, this); + fwd_tempfile_->open(); + tempname = QString("%1/wireshark_iax2_r").arg(QDir::tempPath()); + rev_tempfile_ = new QTemporaryFile(tempname, this); + rev_tempfile_->open(); + + if (fwd_tempfile_->error() != QFile::NoError || rev_tempfile_->error() != QFile::NoError) { + err_str_ = tr("Unable to save RTP data."); + ui->actionSaveAudio->setEnabled(false); + ui->actionSaveForwardAudio->setEnabled(false); + ui->actionSaveReverseAudio->setEnabled(false); + } + + QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save); + QMenu *save_menu = new QMenu(save_bt); + save_menu->addAction(ui->actionSaveAudio); + save_menu->addAction(ui->actionSaveForwardAudio); + save_menu->addAction(ui->actionSaveReverseAudio); + save_menu->addSeparator(); + save_menu->addAction(ui->actionSaveCsv); + save_menu->addAction(ui->actionSaveForwardCsv); + save_menu->addAction(ui->actionSaveReverseCsv); + save_menu->addSeparator(); + save_menu->addAction(ui->actionSaveGraph); + save_bt->setMenu(save_menu); + + ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true); + + resetStatistics(); + updateStatistics(); // Initialize stats if an error occurs + +#if 0 + /* Only accept Voice or MiniPacket packets */ + const gchar filter_text[] = "iax2.call && (ip || ipv6)"; +#else + const gchar filter_text[] = "iax2 && (ip || ipv6)"; +#endif + dfilter_t *sfcode; + df_error_t *df_err; + + /* Try to compile the filter. */ + if (!dfilter_compile(filter_text, &sfcode, &df_err)) { + err_str_ = QString(df_err->msg); + df_error_free(&df_err); + updateWidgets(); + return; + } + + if (!cap_file_.capFile() || !cap_file_.capFile()->current_frame) { + dfilter_free(sfcode); + err_str_ = tr("Please select an IAX2 packet."); + save_payload_error_ = TAP_IAX2_NO_PACKET_SELECTED; + updateWidgets(); + return; + } + + if (!cf_read_current_record(cap_file_.capFile())) close(); + + frame_data *fdata = cap_file_.capFile()->current_frame; + + epan_dissect_t edt; + + epan_dissect_init(&edt, cap_file_.capFile()->epan, TRUE, FALSE); + epan_dissect_prime_with_dfilter(&edt, sfcode); + epan_dissect_run(&edt, cap_file_.capFile()->cd_t, &cap_file_.capFile()->rec, + frame_tvbuff_new_buffer(&cap_file_.capFile()->provider, fdata, &cap_file_.capFile()->buf), + fdata, NULL); + + // This shouldn't happen (the menu item should be disabled) but check anyway + if (!dfilter_apply_edt(sfcode, &edt)) { + epan_dissect_cleanup(&edt); + dfilter_free(sfcode); + err_str_ = tr("Please select an IAX2 packet."); + save_payload_error_ = TAP_IAX2_NO_PACKET_SELECTED; + updateWidgets(); + return; + } + + dfilter_free(sfcode); + + /* ok, it is a IAX2 frame, so let's get the ip and port values */ + rtpstream_id_copy_pinfo(&(edt.pi),&(fwd_id_),FALSE); + + /* assume the inverse ip/port combination for the reverse direction */ + rtpstream_id_copy_pinfo(&(edt.pi),&(rev_id_),TRUE); + + epan_dissect_cleanup(&edt); + +#ifdef IAX2_RTP_STREAM_CHECK + rtpstream_tapinfo_t tapinfo; + + /* Register the tap listener */ + rtpstream_info_init(&tapinfo); + + tapinfo.tap_data = this; + tapinfo.mode = TAP_ANALYSE; + +// register_tap_listener_rtpstream(&tapinfo, NULL); + /* Scan for RTP streams (redissect all packets) */ + rtpstream_scan(&tapinfo, cap_file_.capFile(), Q_NULLPTR); + + int num_streams = 0; + // TODO: Not used + //GList *filtered_list = NULL; + for (GList *strinfo_list = g_list_first(tapinfo.strinfo_list); strinfo_list; strinfo_list = gxx_list_next(strinfo_list)) { + rtpstream_info_t * strinfo = gxx_list_data(rtpstream_info_t*, strinfo_list); + if (rtpstream_id_equal(&(strinfo->id), &(fwd_id_),RTPSTREAM_ID_EQUAL_NONE)) + { + ++num_streams; + // TODO: Not used + //filtered_list = g_list_prepend(filtered_list, strinfo); + } + + if (rtpstream_id_equal(&(strinfo->id), &(rev_id_),RTPSTREAM_ID_EQUAL_NONE)) + { + ++num_streams; + // TODO: Not used + //filtered_list = g_list_append(filtered_list, strinfo); + } + + rtpstream_info_free_data(strinfo); + g_free(list->data); + } + g_list_free(tapinfo->strinfo_list); + + if (num_streams > 1) { + // Open the RTP streams dialog. + } +#endif + + connect(ui->tabWidget, SIGNAL(currentChanged(int)), + this, SLOT(updateWidgets())); + connect(ui->forwardTreeWidget, SIGNAL(itemSelectionChanged()), + this, SLOT(updateWidgets())); + connect(ui->reverseTreeWidget, SIGNAL(itemSelectionChanged()), + this, SLOT(updateWidgets())); + updateWidgets(); + + registerTapListener("IAX2", this, Q_NULLPTR, 0, tapReset, tapPacket, tapDraw); + cap_file_.retapPackets(); + removeTapListeners(); + + updateStatistics(); +} + +Iax2AnalysisDialog::~Iax2AnalysisDialog() +{ + delete ui; +// remove_tap_listener_rtpstream(&tapinfo); + delete fwd_tempfile_; + delete rev_tempfile_; +} + +void Iax2AnalysisDialog::updateWidgets() +{ + bool enable_tab = false; + QString hint = err_str_; + + if (hint.isEmpty() || save_payload_error_ != TAP_IAX2_NO_ERROR) { + /* We cannot save the payload but can still display the widget + or save CSV data */ + enable_tab = true; + } + + bool enable_nav = false; + if (!file_closed_ + && ((ui->tabWidget->currentWidget() == ui->forwardTreeWidget + && ui->forwardTreeWidget->selectedItems().length() > 0) + || (ui->tabWidget->currentWidget() == ui->reverseTreeWidget + && ui->reverseTreeWidget->selectedItems().length() > 0))) { + enable_nav = true; + } + ui->actionGoToPacket->setEnabled(enable_nav); + ui->actionNextProblem->setEnabled(enable_nav); + + if (enable_nav) { + hint.append(tr(" G: Go to packet, N: Next problem packet")); + } + + bool enable_save_fwd_audio = fwd_tempfile_->isOpen() && (save_payload_error_ == TAP_IAX2_NO_ERROR); + bool enable_save_rev_audio = rev_tempfile_->isOpen() && (save_payload_error_ == TAP_IAX2_NO_ERROR); + ui->actionSaveAudio->setEnabled(enable_save_fwd_audio && enable_save_rev_audio); + ui->actionSaveForwardAudio->setEnabled(enable_save_fwd_audio); + ui->actionSaveReverseAudio->setEnabled(enable_save_rev_audio); + + bool enable_save_fwd_csv = ui->forwardTreeWidget->topLevelItemCount() > 0; + bool enable_save_rev_csv = ui->reverseTreeWidget->topLevelItemCount() > 0; + ui->actionSaveCsv->setEnabled(enable_save_fwd_csv && enable_save_rev_csv); + ui->actionSaveForwardCsv->setEnabled(enable_save_fwd_csv); + ui->actionSaveReverseCsv->setEnabled(enable_save_rev_csv); + + ui->tabWidget->setEnabled(enable_tab); + hint.prepend(""); + hint.append(""); + ui->hintLabel->setText(hint); + + WiresharkDialog::updateWidgets(); +} + +void Iax2AnalysisDialog::on_actionGoToPacket_triggered() +{ + if (file_closed_) return; + QTreeWidget *cur_tree = qobject_cast(ui->tabWidget->currentWidget()); + if (!cur_tree || cur_tree->selectedItems().length() < 1) return; + + QTreeWidgetItem *ti = cur_tree->selectedItems()[0]; + if (ti->type() != iax2_analysis_type_) return; + + Iax2AnalysisTreeWidgetItem *ra_ti = dynamic_cast((Iax2AnalysisTreeWidgetItem *)ti); + emit goToPacket(ra_ti->frameNum()); +} + +void Iax2AnalysisDialog::on_actionNextProblem_triggered() +{ + QTreeWidget *cur_tree = qobject_cast(ui->tabWidget->currentWidget()); + if (!cur_tree || cur_tree->topLevelItemCount() < 2) return; + + // Choose convenience over correctness. + if (cur_tree->selectedItems().length() < 1) { + cur_tree->setCurrentItem(cur_tree->topLevelItem(0)); + } + + QTreeWidgetItem *sel_ti = cur_tree->selectedItems()[0]; + if (sel_ti->type() != iax2_analysis_type_) return; + QTreeWidgetItem *test_ti = cur_tree->itemBelow(sel_ti); + while (test_ti != sel_ti) { + if (!test_ti) test_ti = cur_tree->topLevelItem(0); + Iax2AnalysisTreeWidgetItem *ra_ti = dynamic_cast((Iax2AnalysisTreeWidgetItem *)test_ti); + if (!ra_ti->frameStatus()) { + cur_tree->setCurrentItem(ra_ti); + break; + } + + test_ti = cur_tree->itemBelow(test_ti); + } +} + +void Iax2AnalysisDialog::on_fJitterCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(fwd_jitter_graph_)->setVisible(checked); + updateGraph(); +} + +void Iax2AnalysisDialog::on_fDiffCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(fwd_diff_graph_)->setVisible(checked); + updateGraph(); +} + +void Iax2AnalysisDialog::on_rJitterCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(rev_jitter_graph_)->setVisible(checked); + updateGraph(); +} + +void Iax2AnalysisDialog::on_rDiffCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(rev_diff_graph_)->setVisible(checked); + updateGraph(); +} + +void Iax2AnalysisDialog::on_actionSaveAudio_triggered() +{ + saveAudio(dir_both_); +} + +void Iax2AnalysisDialog::on_actionSaveForwardAudio_triggered() +{ + saveAudio(dir_forward_); +} + +void Iax2AnalysisDialog::on_actionSaveReverseAudio_triggered() +{ + saveAudio(dir_reverse_); +} + +void Iax2AnalysisDialog::on_actionSaveCsv_triggered() +{ + saveCsv(dir_both_); +} + +void Iax2AnalysisDialog::on_actionSaveForwardCsv_triggered() +{ + saveCsv(dir_forward_); +} + +void Iax2AnalysisDialog::on_actionSaveReverseCsv_triggered() +{ + saveCsv(dir_reverse_); +} + +void Iax2AnalysisDialog::on_actionSaveGraph_triggered() +{ + ui->tabWidget->setCurrentWidget(ui->graphTab); + + QString file_name, extension; + QDir path(mainApp->openDialogInitialDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + 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)"); + QString filter = QString("%1;;%2;;%3;;%4") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter); + + QString save_file = path.canonicalPath(); + if (!file_closed_) { + save_file += QString("/%1").arg(cap_file_.fileBaseName()); + } + file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Graph As…")), + save_file, filter, &extension); + + if (!file_name.isEmpty()) { + bool save_ok = false; + // https://www.qcustomplot.com/index.php/support/forum/63 +// ui->streamGraph->legend->setVisible(true); + if (extension.compare(pdf_filter) == 0) { + save_ok = ui->streamGraph->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = ui->streamGraph->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = ui->streamGraph->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = ui->streamGraph->saveJpg(file_name); + } +// ui->streamGraph->legend->setVisible(false); + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } +} + +void Iax2AnalysisDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_IAX2_ANALYSIS_DIALOG); +} + +void Iax2AnalysisDialog::tapReset(void *tapinfoptr) +{ + Iax2AnalysisDialog *iax2_analysis_dialog = dynamic_cast((Iax2AnalysisDialog*)tapinfoptr); + if (!iax2_analysis_dialog) return; + + iax2_analysis_dialog->resetStatistics(); +} + +tap_packet_status Iax2AnalysisDialog::tapPacket(void *tapinfoptr, packet_info *pinfo, struct epan_dissect *, const void *iax2info_ptr, tap_flags_t) +{ + Iax2AnalysisDialog *iax2_analysis_dialog = dynamic_cast((Iax2AnalysisDialog*)tapinfoptr); + if (!iax2_analysis_dialog) return TAP_PACKET_DONT_REDRAW; + + const iax2_info_t *iax2info = (const iax2_info_t *)iax2info_ptr; + if (!iax2info) return TAP_PACKET_DONT_REDRAW; + + /* we ignore packets that are not displayed */ + if (pinfo->fd->passed_dfilter == 0) + return TAP_PACKET_DONT_REDRAW; + + /* we ignore packets that carry no data */ + if (iax2info->payload_len < 1) + return TAP_PACKET_DONT_REDRAW; + + /* is it the forward direction? */ + else if ((cmp_address(&(iax2_analysis_dialog->fwd_id_.src_addr), &(pinfo->src)) == 0) + && (iax2_analysis_dialog->fwd_id_.src_port == pinfo->srcport) + && (cmp_address(&(iax2_analysis_dialog->fwd_id_.dst_addr), &(pinfo->dst)) == 0) + && (iax2_analysis_dialog->fwd_id_.dst_port == pinfo->destport)) { + + iax2_analysis_dialog->addPacket(true, pinfo, iax2info); + } + /* is it the reversed direction? */ + else if ((cmp_address(&(iax2_analysis_dialog->rev_id_.src_addr), &(pinfo->src)) == 0) + && (iax2_analysis_dialog->rev_id_.src_port == pinfo->srcport) + && (cmp_address(&(iax2_analysis_dialog->rev_id_.dst_addr), &(pinfo->dst)) == 0) + && (iax2_analysis_dialog->rev_id_.dst_port == pinfo->destport)) { + + iax2_analysis_dialog->addPacket(false, pinfo, iax2info); + } + return TAP_PACKET_DONT_REDRAW; +} + +void Iax2AnalysisDialog::tapDraw(void *tapinfoptr) +{ + Iax2AnalysisDialog *iax2_analysis_dialog = dynamic_cast((Iax2AnalysisDialog*)tapinfoptr); + if (!iax2_analysis_dialog) return; + iax2_analysis_dialog->updateStatistics(); +} + +void Iax2AnalysisDialog::resetStatistics() +{ + memset(&fwd_statinfo_, 0, sizeof(fwd_statinfo_)); + memset(&rev_statinfo_, 0, sizeof(rev_statinfo_)); + + fwd_statinfo_.first_packet = TRUE; + rev_statinfo_.first_packet = TRUE; + fwd_statinfo_.reg_pt = PT_UNDEFINED; + rev_statinfo_.reg_pt = PT_UNDEFINED; + + ui->forwardTreeWidget->clear(); + ui->reverseTreeWidget->clear(); + + for (int i = 0; i < ui->streamGraph->graphCount(); i++) { + ui->streamGraph->graph(i)->data()->clear(); + } + + fwd_time_vals_.clear(); + fwd_jitter_vals_.clear(); + fwd_diff_vals_.clear(); + rev_time_vals_.clear(); + rev_jitter_vals_.clear(); + rev_diff_vals_.clear(); + + fwd_tempfile_->resize(0); + rev_tempfile_->resize(0); +} + +void Iax2AnalysisDialog::addPacket(bool forward, packet_info *pinfo, const struct _iax2_info_t *iax2info) +{ + /* add this RTP for future listening using the RTP Player*/ +// add_rtp_packet(rtpinfo, pinfo); + + if (forward) { + iax2_packet_analyse(&fwd_statinfo_, pinfo, iax2info); + new Iax2AnalysisTreeWidgetItem(ui->forwardTreeWidget, &fwd_statinfo_, pinfo); + + fwd_time_vals_.append((fwd_statinfo_.time)); + fwd_jitter_vals_.append(fwd_statinfo_.jitter * 1000); + fwd_diff_vals_.append(fwd_statinfo_.diff * 1000); + + savePayload(fwd_tempfile_, pinfo, iax2info); + } else { + iax2_packet_analyse(&rev_statinfo_, pinfo, iax2info); + new Iax2AnalysisTreeWidgetItem(ui->reverseTreeWidget, &rev_statinfo_, pinfo); + + rev_time_vals_.append((rev_statinfo_.time)); + rev_jitter_vals_.append(rev_statinfo_.jitter * 1000); + rev_diff_vals_.append(rev_statinfo_.diff * 1000); + + savePayload(rev_tempfile_, pinfo, iax2info); + } + +} + +// iax2_analysis.c:rtp_packet_save_payload +const guint8 silence_pcmu_ = 0xff; +const guint8 silence_pcma_ = 0x55; +void Iax2AnalysisDialog::savePayload(QTemporaryFile *tmpfile, packet_info *pinfo, const struct _iax2_info_t *iax2info) +{ + /* Is this the first packet we got in this direction? */ +// if (statinfo->flags & STAT_FLAG_FIRST) { +// if (saveinfo->fp == NULL) { +// saveinfo->saved = FALSE; +// saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR; +// } else { +// saveinfo->saved = TRUE; +// } +// } + + /* Save the voice information */ + + /* If there was already an error, we quit */ + if (!tmpfile->isOpen() || tmpfile->error() != QFile::NoError) return; + + /* Quit if the captured length and packet length aren't equal. + */ + if (pinfo->fd->pkt_len != pinfo->fd->cap_len) { + tmpfile->close(); + err_str_ = tr("Can't save in a file: Wrong length of captured packets."); + save_payload_error_ = TAP_IAX2_WRONG_LENGTH; + return; + } + + if (iax2info->payload_len > 0) { + const char *data = (const char *) iax2info->payload_data; + qint64 nchars; + + nchars = tmpfile->write(data, iax2info->payload_len); + if (nchars != (iax2info->payload_len)) { + /* Write error or short write */ + err_str_ = tr("Can't save in a file: File I/O problem."); + save_payload_error_ = TAP_IAX2_FILE_IO_ERROR; + tmpfile->close(); + return; + } + return; + } + return; +} + +void Iax2AnalysisDialog::updateStatistics() +{ + double f_duration = fwd_statinfo_.time - fwd_statinfo_.start_time; // s + double r_duration = rev_statinfo_.time - rev_statinfo_.start_time; +#if 0 // Marked as "TODO" in tap-iax2-analysis.c:128 + unsigned int f_expected = fwd_statinfo_.stop_seq_nr - fwd_statinfo_.start_seq_nr + 1; + unsigned int r_expected = rev_statinfo_.stop_seq_nr - rev_statinfo_.start_seq_nr + 1; + int f_lost = f_expected - fwd_statinfo_.total_nr; + int r_lost = r_expected - rev_statinfo_.total_nr; + double f_perc, r_perc; + + if (f_expected) { + f_perc = (double)(f_lost*100)/(double)f_expected; + } else { + f_perc = 0; + } + if (r_expected) { + r_perc = (double)(r_lost*100)/(double)r_expected; + } else { + r_perc = 0; + } +#endif + + QString stats_tables = "\n"; + stats_tables += QString("

%1:%2 " UTF8_LEFT_RIGHT_ARROW) + .arg(address_to_qstring(&fwd_id_.src_addr, true)) + .arg(fwd_id_.src_port); + stats_tables += QString("
%1:%2

\n") + .arg(address_to_qstring(&fwd_id_.dst_addr, true)) + .arg(fwd_id_.dst_port); + stats_tables += "

Forward

\n"; + stats_tables += "

\n"; + stats_tables += QString("") + .arg(fwd_statinfo_.max_delta, 0, 'f', 2) + .arg(fwd_statinfo_.max_nr); + stats_tables += QString("") + .arg(fwd_statinfo_.max_jitter, 0, 'f', 2); + stats_tables += QString("") + .arg(fwd_statinfo_.mean_jitter, 0, 'f', 2); + stats_tables += QString("") + .arg(fwd_statinfo_.total_nr); +#if 0 + stats_tables += QString("") + .arg(f_expected); + stats_tables += QString("") + .arg(f_lost).arg(f_perc, 0, 'f', 2); + stats_tables += QString("") + .arg(fwd_statinfo_.sequence); +#endif + stats_tables += QString("") + .arg(f_duration, 0, 'f', 2); + stats_tables += "
Max Delta%1 ms @ %2
Max Jitter%1 ms
Mean Jitter%1 ms
IAX2 Packets%1
Expected%1
Lost%1 (%2 %)
Seq Errs%1
Duration%1 s

\n"; + + stats_tables += "

Reverse

\n"; + stats_tables += "

\n"; + stats_tables += QString("") + .arg(rev_statinfo_.max_delta, 0, 'f', 2) + .arg(rev_statinfo_.max_nr); + stats_tables += QString("") + .arg(rev_statinfo_.max_jitter, 0, 'f', 2); + stats_tables += QString("") + .arg(rev_statinfo_.mean_jitter, 0, 'f', 2); + stats_tables += QString("") + .arg(rev_statinfo_.total_nr); +#if 0 + stats_tables += QString("") + .arg(r_expected); + stats_tables += QString("") + .arg(r_lost).arg(r_perc, 0, 'f', 2); + stats_tables += QString("") + .arg(rev_statinfo_.sequence); +#endif + stats_tables += QString("") + .arg(r_duration, 0, 'f', 2); + stats_tables += "
Max Delta%1 ms @ %2
Max Jitter%1 ms
Mean Jitter%1 ms
IAX2 Packets%1
Expected%1
Lost%1 (%2 %)
Seq Errs%1
Duration%1 s

\n"; + + ui->statisticsLabel->setText(stats_tables); + + for (int col = 0; col < ui->forwardTreeWidget->columnCount() - 1; col++) { + ui->forwardTreeWidget->resizeColumnToContents(col); + ui->reverseTreeWidget->resizeColumnToContents(col); + } + + graphs_[fwd_jitter_graph_]->setData(fwd_time_vals_, fwd_jitter_vals_); + graphs_[fwd_diff_graph_]->setData(fwd_time_vals_, fwd_diff_vals_); + graphs_[rev_jitter_graph_]->setData(rev_time_vals_, rev_jitter_vals_); + graphs_[rev_diff_graph_]->setData(rev_time_vals_, rev_diff_vals_); + + updateGraph(); + + updateWidgets(); +} + +void Iax2AnalysisDialog::updateGraph() +{ + for (int i = 0; i < ui->streamGraph->graphCount(); i++) { + if (ui->streamGraph->graph(i)->visible()) { + ui->streamGraph->graph(i)->rescaleAxes(i > 0); + } + } + ui->streamGraph->replot(); +} + +// iax2_analysis.c:copy_file +enum { save_audio_none_, save_audio_au_, save_audio_raw_ }; +void Iax2AnalysisDialog::saveAudio(Iax2AnalysisDialog::StreamDirection direction) +{ + if (!fwd_tempfile_->isOpen() || !rev_tempfile_->isOpen()) return; + + QString caption; + + switch (direction) { + case dir_forward_: + caption = tr("Save forward stream audio"); + break; + case dir_reverse_: + caption = tr("Save reverse stream audio"); + break; + case dir_both_: + default: + caption = tr("Save audio"); + break; + } + + QString ext_filter = tr("Sun Audio (*.au)"); + if (direction != dir_both_) { + ext_filter.append(tr(";;Raw (*.raw)")); + } + QString sel_filter; + QString file_path = WiresharkFileDialog::getSaveFileName( + this, caption, mainApp->openDialogInitialDir().absoluteFilePath("Saved RTP Audio.au"), + ext_filter, &sel_filter); + + if (file_path.isEmpty()) return; + + int save_format = save_audio_none_; + if (file_path.endsWith(".au")) { + save_format = save_audio_au_; + } else if (file_path.endsWith(".raw")) { + save_format = save_audio_raw_; + } + + if (save_format == save_audio_none_) { + QMessageBox::warning(this, tr("Warning"), tr("Unable to save in that format")); + return; + } + + QFile save_file(file_path); + gint16 sample; + guint8 pd[4]; + gboolean stop_flag = FALSE; + qint64 nchars; + + save_file.open(QIODevice::WriteOnly); + fwd_tempfile_->seek(0); + rev_tempfile_->seek(0); + + if (save_file.error() != QFile::NoError) { + QMessageBox::warning(this, tr("Warning"), tr("Unable to save %1").arg(save_file.fileName())); + return; + } + + ui->hintLabel->setText(tr("Saving %1…").arg(save_file.fileName())); + ui->progressFrame->showProgress(tr("Analyzing IAX2"), true, true, &stop_flag); + + if (save_format == save_audio_au_) { /* au format; https://pubs.opengroup.org/external/auformat.html */ + /* First we write the .au header. All values in the header are + * 4-byte big-endian values, so we use pntoh32() to copy them + * to a 4-byte buffer, in big-endian order, and then write out + * the buffer. */ + + /* the magic word 0x2e736e64 == .snd */ + phton32(pd, 0x2e736e64); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* header offset == 24 bytes */ + phton32(pd, 24); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* total length; it is permitted to set this to 0xffffffff */ + phton32(pd, 0xffffffff); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* encoding format == 16-bit linear PCM */ + phton32(pd, 3); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* sample rate == 8000 Hz */ + phton32(pd, 8000); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* channels == 1 */ + phton32(pd, 1); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + + switch (direction) { + /* Only forward direction */ + case dir_forward_: + { + char f_rawvalue; + while (fwd_tempfile_->getChar(&f_rawvalue)) { + if (stop_flag) { + break; + } + ui->progressFrame->setValue(int(fwd_tempfile_->pos() * 100 / fwd_tempfile_->size())); + + if (fwd_statinfo_.pt == PT_PCMU) { + sample = ulaw2linear((unsigned char)f_rawvalue); + phton16(pd, sample); + } else if (fwd_statinfo_.pt == PT_PCMA) { + sample = alaw2linear((unsigned char)f_rawvalue); + phton16(pd, sample); + } else { + goto copy_file_err; + } + + nchars = save_file.write((const char *)pd, 2); + if (nchars < 2) { + goto copy_file_err; + } + } + break; + } + /* Only reverse direction */ + case dir_reverse_: + { + char r_rawvalue; + while (rev_tempfile_->getChar(&r_rawvalue)) { + if (stop_flag) { + break; + } + ui->progressFrame->setValue(int(rev_tempfile_->pos() * 100 / rev_tempfile_->size())); + + if (rev_statinfo_.pt == PT_PCMU) { + sample = ulaw2linear((unsigned char)r_rawvalue); + phton16(pd, sample); + } else if (rev_statinfo_.pt == PT_PCMA) { + sample = alaw2linear((unsigned char)r_rawvalue); + phton16(pd, sample); + } else { + goto copy_file_err; + } + + nchars = save_file.write((const char *)pd, 2); + if (nchars < 2) { + goto copy_file_err; + } + } + break; + } + /* Both directions */ + case dir_both_: + { + char f_rawvalue, r_rawvalue; + guint32 f_write_silence = 0; + guint32 r_write_silence = 0; + /* since conversation in one way can start later than in the other one, + * we have to write some silence information for one channel */ + if (fwd_statinfo_.start_time > rev_statinfo_.start_time) { + f_write_silence = (guint32) + ((fwd_statinfo_.start_time - rev_statinfo_.start_time) + * (8000/1000)); + } else if (fwd_statinfo_.start_time < rev_statinfo_.start_time) { + r_write_silence = (guint32) + ((rev_statinfo_.start_time - fwd_statinfo_.start_time) + * (8000/1000)); + } + for (;;) { + if (stop_flag) { + break; + } + int fwd_pct = int(fwd_tempfile_->pos() * 100 / fwd_tempfile_->size()); + int rev_pct = int(rev_tempfile_->pos() * 100 / rev_tempfile_->size()); + ui->progressFrame->setValue(qMin(fwd_pct, rev_pct)); + + if (f_write_silence > 0) { + rev_tempfile_->getChar(&r_rawvalue); + switch (fwd_statinfo_.reg_pt) { + case PT_PCMU: + f_rawvalue = silence_pcmu_; + break; + case PT_PCMA: + f_rawvalue = silence_pcma_; + break; + default: + f_rawvalue = 0; + break; + } + f_write_silence--; + } else if (r_write_silence > 0) { + fwd_tempfile_->getChar(&f_rawvalue); + switch (rev_statinfo_.reg_pt) { + case PT_PCMU: + r_rawvalue = silence_pcmu_; + break; + case PT_PCMA: + r_rawvalue = silence_pcma_; + break; + default: + r_rawvalue = 0; + break; + } + r_write_silence--; + } else { + fwd_tempfile_->getChar(&f_rawvalue); + rev_tempfile_->getChar(&r_rawvalue); + } + if (fwd_tempfile_->atEnd() && rev_tempfile_->atEnd()) + break; + if ((fwd_statinfo_.pt == PT_PCMU) + && (rev_statinfo_.pt == PT_PCMU)) { + sample = (ulaw2linear((unsigned char)r_rawvalue) + + ulaw2linear((unsigned char)f_rawvalue)) / 2; + phton16(pd, sample); + } + else if ((fwd_statinfo_.pt == PT_PCMA) + && (rev_statinfo_.pt == PT_PCMA)) { + sample = (alaw2linear((unsigned char)r_rawvalue) + + alaw2linear((unsigned char)f_rawvalue)) / 2; + phton16(pd, sample); + } else { + goto copy_file_err; + } + + nchars = save_file.write((const char *)pd, 2); + if (nchars < 2) { + goto copy_file_err; + } + } + } + } + } else if (save_format == save_audio_raw_) { /* raw format */ + QFile *tempfile; + int progress_pct; + + switch (direction) { + /* Only forward direction */ + case dir_forward_: { + progress_pct = int(fwd_tempfile_->pos() * 100 / fwd_tempfile_->size()); + tempfile = fwd_tempfile_; + break; + } + /* only reversed direction */ + case dir_reverse_: { + progress_pct = int(rev_tempfile_->pos() * 100 / rev_tempfile_->size()); + tempfile = rev_tempfile_; + break; + } + default: { + goto copy_file_err; + } + } + + qsizetype chunk_size = 65536; + /* XXX how do you just copy the file? */ + while (chunk_size > 0) { + if (stop_flag) + break; + QByteArray bytes = tempfile->read(chunk_size); + ui->progressFrame->setValue(progress_pct); + + if (!save_file.write(bytes)) { + goto copy_file_err; + } + chunk_size = bytes.length(); + } + } + +copy_file_err: + ui->progressFrame->hide(); + updateWidgets(); + return; +} + +// XXX The GTK+ UI saves the length and timestamp. +void Iax2AnalysisDialog::saveCsv(Iax2AnalysisDialog::StreamDirection direction) +{ + QString caption; + + switch (direction) { + case dir_forward_: + caption = tr("Save forward stream CSV"); + break; + case dir_reverse_: + caption = tr("Save reverse stream CSV"); + break; + case dir_both_: + default: + caption = tr("Save CSV"); + break; + } + + QString file_path = WiresharkFileDialog::getSaveFileName( + this, caption, mainApp->openDialogInitialDir().absoluteFilePath("RTP Packet Data.csv"), + tr("Comma-separated values (*.csv)")); + + if (file_path.isEmpty()) return; + + QFile save_file(file_path); + save_file.open(QFile::WriteOnly); + + if (direction == dir_forward_ || direction == dir_both_) { + save_file.write("Forward\n"); + + for (int row = 0; row < ui->forwardTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->forwardTreeWidget->topLevelItem(row); + if (ti->type() != iax2_analysis_type_) continue; + Iax2AnalysisTreeWidgetItem *ra_ti = dynamic_cast((Iax2AnalysisTreeWidgetItem *)ti); + QStringList values; + foreach (QVariant v, ra_ti->rowData()) { + if (!v.isValid()) { + values << "\"\""; + } else if (v.userType() == QMetaType::QString) { + values << QString("\"%1\"").arg(v.toString()); + } else { + values << v.toString(); + } + } + save_file.write(values.join(",").toUtf8()); + save_file.write("\n"); + } + } + if (direction == dir_both_) { + save_file.write("\n"); + } + if (direction == dir_reverse_ || direction == dir_both_) { + save_file.write("Reverse\n"); + + for (int row = 0; row < ui->reverseTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->reverseTreeWidget->topLevelItem(row); + if (ti->type() != iax2_analysis_type_) continue; + Iax2AnalysisTreeWidgetItem *ra_ti = dynamic_cast((Iax2AnalysisTreeWidgetItem *)ti); + QStringList values; + foreach (QVariant v, ra_ti->rowData()) { + if (!v.isValid()) { + values << "\"\""; + } else if (v.userType() == QMetaType::QString) { + values << QString("\"%1\"").arg(v.toString()); + } else { + values << v.toString(); + } + } + save_file.write(values.join(",").toUtf8()); + save_file.write("\n"); + } + } +} + +bool Iax2AnalysisDialog::eventFilter(QObject *, QEvent *event) +{ + if (event->type() != QEvent::KeyPress) return false; + + QKeyEvent *kevt = static_cast(event); + + switch(kevt->key()) { + case Qt::Key_G: + on_actionGoToPacket_triggered(); + return true; + case Qt::Key_N: + on_actionNextProblem_triggered(); + return true; + default: + break; + } + return false; +} + +void Iax2AnalysisDialog::graphClicked(QMouseEvent *event) +{ + updateWidgets(); + if (event->button() == Qt::RightButton) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + graph_ctx_menu_.popup(event->globalPosition().toPoint()); +#else + graph_ctx_menu_.popup(event->globalPos()); +#endif + } +} + +void Iax2AnalysisDialog::showStreamMenu(QPoint pos) +{ + QTreeWidget *cur_tree = qobject_cast(ui->tabWidget->currentWidget()); + if (!cur_tree) return; + + updateWidgets(); + stream_ctx_menu_.popup(cur_tree->viewport()->mapToGlobal(pos)); +} diff --git a/ui/qt/iax2_analysis_dialog.h b/ui/qt/iax2_analysis_dialog.h new file mode 100644 index 00000000..c6daedd0 --- /dev/null +++ b/ui/qt/iax2_analysis_dialog.h @@ -0,0 +1,130 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IAX2_ANALYSIS_DIALOG_H +#define IAX2_ANALYSIS_DIALOG_H + +// The GTK+ UI checks for multiple RTP streams, and if found opens the RTP +// stream dialog. That seems to violate the principle of least surprise. +// Migrate the code but disable it. +// #define IAX2_RTP_STREAM_CHECK + +#include + +#include + +#include + +#include "ui/tap-iax2-analysis.h" +#include "ui/rtp_stream_id.h" + +#include +#include + +#include "wireshark_dialog.h" + +namespace Ui { +class Iax2AnalysisDialog; +} + +class QCPGraph; +class QTemporaryFile; + +typedef enum { + TAP_IAX2_NO_ERROR, + TAP_IAX2_NO_PACKET_SELECTED, + TAP_IAX2_WRONG_LENGTH, + TAP_IAX2_FILE_IO_ERROR +} iax2_error_type_t; + + +class Iax2AnalysisDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit Iax2AnalysisDialog(QWidget &parent, CaptureFile &cf); + ~Iax2AnalysisDialog(); + +signals: + void goToPacket(int packet_num); + +protected slots: + virtual void updateWidgets(); + +private slots: + void on_actionGoToPacket_triggered(); + void on_actionNextProblem_triggered(); + void on_fJitterCheckBox_toggled(bool checked); + void on_fDiffCheckBox_toggled(bool checked); + void on_rJitterCheckBox_toggled(bool checked); + void on_rDiffCheckBox_toggled(bool checked); + void on_actionSaveAudio_triggered(); + void on_actionSaveForwardAudio_triggered(); + void on_actionSaveReverseAudio_triggered(); + void on_actionSaveCsv_triggered(); + void on_actionSaveForwardCsv_triggered(); + void on_actionSaveReverseCsv_triggered(); + void on_actionSaveGraph_triggered(); + void on_buttonBox_helpRequested(); + void showStreamMenu(QPoint pos); + void graphClicked(QMouseEvent *event); + +private: + Ui::Iax2AnalysisDialog *ui; + enum StreamDirection { dir_both_, dir_forward_, dir_reverse_ }; + + rtpstream_id_t fwd_id_; + rtpstream_id_t rev_id_; + + tap_iax2_stat_t fwd_statinfo_; + tap_iax2_stat_t rev_statinfo_; + + QTemporaryFile *fwd_tempfile_; + QTemporaryFile *rev_tempfile_; + + // Graph data for QCustomPlot + QListgraphs_; + QVector fwd_time_vals_; + QVector fwd_jitter_vals_; + QVector fwd_diff_vals_; + + QVector rev_time_vals_; + QVector rev_jitter_vals_; + QVector rev_diff_vals_; + + QString err_str_; + iax2_error_type_t save_payload_error_; + + QMenu stream_ctx_menu_; + QMenu graph_ctx_menu_; + + // Tap callbacks + static void tapReset(void *tapinfo_ptr); + static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, struct epan_dissect *, const void *iax2info_ptr, tap_flags_t flags); + static void tapDraw(void *tapinfo_ptr); + + void resetStatistics(); + void addPacket(bool forward, packet_info *pinfo, const struct _iax2_info_t *iax2info); + void savePayload(QTemporaryFile *tmpfile, packet_info *pinfo, const struct _iax2_info_t *iax2info); + void updateStatistics(); + void updateGraph(); + + void saveAudio(StreamDirection direction); + void saveCsv(StreamDirection direction); + +#if 0 + guint32 processNode(proto_node *ptree_node, header_field_info *hfinformation, const gchar* proto_field, bool *ok); + guint32 getIntFromProtoTree(proto_tree *protocol_tree, const gchar *proto_name, const gchar *proto_field, bool *ok); +#endif + + bool eventFilter(QObject*, QEvent* event); +}; + +#endif // IAX2_ANALYSIS_DIALOG_H diff --git a/ui/qt/iax2_analysis_dialog.ui b/ui/qt/iax2_analysis_dialog.ui new file mode 100644 index 00000000..9a3c4428 --- /dev/null +++ b/ui/qt/iax2_analysis_dialog.ui @@ -0,0 +1,391 @@ + + + Iax2AnalysisDialog + + + + 0 + 0 + 650 + 475 + + + + Dialog + + + + + + + + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + + + Qt::RichText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + + false + + + false + + + false + + + Forward + + + + Packet + + + + + Delta (ms) + + + + + Jitter (ms) + + + + + Bandwidth + + + + + Status + + + + + Length + + + + + + false + + + false + + + Reverse + + + + 1 + + + + + + Graph + + + + + + + + + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + + + Forward Jitter + + + + + + + Qt::Horizontal + + + + 10 + 5 + + + + + + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + + + Forward Difference + + + + + + + Qt::Horizontal + + + + 10 + 5 + + + + + + + + + + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + + + Reverse Jitter + + + + + + + Qt::Horizontal + + + + 10 + 5 + + + + + + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + + + Reverse Difference + + + + + + + Qt::Horizontal + + + + 10 + 5 + + + + + + + + + + + + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save + + + + + + + Audio + + + Save the audio data for both channels. + + + + + Forward Stream Audio + + + Save the forward stream audio data. + + + + + Reverse Stream Audio + + + Save the reverse stream audio data. + + + + + CSV + + + Save both tables as CSV. + + + + + Forward Stream CSV + + + Save the forward table as CSV. + + + + + Reverse Stream CSV + + + Save the reverse table as CSV. + + + + + Save Graph + + + Save the graph image. + + + + + Go to Packet + + + Select the corresponding packet in the packet list. + + + G + + + + + Next Problem Packet + + + Go to the next problem packet + + + N + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+ + ProgressFrame + QFrame +
progress_frame.h
+ 1 +
+
+ + + + buttonBox + accepted() + Iax2AnalysisDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Iax2AnalysisDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/import_text_dialog.cpp b/ui/qt/import_text_dialog.cpp new file mode 100644 index 00000000..0916c417 --- /dev/null +++ b/ui/qt/import_text_dialog.cpp @@ -0,0 +1,1089 @@ +/* import_text_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "import_text_dialog.h" + +#include "wiretap/wtap.h" +#include "wiretap/pcap-encap.h" + +#include + +#include "ui/text_import_scanner.h" +#include "ui/util.h" +#include "ui/alert_box.h" +#include "ui/help_url.h" +#include "ui/capture_globals.h" + +#include "file.h" +#include "wsutil/file_util.h" +#include "wsutil/inet_addr.h" +#include "wsutil/time_util.h" +#include "wsutil/tempfile.h" +#include "wsutil/filesystem.h" + +#include +#include "main_application.h" +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include + +#define HINT_BEGIN "" +#define HINT_END "" +#define HTML_LT "<" +#define HTML_GT ">" + +static const QString default_regex_hint = ImportTextDialog::tr("Supported fields are data, dir, time, seqno"); +static const QString missing_data_hint = ImportTextDialog::tr("Missing capturing group data (use (?" HTML_LT "data" HTML_GT "(...)) )"); + +#define SETTINGS_FILE "import_hexdump.json" + +ImportTextDialog::ImportTextDialog(QWidget *parent) : + QDialog(parent), + ti_ui_(new Ui::ImportTextDialog), + import_info_(), + file_ok_(false), + timestamp_format_ok_(true), + regex_ok_(false), + re_has_dir_(false), + in_indication_ok_(false), + out_indication_ok_(false), + re_has_time_(false), + ether_type_ok_(true), + proto_ok_(true), + source_addr_ok_(true), + dest_addr_ok_(true), + source_port_ok_(true), + dest_port_ok_(true), + tag_ok_(true), + ppi_ok_(true), + payload_ok_(true), + max_len_ok_(true) +{ + int encap; + int i; + int file_type_subtype; + + ti_ui_->setupUi(this); + setWindowTitle(mainApp->windowTitleString(tr("Import From Hex Dump"))); + memset(&import_info_, 0, sizeof(import_info_)); + + import_button_ = ti_ui_->buttonBox->button(QDialogButtonBox::Open); + import_button_->setText(tr("Import")); + import_button_->setEnabled(false); + + ti_ui_->regexHintLabel->setSmallText(true); + +#ifdef Q_OS_MAC + // The grid layout squishes each line edit otherwise. + int le_height = ti_ui_->textFileLineEdit->sizeHint().height(); + ti_ui_->ethertypeLineEdit->setMinimumHeight(le_height); + ti_ui_->protocolLineEdit->setMinimumHeight(le_height); + ti_ui_->sourceAddressLineEdit->setMinimumHeight(le_height); + ti_ui_->destinationAddressLineEdit->setMinimumHeight(le_height); + ti_ui_->sourcePortLineEdit->setMinimumHeight(le_height); + ti_ui_->destinationPortLineEdit->setMinimumHeight(le_height); + ti_ui_->tagLineEdit->setMinimumHeight(le_height); + ti_ui_->ppiLineEdit->setMinimumHeight(le_height); +#endif + + on_timestampFormatLineEdit_textChanged(ti_ui_->timestampFormatLineEdit->text()); + + encap_buttons = new QButtonGroup(this); + for (i = 0; i < ti_ui_->headerGridLayout->count(); i++) { + QRadioButton *rb = qobject_cast(ti_ui_->headerGridLayout->itemAt(i)->widget()); + + if (rb) encap_buttons->addButton(rb); + } + /* There are two QButtonGroup::buttonToggled signals from Qt 5.2-5.15 with + * different parameters. This breaks connectSlotsByName, which only finds + * the deprecated one that doesn't exist in Qt 6. So we have to connect it + * manually, and avoid naming the slot in the normal way. + */ + connect(encap_buttons, SIGNAL(buttonToggled(QAbstractButton*, bool)), this, SLOT(encap_buttonsToggled(QAbstractButton*, bool))); + + /* fill the IP version combobox */ + ti_ui_->ipVersionComboBox->addItem("IPv4", QVariant(4)); + ti_ui_->ipVersionComboBox->addItem("IPv6", QVariant(6)); + + /* fill the data encoding dropdown in regex tab*/ + struct { + const char* name; + enum data_encoding id; + } encodings[] = { + {"Plain hex", ENCODING_PLAIN_HEX}, + {"Plain oct", ENCODING_PLAIN_OCT}, + {"Plain bin", ENCODING_PLAIN_BIN}, + {"Base 64", ENCODING_BASE64} + }; + for (i = 0; i < (int) (sizeof(encodings) / sizeof(encodings[0])); ++i) { + ti_ui_->dataEncodingComboBox->addItem(encodings[i].name, QVariant(encodings[i].id)); + } + + /* + * Scan all Wiretap encapsulation types. + * + * XXX - this "knows" that WTAP_ENCAP_ETHERNET is the first encapsulation + * type, skipping the special non-types WTAP_ENCAP_PER_PACKET and + * WTAP_ENCAP_UNKNOWN. We need a better way to express the notion + * of "for (all encapsulation types)". + */ + import_info_.encapsulation = WTAP_ENCAP_ETHERNET; + file_type_subtype = wtap_pcapng_file_type_subtype(); + for (encap = import_info_.encapsulation; encap < wtap_get_num_encap_types(); encap++) + { + /* Check if we can write to a pcapng file + * + * Exclude wtap encapsulations that require a pseudo header, + * because we won't setup one from the text we import and + * wiretap doesn't allow us to write 'raw' frames + */ + if (wtap_dump_can_write_encap(file_type_subtype, encap) && + !wtap_encap_requires_phdr(encap)) { + const char *name; + /* If it has got a name */ + if ((name = wtap_encap_description(encap))) + { + ti_ui_->encapComboBox->addItem(name, QVariant(encap)); + } + } + } + ti_ui_->encapComboBox->model()->sort(0); + + /* fill the dissector combo box */ + GList* dissector_names = get_dissector_names(); + for (GList* l = dissector_names; l != NULL; l = l->next) { + const char* name = (const char*) l->data; + ti_ui_->dissectorComboBox->addItem(name, QVariant(name)); + } + ti_ui_->dissectorComboBox->model()->sort(0); + g_list_free(dissector_names); + + ti_ui_->regexHintLabel->setText(default_regex_hint); + + applyDialogSettings(); + updateImportButtonState(); +} + +ImportTextDialog::~ImportTextDialog() +{ + storeDialogSettings(); + + delete ti_ui_; +} + +void ImportTextDialog::loadSettingsFile() +{ + QFileInfo fileInfo(gchar_free_to_qstring(get_profile_dir(get_profile_name(), FALSE)), QString(SETTINGS_FILE)); + QFile loadFile(fileInfo.filePath()); + + if (!fileInfo.exists() || !fileInfo.isFile()) { + return; + } + + if (loadFile.open(QIODevice::ReadOnly)) { + QByteArray loadData = loadFile.readAll(); + QJsonDocument document = QJsonDocument::fromJson(loadData); + + settings = document.object().toVariantMap(); + } +} + +void ImportTextDialog::saveSettingsFile() +{ + QFileInfo fileInfo(gchar_free_to_qstring(get_profile_dir(get_profile_name(), FALSE)), QString(SETTINGS_FILE)); + QFile saveFile(fileInfo.filePath()); + + if (fileInfo.exists() && !fileInfo.isFile()) { + return; + } + + if (saveFile.open(QIODevice::WriteOnly)) { + QJsonDocument document = QJsonDocument::fromVariant(settings); + QByteArray saveData = document.toJson(); + + saveFile.write(saveData); + } +} + +void ImportTextDialog::applyDialogSettings() +{ + loadSettingsFile(); + + // Hex Dump + QString offsetType = settings["hexdump.offsets"].toString(); + if (offsetType == "hex") { + ti_ui_->hexOffsetButton->setChecked(true); + } else if (offsetType == "dec") { + ti_ui_->decimalOffsetButton->setChecked(true); + } else if (offsetType == "oct") { + ti_ui_->octalOffsetButton->setChecked(true); + } else if (offsetType == "none") { + ti_ui_->noOffsetButton->setChecked(true); + } + ti_ui_->directionIndicationCheckBox->setChecked(settings["hexdump.hasDirection"].toBool()); + ti_ui_->asciiIdentificationCheckBox->setChecked(settings["hexdump.identifyAscii"].toBool()); + + // Regular Expression + ti_ui_->regexTextEdit->setText(settings["regex.format"].toString()); + QString encoding = settings["regex.encoding"].toString(); + if (encoding == "plainHex") { + ti_ui_->dataEncodingComboBox->setCurrentIndex(0); + } else if (encoding == "plainOct") { + ti_ui_->dataEncodingComboBox->setCurrentIndex(1); + } else if (encoding == "plainBin") { + ti_ui_->dataEncodingComboBox->setCurrentIndex(2); + } else if (encoding == "base64") { + ti_ui_->dataEncodingComboBox->setCurrentIndex(3); + } + ti_ui_->dirInIndicationLineEdit->setText(settings["regex.inIndication"].toString()); + ti_ui_->dirOutIndicationLineEdit->setText(settings["regex.outIndication"].toString()); + + // Import info + ti_ui_->timestampFormatLineEdit->setText(settings["timestampFormat"].toString()); + + const char *name = wtap_encap_description(settings["encapsulation"].toInt()); + ti_ui_->encapComboBox->setCurrentText(name); + + QString dummyHeader = settings["dummyHeader"].toString(); + if (dummyHeader == "ethernet") { + ti_ui_->ethernetButton->setChecked(true); + } else if (dummyHeader == "ipv4") { + ti_ui_->ipv4Button->setChecked(true); + } else if (dummyHeader == "udp") { + ti_ui_->udpButton->setChecked(true); + } else if (dummyHeader == "tcp") { + ti_ui_->tcpButton->setChecked(true); + } else if (dummyHeader == "sctp") { + ti_ui_->sctpButton->setChecked(true); + } else if (dummyHeader == "sctpData") { + ti_ui_->sctpDataButton->setChecked(true); + } else if (dummyHeader == "exportPDU") { + ti_ui_->exportPduButton->setChecked(true); + } else if (dummyHeader == "none") { + ti_ui_->noDummyButton->setChecked(true); + } + + if (settings["ipVersion"].toUInt() == 6) { + ti_ui_->ipVersionComboBox->setCurrentIndex(1); + } else { + ti_ui_->ipVersionComboBox->setCurrentIndex(0); + } + ti_ui_->ethertypeLineEdit->setText(settings["ethertype"].toString()); + ti_ui_->protocolLineEdit->setText(settings["ipProtocol"].toString()); + ti_ui_->sourceAddressLineEdit->setText(settings["sourceAddress"].toString()); + ti_ui_->destinationAddressLineEdit->setText(settings["destinationAddress"].toString()); + ti_ui_->sourcePortLineEdit->setText(settings["sourcePort"].toString()); + ti_ui_->destinationPortLineEdit->setText(settings["destinationPort"].toString()); + ti_ui_->tagLineEdit->setText(settings["sctpTag"].toString()); + ti_ui_->ppiLineEdit->setText(settings["sctpPPI"].toString()); + + if (settings.contains("pduPayload")) { + ti_ui_->dissectorComboBox->setCurrentText(settings["pduPayload"].toString()); + } else { + // Default to the data dissector when not previously set + ti_ui_->dissectorComboBox->setCurrentText("data"); + } + + ti_ui_->interfaceLineEdit->setText(settings["interfaceName"].toString()); + ti_ui_->maxLengthLineEdit->setText(settings["maxFrameLength"].toString()); + + // Select mode tab last to enableFieldWidgets() + QString mode(settings["mode"].toString()); + int modeIndex = (mode == "regex") ? 1 : 0; + ti_ui_->modeTabWidget->setCurrentIndex(modeIndex); + on_modeTabWidget_currentChanged(modeIndex); +} + +void ImportTextDialog::storeDialogSettings() +{ + int modeIndex = ti_ui_->modeTabWidget->currentIndex(); + if (modeIndex == 0) { + settings["mode"] = "hexdump"; + } else { + settings["mode"] = "regex"; + } + + // Hex Dump + if (ti_ui_->hexOffsetButton->isChecked()) { + settings["hexdump.offsets"] = "hex"; + } else if (ti_ui_->decimalOffsetButton->isChecked()) { + settings["hexdump.offsets"] = "dec"; + } else if (ti_ui_->octalOffsetButton->isChecked()) { + settings["hexdump.offsets"] = "oct"; + } else { + settings["hexdump.offsets"] = "none"; + } + settings["hexdump.hasDirection"] = ti_ui_->directionIndicationCheckBox->isChecked(); + settings["hexdump.identifyAscii"] = ti_ui_->asciiIdentificationCheckBox->isChecked(); + + // Regular Expression + settings["regex.format"] = ti_ui_->regexTextEdit->toPlainText(); + QVariant encodingVal = ti_ui_->dataEncodingComboBox->itemData(ti_ui_->dataEncodingComboBox->currentIndex()); + if (encodingVal.isValid()) { + enum data_encoding encoding = (enum data_encoding) encodingVal.toUInt(); + switch (encoding) { + case ENCODING_PLAIN_HEX: + settings["regex.encoding"] = "plainHex"; + break; + case ENCODING_PLAIN_OCT: + settings["regex.encoding"] = "plainOct"; + break; + case ENCODING_PLAIN_BIN: + settings["regex.encoding"] = "plainBin"; + break; + case ENCODING_BASE64: + settings["regex.encoding"] = "base64"; + break; + } + } else { + settings["regex.encoding"] = "plainHex"; + } + settings["regex.inIndication"] = ti_ui_->dirInIndicationLineEdit->text(); + settings["regex.outIndication"] = ti_ui_->dirOutIndicationLineEdit->text(); + + // Import info + settings["timestampFormat"] = ti_ui_->timestampFormatLineEdit->text(); + + QVariant encapVal = ti_ui_->encapComboBox->itemData(ti_ui_->encapComboBox->currentIndex()); + if (encapVal.isValid()) { + settings["encapsulation"] = encapVal.toUInt(); + } else { + settings["encapsulation"] = WTAP_ENCAP_ETHERNET; + } + + if (ti_ui_->ethernetButton->isChecked()) { + settings["dummyHeader"] = "ethernet"; + } else if (ti_ui_->ipv4Button->isChecked()) { + settings["dummyHeader"] = "ipv4"; + } else if (ti_ui_->udpButton->isChecked()) { + settings["dummyHeader"] = "udp"; + } else if (ti_ui_->tcpButton->isChecked()) { + settings["dummyHeader"] = "tcp"; + } else if (ti_ui_->sctpButton->isChecked()) { + settings["dummyHeader"] = "sctp"; + } else if (ti_ui_->sctpDataButton->isChecked()) { + settings["dummyHeader"] = "sctpData"; + } else if (ti_ui_->exportPduButton->isChecked()) { + settings["dummyHeader"] = "exportPDU"; + } else { + settings["dummyHeader"] = "none"; + } + + settings["ipVersion"] = ti_ui_->ipVersionComboBox->currentData().toUInt(); + settings["ethertype"] = ti_ui_->ethertypeLineEdit->text(); + settings["ipProtocol"] = ti_ui_->protocolLineEdit->text(); + settings["sourceAddress"] = ti_ui_->sourceAddressLineEdit->text(); + settings["destinationAddress"] = ti_ui_->destinationAddressLineEdit->text(); + settings["sourcePort"] = ti_ui_->sourcePortLineEdit->text(); + settings["destinationPort"] = ti_ui_->destinationPortLineEdit->text(); + settings["sctpTag"] = ti_ui_->tagLineEdit->text(); + settings["sctpPPI"] = ti_ui_->ppiLineEdit->text(); + settings["pduPayload"] = ti_ui_->dissectorComboBox->currentData().toString(); + + settings["interfaceName"] = ti_ui_->interfaceLineEdit->text(); + settings["maxFrameLength"] = ti_ui_->maxLengthLineEdit->text(); + + saveSettingsFile(); +} + +QString &ImportTextDialog::capfileName() { + return capfile_name_; +} + +int ImportTextDialog::exec() { + QVariant encap_val; + char* tmp; + GError* gerror = NULL; + int err; + gchar *err_info; + wtap_dump_params params; + int file_type_subtype; + QString interface_name; + + QDialog::exec(); + + if (result() != QDialog::Accepted) { + return result(); + } + + /* from here on the cleanup labels are used to free allocated resources in + * reverse order. + * naming is cleanup_ + * Don't Declare new variables from here on + */ + + import_info_.import_text_filename = qstring_strdup(ti_ui_->textFileLineEdit->text()); + import_info_.timestamp_format = qstring_strdup(ti_ui_->timestampFormatLineEdit->text()); + if (strlen(import_info_.timestamp_format) == 0) { + g_free((gpointer) import_info_.timestamp_format); + import_info_.timestamp_format = NULL; + } + + switch (import_info_.mode) { + default: /* should never happen */ + setResult(QDialog::Rejected); + return QDialog::Rejected; + case TEXT_IMPORT_HEXDUMP: + import_info_.hexdump.import_text_FILE = ws_fopen(import_info_.import_text_filename, "rb"); + if (!import_info_.hexdump.import_text_FILE) { + open_failure_alert_box(import_info_.import_text_filename, errno, FALSE); + setResult(QDialog::Rejected); + goto cleanup_mode; + } + + import_info_.hexdump.offset_type = + ti_ui_->hexOffsetButton->isChecked() ? OFFSET_HEX : + ti_ui_->decimalOffsetButton->isChecked() ? OFFSET_DEC : + ti_ui_->octalOffsetButton->isChecked() ? OFFSET_OCT : + OFFSET_NONE; + break; + case TEXT_IMPORT_REGEX: + import_info_.regex.import_text_GMappedFile = g_mapped_file_new(import_info_.import_text_filename, true, &gerror); + if (gerror) { + open_failure_alert_box(import_info_.import_text_filename, gerror->code, FALSE); + g_error_free(gerror); + setResult(QDialog::Rejected); + goto cleanup_mode; + } + tmp = qstring_strdup(ti_ui_->regexTextEdit->toPlainText()); + import_info_.regex.format = g_regex_new(tmp, (GRegexCompileFlags) (G_REGEX_DUPNAMES | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE), G_REGEX_MATCH_NOTEMPTY, &gerror); + g_free(tmp); + if (re_has_dir_) { + import_info_.regex.in_indication = qstring_strdup(ti_ui_->dirInIndicationLineEdit->text()); + import_info_.regex.out_indication = qstring_strdup(ti_ui_->dirOutIndicationLineEdit->text()); + } else { + import_info_.regex.in_indication = NULL; + import_info_.regex.out_indication = NULL; + } + break; + } + + encap_val = ti_ui_->encapComboBox->itemData(ti_ui_->encapComboBox->currentIndex()); + import_info_.dummy_header_type = HEADER_NONE; + if (encap_val.isValid() && (encap_buttons->checkedButton()->isEnabled()) + && !ti_ui_->noDummyButton->isChecked()) { + // Inputs were validated in the on_xxx_textChanged slots. + if (ti_ui_->ethernetButton->isChecked()) { + import_info_.dummy_header_type = HEADER_ETH; + } else if (ti_ui_->ipv4Button->isChecked()) { + import_info_.dummy_header_type = HEADER_IPV4; + } else if (ti_ui_->udpButton->isChecked()) { + import_info_.dummy_header_type = HEADER_UDP; + } else if (ti_ui_->tcpButton->isChecked()) { + import_info_.dummy_header_type = HEADER_TCP; + } else if (ti_ui_->sctpButton->isChecked()) { + import_info_.dummy_header_type = HEADER_SCTP; + } else if (ti_ui_->sctpDataButton->isChecked()) { + import_info_.dummy_header_type = HEADER_SCTP_DATA; + } else if (ti_ui_->exportPduButton->isChecked()) { + import_info_.dummy_header_type = HEADER_EXPORT_PDU; + } + } + if (import_info_.max_frame_length == 0) { + import_info_.max_frame_length = WTAP_MAX_PACKET_SIZE_STANDARD; + } + + import_info_.payload = qstring_strdup(ti_ui_->dissectorComboBox->currentData().toString()); + + capfile_name_.clear(); + wtap_dump_params_init(¶ms, NULL); + params.encap = import_info_.encapsulation; + params.snaplen = import_info_.max_frame_length; + params.tsprec = WTAP_TSPREC_NSEC; /* XXX - support other precisions? */ + /* Write a pcapng temporary file */ + file_type_subtype = wtap_pcapng_file_type_subtype(); + if (ti_ui_->interfaceLineEdit->text().length()) { + interface_name = ti_ui_->interfaceLineEdit->text(); + } else { + interface_name = ti_ui_->interfaceLineEdit->placeholderText(); + } + text_import_pre_open(¶ms, file_type_subtype, import_info_.import_text_filename, interface_name.toUtf8().constData()); + /* Use a random name for the temporary import buffer */ + import_info_.wdh = wtap_dump_open_tempfile(global_capture_opts.temp_dir, &tmp, "import", file_type_subtype, WTAP_UNCOMPRESSED, ¶ms, &err, &err_info); + capfile_name_.append(tmp ? tmp : "temporary file"); + import_info_.output_filename = tmp; + + if (import_info_.wdh == NULL) { + cfile_dump_open_failure_alert_box(capfile_name_.toUtf8().constData(), err, err_info, file_type_subtype); + setResult(QDialog::Rejected); + goto cleanup_wtap; + } + + err = text_import(&import_info_); + + if (err != 0) { + failure_alert_box("Import failed"); + setResult(QDialog::Rejected); + goto cleanup; + } + + cleanup: /* free in reverse order of allocation */ + if (!wtap_dump_close(import_info_.wdh, NULL, &err, &err_info)) + { + cfile_close_failure_alert_box(capfile_name_.toUtf8().constData(), err, err_info); + } + cleanup_wtap: + /* g_free checks for null */ + wtap_free_idb_info(params.idb_inf); + wtap_dump_params_cleanup(¶ms); + g_free(tmp); + g_free((gpointer) import_info_.payload); + switch (import_info_.mode) { + case TEXT_IMPORT_HEXDUMP: + fclose(import_info_.hexdump.import_text_FILE); + break; + case TEXT_IMPORT_REGEX: + g_mapped_file_unref(import_info_.regex.import_text_GMappedFile); + g_regex_unref((GRegex*) import_info_.regex.format); + g_free((gpointer) import_info_.regex.in_indication); + g_free((gpointer) import_info_.regex.out_indication); + break; + } + cleanup_mode: + g_free((gpointer) import_info_.import_text_filename); + g_free((gpointer) import_info_.timestamp_format); + return result(); +} + +/******************************************************************************* + * General Input + */ + +void ImportTextDialog::updateImportButtonState() +{ + /* XXX: This requires even buttons that aren't being used to have valid + * entries (addresses, ports, etc.) Fixing that can mean changing the + * encapsulation type in order to enable the line edits, which is a little + * awkward for the user. + */ + if (file_ok_ && timestamp_format_ok_ && ether_type_ok_ && + proto_ok_ && source_addr_ok_ && dest_addr_ok_ && + source_port_ok_ && dest_port_ok_ && + tag_ok_ && ppi_ok_ && payload_ok_ && max_len_ok_ && + ( + ( + import_info_.mode == TEXT_IMPORT_REGEX && regex_ok_ && + (!re_has_dir_ || (in_indication_ok_ && out_indication_ok_)) + ) || ( + import_info_.mode == TEXT_IMPORT_HEXDUMP + ) + ) + ) { + import_button_->setEnabled(true); + } else { + import_button_->setEnabled(false); + } +} + +void ImportTextDialog::on_textFileLineEdit_textChanged(const QString &file_name) +{ + QFile text_file(file_name); + + if (file_name.length() > 0 && text_file.open(QIODevice::ReadOnly)) { + file_ok_ = true; + text_file.close(); + } else { + file_ok_ = false; + } + updateImportButtonState(); +} + +void ImportTextDialog::on_textFileBrowseButton_clicked() +{ + QString open_dir; + if (ti_ui_->textFileLineEdit->text().length() > 0) { + open_dir = ti_ui_->textFileLineEdit->text(); + } else { + switch (prefs.gui_fileopen_style) { + + case FO_STYLE_LAST_OPENED: + /* The user has specified that we should start out in the last directory + we looked in. If we've already opened a file, use its containing + directory, if we could determine it, as the directory, otherwise + use the "last opened" directory saved in the preferences file if + there was one. */ + /* This is now the default behaviour in file_selection_new() */ + open_dir = get_open_dialog_initial_dir(); + break; + + case FO_STYLE_SPECIFIED: + /* The user has specified that we should always start out in a + specified directory; if they've specified that directory, + start out by showing the files in that dir. */ + if (prefs.gui_fileopen_dir[0] != '\0') + open_dir = prefs.gui_fileopen_dir; + break; + } + } + + QString file_name = WiresharkFileDialog::getOpenFileName(this, mainApp->windowTitleString(tr("Import Text File")), open_dir); + ti_ui_->textFileLineEdit->setText(file_name); +} + +bool ImportTextDialog::checkDateTimeFormat(const QString &time_format) +{ + /* nonstandard is f for fractions of seconds */ + const QString valid_code = "aAbBcdDFfHIjmMpsSTUwWxXyYzZ%"; + int idx = 0; + int ret = false; + + /* XXX: Temporary(?) hack to allow ISO format time, a checkbox is + * probably better */ + if (time_format == "ISO") { + ret = true; + } else while ((idx = static_cast(time_format.indexOf("%", idx))) != -1) { + idx++; + if ((idx == time_format.size()) || !valid_code.contains(time_format[idx])) { + return false; + } + idx++; + ret = true; + } + return ret; +} + +void ImportTextDialog::on_timestampFormatLineEdit_textChanged(const QString &time_format) +{ + if (time_format.length() > 0) { + if (checkDateTimeFormat(time_format)) { + struct timespec timenow; + struct tm *cur_tm; + struct tm fallback; + char time_str[100]; + QString timefmt = QString(time_format); + + ws_clock_get_realtime(&timenow); + + /* On windows strftime/wcsftime does not support %s yet, this works on all OSs */ + timefmt.replace(QString("%s"), QString::number(timenow.tv_sec)); + /* subsecond example as usec */ + timefmt.replace(QString("%f"), QString("%1").arg(timenow.tv_nsec, 6, 10, QChar('0'))); + + cur_tm = localtime(&timenow.tv_sec); + if (cur_tm == NULL) { + memset(&fallback, 0, sizeof(fallback)); + cur_tm = &fallback; + } + strftime(time_str, sizeof time_str, timefmt.toUtf8(), cur_tm); + ti_ui_->timestampExampleLabel->setText(QString(tr(HINT_BEGIN "Example: %1" HINT_END)).arg(QString(time_str).toHtmlEscaped())); + timestamp_format_ok_ = true; + } + else { + ti_ui_->timestampExampleLabel->setText(tr(HINT_BEGIN "(Wrong date format)" HINT_END)); + timestamp_format_ok_ = false; + } + } else { + ti_ui_->timestampExampleLabel->setText(tr(HINT_BEGIN "(No format will be applied)" HINT_END)); + timestamp_format_ok_ = true; + } + updateImportButtonState(); +} + +void ImportTextDialog::on_modeTabWidget_currentChanged(int index) { + switch (index) { + default: + ti_ui_->modeTabWidget->setCurrentIndex(0); + /* fall through */ + case 0: /* these numbers depend on the UI */ + import_info_.mode = TEXT_IMPORT_HEXDUMP; + memset(&import_info_.hexdump, 0, sizeof(import_info_.hexdump)); + on_directionIndicationCheckBox_toggled(ti_ui_->directionIndicationCheckBox->isChecked()); + on_asciiIdentificationCheckBox_toggled(ti_ui_->asciiIdentificationCheckBox->isChecked()); + enableFieldWidgets(false, true); + break; + case 1: + import_info_.mode = TEXT_IMPORT_REGEX; + memset(&import_info_.regex, 0, sizeof(import_info_.regex)); + on_dataEncodingComboBox_currentIndexChanged(ti_ui_->dataEncodingComboBox->currentIndex()); + enableFieldWidgets(re_has_dir_, re_has_time_); + break; + } + on_textFileLineEdit_textChanged(ti_ui_->textFileLineEdit->text()); +} + +/******************************************************************************* + * Hex Dump Tab + */ + +void ImportTextDialog::on_noOffsetButton_toggled(bool checked) +{ + if (checked) { + ti_ui_->noOffsetLabel->setText("(only one packet will be created)"); + } else { + ti_ui_->noOffsetLabel->setText(""); + } +} + +void ImportTextDialog::on_directionIndicationCheckBox_toggled(bool checked) +{ + import_info_.hexdump.has_direction = checked; +} + +void ImportTextDialog::on_asciiIdentificationCheckBox_toggled(bool checked) +{ + import_info_.hexdump.identify_ascii = checked; +} + +/******************************************************************************* + * Regex Tab + */ + +void ImportTextDialog::on_regexTextEdit_textChanged() +{ + gchar* regex_gchar_p = qstring_strdup(ti_ui_->regexTextEdit->toPlainText());; + GError* gerror = NULL; + /* TODO: Use GLib's c++ interface or enable C++ int to enum casting + * because the flags are declared as enum, so we can't pass 0 like + * the specification recommends. These options don't hurt. + */ + GRegex* regex = g_regex_new(regex_gchar_p, G_REGEX_DUPNAMES, G_REGEX_MATCH_NOTEMPTY, &gerror); + if (gerror) { + regex_ok_ = false; + ti_ui_->regexHintLabel->setText(QString(gerror->message).toHtmlEscaped()); + g_error_free(gerror); + } else { + regex_ok_ = 0 <= g_regex_get_string_number(regex, "data"); + if (regex_ok_) + ti_ui_->regexHintLabel->setText(default_regex_hint); + else + ti_ui_->regexHintLabel->setText(missing_data_hint); + re_has_dir_ = 0 <= g_regex_get_string_number(regex, "dir"); + re_has_time_ = 0 <= g_regex_get_string_number(regex, "time"); + //re_has_seqno = 0 <= g_regex_get_string_number(regex, "seqno"); + g_regex_unref(regex); + } + g_free(regex_gchar_p); + enableFieldWidgets(re_has_dir_, re_has_time_); + updateImportButtonState(); +} + +void ImportTextDialog::enableFieldWidgets(bool enable_direction_input, bool enable_time_input) { + ti_ui_->dirIndicationLabel->setEnabled(enable_direction_input); + ti_ui_->dirInIndicationLineEdit->setEnabled(enable_direction_input); + ti_ui_->dirOutIndicationLineEdit->setEnabled(enable_direction_input); + ti_ui_->timestampLabel->setEnabled(enable_time_input); + ti_ui_->timestampFormatLineEdit->setEnabled(enable_time_input); + ti_ui_->timestampExampleLabel->setEnabled(enable_time_input); +} + +void ImportTextDialog::on_dataEncodingComboBox_currentIndexChanged(int index) +{ + QVariant val = ti_ui_->dataEncodingComboBox->itemData(index); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (val.canConvert()) +#else + if (val != QVariant::Invalid) +#endif + { + // data_encoding_ok = true; + import_info_.regex.encoding = (enum data_encoding) val.toUInt(); + switch (import_info_.regex.encoding) { + case ENCODING_PLAIN_HEX: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-9a-fA-F:\\s]+)" HINT_END); + break; + case ENCODING_PLAIN_BIN: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-1\\s]+)" HINT_END); + break; + case ENCODING_PLAIN_OCT: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-8:\\s]+)" HINT_END); + break; + case ENCODING_BASE64: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-9a-zA-Z+\\/\\s]+=*)" HINT_END); + break; + default: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN HTML_LT "no example" HTML_GT HINT_END); + break; + } + /* for some reason this breaks when changing the text */ + ti_ui_->encodingRegexExample->setTextInteractionFlags(Qt::TextSelectableByMouse); + } + updateImportButtonState(); +} + +void ImportTextDialog::on_dirInIndicationLineEdit_textChanged(const QString &in_indication) +{ + in_indication_ok_ = in_indication.length() > 0; + updateImportButtonState(); +} + +void ImportTextDialog::on_dirOutIndicationLineEdit_textChanged(const QString &out_indication) +{ + out_indication_ok_ = out_indication.length() > 0; + updateImportButtonState(); +} + +/******************************************************************************* + * Encapsulation input + */ + +void ImportTextDialog::enableHeaderWidgets(uint encapsulation) { + bool ethertype = false; + bool ipv4_proto = false; + bool ip_address = false; + bool port = false; + bool sctp_tag = false; + bool sctp_ppi = false; + bool export_pdu = false; + bool enable_ethernet_buttons = (encapsulation == WTAP_ENCAP_ETHERNET); + bool enable_ip_buttons = (encapsulation == WTAP_ENCAP_RAW_IP || encapsulation == WTAP_ENCAP_RAW_IP4 || encapsulation == WTAP_ENCAP_RAW_IP6); + bool enable_export_pdu_buttons = (encapsulation == WTAP_ENCAP_WIRESHARK_UPPER_PDU); + + if (enable_ethernet_buttons) { + if (ti_ui_->ethernetButton->isChecked()) { + ethertype = true; + on_ethertypeLineEdit_textChanged(ti_ui_->ethertypeLineEdit->text()); + } + enable_ip_buttons = true; + } + + if (enable_ip_buttons) { + if (ti_ui_->ipv4Button->isChecked()) { + ipv4_proto = true; + ip_address = true; + on_protocolLineEdit_textChanged(ti_ui_->protocolLineEdit->text()); + } else if (ti_ui_->udpButton->isChecked() || ti_ui_->tcpButton->isChecked()) { + ip_address = true; + port = true; + on_sourcePortLineEdit_textChanged(ti_ui_->sourcePortLineEdit->text()); + on_destinationPortLineEdit_textChanged(ti_ui_->destinationPortLineEdit->text()); + } else if (ti_ui_->sctpButton->isChecked()) { + ip_address = true; + port = true; + sctp_tag = true; + on_sourcePortLineEdit_textChanged(ti_ui_->sourcePortLineEdit->text()); + on_destinationPortLineEdit_textChanged(ti_ui_->destinationPortLineEdit->text()); + on_tagLineEdit_textChanged(ti_ui_->tagLineEdit->text()); + } + if (ti_ui_->sctpDataButton->isChecked()) { + ip_address = true; + port = true; + sctp_ppi = true; + on_sourcePortLineEdit_textChanged(ti_ui_->sourcePortLineEdit->text()); + on_destinationPortLineEdit_textChanged(ti_ui_->destinationPortLineEdit->text()); + on_ppiLineEdit_textChanged(ti_ui_->ppiLineEdit->text()); + } + } + + if (enable_export_pdu_buttons) { + if (ti_ui_->exportPduButton->isChecked()) { + export_pdu = true; + } + } + + foreach (auto &&rb, encap_buttons->buttons()) { + rb->setEnabled(enable_ip_buttons); + } + + ti_ui_->ethernetButton->setEnabled(enable_ethernet_buttons); + ti_ui_->exportPduButton->setEnabled(enable_export_pdu_buttons); + ti_ui_->noDummyButton->setEnabled(enable_export_pdu_buttons || enable_ip_buttons); + + ti_ui_->ethertypeLabel->setEnabled(ethertype); + ti_ui_->ethertypeLineEdit->setEnabled(ethertype); + ti_ui_->protocolLabel->setEnabled(ipv4_proto); + ti_ui_->protocolLineEdit->setEnabled(ipv4_proto); + ti_ui_->ipVersionLabel->setEnabled(ip_address); + if (encapsulation == WTAP_ENCAP_RAW_IP4) { + ti_ui_->ipVersionComboBox->setEnabled(false); + ti_ui_->ipVersionComboBox->setCurrentIndex(0); + } else if (encapsulation == WTAP_ENCAP_RAW_IP6) { + ti_ui_->ipVersionComboBox->setEnabled(false); + ti_ui_->ipVersionComboBox->setCurrentIndex(1); + } else { + ti_ui_->ipVersionComboBox->setEnabled(ip_address); + } + ti_ui_->sourceAddressLabel->setEnabled(ip_address); + ti_ui_->sourceAddressLineEdit->setEnabled(ip_address); + ti_ui_->destinationAddressLabel->setEnabled(ip_address); + ti_ui_->destinationAddressLineEdit->setEnabled(ip_address); + ti_ui_->sourcePortLabel->setEnabled(port); + ti_ui_->sourcePortLineEdit->setEnabled(port); + ti_ui_->destinationPortLabel->setEnabled(port); + ti_ui_->destinationPortLineEdit->setEnabled(port); + ti_ui_->tagLabel->setEnabled(sctp_tag); + ti_ui_->tagLineEdit->setEnabled(sctp_tag); + ti_ui_->ppiLabel->setEnabled(sctp_ppi); + ti_ui_->ppiLineEdit->setEnabled(sctp_ppi); + ti_ui_->payloadLabel->setEnabled(export_pdu); + ti_ui_->dissectorComboBox->setEnabled(export_pdu); + + if (ti_ui_->noDummyButton->isEnabled() && !(encap_buttons->checkedButton()->isEnabled())) { + ti_ui_->noDummyButton->toggle(); + } +} + +void ImportTextDialog::on_encapComboBox_currentIndexChanged(int index) +{ + QVariant val = ti_ui_->encapComboBox->itemData(index); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (val.canConvert()) +#else + if (val != QVariant::Invalid) +#endif + { + import_info_.encapsulation = val.toUInt(); + } else { + import_info_.encapsulation = WTAP_ENCAP_UNKNOWN; + } + + enableHeaderWidgets(import_info_.encapsulation); +} + +void ImportTextDialog::encap_buttonsToggled(QAbstractButton *button _U_, bool checked) +{ + if (checked) enableHeaderWidgets(import_info_.encapsulation); +} + +void ImportTextDialog::on_ipVersionComboBox_currentIndexChanged(int index) +{ + import_info_.ipv6 = (index == 1) ? 1 : 0; + on_sourceAddressLineEdit_textChanged(ti_ui_->sourceAddressLineEdit->text()); + on_destinationAddressLineEdit_textChanged(ti_ui_->destinationAddressLineEdit->text()); +} + +void ImportTextDialog::check_line_edit(SyntaxLineEdit *le, bool &ok_enabled, const QString &num_str, int base, guint max_val, bool is_short, guint *val_ptr) { + bool conv_ok; + SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty; + + if (!le || !val_ptr) + return; + + ok_enabled = true; + if (num_str.length() < 1) { + *val_ptr = 0; + } else { + if (is_short) { + *val_ptr = num_str.toUShort(&conv_ok, base); + } else { + *val_ptr = (guint)num_str.toULong(&conv_ok, base); + } + if (conv_ok && *val_ptr <= max_val) { + syntax_state = SyntaxLineEdit::Valid; + } else { + syntax_state = SyntaxLineEdit::Invalid; + ok_enabled = false; + } + } + le->setSyntaxState(syntax_state); + updateImportButtonState(); +} + +void ImportTextDialog::checkAddress(SyntaxLineEdit *le, bool &ok_enabled, const QString &addr_str, ws_in4_addr *val_ptr) { + bool conv_ok; + SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty; + + if (!le || !val_ptr) + return; + + ok_enabled = true; + if (addr_str.length() < 1) { + *val_ptr = 0; + } else { + conv_ok = ws_inet_pton4(addr_str.toUtf8().data(), (ws_in4_addr*)val_ptr); + if (conv_ok) { + syntax_state= SyntaxLineEdit::Valid; + } else { + syntax_state = SyntaxLineEdit::Invalid; + ok_enabled = false; + } + } + le->setSyntaxState(syntax_state); + updateImportButtonState(); +} + +void ImportTextDialog::checkIPv6Address(SyntaxLineEdit *le, bool &ok_enabled, const QString &addr_str, ws_in6_addr *val_ptr) { + bool conv_ok; + SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty; + + if (!le || !val_ptr) + return; + + ok_enabled = true; + if (addr_str.length() < 1) { + *val_ptr = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + } else { + conv_ok = ws_inet_pton6(addr_str.toUtf8().data(), (ws_in6_addr*)val_ptr); + if (conv_ok) { + syntax_state= SyntaxLineEdit::Valid; + } else { + syntax_state = SyntaxLineEdit::Invalid; + ok_enabled = false; + } + } + le->setSyntaxState(syntax_state); + updateImportButtonState(); +} + +void ImportTextDialog::on_ethertypeLineEdit_textChanged(const QString ðertype_str) +{ + check_line_edit(ti_ui_->ethertypeLineEdit, ether_type_ok_, ethertype_str, 16, 0xffff, true, &import_info_.pid); +} + +void ImportTextDialog::on_protocolLineEdit_textChanged(const QString &protocol_str) +{ + check_line_edit(ti_ui_->protocolLineEdit, proto_ok_, protocol_str, 10, 0xff, true, &import_info_.protocol); +} + +void ImportTextDialog::on_sourceAddressLineEdit_textChanged(const QString &source_addr_str) +{ + if (ti_ui_->ipVersionComboBox->currentIndex() == 1) { + checkIPv6Address(ti_ui_->sourceAddressLineEdit, source_addr_ok_, source_addr_str, &import_info_.ip_src_addr.ipv6); + } else { + checkAddress(ti_ui_->sourceAddressLineEdit, source_addr_ok_, source_addr_str, &import_info_.ip_src_addr.ipv4); + } +} + +void ImportTextDialog::on_destinationAddressLineEdit_textChanged(const QString &destination_addr_str) +{ + if (ti_ui_->ipVersionComboBox->currentIndex() == 1) { + checkIPv6Address(ti_ui_->destinationAddressLineEdit, dest_addr_ok_, destination_addr_str, &import_info_.ip_dest_addr.ipv6); + } else { + checkAddress(ti_ui_->destinationAddressLineEdit, dest_addr_ok_, destination_addr_str, &import_info_.ip_dest_addr.ipv4); + } +} + +void ImportTextDialog::on_sourcePortLineEdit_textChanged(const QString &source_port_str) +{ + check_line_edit(ti_ui_->sourcePortLineEdit, source_port_ok_, source_port_str, 10, 0xffff, true, &import_info_.src_port); +} + +void ImportTextDialog::on_destinationPortLineEdit_textChanged(const QString &destination_port_str) +{ + check_line_edit(ti_ui_->destinationPortLineEdit, dest_port_ok_, destination_port_str, 10, 0xffff, true, &import_info_.dst_port); +} + +void ImportTextDialog::on_tagLineEdit_textChanged(const QString &tag_str) +{ + check_line_edit(ti_ui_->tagLineEdit, tag_ok_, tag_str, 10, 0xffffffff, false, &import_info_.tag); +} + +void ImportTextDialog::on_ppiLineEdit_textChanged(const QString &ppi_str) +{ + check_line_edit(ti_ui_->ppiLineEdit, ppi_ok_, ppi_str, 10, 0xffffffff, false, &import_info_.ppi); +} + +/******************************************************************************* +* Footer +*/ + +void ImportTextDialog::on_maxLengthLineEdit_textChanged(const QString &max_frame_len_str) +{ + check_line_edit(ti_ui_->maxLengthLineEdit, max_len_ok_, max_frame_len_str, 10, WTAP_MAX_PACKET_SIZE_STANDARD, true, &import_info_.max_frame_length); +} + +void ImportTextDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_IMPORT_DIALOG); +} diff --git a/ui/qt/import_text_dialog.h b/ui/qt/import_text_dialog.h new file mode 100644 index 00000000..ac9537d9 --- /dev/null +++ b/ui/qt/import_text_dialog.h @@ -0,0 +1,126 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IMPORT_TEXT_DIALOG_H +#define IMPORT_TEXT_DIALOG_H + +#include + +#include + +#include + +#include "ui/text_import.h" + +#include + +#include +#include +#include +#include + +namespace Ui { +class ImportTextDialog; +} + +class ImportTextDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ImportTextDialog(QWidget *parent = 0); + ~ImportTextDialog(); + QString &capfileName(); + +private: + void enableHeaderWidgets(uint encapsulation = WTAP_ENCAP_ETHERNET); + + /* regex fields */ + void enableFieldWidgets(bool enable_direction_input = true, bool enable_time_input = true); + + void check_line_edit(SyntaxLineEdit *le, bool &ok_enable, const QString &num_str, int base, guint max_val, bool is_short, guint *val_ptr); + void checkAddress(SyntaxLineEdit *le, bool &ok_enable, const QString &addr_str, ws_in4_addr *val_ptr); + void checkIPv6Address(SyntaxLineEdit *le, bool &ok_enable, const QString &addr_str, ws_in6_addr *val_ptr); + bool checkDateTimeFormat(const QString &time_format); + + void loadSettingsFile(); + void saveSettingsFile(); + void applyDialogSettings(); + void storeDialogSettings(); + + void updateImportButtonState(); + + Ui::ImportTextDialog *ti_ui_; + QVariantMap settings; + + QPushButton *import_button_; + QButtonGroup *encap_buttons; + text_import_info_t import_info_; + QString capfile_name_; + bool file_ok_; + bool timestamp_format_ok_; + + /* Regex input */ + bool regex_ok_; + bool re_has_dir_; + bool in_indication_ok_; + bool out_indication_ok_; + bool re_has_time_; + + bool ether_type_ok_; + bool proto_ok_; + bool source_addr_ok_; + bool dest_addr_ok_; + bool source_port_ok_; + bool dest_port_ok_; + bool tag_ok_; + bool ppi_ok_; + bool payload_ok_; + bool max_len_ok_; + +public slots: + int exec(); + +private slots: + void on_textFileBrowseButton_clicked(); + void on_textFileLineEdit_textChanged(const QString &arg1); + void on_modeTabWidget_currentChanged(int index); + void on_timestampFormatLineEdit_textChanged(const QString &arg1); + + /* Hex Dump input */ + void on_noOffsetButton_toggled(bool checked); + void on_directionIndicationCheckBox_toggled(bool checked); + void on_asciiIdentificationCheckBox_toggled(bool checked); + + /* Regex input */ + void on_regexTextEdit_textChanged(); + void on_dataEncodingComboBox_currentIndexChanged(int index); + void on_dirInIndicationLineEdit_textChanged(const QString &arg1); + void on_dirOutIndicationLineEdit_textChanged(const QString &arg1); + + /* Encapsulation input */ + void on_encapComboBox_currentIndexChanged(int index); + void encap_buttonsToggled(QAbstractButton *button, bool checked); + void on_ipVersionComboBox_currentIndexChanged(int index); + void on_ethertypeLineEdit_textChanged(const QString ðertype_str); + void on_protocolLineEdit_textChanged(const QString &protocol_str); + void on_sourceAddressLineEdit_textChanged(const QString &source_addr_str); + void on_destinationAddressLineEdit_textChanged(const QString &destination_addr_str); + void on_sourcePortLineEdit_textChanged(const QString &source_port_str); + void on_destinationPortLineEdit_textChanged(const QString &destination_port_str); + void on_tagLineEdit_textChanged(const QString &tag_str); + void on_ppiLineEdit_textChanged(const QString &ppi_str); + + /* Footer input */ + void on_maxLengthLineEdit_textChanged(const QString &max_frame_len_str); + void on_buttonBox_helpRequested(); +}; + + +#endif // IMPORT_TEXT_DIALOG_H diff --git a/ui/qt/import_text_dialog.ui b/ui/qt/import_text_dialog.ui new file mode 100644 index 00000000..756ff63a --- /dev/null +++ b/ui/qt/import_text_dialog.ui @@ -0,0 +1,868 @@ + + + ImportTextDialog + + + + 0 + 0 + 562 + 832 + + + + + 16777215 + 1000 + + + + true + + + true + + + + + + + + File: + + + + + + + + 0 + 0 + + + + Set name of text file to import + + + + + + + Browse for text file to import + + + Browse… + + + + + + + + + + 0 + 0 + + + + 0 + + + + Hex Dump + + + Import a standard hex dump as exported by Wireshark + + + + + + + + 1 + + + + + Offsets in the text file are in octal notation + + + Octal + + + + + + + Offsets: + + + + + + + Offsets in the text file are in hexadecimal notation + + + Hexadecimal + + + true + + + + + + + Offsets in the text file are in decimal notation + + + Decimal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + The text file has no offset + + + None + + + + + + + + false + + + + + + + + + + + + + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + + + Direction indication: + + + + + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + + + ASCII identification: + + + + + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Regular Expression + + + Import a file formatted according to a custom regular expression + + + + + + + + Packet format regular expression + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 1 + 1 + + + + + 100 + 100 + + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + + + 24 + + + false + + + ^(?<dir>(<|>))\s*(?<time>(\d\d\:){2}\d\d)\s+(?<seqno>\d{5})\s+(?<data>[0-9a-fA-F]*)$\s+ + + + + + + + true + + + This is regexHintLabel, it will be set to default_regex_hint + + + true + + + + + + + QLayout::SetDefaultConstraint + + + + + Data encoding: + + + + + + + How data is encoded + + + + + + + <small><i>recommended regex:</small></i> + + + + + + + encodingRegexExample + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Direction indication: + + + + + + + + 0 + 0 + + + + List of characters indicating incoming packets + + + 6 + + + iI< + + + + + + + true + + + List of characters indicating outgoing packets + + + oO> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + + + Timestamp format: + + + + + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + + + %H:%M:%S.%f + + + + + + + timestampExampleLabel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Encapsulation + + + true + + + + QFormLayout::FieldsStayAtSizeHint + + + + + + + Encapsulation Type: + + + + + + + Encapsulation type of the frames in the import capture file + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 1 + + + + + Leave frames unchanged + + + No dummy header + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Prefix each frame with an Ethernet header + + + Ethernet + + + + + + + Prefix each frame with an Ethernet and IP header + + + IP + + + + + + + Prefix each frame with an Ethernet, IP and UDP header + + + UDP + + + + + + + Prefix each frame with an Ethernet, IP and TCP header + + + TCP + + + + + + + Prefix each frame with an Ethernet, IP and SCTP header + + + SCTP + + + + + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + + + SCTP (Data) + + + + + + + ExportPDU + + + + + + + Ethertype (hex): + + + + + + + Protocol (dec): + + + + + + + Source address: + + + + + + + Destination address: + + + + + + + Source port: + + + + + + + Destination port: + + + + + + + Tag: + + + + + + + PPI: + + + + + + + Dissector + + + + + + + The Ethertype value of each frame + + + 0 + + + + + + + The IP protocol ID for each frame + + + + + + + The IP source address for each frame + + + + + + + The IP destination address for each frame + + + + + + + The UDP, TCP or SCTP source port for each frame + + + + + + + The UDP, TCP or SCTP destination port for each frame + + + + + + + The SCTP verification tag for each frame + + + + + + + The SCTP DATA payload protocol identifier for each frame + + + + + + + The dissector to use for each frame + + + + + + + IP version: + + + + + + + The IP Version to use for the dummy IP header + + + + + + + + + + + + + + Interface name: + + + + + + + The name of the interface to write to the import capture file + + + Fake IF, Import from Hex Dump + + + + + + + + + + + Maximum frame length: + + + + + + + The maximum size of the frames to write to the import capture file (max 256kiB) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Open + + + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+ + SyntaxLineEdit + QLineEdit +
widgets/syntax_line_edit.h
+
+
+ + + + buttonBox + accepted() + ImportTextDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ImportTextDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/interface_frame.cpp b/ui/qt/interface_frame.cpp new file mode 100644 index 00000000..b1f34b22 --- /dev/null +++ b/ui/qt/interface_frame.cpp @@ -0,0 +1,542 @@ +/* interface_frame.cpp + * Display of interfaces, including their respective data, and the + * capability to filter interfaces by type + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "config.h" +#include + +#include "capture/capture_ifinfo.h" + +#ifdef Q_OS_WIN +#include "capture/capture-wpcap.h" +#endif + +#include "ui/qt/interface_frame.h" +#include +#include + +#include +#include + +#include + + +#include "extcap.h" + +#include +#include "capture_opts.h" +#include "ui/capture_globals.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BTN_IFTYPE_PROPERTY "ifType" + +#ifdef HAVE_LIBPCAP +const int stat_update_interval_ = 1000; // ms +#endif +const char *no_capture_link = "#no_capture"; + +InterfaceFrame::InterfaceFrame(QWidget * parent) +: QFrame(parent), + ui(new Ui::InterfaceFrame) + , proxy_model_(Q_NULLPTR) + , source_model_(Q_NULLPTR) + , info_model_(this) +#ifdef HAVE_LIBPCAP + ,stat_timer_(NULL) +#endif // HAVE_LIBPCAP +{ + ui->setupUi(this); + + setStyleSheet(QString( + "QFrame {" + " border: 0;" + "}" + "QTreeView {" + " border: 0;" + "}" + )); + + ui->warningLabel->hide(); + +#ifdef Q_OS_MAC + ui->interfaceTree->setAttribute(Qt::WA_MacShowFocusRect, false); +#endif + + /* TODO: There must be a better way to do this */ + ifTypeDescription.insert(IF_WIRED, tr("Wired")); + ifTypeDescription.insert(IF_AIRPCAP, tr("AirPCAP")); + ifTypeDescription.insert(IF_PIPE, tr("Pipe")); + ifTypeDescription.insert(IF_STDIN, tr("STDIN")); + ifTypeDescription.insert(IF_BLUETOOTH, tr("Bluetooth")); + ifTypeDescription.insert(IF_WIRELESS, tr("Wireless")); + ifTypeDescription.insert(IF_DIALUP, tr("Dial-Up")); + ifTypeDescription.insert(IF_USB, tr("USB")); + ifTypeDescription.insert(IF_EXTCAP, tr("External Capture")); + ifTypeDescription.insert(IF_VIRTUAL, tr ("Virtual")); + + QList columns; + columns.append(IFTREE_COL_EXTCAP); + columns.append(IFTREE_COL_DISPLAY_NAME); + columns.append(IFTREE_COL_STATS); + proxy_model_.setColumns(columns); + proxy_model_.setStoreOnChange(true); + proxy_model_.setSortByActivity(true); + proxy_model_.setSourceModel(&source_model_); + + info_model_.setSourceModel(&proxy_model_); + info_model_.setColumn(static_cast(columns.indexOf(IFTREE_COL_STATS))); + + ui->interfaceTree->setModel(&info_model_); + ui->interfaceTree->setSortingEnabled(true); + + ui->interfaceTree->setItemDelegateForColumn(proxy_model_.mapSourceToColumn(IFTREE_COL_STATS), new SparkLineDelegate(this)); + + ui->interfaceTree->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->interfaceTree, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(interfaceListChanged())); + connect(mainApp, SIGNAL(localInterfaceListChanged()), this, SLOT(interfaceListChanged())); + + connect(ui->interfaceTree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(interfaceTreeSelectionChanged(const QItemSelection &, const QItemSelection &))); +} + +InterfaceFrame::~InterfaceFrame() +{ + delete ui; +} + +QMenu * InterfaceFrame::getSelectionMenu() +{ + QMenu * contextMenu = new QMenu(this); + QList typesDisplayed = proxy_model_.typesDisplayed(); + + QMap::const_iterator it = ifTypeDescription.constBegin(); + while (it != ifTypeDescription.constEnd()) + { + int ifType = it.key(); + + if (typesDisplayed.contains(ifType)) + { + QAction *endp_action = new QAction(it.value(), this); + endp_action->setData(QVariant::fromValue(ifType)); + endp_action->setCheckable(true); + endp_action->setChecked(proxy_model_.isInterfaceTypeShown(ifType)); + connect(endp_action, SIGNAL(triggered()), this, SLOT(triggeredIfTypeButton())); + contextMenu->addAction(endp_action); + } + ++it; + } + +#ifdef HAVE_PCAP_REMOTE + if (proxy_model_.remoteInterfacesExist()) + { + QAction * toggleRemoteAction = new QAction(tr("Remote interfaces"), this); + toggleRemoteAction->setCheckable(true); + toggleRemoteAction->setChecked(proxy_model_.remoteDisplay()); + connect(toggleRemoteAction, SIGNAL(triggered()), this, SLOT(toggleRemoteInterfaces())); + contextMenu->addAction(toggleRemoteAction); + } +#endif + + contextMenu->addSeparator(); + QAction * toggleHideAction = new QAction(tr("Show hidden interfaces"), this); + toggleHideAction->setCheckable(true); + toggleHideAction->setChecked(! proxy_model_.filterHidden()); + connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHiddenInterfaces())); + contextMenu->addAction(toggleHideAction); + + return contextMenu; +} + +int InterfaceFrame::interfacesHidden() +{ + return proxy_model_.interfacesHidden(); +} + +int InterfaceFrame::interfacesPresent() +{ + return source_model_.rowCount() - proxy_model_.interfacesHidden(); +} + +void InterfaceFrame::ensureSelectedInterface() +{ +#ifdef HAVE_LIBPCAP + if (interfacesPresent() < 1) return; + + if (source_model_.selectedDevices().count() < 1) { + QModelIndex first_idx = info_model_.mapFromSource(proxy_model_.index(0, 0)); + ui->interfaceTree->setCurrentIndex(first_idx); + } + + ui->interfaceTree->setFocus(); +#endif +} + +void InterfaceFrame::hideEvent(QHideEvent *) { +#ifdef HAVE_LIBPCAP + if (stat_timer_) + stat_timer_->stop(); + source_model_.stopStatistic(); +#endif // HAVE_LIBPCAP +} + +void InterfaceFrame::showEvent(QShowEvent *) { + +#ifdef HAVE_LIBPCAP + if (stat_timer_) + stat_timer_->start(stat_update_interval_); +#endif // HAVE_LIBPCAP +} + +void InterfaceFrame::actionButton_toggled(bool checked) +{ + QVariant ifType = sender()->property(BTN_IFTYPE_PROPERTY); + if (ifType.isValid()) + { + proxy_model_.setInterfaceTypeVisible(ifType.toInt(), checked); + } + + resetInterfaceTreeDisplay(); +} + +void InterfaceFrame::triggeredIfTypeButton() +{ + QAction *sender = qobject_cast(QObject::sender()); + if (sender) + { + int ifType = sender->data().value(); + proxy_model_.toggleTypeVisibility(ifType); + + resetInterfaceTreeDisplay(); + emit typeSelectionChanged(); + } +} + +void InterfaceFrame::interfaceListChanged() +{ + info_model_.clearInfos(); + if (prefs.capture_no_extcap) + info_model_.appendInfo(tr("External capture interfaces disabled.")); + + resetInterfaceTreeDisplay(); + // Ensure that device selection is consistent with the displayed selection. + updateSelectedInterfaces(); + +#ifdef HAVE_LIBPCAP + if (!stat_timer_) { + updateStatistics(); + stat_timer_ = new QTimer(this); + connect(stat_timer_, SIGNAL(timeout()), this, SLOT(updateStatistics())); + stat_timer_->start(stat_update_interval_); + } +#endif +} + +void InterfaceFrame::toggleHiddenInterfaces() +{ + source_model_.interfaceListChanged(); + proxy_model_.toggleFilterHidden(); + + emit typeSelectionChanged(); +} + +#ifdef HAVE_PCAP_REMOTE +void InterfaceFrame::toggleRemoteInterfaces() +{ + proxy_model_.toggleRemoteDisplay(); + emit typeSelectionChanged(); +} +#endif + +void InterfaceFrame::resetInterfaceTreeDisplay() +{ + ui->warningLabel->hide(); + ui->warningLabel->clear(); + + ui->warningLabel->setStyleSheet(QString( + "QLabel {" + " border-radius: 0.5em;" + " padding: 0.33em;" + " margin-bottom: 0.25em;" + // We might want to transition this to normal colors this after a timeout. + " background-color: %2;" + "}" + ).arg(ColorUtils::warningBackground().name())); + +#ifdef HAVE_LIBPCAP +#ifdef Q_OS_WIN + if (!has_wpcap) { + ui->warningLabel->setText(tr( + "

" + "Local interfaces are unavailable because no packet capture driver is installed." + "

" + "You can fix this by installing Npcap." + "

")); + } else if (!npf_sys_is_running()) { + ui->warningLabel->setText(tr( + "

" + "Local interfaces are unavailable because the packet capture driver isn't loaded." + "

" + "You can fix this by running

net start npcap
if you have Npcap installed" + " or
net start npf
if you have WinPcap installed." + " Both commands must be run as Administrator." + "

")); + } +#endif + + if (!haveLocalCapturePermissions()) + { +#ifdef Q_OS_MAC + // + // NOTE: if you change this text, you must also change the + // definition of PLATFORM_PERMISSIONS_SUGGESTION that is + // used if __APPLE__ is defined, so that it reflects the + // new message text. + // + QString install_chmodbpf_path = mainApp->applicationDirPath() + "/../Resources/Extras/Install ChmodBPF.pkg"; + ui->warningLabel->setText(tr( + "

" + "You don't have permission to capture on local interfaces." + "

" + "You can fix this by installing ChmodBPF." + "

") + .arg(install_chmodbpf_path)); +#else + // + // XXX - should this give similar platform-dependent recommendations, + // just as dumpcap gives platform-dependent recommendations in its + // PLATFORM_PERMISSIONS_SUGGESTION #define? + // + ui->warningLabel->setText(tr("You don't have permission to capture on local interfaces.")); +#endif + } + + if (proxy_model_.rowCount() == 0) + { + ui->warningLabel->setText(tr("No interfaces found.")); + ui->warningLabel->setText(proxy_model_.interfaceError()); + if (prefs.capture_no_interface_load) { + ui->warningLabel->setText(tr("Interfaces not loaded (due to preference). Go to Capture " UTF8_RIGHTWARDS_ARROW " Refresh Interfaces to load.")); + } + } + + // XXX Should we have a separate recent pref for each message? + if (!ui->warningLabel->text().isEmpty() && recent.sys_warn_if_no_capture) + { + QString warning_text = ui->warningLabel->text(); + warning_text.append(QString("

%2

") + .arg(no_capture_link) + .arg(SimpleDialog::dontShowThisAgain())); + ui->warningLabel->setText(warning_text); + + ui->warningLabel->show(); + } +#endif // HAVE_LIBPCAP + + if (proxy_model_.rowCount() > 0) + { + ui->interfaceTree->show(); + ui->interfaceTree->resizeColumnToContents(proxy_model_.mapSourceToColumn(IFTREE_COL_EXTCAP)); + ui->interfaceTree->resizeColumnToContents(proxy_model_.mapSourceToColumn(IFTREE_COL_DISPLAY_NAME)); + ui->interfaceTree->resizeColumnToContents(proxy_model_.mapSourceToColumn(IFTREE_COL_STATS)); + } + else + { + ui->interfaceTree->hide(); + } +} + +// XXX Should this be in capture/capture-pcap-util.[ch]? +bool InterfaceFrame::haveLocalCapturePermissions() const +{ +#ifdef Q_OS_MAC + QFileInfo bpf0_fi = QFileInfo("/dev/bpf0"); + return bpf0_fi.isReadable() && bpf0_fi.isWritable(); +#else + // XXX Add checks for other platforms. + return true; +#endif +} + +void InterfaceFrame::updateSelectedInterfaces() +{ + if (source_model_.rowCount() == 0) + return; +#ifdef HAVE_LIBPCAP + QItemSelection sourceSelection = source_model_.selectedDevices(); + QItemSelection mySelection = info_model_.mapSelectionFromSource(proxy_model_.mapSelectionFromSource(sourceSelection)); + + ui->interfaceTree->selectionModel()->clearSelection(); + ui->interfaceTree->selectionModel()->select(mySelection, QItemSelectionModel::SelectCurrent); +#endif +} + +void InterfaceFrame::interfaceTreeSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected) +{ + if (selected.count() == 0 && deselected.count() == 0) + return; + if (source_model_.rowCount() == 0) + return; + +#ifdef HAVE_LIBPCAP + /* Take all selected interfaces, not just the newly ones */ + QItemSelection allSelected = ui->interfaceTree->selectionModel()->selection(); + QItemSelection sourceSelection = proxy_model_.mapSelectionToSource(info_model_.mapSelectionToSource(allSelected)); + + if (source_model_.updateSelectedDevices(sourceSelection)) + emit itemSelectionChanged(); +#endif +} + +void InterfaceFrame::on_interfaceTree_doubleClicked(const QModelIndex &index) +{ + QModelIndex realIndex = proxy_model_.mapToSource(info_model_.mapToSource(index)); + + if (! realIndex.isValid()) + return; + + QStringList interfaces; + +#ifdef HAVE_LIBPCAP + + QString device_name = source_model_.getColumnContent(realIndex.row(), IFTREE_COL_NAME).toString(); + QString extcap_string = source_model_.getColumnContent(realIndex.row(), IFTREE_COL_EXTCAP_PATH).toString(); + + interfaces << device_name; + + /* We trust the string here. If this interface is really extcap, the string is + * being checked immediatly before the dialog is being generated */ + if (extcap_string.length() > 0) + { + /* this checks if configuration is required and not yet provided or saved via prefs */ + if (extcap_requires_configuration((const char *)(device_name.toStdString().c_str()))) + { + emit showExtcapOptions(device_name, true); + return; + } + } +#endif + + // Start capture for all columns except the first one with extcap + if (IFTREE_COL_EXTCAP != realIndex.column()) { + startCapture(interfaces); + } +} + +#ifdef HAVE_LIBPCAP +void InterfaceFrame::on_interfaceTree_clicked(const QModelIndex &index) +{ + if (index.column() == 0) + { + QModelIndex realIndex = proxy_model_.mapToSource(info_model_.mapToSource(index)); + + if (! realIndex.isValid()) + return; + + QString device_name = source_model_.getColumnContent(realIndex.row(), IFTREE_COL_NAME).toString(); + QString extcap_string = source_model_.getColumnContent(realIndex.row(), IFTREE_COL_EXTCAP_PATH).toString(); + + /* We trust the string here. If this interface is really extcap, the string is + * being checked immediatly before the dialog is being generated */ + if (extcap_string.length() > 0) + { + /* this checks if configuration is required and not yet provided or saved via prefs */ + if (extcap_has_configuration((const char *)(device_name.toStdString().c_str()))) + { + emit showExtcapOptions(device_name, false); + return; + } + } + } +} +#endif + +void InterfaceFrame::updateStatistics(void) +{ + if (source_model_.rowCount() == 0) + return; + +#ifdef HAVE_LIBPCAP + + for (int idx = 0; idx < source_model_.rowCount(); idx++) + { + QModelIndex selectIndex = info_model_.mapFromSource(proxy_model_.mapFromSource(source_model_.index(idx, 0))); + + /* Proxy model has not masked out the interface */ + if (selectIndex.isValid()) + source_model_.updateStatistic(idx); + } +#endif +} + +void InterfaceFrame::showRunOnFile(void) +{ + ui->warningLabel->setText("Interfaces not loaded on startup (run on capture file). Go to Capture -> Refresh Interfaces to load."); +} + +void InterfaceFrame::showContextMenu(QPoint pos) +{ + QMenu * ctx_menu = new QMenu(this); + // Work around QTBUG-106718. For some reason Qt::WA_DeleteOnClose + + // Qt::QueuedConnection don't work here. + QObject::connect(ctx_menu, &QMenu::triggered, ctx_menu, &QMenu::deleteLater); + + ctx_menu->addAction(tr("Start capture"), this, [=] () { + QStringList ifaces; + QModelIndexList selIndices = ui->interfaceTree->selectionModel()->selectedIndexes(); + foreach(QModelIndex idx, selIndices) + { + QModelIndex realIndex = proxy_model_.mapToSource(info_model_.mapToSource(idx)); + if (realIndex.column() != IFTREE_COL_NAME) + realIndex = realIndex.sibling(realIndex.row(), IFTREE_COL_NAME); + QString name = realIndex.data(Qt::DisplayRole).toString(); + if (! ifaces.contains(name)) + ifaces << name; + } + + startCapture(ifaces); + }); + + ctx_menu->addSeparator(); + + QModelIndex actIndex = ui->interfaceTree->indexAt(pos); + QModelIndex realIndex = proxy_model_.mapToSource(info_model_.mapToSource(actIndex)); + bool isHidden = realIndex.sibling(realIndex.row(), IFTREE_COL_HIDDEN).data(Qt::UserRole).toBool(); + QAction * hideAction = ctx_menu->addAction(tr("Hide Interface"), this, [=] () { + /* Attention! Only realIndex.row is a 1:1 correlation to all_ifaces */ + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, realIndex.row()); + device->hidden = ! device->hidden; + mainApp->emitAppSignal(MainApplication::LocalInterfacesChanged); + }); + hideAction->setCheckable(true); + hideAction->setChecked(isHidden); + + ctx_menu->popup(ui->interfaceTree->mapToGlobal(pos)); +} + +void InterfaceFrame::on_warningLabel_linkActivated(const QString &link) +{ + if (link.compare(no_capture_link) == 0) { + recent.sys_warn_if_no_capture = FALSE; + resetInterfaceTreeDisplay(); + } else { + QDesktopServices::openUrl(QUrl(link)); + } +} diff --git a/ui/qt/interface_frame.h b/ui/qt/interface_frame.h new file mode 100644 index 00000000..f36ce945 --- /dev/null +++ b/ui/qt/interface_frame.h @@ -0,0 +1,99 @@ +/** @file + * + * Display of interfaces, including their respective data, and the + * capability to filter interfaces by type + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INTERFACE_FRAME_H +#define INTERFACE_FRAME_H + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Ui { +class InterfaceFrame; +} + +class InterfaceFrame : public QFrame +{ + Q_OBJECT +public: + explicit InterfaceFrame(QWidget *parent = 0); + ~InterfaceFrame(); + + int interfacesHidden(); + + QMenu * getSelectionMenu(); + int interfacesPresent(); + void ensureSelectedInterface(); + +Q_SIGNALS: + void showExtcapOptions(QString device_name, bool startCaptureOnClose); + void startCapture(QStringList); + void itemSelectionChanged(); + void typeSelectionChanged(); + +public slots: + void updateSelectedInterfaces(); + void interfaceListChanged(); + void toggleHiddenInterfaces(); +#ifdef HAVE_PCAP_REMOTE + void toggleRemoteInterfaces(); +#endif + void showRunOnFile(); + void showContextMenu(QPoint pos); + +protected: + void hideEvent(QHideEvent *evt); + void showEvent(QShowEvent *evt); + +private: + + void resetInterfaceTreeDisplay(); + bool haveLocalCapturePermissions() const; + + Ui::InterfaceFrame *ui; + + InterfaceSortFilterModel proxy_model_; + InterfaceTreeModel source_model_; + InfoProxyModel info_model_; + + QMap ifTypeDescription; + +#ifdef HAVE_LIBPCAP + QTimer *stat_timer_; +#endif // HAVE_LIBPCAP + +private slots: + void interfaceTreeSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected); + + void on_interfaceTree_doubleClicked(const QModelIndex &index); +#ifdef HAVE_LIBPCAP + void on_interfaceTree_clicked(const QModelIndex &index); +#endif + + void updateStatistics(void); + void actionButton_toggled(bool checked); + void triggeredIfTypeButton(); + void on_warningLabel_linkActivated(const QString &link); +}; + +#endif // INTERFACE_FRAME_H diff --git a/ui/qt/interface_frame.ui b/ui/qt/interface_frame.ui new file mode 100644 index 00000000..a49bcf90 --- /dev/null +++ b/ui/qt/interface_frame.ui @@ -0,0 +1,83 @@ + + + InterfaceFrame + + + + 0 + 0 + 256 + 209 + + + + + 0 + 0 + + + + Frame + + + 0 + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + Qt::RichText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + Qt::TextBrowserInteraction + + + + + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + + + + + + diff --git a/ui/qt/interface_toolbar.cpp b/ui/qt/interface_toolbar.cpp new file mode 100644 index 00000000..be2cde66 --- /dev/null +++ b/ui/qt/interface_toolbar.cpp @@ -0,0 +1,1018 @@ +/* interface_toolbar.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_OFF(stringop-overflow) +#if WS_IS_AT_LEAST_GNUC_VERSION(13,0) +DIAG_OFF(restrict) +#endif +#endif +#include "interface_toolbar.h" +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_ON(stringop-overflow) +#if WS_IS_AT_LEAST_GNUC_VERSION(13,0) +DIAG_ON(restrict) +#endif +#endif +#include +#include "simple_dialog.h" +#include "main_application.h" +#include + +#include "capture_opts.h" +#include "ui/capture_globals.h" +#include "sync_pipe.h" +#include "wsutil/file_util.h" + +#ifdef _WIN32 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +static const char *interface_type_property = "control_type"; +static const char *interface_role_property = "control_role"; + +// From interface control protocol. +enum InterfaceControlCommand { + commandControlInitialized = 0, + commandControlSet = 1, + commandControlAdd = 2, + commandControlRemove = 3, + commandControlEnable = 4, + commandControlDisable = 5, + commandStatusMessage = 6, + commandInformationMessage = 7, + commandWarningMessage = 8, + commandErrorMessage = 9 +}; + +// To do: +// - Move control pipe handling to extcap + +InterfaceToolbar::InterfaceToolbar(QWidget *parent, const iface_toolbar *toolbar) : + QFrame(parent), + ui(new Ui::InterfaceToolbar), + help_link_(toolbar->help), + use_spacer_(true) +{ + ui->setupUi(this); + + // Fill inn interfaces list and initialize default interface values + for (GList *walker = toolbar->ifnames; walker; walker = walker->next) + { + QString ifname((gchar *)walker->data); + interface_[ifname].reader_thread = NULL; + interface_[ifname].out_fd = -1; + } + + initializeControls(toolbar); + +#ifdef Q_OS_MAC + foreach (QWidget *w, findChildren()) + { + w->setAttribute(Qt::WA_MacSmallSize, true); + } +#endif + + if (!use_spacer_) + { + ui->horizontalSpacer->changeSize(0,0, QSizePolicy::Fixed, QSizePolicy::Fixed); + } + + updateWidgets(); +} + +InterfaceToolbar::~InterfaceToolbar() +{ + foreach (QString ifname, interface_.keys()) + { + foreach (int num, control_widget_.keys()) + { + if (interface_[ifname].log_dialog.contains(num)) + { + interface_[ifname].log_dialog[num]->close(); + } + } + } + + delete ui; +} + +void InterfaceToolbar::initializeControls(const iface_toolbar *toolbar) +{ + for (GList *walker = toolbar->controls; walker; walker = walker->next) + { + iface_toolbar_control *control = (iface_toolbar_control *)walker->data; + + if (control_widget_.contains(control->num)) + { + // Already have a widget with this number + continue; + } + + QWidget *widget = NULL; + switch (control->ctrl_type) + { + case INTERFACE_TYPE_BOOLEAN: + widget = createCheckbox(control); + break; + + case INTERFACE_TYPE_BUTTON: + widget = createButton(control); + break; + + case INTERFACE_TYPE_SELECTOR: + widget = createSelector(control); + break; + + case INTERFACE_TYPE_STRING: + widget = createString(control); + break; + + default: + // Not supported + break; + } + + if (widget) + { + widget->setProperty(interface_type_property, control->ctrl_type); + widget->setProperty(interface_role_property, control->ctrl_role); + control_widget_[control->num] = widget; + } + } +} + +void InterfaceToolbar::setDefaultValue(int num, const QByteArray &value) +{ + foreach (QString ifname, interface_.keys()) + { + // Adding default value to all interfaces + interface_[ifname].value[num] = value; + } + default_value_[num] = value; +} + +QWidget *InterfaceToolbar::createCheckbox(iface_toolbar_control *control) +{ + QCheckBox *checkbox = new QCheckBox(QString().fromUtf8(control->display)); + checkbox->setToolTip(QString().fromUtf8(control->tooltip)); + + if (control->default_value.boolean) + { + checkbox->setCheckState(Qt::Checked); + QByteArray default_value(1, 1); + setDefaultValue(control->num, default_value); + } + + connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxChanged(int))); + + ui->leftLayout->addWidget(checkbox); + + return checkbox; +} + +QWidget *InterfaceToolbar::createButton(iface_toolbar_control *control) +{ + QPushButton *button = new QPushButton(QString().fromUtf8((gchar *)control->display)); + button->setMaximumHeight(27); + button->setToolTip(QString().fromUtf8(control->tooltip)); + + switch (control->ctrl_role) + { + case INTERFACE_ROLE_CONTROL: + setDefaultValue(control->num, (gchar *)control->display); + connect(button, SIGNAL(clicked()), this, SLOT(onControlButtonClicked())); + break; + + case INTERFACE_ROLE_HELP: + connect(button, SIGNAL(clicked()), this, SLOT(onHelpButtonClicked())); + if (help_link_.isEmpty()) + { + // No help URL provided + button->hide(); + } + break; + + case INTERFACE_ROLE_LOGGER: + connect(button, SIGNAL(clicked()), this, SLOT(onLogButtonClicked())); + break; + + case INTERFACE_ROLE_RESTORE: + connect(button, SIGNAL(clicked()), this, SLOT(onRestoreButtonClicked())); + break; + + default: + // Not supported + break; + } + + ui->rightLayout->addWidget(button); + + return button; +} + +QWidget *InterfaceToolbar::createSelector(iface_toolbar_control *control) +{ + QLabel *label = new QLabel(QString().fromUtf8(control->display)); + label->setToolTip(QString().fromUtf8(control->tooltip)); + QComboBox *combobox = new QComboBox(); + combobox->setToolTip(QString().fromUtf8(control->tooltip)); + combobox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + for (GList *walker = control->values; walker; walker = walker->next) + { + iface_toolbar_value *val = (iface_toolbar_value *)walker->data; + QString value = QString().fromUtf8((gchar *)val->value); + if (value.isEmpty()) + { + // Invalid value + continue; + } + QString display = QString().fromUtf8((gchar *)val->display); + QByteArray interface_value; + + interface_value.append(value.toUtf8()); + if (display.isEmpty()) + { + display = value; + } + else + { + interface_value.append(QString('\0' + display).toUtf8()); + } + combobox->addItem(display, value); + if (val->is_default) + { + combobox->setCurrentText(display); + setDefaultValue(control->num, value.toUtf8()); + } + foreach (QString ifname, interface_.keys()) + { + // Adding values to all interfaces + interface_[ifname].list[control->num].append(interface_value); + } + default_list_[control->num].append(interface_value); + } + + connect(combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(onComboBoxChanged(int))); + + ui->leftLayout->addWidget(label); + ui->leftLayout->addWidget(combobox); + label_widget_[control->num] = label; + + return combobox; +} + +QWidget *InterfaceToolbar::createString(iface_toolbar_control *control) +{ + QLabel *label = new QLabel(QString().fromUtf8(control->display)); + label->setToolTip(QString().fromUtf8(control->tooltip)); + InterfaceToolbarLineEdit *lineedit = new InterfaceToolbarLineEdit(NULL, control->validation, control->is_required); + lineedit->setToolTip(QString().fromUtf8(control->tooltip)); + lineedit->setPlaceholderText(QString().fromUtf8(control->placeholder)); + + if (control->default_value.string) + { + lineedit->setText(QString().fromUtf8(control->default_value.string)); + setDefaultValue(control->num, control->default_value.string); + } + + connect(lineedit, SIGNAL(editedTextApplied()), this, SLOT(onLineEditChanged())); + + ui->leftLayout->addWidget(label); + ui->leftLayout->addWidget(lineedit); + label_widget_[control->num] = label; + use_spacer_ = false; + + return lineedit; +} + +void InterfaceToolbar::setWidgetValue(QWidget *widget, int command, QByteArray payload) +{ + if (QComboBox *combobox = qobject_cast(widget)) + { + combobox->blockSignals(true); + switch (command) + { + case commandControlSet: + { + int idx = combobox->findData(payload); + if (idx != -1) + { + combobox->setCurrentIndex(idx); + } + break; + } + + case commandControlAdd: + { + QString value; + QString display; + if (payload.contains('\0')) + { + // The payload contains "value\0display" + QList values = payload.split('\0'); + value = values[0]; + display = values[1]; + } + else + { + value = display = payload; + } + + int idx = combobox->findData(value); + if (idx != -1) + { + // The value already exists, update item text + combobox->setItemText(idx, display); + } + else + { + combobox->addItem(display, value); + } + break; + } + + case commandControlRemove: + { + if (payload.size() == 0) + { + combobox->clear(); + } + else + { + int idx = combobox->findData(payload); + if (idx != -1) + { + combobox->removeItem(idx); + } + } + break; + } + + default: + break; + } + combobox->blockSignals(false); + } + else if (InterfaceToolbarLineEdit *lineedit = qobject_cast(widget)) + { + // We don't block signals here because changes are applied with enter or apply button, + // and we want InterfaceToolbarLineEdit to always syntax check the text. + switch (command) + { + case commandControlSet: + lineedit->setText(payload); + lineedit->disableApplyButton(); + break; + + default: + break; + } + } + else if (QCheckBox *checkbox = qobject_cast(widget)) + { + checkbox->blockSignals(true); + switch (command) + { + case commandControlSet: + { + Qt::CheckState state = Qt::Unchecked; + if (payload.size() > 0 && payload.at(0) != 0) + { + state = Qt::Checked; + } + checkbox->setCheckState(state); + break; + } + + default: + break; + } + checkbox->blockSignals(false); + } + else if (QPushButton *button = qobject_cast(widget)) + { + if ((command == commandControlSet) && + widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL) + { + button->setText(payload); + } + } +} + +void InterfaceToolbar::setInterfaceValue(QString ifname, QWidget *widget, int num, int command, QByteArray payload) +{ + if (!widget) { + return; + } + + if (qobject_cast(widget)) + { + switch (command) + { + case commandControlSet: + if (interface_[ifname].value[num] != payload) + { + interface_[ifname].value_changed[num] = false; + } + foreach (QByteArray entry, interface_[ifname].list[num]) + { + if (entry == payload || entry.startsWith(payload + '\0')) + { + interface_[ifname].value[num] = payload; + } + } + break; + + case commandControlAdd: + interface_[ifname].list[num].append(payload); + break; + + case commandControlRemove: + if (payload.size() == 0) + { + interface_[ifname].value[num].clear(); + interface_[ifname].list[num].clear(); + } + else + { + foreach (QByteArray entry, interface_[ifname].list[num]) + { + if (entry == payload || entry.startsWith(payload + '\0')) + { + interface_[ifname].list[num].removeAll(entry); + } + } + } + break; + + default: + break; + } + } + else if (qobject_cast(widget)) + { + switch (command) + { + case commandControlSet: + if (interface_[ifname].value[num] != payload) + { + interface_[ifname].value_changed[num] = false; + } + interface_[ifname].value[num] = payload; + break; + + default: + break; + } + } + else if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) && + (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_LOGGER)) + { + if (command == commandControlSet) + { + if (interface_[ifname].log_dialog.contains(num)) + { + interface_[ifname].log_dialog[num]->clearText(); + } + interface_[ifname].log_text.clear(); + } + if (command == commandControlSet || command == commandControlAdd) + { + if (interface_[ifname].log_dialog.contains(num)) + { + interface_[ifname].log_dialog[num]->appendText(payload); + } + interface_[ifname].log_text[num].append(payload); + } + } + else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL) + { + // QCheckBox or QPushButton + switch (command) + { + case commandControlSet: + if (interface_[ifname].value[num] != payload) + { + interface_[ifname].value_changed[num] = false; + } + interface_[ifname].value[num] = payload; + break; + + default: + break; + } + } +} + +void InterfaceToolbar::controlReceived(QString ifname, int num, int command, QByteArray payload) +{ + switch (command) + { + case commandControlSet: + case commandControlAdd: + case commandControlRemove: + if (control_widget_.contains(num)) + { + QWidget *widget = control_widget_[num]; + setInterfaceValue(ifname, widget, num, command, payload); + + if (ifname.compare(ui->interfacesComboBox->currentText()) == 0) + { + setWidgetValue(widget, command, payload); + } + } + break; + + case commandControlEnable: + case commandControlDisable: + if (control_widget_.contains(num)) + { + QWidget *widget = control_widget_[num]; + if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL) + { + bool enable = (command == commandControlEnable ? true : false); + interface_[ifname].widget_disabled[num] = !enable; + + if (ifname.compare(ui->interfacesComboBox->currentText()) == 0) + { + widget->setEnabled(enable); + if (label_widget_.contains(num)) + { + label_widget_[num]->setEnabled(enable); + } + } + } + } + break; + + case commandStatusMessage: + mainApp->pushStatus(MainApplication::TemporaryStatus, payload); + break; + + case commandInformationMessage: + simple_dialog_async(ESD_TYPE_INFO, ESD_BTN_OK, "%s", payload.data()); + break; + + case commandWarningMessage: + simple_dialog_async(ESD_TYPE_WARN, ESD_BTN_OK, "%s", payload.data()); + break; + + case commandErrorMessage: + simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", payload.data()); + break; + + default: + // Unknown commands are silently ignored + break; + } +} + +void InterfaceToolbar::controlSend(QString ifname, int num, int command, const QByteArray &payload = QByteArray()) +{ + if (payload.length() > 65535) + { + // Not supported + return; + } + + if (ifname.isEmpty() || interface_[ifname].out_fd == -1) + { + // Does not have a control out channel + return; + } + + ssize_t payload_length = payload.length() + 2; + unsigned char high_nibble = (payload_length >> 16) & 0xFF; + unsigned char mid_nibble = (payload_length >> 8) & 0xFF; + unsigned char low_nibble = (payload_length >> 0) & 0xFF; + + QByteArray ba; + + ba.append(SP_TOOLBAR_CTRL); + ba.append(high_nibble); + ba.append(mid_nibble); + ba.append(low_nibble); + ba.append(num); + ba.append(command); + ba.append(payload); + + if (ws_write(interface_[ifname].out_fd, ba.data(), ba.length()) != ba.length()) + { + simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK, + "Unable to send control message:\n%s.", + g_strerror(errno)); + } +} + +void InterfaceToolbar::onControlButtonClicked() +{ + const QString &ifname = ui->interfacesComboBox->currentText(); + QPushButton *button = static_cast(sender()); + int num = control_widget_.key(button); + + controlSend(ifname, num, commandControlSet); +} + +void InterfaceToolbar::onCheckBoxChanged(int state) +{ + const QString &ifname = ui->interfacesComboBox->currentText(); + QCheckBox *checkbox = static_cast(sender()); + int num = control_widget_.key(checkbox); + + QByteArray payload(1, state == Qt::Unchecked ? 0 : 1); + controlSend(ifname, num, commandControlSet, payload); + interface_[ifname].value[num] = payload; + interface_[ifname].value_changed[num] = true; +} + +void InterfaceToolbar::onComboBoxChanged(int idx) +{ + const QString &ifname = ui->interfacesComboBox->currentText(); + QComboBox *combobox = static_cast(sender()); + int num = control_widget_.key(combobox); + QString value = combobox->itemData(idx).toString(); + + QByteArray payload(value.toUtf8()); + controlSend(ifname, num, commandControlSet, payload); + interface_[ifname].value[num] = payload; + interface_[ifname].value_changed[num] = true; +} + +void InterfaceToolbar::onLineEditChanged() +{ + const QString &ifname = ui->interfacesComboBox->currentText(); + InterfaceToolbarLineEdit *lineedit = static_cast(sender()); + int num = control_widget_.key(lineedit); + + QByteArray payload(lineedit->text().toUtf8()); + controlSend(ifname, num, commandControlSet, payload); + interface_[ifname].value[num] = payload; + interface_[ifname].value_changed[num] = true; +} + +void InterfaceToolbar::onLogButtonClicked() +{ + const QString &ifname = ui->interfacesComboBox->currentText(); + QPushButton *button = static_cast(sender()); + int num = control_widget_.key(button); + + if (!interface_[ifname].log_dialog.contains(num)) + { + interface_[ifname].log_dialog[num] = new FunnelTextDialog(window(), ifname + " " + button->text()); + connect(interface_[ifname].log_dialog[num], SIGNAL(accepted()), this, SLOT(closeLog())); + connect(interface_[ifname].log_dialog[num], SIGNAL(rejected()), this, SLOT(closeLog())); + + interface_[ifname].log_dialog[num]->setText(interface_[ifname].log_text[num]); + } + + interface_[ifname].log_dialog[num]->show(); + interface_[ifname].log_dialog[num]->raise(); + interface_[ifname].log_dialog[num]->activateWindow(); +} + +void InterfaceToolbar::onHelpButtonClicked() +{ + QUrl help_url(help_link_); + + if (help_url.scheme().compare("file") != 0) + { + QDesktopServices::openUrl(help_url); + } +} + +void InterfaceToolbar::closeLog() +{ + FunnelTextDialog *log_dialog = static_cast(sender()); + + foreach (QString ifname, interface_.keys()) + { + foreach (int num, control_widget_.keys()) + { + if (interface_[ifname].log_dialog.value(num) == log_dialog) + { + interface_[ifname].log_dialog.remove(num); + } + } + } +} + + +void InterfaceToolbar::startReaderThread(QString ifname, void *control_in) +{ + QThread *thread = new QThread; + InterfaceToolbarReader *reader = new InterfaceToolbarReader(ifname, control_in); + reader->moveToThread(thread); + + connect(thread, SIGNAL(started()), reader, SLOT(loop())); + connect(reader, SIGNAL(finished()), thread, SLOT(quit())); + connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater())); + connect(thread, SIGNAL(finished()), reader, SLOT(deleteLater())); + connect(reader, SIGNAL(received(QString, int, int, QByteArray)), + this, SLOT(controlReceived(QString, int, int, QByteArray))); + + interface_[ifname].reader_thread = thread; + + thread->start(); +} + +void InterfaceToolbar::startCapture(GArray *ifaces) +{ + if (!ifaces || ifaces->len == 0) + return; + + const QString &selected_ifname = ui->interfacesComboBox->currentText(); + QString first_capturing_ifname; + bool selected_found = false; + + for (guint i = 0; i < ifaces->len; i++) + { + interface_options *interface_opts = &g_array_index(ifaces, interface_options, i); + QString ifname(interface_opts->name); + + if (!interface_.contains(ifname)) + // This interface is not for us + continue; + + if (first_capturing_ifname.isEmpty()) + first_capturing_ifname = ifname; + + if (ifname.compare(selected_ifname) == 0) + selected_found = true; + + if (interface_[ifname].out_fd != -1) + // Already have control channels for this interface + continue; + + // Open control out channel +#ifdef _WIN32 + startReaderThread(ifname, interface_opts->extcap_control_in_h); + // Duplicate control out handle and pass the duplicate handle to _open_osfhandle(). + // This allows the C run-time file descriptor (out_fd) and the extcap_control_out_h to be closed independently. + // The duplicated handle will get closed at the same time the file descriptor is closed. + // The control out pipe will close when both out_fd and extcap_control_out_h are closed. + HANDLE duplicate_out_handle = INVALID_HANDLE_VALUE; + if (!DuplicateHandle(GetCurrentProcess(), interface_opts->extcap_control_out_h, + GetCurrentProcess(), &duplicate_out_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK, + "Failed to duplicate extcap control out handle: %s\n.", + win32strerror(GetLastError())); + } + else + { + interface_[ifname].out_fd = _open_osfhandle((intptr_t)duplicate_out_handle, O_APPEND | O_BINARY); + } +#else + startReaderThread(ifname, interface_opts->extcap_control_in); + interface_[ifname].out_fd = ws_open(interface_opts->extcap_control_out, O_WRONLY | O_BINARY, 0); +#endif + sendChangedValues(ifname); + controlSend(ifname, 0, commandControlInitialized); + } + + if (!selected_found && !first_capturing_ifname.isEmpty()) + { + ui->interfacesComboBox->setCurrentText(first_capturing_ifname); + } + else + { + updateWidgets(); + } +} + +void InterfaceToolbar::stopCapture() +{ + foreach (QString ifname, interface_.keys()) + { + if (interface_[ifname].reader_thread) + { + if (!interface_[ifname].reader_thread->isFinished()) + { + interface_[ifname].reader_thread->requestInterruption(); + } + interface_[ifname].reader_thread = NULL; + } + + if (interface_[ifname].out_fd != -1) + { + ws_close_if_possible (interface_[ifname].out_fd); + + interface_[ifname].out_fd = -1; + } + + foreach (int num, control_widget_.keys()) + { + // Reset disabled property for all widgets + interface_[ifname].widget_disabled[num] = false; + + QWidget *widget = control_widget_[num]; + if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) && + (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)) + { + // Reset default value for control buttons + interface_[ifname].value[num] = default_value_[num]; + + if (ifname.compare(ui->interfacesComboBox->currentText()) == 0) + { + setWidgetValue(widget, commandControlSet, default_value_[num]); + } + } + } + } + + updateWidgets(); +} + +void InterfaceToolbar::sendChangedValues(QString ifname) +{ + // Send all values which has changed + foreach (int num, control_widget_.keys()) + { + QWidget *widget = control_widget_[num]; + if ((interface_[ifname].value_changed[num]) && + (widget->property(interface_type_property).toInt() != INTERFACE_TYPE_BUTTON) && + (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)) + { + controlSend(ifname, num, commandControlSet, interface_[ifname].value[num]); + } + } +} + +void InterfaceToolbar::onRestoreButtonClicked() +{ + const QString &ifname = ui->interfacesComboBox->currentText(); + + // Set default values to all widgets and interfaces + foreach (int num, control_widget_.keys()) + { + QWidget *widget = control_widget_[num]; + if (default_list_[num].size() > 0) + { + // This is a QComboBox. Clear list and add new entries. + setWidgetValue(widget, commandControlRemove, QByteArray()); + interface_[ifname].list[num].clear(); + + foreach (QByteArray value, default_list_[num]) + { + setWidgetValue(widget, commandControlAdd, value); + interface_[ifname].list[num].append(value); + } + } + + switch (widget->property(interface_role_property).toInt()) + { + case INTERFACE_ROLE_CONTROL: + setWidgetValue(widget, commandControlSet, default_value_[num]); + interface_[ifname].value[num] = default_value_[num]; + interface_[ifname].value_changed[num] = false; + break; + + case INTERFACE_ROLE_LOGGER: + if (interface_[ifname].log_dialog.contains(num)) + { + interface_[ifname].log_dialog[num]->clearText(); + } + interface_[ifname].log_text[num].clear(); + break; + + default: + break; + } + } +} + +bool InterfaceToolbar::hasInterface(QString ifname) +{ + return interface_.contains(ifname); +} + +void InterfaceToolbar::updateWidgets() +{ + const QString &ifname = ui->interfacesComboBox->currentText(); + bool is_capturing = (interface_[ifname].out_fd == -1 ? false : true); + + foreach (int num, control_widget_.keys()) + { + QWidget *widget = control_widget_[num]; + bool widget_enabled = true; + + if (ifname.isEmpty() && + (widget->property(interface_role_property).toInt() != INTERFACE_ROLE_HELP)) + { + // No interface selected, disable all but Help button + widget_enabled = false; + } + else if (!is_capturing && + (widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) && + (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)) + { + widget_enabled = false; + } + else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL) + { + widget_enabled = !interface_[ifname].widget_disabled[num]; + } + + widget->setEnabled(widget_enabled); + if (label_widget_.contains(num)) + { + label_widget_[num]->setEnabled(widget_enabled); + } + } + + foreach (int num, control_widget_.keys()) + { + QWidget *widget = control_widget_[num]; + if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) && + (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_RESTORE)) + { + widget->setEnabled(!is_capturing); + } + } +} + +void InterfaceToolbar::interfaceListChanged() +{ +#ifdef HAVE_LIBPCAP + const QString &selected_ifname = ui->interfacesComboBox->currentText(); + bool keep_selected = false; + + ui->interfacesComboBox->blockSignals(true); + ui->interfacesComboBox->clear(); + + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) + { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (device->hidden) + continue; + + if (interface_.keys().contains(device->name)) + { + ui->interfacesComboBox->addItem(device->name); + if (selected_ifname.compare(device->name) == 0) + { + // Keep selected interface + ui->interfacesComboBox->setCurrentText(device->name); + keep_selected = true; + } + } + } + + ui->interfacesComboBox->blockSignals(false); + + if (!keep_selected) + { + // Select the first interface + on_interfacesComboBox_currentTextChanged(ui->interfacesComboBox->currentText()); + } + + updateWidgets(); +#endif +} + +void InterfaceToolbar::on_interfacesComboBox_currentTextChanged(const QString &ifname) +{ + foreach (int num, control_widget_.keys()) + { + QWidget *widget = control_widget_[num]; + if (interface_[ifname].list[num].size() > 0) + { + // This is a QComboBox. Clear list and add new entries. + setWidgetValue(widget, commandControlRemove, QByteArray()); + + foreach (QByteArray value, interface_[ifname].list[num]) + { + setWidgetValue(widget, commandControlAdd, value); + } + } + + if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL) + { + setWidgetValue(widget, commandControlSet, interface_[ifname].value[num]); + } + } + + updateWidgets(); +} diff --git a/ui/qt/interface_toolbar.h b/ui/qt/interface_toolbar.h new file mode 100644 index 00000000..f9879fa2 --- /dev/null +++ b/ui/qt/interface_toolbar.h @@ -0,0 +1,98 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INTERFACE_TOOLBAR_H +#define INTERFACE_TOOLBAR_H + +#include + +#include "ui/iface_toolbar.h" +#include "funnel_text_dialog.h" +#include "interface_toolbar_reader.h" + +#include +#include +#include +#include + + +namespace Ui { +class InterfaceToolbar; +} + +struct interface_values +{ + QThread *reader_thread; + int out_fd; + QMap value; + QMap value_changed; + QMap > list; + QMap log_dialog; + QMap log_text; + QMap widget_disabled; +}; + +class InterfaceToolbar : public QFrame +{ + Q_OBJECT + +public: + explicit InterfaceToolbar(QWidget *parent = 0, const iface_toolbar *toolbar = NULL); + ~InterfaceToolbar(); + + void startCapture(GArray *ifaces); + void stopCapture(); + bool hasInterface(QString ifname); + +public slots: + void interfaceListChanged(); + void controlReceived(QString ifname, int num, int command, QByteArray message); + +signals: + void closeReader(); + +private slots: + void startReaderThread(QString ifname, void *control_in); + void updateWidgets(); + + void onControlButtonClicked(); + void onLogButtonClicked(); + void onHelpButtonClicked(); + void onRestoreButtonClicked(); + void onCheckBoxChanged(int state); + void onComboBoxChanged(int idx); + void onLineEditChanged(); + + void closeLog(); + + void on_interfacesComboBox_currentTextChanged(const QString &ifname); + +private: + void initializeControls(const iface_toolbar *toolbar); + void setDefaultValue(int num, const QByteArray &value); + void sendChangedValues(QString ifname); + QWidget *createCheckbox(iface_toolbar_control *control); + QWidget *createButton(iface_toolbar_control *control); + QWidget *createSelector(iface_toolbar_control *control); + QWidget *createString(iface_toolbar_control *control); + void controlSend(QString ifname, int num, int type, const QByteArray &payload); + void setWidgetValue(QWidget *widget, int type, QByteArray payload); + void setInterfaceValue(QString ifname, QWidget *widget, int num, int type, QByteArray payload); + + Ui::InterfaceToolbar *ui; + QMap interface_; + QMap default_value_; + QMap > default_list_; + QMap control_widget_; + QMap label_widget_; + QString help_link_; + bool use_spacer_; +}; + +#endif // INTERFACE_TOOLBAR_H diff --git a/ui/qt/interface_toolbar.ui b/ui/qt/interface_toolbar.ui new file mode 100644 index 00000000..3c4f47c0 --- /dev/null +++ b/ui/qt/interface_toolbar.ui @@ -0,0 +1,72 @@ + + + InterfaceToolbar + + + + 0 + 0 + 600 + 32 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + + + Select interface + + + Interface + + + + + + + QComboBox::AdjustToContents + + + Select interface + + + + + + + + + + Qt::Horizontal + + + + 40 + 5 + + + + + + + + + + + + diff --git a/ui/qt/interface_toolbar_reader.cpp b/ui/qt/interface_toolbar_reader.cpp new file mode 100644 index 00000000..d12f7472 --- /dev/null +++ b/ui/qt/interface_toolbar_reader.cpp @@ -0,0 +1,181 @@ +/* interface_toolbar_reader.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include "interface_toolbar_reader.h" +#include "sync_pipe.h" +#include "wsutil/file_util.h" + +#include + +const int header_size = 6; + +#ifdef _WIN32 +int InterfaceToolbarReader::async_pipe_read(void *data, int nbyte) +{ + BOOL success; + DWORD nof_bytes_read; + OVERLAPPED overlap; + int bytes_read = -1; + + overlap.Pointer = 0; + overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (overlap.hEvent == NULL) + { + // CreateEvent failed with error code GetLastError() + return -1; + } + + success = ReadFile(control_in_, data, nbyte, &nof_bytes_read, &overlap); + + if (success && nof_bytes_read != 0) + { + // The read operation completed successfully. + bytes_read = nof_bytes_read; + } + else if (!success && GetLastError() == ERROR_IO_PENDING) + { + // The operation is still pending, wait for a signal. + if (WaitForSingleObject(overlap.hEvent, INFINITE) == WAIT_OBJECT_0) + { + // The wait operation has completed. + success = GetOverlappedResult(control_in_, &overlap, &nof_bytes_read, FALSE); + + if (success && nof_bytes_read != 0) + { + // The get result operation completed successfully. + bytes_read = nof_bytes_read; + } + } + } + + CloseHandle(overlap.hEvent); + return bytes_read; +} +#endif + +int InterfaceToolbarReader::pipe_read(char *data, int nbyte) +{ + int total_len = 0; + + while (total_len < nbyte) + { + char *data_ptr = data + total_len; + int data_len = nbyte - total_len; + +#ifdef _WIN32 + int read_len = async_pipe_read(data_ptr, data_len); +#else + int read_len = (int)ws_read(fd_in_, data_ptr, data_len); +#endif + if (read_len == -1) + { + if (errno != EAGAIN) + { + return -1; + } + } + else + { + total_len += read_len; + } + + if (QThread::currentThread()->isInterruptionRequested()) + { + return -1; + } + } + + return total_len; +} + +void InterfaceToolbarReader::loop() +{ + QByteArray header; + QByteArray payload; + +#ifndef _WIN32 + struct timeval timeout; + fd_set readfds; + fd_in_ = ws_open(control_in_.toUtf8(), O_RDONLY | O_BINARY | O_NONBLOCK, 0); + + if (fd_in_ == -1) + { + emit finished(); + return; + } +#endif + + header.resize(header_size); + + forever + { +#ifndef _WIN32 + FD_ZERO(&readfds); + FD_SET(fd_in_, &readfds); + + timeout.tv_sec = 2; + timeout.tv_usec = 0; + + int ret = select(fd_in_ + 1, &readfds, NULL, NULL, &timeout); + if (ret == -1) + { + break; + } + + if (QThread::currentThread()->isInterruptionRequested()) + { + break; + } + + if (ret == 0 || !FD_ISSET(fd_in_, &readfds)) + { + continue; + } +#endif + + // Read the header from the pipe. + if (pipe_read(header.data(), header_size) != header_size) + { + break; + } + + unsigned char high_nibble = header[1] & 0xFF; + unsigned char mid_nibble = header[2] & 0xFF; + unsigned char low_nibble = header[3] & 0xFF; + int payload_len = (int)((high_nibble << 16) + (mid_nibble << 8) + low_nibble) - 2; + + payload.resize(payload_len); + // Read the payload from the pipe. + if (pipe_read(payload.data(), payload_len) != payload_len) + { + break; + } + + if (header[0] == SP_TOOLBAR_CTRL) + { + emit received(ifname_, (unsigned char)header[4], (unsigned char)header[5], payload); + } + } + +#ifndef _WIN32 + ws_close(fd_in_); +#endif + + emit finished(); +} diff --git a/ui/qt/interface_toolbar_reader.h b/ui/qt/interface_toolbar_reader.h new file mode 100644 index 00000000..88c3b0c0 --- /dev/null +++ b/ui/qt/interface_toolbar_reader.h @@ -0,0 +1,63 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INTERFACE_TOOLBAR_READER_H +#define INTERFACE_TOOLBAR_READER_H + +#include +#include + +#ifdef _WIN32 +#include +#endif + +namespace Ui { +class InterfaceToolbarReader; +} + +class InterfaceToolbarReader : public QObject +{ + Q_OBJECT + +public: + InterfaceToolbarReader(QString ifname, void *control_in, QObject *parent = 0) : + QObject(parent), + ifname_(ifname), +#ifdef _WIN32 + control_in_((HANDLE)control_in) +#else + control_in_((char *)control_in), + fd_in_(-1) +#endif + { + } + +public slots: + void loop(); + +signals: + void received(QString ifname, int num, int command, QByteArray payload); + void finished(); + +private: +#ifdef _WIN32 + int async_pipe_read(void *data, int nbyte); +#endif + int pipe_read(char *data, int nbyte); + + QString ifname_; +#ifdef _WIN32 + HANDLE control_in_; +#else + QString control_in_; + int fd_in_; +#endif +}; + +#endif // INTERFACE_TOOLBAR_READER_H diff --git a/ui/qt/io_console_dialog.cpp b/ui/qt/io_console_dialog.cpp new file mode 100644 index 00000000..92d4c542 --- /dev/null +++ b/ui/qt/io_console_dialog.cpp @@ -0,0 +1,139 @@ +/* + * io_console_dialog.c + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#define WS_LOG_DOMAIN LOG_DOMAIN_QTUI +#include "io_console_dialog.h" +#include + +#include "main_application.h" + +extern "C" { +static void print_function(const char *str, void *ptr); +} + +static void print_function(const char *str, void *print_data) +{ + IOConsoleDialog *dialog = static_cast(print_data); + dialog->appendOutputText(QString(str)); +} + +IOConsoleDialog::IOConsoleDialog(QWidget &parent, + QString title, + funnel_console_eval_cb_t eval_cb, + funnel_console_open_cb_t open_cb, + funnel_console_close_cb_t close_cb, + void *callback_data = nullptr) : + GeometryStateDialog(&parent), + ui(new Ui::IOConsoleDialog), + eval_cb_(eval_cb), + open_cb_(open_cb), + close_cb_(close_cb), + callback_data_(callback_data) +{ + ui->setupUi(this); + + if (title.isEmpty()) + title = QString("Console"); + + loadGeometry(0, 0, title); + setWindowTitle(mainApp->windowTitleString(title)); + + QPushButton *eval_button = ui->buttonBox->addButton(tr("Evaluate"), QDialogButtonBox::ActionRole); + eval_button->setDefault(true); + eval_button->setShortcut(QKeySequence("Ctrl+Return")); + connect(eval_button, &QPushButton::clicked, this, &IOConsoleDialog::acceptInput); + + QPushButton *clear_button = ui->buttonBox->addButton(tr("Clear"), QDialogButtonBox::ActionRole); + connect(clear_button, &QPushButton::clicked, this, &IOConsoleDialog::on_clearActivated); + + ui->inputTextEdit->setFont(mainApp->monospaceFont()); + ui->inputTextEdit->setPlaceholderText(QString(tr("Use %1 to evaluate.")) + .arg(eval_button->shortcut().toString(QKeySequence::NativeText))); + + ui->outputTextEdit->setFont(mainApp->monospaceFont()); + ui->outputTextEdit->setReadOnly(true); + + ui->hintLabel->clear(); + + // Install print + open_cb_(print_function, this, callback_data_); +} + +IOConsoleDialog::~IOConsoleDialog() +{ + delete ui; + // Remove print + close_cb_(callback_data_); +} + +void IOConsoleDialog::setHintText(const QString &text) +{ + ui->hintLabel->setText(QString("%1.").arg(text)); +} + +void IOConsoleDialog::clearHintText() +{ + ui->hintLabel->clear(); +} + +void IOConsoleDialog::clearSuccessHint() +{ + // Text changed so we no longer have a success. + ui->hintLabel->clear(); + // Disconnect this slot until the next success. + disconnect(ui->inputTextEdit, &QTextEdit::textChanged, this, &IOConsoleDialog::clearSuccessHint); +} + +void IOConsoleDialog::acceptInput() +{ + clearHintText(); + + QString text = ui->inputTextEdit->toPlainText(); + if (text.isEmpty()) + return; + + char *error_str = nullptr; + char *error_hint = nullptr; + int result = eval_cb_(qUtf8Printable(text), &error_str, &error_hint, callback_data_); + if (result != 0) { + if (error_hint) { + QString hint(error_hint); + setHintText(hint.at(0).toUpper() + hint.mid(1)); + g_free(error_hint); + } + else if (result < 0) { + setHintText("Error loading string"); + } + else { + setHintText("Error running chunk"); + } + if (error_str) { + appendOutputText(QString(error_str)); + g_free(error_str); + } + } + else { + setHintText("Code evaluated successfully"); + connect(ui->inputTextEdit, &QTextEdit::textChanged, this, &IOConsoleDialog::clearSuccessHint); + } +} + +void IOConsoleDialog::appendOutputText(const QString &text) +{ + ui->outputTextEdit->append(text); +} + +void IOConsoleDialog::on_clearActivated() +{ + ui->inputTextEdit->clear(); + ui->outputTextEdit->clear(); + ui->hintLabel->clear(); +} diff --git a/ui/qt/io_console_dialog.h b/ui/qt/io_console_dialog.h new file mode 100644 index 00000000..8a685d36 --- /dev/null +++ b/ui/qt/io_console_dialog.h @@ -0,0 +1,57 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IO_CONSOLE_DIALOG_H +#define IO_CONSOLE_DIALOG_H + +#include + +#include +#include +#include +#include +#include + +#include "geometry_state_dialog.h" +#include + +namespace Ui { +class IOConsoleDialog; +} + +class IOConsoleDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit IOConsoleDialog(QWidget &parent, + QString title, + funnel_console_eval_cb_t eval_cb, + funnel_console_open_cb_t open_cb, + funnel_console_close_cb_t close_cb, + void *callback_data); + ~IOConsoleDialog(); + void appendOutputText(const QString &text); + void setHintText(const QString &text); + void clearHintText(); + +private slots: + void acceptInput(); + void on_clearActivated(void); + void clearSuccessHint(void); + +private: + Ui::IOConsoleDialog *ui; + funnel_console_eval_cb_t eval_cb_; + funnel_console_open_cb_t open_cb_; + funnel_console_close_cb_t close_cb_; + void *callback_data_; +}; + +#endif // IO_CONSOLE_DIALOG_H diff --git a/ui/qt/io_console_dialog.ui b/ui/qt/io_console_dialog.ui new file mode 100644 index 00000000..29688c5a --- /dev/null +++ b/ui/qt/io_console_dialog.ui @@ -0,0 +1,90 @@ + + + IOConsoleDialog + + + + 0 + 0 + 596 + 430 + + + + Dialog + + + + + + Enter code + + + + + + + Qt::Vertical + + + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + IOConsoleDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + IOConsoleDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/io_graph_dialog.cpp b/ui/qt/io_graph_dialog.cpp new file mode 100644 index 00000000..002b98f9 --- /dev/null +++ b/ui/qt/io_graph_dialog.cpp @@ -0,0 +1,2379 @@ +/* io_graph_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "io_graph_dialog.h" +#include + +#include "file.h" + +#include +#include "epan/stats_tree_priv.h" +#include "epan/uat-int.h" + +#include +#include + +#include + +#include + +#include +#include +#include "progress_frame.h" +#include "main_application.h" + +#include +#include + +#include //provides some default colors +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Bugs and uncertainties: +// - Regular (non-stacked) bar graphs are drawn on top of each other on the Z axis. +// The QCP forum suggests drawing them side by side: +// https://www.qcustomplot.com/index.php/support/forum/62 +// - We retap and redraw more than we should. +// - Smoothing doesn't seem to match GTK+ +// - Closing the color picker on macOS sends the dialog to the background. +// - The color picker triggers https://bugreports.qt.io/browse/QTBUG-58699. + +// To do: +// - Use scroll bars? +// - Scroll during live captures +// - Set ticks per pixel (e.g. pressing "2" sets 2 tpp). +// - Explicitly handle missing values, e.g. via NAN. +// - Add a "show missing" or "show zero" option to the UAT? +// It would add yet another graph configuration column. + +const qreal graph_line_width_ = 1.0; + +const int DEFAULT_MOVING_AVERAGE = 0; +const int DEFAULT_Y_AXIS_FACTOR = 1; + +// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph +// in zoom mode. +const int min_zoom_pixels_ = 20; + +const int stat_update_interval_ = 200; // ms + +// Saved graph settings +typedef struct _io_graph_settings_t { + gboolean enabled; + char* name; + char* dfilter; + guint color; + guint32 style; + guint32 yaxis; + char* yfield; + guint32 sma_period; + guint32 y_axis_factor; +} io_graph_settings_t; + +static const value_string graph_style_vs[] = { + { IOGraph::psLine, "Line" }, + { IOGraph::psDotLine, "Dot Line" }, + { IOGraph::psStepLine, "Step Line" }, + { IOGraph::psDotStepLine, "Dot Step Line" }, + { IOGraph::psImpulse, "Impulse" }, + { IOGraph::psBar, "Bar" }, + { IOGraph::psStackedBar, "Stacked Bar" }, + { IOGraph::psDot, "Dot" }, + { IOGraph::psSquare, "Square" }, + { IOGraph::psDiamond, "Diamond" }, + { IOGraph::psCross, "Cross" }, + { IOGraph::psCircle, "Circle" }, + { IOGraph::psPlus, "Plus" }, + { 0, NULL } +}; + +static const value_string y_axis_vs[] = { + { IOG_ITEM_UNIT_PACKETS, "Packets" }, + { IOG_ITEM_UNIT_BYTES, "Bytes" }, + { IOG_ITEM_UNIT_BITS, "Bits" }, + { IOG_ITEM_UNIT_CALC_SUM, "SUM(Y Field)" }, + { IOG_ITEM_UNIT_CALC_FRAMES, "COUNT FRAMES(Y Field)" }, + { IOG_ITEM_UNIT_CALC_FIELDS, "COUNT FIELDS(Y Field)" }, + { IOG_ITEM_UNIT_CALC_MAX, "MAX(Y Field)" }, + { IOG_ITEM_UNIT_CALC_MIN, "MIN(Y Field)" }, + { IOG_ITEM_UNIT_CALC_AVERAGE, "AVG(Y Field)" }, + { IOG_ITEM_UNIT_CALC_LOAD, "LOAD(Y Field)" }, + { 0, NULL } +}; + +static const value_string moving_avg_vs[] = { + { 0, "None" }, + { 10, "10 interval SMA" }, + { 20, "20 interval SMA" }, + { 50, "50 interval SMA" }, + { 100, "100 interval SMA" }, + { 200, "200 interval SMA" }, + { 500, "500 interval SMA" }, + { 1000, "1000 interval SMA" }, + { 0, NULL } +}; + +static io_graph_settings_t *iog_settings_ = NULL; +static guint num_io_graphs_ = 0; +static uat_t *iog_uat_ = NULL; + +// y_axis_factor was added in 3.6. Provide backward compatibility. +static const char *iog_uat_defaults_[] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "1" +}; + +extern "C" { + +//Allow the enable/disable field to be a checkbox, but for backwards compatibility, +//the strings have to be "Enabled"/"Disabled", not "TRUE"/"FALSE" +#define UAT_BOOL_ENABLE_CB_DEF(basename,field_name,rec_t) \ +static void basename ## _ ## field_name ## _set_cb(void* rec, const char* buf, guint len, const void* UNUSED_PARAMETER(u1), const void* UNUSED_PARAMETER(u2)) {\ + char* tmp_str = g_strndup(buf,len); \ + if ((g_strcmp0(tmp_str, "Enabled") == 0) || \ + (g_strcmp0(tmp_str, "TRUE") == 0)) \ + ((rec_t*)rec)->field_name = 1; \ + else \ + ((rec_t*)rec)->field_name = 0; \ + g_free(tmp_str); } \ +static void basename ## _ ## field_name ## _tostr_cb(void* rec, char** out_ptr, unsigned* out_len, const void* UNUSED_PARAMETER(u1), const void* UNUSED_PARAMETER(u2)) {\ + *out_ptr = ws_strdup_printf("%s",((rec_t*)rec)->field_name ? "Enabled" : "Disabled"); \ + *out_len = (unsigned)strlen(*out_ptr); } + +static bool uat_fld_chk_enable(void* u1 _U_, const char* strptr, guint len, const void* u2 _U_, const void* u3 _U_, char** err) +{ + char* str = g_strndup(strptr,len); + + if ((g_strcmp0(str, "Enabled") == 0) || + (g_strcmp0(str, "Disabled") == 0) || + (g_strcmp0(str, "TRUE") == 0) || //just for UAT functionality + (g_strcmp0(str, "FALSE") == 0)) { + *err = NULL; + g_free(str); + return TRUE; + } + + //User should never see this unless they are manually modifying UAT + *err = ws_strdup_printf("invalid value: %s (must be Enabled or Disabled)", str); + g_free(str); + return FALSE; +} + +#define UAT_FLD_BOOL_ENABLE(basename,field_name,title,desc) \ +{#field_name, title, PT_TXTMOD_BOOL,{uat_fld_chk_enable,basename ## _ ## field_name ## _set_cb,basename ## _ ## field_name ## _tostr_cb},{0,0,0},0,desc,FLDFILL} + +//"Custom" handler for sma_period enumeration for backwards compatibility +static void io_graph_sma_period_set_cb(void* rec, const char* buf, guint len, const void* vs, const void* u2 _U_) +{ + guint i; + char* str = g_strndup(buf,len); + const char* cstr; + ((io_graph_settings_t*)rec)->sma_period = 0; + + //Original UAT had just raw numbers and not enumerated values with "interval SMA" + if (strstr(str, "interval SMA") == NULL) { + if (strcmp(str, "None") == 0) { //Valid enumerated value + } else if (strcmp(str, "0") == 0) { + g_free(str); + str = g_strdup("None"); + } else { + char *str2 = ws_strdup_printf("%s interval SMA", str); + g_free(str); + str = str2; + } + } + + for (i=0; (cstr = ((const value_string*)vs)[i].strptr) ;i++) { + if (g_str_equal(cstr,str)) { + ((io_graph_settings_t*)rec)->sma_period = (guint32)((const value_string*)vs)[i].value; + g_free(str); + return; + } + } + g_free(str); +} +//Duplicated because macro covers both functions +static void io_graph_sma_period_tostr_cb(void* rec, char** out_ptr, unsigned* out_len, const void* vs, const void* u2 _U_) +{ + guint i; + for (i=0;((const value_string*)vs)[i].strptr;i++) { + if (((const value_string*)vs)[i].value == ((io_graph_settings_t*)rec)->sma_period) { + *out_ptr = g_strdup(((const value_string*)vs)[i].strptr); + *out_len = (unsigned)strlen(*out_ptr); + return; + } + } + *out_ptr = g_strdup("None"); + *out_len = (unsigned)strlen("None"); +} + +static bool sma_period_chk_enum(void* u1 _U_, const char* strptr, guint len, const void* v, const void* u3 _U_, char** err) { + char *str = g_strndup(strptr,len); + guint i; + const value_string* vs = (const value_string *)v; + + //Original UAT had just raw numbers and not enumerated values with "interval SMA" + if (strstr(str, "interval SMA") == NULL) { + if (strcmp(str, "None") == 0) { //Valid enumerated value + } else if (strcmp(str, "0") == 0) { + g_free(str); + str = g_strdup("None"); + } else { + char *str2 = ws_strdup_printf("%s interval SMA", str); + g_free(str); + str = str2; + } + } + + for (i=0;vs[i].strptr;i++) { + if (g_strcmp0(vs[i].strptr,str) == 0) { + *err = NULL; + g_free(str); + return TRUE; + } + } + + *err = ws_strdup_printf("invalid value: %s",str); + g_free(str); + return FALSE; +} + +#define UAT_FLD_SMA_PERIOD(basename,field_name,title,enum,desc) \ + {#field_name, title, PT_TXTMOD_ENUM,{sma_period_chk_enum,basename ## _ ## field_name ## _set_cb,basename ## _ ## field_name ## _tostr_cb},{&(enum),&(enum),&(enum)},&(enum),desc,FLDFILL} + + +UAT_BOOL_ENABLE_CB_DEF(io_graph, enabled, io_graph_settings_t) +UAT_CSTRING_CB_DEF(io_graph, name, io_graph_settings_t) +UAT_DISPLAY_FILTER_CB_DEF(io_graph, dfilter, io_graph_settings_t) +UAT_COLOR_CB_DEF(io_graph, color, io_graph_settings_t) +UAT_VS_DEF(io_graph, style, io_graph_settings_t, guint32, 0, "Line") +UAT_VS_DEF(io_graph, yaxis, io_graph_settings_t, guint32, 0, "Packets") +UAT_PROTO_FIELD_CB_DEF(io_graph, yfield, io_graph_settings_t) +UAT_DEC_CB_DEF(io_graph, y_axis_factor, io_graph_settings_t) + +static uat_field_t io_graph_fields[] = { + UAT_FLD_BOOL_ENABLE(io_graph, enabled, "Enabled", "Graph visibility"), + UAT_FLD_CSTRING(io_graph, name, "Graph Name", "The name of the graph"), + UAT_FLD_DISPLAY_FILTER(io_graph, dfilter, "Display Filter", "Graph packets matching this display filter"), + UAT_FLD_COLOR(io_graph, color, "Color", "Graph color (#RRGGBB)"), + UAT_FLD_VS(io_graph, style, "Style", graph_style_vs, "Graph style (Line, Bars, etc.)"), + UAT_FLD_VS(io_graph, yaxis, "Y Axis", y_axis_vs, "Y Axis units"), + UAT_FLD_PROTO_FIELD(io_graph, yfield, "Y Field", "Apply calculations to this field"), + UAT_FLD_SMA_PERIOD(io_graph, sma_period, "SMA Period", moving_avg_vs, "Simple moving average period"), + UAT_FLD_DEC(io_graph, y_axis_factor, "Y Axis Factor", "Y Axis Factor"), + + UAT_END_FIELDS +}; + +static void* io_graph_copy_cb(void* dst_ptr, const void* src_ptr, size_t) { + io_graph_settings_t* dst = (io_graph_settings_t *)dst_ptr; + const io_graph_settings_t* src = (const io_graph_settings_t *)src_ptr; + + dst->enabled = src->enabled; + dst->name = g_strdup(src->name); + dst->dfilter = g_strdup(src->dfilter); + dst->color = src->color; + dst->style = src->style; + dst->yaxis = src->yaxis; + dst->yfield = g_strdup(src->yfield); + dst->sma_period = src->sma_period; + dst->y_axis_factor = src->y_axis_factor; + + return dst; +} + +static void io_graph_free_cb(void* p) { + io_graph_settings_t *iogs = (io_graph_settings_t *)p; + g_free(iogs->name); + g_free(iogs->dfilter); + g_free(iogs->yfield); +} + +} // extern "C" + +IOGraphDialog::IOGraphDialog(QWidget &parent, CaptureFile &cf, QString displayFilter) : + WiresharkDialog(parent, cf), + ui(new Ui::IOGraphDialog), + uat_model_(nullptr), + uat_delegate_(nullptr), + base_graph_(nullptr), + tracer_(nullptr), + start_time_(0.0), + mouse_drags_(true), + rubber_band_(nullptr), + stat_timer_(nullptr), + need_replot_(false), + need_retap_(false), + auto_axes_(true), + number_ticker_(new QCPAxisTicker), + datetime_ticker_(new QCPAxisTickerDateTime) +{ + ui->setupUi(this); + ui->hintLabel->setSmallText(); + loadGeometry(); + + setWindowSubtitle(tr("I/O Graphs")); + setAttribute(Qt::WA_DeleteOnClose, true); + QCustomPlot *iop = ui->ioPlot; + + ui->newToolButton->setStockIcon("list-add"); + ui->deleteToolButton->setStockIcon("list-remove"); + ui->copyToolButton->setStockIcon("list-copy"); + ui->clearToolButton->setStockIcon("list-clear"); + ui->moveUpwardsToolButton->setStockIcon("list-move-up"); + ui->moveDownwardsToolButton->setStockIcon("list-move-down"); + +#ifdef Q_OS_MAC + ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->clearToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->moveUpwardsToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->moveDownwardsToolButton->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save); + save_bt->setText(tr("Save As…")); + + QPushButton *copy_bt = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); + connect (copy_bt, SIGNAL(clicked()), this, SLOT(copyAsCsvClicked())); + + CopyFromProfileButton * copy_button = new CopyFromProfileButton(this, "io_graphs", tr("Copy graphs from another profile.")); + ui->buttonBox->addButton(copy_button, QDialogButtonBox::ActionRole); + connect(copy_button, &CopyFromProfileButton::copyProfile, this, &IOGraphDialog::copyFromProfile); + + QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close); + if (close_bt) { + close_bt->setDefault(true); + } + + ui->automaticUpdateCheckBox->setChecked(prefs.gui_io_graph_automatic_update ? true : false); + + ui->enableLegendCheckBox->setChecked(prefs.gui_io_graph_enable_legend ? true : false); + + stat_timer_ = new QTimer(this); + connect(stat_timer_, SIGNAL(timeout()), this, SLOT(updateStatistics())); + stat_timer_->start(stat_update_interval_); + + // Intervals (ms) + ui->intervalComboBox->addItem(tr("1 ms"), 1); + ui->intervalComboBox->addItem(tr("2 ms"), 2); + ui->intervalComboBox->addItem(tr("5 ms"), 5); + ui->intervalComboBox->addItem(tr("10 ms"), 10); + ui->intervalComboBox->addItem(tr("20 ms"), 20); + ui->intervalComboBox->addItem(tr("50 ms"), 50); + ui->intervalComboBox->addItem(tr("100 ms"), 100); + ui->intervalComboBox->addItem(tr("200 ms"), 200); + ui->intervalComboBox->addItem(tr("500 ms"), 500); + ui->intervalComboBox->addItem(tr("1 sec"), 1000); + ui->intervalComboBox->addItem(tr("2 sec"), 2000); + ui->intervalComboBox->addItem(tr("5 sec"), 5000); + ui->intervalComboBox->addItem(tr("10 sec"), 10000); + ui->intervalComboBox->addItem(tr("1 min"), 60000); + ui->intervalComboBox->addItem(tr("10 min"), 600000); + ui->intervalComboBox->setCurrentIndex(9); + + ui->todCheckBox->setChecked(false); + iop->xAxis->setTicker(number_ticker_); + + ui->dragRadioButton->setChecked(mouse_drags_); + + ctx_menu_.addAction(ui->actionZoomIn); + ctx_menu_.addAction(ui->actionZoomInX); + ctx_menu_.addAction(ui->actionZoomInY); + ctx_menu_.addAction(ui->actionZoomOut); + ctx_menu_.addAction(ui->actionZoomOutX); + ctx_menu_.addAction(ui->actionZoomOutY); + ctx_menu_.addAction(ui->actionReset); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionMoveRight10); + ctx_menu_.addAction(ui->actionMoveLeft10); + ctx_menu_.addAction(ui->actionMoveUp10); + ctx_menu_.addAction(ui->actionMoveDown10); + ctx_menu_.addAction(ui->actionMoveRight1); + ctx_menu_.addAction(ui->actionMoveLeft1); + ctx_menu_.addAction(ui->actionMoveUp1); + ctx_menu_.addAction(ui->actionMoveDown1); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionGoToPacket); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionDragZoom); + ctx_menu_.addAction(ui->actionToggleTimeOrigin); + ctx_menu_.addAction(ui->actionCrosshairs); + set_action_shortcuts_visible_in_context_menu(ctx_menu_.actions()); + + iop->xAxis->setLabel(tr("Time (s)")); + + iop->setMouseTracking(true); + iop->setEnabled(true); + + QCPTextElement *title = new QCPTextElement(iop); + iop->plotLayout()->insertRow(0); + iop->plotLayout()->addElement(0, 0, title); + title->setText(tr("Wireshark I/O Graphs: %1").arg(cap_file_.fileDisplayName())); + + tracer_ = new QCPItemTracer(iop); + + loadProfileGraphs(); + bool filterExists = false; + QString graph_name = is_packet_configuration_namespace() ? tr("Filtered packets") : tr("Filtered events"); + if (uat_model_->rowCount() > 0) { + for (int i = 0; i < uat_model_->rowCount(); i++) { + createIOGraph(i); + if (ioGraphs_.at(i)->filter().compare(displayFilter) == 0) + filterExists = true; + } + if (! filterExists && displayFilter.length() > 0) + addGraph(true, graph_name, displayFilter, ColorUtils::graphColor(uat_model_->rowCount()), + IOGraph::psLine, IOG_ITEM_UNIT_PACKETS, QString(), DEFAULT_MOVING_AVERAGE, DEFAULT_Y_AXIS_FACTOR); + } else { + addDefaultGraph(true, 0); + addDefaultGraph(true, 1); + if (displayFilter.length() > 0) + addGraph(true, graph_name, displayFilter, ColorUtils::graphColor(uat_model_->rowCount()), + IOGraph::psLine, IOG_ITEM_UNIT_PACKETS, QString(), DEFAULT_MOVING_AVERAGE, DEFAULT_Y_AXIS_FACTOR); + } + + toggleTracerStyle(true); + iop->setFocus(); + + iop->rescaleAxes(); + + ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0); + + ui->splitter->setStretchFactor(0, 95); + ui->splitter->setStretchFactor(1, 5); + + //XXX - resize columns? + + ProgressFrame::addToButtonBox(ui->buttonBox, &parent); + + connect(iop, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*))); + connect(iop, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + connect(iop, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*))); +} + +IOGraphDialog::~IOGraphDialog() +{ + cap_file_.stopLoading(); + foreach(IOGraph* iog, ioGraphs_) { + delete iog; + } + delete ui; + ui = NULL; +} + +void IOGraphDialog::copyFromProfile(QString filename) +{ + guint orig_data_len = iog_uat_->raw_data->len; + + gchar *err = NULL; + if (uat_load(iog_uat_, filename.toUtf8().constData(), &err)) { + iog_uat_->changed = TRUE; + uat_model_->reloadUat(); + for (guint i = orig_data_len; i < iog_uat_->raw_data->len; i++) { + createIOGraph(i); + } + } else { + report_failure("Error while loading %s: %s", iog_uat_->name, err); + g_free(err); + } +} + +void IOGraphDialog::addGraph(bool checked, QString name, QString dfilter, QRgb color_idx, IOGraph::PlotStyles style, io_graph_item_unit_t value_units, QString yfield, int moving_average, int y_axis_factor) +{ + + QVariantList newRowData; + newRowData.append(checked ? Qt::Checked : Qt::Unchecked); + newRowData.append(name); + newRowData.append(dfilter); + newRowData.append(QColor(color_idx)); + newRowData.append(val_to_str_const(style, graph_style_vs, "None")); + if (is_packet_configuration_namespace()) { + newRowData.append(val_to_str_const(value_units, y_axis_vs, "Packets")); + } else { + newRowData.append(val_to_str_const(value_units, y_axis_vs, "Events")); + } + newRowData.append(yfield); + newRowData.append(val_to_str_const((guint32) moving_average, moving_avg_vs, "None")); + newRowData.append(y_axis_factor); + + QModelIndex newIndex = uat_model_->appendEntry(newRowData); + if ( !newIndex.isValid() ) + { + qDebug() << "Failed to add a new record"; + return; + } + ui->graphUat->setCurrentIndex(newIndex); + createIOGraph(newIndex.row()); +} + +void IOGraphDialog::addGraph(bool copy_from_current) +{ + const QModelIndex ¤t = ui->graphUat->currentIndex(); + if (copy_from_current && !current.isValid()) + return; + + QModelIndex copyIdx; + + if (copy_from_current) { + copyIdx = uat_model_->copyRow(current); + if (!copyIdx.isValid()) + { + qDebug() << "Failed to add a new record"; + return; + } + createIOGraph(copyIdx.row()); + + ui->graphUat->setCurrentIndex(copyIdx); + } else { + addDefaultGraph(false); + copyIdx = uat_model_->index(uat_model_->rowCount() - 1, 0); + } + + ui->graphUat->setCurrentIndex(copyIdx); +} + +void IOGraphDialog::createIOGraph(int currentRow) +{ + // XXX - Should IOGraph have it's own list that has to sync with UAT? + ioGraphs_.append(new IOGraph(ui->ioPlot)); + IOGraph* iog = ioGraphs_[currentRow]; + + connect(this, SIGNAL(recalcGraphData(capture_file *, bool)), iog, SLOT(recalcGraphData(capture_file *, bool))); + connect(this, SIGNAL(reloadValueUnitFields()), iog, SLOT(reloadValueUnitField())); + connect(&cap_file_, SIGNAL(captureEvent(CaptureEvent)), + iog, SLOT(captureEvent(CaptureEvent))); + connect(iog, SIGNAL(requestRetap()), this, SLOT(scheduleRetap())); + connect(iog, SIGNAL(requestRecalc()), this, SLOT(scheduleRecalc())); + connect(iog, SIGNAL(requestReplot()), this, SLOT(scheduleReplot())); + + syncGraphSettings(currentRow); + if (iog->visible()) { + scheduleRetap(); + } +} + +void IOGraphDialog::addDefaultGraph(bool enabled, int idx) +{ + if (is_packet_configuration_namespace()) { + switch (idx % 2) { + case 0: + addGraph(enabled, tr("All Packets"), QString(), ColorUtils::graphColor(idx), + IOGraph::psLine, IOG_ITEM_UNIT_PACKETS, QString(), DEFAULT_MOVING_AVERAGE, DEFAULT_Y_AXIS_FACTOR); + break; + default: + addGraph(enabled, tr("TCP Errors"), "tcp.analysis.flags", ColorUtils::graphColor(4), // 4 = red + IOGraph::psBar, IOG_ITEM_UNIT_PACKETS, QString(), DEFAULT_MOVING_AVERAGE, DEFAULT_Y_AXIS_FACTOR); + break; + } + } else { + switch (idx % 2) { + case 0: + addGraph(enabled, tr("All Events"), QString(), ColorUtils::graphColor(idx), + IOGraph::psLine, IOG_ITEM_UNIT_PACKETS, QString(), DEFAULT_MOVING_AVERAGE, DEFAULT_Y_AXIS_FACTOR); + break; + default: + addGraph(enabled, tr("Access Denied"), "ct.error == \"AccessDenied\"", ColorUtils::graphColor(4), // 4 = red + IOGraph::psDot, IOG_ITEM_UNIT_PACKETS, QString(), DEFAULT_MOVING_AVERAGE, DEFAULT_Y_AXIS_FACTOR); + break; + } + } +} + +// Sync the settings from UAT model to its IOGraph. +// Disables the graph if any errors are found. +// +// NOTE: Setting dfilter, yaxis and yfield here will all end up in setFilter() and this +// has a chicken-and-egg problem because setFilter() depends on previous assigned +// values for filter_, val_units_ and vu_field_. Setting values in wrong order +// may give unpredicted results because setFilter() does not always set filter_ +// on errors. +// TODO: The issues in the above note should be fixed and setFilter() should not be +// called so frequently. + +void IOGraphDialog::syncGraphSettings(int row) +{ + IOGraph *iog = ioGraphs_.value(row, Q_NULLPTR); + + if (!uat_model_->index(row, colEnabled).isValid() || !iog) + return; + + bool visible = graphIsEnabled(row); + bool retap = !iog->visible() && visible; + QString data_str; + + iog->setName(uat_model_->data(uat_model_->index(row, colName)).toString()); + iog->setFilter(uat_model_->data(uat_model_->index(row, colDFilter)).toString()); + + /* plot style depend on the value unit, so set it first. */ + data_str = uat_model_->data(uat_model_->index(row, colYAxis)).toString(); + iog->setValueUnits((int) str_to_val(qUtf8Printable(data_str), y_axis_vs, IOG_ITEM_UNIT_PACKETS)); + iog->setValueUnitField(uat_model_->data(uat_model_->index(row, colYField)).toString()); + + iog->setColor(uat_model_->data(uat_model_->index(row, colColor), Qt::DecorationRole).value().rgb()); + data_str = uat_model_->data(uat_model_->index(row, colStyle)).toString(); + iog->setPlotStyle((int) str_to_val(qUtf8Printable(data_str), graph_style_vs, 0)); + + data_str = uat_model_->data(uat_model_->index(row, colSMAPeriod)).toString(); + iog->moving_avg_period_ = str_to_val(qUtf8Printable(data_str), moving_avg_vs, 0); + + iog->y_axis_factor_ = uat_model_->data(uat_model_->index(row, colYAxisFactor)).toInt(); + + iog->setInterval(ui->intervalComboBox->itemData(ui->intervalComboBox->currentIndex()).toInt()); + + if (!iog->configError().isEmpty()) { + hint_err_ = iog->configError(); + visible = false; + retap = false; + } else { + hint_err_.clear(); + } + + iog->setVisible(visible); + + getGraphInfo(); + mouseMoved(NULL); // Update hint + updateLegend(); + + if (visible) { + if (retap) { + scheduleRetap(); + } else { + scheduleReplot(); + } + } +} + +void IOGraphDialog::updateWidgets() +{ + WiresharkDialog::updateWidgets(); +} + +void IOGraphDialog::scheduleReplot(bool now) +{ + need_replot_ = true; + if (now) updateStatistics(); + // A plot finished, force an update of the legend now in case a time unit + // was involved (which might append "(ms)" to the label). + updateLegend(); +} + +void IOGraphDialog::scheduleRecalc(bool now) +{ + need_recalc_ = true; + if (now) updateStatistics(); +} + +void IOGraphDialog::scheduleRetap(bool now) +{ + need_retap_ = true; + if (now) updateStatistics(); +} + +void IOGraphDialog::reloadFields() +{ + emit reloadValueUnitFields(); +} + +void IOGraphDialog::keyPressEvent(QKeyEvent *event) +{ + int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10; + + switch(event->key()) { + case Qt::Key_Minus: + case Qt::Key_Underscore: // Shifted minus on U.S. keyboards + case Qt::Key_O: // GTK+ + case Qt::Key_R: + zoomAxes(false); + break; + case Qt::Key_Plus: + case Qt::Key_Equal: // Unshifted plus on U.S. keyboards + case Qt::Key_I: // GTK+ + zoomAxes(true); + break; + case Qt::Key_X: // Zoom X axis only + if (event->modifiers() & Qt::ShiftModifier) { + zoomXAxis(false); // upper case X -> Zoom out + } else { + zoomXAxis(true); // lower case x -> Zoom in + } + break; + case Qt::Key_Y: // Zoom Y axis only + if (event->modifiers() & Qt::ShiftModifier) { + zoomYAxis(false); // upper case Y -> Zoom out + } else { + zoomYAxis(true); // lower case y -> Zoom in + } + break; + case Qt::Key_Right: + case Qt::Key_L: + panAxes(pan_pixels, 0); + break; + case Qt::Key_Left: + case Qt::Key_H: + panAxes(-1 * pan_pixels, 0); + break; + case Qt::Key_Up: + case Qt::Key_K: + panAxes(0, pan_pixels); + break; + case Qt::Key_Down: + case Qt::Key_J: + panAxes(0, -1 * pan_pixels); + break; + + case Qt::Key_Space: + toggleTracerStyle(); + break; + + case Qt::Key_0: + case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards + case Qt::Key_Home: + resetAxes(); + break; + + case Qt::Key_G: + on_actionGoToPacket_triggered(); + break; + case Qt::Key_T: + on_actionToggleTimeOrigin_triggered(); + break; + case Qt::Key_Z: + on_actionDragZoom_triggered(); + break; + } + + QDialog::keyPressEvent(event); +} + +void IOGraphDialog::reject() +{ + if (!uat_model_) + return; + + // Changes to the I/O Graphs settings are always saved, + // there is no possibility for "rejection". + QString error; + if (uat_model_->applyChanges(error)) { + if (!error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } + } + + QDialog::reject(); +} + +void IOGraphDialog::zoomAxes(bool in) +{ + QCustomPlot *iop = ui->ioPlot; + double h_factor = iop->axisRect()->rangeZoomFactor(Qt::Horizontal); + double v_factor = iop->axisRect()->rangeZoomFactor(Qt::Vertical); + + auto_axes_ = false; + + if (!in) { + h_factor = pow(h_factor, -1); + v_factor = pow(v_factor, -1); + } + + iop->xAxis->scaleRange(h_factor, iop->xAxis->range().center()); + iop->yAxis->scaleRange(v_factor, iop->yAxis->range().center()); + iop->replot(); +} + +void IOGraphDialog::zoomXAxis(bool in) +{ + QCustomPlot *iop = ui->ioPlot; + double h_factor = iop->axisRect()->rangeZoomFactor(Qt::Horizontal); + + auto_axes_ = false; + + if (!in) { + h_factor = pow(h_factor, -1); + } + + iop->xAxis->scaleRange(h_factor, iop->xAxis->range().center()); + iop->replot(); +} + +void IOGraphDialog::zoomYAxis(bool in) +{ + QCustomPlot *iop = ui->ioPlot; + double v_factor = iop->axisRect()->rangeZoomFactor(Qt::Vertical); + + auto_axes_ = false; + + if (!in) { + v_factor = pow(v_factor, -1); + } + + iop->yAxis->scaleRange(v_factor, iop->yAxis->range().center()); + iop->replot(); +} + +void IOGraphDialog::panAxes(int x_pixels, int y_pixels) +{ + QCustomPlot *iop = ui->ioPlot; + double h_pan = 0.0; + double v_pan = 0.0; + + auto_axes_ = false; + + h_pan = iop->xAxis->range().size() * x_pixels / iop->xAxis->axisRect()->width(); + v_pan = iop->yAxis->range().size() * y_pixels / iop->yAxis->axisRect()->height(); + // The GTK+ version won't pan unless we're zoomed. Should we do the same here? + if (h_pan) { + iop->xAxis->moveRange(h_pan); + iop->replot(); + } + if (v_pan) { + iop->yAxis->moveRange(v_pan); + iop->replot(); + } +} + + +void IOGraphDialog::toggleTracerStyle(bool force_default) +{ + if (!tracer_->visible() && !force_default) return; + if (!ui->ioPlot->graph(0)) return; + + QPen sp_pen = ui->ioPlot->graph(0)->pen(); + QCPItemTracer::TracerStyle tstyle = QCPItemTracer::tsCrosshair; + QPen tr_pen = QPen(tracer_->pen()); + QColor tr_color = sp_pen.color(); + + if (force_default || tracer_->style() != QCPItemTracer::tsCircle) { + tstyle = QCPItemTracer::tsCircle; + tr_color.setAlphaF(1.0); + tr_pen.setWidthF(1.5); + } else { + tr_color.setAlphaF(0.5); + tr_pen.setWidthF(1.0); + } + + tracer_->setStyle(tstyle); + tr_pen.setColor(tr_color); + tracer_->setPen(tr_pen); + ui->ioPlot->replot(); +} + +// Returns the IOGraph which is most likely to be used by the user. This is the +// currently selected, visible graph or the first visible graph otherwise. +IOGraph *IOGraphDialog::currentActiveGraph() const +{ + QModelIndex index = ui->graphUat->currentIndex(); + if (index.isValid()) { + return ioGraphs_.value(index.row(), NULL); + } + + //if no currently selected item, go with first item enabled + for (int row = 0; row < uat_model_->rowCount(); row++) + { + if (graphIsEnabled(row)) { + return ioGraphs_.value(row, NULL); + } + } + + return NULL; +} + +bool IOGraphDialog::graphIsEnabled(int row) const +{ + Qt::CheckState state = static_cast(uat_model_->data(uat_model_->index(row, colEnabled), Qt::CheckStateRole).toInt()); + return state == Qt::Checked; +} + +// Scan through our graphs and gather information. +// QCPItemTracers can only be associated with QCPGraphs. Find the first one +// and associate it with our tracer. Set bar stacking order while we're here. +void IOGraphDialog::getGraphInfo() +{ + base_graph_ = NULL; + QCPBars *prev_bars = NULL; + start_time_ = 0.0; + + tracer_->setGraph(NULL); + IOGraph *selectedGraph = currentActiveGraph(); + + if (uat_model_ != NULL) { + //all graphs may not be created yet, so bounds check the graph array + for (int row = 0; row < uat_model_->rowCount(); row++) { + IOGraph* iog = ioGraphs_.value(row, Q_NULLPTR); + if (iog && graphIsEnabled(row)) { + QCPGraph *graph = iog->graph(); + QCPBars *bars = iog->bars(); + if (graph && (!base_graph_ || iog == selectedGraph)) { + base_graph_ = graph; + } else if (bars && + (uat_model_->data(uat_model_->index(row, colStyle), Qt::DisplayRole).toString().compare(graph_style_vs[IOGraph::psStackedBar].strptr) == 0) && + iog->visible()) { + bars->moveBelow(NULL); // Remove from existing stack + bars->moveBelow(prev_bars); + prev_bars = bars; + } + if (iog->visible() && iog->maxInterval() >= 0) { + double iog_start = iog->startOffset(); + if (start_time_ == 0.0 || iog_start < start_time_) { + start_time_ = iog_start; + } + } + + } + } + } + if (base_graph_ && base_graph_->data()->size() > 0) { + tracer_->setGraph(base_graph_); + tracer_->setVisible(true); + } +} + +void IOGraphDialog::updateLegend() +{ + QCustomPlot *iop = ui->ioPlot; + QSet vu_label_set; + QString intervalText = ui->intervalComboBox->itemText(ui->intervalComboBox->currentIndex()); + + iop->legend->setVisible(false); + iop->yAxis->setLabel(QString()); + + // Find unique labels + if (uat_model_ != NULL) { + for (int row = 0; row < uat_model_->rowCount(); row++) { + IOGraph *iog = ioGraphs_.value(row, Q_NULLPTR); + if (graphIsEnabled(row) && iog) { + QString label(iog->valueUnitLabel()); + if (!iog->scaledValueUnit().isEmpty()) { + label += " (" + iog->scaledValueUnit() + ")"; + } + vu_label_set.insert(label); + } + } + } + + // Nothing. + if (vu_label_set.size() < 1) { + return; + } + + // All the same. Use the Y Axis label. + if (vu_label_set.size() == 1) { + iop->yAxis->setLabel(vu_label_set.values()[0] + "/" + intervalText); + return; + } + + // Differing labels. Create a legend with a Title label at top. + // Legend Title thanks to: https://www.qcustomplot.com/index.php/support/forum/443 + QCPTextElement* legendTitle = qobject_cast(iop->legend->elementAt(0)); + if (legendTitle == NULL) { + legendTitle = new QCPTextElement(iop, QString("")); + iop->legend->insertRow(0); + iop->legend->addElement(0, 0, legendTitle); + } + legendTitle->setText(QString(intervalText + " Intervals ")); + + if (uat_model_ != NULL) { + for (int row = 0; row < uat_model_->rowCount(); row++) { + IOGraph *iog = ioGraphs_.value(row, Q_NULLPTR); + if (iog) { + if (graphIsEnabled(row)) { + iog->addToLegend(); + } else { + iog->removeFromLegend(); + } + } + } + } + + // Only show legend if the user requested it + if (prefs.gui_io_graph_enable_legend) { + iop->legend->setVisible(true); + } + else { + iop->legend->setVisible(false); + } +} + +QRectF IOGraphDialog::getZoomRanges(QRect zoom_rect) +{ + QRectF zoom_ranges = QRectF(); + + if (zoom_rect.width() < min_zoom_pixels_ && zoom_rect.height() < min_zoom_pixels_) { + return zoom_ranges; + } + + QCustomPlot *iop = ui->ioPlot; + QRect zr = zoom_rect.normalized(); + QRect ar = iop->axisRect()->rect(); + if (ar.intersects(zr)) { + QRect zsr = ar.intersected(zr); + zoom_ranges.setX(iop->xAxis->range().lower + + iop->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width()); + zoom_ranges.setWidth(iop->xAxis->range().size() * zsr.width() / ar.width()); + + // QRects grow down + zoom_ranges.setY(iop->yAxis->range().lower + + iop->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height()); + zoom_ranges.setHeight(iop->yAxis->range().size() * zsr.height() / ar.height()); + } + return zoom_ranges; +} + +void IOGraphDialog::graphClicked(QMouseEvent *event) +{ + QCustomPlot *iop = ui->ioPlot; + + if (event->button() == Qt::RightButton) { + // XXX We should find some way to get ioPlot to handle a + // contextMenuEvent instead. +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + ctx_menu_.popup(event->globalPosition().toPoint()); +#else + ctx_menu_.popup(event->globalPos()); +#endif + } else if (mouse_drags_) { + if (iop->axisRect()->rect().contains(event->pos())) { + iop->setCursor(QCursor(Qt::ClosedHandCursor)); + } + on_actionGoToPacket_triggered(); + } else { + if (!rubber_band_) { + rubber_band_ = new QRubberBand(QRubberBand::Rectangle, iop); + } + rb_origin_ = event->pos(); + rubber_band_->setGeometry(QRect(rb_origin_, QSize())); + rubber_band_->show(); + } + iop->setFocus(); +} + +void IOGraphDialog::mouseMoved(QMouseEvent *event) +{ + QCustomPlot *iop = ui->ioPlot; + QString hint; + Qt::CursorShape shape = Qt::ArrowCursor; + + // XXX: ElidedLabel doesn't support rich text / HTML, we + // used to bold this error + if (!hint_err_.isEmpty()) { + hint += QString("%1 ").arg(hint_err_); + } + if (event) { + if (event->buttons().testFlag(Qt::LeftButton)) { + if (mouse_drags_) { + shape = Qt::ClosedHandCursor; + } else { + shape = Qt::CrossCursor; + } + } else if (iop->axisRect()->rect().contains(event->pos())) { + if (mouse_drags_) { + shape = Qt::OpenHandCursor; + } else { + shape = Qt::CrossCursor; + } + } + iop->setCursor(QCursor(shape)); + } + + if (mouse_drags_) { + double ts = 0; + packet_num_ = 0; + int interval_packet = -1; + + if (event && tracer_->graph()) { + tracer_->setGraphKey(iop->xAxis->pixelToCoord(event->pos().x())); + ts = tracer_->position->key(); + if (IOGraph *iog = currentActiveGraph()) { + interval_packet = iog->packetFromTime(ts - start_time_); + } + } + + if (interval_packet < 0) { + hint += tr("Hover over the graph for details."); + } else { + QString msg = is_packet_configuration_namespace() ? tr("No packets in interval") : tr("No events in interval"); + QString val; + if (interval_packet > 0) { + packet_num_ = (guint32) interval_packet; + if (is_packet_configuration_namespace()) { + msg = QString("%1 %2") + .arg(!file_closed_ ? tr("Click to select packet") : tr("Packet")) + .arg(packet_num_); + } else { + msg = QString("%1 %2") + .arg(!file_closed_ ? tr("Click to select event") : tr("Event")) + .arg(packet_num_); + } + val = " = " + QString::number(tracer_->position->value(), 'g', 4); + } + hint += tr("%1 (%2s%3).") + .arg(msg) + .arg(QString::number(ts, 'g', 4)) + .arg(val); + } + iop->replot(); + } else { + if (event && rubber_band_ && rubber_band_->isVisible()) { + rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized()); + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + hint += tr("Release to zoom, x = %1 to %2, y = %3 to %4") + .arg(zoom_ranges.x()) + .arg(zoom_ranges.x() + zoom_ranges.width()) + .arg(zoom_ranges.y()) + .arg(zoom_ranges.y() + zoom_ranges.height()); + } else { + hint += tr("Unable to select range."); + } + } else { + hint += tr("Click to select a portion of the graph."); + } + } + + ui->hintLabel->setText(hint); +} + +void IOGraphDialog::mouseReleased(QMouseEvent *event) +{ + QCustomPlot *iop = ui->ioPlot; + auto_axes_ = false; + if (rubber_band_) { + rubber_band_->hide(); + if (!mouse_drags_) { + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + iop->xAxis->setRangeLower(zoom_ranges.x()); + iop->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width()); + iop->yAxis->setRangeLower(zoom_ranges.y()); + iop->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height()); + iop->replot(); + } + } + } else if (iop->cursor().shape() == Qt::ClosedHandCursor) { + iop->setCursor(QCursor(Qt::OpenHandCursor)); + } +} + +void IOGraphDialog::resetAxes() +{ + QCustomPlot *iop = ui->ioPlot; + QCPRange x_range = iop->xAxis->scaleType() == QCPAxis::stLogarithmic ? + iop->xAxis->range().sanitizedForLogScale() : iop->xAxis->range(); + + double pixel_pad = 10.0; // per side + + iop->rescaleAxes(true); + + double axis_pixels = iop->xAxis->axisRect()->width(); + iop->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, x_range.center()); + + axis_pixels = iop->yAxis->axisRect()->height(); + iop->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, iop->yAxis->range().center()); + + auto_axes_ = true; + iop->replot(); +} + +void IOGraphDialog::updateStatistics() +{ + if (!isVisible()) return; + + if (need_retap_ && !file_closed_ && prefs.gui_io_graph_automatic_update) { + need_retap_ = false; + cap_file_.retapPackets(); + // The user might have closed the window while tapping, which means + // we might no longer exist. + } else { + if (need_recalc_ && !file_closed_ && prefs.gui_io_graph_automatic_update) { + need_recalc_ = false; + need_replot_ = true; + int enabled_graphs = 0; + + if (uat_model_ != NULL) { + for (int row = 0; row < uat_model_->rowCount(); row++) { + if (graphIsEnabled(row)) { + ++enabled_graphs; + } + } + } + // With multiple visible graphs, disable Y scaling to avoid + // multiple, distinct units. + emit recalcGraphData(cap_file_.capFile(), enabled_graphs == 1); + if (!tracer_->graph()) { + if (base_graph_ && base_graph_->data()->size() > 0) { + tracer_->setGraph(base_graph_); + tracer_->setVisible(true); + } else { + tracer_->setVisible(false); + } + } + } + if (need_replot_) { + need_replot_ = false; + if (auto_axes_) { + resetAxes(); + } + ui->ioPlot->replot(); + } + } +} + +void IOGraphDialog::loadProfileGraphs() +{ + if (iog_uat_ == NULL) { + + iog_uat_ = uat_new("I/O Graphs", + sizeof(io_graph_settings_t), + "io_graphs", + TRUE, + &iog_settings_, + &num_io_graphs_, + 0, /* doesn't affect anything that requires a GUI update */ + "ChStatIOGraphs", + io_graph_copy_cb, + NULL, + io_graph_free_cb, + NULL, + NULL, + io_graph_fields); + + uat_set_default_values(iog_uat_, iog_uat_defaults_); + + char* err = NULL; + if (!uat_load(iog_uat_, NULL, &err)) { + report_failure("Error while loading %s: %s. Default graph values will be used", iog_uat_->name, err); + g_free(err); + uat_clear(iog_uat_); + } + } + + uat_model_ = new UatModel(NULL, iog_uat_); + uat_delegate_ = new UatDelegate; + ui->graphUat->setModel(uat_model_); + ui->graphUat->setItemDelegate(uat_delegate_); + + connect(uat_model_, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(modelDataChanged(QModelIndex))); + connect(uat_model_, SIGNAL(modelReset()), this, SLOT(modelRowsReset())); +} + +// Slots + +void IOGraphDialog::on_intervalComboBox_currentIndexChanged(int) +{ + int interval = ui->intervalComboBox->itemData(ui->intervalComboBox->currentIndex()).toInt(); + bool need_retap = false; + + if (uat_model_ != NULL) { + for (int row = 0; row < uat_model_->rowCount(); row++) { + IOGraph *iog = ioGraphs_.value(row, NULL); + if (iog) { + iog->setInterval(interval); + if (iog->visible()) { + need_retap = true; + } + } + } + } + + if (need_retap) { + scheduleRetap(true); + } + + updateLegend(); +} + +void IOGraphDialog::on_todCheckBox_toggled(bool checked) +{ + double orig_start = start_time_; + bool orig_auto = auto_axes_; + + if (checked) { + ui->ioPlot->xAxis->setTicker(datetime_ticker_); + } else { + ui->ioPlot->xAxis->setTicker(number_ticker_); + } + auto_axes_ = false; + scheduleRecalc(true); + auto_axes_ = orig_auto; + getGraphInfo(); + ui->ioPlot->xAxis->moveRange(start_time_ - orig_start); + mouseMoved(NULL); // Update hint +} + +void IOGraphDialog::modelRowsReset() +{ + ui->deleteToolButton->setEnabled(false); + ui->copyToolButton->setEnabled(false); + ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0); +} + +void IOGraphDialog::on_graphUat_currentItemChanged(const QModelIndex ¤t, const QModelIndex&) +{ + if (current.isValid()) { + ui->deleteToolButton->setEnabled(true); + ui->copyToolButton->setEnabled(true); + ui->clearToolButton->setEnabled(true); + ui->moveUpwardsToolButton->setEnabled(true); + ui->moveDownwardsToolButton->setEnabled(true); + } else { + ui->deleteToolButton->setEnabled(false); + ui->copyToolButton->setEnabled(false); + ui->clearToolButton->setEnabled(false); + ui->moveUpwardsToolButton->setEnabled(false); + ui->moveDownwardsToolButton->setEnabled(false); + } +} + +void IOGraphDialog::modelDataChanged(const QModelIndex &index) +{ + bool recalc = false; + + switch (index.column()) + { + case colYAxis: + case colSMAPeriod: + recalc = true; + } + + syncGraphSettings(index.row()); + + if (recalc) { + scheduleRecalc(true); + } else { + scheduleReplot(true); + } +} + +void IOGraphDialog::on_resetButton_clicked() +{ + resetAxes(); +} + +void IOGraphDialog::on_newToolButton_clicked() +{ + addGraph(); +} + +void IOGraphDialog::on_deleteToolButton_clicked() +{ + const QModelIndex ¤t = ui->graphUat->currentIndex(); + if (uat_model_ && current.isValid()) { + delete ioGraphs_[current.row()]; + ioGraphs_.remove(current.row()); + + if (!uat_model_->removeRows(current.row(), 1)) { + qDebug() << "Failed to remove row"; + } + } + + // We should probably be smarter about this. + hint_err_.clear(); + mouseMoved(NULL); +} + +void IOGraphDialog::on_copyToolButton_clicked() +{ + addGraph(true); +} + +void IOGraphDialog::on_clearToolButton_clicked() +{ + if (uat_model_) { + foreach(IOGraph* iog, ioGraphs_) { + delete iog; + } + ioGraphs_.clear(); + uat_model_->clearAll(); + } + + hint_err_.clear(); + mouseMoved(NULL); +} + +void IOGraphDialog::on_moveUpwardsToolButton_clicked() +{ + const QModelIndex& current = ui->graphUat->currentIndex(); + if (uat_model_ && current.isValid()) { + + int current_row = current.row(); + if (current_row > 0){ + // Swap current row with the one above + IOGraph* temp = ioGraphs_[current_row - 1]; + ioGraphs_[current_row - 1] = ioGraphs_[current_row]; + ioGraphs_[current_row] = temp; + + uat_model_->moveRow(current_row, current_row - 1); + } + } +} + +void IOGraphDialog::on_moveDownwardsToolButton_clicked() +{ + const QModelIndex& current = ui->graphUat->currentIndex(); + if (uat_model_ && current.isValid()) { + + int current_row = current.row(); + if (current_row < uat_model_->rowCount() - 1) { + // Swap current row with the one below + IOGraph* temp = ioGraphs_[current_row + 1]; + ioGraphs_[current_row + 1] = ioGraphs_[current_row]; + ioGraphs_[current_row] = temp; + + uat_model_->moveRow(current_row, current_row + 1); + } + } +} + +void IOGraphDialog::on_dragRadioButton_toggled(bool checked) +{ + if (checked) mouse_drags_ = true; + ui->ioPlot->setInteractions( + QCP::iRangeDrag | + QCP::iRangeZoom + ); +} + +void IOGraphDialog::on_zoomRadioButton_toggled(bool checked) +{ + if (checked) mouse_drags_ = false; + ui->ioPlot->setInteractions(QCP::Interactions()); +} + +void IOGraphDialog::on_logCheckBox_toggled(bool checked) +{ + QCustomPlot *iop = ui->ioPlot; + + iop->yAxis->setScaleType(checked ? QCPAxis::stLogarithmic : QCPAxis::stLinear); + iop->replot(); +} + +void IOGraphDialog::on_automaticUpdateCheckBox_toggled(bool checked) +{ + prefs.gui_io_graph_automatic_update = checked ? TRUE : FALSE; + + prefs_main_write(); + + if(prefs.gui_io_graph_automatic_update) + { + updateStatistics(); + } +} + +void IOGraphDialog::on_enableLegendCheckBox_toggled(bool checked) +{ + prefs.gui_io_graph_enable_legend = checked ? TRUE : FALSE; + + prefs_main_write(); + + updateLegend(); +} + +void IOGraphDialog::on_actionReset_triggered() +{ + on_resetButton_clicked(); +} + +void IOGraphDialog::on_actionZoomIn_triggered() +{ + zoomAxes(true); +} + +void IOGraphDialog::on_actionZoomInX_triggered() +{ + zoomXAxis(true); +} + +void IOGraphDialog::on_actionZoomInY_triggered() +{ + zoomYAxis(true); +} + +void IOGraphDialog::on_actionZoomOut_triggered() +{ + zoomAxes(false); +} + +void IOGraphDialog::on_actionZoomOutX_triggered() +{ + zoomXAxis(false); +} + +void IOGraphDialog::on_actionZoomOutY_triggered() +{ + zoomYAxis(false); +} + +void IOGraphDialog::on_actionMoveUp10_triggered() +{ + panAxes(0, 10); +} + +void IOGraphDialog::on_actionMoveLeft10_triggered() +{ + panAxes(-10, 0); +} + +void IOGraphDialog::on_actionMoveRight10_triggered() +{ + panAxes(10, 0); +} + +void IOGraphDialog::on_actionMoveDown10_triggered() +{ + panAxes(0, -10); +} + +void IOGraphDialog::on_actionMoveUp1_triggered() +{ + panAxes(0, 1); +} + +void IOGraphDialog::on_actionMoveLeft1_triggered() +{ + panAxes(-1, 0); +} + +void IOGraphDialog::on_actionMoveRight1_triggered() +{ + panAxes(1, 0); +} + +void IOGraphDialog::on_actionMoveDown1_triggered() +{ + panAxes(0, -1); +} + +void IOGraphDialog::on_actionGoToPacket_triggered() +{ + if (tracer_->visible() && !file_closed_ && packet_num_ > 0) { + emit goToPacket(packet_num_); + } +} + +void IOGraphDialog::on_actionDragZoom_triggered() +{ + if (mouse_drags_) { + ui->zoomRadioButton->toggle(); + } else { + ui->dragRadioButton->toggle(); + } +} + +void IOGraphDialog::on_actionToggleTimeOrigin_triggered() +{ + +} + +void IOGraphDialog::on_actionCrosshairs_triggered() +{ + +} + +void IOGraphDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_STATS_IO_GRAPH_DIALOG); +} + +// XXX - We have similar code in tcp_stream_dialog and packet_diagram. Should this be a common routine? +void IOGraphDialog::on_buttonBox_accepted() +{ + QString file_name, extension; + QDir path(mainApp->openDialogInitialDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + 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)"); + QString csv_filter = tr("Comma Separated Values (*.csv)"); + QString filter = QString("%1;;%2;;%3;;%4;;%5") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter) + .arg(csv_filter); + + QString save_file = path.canonicalPath(); + if (!file_closed_) { + save_file += QString("/%1").arg(cap_file_.fileBaseName()); + } + file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Graph As…")), + save_file, filter, &extension); + + if (file_name.length() > 0) { + bool save_ok = false; + if (extension.compare(pdf_filter) == 0) { + save_ok = ui->ioPlot->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = ui->ioPlot->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = ui->ioPlot->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = ui->ioPlot->saveJpg(file_name); + } else if (extension.compare(csv_filter) == 0) { + save_ok = saveCsv(file_name); + } + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } +} + +void IOGraphDialog::makeCsv(QTextStream &stream) const +{ + QList activeGraphs; + + int ui_interval = ui->intervalComboBox->itemData(ui->intervalComboBox->currentIndex()).toInt(); + int max_interval = 0; + + stream << "\"Interval start\""; + if (uat_model_ != NULL) { + for (int row = 0; row < uat_model_->rowCount(); row++) { + if (graphIsEnabled(row) && ioGraphs_[row] != NULL) { + activeGraphs.append(ioGraphs_[row]); + if (max_interval < ioGraphs_[row]->maxInterval()) { + max_interval = ioGraphs_[row]->maxInterval(); + } + QString name = ioGraphs_[row]->name().toUtf8(); + name = QString("\"%1\"").arg(name.replace("\"", "\"\"")); // RFC 4180 + stream << "," << name; + } + } + } + + stream << '\n'; + + for (int interval = 0; interval <= max_interval; interval++) { + double interval_start = (double)interval * ((double)ui_interval / 1000.0); + stream << interval_start; + foreach (IOGraph *iog, activeGraphs) { + double value = 0.0; + if (interval <= iog->maxInterval()) { + value = iog->getItemValue(interval, cap_file_.capFile()); + } + stream << "," << value; + } + stream << '\n'; + } +} + +void IOGraphDialog::copyAsCsvClicked() +{ + QString csv; + QTextStream stream(&csv, QIODevice::Text); + makeCsv(stream); + mainApp->clipboard()->setText(stream.readAll()); +} + +bool IOGraphDialog::saveCsv(const QString &file_name) const +{ + QFile save_file(file_name); + save_file.open(QFile::WriteOnly | QFile::Text); + QTextStream out(&save_file); + makeCsv(out); + + return true; +} + +// IOGraph + +IOGraph::IOGraph(QCustomPlot *parent) : + parent_(parent), + visible_(false), + graph_(NULL), + bars_(NULL), + val_units_(IOG_ITEM_UNIT_FIRST), + hf_index_(-1), + cur_idx_(-1) +{ + Q_ASSERT(parent_ != NULL); + graph_ = parent_->addGraph(parent_->xAxis, parent_->yAxis); + Q_ASSERT(graph_ != NULL); + + GString *error_string; + error_string = register_tap_listener("frame", + this, + "", + TL_REQUIRES_PROTO_TREE, + tapReset, + tapPacket, + tapDraw, + NULL); + if (error_string) { +// QMessageBox::critical(this, tr("%1 failed to register tap listener").arg(name_), +// error_string->str); +// config_err_ = error_string->str; + g_string_free(error_string, TRUE); + } +} + +IOGraph::~IOGraph() { + remove_tap_listener(this); + if (graph_) { + parent_->removeGraph(graph_); + } + if (bars_) { + parent_->removePlottable(bars_); + } +} + +// Construct a full filter string from the display filter and value unit / Y axis. +// Check for errors and sets config_err_ if any are found. +void IOGraph::setFilter(const QString &filter) +{ + GString *error_string; + QString full_filter(filter.trimmed()); + + config_err_.clear(); + + // Make sure we have a good display filter + if (!full_filter.isEmpty()) { + dfilter_t *dfilter; + bool status; + df_error_t *df_err = NULL; + status = dfilter_compile(full_filter.toUtf8().constData(), &dfilter, &df_err); + dfilter_free(dfilter); + if (!status) { + config_err_ = QString::fromUtf8(df_err->msg); + df_error_free(&df_err); + filter_ = full_filter; + return; + } + } + + // Check our value unit + field combo. + error_string = check_field_unit(vu_field_.toUtf8().constData(), NULL, val_units_); + if (error_string) { + config_err_ = error_string->str; + g_string_free(error_string, TRUE); + return; + } + + // Make sure vu_field_ survives edt tree pruning by adding it to our filter + // expression. + if (val_units_ >= IOG_ITEM_UNIT_CALC_SUM && !vu_field_.isEmpty() && hf_index_ >= 0) { + if (full_filter.isEmpty()) { + full_filter = vu_field_; + } else { + full_filter += QString(" && (%1)").arg(vu_field_); + } + } + + error_string = set_tap_dfilter(this, full_filter.toUtf8().constData()); + if (error_string) { + config_err_ = error_string->str; + g_string_free(error_string, TRUE); + return; + } else { + if (filter_.compare(filter) && visible_) { + emit requestRetap(); + } + filter_ = filter; + } +} + +void IOGraph::applyCurrentColor() +{ + if (graph_) { + graph_->setPen(QPen(color_, graph_line_width_)); + } else if (bars_) { + bars_->setPen(QPen(QBrush(ColorUtils::graphColor(0)), graph_line_width_)); // ...or omit it altogether? + bars_->setBrush(color_); + } +} + +void IOGraph::setVisible(bool visible) +{ + bool old_visibility = visible_; + visible_ = visible; + if (graph_) { + graph_->setVisible(visible_); + } + if (bars_) { + bars_->setVisible(visible_); + } + if (old_visibility != visible_) { + emit requestReplot(); + } +} + +void IOGraph::setName(const QString &name) +{ + name_ = name; + if (graph_) { + graph_->setName(name_); + } + if (bars_) { + bars_->setName(name_); + } +} + +QRgb IOGraph::color() +{ + return color_.color().rgb(); +} + +void IOGraph::setColor(const QRgb color) +{ + color_ = QBrush(color); + applyCurrentColor(); +} + +void IOGraph::setPlotStyle(int style) +{ + // Switch plottable if needed + switch (style) { + case psBar: + case psStackedBar: + if (graph_) { + bars_ = new QCPBars(parent_->xAxis, parent_->yAxis); + parent_->removeGraph(graph_); + graph_ = NULL; + } + break; + default: + if (bars_) { + graph_ = parent_->addGraph(parent_->xAxis, parent_->yAxis); + parent_->removePlottable(bars_); + bars_ = NULL; + } + break; + } + setValueUnits(val_units_); + + if (graph_) { + graph_->setLineStyle(QCPGraph::lsNone); + graph_->setScatterStyle(QCPScatterStyle::ssNone); + } + switch (style) { + case psLine: + if (graph_) { + graph_->setLineStyle(QCPGraph::lsLine); + } + break; + case psDotLine: + if (graph_) { + graph_->setLineStyle(QCPGraph::lsLine); + graph_->setScatterStyle(QCPScatterStyle::ssDisc); + } + break; + case psStepLine: + if (graph_) { + graph_->setLineStyle(QCPGraph::lsStepLeft); + } + break; + case psDotStepLine: + if (graph_) { + graph_->setLineStyle(QCPGraph::lsStepLeft); + graph_->setScatterStyle(QCPScatterStyle::ssDisc); + } + break; + case psImpulse: + if (graph_) { + graph_->setLineStyle(QCPGraph::lsImpulse); + } + break; + case psDot: + if (graph_) { + graph_->setScatterStyle(QCPScatterStyle::ssDisc); + } + break; + case psSquare: + if (graph_) { + graph_->setScatterStyle(QCPScatterStyle::ssSquare); + } + break; + case psDiamond: + if (graph_) { + graph_->setScatterStyle(QCPScatterStyle::ssDiamond); + } + break; + case psCross: + if (graph_) { + graph_->setScatterStyle(QCPScatterStyle::ssCross); + } + break; + case psPlus: + if (graph_) { + graph_->setScatterStyle(QCPScatterStyle::ssPlus); + } + break; + case psCircle: + if (graph_) { + graph_->setScatterStyle(QCPScatterStyle::ssCircle); + } + break; + + case psBar: + case IOGraph::psStackedBar: + // Stacking set in scanGraphs + bars_->moveBelow(NULL); + break; + } + + setName(name_); + applyCurrentColor(); +} + +const QString IOGraph::valueUnitLabel() +{ + return val_to_str_const(val_units_, y_axis_vs, "Unknown"); +} + +void IOGraph::setValueUnits(int val_units) +{ + if (val_units >= IOG_ITEM_UNIT_FIRST && val_units <= IOG_ITEM_UNIT_LAST) { + int old_val_units = val_units_; + val_units_ = (io_graph_item_unit_t)val_units; + + if (old_val_units != val_units) { + setFilter(filter_); // Check config & prime vu field + if (val_units < IOG_ITEM_UNIT_CALC_SUM) { + emit requestRecalc(); + } + } + } +} + +void IOGraph::setValueUnitField(const QString &vu_field) +{ + int old_hf_index = hf_index_; + + vu_field_ = vu_field.trimmed(); + hf_index_ = -1; + + header_field_info *hfi = proto_registrar_get_byname(vu_field_.toUtf8().constData()); + if (hfi) { + hf_index_ = hfi->id; + } + + if (old_hf_index != hf_index_) { + setFilter(filter_); // Check config & prime vu field + } +} + +bool IOGraph::addToLegend() +{ + if (graph_) { + return graph_->addToLegend(); + } + if (bars_) { + return bars_->addToLegend(); + } + return false; +} + +bool IOGraph::removeFromLegend() +{ + if (graph_) { + return graph_->removeFromLegend(); + } + if (bars_) { + return bars_->removeFromLegend(); + } + return false; +} + +double IOGraph::startOffset() +{ + if (graph_ && qSharedPointerDynamicCast(graph_->keyAxis()->ticker()) && graph_->data()->size() > 0) { + return graph_->data()->at(0)->key; + } + if (bars_ && qSharedPointerDynamicCast(bars_->keyAxis()->ticker()) && bars_->data()->size() > 0) { + return bars_->data()->at(0)->key; + } + return 0.0; +} + +int IOGraph::packetFromTime(double ts) +{ + int idx = ts * 1000 / interval_; + if (idx >= 0 && idx < (int) cur_idx_) { + switch (val_units_) { + case IOG_ITEM_UNIT_CALC_MAX: + case IOG_ITEM_UNIT_CALC_MIN: + return items_[idx].extreme_frame_in_invl; + default: + return items_[idx].last_frame_in_invl; + } + } + return -1; +} + +void IOGraph::clearAllData() +{ + cur_idx_ = -1; + reset_io_graph_items(items_, max_io_items_); + if (graph_) { + graph_->data()->clear(); + } + if (bars_) { + bars_->data()->clear(); + } + start_time_ = 0.0; +} + +void IOGraph::recalcGraphData(capture_file *cap_file, bool enable_scaling) +{ + /* Moving average variables */ + unsigned int mavg_in_average_count = 0, mavg_left = 0; + unsigned int mavg_to_remove = 0, mavg_to_add = 0; + double mavg_cumulated = 0; + QCPAxis *x_axis = nullptr; + + if (graph_) { + graph_->data()->clear(); + x_axis = graph_->keyAxis(); + } + if (bars_) { + bars_->data()->clear(); + x_axis = bars_->keyAxis(); + } + + if (moving_avg_period_ > 0 && cur_idx_ >= 0) { + /* "Warm-up phase" - calculate average on some data not displayed; + * just to make sure average on leftmost and rightmost displayed + * values is as reliable as possible + */ + guint64 warmup_interval = 0; + +// for (; warmup_interval < first_interval; warmup_interval += interval_) { +// mavg_cumulated += get_it_value(io, i, (int)warmup_interval/interval_); +// mavg_in_average_count++; +// mavg_left++; +// } + mavg_cumulated += getItemValue((int)warmup_interval/interval_, cap_file); + mavg_in_average_count++; + for (warmup_interval = interval_; + ((warmup_interval < (0 + (moving_avg_period_ / 2) * (guint64)interval_)) && + (warmup_interval <= (cur_idx_ * (guint64)interval_))); + warmup_interval += interval_) { + + mavg_cumulated += getItemValue((int)warmup_interval / interval_, cap_file); + mavg_in_average_count++; + } + mavg_to_add = (unsigned int)warmup_interval; + } + + for (int i = 0; i <= cur_idx_; i++) { + double ts = (double) i * interval_ / 1000; + if (x_axis && qSharedPointerDynamicCast(x_axis->ticker())) { + ts += start_time_; + } + double val = getItemValue(i, cap_file); + + if (moving_avg_period_ > 0) { + if (i != 0) { + mavg_left++; + if (mavg_left > moving_avg_period_ / 2) { + mavg_left--; + mavg_in_average_count--; + mavg_cumulated -= getItemValue((int)mavg_to_remove / interval_, cap_file); + mavg_to_remove += interval_; + } + if (mavg_to_add <= (unsigned int) cur_idx_ * interval_) { + mavg_in_average_count++; + mavg_cumulated += getItemValue((int)mavg_to_add / interval_, cap_file); + mavg_to_add += interval_; + } + } + if (mavg_in_average_count > 0) { + val = mavg_cumulated / mavg_in_average_count; + } + } + + val *= y_axis_factor_; + + if (hasItemToShow(i, val)) + { + if (graph_) { + graph_->addData(ts, val); + } + if (bars_) { + bars_->addData(ts, val); + } + } +// qDebug() << "=rgd i" << i << ts << val; + } + + // attempt to rescale time values to specific units + if (enable_scaling) { + calculateScaledValueUnit(); + } else { + scaled_value_unit_.clear(); + } + + emit requestReplot(); +} + +void IOGraph::calculateScaledValueUnit() +{ + // Reset unit and recalculate if needed. + scaled_value_unit_.clear(); + + // If there is no field, scaling is not possible. + if (hf_index_ < 0) { + return; + } + + switch (val_units_) { + case IOG_ITEM_UNIT_CALC_SUM: + case IOG_ITEM_UNIT_CALC_MAX: + case IOG_ITEM_UNIT_CALC_MIN: + case IOG_ITEM_UNIT_CALC_AVERAGE: + // Unit is not yet known, continue detecting it. + break; + default: + // Unit is Packets, Bytes, Bits, etc. + return; + } + + if (proto_registrar_get_ftype(hf_index_) == FT_RELATIVE_TIME) { + // find maximum absolute value and scale accordingly + double maxValue = 0; + if (graph_) { + maxValue = maxValueFromGraphData(*graph_->data()); + } else if (bars_) { + maxValue = maxValueFromGraphData(*bars_->data()); + } + // If the maximum value is zero, then either we have no data or + // everything is zero, do not scale the unit in this case. + if (maxValue == 0) { + return; + } + + // XXX GTK+ always uses "ms" for log scale, should we do that too? + int value_multiplier; + if (maxValue >= 1.0) { + scaled_value_unit_ = "s"; + value_multiplier = 1; + } else if (maxValue >= 0.001) { + scaled_value_unit_ = "ms"; + value_multiplier = 1000; + } else { + scaled_value_unit_ = "us"; + value_multiplier = 1000000; + } + + if (graph_) { + scaleGraphData(*graph_->data(), value_multiplier); + } else if (bars_) { + scaleGraphData(*bars_->data(), value_multiplier); + } + } +} + +template +double IOGraph::maxValueFromGraphData(const DataMap &map) +{ + double maxValue = 0; + typename DataMap::const_iterator it = map.constBegin(); + while (it != map.constEnd()) { + maxValue = MAX(fabs((*it).value), maxValue); + ++it; + } + return maxValue; +} + +template +void IOGraph::scaleGraphData(DataMap &map, int scalar) +{ + if (scalar != 1) { + typename DataMap::iterator it = map.begin(); + while (it != map.end()) { + (*it).value *= scalar; + ++it; + } + } +} + +void IOGraph::captureEvent(CaptureEvent e) +{ + if ((e.captureContext() == CaptureEvent::File) && + (e.eventType() == CaptureEvent::Closing)) + { + remove_tap_listener(this); + } +} + +void IOGraph::reloadValueUnitField() +{ + if (vu_field_.length() > 0) { + setValueUnitField(vu_field_); + } +} + +// Check if a packet is available at the given interval (idx). +bool IOGraph::hasItemToShow(int idx, double value) const +{ + ws_assert(idx < max_io_items_); + + bool result = false; + + const io_graph_item_t *item = &items_[idx]; + + switch (val_units_) { + case IOG_ITEM_UNIT_PACKETS: + case IOG_ITEM_UNIT_BYTES: + case IOG_ITEM_UNIT_BITS: + case IOG_ITEM_UNIT_CALC_FRAMES: + case IOG_ITEM_UNIT_CALC_FIELDS: + if(value == 0.0 && (graph_ && graph_->scatterStyle().shape() != QCPScatterStyle::ssNone)) { + result = false; + } + else { + result = true; + } + break; + + case IOG_ITEM_UNIT_CALC_SUM: + case IOG_ITEM_UNIT_CALC_MAX: + case IOG_ITEM_UNIT_CALC_MIN: + case IOG_ITEM_UNIT_CALC_AVERAGE: + case IOG_ITEM_UNIT_CALC_LOAD: + if (item->fields) { + result = true; + } + break; + + default: + result = true; + break; + } + + return result; +} + +void IOGraph::setInterval(int interval) +{ + interval_ = interval; +} + +// Get the value at the given interval (idx) for the current value unit. +double IOGraph::getItemValue(int idx, const capture_file *cap_file) const +{ + ws_assert(idx < max_io_items_); + + return get_io_graph_item(items_, val_units_, idx, hf_index_, cap_file, interval_, cur_idx_); +} + +// "tap_reset" callback for register_tap_listener +void IOGraph::tapReset(void *iog_ptr) +{ + IOGraph *iog = static_cast(iog_ptr); + if (!iog) return; + +// qDebug() << "=tapReset" << iog->name_; + iog->clearAllData(); +} + +// "tap_packet" callback for register_tap_listener +tap_packet_status IOGraph::tapPacket(void *iog_ptr, packet_info *pinfo, epan_dissect_t *edt, const void *, tap_flags_t) +{ + IOGraph *iog = static_cast(iog_ptr); + if (!pinfo || !iog) { + return TAP_PACKET_DONT_REDRAW; + } + + int idx = get_io_graph_index(pinfo, iog->interval_); + bool recalc = false; + + /* some sanity checks */ + if ((idx < 0) || (idx >= max_io_items_)) { + iog->cur_idx_ = max_io_items_ - 1; + return TAP_PACKET_DONT_REDRAW; + } + + /* update num_items */ + if (idx > iog->cur_idx_) { + iog->cur_idx_ = (guint32) idx; + recalc = true; + } + + /* set start time */ + if (iog->start_time_ == 0.0) { + nstime_t start_nstime; + nstime_set_zero(&start_nstime); + nstime_delta(&start_nstime, &pinfo->abs_ts, &pinfo->rel_ts); + iog->start_time_ = nstime_to_sec(&start_nstime); + } + + epan_dissect_t *adv_edt = NULL; + /* For ADVANCED mode we need to keep track of some more stuff than just frame and byte counts */ + if (iog->val_units_ >= IOG_ITEM_UNIT_CALC_SUM) { + adv_edt = edt; + } + + if (!update_io_graph_item(iog->items_, idx, pinfo, adv_edt, iog->hf_index_, iog->val_units_, iog->interval_)) { + return TAP_PACKET_DONT_REDRAW; + } + +// qDebug() << "=tapPacket" << iog->name_ << idx << iog->hf_index_ << iog->val_units_ << iog->num_items_; + + if (recalc) { + emit iog->requestRecalc(); + } + return TAP_PACKET_REDRAW; +} + +// "tap_draw" callback for register_tap_listener +void IOGraph::tapDraw(void *iog_ptr) +{ + IOGraph *iog = static_cast(iog_ptr); + if (!iog) return; + emit iog->requestRecalc(); + + if (iog->graph_) { +// qDebug() << "=tapDraw g" << iog->name_ << iog->graph_->data()->keys().size(); + } + if (iog->bars_) { +// qDebug() << "=tapDraw b" << iog->name_ << iog->bars_->data()->keys().size(); + } +} + +// Stat command + args + +static void +io_graph_init(const char *, void*) { + mainApp->emitStatCommandSignal("IOGraph", NULL, NULL); +} + +static stat_tap_ui io_stat_ui = { + REGISTER_STAT_GROUP_GENERIC, + NULL, + "io,stat", + io_graph_init, + 0, + NULL +}; + +extern "C" { + +void register_tap_listener_qt_iostat(void); + +void +register_tap_listener_qt_iostat(void) +{ + register_stat_tap_ui(&io_stat_ui, NULL); +} + +} diff --git a/ui/qt/io_graph_dialog.h b/ui/qt/io_graph_dialog.h new file mode 100644 index 00000000..f0bd4cdf --- /dev/null +++ b/ui/qt/io_graph_dialog.h @@ -0,0 +1,262 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IO_GRAPH_DIALOG_H +#define IO_GRAPH_DIALOG_H + +#include + +#include + +#include "epan/epan_dissect.h" +#include "epan/prefs.h" +#include "ui/preference_utils.h" + +#include "ui/io_graph_item.h" + +#include "wireshark_dialog.h" + +#include +#include + +#include +#include +#include + +class QRubberBand; +class QTimer; + +class QCPBars; +class QCPGraph; +class QCPItemTracer; +class QCustomPlot; +class QCPAxisTicker; +class QCPAxisTickerDateTime; + +// GTK+ sets this to 100000 (NUM_IO_ITEMS) +const int max_io_items_ = 250000; + +// XXX - Move to its own file? +class IOGraph : public QObject { +Q_OBJECT +public: + // COUNT_TYPE_* in gtk/io_graph.c + enum PlotStyles { psLine, psDotLine, psStepLine, psDotStepLine, psImpulse, psBar, psStackedBar, psDot, psSquare, psDiamond, psCross, psPlus, psCircle }; + + explicit IOGraph(QCustomPlot *parent); + ~IOGraph(); + const QString configError() { return config_err_; } + const QString name() { return name_; } + void setName(const QString &name); + const QString filter() { return filter_; } + void setFilter(const QString &filter); + void applyCurrentColor(); + bool visible() { return visible_; } + void setVisible(bool visible); + QRgb color(); + void setColor(const QRgb color); + void setPlotStyle(int style); + const QString valueUnitLabel(); + void setValueUnits(int val_units); + const QString valueUnitField() { return vu_field_; } + void setValueUnitField(const QString &vu_field); + unsigned int movingAveragePeriod() { return moving_avg_period_; } + void setInterval(int interval); + bool addToLegend(); + bool removeFromLegend(); + QCPGraph *graph() { return graph_; } + QCPBars *bars() { return bars_; } + double startOffset(); + int packetFromTime(double ts); + bool hasItemToShow(int idx, double value) const; + double getItemValue(int idx, const capture_file *cap_file) const; + int maxInterval () const { return cur_idx_; } + QString scaledValueUnit() const { return scaled_value_unit_; } + + void clearAllData(); + + unsigned int moving_avg_period_; + unsigned int y_axis_factor_; + +public slots: + void recalcGraphData(capture_file *cap_file, bool enable_scaling); + void captureEvent(CaptureEvent e); + void reloadValueUnitField(); + +signals: + void requestReplot(); + void requestRecalc(); + void requestRetap(); + +private: + // Callbacks for register_tap_listener + static void tapReset(void *iog_ptr); + static tap_packet_status tapPacket(void *iog_ptr, packet_info *pinfo, epan_dissect_t *edt, const void *data, tap_flags_t flags); + static void tapDraw(void *iog_ptr); + + void calculateScaledValueUnit(); + template double maxValueFromGraphData(const DataMap &map); + template void scaleGraphData(DataMap &map, int scalar); + + QCustomPlot *parent_; + QString config_err_; + QString name_; + bool visible_; + QCPGraph *graph_; + QCPBars *bars_; + QString filter_; + QBrush color_; + io_graph_item_unit_t val_units_; + QString vu_field_; + int hf_index_; + int interval_; + double start_time_; + QString scaled_value_unit_; + + // Cached data. We should be able to change the Y axis without retapping as + // much as is feasible. + io_graph_item_t items_[max_io_items_]; + int cur_idx_; +}; + +namespace Ui { +class IOGraphDialog; +} + +class IOGraphDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit IOGraphDialog(QWidget &parent, CaptureFile &cf, QString displayFilter = QString()); + ~IOGraphDialog(); + + enum UatColumns { colEnabled = 0, colName, colDFilter, colColor, colStyle, colYAxis, colYField, colSMAPeriod, colYAxisFactor, colMaxNum}; + + void addGraph(bool checked, QString name, QString dfilter, QRgb color_idx, IOGraph::PlotStyles style, + io_graph_item_unit_t value_units, QString yfield, int moving_average, int yaxisfactor); + void addGraph(bool copy_from_current = false); + void addDefaultGraph(bool enabled, int idx = 0); + void syncGraphSettings(int row); + +public slots: + void scheduleReplot(bool now = false); + void scheduleRecalc(bool now = false); + void scheduleRetap(bool now = false); + void modelRowsReset(); + void reloadFields(); + +protected: + void keyPressEvent(QKeyEvent *event); + void reject(); + +signals: + void goToPacket(int packet_num); + void recalcGraphData(capture_file *cap_file, bool enable_scaling); + void intervalChanged(int interval); + void reloadValueUnitFields(); + +private: + Ui::IOGraphDialog *ui; + + //Model and delegate were chosen over UatFrame because add/remove/copy + //buttons would need realignment (UatFrame has its own) + UatModel *uat_model_; + UatDelegate *uat_delegate_; + + // XXX - This needs to stay synced with UAT index + QVector ioGraphs_; + + QString hint_err_; + QCPGraph *base_graph_; + QCPItemTracer *tracer_; + guint32 packet_num_; + double start_time_; + bool mouse_drags_; + QRubberBand *rubber_band_; + QPoint rb_origin_; + QMenu ctx_menu_; + QTimer *stat_timer_; + bool need_replot_; // Light weight: tell QCP to replot existing data + bool need_recalc_; // Medium weight: recalculate values, then replot + bool need_retap_; // Heavy weight: re-read packet data + bool auto_axes_; + + QSharedPointer number_ticker_; + QSharedPointer datetime_ticker_; + + +// void fillGraph(); + void zoomAxes(bool in); + void zoomXAxis(bool in); + void zoomYAxis(bool in); + void panAxes(int x_pixels, int y_pixels); + void toggleTracerStyle(bool force_default = false); + void getGraphInfo(); + void updateLegend(); + QRectF getZoomRanges(QRect zoom_rect); + void createIOGraph(int currentRow); + void loadProfileGraphs(); + void makeCsv(QTextStream &stream) const; + bool saveCsv(const QString &file_name) const; + IOGraph *currentActiveGraph() const; + bool graphIsEnabled(int row) const; + +private slots: + void copyFromProfile(QString filename); + void updateWidgets(); + void graphClicked(QMouseEvent *event); + void mouseMoved(QMouseEvent *event); + void mouseReleased(QMouseEvent *event); + + void resetAxes(); + void updateStatistics(void); + void copyAsCsvClicked(); + + void on_intervalComboBox_currentIndexChanged(int index); + void on_todCheckBox_toggled(bool checked); + void modelDataChanged(const QModelIndex &index); + void on_graphUat_currentItemChanged(const QModelIndex ¤t, const QModelIndex &previous); + + void on_resetButton_clicked(); + void on_logCheckBox_toggled(bool checked); + void on_automaticUpdateCheckBox_toggled(bool checked); + void on_enableLegendCheckBox_toggled(bool checked); + void on_newToolButton_clicked(); + void on_deleteToolButton_clicked(); + void on_copyToolButton_clicked(); + void on_clearToolButton_clicked(); + void on_moveUpwardsToolButton_clicked(); + void on_moveDownwardsToolButton_clicked(); + void on_dragRadioButton_toggled(bool checked); + void on_zoomRadioButton_toggled(bool checked); + void on_actionReset_triggered(); + void on_actionZoomIn_triggered(); + void on_actionZoomInX_triggered(); + void on_actionZoomInY_triggered(); + void on_actionZoomOut_triggered(); + void on_actionZoomOutX_triggered(); + void on_actionZoomOutY_triggered(); + void on_actionMoveUp10_triggered(); + void on_actionMoveLeft10_triggered(); + void on_actionMoveRight10_triggered(); + void on_actionMoveDown10_triggered(); + void on_actionMoveUp1_triggered(); + void on_actionMoveLeft1_triggered(); + void on_actionMoveRight1_triggered(); + void on_actionMoveDown1_triggered(); + void on_actionGoToPacket_triggered(); + void on_actionDragZoom_triggered(); + void on_actionToggleTimeOrigin_triggered(); + void on_actionCrosshairs_triggered(); + void on_buttonBox_helpRequested(); + void on_buttonBox_accepted(); +}; + +#endif // IO_GRAPH_DIALOG_H diff --git a/ui/qt/io_graph_dialog.ui b/ui/qt/io_graph_dialog.ui new file mode 100644 index 00000000..91f071ee --- /dev/null +++ b/ui/qt/io_graph_dialog.ui @@ -0,0 +1,578 @@ + + + IOGraphDialog + + + + 0 + 0 + 850 + 640 + + + + Dialog + + + + + + Qt::Vertical + + + + + + + + 0 + 90 + + + + + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + Add a new graph. + + + + + + + + + + Remove this graph. + + + + + + + Duplicate this graph. + + + + + + + + + + false + + + Clear all graphs. + + + + + + + false + + + Move this graph upwards. + + + + + + + false + + + Move this graph downwards. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Mouse + + + + + + + Drag using the mouse button. + + + drags + + + true + + + + + + + Select using the mouse button. + + + zooms + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Interval + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Time of day + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Log scale + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Automatic update + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Enable legend + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Reset + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save + + + + + + + Reset Graph + + + Reset the graph to its initial state. + + + 0 + + + + + Zoom In + + + Zoom In + + + + + + + + + Zoom Out + + + Zoom Out + + + - + + + + + Move Up 10 Pixels + + + Move Up 10 Pixels + + + Up + + + + + Move Left 10 Pixels + + + Move Left 10 Pixels + + + Left + + + + + Move Right 10 Pixels + + + Move Right 10 Pixels + + + Right + + + + + Move Down 10 Pixels + + + Move Down 10 Pixels + + + Down + + + + + Move Up 1 Pixel + + + Move Up 1 Pixel + + + Shift+Up + + + + + Move Left 1 Pixel + + + Move Left 1 Pixel + + + Shift+Left + + + + + Move Right 1 Pixel + + + Move Right 1 Pixel + + + Shift+Right + + + + + Move Down 1 Pixel + + + Move down 1 Pixel + + + Shift+Down + + + + + Go To Packet Under Cursor + + + Go to packet currently under the cursor + + + G + + + + + Drag / Zoom + + + Toggle mouse drag / zoom behavior + + + Z + + + + + Capture / Session Time Origin + + + Toggle capture / session time origin + + + T + + + + + Crosshairs + + + Toggle crosshairs + + + Space + + + + + Zoom In X Axis + + + Zoom In X Axis + + + X + + + + + Zoom Out X Axis + + + Zoom Out X Axis + + + Shift+X + + + + + Zoom In Y Axis + + + Zoom In Y Axis + + + Y + + + + + Zoom Out Y Axis + + + Zoom Out Y Axis + + + Shift+Y + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+ + TabnavTreeView + QTreeView +
widgets/tabnav_tree_view.h
+
+ + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+ + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+
+ + + + buttonBox + rejected() + IOGraphDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/layout_preferences_frame.cpp b/ui/qt/layout_preferences_frame.cpp new file mode 100644 index 00000000..07b97c57 --- /dev/null +++ b/ui/qt/layout_preferences_frame.cpp @@ -0,0 +1,396 @@ +/* layout_preferences_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "layout_preferences_frame.h" +#include + +#include +#include +#include + +#include +#include +#include + +LayoutPreferencesFrame::LayoutPreferencesFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::LayoutPreferencesFrame) +{ + ui->setupUi(this); + + pref_layout_type_ = prefFromPrefPtr(&prefs.gui_layout_type); + pref_layout_content_1_ = prefFromPrefPtr(&prefs.gui_layout_content_1); + pref_layout_content_2_ = prefFromPrefPtr(&prefs.gui_layout_content_2); + pref_layout_content_3_ = prefFromPrefPtr(&prefs.gui_layout_content_3); + + QString image_pad_ss = "QToolButton { padding: 0.3em; }"; + ui->layout1ToolButton->setStyleSheet(image_pad_ss); + ui->layout2ToolButton->setStyleSheet(image_pad_ss); + ui->layout3ToolButton->setStyleSheet(image_pad_ss); + ui->layout4ToolButton->setStyleSheet(image_pad_ss); + ui->layout5ToolButton->setStyleSheet(image_pad_ss); + ui->layout6ToolButton->setStyleSheet(image_pad_ss); + + QStyleOption style_opt; + QString indent_ss = QString( + "QCheckBox, QLabel {" + " margin-left: %1px;" + "}" + ).arg(ui->packetListSeparatorCheckBox->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left()); + ui->packetListSeparatorCheckBox->setStyleSheet(indent_ss); + ui->packetListHeaderShowColumnDefinition->setStyleSheet(indent_ss); + ui->packetListHoverStyleCheckbox->setStyleSheet(indent_ss); + ui->packetListAllowSorting->setStyleSheet(indent_ss); + ui->packetListCachedRowsLabel->setStyleSheet(indent_ss); + ui->statusBarShowSelectedPacketCheckBox->setStyleSheet(indent_ss); + ui->statusBarShowFileLoadTimeCheckBox->setStyleSheet(indent_ss); + + pref_packet_list_separator_ = prefFromPrefPtr(&prefs.gui_packet_list_separator); + ui->packetListSeparatorCheckBox->setChecked(prefs_get_bool_value(pref_packet_list_separator_, pref_stashed)); + + pref_packet_header_column_definition_ = prefFromPrefPtr(&prefs.gui_packet_header_column_definition); + ui->packetListHeaderShowColumnDefinition->setChecked(prefs_get_bool_value(pref_packet_header_column_definition_, pref_stashed)); + + pref_packet_list_hover_style_ = prefFromPrefPtr(&prefs.gui_packet_list_hover_style); + ui->packetListHoverStyleCheckbox->setChecked(prefs_get_bool_value(pref_packet_list_hover_style_, pref_stashed)); + + pref_packet_list_sorting_ = prefFromPrefPtr(&prefs.gui_packet_list_sortable); + ui->packetListAllowSorting->setChecked(prefs_get_bool_value(pref_packet_list_sorting_, pref_stashed)); + + pref_packet_list_cached_rows_max_ = prefFromPrefPtr(&prefs.gui_packet_list_cached_rows_max); + + pref_show_selected_packet_ = prefFromPrefPtr(&prefs.gui_show_selected_packet); + ui->statusBarShowSelectedPacketCheckBox->setChecked(prefs_get_bool_value(pref_show_selected_packet_, pref_stashed)); + + pref_show_file_load_time_ = prefFromPrefPtr(&prefs.gui_show_file_load_time); + ui->statusBarShowFileLoadTimeCheckBox->setChecked(prefs_get_bool_value(pref_show_file_load_time_, pref_stashed)); +} + +LayoutPreferencesFrame::~LayoutPreferencesFrame() +{ + delete ui; +} + +void LayoutPreferencesFrame::showEvent(QShowEvent *) +{ + updateWidgets(); +} + +void LayoutPreferencesFrame::updateWidgets() +{ + switch (prefs_get_uint_value_real(pref_layout_type_, pref_stashed)) { + case layout_type_5: + ui->layout5ToolButton->setChecked(true); + break; + case layout_type_2: + ui->layout2ToolButton->setChecked(true); + break; + case layout_type_1: + ui->layout1ToolButton->setChecked(true); + break; + case layout_type_4: + ui->layout4ToolButton->setChecked(true); + break; + case layout_type_3: + ui->layout3ToolButton->setChecked(true); + break; + case layout_type_6: + ui->layout6ToolButton->setChecked(true); + break; + } + + switch (prefs_get_enum_value(pref_layout_content_1_, pref_stashed)) { + case layout_pane_content_plist: + ui->pane1PacketListRadioButton->setChecked(true); + break; + case layout_pane_content_pdetails: + ui->pane1PacketDetailsRadioButton->setChecked(true); + break; + case layout_pane_content_pbytes: + ui->pane1PacketBytesRadioButton->setChecked(true); + break; + case layout_pane_content_pdiagram: + ui->pane1PacketDiagramRadioButton->setChecked(true); + break; + case layout_pane_content_none: + ui->pane1NoneRadioButton->setChecked(true); + break; + } + + switch (prefs_get_enum_value(pref_layout_content_2_, pref_stashed)) { + case layout_pane_content_plist: + ui->pane2PacketListRadioButton->setChecked(true); + break; + case layout_pane_content_pdetails: + ui->pane2PacketDetailsRadioButton->setChecked(true); + break; + case layout_pane_content_pbytes: + ui->pane2PacketBytesRadioButton->setChecked(true); + break; + case layout_pane_content_pdiagram: + ui->pane2PacketDiagramRadioButton->setChecked(true); + break; + case layout_pane_content_none: + ui->pane2NoneRadioButton->setChecked(true); + break; + } + + switch (prefs_get_enum_value(pref_layout_content_3_, pref_stashed)) { + case layout_pane_content_plist: + ui->pane3PacketListRadioButton->setChecked(true); + break; + case layout_pane_content_pdetails: + ui->pane3PacketDetailsRadioButton->setChecked(true); + break; + case layout_pane_content_pbytes: + ui->pane3PacketBytesRadioButton->setChecked(true); + break; + case layout_pane_content_pdiagram: + ui->pane3PacketDiagramRadioButton->setChecked(true); + break; + case layout_pane_content_none: + ui->pane3NoneRadioButton->setChecked(true); + break; + } + + ui->packetListCachedRowsLineEdit->setText(QString::number(prefs_get_uint_value_real(pref_packet_list_cached_rows_max_, pref_stashed))); +} + +void LayoutPreferencesFrame::on_layout5ToolButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_uint_value(pref_layout_type_, layout_type_5, pref_stashed); +} + +void LayoutPreferencesFrame::on_layout2ToolButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_uint_value(pref_layout_type_, layout_type_2, pref_stashed); +} + +void LayoutPreferencesFrame::on_layout1ToolButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_uint_value(pref_layout_type_, layout_type_1, pref_stashed); +} + +void LayoutPreferencesFrame::on_layout4ToolButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_uint_value(pref_layout_type_, layout_type_4, pref_stashed); +} + +void LayoutPreferencesFrame::on_layout3ToolButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_uint_value(pref_layout_type_, layout_type_3, pref_stashed); +} + +void LayoutPreferencesFrame::on_layout6ToolButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_uint_value(pref_layout_type_, layout_type_6, pref_stashed); +} + +void LayoutPreferencesFrame::on_pane1PacketListRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_1_, layout_pane_content_plist, pref_stashed); + if (ui->pane2PacketListRadioButton->isChecked()) + ui->pane2NoneRadioButton->click(); + if (ui->pane3PacketListRadioButton->isChecked()) + ui->pane3NoneRadioButton->click(); +} + +void LayoutPreferencesFrame::on_pane1PacketDetailsRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_1_, layout_pane_content_pdetails, pref_stashed); + if (ui->pane2PacketDetailsRadioButton->isChecked()) + ui->pane2NoneRadioButton->click(); + if (ui->pane3PacketDetailsRadioButton->isChecked()) + ui->pane3NoneRadioButton->click(); +} + +void LayoutPreferencesFrame::on_pane1PacketBytesRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_1_, layout_pane_content_pbytes, pref_stashed); + if (ui->pane2PacketBytesRadioButton->isChecked()) + ui->pane2NoneRadioButton->click(); + if (ui->pane3PacketBytesRadioButton->isChecked()) + 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) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_1_, layout_pane_content_none, pref_stashed); +} + +void LayoutPreferencesFrame::on_pane2PacketListRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_2_, layout_pane_content_plist, pref_stashed); + if (ui->pane1PacketListRadioButton->isChecked()) + ui->pane1NoneRadioButton->click(); + if (ui->pane3PacketListRadioButton->isChecked()) + ui->pane3NoneRadioButton->click(); +} + +void LayoutPreferencesFrame::on_pane2PacketDetailsRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_2_, layout_pane_content_pdetails, pref_stashed); + if (ui->pane1PacketDetailsRadioButton->isChecked()) + ui->pane1NoneRadioButton->click(); + if (ui->pane3PacketDetailsRadioButton->isChecked()) + ui->pane3NoneRadioButton->click(); +} + +void LayoutPreferencesFrame::on_pane2PacketBytesRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_2_, layout_pane_content_pbytes, pref_stashed); + if (ui->pane1PacketBytesRadioButton->isChecked()) + ui->pane1NoneRadioButton->click(); + if (ui->pane3PacketBytesRadioButton->isChecked()) + 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) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_2_, layout_pane_content_none, pref_stashed); +} + +void LayoutPreferencesFrame::on_pane3PacketListRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_3_, layout_pane_content_plist, pref_stashed); + if (ui->pane1PacketListRadioButton->isChecked()) + ui->pane1NoneRadioButton->click(); + if (ui->pane2PacketListRadioButton->isChecked()) + ui->pane2NoneRadioButton->click(); +} + +void LayoutPreferencesFrame::on_pane3PacketDetailsRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_3_, layout_pane_content_pdetails, pref_stashed); + if (ui->pane1PacketDetailsRadioButton->isChecked()) + ui->pane1NoneRadioButton->click(); + if (ui->pane2PacketDetailsRadioButton->isChecked()) + ui->pane2NoneRadioButton->click(); +} + +void LayoutPreferencesFrame::on_pane3PacketBytesRadioButton_toggled(bool checked) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_3_, layout_pane_content_pbytes, pref_stashed); + if (ui->pane1PacketBytesRadioButton->isChecked()) + ui->pane1NoneRadioButton->click(); + if (ui->pane2PacketBytesRadioButton->isChecked()) + 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) +{ + if (!checked) return; + prefs_set_enum_value(pref_layout_content_3_, layout_pane_content_none, pref_stashed); +} + +void LayoutPreferencesFrame::on_restoreButtonBox_clicked(QAbstractButton *) +{ + reset_stashed_pref(pref_layout_type_); + reset_stashed_pref(pref_layout_content_1_); + updateWidgets(); + reset_stashed_pref(pref_layout_content_2_); + updateWidgets(); + reset_stashed_pref(pref_layout_content_3_); + updateWidgets(); + + ui->packetListSeparatorCheckBox->setChecked(prefs_get_bool_value(pref_packet_list_separator_, pref_default)); + ui->packetListHeaderShowColumnDefinition->setChecked(prefs_get_bool_value(pref_packet_header_column_definition_, pref_default)); + ui->packetListHoverStyleCheckbox->setChecked(prefs_get_bool_value(pref_packet_list_hover_style_, pref_default)); + ui->packetListAllowSorting->setChecked(prefs_get_bool_value(pref_packet_list_sorting_, pref_default)); + ui->statusBarShowSelectedPacketCheckBox->setChecked(prefs_get_bool_value(pref_show_selected_packet_, pref_default)); + ui->statusBarShowFileLoadTimeCheckBox->setChecked(prefs_get_bool_value(pref_show_file_load_time_, pref_default)); +} + +void LayoutPreferencesFrame::on_packetListSeparatorCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_packet_list_separator_, (gboolean) checked, pref_stashed); +} + +void LayoutPreferencesFrame::on_packetListHeaderShowColumnDefinition_toggled(bool checked) +{ + prefs_set_bool_value(pref_packet_header_column_definition_, (gboolean) checked, pref_stashed); +} + +void LayoutPreferencesFrame::on_packetListHoverStyleCheckbox_toggled(bool checked) +{ + prefs_set_bool_value(pref_packet_list_hover_style_, (gboolean) checked, pref_stashed); +} + +void LayoutPreferencesFrame::on_packetListAllowSorting_toggled(bool checked) +{ + prefs_set_bool_value(pref_packet_list_sorting_, (gboolean) checked, pref_stashed); +} + +void LayoutPreferencesFrame::on_packetListCachedRowsLineEdit_textEdited(const QString &new_str) +{ + bool ok; + uint new_uint = new_str.toUInt(&ok, 0); + if (ok) { + prefs_set_uint_value(pref_packet_list_cached_rows_max_, new_uint, pref_stashed); + } +} + +void LayoutPreferencesFrame::on_statusBarShowSelectedPacketCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_show_selected_packet_, (gboolean) checked, pref_stashed); +} + +void LayoutPreferencesFrame::on_statusBarShowFileLoadTimeCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_show_file_load_time_, (gboolean) checked, pref_stashed); +} diff --git a/ui/qt/layout_preferences_frame.h b/ui/qt/layout_preferences_frame.h new file mode 100644 index 00000000..6773c5ee --- /dev/null +++ b/ui/qt/layout_preferences_frame.h @@ -0,0 +1,82 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef LAYOUT_PREFERENCES_FRAME_H +#define LAYOUT_PREFERENCES_FRAME_H + +#include + +#include +#include + +namespace Ui { +class LayoutPreferencesFrame; +} + +class LayoutPreferencesFrame : public QFrame +{ + Q_OBJECT + +public: + explicit LayoutPreferencesFrame(QWidget *parent = 0); + ~LayoutPreferencesFrame(); + +protected: + void showEvent(QShowEvent *evt); + +private: + Ui::LayoutPreferencesFrame *ui; + + pref_t *pref_layout_type_; + pref_t *pref_layout_content_1_; + pref_t *pref_layout_content_2_; + pref_t *pref_layout_content_3_; + pref_t *pref_packet_list_separator_; + pref_t *pref_packet_header_column_definition_; + pref_t *pref_packet_list_hover_style_; + pref_t *pref_packet_list_sorting_; + pref_t *pref_packet_list_cached_rows_max_; + pref_t *pref_show_selected_packet_; + pref_t *pref_show_file_load_time_; + + void updateWidgets(); + +private slots: + void on_layout5ToolButton_toggled(bool checked); + void on_layout2ToolButton_toggled(bool checked); + void on_layout1ToolButton_toggled(bool checked); + void on_layout4ToolButton_toggled(bool checked); + void on_layout3ToolButton_toggled(bool checked); + void on_layout6ToolButton_toggled(bool checked); + void on_pane1PacketListRadioButton_toggled(bool checked); + void on_pane1PacketDetailsRadioButton_toggled(bool checked); + void on_pane1PacketBytesRadioButton_toggled(bool checked); + void on_pane1PacketDiagramRadioButton_toggled(bool checked); + void on_pane1NoneRadioButton_toggled(bool checked); + void on_pane2PacketListRadioButton_toggled(bool checked); + void on_pane2PacketDetailsRadioButton_toggled(bool checked); + void on_pane2PacketBytesRadioButton_toggled(bool checked); + void on_pane2PacketDiagramRadioButton_toggled(bool checked); + void on_pane2NoneRadioButton_toggled(bool checked); + void on_pane3PacketListRadioButton_toggled(bool checked); + void on_pane3PacketDetailsRadioButton_toggled(bool checked); + void on_pane3PacketBytesRadioButton_toggled(bool checked); + void on_pane3PacketDiagramRadioButton_toggled(bool checked); + void on_pane3NoneRadioButton_toggled(bool checked); + void on_restoreButtonBox_clicked(QAbstractButton *button); + void on_packetListSeparatorCheckBox_toggled(bool checked); + void on_packetListHeaderShowColumnDefinition_toggled(bool checked); + void on_packetListHoverStyleCheckbox_toggled(bool checked); + void on_packetListAllowSorting_toggled(bool checked); + void on_packetListCachedRowsLineEdit_textEdited(const QString &new_str); + void on_statusBarShowSelectedPacketCheckBox_toggled(bool checked); + void on_statusBarShowFileLoadTimeCheckBox_toggled(bool checked); +}; + +#endif // LAYOUT_PREFERENCES_FRAME_H diff --git a/ui/qt/layout_preferences_frame.ui b/ui/qt/layout_preferences_frame.ui new file mode 100644 index 00000000..70600706 --- /dev/null +++ b/ui/qt/layout_preferences_frame.ui @@ -0,0 +1,509 @@ + + + LayoutPreferencesFrame + + + + 0 + 0 + 414 + 409 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + + + + 0 + 0 + + + + + + + + :/layout/layout_5.png:/layout/layout_5.png + + + + 48 + 48 + + + + true + + + layoutButtonGroup + + + + + + + + + + + :/layout/layout_2.png:/layout/layout_2.png + + + + 48 + 48 + + + + true + + + layoutButtonGroup + + + + + + + + + + + :/layout/layout_1.png:/layout/layout_1.png + + + + 48 + 48 + + + + true + + + layoutButtonGroup + + + + + + + + + + + :/layout/layout_4.png:/layout/layout_4.png + + + + 48 + 48 + + + + true + + + layoutButtonGroup + + + + + + + + + + + :/layout/layout_3.png:/layout/layout_3.png + + + + 48 + 48 + + + + true + + + layoutButtonGroup + + + + + + + + + + + :/layout/layout_6.png:/layout/layout_6.png + + + + 48 + 48 + + + + true + + + layoutButtonGroup + + + + + + + + + + + + + Pane 1: + + + + + + + Packet List + + + pane1ButtonGroup + + + + + + + Packet Details + + + pane1ButtonGroup + + + + + + + Packet Bytes + + + pane1ButtonGroup + + + + + + + Packet Diagram + + + pane1ButtonGroup + + + + + + + None + + + pane1ButtonGroup + + + + + + + + + + + Pane 2: + + + + + + + Packet List + + + pane2ButtonGroup + + + + + + + Packet Details + + + pane2ButtonGroup + + + + + + + Packet Bytes + + + pane2ButtonGroup + + + + + + + Packet Diagram + + + pane2ButtonGroup + + + + + + + None + + + pane2ButtonGroup + + + + + + + + + + + Pane 3: + + + + + + + Packet List + + + pane3ButtonGroup + + + + + + + Packet Details + + + pane3ButtonGroup + + + + + + + Packet Bytes + + + pane3ButtonGroup + + + + + + + Packet Diagram + + + pane3ButtonGroup + + + + + + + None + + + pane3ButtonGroup + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Packet List settings: + + + + + + + Show packet separator + + + + + + + Show column definition in column context menu + + + + + + + Allow the list to be sorted + + + + + + + + + Maximum number of cached rows (affects sorting) + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + + + + Qt:Horizontal + + + + + + + + + Enable mouse-over colorization + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Status Bar settings: + + + + + + + Show selected packet number + + + + + + + Show file load time + + + + + + + Qt::Vertical + + + + 68 + 13 + + + + + + + + QDialogButtonBox::RestoreDefaults + + + + + + + + + + + + + + + + diff --git a/ui/qt/lbm_lbtrm_transport_dialog.cpp b/ui/qt/lbm_lbtrm_transport_dialog.cpp new file mode 100644 index 00000000..9391183e --- /dev/null +++ b/ui/qt/lbm_lbtrm_transport_dialog.cpp @@ -0,0 +1,1610 @@ +/* lbm_lbtrm_transport_dialog.cpp + * + * Copyright (c) 2005-2014 Informatica Corporation. All Rights Reserved. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "lbm_lbtrm_transport_dialog.h" +#include + +#include "file.h" + +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace +{ + static const int Source_AddressTransport_Column = 0; + static const int Source_DataFrames_Column = 1; + static const int Source_DataBytes_Column = 2; + static const int Source_DataFramesBytes_Column = 3; + static const int Source_DataRate_Column = 4; + static const int Source_RXDataFrames_Column = 5; + static const int Source_RXDataBytes_Column = 6; + static const int Source_RXDataFramesBytes_Column = 7; + static const int Source_RXDataRate_Column = 8; + static const int Source_NCFFrames_Column = 9; + static const int Source_NCFCount_Column = 10; + static const int Source_NCFBytes_Column = 11; + static const int Source_NCFFramesBytes_Column = 12; + static const int Source_NCFCountBytes_Column = 13; + static const int Source_NCFFramesCount_Column = 14; + static const int Source_NCFFramesCountBytes_Column = 15; + static const int Source_NCFRate_Column = 16; + static const int Source_SMFrames_Column = 17; + static const int Source_SMBytes_Column = 18; + static const int Source_SMFramesBytes_Column = 19; + static const int Source_SMRate_Column = 20; + + static const int Receiver_AddressTransport_Column = 0; + static const int Receiver_NAKFrames_Column = 1; + static const int Receiver_NAKCount_Column = 2; + static const int Receiver_NAKBytes_Column = 3; + static const int Receiver_NAKRate_Column = 4; + + static const int Detail_SQN_Column = 0; + static const int Detail_Count_Column = 1; + static const int Detail_Frame_Column = 2; + + static const double OneKilobit = 1000.0; + static const double OneMegabit = OneKilobit * OneKilobit; + static const double OneGigabit = OneMegabit * OneKilobit; +} + +static QString format_rate(const nstime_t & elapsed, guint64 bytes) +{ + QString result; + double elapsed_sec; + double rate; + + if (((elapsed.secs == 0) && (elapsed.nsecs == 0)) || (bytes == 0)) + { + return (QString("0")); + } + + elapsed_sec = elapsed.secs + (((double)elapsed.nsecs) / 1000000000.0); + rate = ((double)(bytes * 8)) / elapsed_sec; + + // Currently rate is in bps + if (rate >= OneGigabit) + { + rate /= OneGigabit; + result = QString("%1G").arg(rate, 0, 'f', 2); + } + else if (rate >= OneMegabit) + { + rate /= OneMegabit; + result = QString("%1M").arg(rate, 0, 'f', 2); + } + else if (rate >= OneKilobit) + { + rate /= OneKilobit; + result = QString("%1K").arg(rate, 0, 'f', 2); + } + else + { + result = QString("%1").arg(rate, 0, 'f', 2); + } + return (result); +} + +// Note: +// LBMLBTRMFrameEntry, LBMLBTRMSQNEntry, LBMLBTRMNCFReasonEntry, LBMLBTRMNCFSQNEntry, LBMLBTRMSourceTransportEntry, LBMLBTRMSourceEntry, +// LBMLBTRMReceiverTransportEntry, and LBMLBTRMReceiverEntry are all derived from a QTreeWidgetItem. Each instantiation can exist +// in two places: in a QTreeWidget, and in a containing QMap. +// +// For example: +// - LBMLBTRMTransportDialogInfo contains a QMap of the sources (LBMLBTRMSourceEntry) and receivers (LBMLBTRMReceiverEntry) +// - A source (LBMLBTRMSourceEntry) contains a QMap of the source transports originating from it (LBMLBTRMSourceTransportEntry) +// - A source transport (LBMLBTRMSourceTransportEntry) contains QMaps of data, RX data, and SM SQNs (LBMLBTRMSQNEntry) and NCF SQNs +// (LBMLBTRMNCFSQNEntry) +// - A data SQN (LBMLBTRMSQNEntry) contains a QMap of the frames (LBMLBTRMFrameEntry) in which that SQN appears +// +// Not all of the entries actually appear in a QTreeWidget at one time. For example, in the source details, if no specific source +// transport is selected, nothing is in the source details tree. If Data SQNs is selected, then those details appear in the source +// details tree. Switching to RX Data SQNs removes whatever is currently in the source details tree, and adds the RX details for +// the selected transport. +// +// The actual owner of one of the above QTreeWidgetItem-derived items is the QMap container in its parent. The item is "loaned" to +// the QTreeWidget for display. +// +// All of this is to explain why +// 1) we are frequently adding things to a QTreeWidget +// 2) things are removed (takeTopLevelItem) from a QTreeWidget +// 3) destruction involves removing all items from all QTreeWidgets (rather than letting QTreeWidget delete them) +// 4) the destructor for each item has the form +// +// for (XXXMapIterator it = m_xxx.begin(); it != m_xxx.end(); it++) +// { +// delete *it; +// } +// m_xxx.clear(); +// The for-loop calls the destructor for each item, while the clear() cleans up the space used by the QMap itself. + +// A frame entry +class LBMLBTRMFrameEntry : public QTreeWidgetItem +{ + public: + LBMLBTRMFrameEntry(guint32 frame); + virtual ~LBMLBTRMFrameEntry(void) { } + guint32 getFrame(void) { return (m_frame); } + + private: + guint32 m_frame; +}; + +LBMLBTRMFrameEntry::LBMLBTRMFrameEntry(guint32 frame) : + QTreeWidgetItem(), + m_frame(frame) +{ + setText(Detail_SQN_Column, QString(" ")); + setText(Detail_Count_Column, QString(" ")); + setText(Detail_Frame_Column, QString("%1").arg(m_frame)); +} + +typedef QMap LBMLBTRMFrameMap; +typedef QMap::iterator LBMLBTRMFrameMapIterator; + +// A SQN (SeQuence Number) entry +class LBMLBTRMSQNEntry : public QTreeWidgetItem +{ + public: + LBMLBTRMSQNEntry(guint32 sqn); + virtual ~LBMLBTRMSQNEntry(void); + void processFrame(guint32 frame); + + private: + LBMLBTRMSQNEntry(void); + guint32 m_sqn; + guint32 m_count; + LBMLBTRMFrameMap m_frames; +}; + +LBMLBTRMSQNEntry::LBMLBTRMSQNEntry(guint32 sqn) : + QTreeWidgetItem(), + m_sqn(sqn), + m_count(0), + m_frames() +{ + setText(Detail_SQN_Column, QString("%1").arg(m_sqn)); + setTextAlignment(Detail_SQN_Column, Qt::AlignRight); + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + setText(Detail_Frame_Column, QString(" ")); +} + +LBMLBTRMSQNEntry::~LBMLBTRMSQNEntry(void) +{ + for (LBMLBTRMFrameMapIterator it = m_frames.begin(); it != m_frames.end(); ++it) + { + delete *it; + } + m_frames.clear(); +} + +void LBMLBTRMSQNEntry::processFrame(guint32 frame) +{ + LBMLBTRMFrameMapIterator it; + + it = m_frames.find(frame); + if (m_frames.end() == it) + { + LBMLBTRMFrameEntry * entry = new LBMLBTRMFrameEntry(frame); + m_frames.insert(frame, entry); + addChild(entry); + sortChildren(Detail_Frame_Column, Qt::AscendingOrder); + } + m_count++; + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); +} + +// An NCF (Nak ConFirmation) Reason entry +class LBMLBTRMNCFReasonEntry : public QTreeWidgetItem +{ + public: + LBMLBTRMNCFReasonEntry(guint8 reason); + virtual ~LBMLBTRMNCFReasonEntry(void); + void processFrame(guint32 frame); + + private: + LBMLBTRMNCFReasonEntry(void); + guint8 m_reason; + QString m_reason_string; + guint32 m_count; + LBMLBTRMFrameMap m_frames; +}; + +LBMLBTRMNCFReasonEntry::LBMLBTRMNCFReasonEntry(guint8 reason) : + QTreeWidgetItem(), + m_reason(reason), + m_reason_string(), + m_count(0), + m_frames() +{ + switch (m_reason) + { + case LBTRM_NCF_REASON_NO_RETRY: + m_reason_string = "No Retry"; + break; + case LBTRM_NCF_REASON_IGNORED: + m_reason_string = "Ignored"; + break; + case LBTRM_NCF_REASON_RX_DELAY: + m_reason_string = "Retransmit Delay"; + break; + case LBTRM_NCF_REASON_SHED: + m_reason_string = "Shed"; + break; + default: + m_reason_string = QString("Unknown (%1)").arg(m_reason); + break; + } + setText(Detail_SQN_Column, m_reason_string); + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + setText(Detail_Frame_Column, QString(" ")); +} + +LBMLBTRMNCFReasonEntry::~LBMLBTRMNCFReasonEntry(void) +{ + for (LBMLBTRMFrameMapIterator it = m_frames.begin(); it != m_frames.end(); ++it) + { + delete *it; + } + m_frames.clear(); +} + +void LBMLBTRMNCFReasonEntry::processFrame(guint32 frame) +{ + LBMLBTRMFrameMapIterator it; + + it = m_frames.find(frame); + if (m_frames.end() == it) + { + LBMLBTRMFrameEntry * entry = new LBMLBTRMFrameEntry(frame); + m_frames.insert(frame, entry); + addChild(entry); + sortChildren(Detail_Frame_Column, Qt::AscendingOrder); + } + m_count++; + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRMNCFReasonMap; +typedef QMap::iterator LBMLBTRMNCFReasonMapIterator; + +// An NCF SQN entry +class LBMLBTRMNCFSQNEntry : public QTreeWidgetItem +{ + public: + LBMLBTRMNCFSQNEntry(guint32 sqn); + virtual ~LBMLBTRMNCFSQNEntry(void); + void processFrame(guint8 reason, guint32 frame); + + private: + LBMLBTRMNCFSQNEntry(void); + guint32 m_sqn; + guint32 m_count; + LBMLBTRMNCFReasonMap m_reasons; +}; + +LBMLBTRMNCFSQNEntry::LBMLBTRMNCFSQNEntry(guint32 sqn) : + QTreeWidgetItem(), + m_sqn(sqn), + m_count(0), + m_reasons() +{ + setText(Detail_SQN_Column, QString("%1").arg(m_sqn)); + setTextAlignment(Detail_SQN_Column, Qt::AlignRight); + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + setText(Detail_Frame_Column, QString(" ")); +} + +LBMLBTRMNCFSQNEntry::~LBMLBTRMNCFSQNEntry(void) +{ + for (LBMLBTRMNCFReasonMapIterator it = m_reasons.begin(); it != m_reasons.end(); ++it) + { + delete *it; + } + m_reasons.clear(); +} + +void LBMLBTRMNCFSQNEntry::processFrame(guint8 reason, guint32 frame) +{ + LBMLBTRMNCFReasonMapIterator it; + LBMLBTRMNCFReasonEntry * entry = NULL; + + it = m_reasons.find(reason); + if (m_reasons.end() == it) + { + entry = new LBMLBTRMNCFReasonEntry(reason); + m_reasons.insert(reason, entry); + addChild(entry); + sortChildren(Detail_Frame_Column, Qt::AscendingOrder); + } + else + { + entry = it.value(); + } + m_count++; + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + entry->processFrame(frame); +} + +typedef QMap LBMLBTRMSQNMap; +typedef QMap::iterator LBMLBTRMSQNMapIterator; +typedef QMap LBMLBTRMNCFSQNMap; +typedef QMap::iterator LBMLBTRMNCFSQNMapIterator; + +// A source transport entry +class LBMLBTRMSourceTransportEntry : public QTreeWidgetItem +{ + friend class LBMLBTRMTransportDialog; + + public: + LBMLBTRMSourceTransportEntry(const QString & transport); + virtual ~LBMLBTRMSourceTransportEntry(void); + void processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info); + + protected: + QString m_transport; + + private: + void fillItem(void); + guint64 m_data_frames; + guint64 m_data_bytes; + guint64 m_rx_data_frames; + guint64 m_rx_data_bytes; + guint64 m_ncf_frames; + guint64 m_ncf_count; + guint64 m_ncf_bytes; + guint64 m_sm_frames; + guint64 m_sm_bytes; + nstime_t m_first_frame_timestamp; + bool m_first_frame_timestamp_valid; + nstime_t m_last_frame_timestamp; + + protected: + LBMLBTRMSQNMap m_data_sqns; + LBMLBTRMSQNMap m_rx_data_sqns; + LBMLBTRMNCFSQNMap m_ncf_sqns; + LBMLBTRMSQNMap m_sm_sqns; +}; + +LBMLBTRMSourceTransportEntry::LBMLBTRMSourceTransportEntry(const QString & transport) : + QTreeWidgetItem(), + m_transport(transport), + m_data_frames(0), + m_data_bytes(0), + m_rx_data_frames(0), + m_rx_data_bytes(0), + m_ncf_frames(0), + m_ncf_count(0), + m_ncf_bytes(0), + m_sm_frames(0), + m_sm_bytes(0), + m_first_frame_timestamp_valid(false), + m_data_sqns(), + m_rx_data_sqns(), + m_ncf_sqns(), + m_sm_sqns() +{ + m_first_frame_timestamp.secs = 0; + m_first_frame_timestamp.nsecs = 0; + m_last_frame_timestamp.secs = 0; + m_last_frame_timestamp.nsecs = 0; + setText(Source_AddressTransport_Column, m_transport); +} + +LBMLBTRMSourceTransportEntry::~LBMLBTRMSourceTransportEntry(void) +{ + for (LBMLBTRMSQNMapIterator it = m_data_sqns.begin(); it != m_data_sqns.end(); ++it) + { + delete *it; + } + m_data_sqns.clear(); + + for (LBMLBTRMSQNMapIterator it = m_rx_data_sqns.begin(); it != m_rx_data_sqns.end(); ++it) + { + delete *it; + } + m_rx_data_sqns.clear(); + + for (LBMLBTRMNCFSQNMapIterator it = m_ncf_sqns.begin(); it != m_ncf_sqns.end(); ++it) + { + delete *it; + } + m_ncf_sqns.clear(); + + for (LBMLBTRMSQNMapIterator it = m_sm_sqns.begin(); it != m_sm_sqns.end(); ++it) + { + delete *it; + } + m_sm_sqns.clear(); +} + +void LBMLBTRMSourceTransportEntry::processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info) +{ + if (m_first_frame_timestamp_valid) + { + if (nstime_cmp(&(pinfo->abs_ts), &m_first_frame_timestamp) < 0) + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + } + } + else + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + m_first_frame_timestamp_valid = true; + } + if (nstime_cmp(&(pinfo->abs_ts), &m_last_frame_timestamp) > 0) + { + nstime_copy(&(m_last_frame_timestamp), &(pinfo->abs_ts)); + } + if (tap_info->type == LBTRM_PACKET_TYPE_DATA) + { + LBMLBTRMSQNEntry * sqn = NULL; + LBMLBTRMSQNMapIterator it; + + if (tap_info->retransmission) + { + m_rx_data_frames++; + m_rx_data_bytes += pinfo->fd->pkt_len; + it = m_rx_data_sqns.find(tap_info->sqn); + if (m_rx_data_sqns.end() == it) + { + sqn = new LBMLBTRMSQNEntry(tap_info->sqn); + m_rx_data_sqns.insert(tap_info->sqn, sqn); + } + else + { + sqn = it.value(); + } + } + else + { + m_data_frames++; + m_data_bytes += pinfo->fd->pkt_len; + it = m_data_sqns.find(tap_info->sqn); + if (m_data_sqns.end() == it) + { + sqn = new LBMLBTRMSQNEntry(tap_info->sqn); + m_data_sqns.insert(tap_info->sqn, sqn); + } + else + { + sqn = it.value(); + } + } + sqn->processFrame(pinfo->num); + } + else if (tap_info->type == LBTRM_PACKET_TYPE_NCF) + { + guint16 idx; + LBMLBTRMNCFSQNMapIterator it; + LBMLBTRMNCFSQNEntry * sqn = NULL; + + m_ncf_frames++; + m_ncf_bytes += pinfo->fd->pkt_len; + m_ncf_count += (guint64)tap_info->num_sqns; + for (idx = 0; idx < tap_info->num_sqns; idx++) + { + it = m_ncf_sqns.find(tap_info->sqns[idx]); + if (m_ncf_sqns.end() == it) + { + sqn = new LBMLBTRMNCFSQNEntry(tap_info->sqns[idx]); + m_ncf_sqns.insert(tap_info->sqns[idx], sqn); + } + else + { + sqn = it.value(); + } + sqn->processFrame(tap_info->ncf_reason, pinfo->num); + } + } + else if (tap_info->type == LBTRM_PACKET_TYPE_SM) + { + LBMLBTRMSQNEntry * sqn = NULL; + LBMLBTRMSQNMapIterator it; + + m_sm_frames++; + m_sm_bytes += pinfo->fd->pkt_len; + it = m_sm_sqns.find(tap_info->sqn); + if (m_sm_sqns.end() == it) + { + sqn = new LBMLBTRMSQNEntry(tap_info->sqn); + m_sm_sqns.insert(tap_info->sqn, sqn); + } + else + { + sqn = it.value(); + } + sqn->processFrame(pinfo->num); + } + else + { + return; + } + fillItem(); +} + +void LBMLBTRMSourceTransportEntry::fillItem(void) +{ + nstime_t delta; + + nstime_delta(&delta, &m_last_frame_timestamp, &m_first_frame_timestamp); + setText(Source_DataFrames_Column, QString("%1").arg(m_data_frames)); + setTextAlignment(Source_DataFrames_Column, Qt::AlignRight); + setText(Source_DataBytes_Column, QString("%1").arg(m_data_bytes)); + setTextAlignment(Source_DataBytes_Column, Qt::AlignRight); + setText(Source_DataFramesBytes_Column, QString("%1/%2").arg(m_data_frames).arg(m_data_bytes)); + setTextAlignment(Source_DataFramesBytes_Column, Qt::AlignHCenter); + setText(Source_DataRate_Column, format_rate(delta, m_data_bytes)); + setTextAlignment(Source_DataRate_Column, Qt::AlignRight); + setText(Source_RXDataFrames_Column, QString("%1").arg(m_rx_data_frames)); + setTextAlignment(Source_RXDataFrames_Column, Qt::AlignRight); + setText(Source_RXDataBytes_Column, QString("%1").arg(m_rx_data_bytes)); + setTextAlignment(Source_RXDataBytes_Column, Qt::AlignRight); + setText(Source_RXDataFramesBytes_Column, QString("%1/%2").arg(m_rx_data_frames).arg(m_rx_data_bytes)); + setTextAlignment(Source_RXDataFramesBytes_Column, Qt::AlignHCenter); + setText(Source_RXDataRate_Column, format_rate(delta, m_rx_data_bytes)); + setTextAlignment(Source_RXDataRate_Column, Qt::AlignRight); + setText(Source_NCFFrames_Column, QString("%1").arg(m_ncf_frames)); + setTextAlignment(Source_NCFFrames_Column, Qt::AlignRight); + setText(Source_NCFCount_Column, QString("%1").arg(m_ncf_count)); + setTextAlignment(Source_NCFCount_Column, Qt::AlignRight); + setText(Source_NCFBytes_Column, QString("%1").arg(m_ncf_bytes)); + setTextAlignment(Source_NCFBytes_Column, Qt::AlignRight); + setText(Source_NCFFramesBytes_Column, QString("%1/%2").arg(m_ncf_frames).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFFramesBytes_Column, Qt::AlignHCenter); + setText(Source_NCFCountBytes_Column, QString("%1/%2").arg(m_ncf_count).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFCountBytes_Column, Qt::AlignHCenter); + setText(Source_NCFFramesCount_Column, QString("%1/%2").arg(m_ncf_count).arg(m_ncf_count)); + setTextAlignment(Source_NCFFramesCount_Column, Qt::AlignHCenter); + setText(Source_NCFFramesCountBytes_Column, QString("%1/%2/%3").arg(m_ncf_frames).arg(m_ncf_count).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFFramesCountBytes_Column, Qt::AlignHCenter); + setText(Source_NCFRate_Column, format_rate(delta, m_ncf_bytes)); + setTextAlignment(Source_NCFRate_Column, Qt::AlignRight); + setText(Source_SMFrames_Column, QString("%1").arg(m_sm_frames)); + setTextAlignment(Source_SMFrames_Column, Qt::AlignRight); + setText(Source_SMBytes_Column, QString("%1").arg(m_sm_bytes)); + setTextAlignment(Source_SMBytes_Column, Qt::AlignRight); + setText(Source_SMFramesBytes_Column, QString("%1/%2").arg(m_sm_frames).arg(m_sm_bytes)); + setTextAlignment(Source_SMFramesBytes_Column, Qt::AlignHCenter); + setText(Source_SMRate_Column, format_rate(delta, m_sm_bytes)); + setTextAlignment(Source_SMRate_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRMSourceTransportMap; +typedef QMap::iterator LBMLBTRMSourceTransportMapIterator; + +// A source (address) entry +class LBMLBTRMSourceEntry : public QTreeWidgetItem +{ + public: + LBMLBTRMSourceEntry(const QString & source_address); + virtual ~LBMLBTRMSourceEntry(void); + void processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info); + + private: + void fillItem(void); + QString m_address; + QString m_transport; + guint64 m_data_frames; + guint64 m_data_bytes; + guint64 m_rx_data_frames; + guint64 m_rx_data_bytes; + guint64 m_ncf_frames; + guint64 m_ncf_count; + guint64 m_ncf_bytes; + guint64 m_sm_frames; + guint64 m_sm_bytes; + nstime_t m_first_frame_timestamp; + bool m_first_frame_timestamp_valid; + nstime_t m_last_frame_timestamp; + LBMLBTRMSourceTransportMap m_transports; +}; + +LBMLBTRMSourceEntry::LBMLBTRMSourceEntry(const QString & source_address) : + QTreeWidgetItem(), + m_address(source_address), + m_data_frames(0), + m_data_bytes(0), + m_rx_data_frames(0), + m_rx_data_bytes(0), + m_ncf_frames(0), + m_ncf_count(0), + m_ncf_bytes(0), + m_sm_frames(0), + m_sm_bytes(0), + m_first_frame_timestamp_valid(false), + m_transports() +{ + m_first_frame_timestamp.secs = 0; + m_first_frame_timestamp.nsecs = 0; + m_last_frame_timestamp.secs = 0; + m_last_frame_timestamp.nsecs = 0; + setText(Source_AddressTransport_Column, m_address); +} + +LBMLBTRMSourceEntry::~LBMLBTRMSourceEntry(void) +{ + for (LBMLBTRMSourceTransportMapIterator it = m_transports.begin(); it != m_transports.end(); ++it) + { + delete *it; + } + m_transports.clear(); +} + +void LBMLBTRMSourceEntry::processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info) +{ + LBMLBTRMSourceTransportEntry * transport = NULL; + LBMLBTRMSourceTransportMapIterator it; + + if (m_first_frame_timestamp_valid) + { + if (nstime_cmp(&(pinfo->abs_ts), &m_first_frame_timestamp) < 0) + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + } + } + else + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + m_first_frame_timestamp_valid = true; + } + if (nstime_cmp(&(pinfo->abs_ts), &m_last_frame_timestamp) > 0) + { + nstime_copy(&(m_last_frame_timestamp), &(pinfo->abs_ts)); + } + if (tap_info->type == LBTRM_PACKET_TYPE_DATA) + { + if (tap_info->retransmission) + { + m_rx_data_frames++; + m_rx_data_bytes += pinfo->fd->pkt_len; + } + else + { + m_data_frames++; + m_data_bytes += pinfo->fd->pkt_len; + } + } + else if (tap_info->type == LBTRM_PACKET_TYPE_NCF) + { + m_ncf_frames++; + m_ncf_bytes += pinfo->fd->pkt_len; + m_ncf_count += tap_info->num_sqns; + } + else if (tap_info->type == LBTRM_PACKET_TYPE_SM) + { + m_sm_frames++; + m_sm_bytes += pinfo->fd->pkt_len; + } + + it = m_transports.find(tap_info->transport); + if (m_transports.end() == it) + { + transport = new LBMLBTRMSourceTransportEntry(tap_info->transport); + m_transports.insert(tap_info->transport, transport); + addChild(transport); + sortChildren(Source_AddressTransport_Column, Qt::AscendingOrder); + } + else + { + transport = it.value(); + } + fillItem(); + transport->processPacket(pinfo, tap_info); +} + +void LBMLBTRMSourceEntry::fillItem(void) +{ + nstime_t delta; + + nstime_delta(&delta, &m_last_frame_timestamp, &m_first_frame_timestamp); + setText(Source_DataFrames_Column, QString("%1").arg(m_data_frames)); + setTextAlignment(Source_DataFrames_Column, Qt::AlignRight); + setText(Source_DataBytes_Column, QString("%1").arg(m_data_bytes)); + setTextAlignment(Source_DataBytes_Column, Qt::AlignRight); + setText(Source_DataFramesBytes_Column, QString("%1/%2").arg(m_data_frames).arg(m_data_bytes)); + setTextAlignment(Source_DataFramesBytes_Column, Qt::AlignHCenter); + setText(Source_DataRate_Column, format_rate(delta, m_data_bytes)); + setTextAlignment(Source_DataRate_Column, Qt::AlignRight); + setText(Source_RXDataFrames_Column, QString("%1").arg(m_rx_data_frames)); + setTextAlignment(Source_RXDataFrames_Column, Qt::AlignRight); + setText(Source_RXDataBytes_Column, QString("%1").arg(m_rx_data_bytes)); + setTextAlignment(Source_RXDataBytes_Column, Qt::AlignRight); + setText(Source_RXDataFramesBytes_Column, QString("%1/%2").arg(m_rx_data_frames).arg(m_rx_data_bytes)); + setTextAlignment(Source_RXDataFramesBytes_Column, Qt::AlignHCenter); + setText(Source_RXDataRate_Column, format_rate(delta, m_rx_data_bytes)); + setTextAlignment(Source_RXDataRate_Column, Qt::AlignRight); + setText(Source_NCFFrames_Column, QString("%1").arg(m_ncf_frames)); + setTextAlignment(Source_NCFFrames_Column, Qt::AlignRight); + setText(Source_NCFCount_Column, QString("%1").arg(m_ncf_count)); + setTextAlignment(Source_NCFCount_Column, Qt::AlignRight); + setText(Source_NCFBytes_Column, QString("%1").arg(m_ncf_bytes)); + setTextAlignment(Source_NCFBytes_Column, Qt::AlignRight); + setText(Source_NCFFramesBytes_Column, QString("%1/%2").arg(m_ncf_frames).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFFramesBytes_Column, Qt::AlignHCenter); + setText(Source_NCFCountBytes_Column, QString("%1/%2").arg(m_ncf_count).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFCountBytes_Column, Qt::AlignHCenter); + setText(Source_NCFFramesCount_Column, QString("%1/%2").arg(m_ncf_frames).arg(m_ncf_count)); + setTextAlignment(Source_NCFFramesCount_Column, Qt::AlignHCenter); + setText(Source_NCFFramesCountBytes_Column, QString("%1/%2/%3").arg(m_ncf_frames).arg(m_ncf_count).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFFramesCountBytes_Column, Qt::AlignHCenter); + setText(Source_NCFRate_Column, format_rate(delta, m_ncf_bytes)); + setTextAlignment(Source_NCFRate_Column, Qt::AlignRight); + setText(Source_SMFrames_Column, QString("%1").arg(m_sm_frames)); + setTextAlignment(Source_SMFrames_Column, Qt::AlignRight); + setText(Source_SMBytes_Column, QString("%1").arg(m_sm_bytes)); + setTextAlignment(Source_SMBytes_Column, Qt::AlignRight); + setText(Source_SMFramesBytes_Column, QString("%1/%2").arg(m_sm_frames).arg(m_sm_bytes)); + setTextAlignment(Source_SMFramesBytes_Column, Qt::AlignRight); + setText(Source_SMRate_Column, format_rate(delta, m_sm_bytes)); + setTextAlignment(Source_SMRate_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRMSourceMap; +typedef QMap::iterator LBMLBTRMSourceMapIterator; + +// A receiver transport entry +class LBMLBTRMReceiverTransportEntry : public QTreeWidgetItem +{ + friend class LBMLBTRMTransportDialog; + + public: + LBMLBTRMReceiverTransportEntry(const QString & transport); + virtual ~LBMLBTRMReceiverTransportEntry(void); + void processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info); + + private: + void fillItem(void); + QString m_transport; + guint64 m_nak_frames; + guint64 m_nak_count; + guint64 m_nak_bytes; + nstime_t m_first_frame_timestamp; + bool m_first_frame_timestamp_valid; + nstime_t m_last_frame_timestamp; + + protected: + LBMLBTRMSQNMap m_nak_sqns; +}; + +LBMLBTRMReceiverTransportEntry::LBMLBTRMReceiverTransportEntry(const QString & transport) : + QTreeWidgetItem(), + m_transport(transport), + m_nak_frames(0), + m_nak_count(0), + m_nak_bytes(0), + m_first_frame_timestamp_valid(false), + m_nak_sqns() +{ + m_first_frame_timestamp.secs = 0; + m_first_frame_timestamp.nsecs = 0; + m_last_frame_timestamp.secs = 0; + m_last_frame_timestamp.nsecs = 0; + setText(Receiver_AddressTransport_Column, m_transport); +} + +LBMLBTRMReceiverTransportEntry::~LBMLBTRMReceiverTransportEntry(void) +{ + for (LBMLBTRMSQNMapIterator it = m_nak_sqns.begin(); it != m_nak_sqns.end(); ++it) + { + delete *it; + } + m_nak_sqns.clear(); +} + +void LBMLBTRMReceiverTransportEntry::processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info) +{ + if (m_first_frame_timestamp_valid) + { + if (nstime_cmp(&(pinfo->abs_ts), &m_first_frame_timestamp) < 0) + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + } + } + else + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + m_first_frame_timestamp_valid = true; + } + if (nstime_cmp(&(pinfo->abs_ts), &m_last_frame_timestamp) > 0) + { + nstime_copy(&(m_last_frame_timestamp), &(pinfo->abs_ts)); + } + if (tap_info->type == LBTRM_PACKET_TYPE_NAK) + { + guint16 idx; + LBMLBTRMSQNEntry * sqn = NULL; + LBMLBTRMSQNMapIterator it; + + m_nak_frames++; + m_nak_bytes += pinfo->fd->pkt_len; + m_nak_count += tap_info->num_sqns; + for (idx = 0; idx < tap_info->num_sqns; idx++) + { + it = m_nak_sqns.find(tap_info->sqns[idx]); + if (m_nak_sqns.end() == it) + { + sqn = new LBMLBTRMSQNEntry(tap_info->sqns[idx]); + m_nak_sqns.insert(tap_info->sqns[idx], sqn); + } + else + { + sqn = it.value(); + } + sqn->processFrame(pinfo->num); + } + } + else + { + return; + } + fillItem(); +} + +void LBMLBTRMReceiverTransportEntry::fillItem(void) +{ + nstime_t delta; + + nstime_delta(&delta, &m_last_frame_timestamp, &m_first_frame_timestamp); + setText(Receiver_NAKFrames_Column, QString("%1").arg(m_nak_frames)); + setTextAlignment(Receiver_NAKFrames_Column, Qt::AlignRight); + setText(Receiver_NAKCount_Column, QString("%1").arg(m_nak_count)); + setTextAlignment(Receiver_NAKCount_Column, Qt::AlignRight); + setText(Receiver_NAKBytes_Column, QString("%1").arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKBytes_Column, Qt::AlignRight); + setText(Receiver_NAKRate_Column, format_rate(delta, m_nak_bytes)); + setTextAlignment(Receiver_NAKRate_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRMReceiverTransportMap; +typedef QMap::iterator LBMLBTRMReceiverTransportMapIterator; + +// A receiver (address) entry +class LBMLBTRMReceiverEntry : public QTreeWidgetItem +{ + public: + LBMLBTRMReceiverEntry(const QString & receiver_address); + virtual ~LBMLBTRMReceiverEntry(void); + void processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info); + + private: + LBMLBTRMReceiverEntry(void); + void fillItem(void); + QString m_address; + QString m_transport; + guint64 m_nak_frames; + guint64 m_nak_count; + guint64 m_nak_bytes; + nstime_t m_first_frame_timestamp; + bool m_first_frame_timestamp_valid; + nstime_t m_last_frame_timestamp; + LBMLBTRMReceiverTransportMap m_transports; +}; + +LBMLBTRMReceiverEntry::LBMLBTRMReceiverEntry(const QString & receiver_address) : + QTreeWidgetItem(), + m_address(receiver_address), + m_nak_frames(0), + m_nak_count(0), + m_nak_bytes(0), + m_first_frame_timestamp_valid(false), + m_transports() +{ + m_first_frame_timestamp.secs = 0; + m_first_frame_timestamp.nsecs = 0; + m_last_frame_timestamp.secs = 0; + m_last_frame_timestamp.nsecs = 0; + setText(Receiver_AddressTransport_Column, m_address); +} + +LBMLBTRMReceiverEntry::~LBMLBTRMReceiverEntry(void) +{ + for (LBMLBTRMReceiverTransportMapIterator it = m_transports.begin(); it != m_transports.end(); ++it) + { + delete *it; + } + m_transports.clear(); +} + +void LBMLBTRMReceiverEntry::processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info) +{ + LBMLBTRMReceiverTransportEntry * transport = NULL; + LBMLBTRMReceiverTransportMapIterator it; + + if (m_first_frame_timestamp_valid) + { + if (nstime_cmp(&(pinfo->abs_ts), &m_first_frame_timestamp) < 0) + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + } + } + else + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + m_first_frame_timestamp_valid = true; + } + if (nstime_cmp(&(pinfo->abs_ts), &m_last_frame_timestamp) > 0) + { + nstime_copy(&(m_last_frame_timestamp), &(pinfo->abs_ts)); + } + if (tap_info->type == LBTRM_PACKET_TYPE_NAK) + { + m_nak_frames++; + m_nak_bytes += pinfo->fd->pkt_len; + m_nak_count += tap_info->num_sqns; + } + + it = m_transports.find(tap_info->transport); + if (m_transports.end() == it) + { + transport = new LBMLBTRMReceiverTransportEntry(tap_info->transport); + m_transports.insert(tap_info->transport, transport); + addChild(transport); + sortChildren(Receiver_AddressTransport_Column, Qt::AscendingOrder); + } + else + { + transport = it.value(); + } + fillItem(); + transport->processPacket(pinfo, tap_info); +} + +void LBMLBTRMReceiverEntry::fillItem(void) +{ + nstime_t delta; + + nstime_delta(&delta, &m_last_frame_timestamp, &m_first_frame_timestamp); + setText(Receiver_NAKFrames_Column, QString("%1").arg(m_nak_frames)); + setTextAlignment(Receiver_NAKFrames_Column, Qt::AlignRight); + setText(Receiver_NAKCount_Column, QString("%1").arg(m_nak_count)); + setTextAlignment(Receiver_NAKCount_Column, Qt::AlignRight); + setText(Receiver_NAKBytes_Column, QString("%1").arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKBytes_Column, Qt::AlignRight); + setText(Receiver_NAKRate_Column, format_rate(delta, m_nak_bytes)); + setTextAlignment(Receiver_NAKRate_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRMReceiverMap; +typedef QMap::iterator LBMLBTRMReceiverMapIterator; + +class LBMLBTRMTransportDialogInfo +{ + public: + LBMLBTRMTransportDialogInfo(void); + ~LBMLBTRMTransportDialogInfo(void); + void setDialog(LBMLBTRMTransportDialog * dialog); + LBMLBTRMTransportDialog * getDialog(void); + void processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info); + void clearMaps(void); + + private: + LBMLBTRMTransportDialog * m_dialog; + LBMLBTRMSourceMap m_sources; + LBMLBTRMReceiverMap m_receivers; +}; + +LBMLBTRMTransportDialogInfo::LBMLBTRMTransportDialogInfo(void) : + m_dialog(NULL), + m_sources(), + m_receivers() +{ +} + +LBMLBTRMTransportDialogInfo::~LBMLBTRMTransportDialogInfo(void) +{ + clearMaps(); +} + +void LBMLBTRMTransportDialogInfo::setDialog(LBMLBTRMTransportDialog * dialog) +{ + m_dialog = dialog; +} + +LBMLBTRMTransportDialog * LBMLBTRMTransportDialogInfo::getDialog(void) +{ + return (m_dialog); +} + +void LBMLBTRMTransportDialogInfo::processPacket(const packet_info * pinfo, const lbm_lbtrm_tap_info_t * tap_info) +{ + switch (tap_info->type) + { + case LBTRM_PACKET_TYPE_DATA: + case LBTRM_PACKET_TYPE_SM: + case LBTRM_PACKET_TYPE_NCF: + { + LBMLBTRMSourceEntry * source = NULL; + LBMLBTRMSourceMapIterator it; + QString src_address = address_to_qstring(&(pinfo->src)); + + it = m_sources.find(src_address); + if (m_sources.end() == it) + { + QTreeWidgetItem * parent = NULL; + Ui::LBMLBTRMTransportDialog * ui = NULL; + + source = new LBMLBTRMSourceEntry(src_address); + it = m_sources.insert(src_address, source); + ui = m_dialog->getUI(); + ui->sources_TreeWidget->addTopLevelItem(source); + parent = ui->sources_TreeWidget->invisibleRootItem(); + parent->sortChildren(Source_AddressTransport_Column, Qt::AscendingOrder); + ui->sources_TreeWidget->resizeColumnToContents(Source_AddressTransport_Column); + } + else + { + source = it.value(); + } + source->processPacket(pinfo, tap_info); + } + break; + case LBTRM_PACKET_TYPE_NAK: + { + LBMLBTRMReceiverEntry * receiver = NULL; + LBMLBTRMReceiverMapIterator it; + QString src_address = address_to_qstring(&(pinfo->src)); + + it = m_receivers.find(src_address); + if (m_receivers.end() == it) + { + QTreeWidgetItem * parent = NULL; + Ui::LBMLBTRMTransportDialog * ui = NULL; + + receiver = new LBMLBTRMReceiverEntry(src_address); + it = m_receivers.insert(src_address, receiver); + ui = m_dialog->getUI(); + ui->receivers_TreeWidget->addTopLevelItem(receiver); + parent = ui->receivers_TreeWidget->invisibleRootItem(); + parent->sortChildren(Receiver_AddressTransport_Column, Qt::AscendingOrder); + ui->receivers_TreeWidget->resizeColumnToContents(Receiver_AddressTransport_Column); + } + else + { + receiver = it.value(); + } + receiver->processPacket(pinfo, tap_info); + } + break; + default: + break; + } +} + +void LBMLBTRMTransportDialogInfo::clearMaps(void) +{ + for (LBMLBTRMSourceMapIterator it = m_sources.begin(); it != m_sources.end(); ++it) + { + delete *it; + } + m_sources.clear(); + + for (LBMLBTRMReceiverMapIterator it = m_receivers.begin(); it != m_receivers.end(); ++it) + { + delete *it; + } + m_receivers.clear(); +} + +LBMLBTRMTransportDialog::LBMLBTRMTransportDialog(QWidget * parent, capture_file * cfile) : + QDialog(parent), + m_ui(new Ui::LBMLBTRMTransportDialog), + m_dialog_info(NULL), + m_capture_file(cfile), + m_current_source_transport(NULL), + m_current_receiver_transport(NULL), + m_source_context_menu(NULL), + m_source_header(NULL) +{ + m_ui->setupUi(this); + m_dialog_info = new LBMLBTRMTransportDialogInfo(); + + m_ui->tabWidget->setCurrentIndex(0); + m_ui->sources_detail_ComboBox->setCurrentIndex(0); + m_ui->sources_detail_transport_Label->setText(QString(" ")); + m_ui->receivers_detail_transport_Label->setText(QString(" ")); + m_ui->stackedWidget->setCurrentIndex(0); + + m_source_header = m_ui->sources_TreeWidget->header(); + m_source_context_menu = new QMenu(m_source_header); + + m_source_context_menu->addAction(m_ui->action_SourceAutoResizeColumns); + connect(m_ui->action_SourceAutoResizeColumns, SIGNAL(triggered()), this, SLOT(actionSourceAutoResizeColumns_triggered())); + m_source_context_menu->addSeparator(); + + m_ui->action_SourceDataFrames->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceDataFrames); + connect(m_ui->action_SourceDataFrames, SIGNAL(triggered(bool)), this, SLOT(actionSourceDataFrames_triggered(bool))); + m_ui->action_SourceDataBytes->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceDataBytes); + connect(m_ui->action_SourceDataBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceDataBytes_triggered(bool))); + m_ui->action_SourceDataFramesBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceDataFramesBytes); + connect(m_ui->action_SourceDataFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceDataFramesBytes_triggered(bool))); + m_ui->action_SourceDataRate->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceDataRate); + connect(m_ui->action_SourceDataRate, SIGNAL(triggered(bool)), this, SLOT(actionSourceDataRate_triggered(bool))); + + m_ui->action_SourceRXDataFrames->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceRXDataFrames); + connect(m_ui->action_SourceRXDataFrames, SIGNAL(triggered(bool)), this, SLOT(actionSourceRXDataFrames_triggered(bool))); + m_ui->action_SourceRXDataBytes->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceRXDataBytes); + connect(m_ui->action_SourceRXDataBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceRXDataBytes_triggered(bool))); + m_ui->action_SourceRXDataFramesBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceRXDataFramesBytes); + connect(m_ui->action_SourceRXDataFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceRXDataFramesBytes_triggered(bool))); + m_ui->action_SourceRXDataRate->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceRXDataRate); + connect(m_ui->action_SourceRXDataRate, SIGNAL(triggered(bool)), this, SLOT(actionSourceRXDataRate_triggered(bool))); + + m_ui->action_SourceNCFFrames->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceNCFFrames); + connect(m_ui->action_SourceNCFFrames, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFFrames_triggered(bool))); + m_ui->action_SourceNCFCount->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceNCFCount); + connect(m_ui->action_SourceNCFCount, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFCount_triggered(bool))); + m_ui->action_SourceNCFBytes->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceNCFBytes); + connect(m_ui->action_SourceNCFBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFBytes_triggered(bool))); + m_ui->action_SourceNCFFramesBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceNCFFramesBytes); + connect(m_ui->action_SourceNCFFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFFramesBytes_triggered(bool))); + m_ui->action_SourceNCFCountBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceNCFCountBytes); + connect(m_ui->action_SourceNCFCountBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFCountBytes_triggered(bool))); + m_ui->action_SourceNCFFramesCount->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceNCFFramesCount); + connect(m_ui->action_SourceNCFFramesCount, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFFramesCount_triggered(bool))); + m_ui->action_SourceNCFFramesCountBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceNCFFramesCountBytes); + connect(m_ui->action_SourceNCFFramesCountBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFFramesCountBytes_triggered(bool))); + m_ui->action_SourceNCFRate->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceNCFRate); + connect(m_ui->action_SourceNCFRate, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFRate_triggered(bool))); + + m_ui->action_SourceSMFrames->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceSMFrames); + connect(m_ui->action_SourceSMFrames, SIGNAL(triggered(bool)), this, SLOT(actionSourceSMFrames_triggered(bool))); + m_ui->action_SourceSMBytes->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceSMBytes); + connect(m_ui->action_SourceSMBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceSMBytes_triggered(bool))); + m_ui->action_SourceSMFramesBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceSMFramesBytes); + connect(m_ui->action_SourceSMFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceSMFramesBytes_triggered(bool))); + m_ui->action_SourceSMRate->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceSMRate); + connect(m_ui->action_SourceSMRate, SIGNAL(triggered(bool)), this, SLOT(actionSourceSMRate_triggered(bool))); + + m_source_header->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_source_header, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(custom_source_context_menuRequested(const QPoint &))); + + m_ui->sources_TreeWidget->setColumnHidden(Source_DataFramesBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataFramesBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFCountBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesCount_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesCountBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_SMFramesBytes_Column, true); + + setAttribute(Qt::WA_DeleteOnClose, true); + fillTree(); +} + +LBMLBTRMTransportDialog::~LBMLBTRMTransportDialog(void) +{ + resetSourcesDetail(); + resetSources(); + resetReceiversDetail(); + resetReceivers(); + if (m_dialog_info != NULL) + { + delete m_dialog_info; + m_dialog_info = NULL; + } + delete m_source_context_menu; + m_source_context_menu = NULL; + delete m_ui; + m_ui = NULL; + m_capture_file = NULL; +} + +void LBMLBTRMTransportDialog::setCaptureFile(capture_file * cfile) +{ + if (cfile == NULL) // We only want to know when the file closes. + { + m_capture_file = NULL; + m_ui->displayFilterLineEdit->setEnabled(false); + m_ui->applyFilterButton->setEnabled(false); + } +} + +void LBMLBTRMTransportDialog::resetSources(void) +{ + while (m_ui->sources_TreeWidget->takeTopLevelItem(0) != NULL) + {} +} + +void LBMLBTRMTransportDialog::resetReceivers(void) +{ + while (m_ui->receivers_TreeWidget->takeTopLevelItem(0) != NULL) + {} +} + +void LBMLBTRMTransportDialog::resetSourcesDetail(void) +{ + while (m_ui->sources_detail_sqn_TreeWidget->takeTopLevelItem(0) != NULL) + {} + while (m_ui->sources_detail_ncf_sqn_TreeWidget->takeTopLevelItem(0) != NULL) + {} + m_ui->sources_detail_transport_Label->setText(QString(" ")); + m_current_source_transport = NULL; +} + +void LBMLBTRMTransportDialog::resetReceiversDetail(void) +{ + while (m_ui->receivers_detail_TreeWidget->takeTopLevelItem(0) != NULL) + {} + m_ui->receivers_detail_transport_Label->setText(QString(" ")); + m_current_receiver_transport = NULL; +} + +void LBMLBTRMTransportDialog::fillTree(void) +{ + GString * error_string; + + if (m_capture_file == NULL) + { + return; + } + m_dialog_info->setDialog(this); + + error_string = register_tap_listener("lbm_lbtrm", + (void *)m_dialog_info, + m_ui->displayFilterLineEdit->text().toUtf8().constData(), + TL_REQUIRES_COLUMNS, + resetTap, + tapPacket, + drawTreeItems, + NULL); + if (error_string) + { + QMessageBox::critical(this, tr("LBT-RM Statistics failed to attach to tap"), + error_string->str); + g_string_free(error_string, TRUE); + reject(); + } + + cf_retap_packets(m_capture_file); + drawTreeItems(&m_dialog_info); + remove_tap_listener((void *)m_dialog_info); +} + +void LBMLBTRMTransportDialog::resetTap(void * tap_data) +{ + LBMLBTRMTransportDialogInfo * info = (LBMLBTRMTransportDialogInfo *) tap_data; + LBMLBTRMTransportDialog * dialog = info->getDialog(); + if (dialog == NULL) + { + return; + } + dialog->resetSourcesDetail(); + dialog->resetSources(); + dialog->resetReceiversDetail(); + dialog->resetReceivers(); + info->clearMaps(); +} + +tap_packet_status LBMLBTRMTransportDialog::tapPacket(void * tap_data, packet_info * pinfo, epan_dissect_t *, const void * tap_info, tap_flags_t) +{ + if (pinfo->fd->passed_dfilter == 1) + { + const lbm_lbtrm_tap_info_t * tapinfo = (const lbm_lbtrm_tap_info_t *)tap_info; + LBMLBTRMTransportDialogInfo * info = (LBMLBTRMTransportDialogInfo *)tap_data; + + info->processPacket(pinfo, tapinfo); + } + return (TAP_PACKET_REDRAW); +} + +void LBMLBTRMTransportDialog::drawTreeItems(void *) +{ +} + +void LBMLBTRMTransportDialog::on_applyFilterButton_clicked(void) +{ + fillTree(); +} + +void LBMLBTRMTransportDialog::sourcesDetailCurrentChanged(int index) +{ + // Index 0: Data + // Index 1: RX data + // Index 2: NCF + // Index 3: SM + switch (index) + { + case 0: + case 1: + case 3: + m_ui->stackedWidget->setCurrentIndex(0); + break; + case 2: + m_ui->stackedWidget->setCurrentIndex(1); + break; + default: + return; + } + sourcesItemClicked(m_current_source_transport, 0); +} + +void LBMLBTRMTransportDialog::sourcesItemClicked(QTreeWidgetItem * item, int) +{ + LBMLBTRMSourceTransportEntry * transport = dynamic_cast(item); + + resetSourcesDetail(); + if (transport == NULL) + { + // Must be a source item, ignore it? + return; + } + m_current_source_transport = transport; + m_ui->sources_detail_transport_Label->setText(transport->m_transport); + int cur_idx = m_ui->sources_detail_ComboBox->currentIndex(); + switch (cur_idx) + { + case 0: + loadSourceDataDetails(transport); + break; + case 1: + loadSourceRXDataDetails(transport); + break; + case 2: + loadSourceNCFDetails(transport); + break; + case 3: + loadSourceSMDetails(transport); + break; + default: + break; + } +} + +void LBMLBTRMTransportDialog::loadSourceDataDetails(LBMLBTRMSourceTransportEntry * transport) +{ + for (LBMLBTRMSQNMapIterator it = transport->m_data_sqns.begin(); it != transport->m_data_sqns.end(); ++it) + { + LBMLBTRMSQNEntry * sqn = it.value(); + m_ui->sources_detail_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRMTransportDialog::loadSourceRXDataDetails(LBMLBTRMSourceTransportEntry * transport) +{ + for (LBMLBTRMSQNMapIterator it = transport->m_rx_data_sqns.begin(); it != transport->m_rx_data_sqns.end(); ++it) + { + LBMLBTRMSQNEntry * sqn = it.value(); + m_ui->sources_detail_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRMTransportDialog::loadSourceNCFDetails(LBMLBTRMSourceTransportEntry * transport) +{ + for (LBMLBTRMNCFSQNMapIterator it = transport->m_ncf_sqns.begin(); it != transport->m_ncf_sqns.end(); ++it) + { + LBMLBTRMNCFSQNEntry * sqn = it.value(); + m_ui->sources_detail_ncf_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRMTransportDialog::loadSourceSMDetails(LBMLBTRMSourceTransportEntry * transport) +{ + for (LBMLBTRMSQNMapIterator it = transport->m_sm_sqns.begin(); it != transport->m_sm_sqns.end(); ++it) + { + LBMLBTRMSQNEntry * sqn = it.value(); + m_ui->sources_detail_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRMTransportDialog::receiversItemClicked(QTreeWidgetItem * item, int) +{ + LBMLBTRMReceiverTransportEntry * transport = dynamic_cast(item); + + resetReceiversDetail(); + if (transport == NULL) + { + // Must be a receiver item, ignore it? + return; + } + m_current_receiver_transport = transport; + m_ui->receivers_detail_transport_Label->setText(transport->m_transport); + loadReceiverNAKDetails(transport); +} + +void LBMLBTRMTransportDialog::loadReceiverNAKDetails(LBMLBTRMReceiverTransportEntry * transport) +{ + for (LBMLBTRMSQNMapIterator it = transport->m_nak_sqns.begin(); it != transport->m_nak_sqns.end(); ++it) + { + LBMLBTRMSQNEntry * sqn = it.value(); + m_ui->receivers_detail_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRMTransportDialog::sourcesDetailItemDoubleClicked(QTreeWidgetItem * item, int) +{ + LBMLBTRMFrameEntry * frame = dynamic_cast(item); + if (frame == NULL) + { + // Must have double-clicked on something other than an expanded frame entry + return; + } + emit goToPacket((int)frame->getFrame()); +} + +void LBMLBTRMTransportDialog::receiversDetailItemDoubleClicked(QTreeWidgetItem * item, int) +{ + LBMLBTRMFrameEntry * frame = dynamic_cast(item); + if (frame == NULL) + { + // Must have double-clicked on something other than an expanded frame entry + return; + } + emit goToPacket((int)frame->getFrame()); +} + +void LBMLBTRMTransportDialog::custom_source_context_menuRequested(const QPoint & pos) +{ + m_source_context_menu->popup(m_source_header->mapToGlobal(pos)); +} + +void LBMLBTRMTransportDialog::actionSourceDataFrames_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_DataFrames_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceDataBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_DataBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceDataFramesBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_DataFramesBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceDataRate_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_DataRate_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceRXDataFrames_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataFrames_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceRXDataBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceRXDataFramesBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataFramesBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceRXDataRate_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataRate_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceNCFFrames_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFrames_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceNCFCount_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFCount_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceNCFBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFrames_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceNCFFramesBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceNCFCountBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFCountBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceNCFFramesCount_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesCount_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceNCFFramesCountBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesCountBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceNCFRate_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFRate_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceSMFrames_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_SMFrames_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceSMBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_SMBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceSMFramesBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_SMFramesBytes_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceSMRate_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_SMRate_Column, !checked); +} + +void LBMLBTRMTransportDialog::actionSourceAutoResizeColumns_triggered(void) +{ + m_ui->sources_TreeWidget->resizeColumnToContents(Source_AddressTransport_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_DataFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_DataBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_DataFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_DataRate_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RXDataFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RXDataBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RXDataFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RXDataRate_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFCount_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFCountBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFFramesCount_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFFramesCountBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFRate_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_SMFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_SMBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_SMFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_SMRate_Column); +} diff --git a/ui/qt/lbm_lbtrm_transport_dialog.h b/ui/qt/lbm_lbtrm_transport_dialog.h new file mode 100644 index 00000000..2e31dc50 --- /dev/null +++ b/ui/qt/lbm_lbtrm_transport_dialog.h @@ -0,0 +1,110 @@ +/** @file + * + * Copyright (c) 2005-2014 Informatica Corporation. All Rights Reserved. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef LBM_LBTRM_TRANSPORT_DIALOG_H +#define LBM_LBTRM_TRANSPORT_DIALOG_H + +#include + +#include + +#include "cfile.h" +#include +#include +#include + +class QHeaderView; +class QMenu; +class QTreeWidgetItem; + +namespace Ui +{ + class LBMLBTRMTransportDialog; +} + +class LBMLBTRMTransportDialogInfo; +class LBMLBTRMSourceTransportEntry; +class LBMLBTRMReceiverTransportEntry; + +class LBMLBTRMTransportDialog : public QDialog +{ + Q_OBJECT + + public: + explicit LBMLBTRMTransportDialog(QWidget * parent = 0, capture_file * cfile = NULL); + Ui::LBMLBTRMTransportDialog * getUI(void) + { + return (m_ui); + } + public slots: + void setCaptureFile(capture_file * cfile); + + signals: + void goToPacket(int PacketNum); + + private: + Ui::LBMLBTRMTransportDialog * m_ui; + LBMLBTRMTransportDialogInfo * m_dialog_info; + capture_file * m_capture_file; + LBMLBTRMSourceTransportEntry * m_current_source_transport; + LBMLBTRMReceiverTransportEntry * m_current_receiver_transport; + QMenu * m_source_context_menu; + QHeaderView * m_source_header; + + virtual ~LBMLBTRMTransportDialog(void); + void resetSources(void); + void resetReceivers(void); + void resetSourcesDetail(void); + void resetReceiversDetail(void); + void fillTree(void); + static void resetTap(void * tap_data); + static tap_packet_status tapPacket(void * tap_data, packet_info * pinfo, epan_dissect_t * edt, const void * stream_info, tap_flags_t flags); + static void drawTreeItems(void * tap_data); + void loadSourceDataDetails(LBMLBTRMSourceTransportEntry * transport); + void loadSourceRXDataDetails(LBMLBTRMSourceTransportEntry * transport); + void loadSourceNCFDetails(LBMLBTRMSourceTransportEntry * transport); + void loadSourceSMDetails(LBMLBTRMSourceTransportEntry * transport); + void loadSourceRSTDetails(LBMLBTRMSourceTransportEntry * transport); + void loadReceiverNAKDetails(LBMLBTRMReceiverTransportEntry * transport); + + private slots: + void on_applyFilterButton_clicked(void); + + void sourcesDetailCurrentChanged(int Index); + void sourcesItemClicked(QTreeWidgetItem * item, int column); + void receiversItemClicked(QTreeWidgetItem * item, int column); + void sourcesDetailItemDoubleClicked(QTreeWidgetItem * item, int column); + void receiversDetailItemDoubleClicked(QTreeWidgetItem * item, int column); + void actionSourceDataFrames_triggered(bool checked); + void actionSourceDataBytes_triggered(bool checked); + void actionSourceDataFramesBytes_triggered(bool checked); + void actionSourceDataRate_triggered(bool checked); + void actionSourceRXDataFrames_triggered(bool checked); + void actionSourceRXDataBytes_triggered(bool checked); + void actionSourceRXDataFramesBytes_triggered(bool checked); + void actionSourceRXDataRate_triggered(bool checked); + void actionSourceNCFFrames_triggered(bool checked); + void actionSourceNCFCount_triggered(bool checked); + void actionSourceNCFBytes_triggered(bool checked); + void actionSourceNCFFramesBytes_triggered(bool checked); + void actionSourceNCFCountBytes_triggered(bool checked); + void actionSourceNCFFramesCount_triggered(bool checked); + void actionSourceNCFFramesCountBytes_triggered(bool checked); + void actionSourceNCFRate_triggered(bool checked); + void actionSourceSMFrames_triggered(bool checked); + void actionSourceSMBytes_triggered(bool checked); + void actionSourceSMFramesBytes_triggered(bool checked); + void actionSourceSMRate_triggered(bool checked); + void actionSourceAutoResizeColumns_triggered(void); + void custom_source_context_menuRequested(const QPoint & pos); +}; + +#endif diff --git a/ui/qt/lbm_lbtrm_transport_dialog.ui b/ui/qt/lbm_lbtrm_transport_dialog.ui new file mode 100644 index 00000000..ad2672ed --- /dev/null +++ b/ui/qt/lbm_lbtrm_transport_dialog.ui @@ -0,0 +1,836 @@ + + + LBMLBTRMTransportDialog + + + + 0 + 0 + 841 + 563 + + + + LBT-RM Transport Statistics + + + true + + + + + + 0 + + + + + 0 + 0 + + + + Sources + + + + + + Qt::Vertical + + + 10 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + 80 + + + + Address/Transport + + + + + Data frames + + + + + Data bytes + + + + + Data frames/bytes + + + + + Data rate + + + + + RX data frames + + + + + RX data bytes + + + + + RX data frames/bytes + + + + + RX data rate + + + + + NCF frames + + + + + NCF count + + + + + NCF bytes + + + + + NCF frames/bytes + + + + + NCF count/bytes + + + + + NCF frames/count + + + + + NCF frames/count/bytes + + + + + NCF rate + + + + + SM frames + + + + + SM bytes + + + + + SM frames/bytes + + + + + SM rate + + + + + + + + + + + Show + + + + + + + + Data + + + + + RX Data + + + + + NCF + + + + + SM + + + + + + + + sequence numbers for transport + + + + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + true + + + QFrame::NoFrame + + + 1 + + + 0 + + + + + + + + SQN + + + + + Count + + + + + Frame + + + + + + + + + + + + + SQN/Reason + + + + + Count + + + + + Frame + + + + + + + + + + + + + + + + + Receivers + + + + + + Qt::Vertical + + + 10 + + + + + Address/Transport + + + + + NAK frames + + + + + NAK count + + + + + NAK bytes + + + + + NAK rate + + + + + + + + + + + NAK sequence numbers for transport + + + + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 10 + 20 + + + + + + + + + + + SQN + + + + + Count + + + + + Frame + + + + + + + + + + + + + + + + 0 + + + + + Display filter: + + + + + + + + + + Regenerate statistics using this display filter + + + Apply + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Copy as CSV + + + Copy the tree as CSV + + + Ctrl+C + + + + + Copy as YAML + + + Copy the tree as YAML + + + Ctrl+Y + + + + + true + + + Data frames + + + Show the data frames column + + + + + true + + + Data bytes + + + Show the data bytes column + + + + + true + + + Data frames/bytes + + + Show the data frames/bytes column + + + + + true + + + RX data frames + + + Show the RX data frames column + + + + + true + + + RX data bytes + + + Show the RX data bytes column + + + + + true + + + RX data frames/bytes + + + Show the RX data frames/bytes column + + + + + true + + + NCF frames + + + Show the NCF frames column + + + + + true + + + NCF bytes + + + Show the NCF bytes column + + + + + true + + + NCF count + + + Show the NCF count column + + + + + true + + + Data rate + + + Show the data rate column + + + + + true + + + RX data rate + + + Show the RX data rate column + + + + + true + + + NCF frames/bytes + + + Show the NCF frames/bytes column + + + + + true + + + NCF count/bytes + + + Show the NCF count/bytes column + + + + + true + + + NCF frames/count + + + Show the NCF frames/count column + + + + + true + + + NCF frames/count/bytes + + + Show the NCF frames/count/bytes column + + + + + true + + + NCF rate + + + Show the NCF rate column + + + + + true + + + SM frames + + + Show the SM frames column + + + + + true + + + SM bytes + + + Show the SM bytes column + + + + + true + + + SM frames/bytes + + + Show the SM frames/bytes column + + + + + true + + + SM rate + + + Show the SM rate column + + + + + Auto-resize columns to content + + + Resize columns to content size + + + + + + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + + + buttonBox + accepted() + LBMLBTRMTransportDialog + accept() + + + 255 + 555 + + + 157 + 274 + + + + + buttonBox + rejected() + LBMLBTRMTransportDialog + reject() + + + 323 + 555 + + + 286 + 274 + + + + + sources_detail_ComboBox + currentIndexChanged(int) + LBMLBTRMTransportDialog + sourcesDetailCurrentChanged(int) + + + 135 + 269 + + + 446 + 310 + + + + + sources_TreeWidget + itemClicked(QTreeWidgetItem*,int) + LBMLBTRMTransportDialog + sourcesItemClicked(QTreeWidgetItem*,int) + + + 440 + 149 + + + 446 + 310 + + + + + receivers_TreeWidget + itemClicked(QTreeWidgetItem*,int) + LBMLBTRMTransportDialog + receiversItemClicked(QTreeWidgetItem*,int) + + + 420 + 141 + + + 420 + 281 + + + + + receivers_detail_TreeWidget + itemDoubleClicked(QTreeWidgetItem*,int) + LBMLBTRMTransportDialog + receiversDetailItemDoubleClicked(QTreeWidgetItem*,int) + + + 75 + 328 + + + 420 + 281 + + + + + sources_detail_sqn_TreeWidget + itemDoubleClicked(QTreeWidgetItem*,int) + LBMLBTRMTransportDialog + sourcesDetailItemDoubleClicked(QTreeWidgetItem*,int) + + + 420 + 377 + + + 420 + 281 + + + + + sources_detail_ncf_sqn_TreeWidget + itemDoubleClicked(QTreeWidgetItem*,int) + LBMLBTRMTransportDialog + sourcesDetailItemDoubleClicked(QTreeWidgetItem*,int) + + + 66 + 290 + + + 420 + 281 + + + + + + goToPacket(int) + sourcesDetailCurrentChanged(int) + sourcesItemClicked(QTreeWidgetItem*,int) + receiversItemClicked(QTreeWidgetItem*,int) + sourcesDetailItemDoubleClicked(QTreeWidgetItem*,int) + receiversDetailItemDoubleClicked(QTreeWidgetItem*,int) + +
diff --git a/ui/qt/lbm_lbtru_transport_dialog.cpp b/ui/qt/lbm_lbtru_transport_dialog.cpp new file mode 100644 index 00000000..529d7776 --- /dev/null +++ b/ui/qt/lbm_lbtru_transport_dialog.cpp @@ -0,0 +1,2207 @@ +/* lbm_lbtru_transport_dialog.cpp + * + * Copyright (c) 2005-2014 Informatica Corporation. All Rights Reserved. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "lbm_lbtru_transport_dialog.h" +#include + +#include "file.h" + +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace +{ + static const int Source_AddressTransport_Column = 0; + static const int Source_DataFrames_Column = 1; + static const int Source_DataBytes_Column = 2; + static const int Source_DataFramesBytes_Column = 3; + static const int Source_DataRate_Column = 4; + static const int Source_RXDataFrames_Column = 5; + static const int Source_RXDataBytes_Column = 6; + static const int Source_RXDataFramesBytes_Column = 7; + static const int Source_RXDataRate_Column = 8; + static const int Source_NCFFrames_Column = 9; + static const int Source_NCFCount_Column = 10; + static const int Source_NCFBytes_Column = 11; + static const int Source_NCFFramesBytes_Column = 12; + static const int Source_NCFCountBytes_Column = 13; + static const int Source_NCFFramesCount_Column = 14; + static const int Source_NCFFramesCountBytes_Column = 15; + static const int Source_NCFRate_Column = 16; + static const int Source_SMFrames_Column = 17; + static const int Source_SMBytes_Column = 18; + static const int Source_SMFramesBytes_Column = 19; + static const int Source_SMRate_Column = 20; + static const int Source_RSTFrames_Column = 21; + static const int Source_RSTBytes_Column = 22; + static const int Source_RSTFramesBytes_Column = 23; + static const int Source_RSTRate_Column = 24; + + static const int Receiver_AddressTransport_Column = 0; + static const int Receiver_NAKFrames_Column = 1; + static const int Receiver_NAKCount_Column = 2; + static const int Receiver_NAKBytes_Column = 3; + static const int Receiver_NAKFramesCount_Column = 4; + static const int Receiver_NAKCountBytes_Column = 5; + static const int Receiver_NAKFramesBytes_Column = 6; + static const int Receiver_NAKFramesCountBytes_Column = 7; + static const int Receiver_NAKRate_Column = 8; + static const int Receiver_ACKFrames_Column = 9; + static const int Receiver_ACKBytes_Column = 10; + static const int Receiver_ACKFramesBytes_Column = 11; + static const int Receiver_ACKRate_Column = 12; + static const int Receiver_CREQFrames_Column = 13; + static const int Receiver_CREQBytes_Column = 14; + static const int Receiver_CREQFramesBytes_Column = 15; + static const int Receiver_CREQRate_Column = 16; + + static const int Detail_SQNReasonType_Column = 0; + static const int Detail_Count_Column = 1; + static const int Detail_Frame_Column = 2; + + static const double OneKilobit = 1000.0; + static const double OneMegabit = OneKilobit * OneKilobit; + static const double OneGigabit = OneMegabit * OneKilobit; +} + +static QString format_rate(const nstime_t & elapsed, guint64 bytes) +{ + QString result; + double elapsed_sec; + double rate; + + if (((elapsed.secs == 0) && (elapsed.nsecs == 0)) || (bytes == 0)) + { + return (QString("0")); + } + + elapsed_sec = elapsed.secs + (((double)elapsed.nsecs) / 1000000000.0); + rate = ((double)(bytes * 8)) / elapsed_sec; + + // Currently rate is in bps + if (rate >= OneGigabit) + { + rate /= OneGigabit; + result = QString("%1G").arg(rate, 0, 'f', 2); + } + else if (rate >= OneMegabit) + { + rate /= OneMegabit; + result = QString("%1M").arg(rate, 0, 'f', 2); + } + else if (rate >= OneKilobit) + { + rate /= OneKilobit; + result = QString("%1K").arg(rate, 0, 'f', 2); + } + else + { + result = QString("%1").arg(rate, 0, 'f', 2); + } + return (result); +} + +// Note: +// LBMLBTRUFrameEntry, LBMLBTRUSQNEntry, LBMLBTRUNCFReasonEntry, LBMLBTRUNCFSQNEntry, LBMLBTRURSTReasonEntry, LBMLBTRUCREQRequestEntry, +// LBMLBTRUSourceTransportEntry, LBMLBTRUSourceEntry, LBMLBTRUReceiverTransportEntry, and LBMLBTRUReceiverEntry are all derived from +// a QTreeWidgetItem. Each instantiation can exist in two places: in a QTreeWidget, and in a containing QMap. +// +// For example: +// - LBMLBTRUTransportDialogInfo contains a QMap of the sources (LBMLBTRUSourceEntry) and receivers (LBMLBTRUReceiverEntry) +// - A source (LBMLBTRUSourceEntry) contains a QMap of the source transports originating from it (LBMLBTRUSourceTransportEntry) +// - A source transport (LBMLBTRUSourceTransportEntry) contains QMaps of data, RX data, and SM SQNs (LBMLBTRUSQNEntry), NCF SQNs +// (LBMLBTRUNCFSQNEntry), and RST reasons (LBMLBTRURSTReasonEntry) +// - A data SQN (LBMLBTRUSQNEntry) contains a QMap of the frames (LBMLBTRUFrameEntry) in which that SQN appears +// +// Not all of the entries actually appear in a QTreeWidget at one time. For example, in the source details, if no specific source +// transport is selected, nothing is in the source details tree. If Data SQNs is selected, then those details appear in the source +// details tree. Switching to RX Data SQNs removes whatever is currently in the source details tree, and adds the RX details for +// the selected transport. +// +// The actual owner of one of the above QTreeWidgetItem-derived items is the QMap container in its parent. The item is "loaned" to +// the QTreeWidget for display. +// +// All of this is to explain why +// 1) we are frequently adding things to a QTreeWidget +// 2) things are removed (takeTopLevelItem) from a QTreeWidget +// 3) destruction involves removing all items from all QTreeWidgets (rather than letting QTreeWidget delete them) +// 4) the destructor for each item has the form +// +// for (XXXMapIterator it = m_xxx.begin(); it != m_xxx.end(); ++it) +// { +// delete *it; +// } +// m_xxx.clear(); +// The for-loop calls the destructor for each item, while the clear() cleans up the space used by the QMap itself. + +// A frame entry +class LBMLBTRUFrameEntry : public QTreeWidgetItem +{ + public: + LBMLBTRUFrameEntry(guint32 frame); + virtual ~LBMLBTRUFrameEntry(void) { } + guint32 getFrame(void) { return (m_frame); } + + private: + guint32 m_frame; +}; + +LBMLBTRUFrameEntry::LBMLBTRUFrameEntry(guint32 frame) : + QTreeWidgetItem(), + m_frame(frame) +{ + setText(Detail_SQNReasonType_Column, QString(" ")); + setText(Detail_Count_Column, QString(" ")); + setText(Detail_Frame_Column, QString("%1").arg(m_frame)); +} + +typedef QMap LBMLBTRUFrameMap; +typedef QMap::iterator LBMLBTRUFrameMapIterator; + +// An SQN (SeQuence Number) entry +class LBMLBTRUSQNEntry : public QTreeWidgetItem +{ + public: + LBMLBTRUSQNEntry(guint32 sqn); + virtual ~LBMLBTRUSQNEntry(void); + void processFrame(guint32 frame); + + private: + LBMLBTRUSQNEntry(void); + guint32 m_sqn; + guint32 m_count; + LBMLBTRUFrameMap m_frames; +}; + +LBMLBTRUSQNEntry::LBMLBTRUSQNEntry(guint32 sqn) : + QTreeWidgetItem(), + m_sqn(sqn), + m_count(0), + m_frames() +{ + setText(Detail_SQNReasonType_Column, QString("%1").arg(m_sqn)); + setTextAlignment(Detail_SQNReasonType_Column, Qt::AlignRight); + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + setText(Detail_Frame_Column, QString(" ")); +} + +LBMLBTRUSQNEntry::~LBMLBTRUSQNEntry(void) +{ + for (LBMLBTRUFrameMapIterator it = m_frames.begin(); it != m_frames.end(); ++it) + { + delete *it; + } + m_frames.clear(); +} + +void LBMLBTRUSQNEntry::processFrame(guint32 frame) +{ + LBMLBTRUFrameMapIterator it; + + it = m_frames.find(frame); + if (m_frames.end() == it) + { + LBMLBTRUFrameEntry * entry = new LBMLBTRUFrameEntry(frame); + m_frames.insert(frame, entry); + addChild(entry); + sortChildren(Detail_Frame_Column, Qt::AscendingOrder); + } + m_count++; + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); +} + +// An NCF (Nak ConFirmation) Reason entry +class LBMLBTRUNCFReasonEntry : public QTreeWidgetItem +{ + public: + LBMLBTRUNCFReasonEntry(guint8 reason); + virtual ~LBMLBTRUNCFReasonEntry(void); + void processFrame(guint32 frame); + + private: + LBMLBTRUNCFReasonEntry(void); + guint8 m_reason; + guint32 m_count; + LBMLBTRUFrameMap m_frames; +}; + +LBMLBTRUNCFReasonEntry::LBMLBTRUNCFReasonEntry(guint8 reason) : + QTreeWidgetItem(), + m_reason(reason), + m_count(0), + m_frames() +{ + switch (m_reason) + { + case LBTRU_NCF_REASON_NO_RETRY: + setText(Detail_SQNReasonType_Column, QString("No Retry")); + break; + case LBTRU_NCF_REASON_IGNORED: + setText(Detail_SQNReasonType_Column, QString("Ignored")); + break; + case LBTRU_NCF_REASON_RX_DELAY: + setText(Detail_SQNReasonType_Column, QString("Retransmit Delay")); + break; + case LBTRU_NCF_REASON_SHED: + setText(Detail_SQNReasonType_Column, QString("Shed")); + break; + default: + setText(Detail_SQNReasonType_Column, QString("Unknown")); + break; + } + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + setText(Detail_Frame_Column, QString(" ")); +} + +LBMLBTRUNCFReasonEntry::~LBMLBTRUNCFReasonEntry(void) +{ + for (LBMLBTRUFrameMapIterator it = m_frames.begin(); it != m_frames.end(); ++it) + { + delete *it; + } + m_frames.clear(); +} + +void LBMLBTRUNCFReasonEntry::processFrame(guint32 frame) +{ + LBMLBTRUFrameMapIterator it; + + it = m_frames.find(frame); + if (m_frames.end() == it) + { + LBMLBTRUFrameEntry * entry = new LBMLBTRUFrameEntry(frame); + m_frames.insert(frame, entry); + addChild(entry); + sortChildren(Detail_Frame_Column, Qt::AscendingOrder); + } + m_count++; + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRUNCFReasonMap; +typedef QMap::iterator LBMLBTRUNCFReasonMapIterator; + +// An NCF SQN entry +class LBMLBTRUNCFSQNEntry : public QTreeWidgetItem +{ + public: + LBMLBTRUNCFSQNEntry(guint32 sqn); + virtual ~LBMLBTRUNCFSQNEntry(void); + void processFrame(guint8 reason, guint32 frame); + + private: + LBMLBTRUNCFSQNEntry(void); + guint32 m_sqn; + guint32 m_count; + LBMLBTRUNCFReasonMap m_reasons; +}; + +LBMLBTRUNCFSQNEntry::LBMLBTRUNCFSQNEntry(guint32 sqn) : + QTreeWidgetItem(), + m_sqn(sqn), + m_count(0), + m_reasons() +{ + setText(Detail_SQNReasonType_Column, QString("%1").arg(m_sqn)); + setTextAlignment(Detail_SQNReasonType_Column, Qt::AlignRight); + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + setText(Detail_Frame_Column, QString(" ")); +} + +LBMLBTRUNCFSQNEntry::~LBMLBTRUNCFSQNEntry(void) +{ + for (LBMLBTRUNCFReasonMapIterator it = m_reasons.begin(); it != m_reasons.end(); ++it) + { + delete *it; + } + m_reasons.clear(); +} + +void LBMLBTRUNCFSQNEntry::processFrame(guint8 reason, guint32 frame) +{ + LBMLBTRUNCFReasonMapIterator it; + LBMLBTRUNCFReasonEntry * entry = NULL; + + it = m_reasons.find(reason); + if (m_reasons.end() == it) + { + entry = new LBMLBTRUNCFReasonEntry(reason); + m_reasons.insert(reason, entry); + addChild(entry); + sortChildren(Detail_Frame_Column, Qt::AscendingOrder); + } + else + { + entry = it.value(); + } + m_count++; + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + entry->processFrame(frame); +} + +// An RST (ReSeT) Reason entry +class LBMLBTRURSTReasonEntry : public QTreeWidgetItem +{ + public: + LBMLBTRURSTReasonEntry(guint32 reason); + virtual ~LBMLBTRURSTReasonEntry(void); + void processFrame(guint32 frame); + + private: + LBMLBTRURSTReasonEntry(void); + guint32 m_reason; + QString m_reason_string; + guint32 m_count; + LBMLBTRUFrameMap m_frames; +}; + +LBMLBTRURSTReasonEntry::LBMLBTRURSTReasonEntry(guint32 reason) : + QTreeWidgetItem(), + m_reason(reason), + m_reason_string(), + m_count(0), + m_frames() +{ + switch (m_reason) + { + case LBTRU_RST_REASON_DEFAULT: + m_reason_string = "Default"; + break; + default: + m_reason_string = QString("Unknown (%1)").arg(m_reason); + break; + } + setText(Detail_SQNReasonType_Column, m_reason_string); + setTextAlignment(Detail_SQNReasonType_Column, Qt::AlignLeft); + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + setText(Detail_Frame_Column, QString(" ")); +} + +LBMLBTRURSTReasonEntry::~LBMLBTRURSTReasonEntry(void) +{ + for (LBMLBTRUFrameMapIterator it = m_frames.begin(); it != m_frames.end(); ++it) + { + delete *it; + } + m_frames.clear(); +} + +void LBMLBTRURSTReasonEntry::processFrame(guint32 frame) +{ + LBMLBTRUFrameMapIterator it; + + it = m_frames.find(frame); + if (m_frames.end() == it) + { + LBMLBTRUFrameEntry * entry = new LBMLBTRUFrameEntry(frame); + m_frames.insert(frame, entry); + addChild(entry); + sortChildren(Detail_Frame_Column, Qt::AscendingOrder); + } + m_count++; + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); +} + +// A CREQ (Connection REQuest) Request entry +class LBMLBTRUCREQRequestEntry : public QTreeWidgetItem +{ + public: + LBMLBTRUCREQRequestEntry(guint32 request); + virtual ~LBMLBTRUCREQRequestEntry(void); + void processFrame(guint32 frame); + + private: + LBMLBTRUCREQRequestEntry(void); + guint32 m_request; + QString m_request_string; + guint32 m_count; + LBMLBTRUFrameMap m_frames; +}; + +LBMLBTRUCREQRequestEntry::LBMLBTRUCREQRequestEntry(guint32 request) : + QTreeWidgetItem(), + m_request(request), + m_request_string(), + m_count(0), + m_frames() +{ + switch (m_request) + { + case LBTRU_CREQ_REQUEST_SYN: + m_request_string = "SYN"; + break; + default: + m_request_string = QString("Unknown (%1)").arg(m_request); + break; + } + setText(Detail_SQNReasonType_Column, m_request_string); + setTextAlignment(Detail_SQNReasonType_Column, Qt::AlignLeft); + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); + setText(Detail_Frame_Column, QString(" ")); +} + +LBMLBTRUCREQRequestEntry::~LBMLBTRUCREQRequestEntry(void) +{ + for (LBMLBTRUFrameMapIterator it = m_frames.begin(); it != m_frames.end(); ++it) + { + delete *it; + } + m_frames.clear(); +} + +void LBMLBTRUCREQRequestEntry::processFrame(guint32 frame) +{ + LBMLBTRUFrameMapIterator it; + + it = m_frames.find(frame); + if (m_frames.end() == it) + { + LBMLBTRUFrameEntry * entry = new LBMLBTRUFrameEntry(frame); + m_frames.insert(frame, entry); + addChild(entry); + sortChildren(Detail_Frame_Column, Qt::AscendingOrder); + } + m_count++; + setText(Detail_Count_Column, QString("%1").arg(m_count)); + setTextAlignment(Detail_Count_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRUSQNMap; +typedef QMap::iterator LBMLBTRUSQNMapIterator; +typedef QMap LBMLBTRUNCFSQNMap; +typedef QMap::iterator LBMLBTRUNCFSQNMapIterator; +typedef QMap LBMLBTRURSTReasonMap; +typedef QMap::iterator LBMLBTRURSTReasonMapIterator; +typedef QMap LBMLBTRUCREQRequestMap; +typedef QMap::iterator LBMLBTRUCREQRequestMapIterator; + +// A source transport entry +class LBMLBTRUSourceTransportEntry : public QTreeWidgetItem +{ + friend class LBMLBTRUTransportDialog; + + public: + LBMLBTRUSourceTransportEntry(const QString & transport); + virtual ~LBMLBTRUSourceTransportEntry(void); + void processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info); + + protected: + QString m_transport; + + private: + void fillItem(void); + guint64 m_data_frames; + guint64 m_data_bytes; + guint64 m_rx_data_frames; + guint64 m_rx_data_bytes; + guint64 m_ncf_frames; + guint64 m_ncf_count; + guint64 m_ncf_bytes; + guint64 m_sm_frames; + guint64 m_sm_bytes; + guint64 m_rst_frames; + guint64 m_rst_bytes; + nstime_t m_first_frame_timestamp; + bool m_first_frame_timestamp_valid; + nstime_t m_last_frame_timestamp; + + protected: + LBMLBTRUSQNMap m_data_sqns; + LBMLBTRUSQNMap m_rx_data_sqns; + LBMLBTRUNCFSQNMap m_ncf_sqns; + LBMLBTRUSQNMap m_sm_sqns; + LBMLBTRURSTReasonMap m_rst_reasons; +}; + +LBMLBTRUSourceTransportEntry::LBMLBTRUSourceTransportEntry(const QString & transport) : + QTreeWidgetItem(), + m_transport(transport), + m_data_frames(0), + m_data_bytes(0), + m_rx_data_frames(0), + m_rx_data_bytes(0), + m_ncf_frames(0), + m_ncf_count(0), + m_ncf_bytes(0), + m_sm_frames(0), + m_sm_bytes(0), + m_rst_frames(0), + m_rst_bytes(0), + m_first_frame_timestamp_valid(false), + m_data_sqns(), + m_rx_data_sqns(), + m_ncf_sqns(), + m_sm_sqns(), + m_rst_reasons() +{ + m_first_frame_timestamp.secs = 0; + m_first_frame_timestamp.nsecs = 0; + m_last_frame_timestamp.secs = 0; + m_last_frame_timestamp.nsecs = 0; + setText(Source_AddressTransport_Column, m_transport); +} + +LBMLBTRUSourceTransportEntry::~LBMLBTRUSourceTransportEntry(void) +{ + for (LBMLBTRUSQNMapIterator it = m_data_sqns.begin(); it != m_data_sqns.end(); ++it) + { + delete *it; + } + m_data_sqns.clear(); + + for (LBMLBTRUSQNMapIterator it = m_rx_data_sqns.begin(); it != m_rx_data_sqns.end(); ++it) + { + delete *it; + } + m_rx_data_sqns.clear(); + + for (LBMLBTRUNCFSQNMapIterator it = m_ncf_sqns.begin(); it != m_ncf_sqns.end(); ++it) + { + delete *it; + } + m_ncf_sqns.clear(); + + for (LBMLBTRUSQNMapIterator it = m_sm_sqns.begin(); it != m_sm_sqns.end(); ++it) + { + delete *it; + } + m_sm_sqns.clear(); + + for (LBMLBTRURSTReasonMapIterator it = m_rst_reasons.begin(); it != m_rst_reasons.end(); ++it) + { + delete *it; + } + m_rst_reasons.clear(); +} + +void LBMLBTRUSourceTransportEntry::processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info) +{ + if (m_first_frame_timestamp_valid) + { + if (nstime_cmp(&(pinfo->abs_ts), &m_first_frame_timestamp) < 0) + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + } + } + else + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + m_first_frame_timestamp_valid = true; + } + if (nstime_cmp(&(pinfo->abs_ts), &m_last_frame_timestamp) > 0) + { + nstime_copy(&(m_last_frame_timestamp), &(pinfo->abs_ts)); + } + if (tap_info->type == LBTRU_PACKET_TYPE_DATA) + { + LBMLBTRUSQNEntry * sqn = NULL; + LBMLBTRUSQNMapIterator it; + + if (tap_info->retransmission) + { + m_rx_data_frames++; + m_rx_data_bytes += pinfo->fd->pkt_len; + it = m_rx_data_sqns.find(tap_info->sqn); + if (m_rx_data_sqns.end() == it) + { + sqn = new LBMLBTRUSQNEntry(tap_info->sqn); + m_rx_data_sqns.insert(tap_info->sqn, sqn); + } + else + { + sqn = it.value(); + } + sqn->processFrame(pinfo->num); + } + else + { + m_data_frames++; + m_data_bytes += pinfo->fd->pkt_len; + it = m_data_sqns.find(tap_info->sqn); + if (m_data_sqns.end() == it) + { + sqn = new LBMLBTRUSQNEntry(tap_info->sqn); + m_data_sqns.insert(tap_info->sqn, sqn); + } + else + { + sqn = it.value(); + } + } + sqn->processFrame(pinfo->num); + } + else if (tap_info->type == LBTRU_PACKET_TYPE_NCF) + { + guint16 idx; + LBMLBTRUNCFSQNMapIterator it; + LBMLBTRUNCFSQNEntry * sqn = NULL; + + m_ncf_frames++; + m_ncf_bytes += pinfo->fd->pkt_len; + m_ncf_count += (guint64)tap_info->num_sqns; + for (idx = 0; idx < tap_info->num_sqns; idx++) + { + it = m_ncf_sqns.find(tap_info->sqns[idx]); + if (m_ncf_sqns.end() == it) + { + sqn = new LBMLBTRUNCFSQNEntry(tap_info->sqns[idx]); + m_ncf_sqns.insert(tap_info->sqns[idx], sqn); + } + else + { + sqn = it.value(); + } + sqn->processFrame(tap_info->ncf_reason, pinfo->num); + } + } + else if (tap_info->type == LBTRU_PACKET_TYPE_SM) + { + LBMLBTRUSQNEntry * sqn = NULL; + LBMLBTRUSQNMapIterator it; + + m_sm_frames++; + m_sm_bytes += pinfo->fd->pkt_len; + it = m_sm_sqns.find(tap_info->sqn); + if (m_sm_sqns.end() == it) + { + sqn = new LBMLBTRUSQNEntry(tap_info->sqn); + m_sm_sqns.insert(tap_info->sqn, sqn); + } + else + { + sqn = it.value(); + } + sqn->processFrame(pinfo->num); + } + else if (tap_info->type == LBTRU_PACKET_TYPE_RST) + { + LBMLBTRURSTReasonEntry * reason = NULL; + LBMLBTRURSTReasonMapIterator it; + + m_rst_frames++; + m_rst_bytes += pinfo->fd->pkt_len; + it = m_rst_reasons.find(tap_info->rst_type); + if (m_rst_reasons.end() == it) + { + reason = new LBMLBTRURSTReasonEntry(tap_info->rst_type); + m_rst_reasons.insert((unsigned int) tap_info->rst_type, reason); + } + else + { + reason = it.value(); + } + reason->processFrame(pinfo->num); + } + else + { + return; + } + fillItem(); +} + +void LBMLBTRUSourceTransportEntry::fillItem(void) +{ + nstime_t delta; + + nstime_delta(&delta, &m_last_frame_timestamp, &m_first_frame_timestamp); + setText(Source_DataFrames_Column, QString("%1").arg(m_data_frames)); + setTextAlignment(Source_DataFrames_Column, Qt::AlignRight); + setText(Source_DataBytes_Column, QString("%1").arg(m_data_bytes)); + setTextAlignment(Source_DataBytes_Column, Qt::AlignRight); + setText(Source_DataFramesBytes_Column, QString("%1/%2").arg(m_data_frames).arg(m_data_bytes)); + setTextAlignment(Source_DataFramesBytes_Column, Qt::AlignHCenter); + setText(Source_DataRate_Column, format_rate(delta, m_data_bytes)); + setTextAlignment(Source_DataRate_Column, Qt::AlignRight); + setText(Source_RXDataFrames_Column, QString("%1").arg(m_rx_data_frames)); + setTextAlignment(Source_RXDataFrames_Column, Qt::AlignRight); + setText(Source_RXDataBytes_Column, QString("%1").arg(m_rx_data_bytes)); + setTextAlignment(Source_RXDataBytes_Column, Qt::AlignRight); + setText(Source_RXDataFramesBytes_Column, QString("%1/%2").arg(m_rx_data_frames).arg(m_rx_data_bytes)); + setTextAlignment(Source_RXDataFramesBytes_Column, Qt::AlignHCenter); + setText(Source_RXDataRate_Column, format_rate(delta, m_rx_data_bytes)); + setTextAlignment(Source_RXDataRate_Column, Qt::AlignRight); + setText(Source_NCFFrames_Column, QString("%1").arg(m_ncf_frames)); + setTextAlignment(Source_NCFFrames_Column, Qt::AlignRight); + setText(Source_NCFCount_Column, QString("%1").arg(m_ncf_count)); + setTextAlignment(Source_NCFCount_Column, Qt::AlignRight); + setText(Source_NCFBytes_Column, QString("%1").arg(m_ncf_bytes)); + setTextAlignment(Source_NCFBytes_Column, Qt::AlignRight); + setText(Source_NCFFramesBytes_Column, QString("%1/%2").arg(m_ncf_frames).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFFramesBytes_Column, Qt::AlignHCenter); + setText(Source_NCFCountBytes_Column, QString("%1/%2").arg(m_ncf_count).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFCountBytes_Column, Qt::AlignHCenter); + setText(Source_NCFFramesCount_Column, QString("%1/%2").arg(m_ncf_count).arg(m_ncf_count)); + setTextAlignment(Source_NCFFramesCount_Column, Qt::AlignHCenter); + setText(Source_NCFFramesCountBytes_Column, QString("%1/%2/%3").arg(m_ncf_frames).arg(m_ncf_count).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFFramesCountBytes_Column, Qt::AlignHCenter); + setText(Source_NCFRate_Column, format_rate(delta, m_ncf_bytes)); + setTextAlignment(Source_NCFRate_Column, Qt::AlignRight); + setText(Source_SMFrames_Column, QString("%1").arg(m_sm_frames)); + setTextAlignment(Source_SMFrames_Column, Qt::AlignRight); + setText(Source_SMBytes_Column, QString("%1").arg(m_sm_bytes)); + setTextAlignment(Source_SMBytes_Column, Qt::AlignRight); + setText(Source_SMFramesBytes_Column, QString("%1/%2").arg(m_sm_frames).arg(m_sm_bytes)); + setTextAlignment(Source_SMFramesBytes_Column, Qt::AlignHCenter); + setText(Source_SMRate_Column, format_rate(delta, m_sm_bytes)); + setTextAlignment(Source_SMRate_Column, Qt::AlignRight); + setText(Source_RSTFrames_Column, QString("%1").arg(m_rst_frames)); + setTextAlignment(Source_RSTFrames_Column, Qt::AlignRight); + setText(Source_RSTBytes_Column, QString("%1").arg(m_rst_bytes)); + setTextAlignment(Source_RSTBytes_Column, Qt::AlignRight); + setText(Source_RSTFramesBytes_Column, QString("%1/%2").arg(m_rst_frames).arg(m_rst_bytes)); + setTextAlignment(Source_RSTFramesBytes_Column, Qt::AlignHCenter); + setText(Source_RSTRate_Column, format_rate(delta, m_rst_bytes)); + setTextAlignment(Source_RSTRate_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRUSourceTransportMap; +typedef QMap::iterator LBMLBTRUSourceTransportMapIterator; + +// A source (address) entry +class LBMLBTRUSourceEntry : public QTreeWidgetItem +{ + public: + LBMLBTRUSourceEntry(const QString & source_address); + virtual ~LBMLBTRUSourceEntry(void); + void processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info); + + private: + void fillItem(void); + QString m_address; + QString m_transport; + guint64 m_data_frames; + guint64 m_data_bytes; + guint64 m_rx_data_frames; + guint64 m_rx_data_bytes; + guint64 m_ncf_frames; + guint64 m_ncf_count; + guint64 m_ncf_bytes; + guint64 m_sm_frames; + guint64 m_sm_bytes; + guint64 m_rst_frames; + guint64 m_rst_bytes; + nstime_t m_first_frame_timestamp; + bool m_first_frame_timestamp_valid; + nstime_t m_last_frame_timestamp; + LBMLBTRUSourceTransportMap m_transports; +}; + +LBMLBTRUSourceEntry::LBMLBTRUSourceEntry(const QString & source_address) : + QTreeWidgetItem(), + m_address(source_address), + m_data_frames(0), + m_data_bytes(0), + m_rx_data_frames(0), + m_rx_data_bytes(0), + m_ncf_frames(0), + m_ncf_count(0), + m_ncf_bytes(0), + m_sm_frames(0), + m_sm_bytes(0), + m_rst_frames(0), + m_rst_bytes(0), + m_first_frame_timestamp_valid(false), + m_transports() +{ + m_first_frame_timestamp.secs = 0; + m_first_frame_timestamp.nsecs = 0; + m_last_frame_timestamp.secs = 0; + m_last_frame_timestamp.nsecs = 0; + setText(Source_AddressTransport_Column, m_address); +} + +LBMLBTRUSourceEntry::~LBMLBTRUSourceEntry(void) +{ + for (LBMLBTRUSourceTransportMapIterator it = m_transports.begin(); it != m_transports.end(); ++it) + { + delete *it; + } + m_transports.clear(); +} + +void LBMLBTRUSourceEntry::processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info) +{ + LBMLBTRUSourceTransportEntry * transport = NULL; + LBMLBTRUSourceTransportMapIterator it; + + if (m_first_frame_timestamp_valid) + { + if (nstime_cmp(&(pinfo->abs_ts), &m_first_frame_timestamp) < 0) + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + } + } + else + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + m_first_frame_timestamp_valid = true; + } + if (nstime_cmp(&(pinfo->abs_ts), &m_last_frame_timestamp) > 0) + { + nstime_copy(&(m_last_frame_timestamp), &(pinfo->abs_ts)); + } + switch (tap_info->type) + { + case LBTRU_PACKET_TYPE_DATA: + if (tap_info->retransmission) + { + m_rx_data_frames++; + m_rx_data_bytes += pinfo->fd->pkt_len; + } + else + { + m_data_frames++; + m_data_bytes += pinfo->fd->pkt_len; + } + break; + case LBTRU_PACKET_TYPE_NCF: + m_ncf_frames++; + m_ncf_bytes += pinfo->fd->pkt_len; + m_ncf_count += tap_info->num_sqns; + break; + case LBTRU_PACKET_TYPE_SM: + m_sm_frames++; + m_sm_bytes += pinfo->fd->pkt_len; + break; + case LBTRU_PACKET_TYPE_RST: + m_rst_frames++; + m_rst_bytes += pinfo->fd->pkt_len; + break; + } + + it = m_transports.find(tap_info->transport); + if (m_transports.end() == it) + { + transport = new LBMLBTRUSourceTransportEntry(tap_info->transport); + m_transports.insert(tap_info->transport, transport); + addChild(transport); + sortChildren(Source_AddressTransport_Column, Qt::AscendingOrder); + } + else + { + transport = it.value(); + } + fillItem(); + transport->processPacket(pinfo, tap_info); +} + +void LBMLBTRUSourceEntry::fillItem(void) +{ + nstime_t delta; + + nstime_delta(&delta, &m_last_frame_timestamp, &m_first_frame_timestamp); + setText(Source_DataFrames_Column, QString("%1").arg(m_data_frames)); + setTextAlignment(Source_DataFrames_Column, Qt::AlignRight); + setText(Source_DataBytes_Column, QString("%1").arg(m_data_bytes)); + setTextAlignment(Source_DataBytes_Column, Qt::AlignRight); + setText(Source_DataFramesBytes_Column, QString("%1/%2").arg(m_data_frames).arg(m_data_bytes)); + setTextAlignment(Source_DataFramesBytes_Column, Qt::AlignHCenter); + setText(Source_DataRate_Column, format_rate(delta, m_data_bytes)); + setTextAlignment(Source_DataRate_Column, Qt::AlignRight); + setText(Source_RXDataFrames_Column, QString("%1").arg(m_rx_data_frames)); + setTextAlignment(Source_RXDataFrames_Column, Qt::AlignRight); + setText(Source_RXDataBytes_Column, QString("%1").arg(m_rx_data_bytes)); + setTextAlignment(Source_RXDataBytes_Column, Qt::AlignRight); + setText(Source_RXDataFramesBytes_Column, QString("%1/%2").arg(m_rx_data_frames).arg(m_rx_data_bytes)); + setTextAlignment(Source_RXDataFramesBytes_Column, Qt::AlignHCenter); + setText(Source_RXDataRate_Column, format_rate(delta, m_rx_data_bytes)); + setTextAlignment(Source_RXDataRate_Column, Qt::AlignRight); + setText(Source_NCFFrames_Column, QString("%1").arg(m_ncf_frames)); + setTextAlignment(Source_NCFFrames_Column, Qt::AlignRight); + setText(Source_NCFCount_Column, QString("%1").arg(m_ncf_count)); + setTextAlignment(Source_NCFCount_Column, Qt::AlignRight); + setText(Source_NCFBytes_Column, QString("%1").arg(m_ncf_bytes)); + setTextAlignment(Source_NCFBytes_Column, Qt::AlignRight); + setText(Source_NCFFramesBytes_Column, QString("%1/%2").arg(m_ncf_frames).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFFramesBytes_Column, Qt::AlignHCenter); + setText(Source_NCFCountBytes_Column, QString("%1/%2").arg(m_ncf_count).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFCountBytes_Column, Qt::AlignHCenter); + setText(Source_NCFFramesCount_Column, QString("%1/%2").arg(m_ncf_frames).arg(m_ncf_count)); + setTextAlignment(Source_NCFFramesCount_Column, Qt::AlignHCenter); + setText(Source_NCFFramesCountBytes_Column, QString("%1/%2/%3").arg(m_ncf_frames).arg(m_ncf_count).arg(m_ncf_bytes)); + setTextAlignment(Source_NCFFramesCountBytes_Column, Qt::AlignHCenter); + setText(Source_NCFRate_Column, format_rate(delta, m_ncf_bytes)); + setTextAlignment(Source_NCFRate_Column, Qt::AlignRight); + setText(Source_SMFrames_Column, QString("%1").arg(m_sm_frames)); + setTextAlignment(Source_SMFrames_Column, Qt::AlignRight); + setText(Source_SMBytes_Column, QString("%1").arg(m_sm_bytes)); + setTextAlignment(Source_SMBytes_Column, Qt::AlignRight); + setText(Source_SMFramesBytes_Column, QString("%1/%2").arg(m_sm_frames).arg(m_sm_bytes)); + setTextAlignment(Source_SMFramesBytes_Column, Qt::AlignRight); + setText(Source_SMRate_Column, format_rate(delta, m_sm_bytes)); + setTextAlignment(Source_SMRate_Column, Qt::AlignRight); + setText(Source_RSTFrames_Column, QString("%1").arg(m_rst_frames)); + setTextAlignment(Source_RSTFrames_Column, Qt::AlignRight); + setText(Source_RSTBytes_Column, QString("%1").arg(m_rst_bytes)); + setTextAlignment(Source_RSTBytes_Column, Qt::AlignRight); + setText(Source_RSTFramesBytes_Column, QString("%1/%2").arg(m_rst_frames).arg(m_rst_bytes)); + setTextAlignment(Source_RSTFramesBytes_Column, Qt::AlignHCenter); + setText(Source_RSTRate_Column, format_rate(delta, m_rst_bytes)); + setTextAlignment(Source_RSTRate_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRUSourceMap; +typedef QMap::iterator LBMLBTRUSourceMapIterator; + +// A receiver transport entry +class LBMLBTRUReceiverTransportEntry : public QTreeWidgetItem +{ + friend class LBMLBTRUTransportDialog; + + public: + LBMLBTRUReceiverTransportEntry(const QString & transport); + virtual ~LBMLBTRUReceiverTransportEntry(void); + void processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info); + + private: + void fillItem(void); + QString m_transport; + guint64 m_nak_frames; + guint64 m_nak_count; + guint64 m_nak_bytes; + guint64 m_ack_frames; + guint64 m_ack_bytes; + guint64 m_creq_frames; + guint64 m_creq_bytes; + nstime_t m_first_frame_timestamp; + bool m_first_frame_timestamp_valid; + nstime_t m_last_frame_timestamp; + + protected: + LBMLBTRUSQNMap m_nak_sqns; + LBMLBTRUSQNMap m_ack_sqns; + LBMLBTRUCREQRequestMap m_creq_requests; +}; + +LBMLBTRUReceiverTransportEntry::LBMLBTRUReceiverTransportEntry(const QString & transport) : + QTreeWidgetItem(), + m_transport(transport), + m_nak_frames(0), + m_nak_count(0), + m_nak_bytes(0), + m_ack_frames(0), + m_ack_bytes(0), + m_creq_frames(0), + m_creq_bytes(0), + m_first_frame_timestamp_valid(false), + m_nak_sqns(), + m_ack_sqns(), + m_creq_requests() +{ + m_first_frame_timestamp.secs = 0; + m_first_frame_timestamp.nsecs = 0; + m_last_frame_timestamp.secs = 0; + m_last_frame_timestamp.nsecs = 0; + setText(Receiver_AddressTransport_Column, m_transport); +} + +LBMLBTRUReceiverTransportEntry::~LBMLBTRUReceiverTransportEntry(void) +{ + for (LBMLBTRUSQNMapIterator it = m_nak_sqns.begin(); it != m_nak_sqns.end(); ++it) + { + delete *it; + } + m_nak_sqns.clear(); + + for (LBMLBTRUSQNMapIterator it = m_ack_sqns.begin(); it != m_ack_sqns.end(); ++it) + { + delete *it; + } + m_ack_sqns.clear(); + + for (LBMLBTRUCREQRequestMapIterator it = m_creq_requests.begin(); it != m_creq_requests.end(); ++it) + { + delete *it; + } + m_creq_requests.clear(); +} + +void LBMLBTRUReceiverTransportEntry::processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info) +{ + if (m_first_frame_timestamp_valid) + { + if (nstime_cmp(&(pinfo->abs_ts), &m_first_frame_timestamp) < 0) + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + } + } + else + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + m_first_frame_timestamp_valid = true; + } + if (nstime_cmp(&(pinfo->abs_ts), &m_last_frame_timestamp) > 0) + { + nstime_copy(&(m_last_frame_timestamp), &(pinfo->abs_ts)); + } + switch (tap_info->type) + { + case LBTRU_PACKET_TYPE_NAK: + { + guint16 idx; + LBMLBTRUSQNEntry * sqn = NULL; + LBMLBTRUSQNMapIterator it; + + m_nak_frames++; + m_nak_bytes += pinfo->fd->pkt_len; + m_nak_count += tap_info->num_sqns; + for (idx = 0; idx < tap_info->num_sqns; idx++) + { + it = m_nak_sqns.find(tap_info->sqns[idx]); + if (m_nak_sqns.end() == it) + { + sqn = new LBMLBTRUSQNEntry(tap_info->sqns[idx]); + m_nak_sqns.insert(tap_info->sqns[idx], sqn); + } + else + { + sqn = it.value(); + } + sqn->processFrame(pinfo->num); + } + } + break; + case LBTRU_PACKET_TYPE_ACK: + { + LBMLBTRUSQNEntry * sqn = NULL; + LBMLBTRUSQNMapIterator it; + + m_ack_frames++; + m_ack_bytes += pinfo->fd->pkt_len; + it = m_ack_sqns.find(tap_info->sqn); + if (m_ack_sqns.end() == it) + { + sqn = new LBMLBTRUSQNEntry(tap_info->sqn); + m_ack_sqns.insert(tap_info->sqn, sqn); + } + else + { + sqn = it.value(); + } + sqn->processFrame(pinfo->num); + } + break; + case LBTRU_PACKET_TYPE_CREQ: + { + LBMLBTRUCREQRequestEntry * req = NULL; + LBMLBTRUCREQRequestMapIterator it; + + m_creq_frames++; + m_creq_bytes += pinfo->fd->pkt_len; + it = m_creq_requests.find(tap_info->creq_type); + if (m_creq_requests.end() == it) + { + req = new LBMLBTRUCREQRequestEntry(tap_info->creq_type); + m_creq_requests.insert(tap_info->creq_type, req); + } + else + { + req = it.value(); + } + req->processFrame(pinfo->num); + } + break; + default: + return; + break; + } + fillItem(); +} + +void LBMLBTRUReceiverTransportEntry::fillItem(void) +{ + nstime_t delta; + + nstime_delta(&delta, &m_last_frame_timestamp, &m_first_frame_timestamp); + setText(Receiver_NAKFrames_Column, QString("%1").arg(m_nak_frames)); + setTextAlignment(Receiver_NAKFrames_Column, Qt::AlignRight); + setText(Receiver_NAKCount_Column, QString("%1").arg(m_nak_count)); + setTextAlignment(Receiver_NAKCount_Column, Qt::AlignRight); + setText(Receiver_NAKBytes_Column, QString("%1").arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKBytes_Column, Qt::AlignRight); + setText(Receiver_NAKFramesCount_Column, QString("%1/%2").arg(m_nak_frames).arg(m_nak_count)); + setTextAlignment(Receiver_NAKFramesCount_Column, Qt::AlignHCenter); + setText(Receiver_NAKCountBytes_Column, QString("%1/%2").arg(m_nak_count).arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKCountBytes_Column, Qt::AlignHCenter); + setText(Receiver_NAKFramesBytes_Column, QString("%1/%2").arg(m_nak_frames).arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKFramesBytes_Column, Qt::AlignHCenter); + setText(Receiver_NAKFramesCountBytes_Column, QString("%1/%2/%3").arg(m_nak_frames).arg(m_nak_count).arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKFramesCountBytes_Column, Qt::AlignHCenter); + setText(Receiver_NAKRate_Column, format_rate(delta, m_nak_bytes)); + setTextAlignment(Receiver_NAKRate_Column, Qt::AlignRight); + setText(Receiver_ACKFrames_Column, QString("%1").arg(m_ack_frames)); + setTextAlignment(Receiver_ACKFrames_Column, Qt::AlignRight); + setText(Receiver_ACKBytes_Column, QString("%1").arg(m_ack_bytes)); + setTextAlignment(Receiver_ACKBytes_Column, Qt::AlignRight); + setText(Receiver_ACKFramesBytes_Column, QString("%1/%2").arg(m_ack_frames).arg(m_ack_bytes)); + setTextAlignment(Receiver_ACKFramesBytes_Column, Qt::AlignHCenter); + setText(Receiver_ACKRate_Column, format_rate(delta, m_ack_bytes)); + setTextAlignment(Receiver_ACKRate_Column, Qt::AlignRight); + setText(Receiver_CREQFrames_Column, QString("%1").arg(m_creq_frames)); + setTextAlignment(Receiver_CREQFrames_Column, Qt::AlignRight); + setText(Receiver_CREQBytes_Column, QString("%1").arg(m_creq_bytes)); + setTextAlignment(Receiver_CREQBytes_Column, Qt::AlignRight); + setText(Receiver_CREQFramesBytes_Column, QString("%1/%2").arg(m_creq_frames).arg(m_creq_bytes)); + setTextAlignment(Receiver_CREQFramesBytes_Column, Qt::AlignHCenter); + setText(Receiver_CREQRate_Column, format_rate(delta, m_creq_bytes)); + setTextAlignment(Receiver_CREQRate_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRUReceiverTransportMap; +typedef QMap::iterator LBMLBTRUReceiverTransportMapIterator; + +// A receiver (address) entry +class LBMLBTRUReceiverEntry : public QTreeWidgetItem +{ + public: + LBMLBTRUReceiverEntry(const QString & receiver_address); + virtual ~LBMLBTRUReceiverEntry(void); + void processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info); + + private: + void fillItem(void); + QString m_address; + QString m_transport; + guint64 m_nak_frames; + guint64 m_nak_count; + guint64 m_nak_bytes; + guint64 m_ack_frames; + guint64 m_ack_bytes; + guint64 m_creq_frames; + guint64 m_creq_bytes; + nstime_t m_first_frame_timestamp; + bool m_first_frame_timestamp_valid; + nstime_t m_last_frame_timestamp; + LBMLBTRUReceiverTransportMap m_transports; +}; + +LBMLBTRUReceiverEntry::LBMLBTRUReceiverEntry(const QString & receiver_address) : + QTreeWidgetItem(), + m_address(receiver_address), + m_nak_frames(0), + m_nak_count(0), + m_nak_bytes(0), + m_ack_frames(0), + m_ack_bytes(0), + m_creq_frames(0), + m_creq_bytes(0), + m_first_frame_timestamp_valid(false), + m_transports() +{ + m_first_frame_timestamp.secs = 0; + m_first_frame_timestamp.nsecs = 0; + m_last_frame_timestamp.secs = 0; + m_last_frame_timestamp.nsecs = 0; + setText(Receiver_AddressTransport_Column, m_address); +} + +LBMLBTRUReceiverEntry::~LBMLBTRUReceiverEntry(void) +{ + for (LBMLBTRUReceiverTransportMapIterator it = m_transports.begin(); it != m_transports.end(); ++it) + { + delete *it; + } + m_transports.clear(); +} + +void LBMLBTRUReceiverEntry::processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info) +{ + LBMLBTRUReceiverTransportEntry * transport = NULL; + LBMLBTRUReceiverTransportMapIterator it; + + if (m_first_frame_timestamp_valid) + { + if (nstime_cmp(&(pinfo->abs_ts), &m_first_frame_timestamp) < 0) + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + } + } + else + { + nstime_copy(&(m_first_frame_timestamp), &(pinfo->abs_ts)); + m_first_frame_timestamp_valid = true; + } + if (nstime_cmp(&(pinfo->abs_ts), &m_last_frame_timestamp) > 0) + { + nstime_copy(&(m_last_frame_timestamp), &(pinfo->abs_ts)); + } + switch (tap_info->type) + { + case LBTRU_PACKET_TYPE_NAK: + m_nak_frames++; + m_nak_bytes += pinfo->fd->pkt_len; + m_nak_count += tap_info->num_sqns; + break; + case LBTRU_PACKET_TYPE_ACK: + m_ack_frames++; + m_ack_bytes += pinfo->fd->pkt_len; + break; + case LBTRU_PACKET_TYPE_CREQ: + m_creq_frames++; + m_creq_bytes += pinfo->fd->pkt_len; + break; + } + + it = m_transports.find(tap_info->transport); + if (m_transports.end() == it) + { + transport = new LBMLBTRUReceiverTransportEntry(tap_info->transport); + m_transports.insert(tap_info->transport, transport); + addChild(transport); + sortChildren(Receiver_AddressTransport_Column, Qt::AscendingOrder); + } + else + { + transport = it.value(); + } + fillItem(); + transport->processPacket(pinfo, tap_info); +} + +void LBMLBTRUReceiverEntry::fillItem(void) +{ + nstime_t delta; + + nstime_delta(&delta, &m_last_frame_timestamp, &m_first_frame_timestamp); + setText(Receiver_NAKFrames_Column, QString("%1").arg(m_nak_frames)); + setTextAlignment(Receiver_NAKFrames_Column, Qt::AlignRight); + setText(Receiver_NAKCount_Column, QString("%1").arg(m_nak_count)); + setTextAlignment(Receiver_NAKCount_Column, Qt::AlignRight); + setText(Receiver_NAKBytes_Column, QString("%1").arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKBytes_Column, Qt::AlignRight); + setText(Receiver_NAKFramesCount_Column, QString("%1/%2").arg(m_nak_frames).arg(m_nak_count)); + setTextAlignment(Receiver_NAKFramesCount_Column, Qt::AlignHCenter); + setText(Receiver_NAKCountBytes_Column, QString("%1/%2").arg(m_nak_count).arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKCountBytes_Column, Qt::AlignHCenter); + setText(Receiver_NAKFramesBytes_Column, QString("%1/%2").arg(m_nak_frames).arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKFramesBytes_Column, Qt::AlignHCenter); + setText(Receiver_NAKFramesCountBytes_Column, QString("%1/%2/%3").arg(m_nak_frames).arg(m_nak_count).arg(m_nak_bytes)); + setTextAlignment(Receiver_NAKFramesCountBytes_Column, Qt::AlignHCenter); + setText(Receiver_NAKRate_Column, format_rate(delta, m_nak_bytes)); + setTextAlignment(Receiver_NAKRate_Column, Qt::AlignRight); + setText(Receiver_ACKFrames_Column, QString("%1").arg(m_ack_frames)); + setTextAlignment(Receiver_ACKFrames_Column, Qt::AlignRight); + setText(Receiver_ACKBytes_Column, QString("%1").arg(m_ack_bytes)); + setTextAlignment(Receiver_ACKBytes_Column, Qt::AlignRight); + setText(Receiver_ACKFramesBytes_Column, QString("%1/%2").arg(m_ack_frames).arg(m_ack_bytes)); + setTextAlignment(Receiver_ACKFramesBytes_Column, Qt::AlignHCenter); + setText(Receiver_ACKRate_Column, format_rate(delta, m_ack_bytes)); + setTextAlignment(Receiver_ACKRate_Column, Qt::AlignRight); + setText(Receiver_CREQFrames_Column, QString("%1").arg(m_creq_frames)); + setTextAlignment(Receiver_CREQFrames_Column, Qt::AlignRight); + setText(Receiver_CREQBytes_Column, QString("%1").arg(m_creq_bytes)); + setTextAlignment(Receiver_CREQBytes_Column, Qt::AlignRight); + setText(Receiver_CREQFramesBytes_Column, QString("%1/%2").arg(m_creq_frames).arg(m_creq_bytes)); + setTextAlignment(Receiver_CREQFramesBytes_Column, Qt::AlignHCenter); + setText(Receiver_CREQRate_Column, format_rate(delta, m_creq_bytes)); + setTextAlignment(Receiver_CREQRate_Column, Qt::AlignRight); +} + +typedef QMap LBMLBTRUReceiverMap; +typedef QMap::iterator LBMLBTRUReceiverMapIterator; + +class LBMLBTRUTransportDialogInfo +{ + public: + LBMLBTRUTransportDialogInfo(void); + ~LBMLBTRUTransportDialogInfo(void); + void setDialog(LBMLBTRUTransportDialog * dialog); + LBMLBTRUTransportDialog * getDialog(void); + void processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info); + void clearMaps(void); + + private: + LBMLBTRUTransportDialog * m_dialog; + LBMLBTRUSourceMap m_sources; + LBMLBTRUReceiverMap m_receivers; +}; + +LBMLBTRUTransportDialogInfo::LBMLBTRUTransportDialogInfo(void) : + m_dialog(NULL), + m_sources(), + m_receivers() +{ +} + +LBMLBTRUTransportDialogInfo::~LBMLBTRUTransportDialogInfo(void) +{ + clearMaps(); +} + +void LBMLBTRUTransportDialogInfo::setDialog(LBMLBTRUTransportDialog * dialog) +{ + m_dialog = dialog; +} + +LBMLBTRUTransportDialog * LBMLBTRUTransportDialogInfo::getDialog(void) +{ + return (m_dialog); +} + +void LBMLBTRUTransportDialogInfo::processPacket(const packet_info * pinfo, const lbm_lbtru_tap_info_t * tap_info) +{ + switch (tap_info->type) + { + case LBTRU_PACKET_TYPE_DATA: + case LBTRU_PACKET_TYPE_SM: + case LBTRU_PACKET_TYPE_NCF: + case LBTRU_PACKET_TYPE_RST: + { + LBMLBTRUSourceEntry * source = NULL; + LBMLBTRUSourceMapIterator it; + QString src_address = address_to_qstring(&(pinfo->src)); + + it = m_sources.find(src_address); + if (m_sources.end() == it) + { + QTreeWidgetItem * parent = NULL; + Ui::LBMLBTRUTransportDialog * ui = NULL; + + source = new LBMLBTRUSourceEntry(src_address); + it = m_sources.insert(src_address, source); + ui = m_dialog->getUI(); + ui->sources_TreeWidget->addTopLevelItem(source); + parent = ui->sources_TreeWidget->invisibleRootItem(); + parent->sortChildren(Source_AddressTransport_Column, Qt::AscendingOrder); + ui->sources_TreeWidget->resizeColumnToContents(Source_AddressTransport_Column); + } + else + { + source = it.value(); + } + source->processPacket(pinfo, tap_info); + } + break; + case LBTRU_PACKET_TYPE_NAK: + case LBTRU_PACKET_TYPE_ACK: + case LBTRU_PACKET_TYPE_CREQ: + { + LBMLBTRUReceiverEntry * receiver = NULL; + LBMLBTRUReceiverMapIterator it; + QString src_address = address_to_qstring(&(pinfo->src)); + + it = m_receivers.find(src_address); + if (m_receivers.end() == it) + { + QTreeWidgetItem * parent = NULL; + Ui::LBMLBTRUTransportDialog * ui = NULL; + + receiver = new LBMLBTRUReceiverEntry(src_address); + it = m_receivers.insert(src_address, receiver); + ui = m_dialog->getUI(); + ui->receivers_TreeWidget->addTopLevelItem(receiver); + parent = ui->receivers_TreeWidget->invisibleRootItem(); + parent->sortChildren(Receiver_AddressTransport_Column, Qt::AscendingOrder); + ui->receivers_TreeWidget->resizeColumnToContents(Receiver_AddressTransport_Column); + } + else + { + receiver = it.value(); + } + receiver->processPacket(pinfo, tap_info); + } + break; + default: + break; + } +} + +void LBMLBTRUTransportDialogInfo::clearMaps(void) +{ + for (LBMLBTRUSourceMapIterator it = m_sources.begin(); it != m_sources.end(); ++it) + { + delete *it; + } + m_sources.clear(); + + for (LBMLBTRUReceiverMapIterator it = m_receivers.begin(); it != m_receivers.end(); ++it) + { + delete *it; + } + m_receivers.clear(); +} + +LBMLBTRUTransportDialog::LBMLBTRUTransportDialog(QWidget * parent, capture_file * cfile) : + QDialog(parent), + m_ui(new Ui::LBMLBTRUTransportDialog), + m_dialog_info(NULL), + m_capture_file(cfile), + m_current_source_transport(NULL), + m_current_receiver_transport(NULL), + m_source_context_menu(NULL), + m_source_header(NULL) +{ + m_ui->setupUi(this); + + m_dialog_info = new LBMLBTRUTransportDialogInfo(); + m_ui->tabWidget->setCurrentIndex(0); + m_ui->sources_detail_ComboBox->setCurrentIndex(0); + m_ui->sources_detail_transport_Label->setText(QString(" ")); + m_ui->sources_stackedWidget->setCurrentIndex(0); + m_ui->receivers_detail_ComboBox->setCurrentIndex(0); + m_ui->receivers_detail_transport_Label->setText(QString(" ")); + m_ui->receivers_stackedWidget->setCurrentIndex(0); + + // Setup the source context menu + m_source_header = m_ui->sources_TreeWidget->header(); + m_source_context_menu = new QMenu(m_source_header); + + m_source_context_menu->addAction(m_ui->action_SourceAutoResizeColumns); + connect(m_ui->action_SourceAutoResizeColumns, SIGNAL(triggered()), this, SLOT(actionSourceAutoResizeColumns_triggered())); + m_source_context_menu->addSeparator(); + + m_ui->action_SourceDataFrames->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceDataFrames); + connect(m_ui->action_SourceDataFrames, SIGNAL(triggered(bool)), this, SLOT(actionSourceDataFrames_triggered(bool))); + m_ui->action_SourceDataBytes->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceDataBytes); + connect(m_ui->action_SourceDataBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceDataBytes_triggered(bool))); + m_ui->action_SourceDataFramesBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceDataFramesBytes); + connect(m_ui->action_SourceDataFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceDataFramesBytes_triggered(bool))); + m_ui->action_SourceDataRate->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceDataRate); + connect(m_ui->action_SourceDataRate, SIGNAL(triggered(bool)), this, SLOT(actionSourceDataRate_triggered(bool))); + + m_ui->action_SourceRXDataFrames->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceRXDataFrames); + connect(m_ui->action_SourceRXDataFrames, SIGNAL(triggered(bool)), this, SLOT(actionSourceRXDataFrames_triggered(bool))); + m_ui->action_SourceRXDataBytes->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceRXDataBytes); + connect(m_ui->action_SourceRXDataBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceRXDataBytes_triggered(bool))); + m_ui->action_SourceRXDataFramesBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceRXDataFramesBytes); + connect(m_ui->action_SourceRXDataFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceRXDataFramesBytes_triggered(bool))); + m_ui->action_SourceRXDataRate->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceRXDataRate); + connect(m_ui->action_SourceRXDataRate, SIGNAL(triggered(bool)), this, SLOT(actionSourceRXDataRate_triggered(bool))); + + m_ui->action_SourceNCFFrames->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceNCFFrames); + connect(m_ui->action_SourceNCFFrames, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFFrames_triggered(bool))); + m_ui->action_SourceNCFCount->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceNCFCount); + connect(m_ui->action_SourceNCFCount, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFCount_triggered(bool))); + m_ui->action_SourceNCFBytes->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceNCFBytes); + connect(m_ui->action_SourceNCFBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFBytes_triggered(bool))); + m_ui->action_SourceNCFFramesBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceNCFFramesBytes); + connect(m_ui->action_SourceNCFFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFFramesBytes_triggered(bool))); + m_ui->action_SourceNCFCountBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceNCFCountBytes); + connect(m_ui->action_SourceNCFCountBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFCountBytes_triggered(bool))); + m_ui->action_SourceNCFFramesCount->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceNCFFramesCount); + connect(m_ui->action_SourceNCFFramesCount, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFFramesCount_triggered(bool))); + m_ui->action_SourceNCFFramesCountBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceNCFFramesCountBytes); + connect(m_ui->action_SourceNCFFramesCountBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFFramesCountBytes_triggered(bool))); + m_ui->action_SourceNCFRate->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceNCFRate); + connect(m_ui->action_SourceNCFRate, SIGNAL(triggered(bool)), this, SLOT(actionSourceNCFRate_triggered(bool))); + + m_ui->action_SourceSMFrames->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceSMFrames); + connect(m_ui->action_SourceSMFrames, SIGNAL(triggered(bool)), this, SLOT(actionSourceSMFrames_triggered(bool))); + m_ui->action_SourceSMBytes->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceSMBytes); + connect(m_ui->action_SourceSMBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceSMBytes_triggered(bool))); + m_ui->action_SourceSMFramesBytes->setChecked(false); + m_source_context_menu->addAction(m_ui->action_SourceSMFramesBytes); + connect(m_ui->action_SourceSMFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionSourceSMFramesBytes_triggered(bool))); + m_ui->action_SourceSMRate->setChecked(true); + m_source_context_menu->addAction(m_ui->action_SourceSMRate); + connect(m_ui->action_SourceSMRate, SIGNAL(triggered(bool)), this, SLOT(actionSourceSMRate_triggered(bool))); + + m_source_header->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_source_header, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(custom_source_context_menuRequested(const QPoint &))); + + // Setup the receiver context menu + m_receiver_header = m_ui->receivers_TreeWidget->header(); + m_receiver_context_menu = new QMenu(m_receiver_header); + + m_receiver_context_menu->addAction(m_ui->action_ReceiverAutoResizeColumns); + connect(m_ui->action_ReceiverAutoResizeColumns, SIGNAL(triggered()), this, SLOT(actionReceiverAutoResizeColumns_triggered())); + m_receiver_context_menu->addSeparator(); + + m_ui->action_ReceiverNAKFrames->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverNAKFrames); + connect(m_ui->action_ReceiverNAKFrames, SIGNAL(triggered(bool)), this, SLOT(actionReceiverNAKFrames_triggered(bool))); + m_ui->action_ReceiverNAKCount->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverNAKCount); + connect(m_ui->action_ReceiverNAKCount, SIGNAL(triggered(bool)), this, SLOT(actionReceiverNAKCount_triggered(bool))); + m_ui->action_ReceiverNAKBytes->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverNAKBytes); + connect(m_ui->action_ReceiverNAKBytes, SIGNAL(triggered(bool)), this, SLOT(actionReceiverNAKBytes_triggered(bool))); + m_ui->action_ReceiverNAKFramesBytes->setChecked(false); + m_receiver_context_menu->addAction(m_ui->action_ReceiverNAKFramesBytes); + connect(m_ui->action_ReceiverNAKFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionReceiverNAKFramesBytes_triggered(bool))); + m_ui->action_ReceiverNAKCountBytes->setChecked(false); + m_receiver_context_menu->addAction(m_ui->action_ReceiverNAKCountBytes); + connect(m_ui->action_ReceiverNAKCountBytes, SIGNAL(triggered(bool)), this, SLOT(actionReceiverNAKCountBytes_triggered(bool))); + m_ui->action_ReceiverNAKFramesCount->setChecked(false); + m_receiver_context_menu->addAction(m_ui->action_ReceiverNAKFramesCount); + connect(m_ui->action_ReceiverNAKFramesCount, SIGNAL(triggered(bool)), this, SLOT(actionReceiverNAKFramesCount_triggered(bool))); + m_ui->action_ReceiverNAKFramesCountBytes->setChecked(false); + m_receiver_context_menu->addAction(m_ui->action_ReceiverNAKFramesCountBytes); + connect(m_ui->action_ReceiverNAKFramesCountBytes, SIGNAL(triggered(bool)), this, SLOT(actionReceiverNAKFramesCountBytes_triggered(bool))); + m_ui->action_ReceiverNAKRate->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverNAKRate); + connect(m_ui->action_ReceiverNAKRate, SIGNAL(triggered(bool)), this, SLOT(actionReceiverNAKRate_triggered(bool))); + + m_ui->action_ReceiverACKFrames->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverACKFrames); + connect(m_ui->action_ReceiverACKFrames, SIGNAL(triggered(bool)), this, SLOT(actionReceiverACKFrames_triggered(bool))); + m_ui->action_ReceiverACKBytes->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverACKBytes); + connect(m_ui->action_ReceiverACKBytes, SIGNAL(triggered(bool)), this, SLOT(actionReceiverACKBytes_triggered(bool))); + m_ui->action_ReceiverACKFramesBytes->setChecked(false); + m_receiver_context_menu->addAction(m_ui->action_ReceiverACKFramesBytes); + connect(m_ui->action_ReceiverACKFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionReceiverACKFramesBytes_triggered(bool))); + m_ui->action_ReceiverACKRate->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverACKRate); + connect(m_ui->action_ReceiverACKRate, SIGNAL(triggered(bool)), this, SLOT(actionReceiverACKRate_triggered(bool))); + + m_ui->action_ReceiverCREQFrames->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverCREQFrames); + connect(m_ui->action_ReceiverCREQFrames, SIGNAL(triggered(bool)), this, SLOT(actionReceiverCREQFrames_triggered(bool))); + m_ui->action_ReceiverCREQBytes->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverCREQBytes); + connect(m_ui->action_ReceiverCREQBytes, SIGNAL(triggered(bool)), this, SLOT(actionReceiverCREQBytes_triggered(bool))); + m_ui->action_ReceiverCREQFramesBytes->setChecked(false); + m_receiver_context_menu->addAction(m_ui->action_ReceiverCREQFramesBytes); + connect(m_ui->action_ReceiverCREQFramesBytes, SIGNAL(triggered(bool)), this, SLOT(actionReceiverCREQFramesBytes_triggered(bool))); + m_ui->action_ReceiverCREQRate->setChecked(true); + m_receiver_context_menu->addAction(m_ui->action_ReceiverCREQRate); + connect(m_ui->action_ReceiverCREQRate, SIGNAL(triggered(bool)), this, SLOT(actionReceiverCREQRate_triggered(bool))); + + m_receiver_header->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_receiver_header, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(custom_receiver_context_menuRequested(const QPoint &))); + + // Setup the source tree widget header + m_ui->sources_TreeWidget->setColumnHidden(Source_DataFramesBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataFramesBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFCountBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesCount_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesCountBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_SMFramesBytes_Column, true); + m_ui->sources_TreeWidget->setColumnHidden(Source_RSTFramesBytes_Column, true); + + // Setup the receiver tree widget header + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKFramesBytes_Column, true); + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKCountBytes_Column, true); + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKFramesCount_Column, true); + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKFramesCountBytes_Column, true); + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_ACKFramesBytes_Column, true); + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_CREQFramesBytes_Column, true); + + setAttribute(Qt::WA_DeleteOnClose, true); + fillTree(); +} + +LBMLBTRUTransportDialog::~LBMLBTRUTransportDialog(void) +{ + resetSourcesDetail(); + resetSources(); + resetReceiversDetail(); + resetReceivers(); + if (m_dialog_info != NULL) + { + delete m_dialog_info; + m_dialog_info = NULL; + } + delete m_source_context_menu; + m_source_context_menu = NULL; + delete m_ui; + m_ui = NULL; + m_capture_file = NULL; +} + +void LBMLBTRUTransportDialog::setCaptureFile(capture_file * cfile) +{ + if (cfile == NULL) // We only want to know when the file closes. + { + m_capture_file = NULL; + m_ui->displayFilterLineEdit->setEnabled(false); + m_ui->applyFilterButton->setEnabled(false); + } +} + +void LBMLBTRUTransportDialog::resetSources(void) +{ + while (m_ui->sources_TreeWidget->takeTopLevelItem(0) != NULL) + {} +} + +void LBMLBTRUTransportDialog::resetReceivers(void) +{ + while (m_ui->receivers_TreeWidget->takeTopLevelItem(0) != NULL) + {} +} + +void LBMLBTRUTransportDialog::resetSourcesDetail(void) +{ + while (m_ui->sources_detail_sqn_TreeWidget->takeTopLevelItem(0) != NULL) + {} + while (m_ui->sources_detail_ncf_sqn_TreeWidget->takeTopLevelItem(0) != NULL) + {} + while (m_ui->sources_detail_rst_TreeWidget->takeTopLevelItem(0) != NULL) + {} + m_ui->sources_detail_transport_Label->setText(QString(" ")); + m_current_source_transport = NULL; +} + +void LBMLBTRUTransportDialog::resetReceiversDetail(void) +{ + while (m_ui->receivers_detail_sqn_TreeWidget->takeTopLevelItem(0) != NULL) + {} + while (m_ui->receivers_detail_reason_TreeWidget->takeTopLevelItem(0) != NULL) + {} + m_ui->receivers_detail_transport_Label->setText(QString(" ")); + m_current_receiver_transport = NULL; +} + +void LBMLBTRUTransportDialog::fillTree(void) +{ + GString * error_string; + + if (m_capture_file == NULL) + { + return; + } + m_dialog_info->setDialog(this); + + error_string = register_tap_listener("lbm_lbtru", + (void *)m_dialog_info, + m_ui->displayFilterLineEdit->text().toUtf8().constData(), + TL_REQUIRES_COLUMNS, + resetTap, + tapPacket, + drawTreeItems, + NULL); + if (error_string) + { + QMessageBox::critical(this, tr("LBT-RU Statistics failed to attach to tap"), + error_string->str); + g_string_free(error_string, TRUE); + reject(); + } + + cf_retap_packets(m_capture_file); + drawTreeItems(&m_dialog_info); + remove_tap_listener((void *)m_dialog_info); +} + +void LBMLBTRUTransportDialog::resetTap(void * tap_data) +{ + LBMLBTRUTransportDialogInfo * info = (LBMLBTRUTransportDialogInfo *)tap_data; + LBMLBTRUTransportDialog * dialog = info->getDialog(); + if (dialog == NULL) + { + return; + } + dialog->resetSourcesDetail(); + dialog->resetSources(); + dialog->resetReceiversDetail(); + dialog->resetReceivers(); + info->clearMaps(); +} + +tap_packet_status LBMLBTRUTransportDialog::tapPacket(void * tap_data, packet_info * pinfo, epan_dissect_t *, const void * tap_info, tap_flags_t) +{ + if (pinfo->fd->passed_dfilter == 1) + { + const lbm_lbtru_tap_info_t * tapinfo = (const lbm_lbtru_tap_info_t *)tap_info; + LBMLBTRUTransportDialogInfo * info = (LBMLBTRUTransportDialogInfo *)tap_data; + + info->processPacket(pinfo, tapinfo); + } + return (TAP_PACKET_REDRAW); +} + +void LBMLBTRUTransportDialog::drawTreeItems(void *) +{ +} + +void LBMLBTRUTransportDialog::on_applyFilterButton_clicked(void) +{ + fillTree(); +} + +void LBMLBTRUTransportDialog::sourcesDetailCurrentChanged(int index) +{ + // Index 0: Data + // Index 1: RX data + // Index 2: NCF + // Index 3: SM + // Index 4: RST + switch (index) + { + case 0: + case 1: + case 3: + m_ui->sources_stackedWidget->setCurrentIndex(0); + break; + case 2: + m_ui->sources_stackedWidget->setCurrentIndex(2); + break; + case 4: + m_ui->sources_stackedWidget->setCurrentIndex(1); + break; + default: + return; + } + sourcesItemClicked(m_current_source_transport, 0); +} + +void LBMLBTRUTransportDialog::sourcesItemClicked(QTreeWidgetItem * item, int) +{ + LBMLBTRUSourceTransportEntry * transport = dynamic_cast(item); + + resetSourcesDetail(); + if (transport == NULL) + { + // Must be a source item, ignore it? + return; + } + m_current_source_transport = transport; + m_ui->sources_detail_transport_Label->setText(transport->m_transport); + int cur_idx = m_ui->sources_detail_ComboBox->currentIndex(); + switch (cur_idx) + { + case 0: + loadSourceDataDetails(transport); + break; + case 1: + loadSourceRXDataDetails(transport); + break; + case 2: + loadSourceNCFDetails(transport); + break; + case 3: + loadSourceSMDetails(transport); + break; + case 4: + loadSourceRSTDetails(transport); + break; + default: + break; + } +} + +void LBMLBTRUTransportDialog::loadSourceDataDetails(LBMLBTRUSourceTransportEntry * transport) +{ + for (LBMLBTRUSQNMapIterator it = transport->m_data_sqns.begin(); it != transport->m_data_sqns.end(); ++it) + { + LBMLBTRUSQNEntry * sqn = it.value(); + m_ui->sources_detail_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRUTransportDialog::loadSourceRXDataDetails(LBMLBTRUSourceTransportEntry * transport) +{ + for (LBMLBTRUSQNMapIterator it = transport->m_rx_data_sqns.begin(); it != transport->m_rx_data_sqns.end(); ++it) + { + LBMLBTRUSQNEntry * sqn = it.value(); + m_ui->sources_detail_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRUTransportDialog::loadSourceNCFDetails(LBMLBTRUSourceTransportEntry * transport) +{ + for (LBMLBTRUNCFSQNMapIterator it = transport->m_ncf_sqns.begin(); it != transport->m_ncf_sqns.end(); ++it) + { + LBMLBTRUNCFSQNEntry * sqn = it.value(); + m_ui->sources_detail_ncf_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRUTransportDialog::loadSourceSMDetails(LBMLBTRUSourceTransportEntry * transport) +{ + for (LBMLBTRUSQNMapIterator it = transport->m_sm_sqns.begin(); it != transport->m_sm_sqns.end(); ++it) + { + LBMLBTRUSQNEntry * sqn = it.value(); + m_ui->sources_detail_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRUTransportDialog::loadSourceRSTDetails(LBMLBTRUSourceTransportEntry * transport) +{ + for (LBMLBTRURSTReasonMapIterator it = transport->m_rst_reasons.begin(); it != transport->m_rst_reasons.end(); ++it) + { + LBMLBTRURSTReasonEntry * reason = it.value(); + m_ui->sources_detail_rst_TreeWidget->addTopLevelItem(reason); + } +} + +void LBMLBTRUTransportDialog::sourcesDetailItemDoubleClicked(QTreeWidgetItem * item, int) +{ + LBMLBTRUFrameEntry * frame = dynamic_cast(item); + if (frame == NULL) + { + // Must have double-clicked on something other than an expanded frame entry + return; + } + emit goToPacket((int)frame->getFrame()); +} + +void LBMLBTRUTransportDialog::receiversDetailCurrentChanged(int index) +{ + // Index 0: NAK + // Index 1: ACK + // Index 2: CREQ + switch (index) + { + case 0: + case 1: + m_ui->receivers_stackedWidget->setCurrentIndex(0); + break; + case 2: + m_ui->receivers_stackedWidget->setCurrentIndex(1); + break; + default: + return; + } + receiversItemClicked(m_current_receiver_transport, 0); +} + +void LBMLBTRUTransportDialog::receiversItemClicked(QTreeWidgetItem * item, int) +{ + LBMLBTRUReceiverTransportEntry * transport = dynamic_cast(item); + + resetReceiversDetail(); + if (transport == NULL) + { + // Must be a receiver item, ignore it? + return; + } + m_current_receiver_transport = transport; + m_ui->receivers_detail_transport_Label->setText(transport->m_transport); + int cur_idx = m_ui->receivers_detail_ComboBox->currentIndex(); + switch (cur_idx) + { + case 0: + loadReceiverNAKDetails(transport); + break; + case 1: + loadReceiverACKDetails(transport); + break; + case 2: + loadReceiverCREQDetails(transport); + break; + default: + break; + } +} + +void LBMLBTRUTransportDialog::loadReceiverNAKDetails(LBMLBTRUReceiverTransportEntry * transport) +{ + for (LBMLBTRUSQNMapIterator it = transport->m_nak_sqns.begin(); it != transport->m_nak_sqns.end(); ++it) + { + LBMLBTRUSQNEntry * sqn = it.value(); + m_ui->receivers_detail_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRUTransportDialog::loadReceiverACKDetails(LBMLBTRUReceiverTransportEntry * transport) +{ + for (LBMLBTRUSQNMapIterator it = transport->m_ack_sqns.begin(); it != transport->m_ack_sqns.end(); ++it) + { + LBMLBTRUSQNEntry * sqn = it.value(); + m_ui->receivers_detail_sqn_TreeWidget->addTopLevelItem(sqn); + } +} + +void LBMLBTRUTransportDialog::loadReceiverCREQDetails(LBMLBTRUReceiverTransportEntry * transport) +{ + for (LBMLBTRUCREQRequestMapIterator it = transport->m_creq_requests.begin(); it != transport->m_creq_requests.end(); ++it) + { + LBMLBTRUCREQRequestEntry * req = it.value(); + m_ui->receivers_detail_reason_TreeWidget->addTopLevelItem(req); + } +} + +void LBMLBTRUTransportDialog::receiversDetailItemDoubleClicked(QTreeWidgetItem * item, int) +{ + LBMLBTRUFrameEntry * frame = dynamic_cast(item); + if (frame == NULL) + { + // Must have double-clicked on something other than an expanded frame entry + return; + } + emit goToPacket((int)frame->getFrame()); +} + +void LBMLBTRUTransportDialog::custom_source_context_menuRequested(const QPoint & pos) +{ + m_source_context_menu->popup(m_source_header->mapToGlobal(pos)); +} + +void LBMLBTRUTransportDialog::actionSourceDataFrames_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_DataFrames_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceDataBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_DataBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceDataFramesBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_DataFramesBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceDataRate_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_DataRate_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceRXDataFrames_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataFrames_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceRXDataBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceRXDataFramesBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataFramesBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceRXDataRate_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_RXDataRate_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceNCFFrames_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFrames_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceNCFCount_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFCount_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceNCFBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFrames_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceNCFFramesBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceNCFCountBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFCountBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceNCFFramesCount_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesCount_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceNCFFramesCountBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFFramesCountBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceNCFRate_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_NCFRate_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceSMFrames_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_SMFrames_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceSMBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_SMBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceSMFramesBytes_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_SMFramesBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceSMRate_triggered(bool checked) +{ + m_ui->sources_TreeWidget->setColumnHidden(Source_SMRate_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionSourceAutoResizeColumns_triggered(void) +{ + m_ui->sources_TreeWidget->resizeColumnToContents(Source_AddressTransport_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_DataFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_DataBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_DataFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_DataRate_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RXDataFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RXDataBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RXDataFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RXDataRate_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFCount_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFCountBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFFramesCount_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFFramesCountBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_NCFRate_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_SMFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_SMBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_SMFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_SMRate_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RSTFrames_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RSTBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RSTFramesBytes_Column); + m_ui->sources_TreeWidget->resizeColumnToContents(Source_RSTRate_Column); +} + +void LBMLBTRUTransportDialog::custom_receiver_context_menuRequested(const QPoint & pos) +{ + m_receiver_context_menu->popup(m_receiver_header->mapToGlobal(pos)); +} + +void LBMLBTRUTransportDialog::actionReceiverNAKFrames_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKFrames_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverNAKCount_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKCount_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverNAKBytes_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverNAKFramesCount_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKFramesCount_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverNAKCountBytes_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKCountBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverNAKFramesBytes_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKFramesBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverNAKFramesCountBytes_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKFramesCountBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverNAKRate_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_NAKRate_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverACKFrames_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_ACKFrames_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverACKBytes_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_ACKBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverACKFramesBytes_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_ACKFramesBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverACKRate_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_ACKRate_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverCREQFrames_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_CREQFrames_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverCREQBytes_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_CREQBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverCREQFramesBytes_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_CREQFramesBytes_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverCREQRate_triggered(bool checked) +{ + m_ui->receivers_TreeWidget->setColumnHidden(Receiver_CREQRate_Column, !checked); +} + +void LBMLBTRUTransportDialog::actionReceiverAutoResizeColumns_triggered(void) +{ + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_AddressTransport_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_NAKFrames_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_NAKCount_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_NAKBytes_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_NAKFramesBytes_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_NAKCountBytes_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_NAKFramesCount_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_NAKFramesCountBytes_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_NAKRate_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_ACKFrames_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_ACKBytes_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_ACKFramesBytes_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_ACKRate_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_CREQFrames_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_CREQBytes_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_CREQFramesBytes_Column); + m_ui->receivers_TreeWidget->resizeColumnToContents(Receiver_CREQRate_Column); +} diff --git a/ui/qt/lbm_lbtru_transport_dialog.h b/ui/qt/lbm_lbtru_transport_dialog.h new file mode 100644 index 00000000..56590102 --- /dev/null +++ b/ui/qt/lbm_lbtru_transport_dialog.h @@ -0,0 +1,134 @@ +/** @file + * + * Copyright (c) 2005-2014 Informatica Corporation. All Rights Reserved. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef LBM_LBTRU_TRANSPORT_DIALOG_H +#define LBM_LBTRU_TRANSPORT_DIALOG_H + +#include + +#include + +#include "cfile.h" +#include +#include +#include + +class QHeaderView; +class QMenu; +class QTreeWidgetItem; + +namespace Ui +{ + class LBMLBTRUTransportDialog; +} + +class LBMLBTRUTransportDialogInfo; +class LBMLBTRUSourceTransportEntry; +class LBMLBTRUReceiverTransportEntry; + +class LBMLBTRUTransportDialog : public QDialog +{ + Q_OBJECT + + public: + explicit LBMLBTRUTransportDialog(QWidget * parent = 0, capture_file * cfile = NULL); + Ui::LBMLBTRUTransportDialog * getUI(void) + { + return (m_ui); + } + public slots: + void setCaptureFile(capture_file * cfile); + + signals: + void goToPacket(int packet_num); + + private: + Ui::LBMLBTRUTransportDialog * m_ui; + LBMLBTRUTransportDialogInfo * m_dialog_info; + capture_file * m_capture_file; + LBMLBTRUSourceTransportEntry * m_current_source_transport; + LBMLBTRUReceiverTransportEntry * m_current_receiver_transport; + QMenu * m_source_context_menu; + QHeaderView * m_source_header; + QMenu * m_receiver_context_menu; + QHeaderView * m_receiver_header; + + virtual ~LBMLBTRUTransportDialog(void); + void resetSources(void); + void resetReceivers(void); + void resetSourcesDetail(void); + void resetReceiversDetail(void); + void fillTree(void); + static void resetTap(void * tap_data); + static tap_packet_status tapPacket(void * tap_data, packet_info * pinfo, epan_dissect_t * edt, const void * stream_info, tap_flags_t flags); + static void drawTreeItems(void * tap_data); + void loadSourceDataDetails(LBMLBTRUSourceTransportEntry * transport); + void loadSourceRXDataDetails(LBMLBTRUSourceTransportEntry * transport); + void loadSourceNCFDetails(LBMLBTRUSourceTransportEntry * transport); + void loadSourceSMDetails(LBMLBTRUSourceTransportEntry * transport); + void loadSourceRSTDetails(LBMLBTRUSourceTransportEntry * transport); + void loadReceiverNAKDetails(LBMLBTRUReceiverTransportEntry * transport); + void loadReceiverACKDetails(LBMLBTRUReceiverTransportEntry * transport); + void loadReceiverCREQDetails(LBMLBTRUReceiverTransportEntry * transport); + + private slots: + void on_applyFilterButton_clicked(void); + + void sourcesDetailCurrentChanged(int index); + void sourcesItemClicked(QTreeWidgetItem * item, int column); + void sourcesDetailItemDoubleClicked(QTreeWidgetItem * item, int column); + void receiversDetailCurrentChanged(int index); + void receiversItemClicked(QTreeWidgetItem * item, int column); + void receiversDetailItemDoubleClicked(QTreeWidgetItem * item, int column); + + void custom_source_context_menuRequested(const QPoint & pos); + void actionSourceDataFrames_triggered(bool checked); + void actionSourceDataBytes_triggered(bool checked); + void actionSourceDataFramesBytes_triggered(bool checked); + void actionSourceDataRate_triggered(bool checked); + void actionSourceRXDataFrames_triggered(bool checked); + void actionSourceRXDataBytes_triggered(bool checked); + void actionSourceRXDataFramesBytes_triggered(bool checked); + void actionSourceRXDataRate_triggered(bool checked); + void actionSourceNCFFrames_triggered(bool checked); + void actionSourceNCFCount_triggered(bool checked); + void actionSourceNCFBytes_triggered(bool checked); + void actionSourceNCFFramesBytes_triggered(bool checked); + void actionSourceNCFCountBytes_triggered(bool checked); + void actionSourceNCFFramesCount_triggered(bool checked); + void actionSourceNCFFramesCountBytes_triggered(bool checked); + void actionSourceNCFRate_triggered(bool checked); + void actionSourceSMFrames_triggered(bool checked); + void actionSourceSMBytes_triggered(bool checked); + void actionSourceSMFramesBytes_triggered(bool checked); + void actionSourceSMRate_triggered(bool checked); + void actionSourceAutoResizeColumns_triggered(void); + void custom_receiver_context_menuRequested(const QPoint & pos); + void actionReceiverNAKFrames_triggered(bool checked); + void actionReceiverNAKCount_triggered(bool checked); + void actionReceiverNAKBytes_triggered(bool checked); + void actionReceiverNAKFramesCount_triggered(bool checked); + void actionReceiverNAKCountBytes_triggered(bool checked); + void actionReceiverNAKFramesBytes_triggered(bool checked); + void actionReceiverNAKFramesCountBytes_triggered(bool checked); + void actionReceiverNAKRate_triggered(bool checked); + void actionReceiverACKFrames_triggered(bool checked); + void actionReceiverACKBytes_triggered(bool checked); + void actionReceiverACKFramesBytes_triggered(bool checked); + void actionReceiverACKRate_triggered(bool checked); + void actionReceiverCREQFrames_triggered(bool checked); + void actionReceiverCREQBytes_triggered(bool checked); + void actionReceiverCREQFramesBytes_triggered(bool checked); + void actionReceiverCREQRate_triggered(bool checked); + void actionReceiverAutoResizeColumns_triggered(void); +}; + +#endif diff --git a/ui/qt/lbm_lbtru_transport_dialog.ui b/ui/qt/lbm_lbtru_transport_dialog.ui new file mode 100644 index 00000000..2effe781 --- /dev/null +++ b/ui/qt/lbm_lbtru_transport_dialog.ui @@ -0,0 +1,1300 @@ + + + LBMLBTRUTransportDialog + + + + 0 + 0 + 872 + 667 + + + + LBT-RU Transport Statistics + + + true + + + + + + 0 + + + + + 0 + 0 + + + + Sources + + + + + + Qt::Vertical + + + 10 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + 80 + + + + Address/Transport/Client + + + + + Data frames + + + + + Data bytes + + + + + Data frames/bytes + + + + + Data rate + + + + + RX data frames + + + + + RX data bytes + + + + + RX data frames/bytes + + + + + RX data rate + + + + + NCF frames + + + + + NCF count + + + + + NCF bytes + + + + + NCF frames/count + + + + + NCF frames/bytes + + + + + NCF count/bytes + + + + + NCF frames/count/bytes + + + + + NCF rate + + + + + SM frames + + + + + SM bytes + + + + + SM frames/bytes + + + + + SM rate + + + + + RST frames + + + + + RST bytes + + + + + RST frames/bytes + + + + + RST rate + + + + + + + + + + + Show + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + Data SQN + + + + + RX Data SQN + + + + + NCF SQN + + + + + SM SQN + + + + + RST reason + + + + + + + + details for transport + + + + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + true + + + QFrame::NoFrame + + + 1 + + + 1 + + + + + + + + SQN + + + + + Count + + + + + Frame + + + + + + + + + + + + + Reason + + + + + Count + + + + + Frame + + + + + + + + + + + + + SQN/Reason + + + + + Count + + + + + Frame + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Receivers + + + + + + Qt::Vertical + + + 10 + + + + + Address/Transport + + + + + NAK frames + + + + + NAK count + + + + + NAK bytes + + + + + NAK frames/count + + + + + NAK count/bytes + + + + + NAK frames/bytes + + + + + NAK frames/count/bytes + + + + + NAK rate + + + + + ACK frames + + + + + ACK bytes + + + + + ACK frames/bytes + + + + + ACK rate + + + + + CREQ frames + + + + + CREQ bytes + + + + + CREQ frames/bytes + + + + + CREQ rate + + + + + + + + + + + Show + + + + + + + + 130 + 0 + + + + + NAK SQN + + + + + ACK SQN + + + + + CREQ request + + + + + + + + details for transport + + + + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + + + 0 + 0 + + + + + + + + SQN + + + + + Count + + + + + Frame + + + + + + + + + + 0 + 0 + + + + + + + + Reason + + + + + Count + + + + + Frame + + + + + + + + + + + + + + + + + + + + 0 + + + + + Display filter: + + + + + + + + + + Regenerate statistics using this display filter + + + Apply + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Copy as CSV + + + Copy the tree as CSV + + + Ctrl+C + + + + + Copy as YAML + + + Copy the tree as YAML + + + Ctrl+Y + + + + + true + + + Data frames + + + Show the data frames column + + + + + true + + + Data bytes + + + Show the data bytes column + + + + + true + + + Data frames/bytes + + + Show the data frames/bytes column + + + + + true + + + Data rate + + + Show the data rate column + + + + + true + + + RX data frames + + + Show the RX data frames column + + + + + true + + + RX data bytes + + + Show the RX data bytes column + + + + + true + + + RX data frames/bytes + + + Show the RX data frames/bytes column + + + + + true + + + RX data rate + + + Show the RX data rate column + + + + + true + + + NCF frames + + + Show the NCF frames column + + + + + true + + + NCF count + + + Show the NCF count column + + + + + true + + + NCF bytes + + + Show the NCF bytes column + + + + + true + + + NCF frames/bytes + + + Show the NCF frames/bytes column + + + + + true + + + NCF count/bytes + + + Show the NCF count/bytes column + + + + + true + + + NCF frames/count + + + Show the NCF frames/count column + + + + + true + + + NCF frames/count/bytes + + + Show the NCF frames/count/bytes column + + + + + true + + + SM frames + + + Show the SM frames column + + + + + true + + + SM bytes + + + Show the SM bytes column + + + + + true + + + SM frames/bytes + + + Show the SM frames/bytes column + + + + + true + + + SM rate + + + Show the SM rate column + + + + + true + + + RST frames + + + Show the RST frames column + + + + + true + + + RST bytes + + + Show the RST bytes column + + + + + true + + + RST frames/bytes + + + Show the RST frames/bytes column + + + + + true + + + RST rate + + + Show the RST rate column + + + + + true + + + NAK frames + + + Show the NAK frames column + + + + + true + + + NAK count + + + Show the NAK count column + + + + + true + + + NAK bytes + + + Show the NAK bytes column + + + + + true + + + NAK frames/count + + + Show the NAK frames/count column + + + + + true + + + NAK count/bytes + + + Show the NAK count/bytes column + + + + + true + + + NAK frames/bytes + + + Show the NAK frames/bytes column + + + + + true + + + NAK frames/count/bytes + + + Show the NAK frames/count/bytes column + + + + + true + + + NAK rate + + + Show the NAK rate column + + + + + true + + + ACK frames + + + Show the ACK frames column + + + + + true + + + ACK bytes + + + Show the ACK bytes column + + + + + true + + + ACK frames/bytes + + + Show the ACK frames/bytes column + + + + + true + + + ACK rate + + + Show the ACK rate column + + + + + true + + + CREQ frames + + + Show the CREQ frames column + + + + + true + + + CREQ bytes + + + Show the CREQ bytes column + + + + + true + + + CREQ frames/bytes + + + Show the CREQ frames/bytes column + + + + + true + + + CREQ rate + + + Show the CREQ rate column + + + + + Auto-resize columns to content + + + Resize columns to content size + + + + + Auto-resize columns to content + + + Resize columns to content size + + + + + true + + + NCF rate + + + Show the NCF rate column + + + + + + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + + buttonBox + accepted() + LBMLBTRUTransportDialog + accept() + + + 262 + 659 + + + 157 + 274 + + + + + buttonBox + rejected() + LBMLBTRUTransportDialog + reject() + + + 330 + 659 + + + 286 + 274 + + + + + sources_detail_ComboBox + currentIndexChanged(int) + LBMLBTRUTransportDialog + sourcesDetailCurrentChanged(int) + + + 175 + 315 + + + 446 + 310 + + + + + sources_TreeWidget + itemClicked(QTreeWidgetItem*,int) + LBMLBTRUTransportDialog + sourcesItemClicked(QTreeWidgetItem*,int) + + + 440 + 149 + + + 446 + 310 + + + + + sources_detail_sqn_TreeWidget + itemDoubleClicked(QTreeWidgetItem*,int) + LBMLBTRUTransportDialog + sourcesDetailItemDoubleClicked(QTreeWidgetItem*,int) + + + 109 + 344 + + + 420 + 281 + + + + + sources_detail_ncf_sqn_TreeWidget + itemDoubleClicked(QTreeWidgetItem*,int) + LBMLBTRUTransportDialog + sourcesDetailItemDoubleClicked(QTreeWidgetItem*,int) + + + 83 + 344 + + + 420 + 281 + + + + + receivers_TreeWidget + itemClicked(QTreeWidgetItem*,int) + LBMLBTRUTransportDialog + receiversItemClicked(QTreeWidgetItem*,int) + + + 420 + 141 + + + 420 + 281 + + + + + receivers_detail_ComboBox + currentIndexChanged(int) + LBMLBTRUTransportDialog + receiversDetailCurrentChanged(int) + + + 124 + 315 + + + 155 + -8 + + + + + receivers_detail_sqn_TreeWidget + itemDoubleClicked(QTreeWidgetItem*,int) + LBMLBTRUTransportDialog + receiversDetailItemDoubleClicked(QTreeWidgetItem*,int) + + + 435 + 452 + + + 435 + 333 + + + + + receivers_detail_reason_TreeWidget + itemDoubleClicked(QTreeWidgetItem*,int) + LBMLBTRUTransportDialog + receiversDetailItemDoubleClicked(QTreeWidgetItem*,int) + + + 66 + 336 + + + 435 + 333 + + + + + + goToPacket(int) + sourcesDetailCurrentChanged(int) + sourcesItemClicked(QTreeWidgetItem*,int) + receiversItemClicked(QTreeWidgetItem*,int) + sourcesDetailItemDoubleClicked(QTreeWidgetItem*,int) + receiversDetailItemDoubleClicked(QTreeWidgetItem*,int) + receiversDetailCurrentChanged(int) + +
diff --git a/ui/qt/lbm_stream_dialog.cpp b/ui/qt/lbm_stream_dialog.cpp new file mode 100644 index 00000000..65ddcc47 --- /dev/null +++ b/ui/qt/lbm_stream_dialog.cpp @@ -0,0 +1,420 @@ +/* lbm_stream_dialog.cpp + * + * Copyright (c) 2005-2014 Informatica Corporation. All Rights Reserved. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +// Adapted from stats_tree_packet.cpp + +#include "lbm_stream_dialog.h" +#include + +#include "file.h" + +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + static const int Stream_Column = 0; + static const int EndpointA_Column = 1; + static const int EndpointB_Column = 2; + static const int Messages_Column = 3; + static const int Bytes_Column = 4; + static const int FirstFrame_Column = 5; + static const int LastFrame_Column = 6; +} + +class LBMSubstreamEntry +{ + public: + LBMSubstreamEntry(guint64 channel, guint32 substream_id, const address * source_address, guint16 source_port, const address * destination_address, guint16 destination_port); + ~LBMSubstreamEntry(void); + void processPacket(guint32 frame, guint32 bytes); + void setItem(QTreeWidgetItem * item); + QTreeWidgetItem * getItem(void) + { + return (m_item); + } + + private: + void fillItem(gboolean update_only = TRUE); + guint64 m_channel; + guint32 m_substream_id; + QString m_endpoint_a; + QString m_endpoint_b; + guint32 m_first_frame; + guint32 m_flast_frame; + guint32 m_messages; + guint32 m_bytes; + QTreeWidgetItem * m_item; +}; + +LBMSubstreamEntry::LBMSubstreamEntry(guint64 channel, guint32 substream_id, const address * source_address, guint16 source_port, const address * destination_address, guint16 destination_port) : + m_channel(channel), + m_substream_id(substream_id), + m_first_frame((guint32)(~0)), + m_flast_frame(0), + m_messages(0), + m_bytes(0), + m_item(NULL) +{ + m_endpoint_a = QString("%1:%2") + .arg(address_to_qstring(source_address)) + .arg(source_port); + m_endpoint_b = QString("%1:%2") + .arg(address_to_qstring(destination_address)) + .arg(destination_port); +} + +LBMSubstreamEntry::~LBMSubstreamEntry(void) +{ +} + +void LBMSubstreamEntry::processPacket(guint32 frame, guint32 bytes) +{ + if (m_first_frame > frame) + { + m_first_frame = frame; + } + if (m_flast_frame < frame) + { + m_flast_frame = frame; + } + m_bytes += bytes; + m_messages++; + fillItem(); +} + +void LBMSubstreamEntry::setItem(QTreeWidgetItem * item) +{ + m_item = item; + fillItem(FALSE); +} + +void LBMSubstreamEntry::fillItem(gboolean update_only) +{ + if (update_only == FALSE) + { + m_item->setText(Stream_Column, QString("%1.%2").arg(m_channel).arg(m_substream_id)); + m_item->setText(EndpointA_Column, m_endpoint_a); + m_item->setText(EndpointB_Column, m_endpoint_b); + } + m_item->setText(Messages_Column, QString("%1").arg(m_messages)); + m_item->setText(Bytes_Column, QString("%1").arg(m_bytes)); + m_item->setText(FirstFrame_Column, QString("%1").arg(m_first_frame)); + m_item->setText(LastFrame_Column, QString("%1").arg(m_flast_frame)); +} + +typedef QMap LBMSubstreamMap; +typedef QMap::iterator LBMSubstreamMapIterator; + +class LBMStreamEntry +{ + public: + LBMStreamEntry(const packet_info * pinfo, guint64 channel, const lbm_uim_stream_endpoint_t * endpoint_a, const lbm_uim_stream_endpoint_t * endpoint_b); + ~LBMStreamEntry(void); + void processPacket(const packet_info * pinfo, const lbm_uim_stream_tap_info_t * stream_info); + void setItem(QTreeWidgetItem * item); + QTreeWidgetItem * getItem(void) + { + return (m_item); + } + + private: + void fillItem(gboolean update_only = TRUE); + QString formatEndpoint(const packet_info * pinfo, const lbm_uim_stream_endpoint_t * endpoint); + guint64 m_channel; + QString m_endpoint_a; + QString m_endpoint_b; + guint32 m_first_frame; + guint32 m_flast_frame; + guint32 m_messages; + guint32 m_bytes; + QTreeWidgetItem * m_item; + LBMSubstreamMap m_substreams; +}; + +LBMStreamEntry::LBMStreamEntry(const packet_info * pinfo, guint64 channel, const lbm_uim_stream_endpoint_t * endpoint_a, const lbm_uim_stream_endpoint_t * endpoint_b) : + m_channel(channel), + m_first_frame((guint32)(~0)), + m_flast_frame(0), + m_messages(0), + m_bytes(0), + m_item(NULL), + m_substreams() +{ + m_endpoint_a = formatEndpoint(pinfo, endpoint_a); + m_endpoint_b = formatEndpoint(pinfo, endpoint_b); +} + +LBMStreamEntry::~LBMStreamEntry(void) +{ + LBMSubstreamMapIterator it; + + for (it = m_substreams.begin(); it != m_substreams.end(); ++it) + { + delete *it; + } + m_substreams.clear(); +} + +QString LBMStreamEntry::formatEndpoint(const packet_info * pinfo, const lbm_uim_stream_endpoint_t * endpoint) +{ + if (endpoint->type == lbm_uim_instance_stream) + { + return QString(bytes_to_str(pinfo->pool, endpoint->stream_info.ctxinst.ctxinst, sizeof(endpoint->stream_info.ctxinst.ctxinst))); + } + else + { + return QString("%1:%2:%3") + .arg(endpoint->stream_info.dest.domain) + .arg(address_to_str(pinfo->pool, &(endpoint->stream_info.dest.addr))) + .arg(endpoint->stream_info.dest.port); + } +} + +void LBMStreamEntry::processPacket(const packet_info * pinfo, const lbm_uim_stream_tap_info_t * stream_info) +{ + LBMSubstreamEntry * substream = NULL; + LBMSubstreamMapIterator it; + + if (m_first_frame > pinfo->num) + { + m_first_frame = pinfo->num; + } + if (m_flast_frame < pinfo->num) + { + m_flast_frame = pinfo->num; + } + m_bytes += stream_info->bytes; + m_messages++; + it = m_substreams.find(stream_info->substream_id); + if (m_substreams.end() == it) + { + QTreeWidgetItem * item = NULL; + + substream = new LBMSubstreamEntry(m_channel, stream_info->substream_id, &(pinfo->src), pinfo->srcport, &(pinfo->dst), pinfo->destport); + m_substreams.insert(stream_info->substream_id, substream); + item = new QTreeWidgetItem(); + substream->setItem(item); + m_item->addChild(item); + m_item->sortChildren(Stream_Column, Qt::AscendingOrder); + } + else + { + substream = it.value(); + } + fillItem(); + substream->processPacket(pinfo->num, stream_info->bytes); +} + +void LBMStreamEntry::setItem(QTreeWidgetItem * item) +{ + m_item = item; + fillItem(FALSE); +} + +void LBMStreamEntry::fillItem(gboolean update_only) +{ + if (update_only == FALSE) + { + m_item->setData(Stream_Column, Qt::DisplayRole, QVariant((qulonglong)m_channel)); + m_item->setText(EndpointA_Column, m_endpoint_a); + m_item->setText(EndpointB_Column, m_endpoint_b); + } + m_item->setText(Messages_Column, QString("%1").arg(m_messages)); + m_item->setText(Bytes_Column, QString("%1").arg(m_bytes)); + m_item->setText(FirstFrame_Column, QString("%1").arg(m_first_frame)); + m_item->setText(LastFrame_Column, QString("%1").arg(m_flast_frame)); +} + +typedef QMap LBMStreamMap; +typedef QMap::iterator LBMStreamMapIterator; + +class LBMStreamDialogInfo +{ + public: + LBMStreamDialogInfo(void); + ~LBMStreamDialogInfo(void); + void setDialog(LBMStreamDialog * dialog); + LBMStreamDialog * getDialog(void); + void processPacket(const packet_info * pinfo, const lbm_uim_stream_tap_info_t * stream_info); + void resetStreams(void); + + private: + LBMStreamDialog * m_dialog; + LBMStreamMap m_streams; +}; + +LBMStreamDialogInfo::LBMStreamDialogInfo(void) : + m_dialog(NULL), + m_streams() +{ +} + +LBMStreamDialogInfo::~LBMStreamDialogInfo(void) +{ + resetStreams(); +} + +void LBMStreamDialogInfo::setDialog(LBMStreamDialog * dialog) +{ + m_dialog = dialog; +} + +LBMStreamDialog * LBMStreamDialogInfo::getDialog(void) +{ + return (m_dialog); +} + +void LBMStreamDialogInfo::processPacket(const packet_info * pinfo, const lbm_uim_stream_tap_info_t * stream_info) +{ + LBMStreamEntry * stream = NULL; + LBMStreamMapIterator it; + + it = m_streams.find(stream_info->channel); + if (m_streams.end() == it) + { + QTreeWidgetItem * item = NULL; + QTreeWidgetItem * parent = NULL; + Ui::LBMStreamDialog * ui = NULL; + + stream = new LBMStreamEntry(pinfo, stream_info->channel, &(stream_info->endpoint_a), &(stream_info->endpoint_b)); + it = m_streams.insert(stream_info->channel, stream); + item = new QTreeWidgetItem(); + stream->setItem(item); + ui = m_dialog->getUI(); + ui->lbm_stream_TreeWidget->addTopLevelItem(item); + parent = ui->lbm_stream_TreeWidget->invisibleRootItem(); + parent->sortChildren(Stream_Column, Qt::AscendingOrder); + } + else + { + stream = it.value(); + } + stream->processPacket(pinfo, stream_info); +} + +void LBMStreamDialogInfo::resetStreams(void) +{ + LBMStreamMapIterator it = m_streams.begin(); + + while (it != m_streams.end()) + { + delete *it; + ++it; + } + m_streams.clear(); +} + +LBMStreamDialog::LBMStreamDialog(QWidget * parent, capture_file * cfile) : + QDialog(parent), + m_ui(new Ui::LBMStreamDialog), + m_dialog_info(NULL), + m_capture_file(cfile) +{ + m_ui->setupUi(this); + m_dialog_info = new LBMStreamDialogInfo(); + setAttribute(Qt::WA_DeleteOnClose, true); + fillTree(); +} + +LBMStreamDialog::~LBMStreamDialog(void) +{ + delete m_ui; + if (m_dialog_info != NULL) + { + delete m_dialog_info; + } +} + +void LBMStreamDialog::setCaptureFile(capture_file * cfile) +{ + if (cfile == NULL) // We only want to know when the file closes. + { + m_capture_file = NULL; + m_ui->displayFilterLineEdit->setEnabled(false); + m_ui->applyFilterButton->setEnabled(false); + } +} + +void LBMStreamDialog::fillTree(void) +{ + GString * error_string; + + if (m_capture_file == NULL) + { + return; + } + m_dialog_info->setDialog(this); + + error_string = register_tap_listener("lbm_stream", + (void *)m_dialog_info, + m_ui->displayFilterLineEdit->text().toUtf8().constData(), + TL_REQUIRES_COLUMNS, + resetTap, + tapPacket, + drawTreeItems, + NULL); + if (error_string) + { + QMessageBox::critical(this, tr("LBM Stream failed to attach to tap"), + error_string->str); + g_string_free(error_string, TRUE); + reject(); + } + + cf_retap_packets(m_capture_file); + drawTreeItems(&m_dialog_info); + remove_tap_listener((void *)m_dialog_info); +} + +void LBMStreamDialog::resetTap(void * tap_data) +{ + LBMStreamDialogInfo * info = (LBMStreamDialogInfo *)tap_data; + LBMStreamDialog * dialog = info->getDialog(); + if (dialog == NULL) + { + return; + } + info->resetStreams(); + dialog->m_ui->lbm_stream_TreeWidget->clear(); +} + +tap_packet_status LBMStreamDialog::tapPacket(void * tap_data, packet_info * pinfo, epan_dissect_t *, const void * stream_info, tap_flags_t) +{ + if (pinfo->fd->passed_dfilter == 1) + { + const lbm_uim_stream_tap_info_t * tapinfo = (const lbm_uim_stream_tap_info_t *)stream_info; + LBMStreamDialogInfo * info = (LBMStreamDialogInfo *)tap_data; + + info->processPacket(pinfo, tapinfo); + } + return (TAP_PACKET_REDRAW); +} + +void LBMStreamDialog::drawTreeItems(void *) +{ +} + +void LBMStreamDialog::on_applyFilterButton_clicked(void) +{ + fillTree(); +} diff --git a/ui/qt/lbm_stream_dialog.h b/ui/qt/lbm_stream_dialog.h new file mode 100644 index 00000000..0bc75d29 --- /dev/null +++ b/ui/qt/lbm_stream_dialog.h @@ -0,0 +1,60 @@ +/** @file + * + * Copyright (c) 2005-2014 Informatica Corporation. All Rights Reserved. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef LBM_STREAM_DIALOG_H +#define LBM_STREAM_DIALOG_H + +#include + +#include + +#include "cfile.h" +#include +#include +#include + +namespace Ui +{ + class LBMStreamDialog; +} + +class LBMStreamDialogInfo; + +class LBMStreamDialog : public QDialog +{ + Q_OBJECT + + public: + explicit LBMStreamDialog(QWidget * parent = 0, capture_file * cfile = NULL); + ~LBMStreamDialog(void); + Ui::LBMStreamDialog * getUI(void) + { + return (m_ui); + } + + public slots: + void setCaptureFile(capture_file * cfile); + + private: + Ui::LBMStreamDialog * m_ui; + LBMStreamDialogInfo * m_dialog_info; + capture_file * m_capture_file; + + void fillTree(void); + static void resetTap(void * tap_data); + static tap_packet_status tapPacket(void * tap_data, packet_info * pinfo, epan_dissect_t * edt, const void * stream_info, tap_flags_t flags); + static void drawTreeItems(void * tap_data); + + private slots: + void on_applyFilterButton_clicked(void); +}; + +#endif diff --git a/ui/qt/lbm_stream_dialog.ui b/ui/qt/lbm_stream_dialog.ui new file mode 100644 index 00000000..f25881b3 --- /dev/null +++ b/ui/qt/lbm_stream_dialog.ui @@ -0,0 +1,158 @@ + + + LBMStreamDialog + + + + 0 + 0 + 652 + 459 + + + + Dialog + + + + + + + Stream + + + + + Endpoint A + + + + + Endpoint B + + + + + Messages + + + AlignLeft|AlignVCenter + + + + + Bytes + + + + + First Frame + + + + + Last Frame + + + + + + + + + + Display filter: + + + + + + + + + + Regenerate statistics using this display filter + + + Apply + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Copy as CSV + + + Copy the tree as CSV + + + Ctrl+C + + + + + Copy as YAML + + + Copy the tree as YAML + + + Ctrl+Y + + + + + + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + + buttonBox + accepted() + LBMStreamDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + LBMStreamDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/lte_mac_statistics_dialog.cpp b/ui/qt/lte_mac_statistics_dialog.cpp new file mode 100644 index 00000000..691128ea --- /dev/null +++ b/ui/qt/lte_mac_statistics_dialog.cpp @@ -0,0 +1,931 @@ +/* lte_mac_statistics_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "lte_mac_statistics_dialog.h" + +#include +#include +#include + +#include + +#include +#include + +#include +#include "main_application.h" + +// TODO: have never tested in a live capture. + +// Whole-UE headings. +enum { + col_rnti_, + col_type_, + col_ueid_, + // UL-specific + col_ul_frames_, + col_ul_bytes_, + col_ul_mb_s_, + col_ul_padding_percent_, + /* col_ul_crc_failed_, */ + col_ul_retx_, + // DL-specific + col_dl_frames_, + col_dl_bytes_, + col_dl_mb_s_, + col_dl_padding_percent_, + col_dl_crc_failed_, + col_dl_retx_ +}; + + +// Type of tree item, so can set column headings properly. +enum { + mac_whole_ue_row_type_ = 1000, + mac_ulsch_packet_count_row_type, + mac_ulsch_byte_count_row_type, + mac_dlsch_packet_count_row_type, + mac_dlsch_byte_count_row_type +}; + +// Calculate and return a bandwidth figure, in Mbs +static double calculate_bw(const nstime_t *start_time, const nstime_t *stop_time, + guint32 bytes) +{ + // Can only calculate bandwidth if have time delta + if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) { + double elapsed_ms = (((double)stop_time->secs - start_time->secs) * 1000) + + (((double)stop_time->nsecs - start_time->nsecs) / 1000000); + + // Only really meaningful if have a few frames spread over time... + // For now at least avoid dividing by something very close to 0.0 + if (elapsed_ms < 2.0) { + return 0.0f; + } + + // N.B. very small values will display as scientific notation, but rather that than show 0 + // when there is some traffic.. + return ((bytes * 8) / elapsed_ms) / 1000; + } + else { + return 0.0f; + } +} + + +// Channels (by LCID) data node. Used for UL/DL frames/bytes. +class MacULDLTreeWidgetItem : public QTreeWidgetItem +{ +public: + MacULDLTreeWidgetItem(QTreeWidgetItem *parent, unsigned ueid, unsigned rnti, int row_type) : + QTreeWidgetItem (parent, row_type), + ueid_(ueid), + rnti_(rnti) + { + // Init values held for all lcids to 0. + for (int n=0; n < MAC_LTE_DATA_LCID_COUNT_MAX; n++) { + lcids[n] = 0; + } + + // Set first column to show what counts in this row mean. + switch (row_type) { + case mac_ulsch_packet_count_row_type: + setText(col_rnti_, "UL Packets"); + break; + case mac_ulsch_byte_count_row_type: + setText(col_rnti_, "UL Bytes"); + break; + case mac_dlsch_packet_count_row_type: + setText(col_rnti_, "DL Packets"); + break; + case mac_dlsch_byte_count_row_type: + setText(col_rnti_, "DL Bytes"); + break; + default: + // Should never get here... + break; + } + } + + bool operator< (const QTreeWidgetItem &other) const + { + // We want rows with a UE to appear in the order they appear in the row_type enum. + return type() < other.type(); + } + + void draw() + { + // Show current value of counter for each LCID. + // N.B. fields that are set as % using percent_bar_delegate.h + // for UE headings don't display here... + for (int n=0; n < MAC_LTE_DATA_LCID_COUNT_MAX; n++) { + setText(col_type_+n, QString::number((uint)lcids[n])); + } + } + + // Increase value held for lcid by given value. + void updateLCID(guint8 lcid, guint value) + { + lcids[lcid] += value; + } + + // Generate expression for this UE and direction, also filter for SRs and RACH if indicated. + const QString filterExpression(bool showSR, bool showRACH) { + int direction = (type() == mac_dlsch_packet_count_row_type) || + (type() == mac_dlsch_byte_count_row_type); + + QString filter_expr; + + if (showSR) { + filter_expr = QString("(mac-lte.sr-req and mac-lte.ueid == %1) or (").arg(ueid_); + } + + if (showRACH) { + filter_expr += QString("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (").arg(ueid_); + } + + // Main expression matching this UE and direction + filter_expr += QString("mac-lte.ueid==%1 && mac-lte.rnti==%2 && mac-lte.direction==%3"). + arg(ueid_).arg(rnti_).arg(direction); + + // Close () if open because of SR + if (showSR) { + filter_expr += QString(")"); + } + // Close () if open because of RACH + if (showRACH) { + filter_expr += QString(")"); + } + + return filter_expr; + } + + // Not showing anything for individual channels. Headings are different than from UEs, and + // trying to show both would be too confusing. + QList rowData() const + { + return QList(); + } + +private: + unsigned ueid_; + unsigned rnti_; + int lcids[MAC_LTE_DATA_LCID_COUNT_MAX]; /* 0 to 10 and 32 to 38 */ +}; + + + +// Whole UE tree item +class MacUETreeWidgetItem : public QTreeWidgetItem +{ +public: + MacUETreeWidgetItem(QTreeWidget *parent, const mac_lte_tap_info *mlt_info) : + QTreeWidgetItem (parent, mac_whole_ue_row_type_), + rnti_(0), + type_(0), + ueid_(0), + ul_frames_(0), + ul_bytes_(0), + ul_raw_bytes_(0), + ul_padding_bytes_(0), + ul_retx_(0), + dl_frames_(0), + dl_bytes_(0), + dl_raw_bytes_(0), + dl_padding_bytes_(0), + dl_crc_failed_(0), + dl_retx_(0) + { + // Set fixed fields. + rnti_ = mlt_info->rnti; + type_ = mlt_info->rntiType; + ueid_ = mlt_info->ueid; + setText(col_rnti_, QString::number(rnti_)); + setText(col_type_, type_ == C_RNTI ? QObject::tr("C-RNTI") : QObject::tr("SPS-RNTI")); + setText(col_ueid_, QString::number(ueid_)); + + // Add UL/DL packet/byte count subitems. + addDetails(); + } + + // Does this tap-info match this existing UE item? + bool isMatch(const mac_lte_tap_info *mlt_info) { + return ((rnti_ == mlt_info->rnti) && + (type_ == mlt_info->rntiType) && + (ueid_ == mlt_info->ueid)); + } + + // Update this UE according to the tap info + void update(const mac_lte_tap_info *mlt_info) { + + // Uplink. + if (mlt_info->direction == DIRECTION_UPLINK) { + if (mlt_info->isPHYRetx) { + ul_retx_++; + return; + } + + if (mlt_info->crcStatusValid && (mlt_info->crcStatus != crc_success)) { + // TODO: there is not a column for this... + //ul_crc_errors_++; + return; + } + + // Update time range + if (ul_frames_ == 0) { + ul_time_start_ = mlt_info->mac_lte_time; + } + ul_time_stop_ = mlt_info->mac_lte_time; + + ul_frames_++; + + // These values needed for padding % calculation. + ul_raw_bytes_ += mlt_info->raw_length; + ul_padding_bytes_ += mlt_info->padding_bytes; + + // N.B. Not going to support predefined data in Qt version.. + if (!mlt_info->isPredefinedData) { + for (int n=0; n < MAC_LTE_DATA_LCID_COUNT_MAX; n++) { + // Update UL child items + ul_frames_item_->updateLCID(n, mlt_info->sdus_for_lcid[n]); + ul_bytes_item_->updateLCID(n, mlt_info->bytes_for_lcid[n]); + + ul_bytes_ += mlt_info->bytes_for_lcid[n]; + } + } + } + + // Downlink + else { + if (mlt_info->isPHYRetx) { + dl_retx_++; + return; + } + + if (mlt_info->crcStatusValid && (mlt_info->crcStatus != crc_success)) { + switch (mlt_info->crcStatus) { + case crc_fail: + dl_crc_failed_++; + break; + + default: + // Not a reason we currently care about. + break; + } + return; + } + + // Update time range + if (dl_frames_ == 0) { + dl_time_start_ = mlt_info->mac_lte_time; + } + dl_time_stop_ = mlt_info->mac_lte_time; + + dl_frames_++; + + // These values needed for padding % calculation. + dl_raw_bytes_ += mlt_info->raw_length; + dl_padding_bytes_ += mlt_info->padding_bytes; + + // N.B. Not going to support predefined data in Qt version.. + if (!mlt_info->isPredefinedData) { + for (int n=0; n < MAC_LTE_DATA_LCID_COUNT_MAX; n++) { + // Update DL child items + dl_frames_item_->updateLCID(n, mlt_info->sdus_for_lcid[n]); + dl_bytes_item_->updateLCID(n, mlt_info->bytes_for_lcid[n]); + + dl_bytes_ += mlt_info->bytes_for_lcid[n]; + } + } + } + } + + void addDetails() { + // Add UL/DL packet and byte counts. + ul_frames_item_ = new MacULDLTreeWidgetItem(this, ueid_, rnti_, mac_ulsch_packet_count_row_type); + ul_bytes_item_ = new MacULDLTreeWidgetItem(this, ueid_, rnti_, mac_ulsch_byte_count_row_type); + dl_frames_item_ = new MacULDLTreeWidgetItem(this, ueid_, rnti_, mac_dlsch_packet_count_row_type); + dl_bytes_item_ = new MacULDLTreeWidgetItem(this, ueid_, rnti_, mac_dlsch_byte_count_row_type); + + setExpanded(false); + } + + // Draw this UE. + void draw() { + // Fixed fields (rnti, type, ueid) won't change during lifetime of UE entry. + + // Calculate bw now. + double UL_bw = calculate_bw(&ul_time_start_, + &ul_time_stop_, + ul_bytes_); + double DL_bw = calculate_bw(&dl_time_start_, + &dl_time_stop_, + dl_bytes_); + + // Set columns with current values. + setText(col_ul_frames_, QString::number(ul_frames_)); + setText(col_ul_bytes_, QString::number(ul_bytes_)); + setText(col_ul_mb_s_, QString::number(UL_bw)); + setData(col_ul_padding_percent_, Qt::UserRole, + QVariant::fromValue(ul_raw_bytes_ ? + (((double)ul_padding_bytes_ / (double)ul_raw_bytes_) * 100.0) : + 0.0)); + setText(col_ul_retx_, QString::number(ul_retx_)); + + setText(col_dl_frames_, QString::number(dl_frames_)); + setText(col_dl_bytes_, QString::number(dl_bytes_)); + setText(col_dl_mb_s_, QString::number(DL_bw)); + + setData(col_dl_padding_percent_, Qt::UserRole, + QVariant::fromValue(dl_raw_bytes_ ? + (((double)dl_padding_bytes_ / (double)dl_raw_bytes_) * 100.0) : + 0.0)); + setText(col_dl_crc_failed_, QString::number(dl_crc_failed_)); + setText(col_dl_retx_, QString::number(dl_retx_)); + + // Draw child items with per-channel counts. + ul_frames_item_->draw(); + ul_bytes_item_->draw(); + dl_frames_item_->draw(); + dl_bytes_item_->draw(); + } + + // < operator. Compare this item with another item, using the column we are currently sorting on. + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != mac_whole_ue_row_type_) return QTreeWidgetItem::operator< (other); + const MacUETreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case col_rnti_: + return rnti_ < other_row->rnti_; + case col_type_: + return type_ < other_row->type_; + case col_ueid_: + return ueid_ < other_row->ueid_; + // TODO: other fields? + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + + // Generate expression for this UE, also filter for SRs and RACH if indicated. + const QString filterExpression(bool showSR, bool showRACH) { + QString filter_expr; + + if (showSR) { + filter_expr = QString("(mac-lte.sr-req and mac-lte.ueid == %1) or (").arg(ueid_); + } + + if (showRACH) { + filter_expr += QString("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (").arg(ueid_); + } + + // Main expression matching this UE + filter_expr += QString("mac-lte.ueid==%1 && mac-lte.rnti==%2").arg(ueid_).arg(rnti_); + + // Close () if open because of SR + if (showSR) { + filter_expr += QString(")"); + } + // Close () if open because of RACH + if (showRACH) { + filter_expr += QString(")"); + } + + return filter_expr; + } + + // Return the UE-specific fields. + QList rowData() const + { + QList row_data; + + // Key fields + row_data << rnti_ << (type_ == C_RNTI ? QObject::tr("C-RNTI") : QObject::tr("SPS-RNTI")) << ueid_; + + // UL + row_data << ul_frames_ << ul_bytes_ + << calculate_bw(&ul_time_start_, &ul_time_stop_, ul_bytes_) + << QVariant::fromValue(ul_raw_bytes_ ? + (((double)ul_padding_bytes_ / (double)ul_raw_bytes_) * 100.0) : + 0.0) + << ul_retx_; + + // DL + row_data << dl_frames_ << dl_bytes_ + << calculate_bw(&dl_time_start_, &dl_time_stop_, dl_bytes_) + << QVariant::fromValue(dl_raw_bytes_ ? + (((double)dl_padding_bytes_ / (double)dl_raw_bytes_) * 100.0) : + 0.0) + << dl_crc_failed_ << dl_retx_; + return row_data; + } + +private: + // Unchanging (key) fields. + unsigned rnti_; + unsigned type_; + unsigned ueid_; + + // UL-specific. + unsigned ul_frames_; + unsigned ul_bytes_; + unsigned ul_raw_bytes_; + unsigned ul_padding_bytes_; + nstime_t ul_time_start_; + nstime_t ul_time_stop_; + unsigned ul_retx_; + + // DL-specific. + unsigned dl_frames_; + unsigned dl_bytes_; + unsigned dl_raw_bytes_; + unsigned dl_padding_bytes_; + nstime_t dl_time_start_; + nstime_t dl_time_stop_; + unsigned dl_crc_failed_; + unsigned dl_retx_; + + // Child nodes storing per-lcid counts. + MacULDLTreeWidgetItem *ul_frames_item_; + MacULDLTreeWidgetItem *ul_bytes_item_; + MacULDLTreeWidgetItem *dl_frames_item_; + MacULDLTreeWidgetItem *dl_bytes_item_; +}; + + + + +// Label headings. Show according to which type of tree item is currently selected. +static const QStringList mac_whole_ue_row_labels = QStringList() + << QObject::tr("RNTI") << QObject::tr("Type") << QObject::tr("UEId") + << QObject::tr("UL Frames") << QObject::tr("UL Bytes") << QObject::tr("UL MB/s") + << QObject::tr("UL Padding %") << QObject::tr("UL Re TX") + << QObject::tr("DL Frames") << QObject::tr("DL Bytes") << QObject::tr("DL MB/s") + << QObject::tr("DL Padding %") << QObject::tr("DL CRC Failed") + << QObject::tr("DL ReTX") + // 'Blank out' Channel-level fields + << QObject::tr("") << QObject::tr("") << QObject::tr("") << QObject::tr("") << QObject::tr(""); + +static const QStringList mac_channel_counts_labels = QStringList() + << QObject::tr("") << QObject::tr("CCCH") + << QObject::tr("LCID 1") << QObject::tr("LCID 2") << QObject::tr("LCID 3") + << QObject::tr("LCID 4") << QObject::tr("LCID 5") << QObject::tr("LCID 6") + << QObject::tr("LCID 7") << QObject::tr("LCID 8") << QObject::tr("LCID 9") + << QObject::tr("LCID 10") << QObject::tr("LCID 32") << QObject::tr("LCID 33") + << QObject::tr("LCID 34") << QObject::tr("LCID 35") << QObject::tr("LCID 36") + << QObject::tr("LCID 37") << QObject::tr("LCID 38"); + + + +//------------------------------------------------------------------------------------------ +// Dialog + +// Constructor. +LteMacStatisticsDialog::LteMacStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) : + TapParameterDialog(parent, cf, HELP_STATS_LTE_MAC_TRAFFIC_DIALOG), + commonStatsCurrent_(false) +{ + setWindowSubtitle(tr("LTE Mac Statistics")); + loadGeometry(parent.width() * 1, parent.height() * 3 / 4, "LTEMacStatisticsDialog"); + + clearCommonStats(); + + // Create common_stats_grid to appear just above the filter area. + int statstree_layout_idx = verticalLayout()->indexOf(filterLayout()->widget()); + QGridLayout *common_stats_grid = new QGridLayout(); + // Insert into the vertical layout + verticalLayout()->insertLayout(statstree_layout_idx, common_stats_grid); + int one_em = fontMetrics().height(); + common_stats_grid->setColumnMinimumWidth(2, one_em * 2); + common_stats_grid->setColumnStretch(2, 1); + common_stats_grid->setColumnMinimumWidth(5, one_em * 2); + common_stats_grid->setColumnStretch(5, 1); + + // Create statistics label. + commonStatsLabel_ = new QLabel(this); + commonStatsLabel_->setObjectName("statisticsLabel"); + commonStatsLabel_->setTextFormat(Qt::RichText); + commonStatsLabel_->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse); + common_stats_grid->addWidget(commonStatsLabel_); + + + // Create a grid for filtering-related widgetsto also appear in layout. + int filter_controls_layout_idx = verticalLayout()->indexOf(filterLayout()->widget()); + QGridLayout *filter_controls_grid = new QGridLayout(); + // Insert into the vertical layout + verticalLayout()->insertLayout(filter_controls_layout_idx, filter_controls_grid); + filter_controls_grid->setColumnMinimumWidth(2, one_em * 2); + filter_controls_grid->setColumnStretch(2, 1); + filter_controls_grid->setColumnMinimumWidth(5, one_em * 2); + filter_controls_grid->setColumnStretch(5, 1); + + // Add individual controls into the grid + showSRFilterCheckBox_ = new QCheckBox(tr("Include SR frames in filter")); + filter_controls_grid->addWidget(showSRFilterCheckBox_); + showRACHFilterCheckBox_ = new QCheckBox(tr("Include RACH frames in filter")); + filter_controls_grid->addWidget(showRACHFilterCheckBox_); + + // Will set whole-UE headings originally. + updateHeaderLabels(); + + ul_delegate_ = new PercentBarDelegate(); + statsTreeWidget()->setItemDelegateForColumn(col_ul_padding_percent_, ul_delegate_); + dl_delegate_ = new PercentBarDelegate(); + statsTreeWidget()->setItemDelegateForColumn(col_dl_padding_percent_, dl_delegate_); + + statsTreeWidget()->sortByColumn(col_rnti_, Qt::AscendingOrder); + + // Set up column widths. + // resizeColumnToContents doesn't work well here, so set sizes manually. + for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) { + switch (col) { + case col_rnti_: + statsTreeWidget()->setColumnWidth(col, one_em * 8); + break; + case col_ul_frames_: + statsTreeWidget()->setColumnWidth(col, one_em * 5); + break; + case col_ul_bytes_: + statsTreeWidget()->setColumnWidth(col, one_em * 5); + break; + case col_ul_mb_s_: + statsTreeWidget()->setColumnWidth(col, one_em * 4); + break; + case col_ul_padding_percent_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + case col_ul_retx_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + case col_dl_frames_: + statsTreeWidget()->setColumnWidth(col, one_em * 5); + break; + case col_dl_bytes_: + statsTreeWidget()->setColumnWidth(col, one_em * 5); + break; + case col_dl_mb_s_: + statsTreeWidget()->setColumnWidth(col, one_em * 4); + break; + case col_dl_padding_percent_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + case col_dl_crc_failed_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + case col_dl_retx_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + + default: + // The rest are numeric + statsTreeWidget()->setColumnWidth(col, one_em * 4); + statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); + break; + } + } + + addFilterActions(); + + if (filter) { + setDisplayFilter(filter); + } + + // Set handler for when the tree item changes to set the appropriate labels. + connect(statsTreeWidget(), &QTreeWidget::itemSelectionChanged, + this, &LteMacStatisticsDialog::updateHeaderLabels); + + // Set handler for when display filter string is changed. + connect(this, &LteMacStatisticsDialog::updateFilter, + this, &LteMacStatisticsDialog::filterUpdated); +} + +// Destructor. +LteMacStatisticsDialog::~LteMacStatisticsDialog() +{ + delete ul_delegate_; + delete dl_delegate_; +} + +// Update system/common counters, and redraw if changed. +void LteMacStatisticsDialog::updateCommonStats(const mac_lte_tap_info *tap_info) +{ + commonStats_.all_frames++; + + // For common channels, just update global counters + switch (tap_info->rntiType) { + case P_RNTI: + commonStats_.pch_frames++; + commonStats_.pch_bytes += tap_info->single_number_of_bytes; + commonStats_.pch_paging_ids += tap_info->number_of_paging_ids; + commonStatsCurrent_ = false; + break; + case SI_RNTI: + commonStats_.sib_frames++; + commonStats_.sib_bytes += tap_info->single_number_of_bytes; + commonStatsCurrent_ = false; + break; + case NO_RNTI: + commonStats_.mib_frames++; + commonStatsCurrent_ = false; + break; + case RA_RNTI: + commonStats_.rar_frames++; + commonStats_.rar_entries += tap_info->number_of_rars; + commonStatsCurrent_ = false; + break; + case C_RNTI: + case SPS_RNTI: + // UE-specific. + break; + + default: + // Error... + return; + } + + // Check max UEs/tti counter + switch (tap_info->direction) { + case DIRECTION_UPLINK: + if (tap_info->ueInTTI > commonStats_.max_ul_ues_in_tti) { + commonStats_.max_ul_ues_in_tti = tap_info->ueInTTI; + commonStatsCurrent_ = false; + } + break; + case DIRECTION_DOWNLINK: + if (tap_info->ueInTTI > commonStats_.max_dl_ues_in_tti) { + commonStats_.max_dl_ues_in_tti = tap_info->ueInTTI; + commonStatsCurrent_ = false; + } + break; + } +} + +// Draw current common statistics by regenerating label with current values. +void LteMacStatisticsDialog::drawCommonStats() +{ + if (!commonStatsCurrent_) { + QString stats_tables = "\n"; + stats_tables += QString("\n"); + stats_tables += QString("").arg(commonStats_.max_ul_ues_in_tti); + stats_tables += QString("\n").arg(commonStats_.max_dl_ues_in_tti); + + stats_tables += QString("").arg(commonStats_.mib_frames); + stats_tables += QString("\n").arg(commonStats_.sib_frames).arg(commonStats_.sib_bytes); + + stats_tables += QString("\n"). + arg(commonStats_.rar_frames). + arg(commonStats_.rar_entries); + + stats_tables += QString("\n"). + arg(commonStats_.pch_frames). + arg(commonStats_.pch_bytes). + arg(commonStats_.pch_paging_ids); + + stats_tables += QString("
System Max UL UEs/TTI=%1Max DL UEs/TTI=%1
System broadcastMIBs=%1SIBs=%1 (%2 bytes)
RACHRARs=%1 frames (%2 RARs)
PagingPCH=%1 (%2 bytes, %3 IDs)
\n"); + stats_tables += "\n"; + + commonStatsLabel_->setText(stats_tables); + + commonStatsCurrent_ = true; + } +} + +void LteMacStatisticsDialog::clearCommonStats() +{ + memset(&commonStats_, 0, sizeof(commonStats_)); +} + +void LteMacStatisticsDialog::tapReset(void *ws_dlg_ptr) +{ + LteMacStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + if (!ws_dlg) { + return; + } + + ws_dlg->statsTreeWidget()->clear(); + ws_dlg->clearCommonStats(); +} + +//--------------------------------------------------------------------------------------- +// Process tap info from a new packet. +// Returns TAP_PACKET_REDRAW if a redraw is needed, TAP_PACKET_DONT_REDRAW otherwise. +tap_packet_status LteMacStatisticsDialog::tapPacket(void *ws_dlg_ptr, struct _packet_info *, epan_dissect *, const void *mac_lte_tap_info_ptr, tap_flags_t) +{ + // Look up dialog and tap info. + LteMacStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + const mac_lte_tap_info *mlt_info = (const mac_lte_tap_info *) mac_lte_tap_info_ptr; + if (!ws_dlg || !mlt_info) { + return TAP_PACKET_DONT_REDRAW; + } + + // Update common stats. + ws_dlg->updateCommonStats(mlt_info); + + // Nothing more to do if tap entry isn't for a UE. + if ((mlt_info->rntiType != C_RNTI) && (mlt_info->rntiType != SPS_RNTI)) { + return TAP_PACKET_DONT_REDRAW; + } + + // Look for an existing UE to match this tap info. + MacUETreeWidgetItem *mac_ue_ti = NULL; + for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { + QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); + // Make sure we're looking at a UE entry + if (ti->type() != mac_whole_ue_row_type_) { + continue; + } + + // See if current item matches tap. + MacUETreeWidgetItem *cur_muds_ti = static_cast(ti); + if (cur_muds_ti->isMatch(mlt_info)) { + mac_ue_ti = cur_muds_ti; + break; + } + } + + // If don't find matching UE, create a new one. + if (!mac_ue_ti) { + mac_ue_ti = new MacUETreeWidgetItem(ws_dlg->statsTreeWidget(), mlt_info); + for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) { + // int QTreeWidgetItem::textAlignment(int column) const + // Returns the text alignment for the label in the given column. + // Note: This function returns an int for historical reasons. It will be corrected to return Qt::Alignment in Qt 7. + mac_ue_ti->setTextAlignment(col, static_cast(ws_dlg->statsTreeWidget()->headerItem()->textAlignment(col))); + } + } + + // Update the UE item with info from tap! + mac_ue_ti->update(mlt_info); + return TAP_PACKET_REDRAW; +} + +// Return total number of frames tapped. +unsigned LteMacStatisticsDialog::getFrameCount() +{ + return commonStats_.all_frames; +} + +void LteMacStatisticsDialog::tapDraw(void *ws_dlg_ptr) +{ + // Look up dialog. + LteMacStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + if (!ws_dlg) { + return; + } + + // Go over all of the top-level items. + for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { + // Get item, make sure its of the whole-UE type. + QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); + if (ti->type() != mac_whole_ue_row_type_) { + continue; + } + + // Tell the UE item to draw itself. + MacUETreeWidgetItem *mac_ue_ti = static_cast(ti); + mac_ue_ti->draw(); + } + + ws_dlg->drawCommonStats(); + + // Update title + ws_dlg->setWindowSubtitle(QString("LTE Mac Statistics (%1 UEs, %2 frames)"). + arg(ws_dlg->statsTreeWidget()->topLevelItemCount()).arg(ws_dlg->getFrameCount())); +} + +const QString LteMacStatisticsDialog::filterExpression() +{ + QString filter_expr; + + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + + if (ti->type() == mac_whole_ue_row_type_) { + MacUETreeWidgetItem *mac_ue_ti = static_cast(ti); + filter_expr = mac_ue_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked, + showRACHFilterCheckBox_->checkState() > Qt::Unchecked); + } else { + MacULDLTreeWidgetItem *mac_channels_ti = static_cast(ti); + filter_expr = mac_channels_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked, + showRACHFilterCheckBox_->checkState() > Qt::Unchecked); + } + } + return filter_expr; +} + +void LteMacStatisticsDialog::fillTree() +{ + if (!registerTapListener("mac-lte", + this, + displayFilter_.toLatin1().data(), + TL_REQUIRES_NOTHING, + tapReset, + tapPacket, + tapDraw)) { + reject(); + return; + } + + cap_file_.retapPackets(); + tapDraw(this); + removeTapListeners(); +} + +void LteMacStatisticsDialog::updateHeaderLabels() +{ + if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == mac_whole_ue_row_type_) { + // Whole-UE labels + statsTreeWidget()->setHeaderLabels(mac_whole_ue_row_labels); + } else if (statsTreeWidget()->selectedItems().count() > 0) { + // ULDL labels + switch (statsTreeWidget()->selectedItems()[0]->type()) { + case mac_ulsch_packet_count_row_type: + case mac_ulsch_byte_count_row_type: + case mac_dlsch_packet_count_row_type: + case mac_dlsch_byte_count_row_type: + statsTreeWidget()->setHeaderLabels(mac_channel_counts_labels); + break; + + default: + break; + } + } + else { + // Nothing selected yet, but set whole-UE labels. + statsTreeWidget()->setHeaderLabels(mac_whole_ue_row_labels); + } +} + +void LteMacStatisticsDialog::captureFileClosing() +{ + remove_tap_listener(this); + + WiresharkDialog::captureFileClosing(); +} + +// Store filter from signal. +void LteMacStatisticsDialog::filterUpdated(QString filter) +{ + displayFilter_ = filter; +} + +// Get the item for the row, depending upon the type of tree item. +QList LteMacStatisticsDialog::treeItemData(QTreeWidgetItem *item) const +{ + // Cast up to our type. + MacULDLTreeWidgetItem *channel_item = dynamic_cast(item); + if (channel_item) { + return channel_item->rowData(); + } + MacUETreeWidgetItem *ue_item = dynamic_cast(item); + if (ue_item) { + return ue_item->rowData(); + } + + // Need to return something.. + return QList(); +} + + +// Stat command + args + +static void +lte_mac_statistics_init(const char *args, void*) { + QStringList args_l = QString(args).split(','); + QByteArray filter; + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(",").toUtf8(); + } + mainApp->emitStatCommandSignal("LteMacStatistics", filter.constData(), NULL); +} + +static stat_tap_ui lte_mac_statistics_ui = { + REGISTER_STAT_GROUP_TELEPHONY_LTE, + QT_TRANSLATE_NOOP("LteMacStatisticsDialog", "MAC Statistics"), + "mac-lte,stat", + lte_mac_statistics_init, + 0, + NULL +}; + +extern "C" { + +void register_tap_listener_qt_lte_mac_statistics(void); + +void +register_tap_listener_qt_lte_mac_statistics(void) +{ + register_stat_tap_ui(<e_mac_statistics_ui, NULL); +} + +} diff --git a/ui/qt/lte_mac_statistics_dialog.h b/ui/qt/lte_mac_statistics_dialog.h new file mode 100644 index 00000000..411d0614 --- /dev/null +++ b/ui/qt/lte_mac_statistics_dialog.h @@ -0,0 +1,80 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __LTE_MAC_STATISTICS_DIALOG_H__ +#define __LTE_MAC_STATISTICS_DIALOG_H__ + +#include "tap_parameter_dialog.h" + +#include +#include + +#include + +// Common channel stats +typedef struct mac_lte_common_stats { + guint32 all_frames; + guint32 mib_frames; + guint32 sib_frames; + guint32 sib_bytes; + guint32 pch_frames; + guint32 pch_bytes; + guint32 pch_paging_ids; + guint32 rar_frames; + guint32 rar_entries; + + guint16 max_ul_ues_in_tti; + guint16 max_dl_ues_in_tti; +} mac_lte_common_stats; + + +class LteMacStatisticsDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + LteMacStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter); + ~LteMacStatisticsDialog(); + +protected: + void captureFileClosing(); + +private: + // Extra controls needed for this dialog. + QLabel *commonStatsLabel_; + QCheckBox *showSRFilterCheckBox_; + QCheckBox *showRACHFilterCheckBox_; + PercentBarDelegate *ul_delegate_, *dl_delegate_; + QString displayFilter_; + + // Callbacks for register_tap_listener + static void tapReset(void *ws_dlg_ptr); + static tap_packet_status tapPacket(void *ws_dlg_ptr, struct _packet_info *, struct epan_dissect *, const void *mac_lte_tap_info_ptr, tap_flags_t flags); + static void tapDraw(void *ws_dlg_ptr); + + virtual const QString filterExpression(); + + // Common stats. + mac_lte_common_stats commonStats_; + bool commonStatsCurrent_; // Set when changes have not yet been drawn + void updateCommonStats(const struct mac_lte_tap_info *mlt_info); + void drawCommonStats(); + void clearCommonStats(); + + unsigned getFrameCount(); + + QList treeItemData(QTreeWidgetItem *item) const; + +private slots: + virtual void fillTree(); + void updateHeaderLabels(); + void filterUpdated(QString filter); +}; + +#endif // __LTE_MAC_STATISTICS_DIALOG_H__ diff --git a/ui/qt/lte_rlc_graph_dialog.cpp b/ui/qt/lte_rlc_graph_dialog.cpp new file mode 100644 index 00000000..f1ac2b46 --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.cpp @@ -0,0 +1,887 @@ +/* lte_rlc_graph_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "lte_rlc_graph_dialog.h" +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include "main_application.h" +#include "simple_dialog.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include + +#include + + +const QRgb graph_color_ack = tango_sky_blue_4; // Blue for ACK lines +const QRgb graph_color_nack = tango_scarlet_red_3; // Red for NACKs + +// Size of selectable packet points in the base graph +const double pkt_point_size_ = 3.0; + + +// Constructor. +LteRlcGraphDialog::LteRlcGraphDialog(QWidget &parent, CaptureFile &cf, bool channelKnown) : + WiresharkDialog(parent, cf), + ui(new Ui::LteRlcGraphDialog), + mouse_drags_(true), + rubber_band_(NULL), + base_graph_(NULL), + reseg_graph_(NULL), + acks_graph_(NULL), + nacks_graph_(NULL), + tracer_(NULL), + packet_num_(0) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 3 / 4); + + QCustomPlot *rp = ui->rlcPlot; + rp->xAxis->setLabel(tr("Time")); + rp->yAxis->setLabel(tr("Sequence Number")); + + // TODO: couldn't work out how to tell rp->xAxis not to label fractions of a SN... + + ui->dragRadioButton->setChecked(mouse_drags_); + + ctx_menu_ = new QMenu(this); + ctx_menu_->addAction(ui->actionZoomIn); + ctx_menu_->addAction(ui->actionZoomInX); + ctx_menu_->addAction(ui->actionZoomInY); + ctx_menu_->addAction(ui->actionZoomOut); + ctx_menu_->addAction(ui->actionZoomOutX); + ctx_menu_->addAction(ui->actionZoomOutY); + ctx_menu_->addAction(ui->actionReset); + ctx_menu_->addSeparator(); + ctx_menu_->addAction(ui->actionMoveRight10); + ctx_menu_->addAction(ui->actionMoveLeft10); + ctx_menu_->addAction(ui->actionMoveUp10); + ctx_menu_->addAction(ui->actionMoveUp100); + ctx_menu_->addAction(ui->actionMoveDown10); + ctx_menu_->addAction(ui->actionMoveDown100); + ctx_menu_->addAction(ui->actionMoveRight1); + ctx_menu_->addAction(ui->actionMoveLeft1); + ctx_menu_->addAction(ui->actionMoveUp1); + ctx_menu_->addAction(ui->actionMoveDown1); + ctx_menu_->addSeparator(); + ctx_menu_->addAction(ui->actionGoToPacket); + ctx_menu_->addSeparator(); + ctx_menu_->addAction(ui->actionDragZoom); +// ctx_menu_->addAction(ui->actionToggleTimeOrigin); + ctx_menu_->addAction(ui->actionCrosshairs); + ctx_menu_->addSeparator(); + ctx_menu_->addAction(ui->actionSwitchDirection); + set_action_shortcuts_visible_in_context_menu(ctx_menu_->actions()); + + // Zero out this struct. + memset(&graph_, 0, sizeof(graph_)); + + // If channel is known, details will be supplied by setChannelInfo(). + if (!channelKnown) { + completeGraph(); + } +} + +// Destructor +LteRlcGraphDialog::~LteRlcGraphDialog() +{ + delete ui; +} + +// Set the channel information that this graph should show. +void LteRlcGraphDialog::setChannelInfo(guint16 ueid, guint8 rlcMode, + guint16 channelType, guint16 channelId, guint8 direction, + bool maybe_empty) +{ + graph_.ueid = ueid; + graph_.rlcMode = rlcMode; + graph_.channelType = channelType; + graph_.channelId = channelId; + graph_.channelSet = TRUE; + graph_.direction = direction; + + completeGraph(maybe_empty); +} + +// Once channel details are known, complete the graph with details that depend upon the channel. +void LteRlcGraphDialog::completeGraph(bool may_be_empty) +{ + QCustomPlot *rp = ui->rlcPlot; + + // If no channel chosen already, try to use currently selected frame. + findChannel(may_be_empty); + + // Set window title here. + if (graph_.channelSet) { + QString dlg_title = tr("LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5)") + .arg(graph_.ueid) + .arg((graph_.channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB") + .arg(graph_.channelId) + .arg((graph_.direction == DIRECTION_UPLINK) ? "UL" : "DL") + .arg((graph_.rlcMode == RLC_UM_MODE) ? "UM" : "AM"); + setWindowTitle(dlg_title); + } + else { + setWindowTitle(tr("LTE RLC Graph - no channel selected")); + } + + // Set colours/styles for each of the traces on the graph. + QCustomPlot *sp = ui->rlcPlot; + base_graph_ = sp->addGraph(); // All: Selectable segments + base_graph_->setPen(QPen(QBrush(Qt::black), 0.25)); + + reseg_graph_ = sp->addGraph(); + reseg_graph_->setPen(QPen(QBrush(Qt::lightGray), 0.25)); + + acks_graph_ = sp->addGraph(); + acks_graph_->setPen(QPen(QBrush(graph_color_ack), 1.0)); + + nacks_graph_ = sp->addGraph(); + nacks_graph_->setPen(QPen(QBrush(graph_color_nack), 0.25)); + + // Create tracer + tracer_ = new QCPItemTracer(sp); + tracer_->setVisible(false); + toggleTracerStyle(true); + + // Change label on save/export button. + QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save); + save_bt->setText(tr("Save As…")); + + // Don't want to connect again after first time. - causes mouse handlers to get called + // multiple times. + if (!may_be_empty) { + connect(rp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*))); + connect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + connect(rp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*))); + } + this->setResult(QDialog::Accepted); + + // Extract the data that the graph can use. + fillGraph(); +} + +// See if the given segment matches the channel this graph is plotting. +bool LteRlcGraphDialog::compareHeaders(rlc_segment *seg) +{ + return compare_rlc_headers(graph_.ueid, graph_.channelType, + graph_.channelId, graph_.rlcMode, graph_.direction, + seg->ueid, seg->channelType, + seg->channelId, seg->rlcMode, seg->direction, + seg->isControlPDU); +} + +// Look for channel to plot based upon currently selected frame. +void LteRlcGraphDialog::findChannel(bool may_fail) +{ + // Temporarily disconnect mouse move signals. + QCustomPlot *rp = ui->rlcPlot; + disconnect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + + char *err_string = NULL; + + // Rescan for channel data. + rlc_graph_segment_list_free(&graph_); + if (!rlc_graph_segment_list_get(cap_file_.capFile(), &graph_, graph_.channelSet, + &err_string)) { + if (may_fail) { + g_free(err_string); + } else { + // Pop up an error box to report error. + simple_error_message_box("%s", err_string); + g_free(err_string); + return; + } + } + + // Reconnect mouse move signal. + connect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); +} + +// Fill in graph data based upon what was read into the rlc_graph struct. +void LteRlcGraphDialog::fillGraph() +{ + QCustomPlot *sp = ui->rlcPlot; + + // We should always have 4 graphs, but cover case if no channel was chosen. + if (sp->graphCount() < 1) { + return; + } + + tracer_->setGraph(NULL); + + base_graph_->setLineStyle(QCPGraph::lsNone); // dot + reseg_graph_->setLineStyle(QCPGraph::lsNone); // dot + acks_graph_->setLineStyle(QCPGraph::lsStepLeft); // to get step effect... + nacks_graph_->setLineStyle(QCPGraph::lsNone); // dot, but bigger. + + // Will show all graphs with data we find. + for (int i = 0; i < sp->graphCount(); i++) { + sp->graph(i)->data()->clear(); + sp->graph(i)->setVisible(true); + } + + // N.B. ssDisc is really too slow. TODO: work out how to turn off aliasing, or experiment + // with ssCustom. Other styles tried didn't look right. + // GTK version was speeded up noticibly by turning down aliasing level... + base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + reseg_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + acks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + // NACKs are shown bigger than others. + nacks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_*2)); + + // Map timestamps -> segments in first pass. + time_stamp_map_.clear(); + for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + if (!compareHeaders(seg)) { + continue; + } + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + + time_stamp_map_.insert(ts, seg); + } + + // Now sequence numbers. + QVector seq_time, seq, + reseg_seq_time, reseg_seq, + acks_time, acks, + nacks_time, nacks; + for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + double ts = seg->rel_secs + (seg->rel_usecs / 1000000.0); + if (compareHeaders(seg)) { + if (!seg->isControlPDU) { + // Data + if (seg->isResegmented) { + reseg_seq_time.append(ts); + reseg_seq.append(seg->SN); + } + else { + seq_time.append(ts); + seq.append(seg->SN); + } + } + else { + // Status (ACKs/NACKs) + acks_time.append(ts); + acks.append(seg->ACKNo-1); + for (int n=0; n < seg->noOfNACKs; n++) { + nacks_time.append(ts); + nacks.append(seg->NACKs[n]); + } + } + } + } + + // Add the data from the graphs. + base_graph_->setData(seq_time, seq); + reseg_graph_->setData(reseg_seq_time, reseg_seq); + acks_graph_->setData(acks_time, acks); + nacks_graph_->setData(nacks_time, nacks); + + sp->setEnabled(true); + + // Auto-size... + mouseMoved(NULL); + resetAxes(); + + // This is why, in mouseMoved(), we only match the entries + // corresponding to data segments (base_graph_)... + tracer_->setGraph(base_graph_); + + // XXX QCustomPlot doesn't seem to draw any sort of focus indicator. + sp->setFocus(); +} + +// Copied from TCP graphs, seems like a kludge to get the graph resized immediately after it is built... +void LteRlcGraphDialog::showEvent(QShowEvent *) +{ + resetAxes(); +} + +// Respond to a key press. +void LteRlcGraphDialog::keyPressEvent(QKeyEvent *event) +{ + int pan_pixels = (event->modifiers() & Qt::ShiftModifier) ? 1 : 10; + + switch(event->key()) { + case Qt::Key_Minus: + case Qt::Key_Underscore: // Shifted minus on U.S. keyboards + case Qt::Key_O: // GTK+ + zoomAxes(false); + break; + case Qt::Key_Plus: + case Qt::Key_Equal: // Unshifted plus on U.S. keyboards + case Qt::Key_I: // GTK+ + zoomAxes(true); + break; + + case Qt::Key_X: // Zoom X axis only + if (event->modifiers() & Qt::ShiftModifier) { + zoomXAxis(false); // upper case X -> Zoom out + } else { + zoomXAxis(true); // lower case x -> Zoom in + } + break; + case Qt::Key_Y: // Zoom Y axis only + if (event->modifiers() & Qt::ShiftModifier) { + zoomYAxis(false); // upper case Y -> Zoom out + } else { + zoomYAxis(true); // lower case y -> Zoom in + } + break; + + case Qt::Key_Right: + case Qt::Key_L: + panAxes(pan_pixels, 0); + break; + case Qt::Key_Left: + case Qt::Key_H: + panAxes(-1 * pan_pixels, 0); + break; + case Qt::Key_Up: + case Qt::Key_K: + panAxes(0, pan_pixels); + break; + case Qt::Key_Down: + case Qt::Key_J: + panAxes(0, -1 * pan_pixels); + break; + + case Qt::Key_PageUp: + panAxes(0, 20 * pan_pixels); + break; + case Qt::Key_PageDown: + panAxes(0, -20 * pan_pixels); + break; + + case Qt::Key_Space: + toggleTracerStyle(false); + break; + + case Qt::Key_0: + case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards + case Qt::Key_Home: + case Qt::Key_R: + resetAxes(); + break; + + case Qt::Key_G: + on_actionGoToPacket_triggered(); + break; + case Qt::Key_T: +// on_actionToggleTimeOrigin_triggered(); + break; + case Qt::Key_Z: + on_actionDragZoom_triggered(); + break; + + case Qt::Key_D: + on_actionSwitchDirection_triggered(); + break; + + } + + WiresharkDialog::keyPressEvent(event); +} + +void LteRlcGraphDialog::zoomAxes(bool in) +{ + QCustomPlot *rp = ui->rlcPlot; + double h_factor = rp->axisRect()->rangeZoomFactor(Qt::Horizontal); + double v_factor = rp->axisRect()->rangeZoomFactor(Qt::Vertical); + + if (!in) { + h_factor = pow(h_factor, -1); + v_factor = pow(v_factor, -1); + } + + if (in) { + // Don't want to zoom in *too* far on y axis. + if (rp->yAxis->range().size() < 10) { + return; + } + } + else { + // Don't want to zoom out *too* far on y axis. + if (rp->yAxis->range().size() > (65536+10)) { + return; + } + } + + rp->xAxis->scaleRange(h_factor, rp->xAxis->range().center()); + rp->yAxis->scaleRange(v_factor, rp->yAxis->range().center()); + rp->replot(QCustomPlot::rpQueuedReplot); +} + +void LteRlcGraphDialog::zoomXAxis(bool in) +{ + QCustomPlot *rp = ui->rlcPlot; + double h_factor = rp->axisRect()->rangeZoomFactor(Qt::Horizontal); + + if (!in) { + h_factor = pow(h_factor, -1); + } + + rp->xAxis->scaleRange(h_factor, rp->xAxis->range().center()); + rp->replot(QCustomPlot::rpQueuedReplot); +} + +void LteRlcGraphDialog::zoomYAxis(bool in) +{ + QCustomPlot *rp = ui->rlcPlot; + double v_factor = rp->axisRect()->rangeZoomFactor(Qt::Vertical); + + if (in) { + // Don't want to zoom in *too* far on y axis. + if (rp->yAxis->range().size() < 10) { + return; + } + } + else { + // Don't want to zoom out *too* far on y axis. + if (rp->yAxis->range().size() > (65536+10)) { + return; + } + } + + if (!in) { + v_factor = pow(v_factor, -1); + } + + rp->yAxis->scaleRange(v_factor, rp->yAxis->range().center()); + rp->replot(QCustomPlot::rpQueuedReplot); +} + +void LteRlcGraphDialog::panAxes(int x_pixels, int y_pixels) +{ + QCustomPlot *rp = ui->rlcPlot; + double h_pan = 0.0; + double v_pan = 0.0; + + // Don't scroll up beyond max range, or below 0 + if (((y_pixels > 0) && (rp->yAxis->range().upper > 65536)) || + ((y_pixels < 0) && (rp->yAxis->range().lower < 0))) { + return; + } + // Don't scroll left beyond 0. Arguably should be time of first segment. + if ((x_pixels < 0) && (rp->xAxis->range().lower < 0)) { + return; + } + + h_pan = rp->xAxis->range().size() * x_pixels / rp->xAxis->axisRect()->width(); + v_pan = rp->yAxis->range().size() * y_pixels / rp->yAxis->axisRect()->height(); + + // The GTK+ version won't pan unless we're zoomed. Should we do the same here? + if (h_pan) { + rp->xAxis->moveRange(h_pan); + rp->replot(QCustomPlot::rpQueuedReplot); + } + if (v_pan) { + rp->yAxis->moveRange(v_pan); + rp->replot(QCustomPlot::rpQueuedReplot); + } +} + +// Given a selected rect in pixels, work out what this should be in graph units. +// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph +// in zoom mode. +const int min_zoom_pixels_ = 20; +QRectF LteRlcGraphDialog::getZoomRanges(QRect zoom_rect) +{ + QRectF zoom_ranges = QRectF(); + + if (zoom_rect.width() < min_zoom_pixels_ && zoom_rect.height() < min_zoom_pixels_) { + return zoom_ranges; + } + + QCustomPlot *rp = ui->rlcPlot; + QRect zr = zoom_rect.normalized(); + QRect ar = rp->axisRect()->rect(); + if (ar.intersects(zr)) { + QRect zsr = ar.intersected(zr); + zoom_ranges.setX(rp->xAxis->range().lower + + rp->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width()); + zoom_ranges.setWidth(rp->xAxis->range().size() * zsr.width() / ar.width()); + + // QRects grow down + zoom_ranges.setY(rp->yAxis->range().lower + + rp->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height()); + zoom_ranges.setHeight(rp->yAxis->range().size() * zsr.height() / ar.height()); + } + return zoom_ranges; +} + +void LteRlcGraphDialog::graphClicked(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + + if (event->button() == Qt::RightButton) { + // XXX We should find some way to get rlcPlot to handle a + // contextMenuEvent instead. +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + ctx_menu_->popup(event->globalPosition().toPoint()); +#else + ctx_menu_->popup(event->globalPos()); +#endif + } else if (mouse_drags_) { + if (rp->axisRect()->rect().contains(event->pos())) { + rp->setCursor(QCursor(Qt::ClosedHandCursor)); + } + on_actionGoToPacket_triggered(); + } else { + if (!rubber_band_) { + rubber_band_ = new QRubberBand(QRubberBand::Rectangle, rp); + } + rb_origin_ = event->pos(); + rubber_band_->setGeometry(QRect(rb_origin_, QSize())); + rubber_band_->show(); + } + rp->setFocus(); +} + +void LteRlcGraphDialog::mouseMoved(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + Qt::CursorShape shape = Qt::ArrowCursor; + + // Set the cursor shape. + if (event) { + if (event->buttons().testFlag(Qt::LeftButton)) { + if (mouse_drags_) { + shape = Qt::ClosedHandCursor; + } else { + shape = Qt::CrossCursor; + } + } else if (rp->axisRect()->rect().contains(event->pos())) { + if (mouse_drags_) { + shape = Qt::OpenHandCursor; + } else { + shape = Qt::CrossCursor; + } + } + rp->setCursor(QCursor(shape)); + } + + // Trying to let 'hint' grow efficiently. Still pretty slow for a dense graph... + QString hint; + hint.reserve(128); + hint = ""; + + if (mouse_drags_) { + double tr_key = tracer_->position->key(); + struct rlc_segment *packet_seg = NULL; + packet_num_ = 0; + + // XXX If we have multiple packets with the same timestamp tr_key + // may not return the packet we want. It might be possible to fudge + // unique keys using nextafter(). + if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) { + packet_seg = time_stamp_map_.value(tr_key, NULL); + } + + if (!packet_seg) { + tracer_->setVisible(false); + hint += "Hover over the graph for details. "; + ui->hintLabel->setText(hint); + ui->rlcPlot->replot(QCustomPlot::rpQueuedReplot); + return; + } + + tracer_->setVisible(true); + packet_num_ = packet_seg->num; + // N.B. because tracer only looks up entries in base_graph_, + // we know that packet_seg will be a data segment, so no need to check + // iscontrolPDU or isResegmented fields. + hint += tr("%1 %2 (%3s seq %4 len %5)") + .arg(cap_file_.capFile() ? tr("Click to select packet") : tr("Packet")) + .arg(packet_num_) + .arg(QString::number(packet_seg->rel_secs + (packet_seg->rel_usecs / 1000000.0), 'g', 4)) + .arg(packet_seg->SN) + .arg(packet_seg->pduLength); + tracer_->setGraphKey(ui->rlcPlot->xAxis->pixelToCoord(event->pos().x())); + // Redrawing the whole graph is making the update *very* slow! + // TODO: Is there a way just to draw the parts that may have changed? + // In the GTK version, we displayed the stored pixbuf and draw temporary items on top... + rp->replot(QCustomPlot::rpQueuedReplot); + + } else { + if (event && rubber_band_ && rubber_band_->isVisible()) { + // Work out zoom based upon selected region (in pixels). + rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized()); + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + hint += tr("Release to zoom, x = %1 to %2, y = %3 to %4") + .arg(zoom_ranges.x()) + .arg(zoom_ranges.x() + zoom_ranges.width()) + .arg(zoom_ranges.y()) + .arg(zoom_ranges.y() + zoom_ranges.height()); + } else { + hint += tr("Unable to select range."); + } + } else { + hint += tr("Click to select a portion of the graph."); + } + } + + hint.append(""); + ui->hintLabel->setText(hint); +} + +void LteRlcGraphDialog::mouseReleased(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + if (rubber_band_) { + rubber_band_->hide(); + if (!mouse_drags_) { + // N.B. work out range to zoom to *before* resetting axes. + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + resetAxes(); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + rp->xAxis->setRangeLower(zoom_ranges.x()); + rp->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width()); + rp->yAxis->setRangeLower(zoom_ranges.y()); + rp->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height()); + rp->replot(); + } + } + } else if (rp->cursor().shape() == Qt::ClosedHandCursor) { + rp->setCursor(QCursor(Qt::OpenHandCursor)); + } +} + +void LteRlcGraphDialog::resetAxes() +{ + QCustomPlot *rp = ui->rlcPlot; + + QCPRange x_range = rp->xAxis->scaleType() == QCPAxis::stLogarithmic ? + rp->xAxis->range().sanitizedForLogScale() : rp->xAxis->range(); + + double pixel_pad = 10.0; // per side + + rp->rescaleAxes(true); + base_graph_->rescaleValueAxis(false, true); + + double axis_pixels = rp->xAxis->axisRect()->width(); + rp->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, x_range.center()); + + axis_pixels = rp->yAxis->axisRect()->height(); + rp->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, rp->yAxis->range().center()); + + rp->replot(QCustomPlot::rpQueuedReplot); +} + +void LteRlcGraphDialog::on_actionGoToPacket_triggered() +{ + if (tracer_->visible() && cap_file_.capFile() && (packet_num_ > 0)) { + // Signal to the packetlist which frame we want to show. + emit goToPacket(packet_num_); + } +} + +void LteRlcGraphDialog::on_actionCrosshairs_triggered() +{ + toggleTracerStyle(false); +} + +void LteRlcGraphDialog::toggleTracerStyle(bool force_default) +{ + if (!tracer_->visible() && !force_default) { + return; + } + + QPen sp_pen = ui->rlcPlot->graph(0)->pen(); + QCPItemTracer::TracerStyle tstyle = QCPItemTracer::tsCrosshair; + QPen tr_pen = QPen(tracer_->pen()); + QColor tr_color = sp_pen.color(); + + if (force_default || tracer_->style() != QCPItemTracer::tsCircle) { + tstyle = QCPItemTracer::tsCircle; + tr_color.setAlphaF(1.0); + tr_pen.setWidthF(1.5); + } else { + tr_color.setAlphaF(0.5); + tr_pen.setWidthF(1.0); + } + + tracer_->setStyle(tstyle); + tr_pen.setColor(tr_color); + tracer_->setPen(tr_pen); + ui->rlcPlot->replot(); +} + +void LteRlcGraphDialog::on_actionReset_triggered() +{ + resetAxes(); +} + +void LteRlcGraphDialog::on_actionZoomIn_triggered() +{ + zoomAxes(true); +} + +void LteRlcGraphDialog::on_actionZoomOut_triggered() +{ + zoomAxes(false); +} + +void LteRlcGraphDialog::on_actionMoveUp10_triggered() +{ + panAxes(0, 10); +} + +void LteRlcGraphDialog::on_actionMoveUp100_triggered() +{ + panAxes(0, 100); +} + +void LteRlcGraphDialog::on_actionMoveLeft10_triggered() +{ + panAxes(-10, 0); +} + +void LteRlcGraphDialog::on_actionMoveRight10_triggered() +{ + panAxes(10, 0); +} + +void LteRlcGraphDialog::on_actionMoveDown10_triggered() +{ + panAxes(0, -10); +} + +void LteRlcGraphDialog::on_actionMoveDown100_triggered() +{ + panAxes(0, -100); +} + +void LteRlcGraphDialog::on_actionMoveUp1_triggered() +{ + panAxes(0, 1); +} + +void LteRlcGraphDialog::on_actionMoveLeft1_triggered() +{ + panAxes(-1, 0); +} + +void LteRlcGraphDialog::on_actionMoveRight1_triggered() +{ + panAxes(1, 0); +} + +void LteRlcGraphDialog::on_actionMoveDown1_triggered() +{ + panAxes(0, -1); +} + +void LteRlcGraphDialog::on_actionSwitchDirection_triggered() +{ + // Channel settings exactly the same, except change direction. + // N.B. do not fail and close if there are no packets in opposite direction. + setChannelInfo(graph_.ueid, + graph_.rlcMode, + graph_.channelType, + graph_.channelId, + !graph_.direction, + true /* maybe_empty */); +} + + + +// Switch between zoom/drag. +void LteRlcGraphDialog::on_actionDragZoom_triggered() +{ + if (mouse_drags_) { + ui->zoomRadioButton->toggle(); + } else { + ui->dragRadioButton->toggle(); + } +} + +void LteRlcGraphDialog::on_dragRadioButton_toggled(bool checked) +{ + if (checked) { + mouse_drags_ = true; + } + ui->rlcPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); +} + +void LteRlcGraphDialog::on_zoomRadioButton_toggled(bool checked) +{ + if (checked) mouse_drags_ = false; + ui->rlcPlot->setInteractions(QCP::Interactions()); +} + +void LteRlcGraphDialog::on_resetButton_clicked() +{ + resetAxes(); +} + +void LteRlcGraphDialog::on_otherDirectionButton_clicked() +{ + on_actionSwitchDirection_triggered(); +} + +// Prompt for filename/format to save graph to. +// N.B. Copied from tcp_stream_dialog.cpp +void LteRlcGraphDialog::on_buttonBox_accepted() +{ + QString file_name, extension; + QDir path(mainApp->openDialogInitialDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + 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)"); + QString filter = QString("%1;;%2;;%3;;%4") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter); + + file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Graph As…")), + path.canonicalPath(), filter, &extension); + + if (file_name.length() > 0) { + bool save_ok = false; + if (extension.compare(pdf_filter) == 0) { + save_ok = ui->rlcPlot->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = ui->rlcPlot->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = ui->rlcPlot->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = ui->rlcPlot->saveJpg(file_name); + } + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } +} + + +// No need to register tap listeners here. This is done +// in calls to the common functions in ui/tap-rlc-graph.c diff --git a/ui/qt/lte_rlc_graph_dialog.h b/ui/qt/lte_rlc_graph_dialog.h new file mode 100644 index 00000000..07661ab2 --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.h @@ -0,0 +1,116 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef LTE_RLC_GRAPH_DIALOG_H +#define LTE_RLC_GRAPH_DIALOG_H + +#include "wireshark_dialog.h" +#include + +#include + +class QMenu; +class QRubberBand; + +namespace Ui { +class LteRlcGraphDialog; +} + +class LteRlcGraphDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + // TODO: will need to add another constructor option to give channel explicitly, + // rather than find in currently selected packet, for when launch graph from + // RLC statistics dialog. + explicit LteRlcGraphDialog(QWidget &parent, CaptureFile &cf, bool channelKnown); + ~LteRlcGraphDialog(); + + void setChannelInfo(guint16 ueid, guint8 rlcMode, + guint16 channelType, guint16 channelId, guint8 direction, + bool maybe_empty=false); + +signals: + void goToPacket(int packet_num); + +protected: + void showEvent(QShowEvent *event); + void keyPressEvent(QKeyEvent *event); + +private: + Ui::LteRlcGraphDialog *ui; + bool mouse_drags_; + QRubberBand *rubber_band_; + QPoint rb_origin_; + QMenu *ctx_menu_; + + // Data gleaned directly from tapping packets (shared with gtk impl) + struct rlc_graph graph_; + + // Data + QMultiMap time_stamp_map_; + QMap sequence_num_map_; + + QCPGraph *base_graph_; // Clickable packets + QCPGraph *reseg_graph_; + QCPGraph *acks_graph_; + QCPGraph *nacks_graph_; + QCPItemTracer *tracer_; + guint32 packet_num_; + + void completeGraph(bool may_be_empty=false); + + bool compareHeaders(rlc_segment *seg); + + void findChannel(bool may_fail=false); + void fillGraph(); + + void zoomAxes(bool in); + void zoomXAxis(bool in); + void zoomYAxis(bool in); + + void panAxes(int x_pixels, int y_pixels); + QRectF getZoomRanges(QRect zoom_rect); + + void toggleTracerStyle(bool force_default); + +private slots: + void graphClicked(QMouseEvent *event); + void mouseMoved(QMouseEvent *event); + void mouseReleased(QMouseEvent *event); + void resetAxes(); + + void on_dragRadioButton_toggled(bool checked); + void on_zoomRadioButton_toggled(bool checked); + void on_resetButton_clicked(); + void on_otherDirectionButton_clicked(); + + void on_actionReset_triggered(); + void on_actionZoomIn_triggered(); + void on_actionZoomOut_triggered(); + void on_actionMoveUp10_triggered(); + void on_actionMoveLeft10_triggered(); + void on_actionMoveRight10_triggered(); + void on_actionMoveDown10_triggered(); + void on_actionMoveUp1_triggered(); + void on_actionMoveLeft1_triggered(); + void on_actionMoveRight1_triggered(); + void on_actionMoveDown1_triggered(); + void on_actionDragZoom_triggered(); + void on_actionMoveUp100_triggered(); + void on_actionMoveDown100_triggered(); + void on_actionGoToPacket_triggered(); + void on_actionCrosshairs_triggered(); + void on_actionSwitchDirection_triggered(); + + void on_buttonBox_accepted(); +}; + +#endif // LTE_RLC_GRAPH_DIALOG_H diff --git a/ui/qt/lte_rlc_graph_dialog.ui b/ui/qt/lte_rlc_graph_dialog.ui new file mode 100644 index 00000000..c2574cdb --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.ui @@ -0,0 +1,398 @@ + + + LteRlcGraphDialog + + + + 0 + 0 + 660 + 447 + + + + Dialog + + + + + + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + + + + true + + + + + + + + + Mouse + + + + + + + Drag using the mouse button. + + + drags + + + true + + + + + + + Select using the mouse button. + + + zooms + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + + + Reset + + + + + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + + + Switch Direction + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save + + + + + + + Reset Graph + + + Reset the graph to its initial state. + + + 0 + + + + + Zoom In + + + Zoom In + + + + + + + + + Zoom Out + + + Zoom Out + + + - + + + + + Move Up 10 Pixels + + + Move Up 10 Pixels + + + Up + + + + + Move Left 10 Pixels + + + Move Left 10 Pixels + + + Left + + + + + Move Right 10 Pixels + + + Move Right 10 Pixels + + + Right + + + + + Move Down 10 Pixels + + + Move Down 10 Pixels + + + Down + + + + + Move Up 1 Pixel + + + Move Up 1 Pixel + + + Shift+Up + + + + + Move Left 1 Pixel + + + Move Left 1 Pixel + + + Shift+Left + + + + + Move Right 1 Pixel + + + Move Right 1 Pixel + + + Shift+Right + + + + + Move Down 1 Pixel + + + Move down 1 Pixel + + + Shift+Down + + + + + Drag / Zoom + + + Toggle mouse drag / zoom behavior + + + Z + + + + + Crosshairs + + + Toggle crosshairs + + + Space + + + + + Move Up 100 Pixels + + + Move Up 100 Pixels + + + PgUp + + + + + Move Up 100 Pixels + + + Move Up 100 Pixels + + + PgDown + + + + + Go To Packet Under Cursor + + + Go to packet currently under the cursor + + + G + + + + + Zoom In X Axis + + + Zoom In X Axis + + + X + + + + + Zoom Out Y Axis + + + Zoom Out Y Axis + + + Shift+Y + + + + + Zoom In Y Axis + + + Zoom In Y Axis + + + Y + + + + + Zoom Out X Axis + + + Zoom Out X Axis + + + Shift+X + + + + + Switch Direction + + + Switch direction (swap between UL and DL) + + + D + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+
+ + + + buttonBox + rejected() + LteRlcGraphDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/lte_rlc_statistics_dialog.cpp b/ui/qt/lte_rlc_statistics_dialog.cpp new file mode 100644 index 00000000..43b5c81b --- /dev/null +++ b/ui/qt/lte_rlc_statistics_dialog.cpp @@ -0,0 +1,1013 @@ +/* lte_rlc_statistics_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "lte_rlc_statistics_dialog.h" + +#include +#include +#include + +#include + +#include +#include +#include + +#include "main_application.h" + +#include "ui/recent.h" + +// TODO: have never tested in a live capture. + +enum { + col_ueid_, + col_mode_, // channel only + col_priority_, // channel only + col_ul_frames_, + col_ul_bytes_, + col_ul_mb_s_, + col_ul_acks_, + col_ul_nacks_, + col_ul_missing_, + col_dl_frames_, + col_dl_bytes_, + col_dl_mb_s_, + col_dl_acks_, + col_dl_nacks_, + col_dl_missing_ +}; + +enum { + rlc_ue_row_type_ = 1000, + rlc_channel_row_type_ +}; + +/* Calculate and return a bandwidth figure, in Mbs */ +static double calculate_bw(const nstime_t *start_time, const nstime_t *stop_time, guint32 bytes) +{ + /* Can only calculate bandwidth if have time delta */ + if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) { + double elapsed_ms = (((double)stop_time->secs - start_time->secs) * 1000) + + (((double)stop_time->nsecs - start_time->nsecs) / 1000000); + + /* Only really meaningful if have a few frames spread over time... + For now at least avoid dividing by something very close to 0.0 */ + if (elapsed_ms < 2.0) { + return 0.0f; + } + + // N.B. very small values will display as scientific notation, but rather that than show 0 + // when there is some traffic.. + return ((bytes * 8) / elapsed_ms) / 1000; + } + else { + return 0.0f; + } +} + + +// Stats kept for one channel. +typedef struct rlc_channel_stats { + guint8 rlcMode; + guint8 priority; + guint16 channelType; + guint16 channelId; + + guint32 UL_frames; + guint32 UL_bytes; + nstime_t UL_time_start; + nstime_t UL_time_stop; + gboolean UL_has_data; // i.e. not just ACKs for DL. + + guint32 DL_frames; + guint32 DL_bytes; + nstime_t DL_time_start; + nstime_t DL_time_stop; + gboolean DL_has_data; // i.e. not just ACKs for UL. + + guint32 UL_acks; + guint32 UL_nacks; + + guint32 DL_acks; + guint32 DL_nacks; + + guint32 UL_missing; + guint32 DL_missing; +} rlc_channel_stats; + +//------------------------------------------------------------------- +// Channel item. +//------------------------------------------------------------------- +class RlcChannelTreeWidgetItem : public QTreeWidgetItem +{ +public: + RlcChannelTreeWidgetItem(QTreeWidgetItem *parent, + unsigned ueid, + unsigned mode, + unsigned channelType, unsigned channelId) : + QTreeWidgetItem(parent, rlc_channel_row_type_), + ueid_(ueid), + channelType_(channelType), + channelId_(channelId), + mode_(mode), + priority_(0) + { + QString mode_str; + switch (mode_) { + case RLC_TM_MODE: + mode_str = QObject::tr("TM"); + break; + case RLC_UM_MODE: + mode_str = QObject::tr("UM"); + break; + case RLC_AM_MODE: + mode_str = QObject::tr("AM"); + break; + case RLC_PREDEF: + mode_str = QObject::tr("Predef"); + break; + + default: + mode_str = QObject::tr("Unknown (%1)").arg(mode_); + break; + } + + // Set name of channel. + switch (channelType) { + case CHANNEL_TYPE_CCCH: + setText(col_ueid_, QObject::tr("CCCH")); + break; + case CHANNEL_TYPE_SRB: + setText(col_ueid_, QObject::tr("SRB-%1").arg(channelId)); + break; + case CHANNEL_TYPE_DRB: + setText(col_ueid_, QObject::tr("DRB-%1").arg(channelId)); + break; + + default: + setText(col_ueid_, QObject::tr("Unknown")); + break; + } + + // Zero out stats. + memset(&stats_, 0, sizeof(stats_)); + + // TODO: could change, but should only reset string if changes. + setText(col_mode_, mode_str); + } + + // Update UE/channels from tap info. + void update(const rlc_lte_tap_info *tap_info) { + + // Copy these fields into UE stats. + if (tap_info->rlcMode != stats_.rlcMode) { + stats_.rlcMode = tap_info->rlcMode; + // TODO: update the column string! + } + + // TODO: these 2 really shouldn't change!! + stats_.channelType = tap_info->channelType; + stats_.channelId = tap_info->channelId; + + if (tap_info->priority != 0) { + priority_ = tap_info->priority; + // TODO: update the column string! + } + + if (tap_info->direction == DIRECTION_UPLINK) { + // Update time range. + if (stats_.UL_frames == 0) { + stats_.UL_time_start = tap_info->rlc_lte_time; + } + stats_.UL_time_stop = tap_info->rlc_lte_time; + + stats_.UL_frames++; + stats_.UL_bytes += tap_info->pduLength; + stats_.UL_nacks += tap_info->noOfNACKs; + stats_.UL_missing += tap_info->missingSNs; + if (tap_info->isControlPDU) { + stats_.UL_acks++; + } + else { + stats_.UL_has_data = TRUE; + } + } + else { + // Update time range. + if (stats_.DL_frames == 0) { + stats_.DL_time_start = tap_info->rlc_lte_time; + } + stats_.DL_time_stop = tap_info->rlc_lte_time; + + stats_.DL_frames++; + stats_.DL_bytes += tap_info->pduLength; + stats_.DL_nacks += tap_info->noOfNACKs; + stats_.DL_missing += tap_info->missingSNs; + if (tap_info->isControlPDU) { + stats_.DL_acks++; + } + else { + stats_.DL_has_data = TRUE; + } + } + } + + // Draw channel entry. + void draw() { + // Calculate bandwidths. + double UL_bw = calculate_bw(&stats_.UL_time_start, + &stats_.UL_time_stop, + stats_.UL_bytes); + double DL_bw = calculate_bw(&stats_.DL_time_start, + &stats_.DL_time_stop, + stats_.DL_bytes); + + // Priority + setText(col_priority_, QString::number(priority_)); + + // Uplink. + setText(col_ul_frames_, QString::number(stats_.UL_frames)); + setText(col_ul_bytes_, QString::number(stats_.UL_bytes)); + setText(col_ul_mb_s_, QString::number(UL_bw)); + setText(col_ul_acks_, QString::number(stats_.UL_acks)); + setText(col_ul_nacks_, QString::number(stats_.UL_nacks)); + setText(col_ul_missing_, QString::number(stats_.UL_missing)); + + // Downlink. + setText(col_dl_frames_, QString::number(stats_.DL_frames)); + setText(col_dl_bytes_, QString::number(stats_.DL_bytes)); + setText(col_dl_mb_s_, QString::number(DL_bw)); + setText(col_dl_acks_, QString::number(stats_.DL_acks)); + setText(col_dl_nacks_, QString::number(stats_.DL_nacks)); + setText(col_dl_missing_, QString::number(stats_.DL_missing)); + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != rlc_channel_row_type_) return QTreeWidgetItem::operator< (other); + const RlcChannelTreeWidgetItem *other_row = static_cast(&other); + + // Switch by selected column. + switch (treeWidget()->sortColumn()) { + case col_ueid_: + // This is channel name. Rank CCCH before SRB before DRB, then channel ID. + return channelRank() < other_row->channelRank(); + case col_mode_: + return mode_ < other_row->mode_; + case col_priority_: + return priority_ < other_row->priority_; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + + // Filter expression for channel. + const QString filterExpression(bool showSR, bool showRACH) { + // Create an expression to match with all traffic for this UE. + QString filter_expr; + + // Are we taking RLC PDUs from MAC, or not? + if (!recent.gui_rlc_use_pdus_from_mac) { + filter_expr += QString("not mac-lte and "); + } + else { + filter_expr += QString("mac-lte and "); + } + + if (showSR) { + filter_expr += QString("(mac-lte.sr-req and mac-lte.ueid == %1) or (").arg(ueid_); + } + + if (showRACH) { + filter_expr += QString("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (").arg(ueid_); + } + + // Main part of expression. + filter_expr += QString("rlc-lte.ueid==%1 and rlc-lte.channel-type == %2"). + arg(ueid_).arg(channelType_); + if ((channelType_ == CHANNEL_TYPE_SRB) || (channelType_ == CHANNEL_TYPE_DRB)) { + filter_expr += QString(" and rlc-lte.channel-id == %1").arg(channelId_); + } + + // Close () if open because of SR + if (showSR) { + filter_expr += QString(")"); + } + // Close () if open because of RACH + if (showRACH) { + filter_expr += QString(")"); + } + + return filter_expr; + } + + // Accessors (queried for launching graph) + unsigned get_ueid() const { return ueid_; } + unsigned get_channelType() const { return channelType_; } + unsigned get_channelId() const { return channelId_; } + unsigned get_mode() const { return mode_; } + + bool hasULData() const { return stats_.UL_has_data != 0; } + bool hasDLData() const { return stats_.DL_has_data != 0; } + + QList rowData() const + { + // Don't output anything for channel entries when exporting to text. + return QList(); + } + +private: + unsigned ueid_; + unsigned channelType_; + unsigned channelId_; + unsigned mode_; + unsigned priority_; + + unsigned channelRank() const + { + switch (channelType_) { + case CHANNEL_TYPE_CCCH: + return 0; + case CHANNEL_TYPE_SRB: + return channelId_; + case CHANNEL_TYPE_DRB: + return 3 + channelId_; + default: + // Shouldn't really get here.. + return 0; + } + } + + rlc_channel_stats stats_; +}; + + +// Stats for one UE. TODO: private to class? +typedef struct rlc_ue_stats { + + guint32 UL_frames; + guint32 UL_total_bytes; + nstime_t UL_time_start; + nstime_t UL_time_stop; + guint32 UL_total_acks; + guint32 UL_total_nacks; + guint32 UL_total_missing; + + guint32 DL_frames; + guint32 DL_total_bytes; + nstime_t DL_time_start; + nstime_t DL_time_stop; + guint32 DL_total_acks; + guint32 DL_total_nacks; + guint32 DL_total_missing; + +} rlc_ue_stats; + +//------------------------------------------------------------------- +// UE item. +//------------------------------------------------------------------- +class RlcUeTreeWidgetItem : public QTreeWidgetItem +{ +public: + RlcUeTreeWidgetItem(QTreeWidget *parent, const rlc_lte_tap_info *rlt_info) : + QTreeWidgetItem (parent, rlc_ue_row_type_), + ueid_(0) + { + ueid_ = rlt_info->ueid; + setText(col_ueid_, QString::number(ueid_)); + + // We create RlcChannelTreeWidgetItems when first data on new channel is seen. + // Of course, there will be a channel associated with the PDU + // that causes this UE item to be created... + memset(&stats_, 0, sizeof(stats_)); + CCCH_stats_ = NULL; + for (int srb=0; srb < 2; srb++) { + srb_stats_[srb] = NULL; + } + for (int drb=0; drb < 32; drb++) { + drb_stats_[drb] = NULL; + } + } + + // Does UE match? + bool isMatch(const rlc_lte_tap_info *rlt_info) { + return ueid_ == rlt_info->ueid; + } + + // Update UE/channels from tap info. + void update(const rlc_lte_tap_info *tap_info) { + + // Are we ignoring RLC frames that were found in MAC frames, or only those + // that were logged separately? + if ((!recent.gui_rlc_use_pdus_from_mac && tap_info->loggedInMACFrame) || + (recent.gui_rlc_use_pdus_from_mac && !tap_info->loggedInMACFrame)) { + return; + } + + // TODO: update title with number of UEs and frames like MAC does? + + // N.B. not really expecting to see common stats - ignoring them. + switch (tap_info->channelType) { + case CHANNEL_TYPE_BCCH_BCH: + case CHANNEL_TYPE_BCCH_DL_SCH: + case CHANNEL_TYPE_PCCH: + return; + + default: + // Drop through for UE-specific. + break; + } + + // UE-level traffic stats. + if (tap_info->direction == DIRECTION_UPLINK) { + // Update time range. + if (stats_.UL_frames == 0) { + stats_.UL_time_start = tap_info->rlc_lte_time; + } + stats_.UL_time_stop = tap_info->rlc_lte_time; + + stats_.UL_frames++; + stats_.UL_total_bytes += tap_info->pduLength; + + // Status PDU counters. + if (tap_info->isControlPDU) { + stats_.UL_total_acks++; + stats_.UL_total_nacks += tap_info->noOfNACKs; + } + + stats_.UL_total_missing += tap_info->missingSNs; + } + else { + // Update time range. + if (stats_.DL_frames == 0) { + stats_.DL_time_start = tap_info->rlc_lte_time; + } + stats_.DL_time_stop = tap_info->rlc_lte_time; + + stats_.DL_frames++; + stats_.DL_total_bytes += tap_info->pduLength; + + // Status PDU counters. + if (tap_info->isControlPDU) { + stats_.DL_total_acks++; + stats_.DL_total_nacks += tap_info->noOfNACKs; + } + + stats_.DL_total_missing += tap_info->missingSNs; + } + + RlcChannelTreeWidgetItem *channel_item; + + // Find or create tree item for this channel. + switch (tap_info->channelType) { + case CHANNEL_TYPE_CCCH: + channel_item = CCCH_stats_; + if (channel_item == NULL) { + channel_item = CCCH_stats_ = + new RlcChannelTreeWidgetItem(this, tap_info->ueid, RLC_TM_MODE, + tap_info->channelType, tap_info->channelId); + } + break; + + case CHANNEL_TYPE_SRB: + channel_item = srb_stats_[tap_info->channelId-1]; + if (channel_item == NULL) { + channel_item = srb_stats_[tap_info->channelId-1] = + new RlcChannelTreeWidgetItem(this, tap_info->ueid, RLC_AM_MODE, + tap_info->channelType, tap_info->channelId); + } + break; + + case CHANNEL_TYPE_DRB: + channel_item = drb_stats_[tap_info->channelId-1]; + if (channel_item == NULL) { + channel_item = drb_stats_[tap_info->channelId-1] = + new RlcChannelTreeWidgetItem(this, tap_info->ueid, tap_info->rlcMode, + tap_info->channelType, tap_info->channelId); + } + break; + + default: + // Shouldn't get here... + return; + } + + // Update channel with tap_info. + channel_item->update(tap_info); + } + + // Draw UE entry + void draw() { + // Fixed fields only drawn once from constructor so don't redraw here. + + /* Calculate bandwidths. */ + double UL_bw = calculate_bw(&stats_.UL_time_start, + &stats_.UL_time_stop, + stats_.UL_total_bytes); + double DL_bw = calculate_bw(&stats_.DL_time_start, + &stats_.DL_time_stop, + stats_.DL_total_bytes); + + // Uplink. + setText(col_ul_frames_, QString::number(stats_.UL_frames)); + setText(col_ul_bytes_, QString::number(stats_.UL_total_bytes)); + setText(col_ul_mb_s_, QString::number(UL_bw)); + setText(col_ul_acks_, QString::number(stats_.UL_total_acks)); + setText(col_ul_nacks_, QString::number(stats_.UL_total_nacks)); + setText(col_ul_missing_, QString::number(stats_.UL_total_missing)); + + // Downlink. + setText(col_dl_frames_, QString::number(stats_.DL_frames)); + setText(col_dl_bytes_, QString::number(stats_.DL_total_bytes)); + setText(col_dl_mb_s_, QString::number(DL_bw)); + setText(col_dl_acks_, QString::number(stats_.DL_total_acks)); + setText(col_dl_nacks_, QString::number(stats_.DL_total_nacks)); + setText(col_dl_missing_, QString::number(stats_.DL_total_missing)); + + // Call draw() for each channel for this UE. + if (CCCH_stats_ != NULL) { + CCCH_stats_->draw(); + } + for (int srb=0; srb < 2; srb++) { + if (srb_stats_[srb] != NULL) { + srb_stats_[srb]->draw(); + } + } + for (int drb=0; drb < 32; drb++) { + if (drb_stats_[drb] != NULL) { + drb_stats_[drb]->draw(); + } + } + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != rlc_ue_row_type_) return QTreeWidgetItem::operator< (other); + const RlcUeTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case col_ueid_: + return ueid_ < other_row->ueid_; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + + // Filter expression for UE. + const QString filterExpression(bool showSR, bool showRACH) { + // Create an expression to match with all traffic for this UE. + QString filter_expr; + + // Are we taking RLC PDUs from MAC, or not? + if (!recent.gui_rlc_use_pdus_from_mac) { + filter_expr += QString("not mac-lte and "); + } + else { + filter_expr += QString("mac-lte and "); + } + + if (showSR) { + filter_expr += QString("(mac-lte.sr-req and mac-lte.ueid == %1) or (").arg(ueid_); + } + + if (showRACH) { + filter_expr += QString("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (").arg(ueid_); + } + + filter_expr += QString("rlc-lte.ueid==%1").arg(ueid_); + + // Close () if open because of SR + if (showSR) { + filter_expr += QString(")"); + } + // Close () if open because of RACH + if (showRACH) { + filter_expr += QString(")"); + } + + return filter_expr; + } + + QList rowData() const + { + QList row_data; + + // Key fields. + // After the UEId field, there are 2 unused columns for UE entries. + row_data << ueid_ << QString("") << QString(""); + + // UL + row_data << stats_.UL_frames << stats_.UL_total_bytes + << calculate_bw(&stats_.UL_time_start, + &stats_.UL_time_stop, + stats_.UL_total_bytes) + << stats_.UL_total_acks << stats_.UL_total_nacks << stats_.UL_total_missing; + + // DL + row_data << stats_.DL_frames << stats_.DL_total_bytes + << calculate_bw(&stats_.DL_time_start, + &stats_.DL_time_stop, + stats_.DL_total_bytes) + << stats_.DL_total_acks << stats_.DL_total_nacks << stats_.DL_total_missing; + return row_data; + } + +private: + unsigned ueid_; + rlc_ue_stats stats_; + + // Channel counters stored in channel sub-items. + RlcChannelTreeWidgetItem* CCCH_stats_; + RlcChannelTreeWidgetItem* srb_stats_[2]; + RlcChannelTreeWidgetItem* drb_stats_[32]; +}; + + +// Only the first 3 columns headings differ between UE and channel rows. + +static const QString ue_col_0_title_ = QObject::tr("UE Id"); +static const QString ue_col_1_title_ = QObject::tr(""); +static const QString ue_col_2_title_ = QObject::tr(""); + +static const QString channel_col_0_title_ = QObject::tr("Name"); +static const QString channel_col_1_title_ = QObject::tr("Mode"); +static const QString channel_col_2_title_ = QObject::tr("Priority"); + + + +//------------------------------------------------------------------------------------------ +// Dialog + +// Constructor. +LteRlcStatisticsDialog::LteRlcStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) : + TapParameterDialog(parent, cf, HELP_STATS_LTE_MAC_TRAFFIC_DIALOG), + cf_(cf), + packet_count_(0) +{ + setWindowSubtitle(tr("LTE RLC Statistics")); + loadGeometry((parent.width() * 5) / 5, (parent.height() * 3) / 4, "LTERLCStatisticsDialog"); + + // Create a grid for filtering-related widgetsto also appear in layout. + int filter_controls_layout_idx = verticalLayout()->indexOf(filterLayout()->widget()); + QGridLayout *filter_controls_grid = new QGridLayout(); + // Insert into the vertical layout + verticalLayout()->insertLayout(filter_controls_layout_idx, filter_controls_grid); + int one_em = fontMetrics().height(); + filter_controls_grid->setColumnMinimumWidth(2, one_em * 2); + filter_controls_grid->setColumnStretch(2, 1); + filter_controls_grid->setColumnMinimumWidth(5, one_em * 2); + filter_controls_grid->setColumnStretch(5, 1); + + // Add individual controls into the grid + launchULGraph_ = new QPushButton(QString("Launch UL Graph")); + launchULGraph_->setEnabled(false); + filter_controls_grid->addWidget(launchULGraph_); + connect(launchULGraph_, SIGNAL(clicked()), this, SLOT(launchULGraphButtonClicked())); + launchDLGraph_ = new QPushButton(QString("Launch DL Graph")); + launchDLGraph_->setEnabled(false); + filter_controls_grid->addWidget(launchDLGraph_); + connect(launchDLGraph_, SIGNAL(clicked()), this, SLOT(launchDLGraphButtonClicked())); + + showSRFilterCheckBox_ = new QCheckBox(tr("Include SR frames in filter")); + filter_controls_grid->addWidget(showSRFilterCheckBox_); + showRACHFilterCheckBox_ = new QCheckBox(tr("Include RACH frames in filter")); + filter_controls_grid->addWidget(showRACHFilterCheckBox_); + + useRLCFramesFromMacCheckBox_ = new QCheckBox(tr("Use RLC frames only from MAC frames")); + useRLCFramesFromMacCheckBox_->setCheckState(recent.gui_rlc_use_pdus_from_mac ? + Qt::Checked : + Qt::Unchecked); + connect(useRLCFramesFromMacCheckBox_, SIGNAL(clicked(bool)), this, + SLOT(useRLCFramesFromMacCheckBoxToggled(bool))); + filter_controls_grid->addWidget(useRLCFramesFromMacCheckBox_); + + QStringList header_labels = QStringList() + << "" << "" << "" + << tr("UL Frames") << tr("UL Bytes") << tr("UL MB/s") + << tr("UL ACKs") << tr("UL NACKs") << tr("UL Missing") + << tr("DL Frames") << tr("DL Bytes") << tr("DL MB/s") + << tr("DL ACKs") << tr("DL NACKs") << tr("DL Missing"); + statsTreeWidget()->setHeaderLabels(header_labels); + updateHeaderLabels(); + + statsTreeWidget()->sortByColumn(col_ueid_, Qt::AscendingOrder); + + // resizeColumnToContents doesn't work well here, so set sizes manually. + for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) { + switch (col) { + case col_ueid_: + statsTreeWidget()->setColumnWidth(col, one_em * 7); + break; + case col_ul_frames_: + case col_dl_frames_: + statsTreeWidget()->setColumnWidth(col, one_em * 5); + break; + case col_ul_acks_: + case col_dl_acks_: + statsTreeWidget()->setColumnWidth(col, one_em * 5); + break; + case col_ul_nacks_: + case col_dl_nacks_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + case col_ul_missing_: + case col_dl_missing_: + statsTreeWidget()->setColumnWidth(col, one_em * 7); + break; + case col_ul_mb_s_: + case col_dl_mb_s_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + + default: + // The rest are numeric. + statsTreeWidget()->setColumnWidth(col, one_em * 4); + break; + } + } + + addFilterActions(); + + if (filter) { + setDisplayFilter(filter); + } + + // Set handler for when the tree item changes to set the appropriate labels. + connect(statsTreeWidget(), SIGNAL(itemSelectionChanged()), + this, SLOT(updateItemSelectionChanged())); + + // Set handler for when display filter string is changed. + connect(this, SIGNAL(updateFilter(QString)), + this, SLOT(filterUpdated(QString))); +} + +// Destructor. +LteRlcStatisticsDialog::~LteRlcStatisticsDialog() +{ +} + +void LteRlcStatisticsDialog::tapReset(void *ws_dlg_ptr) +{ + LteRlcStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + if (!ws_dlg) { + return; + } + + // Clears/deletes all UEs. + ws_dlg->statsTreeWidget()->clear(); + ws_dlg->packet_count_ = 0; +} + +// Process the tap info from a dissected RLC PDU. +// Returns TAP_PACKET_REDRAW if a redraw is needed, TAP_PACKET_DONT_REDRAW otherwise. +tap_packet_status LteRlcStatisticsDialog::tapPacket(void *ws_dlg_ptr, struct _packet_info *, epan_dissect *, const void *rlc_lte_tap_info_ptr, tap_flags_t) +{ + // Look up dialog. + LteRlcStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + const rlc_lte_tap_info *rlt_info = (const rlc_lte_tap_info *) rlc_lte_tap_info_ptr; + if (!ws_dlg || !rlt_info) { + return TAP_PACKET_DONT_REDRAW; + } + + ws_dlg->incFrameCount(); + + // Look for this UE (TODO: avoid linear search if have lots of UEs in capture...) + RlcUeTreeWidgetItem *ue_ti = NULL; + for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { + QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); + if (ti->type() != rlc_ue_row_type_) continue; + RlcUeTreeWidgetItem *cur_ru_ti = static_cast(ti); + + if (cur_ru_ti->isMatch(rlt_info)) { + ue_ti = cur_ru_ti; + break; + } + } + + if (!ue_ti) { + // Existing UE wasn't found so create a new one. + ue_ti = new RlcUeTreeWidgetItem(ws_dlg->statsTreeWidget(), rlt_info); + for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) { + // int QTreeWidgetItem::textAlignment(int column) const + // Returns the text alignment for the label in the given column. + // Note: This function returns an int for historical reasons. It will be corrected to return Qt::Alignment in Qt 7. + ue_ti->setTextAlignment(col, static_cast(ws_dlg->statsTreeWidget()->headerItem()->textAlignment(col))); + } + } + + // Update the UE from the information in the tap structure. + ue_ti->update(rlt_info); + + return TAP_PACKET_REDRAW; +} + +void LteRlcStatisticsDialog::tapDraw(void *ws_dlg_ptr) +{ + // Look up UE. + LteRlcStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + if (!ws_dlg) { + return; + } + + // Draw each UE. + for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { + QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); + if (ti->type() != rlc_ue_row_type_) continue; + + RlcUeTreeWidgetItem *ru_ti = static_cast(ti); + ru_ti->draw(); + } + + // Update title + ws_dlg->setWindowSubtitle(QString("LTE RLC Statistics (%1 UEs, %2 frames)"). + arg(ws_dlg->statsTreeWidget()->topLevelItemCount()).arg(ws_dlg->getFrameCount())); +} + +void LteRlcStatisticsDialog::useRLCFramesFromMacCheckBoxToggled(bool state) +{ + // Update state to be stored in recent preferences + recent.gui_rlc_use_pdus_from_mac = state; + + // Retap to get updated list of PDUs + fillTree(); +} + +const QString LteRlcStatisticsDialog::filterExpression() +{ + QString filter_expr; + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + + // Generate expression according to what type of item is selected. + if (ti->type() == rlc_ue_row_type_) { + RlcUeTreeWidgetItem *ru_ti = static_cast(ti); + filter_expr = ru_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked, + showRACHFilterCheckBox_->checkState() > Qt::Unchecked); + } else if (ti->type() == rlc_channel_row_type_) { + RlcChannelTreeWidgetItem *rc_ti = static_cast(ti); + filter_expr = rc_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked, + showRACHFilterCheckBox_->checkState() > Qt::Unchecked); + } + } + return filter_expr; +} + +void LteRlcStatisticsDialog::fillTree() +{ + if (!registerTapListener("rlc-lte", + this, + displayFilter_.toLatin1().data(), + TL_REQUIRES_NOTHING, + tapReset, + tapPacket, + tapDraw)) { + reject(); + return; + } + + cap_file_.retapPackets(); + tapDraw(this); + removeTapListeners(); + +} + +void LteRlcStatisticsDialog::updateItemSelectionChanged() +{ + updateHeaderLabels(); + + bool enableULGraphButton = false, enableDLGraphButton = false; + if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + RlcChannelTreeWidgetItem *rc_ti = static_cast(ti); + enableULGraphButton = rc_ti->hasULData(); + enableDLGraphButton = rc_ti->hasDLData(); + } + + // Only enabling graph buttons for channel entries. + launchULGraph_->setEnabled(enableULGraphButton); + launchDLGraph_->setEnabled(enableDLGraphButton); +} + +void LteRlcStatisticsDialog::updateHeaderLabels() +{ + if (statsTreeWidget()->selectedItems().count() > 0 && + statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { + + // UE column headings. + statsTreeWidget()->headerItem()->setText(col_ueid_, channel_col_0_title_); + statsTreeWidget()->headerItem()->setText(col_mode_, channel_col_1_title_); + statsTreeWidget()->headerItem()->setText(col_priority_, channel_col_2_title_); + } else { + // Channel column headings. + statsTreeWidget()->headerItem()->setText(col_ueid_, ue_col_0_title_); + statsTreeWidget()->headerItem()->setText(col_mode_, ue_col_1_title_); + statsTreeWidget()->headerItem()->setText(col_priority_, ue_col_2_title_); + } +} + +void LteRlcStatisticsDialog::captureFileClosing() +{ + remove_tap_listener(this); + + WiresharkDialog::captureFileClosing(); +} + +// Launch a UL graph for the currently-selected channel. +void LteRlcStatisticsDialog::launchULGraphButtonClicked() +{ + if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { + // Get the channel item. + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + RlcChannelTreeWidgetItem *rc_ti = static_cast(ti); + emit launchRLCGraph(true, + rc_ti->get_ueid(), + rc_ti->get_mode(), + rc_ti->get_channelType(), + rc_ti->get_channelId(), + DIRECTION_UPLINK); + } +} + +// Launch a DL graph for the currently-selected channel. +void LteRlcStatisticsDialog::launchDLGraphButtonClicked() +{ + if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { + // Get the channel item. + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + RlcChannelTreeWidgetItem *rc_ti = static_cast(ti); + emit launchRLCGraph(true, + rc_ti->get_ueid(), + rc_ti->get_mode(), + rc_ti->get_channelType(), + rc_ti->get_channelId(), + DIRECTION_DOWNLINK); + } +} + + +// Store filter from signal. +void LteRlcStatisticsDialog::filterUpdated(QString filter) +{ + displayFilter_ = filter; +} + +// Get the item for the row, depending upon the type of tree item. +QList LteRlcStatisticsDialog::treeItemData(QTreeWidgetItem *item) const +{ + // Cast up to our type. + RlcChannelTreeWidgetItem *channel_item = dynamic_cast(item); + if (channel_item) { + return channel_item->rowData(); + } + RlcUeTreeWidgetItem *ue_item = dynamic_cast(item); + if (ue_item) { + return ue_item->rowData(); + } + + // Need to return something.. + return QList(); +} + + +// Stat command + args + +static void +lte_rlc_statistics_init(const char *args, void*) { + QStringList args_l = QString(args).split(','); + QByteArray filter; + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(",").toUtf8(); + } + mainApp->emitStatCommandSignal("LteRlcStatistics", filter.constData(), NULL); +} + +static stat_tap_ui lte_rlc_statistics_ui = { + REGISTER_STAT_GROUP_TELEPHONY_LTE, + QT_TRANSLATE_NOOP("LteRlcStatisticsDialog", "RLC Statistics"), + "rlc-lte,stat", + lte_rlc_statistics_init, + 0, + NULL +}; + +extern "C" { + +void register_tap_listener_qt_lte_rlc_statistics(void); + +void +register_tap_listener_qt_lte_rlc_statistics(void) +{ + register_stat_tap_ui(<e_rlc_statistics_ui, NULL); +} + +} diff --git a/ui/qt/lte_rlc_statistics_dialog.h b/ui/qt/lte_rlc_statistics_dialog.h new file mode 100644 index 00000000..50e1af92 --- /dev/null +++ b/ui/qt/lte_rlc_statistics_dialog.h @@ -0,0 +1,70 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __LTE_RLC_STATISTICS_DIALOG_H__ +#define __LTE_RLC_STATISTICS_DIALOG_H__ + +#include "tap_parameter_dialog.h" + +#include + +class LteRlcStatisticsDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + LteRlcStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter); + ~LteRlcStatisticsDialog(); + + unsigned getFrameCount() { return packet_count_; } + void incFrameCount() { ++packet_count_; } + +protected: + void captureFileClosing(); + +signals: + void launchRLCGraph(bool channelKnown, + guint16 ueid, guint8 rlcMode, + guint16 channelType, guint16 channelId, + guint8 direction); + +private: + // Extra controls needed for this dialog. + QCheckBox *useRLCFramesFromMacCheckBox_; + QCheckBox *showSRFilterCheckBox_; + QCheckBox *showRACHFilterCheckBox_; + QPushButton *launchULGraph_; + QPushButton *launchDLGraph_; + QString displayFilter_; + + CaptureFile &cf_; + int packet_count_; + + // Callbacks for register_tap_listener + static void tapReset(void *ws_dlg_ptr); + static tap_packet_status tapPacket(void *ws_dlg_ptr, struct _packet_info *, struct epan_dissect *, const void *rlc_lte_tap_info_ptr, tap_flags_t flags); + static void tapDraw(void *ws_dlg_ptr); + + void updateHeaderLabels(); + + virtual const QString filterExpression(); + + QList treeItemData(QTreeWidgetItem *item) const; + +private slots: + virtual void fillTree(); + void updateItemSelectionChanged(); + + void useRLCFramesFromMacCheckBoxToggled(bool state); + void launchULGraphButtonClicked(); + void launchDLGraphButtonClicked(); + void filterUpdated(QString filter); +}; + +#endif // __LTE_RLC_STATISTICS_DIALOG_H__ diff --git a/ui/qt/main.cpp b/ui/qt/main.cpp new file mode 100644 index 00000000..582855f2 --- /dev/null +++ b/ui/qt/main.cpp @@ -0,0 +1,1149 @@ +/* main.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#define WS_LOG_DOMAIN LOG_DOMAIN_MAIN + +#include + +#include + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PLUGINS +#include +#endif +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_KERBEROS +#include +#include +#include +#endif + +#include + +#include + +/* general (not Qt specific) */ +#include "file.h" +#include "epan/color_filters.h" + +#include "epan/rtd_table.h" +#include "epan/srt_table.h" + +#include "ui/alert_box.h" +#include "ui/iface_lists.h" +#include "ui/language.h" +#include "ui/persfilepath_opt.h" +#include "ui/recent.h" +#include "ui/simple_dialog.h" +#include "ui/util.h" +#include "ui/dissect_opts.h" +#include "ui/commandline.h" +#include "ui/capture_ui_utils.h" +#include "ui/preference_utils.h" +#include "ui/software_update.h" +#include "ui/taps.h" + +#include "ui/qt/conversation_dialog.h" +#include "ui/qt/utils/color_utils.h" +#include "ui/qt/coloring_rules_dialog.h" +#include "ui/qt/endpoint_dialog.h" +#include "ui/qt/glib_mainloop_on_qeventloop.h" +#include "ui/qt/wireshark_main_window.h" +#include "ui/qt/response_time_delay_dialog.h" +#include "ui/qt/service_response_time_dialog.h" +#include "ui/qt/simple_dialog.h" +#include "ui/qt/simple_statistics_dialog.h" +#include +#include "ui/qt/wireshark_application.h" + +#include "capture/capture-pcap-util.h" + +#include +#include + +#ifdef _WIN32 +# include "capture/capture-wpcap.h" +# include +#endif /* _WIN32 */ + +#ifdef HAVE_AIRPCAP +# include +# include +//# include "airpcap_dlg.h" +//# include "airpcap_gui_utils.h" +#endif + +#include "epan/crypt/dot11decrypt_ws.h" + +/* Handle the addition of View menu items without request */ +#if defined(Q_OS_MAC) +#include +#endif + +#include + +//#define DEBUG_STARTUP_TIME 1 + +/* update the main window */ +void main_window_update(void) +{ + WiresharkApplication::processEvents(); +} + +void exit_application(int status) { + if (wsApp) { + wsApp->quit(); + } + exit(status); +} + +/* + * Report an error in command-line arguments. + * + * On Windows, Wireshark is built for the Windows subsystem, and runs + * without a console, so we create a console on Windows to receive the + * output. + * + * See create_console(), in ui/win32/console_win32.c, for an example + * of code to check whether we need to create a console. + * + * On UN*Xes: + * + * If Wireshark is run from the command line, its output either goes + * to the terminal or to wherever the standard error was redirected. + * + * If Wireshark is run by executing it as a remote command, e.g. with + * ssh, its output either goes to whatever socket was set up for the + * remote command's standard error or to wherever the standard error + * was redirected. + * + * If Wireshark was run from the GUI, e.g. by double-clicking on its + * icon or on a file that it opens, there are no guarantees as to + * where the standard error went. It could be going to /dev/null + * (current macOS), or to a socket to systemd for the journal, or + * to a log file in the user's home directory, or to the "console + * device" ("workstation console"), or.... + * + * Part of determining that, at least for locally-run Wireshark, + * is to try to open /dev/tty to determine whether the process + * has a controlling terminal. (It fails, at a minimum, for + * Wireshark launched from the GUI under macOS, Ubuntu with GNOME, + * and Ubuntu with KDE; in all cases, an attempt to open /dev/tty + * fails with ENXIO.) If it does have a controlling terminal, + * write to the standard error, otherwise assume that the standard + * error might not go anywhere that the user will be able to see. + * That doesn't handle the "run by ssh" case, however; that will + * not have a controlling terminal. (This means running it by + * remote execution, not by remote login.) Perhaps there's an + * environment variable to check there. + */ +// xxx copied from ../gtk/main.c +static void +wireshark_cmdarg_err(const char *fmt, va_list ap) +{ +#ifdef _WIN32 + create_console(); +#endif + fprintf(stderr, "wireshark: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); +} + +/* + * Report additional information for an error in command-line arguments. + * Creates a console on Windows. + */ +// xxx copied from ../gtk/main.c +static void +wireshark_cmdarg_err_cont(const char *fmt, va_list ap) +{ +#ifdef _WIN32 + create_console(); +#endif + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); +} + +void +gather_wireshark_qt_compiled_info(feature_list l) +{ +#ifdef QT_VERSION + with_feature(l, "Qt %s", QT_VERSION_STR); +#else + with_feature(l, "Qt (version unknown)"); +#endif + gather_caplibs_compile_info(l); + epan_gather_compile_info(l); +#ifdef QT_MULTIMEDIA_LIB + with_feature(l, "QtMultimedia"); +#else + without_feature(l, "QtMultimedia"); +#endif + + const char *update_info = software_update_info(); + if (update_info) { + with_feature(l, "automatic updates using %s", update_info); + } else { + without_feature(l, "automatic updates"); + } +#ifdef _WIN32 +#ifdef HAVE_AIRPCAP + gather_airpcap_compile_info(l); +#else + without_feature(l, "AirPcap"); +#endif +#endif /* _WIN32 */ + +#ifdef HAVE_MINIZIP + with_feature(l, "Minizip"); +#else + without_feature(l, "Minizip"); +#endif +} + +void +gather_wireshark_runtime_info(feature_list l) +{ + with_feature(l, "Qt %s", qVersion()); +#ifdef HAVE_LIBPCAP + gather_caplibs_runtime_info(l); +#endif + epan_gather_runtime_info(l); + +#ifdef HAVE_AIRPCAP + gather_airpcap_runtime_info(l); +#endif + + if (mainApp) { + // Display information + const char *display_mode = ColorUtils::themeIsDark() ? "dark" : "light"; + with_feature(l, "%s display mode", display_mode); + + int hidpi_count = 0; + foreach (QScreen *screen, mainApp->screens()) { + if (screen->devicePixelRatio() > 1.0) { + hidpi_count++; + } + } + if (hidpi_count == mainApp->screens().count()) { + with_feature(l, "HiDPI"); + } else if (hidpi_count) { + with_feature(l, "mixed DPI"); + } else { + without_feature(l, "HiDPI"); + } + QString session = qEnvironmentVariable("XDG_SESSION_TYPE"); + if (!session.isEmpty()) { + if (session == "wayland") { + with_feature(l, "Wayland"); + } else if (session == "x11") { + with_feature(l, "Xorg"); + } else { + with_feature(l, "XDG_SESSION_TYPE=%s", qUtf8Printable(session)); + } + } + QString platform = qApp->platformName(); + if (!platform.isEmpty()) { + with_feature(l, "QPA plugin \"%s\"", qUtf8Printable(platform)); + } + } +} + +static void +qt_log_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + enum ws_log_level log_level = LOG_LEVEL_NONE; + + // QMessageLogContext may contain null/zero for release builds. + const char *file = context.file; + int line = context.line > 0 ? context.line : -1; + const char *func = context.function; + + switch (type) { + case QtInfoMsg: + log_level = LOG_LEVEL_INFO; + // Omit the file/line/function for this level. + file = nullptr; + line = -1; + func = nullptr; + break; + case QtWarningMsg: + log_level = LOG_LEVEL_WARNING; + break; + case QtCriticalMsg: + log_level = LOG_LEVEL_CRITICAL; + break; + case QtFatalMsg: + log_level = LOG_LEVEL_ERROR; + break; + // We want qDebug() messages to show up always for temporary print-outs. + case QtDebugMsg: + log_level = LOG_LEVEL_ECHO; + break; + } + + // Qt gives the full method declaration as the function. Our convention + // (following the C/C++ standards) is to display only the function name. + // Hack the name into the message as a workaround to avoid formatting + // issues. + if (func != nullptr) { + ws_log_full(LOG_DOMAIN_QTUI, log_level, file, line, nullptr, + "%s -- %s", func, qUtf8Printable(msg)); + } + else { + ws_log_full(LOG_DOMAIN_QTUI, log_level, file, line, nullptr, + "%s", qUtf8Printable(msg)); + } +} + +#ifdef HAVE_LIBPCAP +/* Check if there's something important to tell the user during startup. + * We want to do this *after* showing the main window so that any windows + * we pop up will be above the main window. + */ +static void +check_and_warn_user_startup() +{ + gchar *cur_user, *cur_group; + + /* Tell the user not to run as root. */ + if (running_with_special_privs() && recent.privs_warn_if_elevated) { + cur_user = get_cur_username(); + cur_group = get_cur_groupname(); + simple_message_box(ESD_TYPE_WARN, &recent.privs_warn_if_elevated, + "Running as user \"%s\" and group \"%s\".\n" + "This could be dangerous.\n\n" + "If you're running Wireshark this way in order to perform live capture, " + "you may want to be aware that there is a better way documented at\n" + WS_WIKI_URL("CaptureSetup/CapturePrivileges"), cur_user, cur_group); + g_free(cur_user); + g_free(cur_group); + } +} +#endif + +#if defined(_WIN32) && !defined(__MINGW32__) +// Try to avoid library search path collisions. QCoreApplication will +// search QT_INSTALL_PREFIX/plugins for platform DLLs before searching +// the application directory. If +// +// - You have Qt version 5.x.y installed in the default location +// (C:\Qt\5.x) on your machine. +// +// and +// +// - You install Wireshark that was built on a machine with Qt version +// 5.x.z installed in the default location. +// +// Qt5Core.dll will load qwindows.dll from your local C:\Qt\5.x\...\plugins +// directory. This may not be compatible with qwindows.dll from that +// same path on the build machine. At any rate, loading DLLs from paths +// you don't control is ill-advised. We work around this by removing every +// path except our application directory. +// +// NOTE: This does not apply to MinGW-w64 using MSYS2. In that case we use +// the system's Qt plugins with the default search paths. +// +// NOTE 2: When using MinGW-w64 without MSYS2 we also search for the system DLLS, +// because our installer might not have deployed them. + +static inline void +win32_reset_library_path(void) +{ + QString app_path = QDir(get_progfile_dir()).path(); + foreach (QString path, QCoreApplication::libraryPaths()) { + QCoreApplication::removeLibraryPath(path); + } + QCoreApplication::addLibraryPath(app_path); +} +#endif + +#ifdef Q_OS_MAC +// Try to work around +// +// https://gitlab.com/wireshark/wireshark/-/issues/17075 +// +// aka +// +// https://bugreports.qt.io/browse/QTBUG-87014 +// +// The fix at +// +// https://codereview.qt-project.org/c/qt/qtbase/+/322228/3/src/plugins/platforms/cocoa/qnsview_drawing.mm +// +// enables layer backing if we're running on Big Sur OR we're running on +// Catalina AND we were built with the Catalina SDK. Enable layer backing +// here by setting QT_MAC_WANTS_LAYER=1, but only if we're running on Big +// Sur and our version of Qt doesn't have a fix for QTBUG-87014. +#include +static inline void +macos_enable_layer_backing(void) +{ + // At the time of this writing, the QTBUG-87014 for layerEnabledByMacOS is... + // + // ...in https://github.com/qt/qtbase/blob/5.12/src/plugins/platforms/cocoa/qnsview_drawing.mm + // ...not in https://github.com/qt/qtbase/blob/5.12.10/src/plugins/platforms/cocoa/qnsview_drawing.mm + // ...in https://github.com/qt/qtbase/blob/5.15/src/plugins/platforms/cocoa/qnsview_drawing.mm + // ...not in https://github.com/qt/qtbase/blob/5.15.2/src/plugins/platforms/cocoa/qnsview_drawing.mm + // ...not in https://github.com/qt/qtbase/blob/6.0/src/plugins/platforms/cocoa/qnsview_drawing.mm + // ...not in https://github.com/qt/qtbase/blob/6.0.0/src/plugins/platforms/cocoa/qnsview_drawing.mm + // + // We'll assume that it will be fixed in 5.12.11, 5.15.3, and 6.0.1. + // Note that we only ship LTS versions of Qt with our macOS packages. + // Feel free to add other versions if needed. +#if \ + (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) && QT_VERSION < QT_VERSION_CHECK(5, 12, 11) \ + || (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) && QT_VERSION < QT_VERSION_CHECK(5, 15, 3)) \ + || (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 1)) \ + ) + QOperatingSystemVersion os_ver = QOperatingSystemVersion::current(); + int major_ver = os_ver.majorVersion(); + int minor_ver = os_ver.minorVersion(); + if ( (major_ver == 10 && minor_ver >= 16) || major_ver >= 11 ) { + if (qgetenv("QT_MAC_WANTS_LAYER").isEmpty()) { + qputenv("QT_MAC_WANTS_LAYER", "1"); + } + } +#endif +} +#endif + +#ifdef HAVE_LIBPCAP +static GList * +capture_opts_get_interface_list(int *err, char **err_str) +{ + /* + * XXX - should this pass an update callback? + * We already have a window up by the time we start parsing + * the majority of the command-line arguments, because + * we need to do a bunch of initialization work before + * parsing those arguments, and we want to let the user + * know that we're doing that initialization, given that + * it can take a while. + */ + return capture_interface_list(err, err_str, NULL); +} +#endif + +/* And now our feature presentation... [ fade to music ] */ +int main(int argc, char *qt_argv[]) +{ + WiresharkMainWindow *main_w; + +#ifdef _WIN32 + LPWSTR *wc_argv; + int wc_argc; +#endif + int ret_val = EXIT_SUCCESS; + char **argv = qt_argv; + + char *rf_path; + int rf_open_errno; +#ifdef HAVE_LIBPCAP + gchar *err_str, *err_str_secondary;; +#else +#ifdef _WIN32 +#ifdef HAVE_AIRPCAP + gchar *err_str; +#endif +#endif +#endif + gchar *err_msg = NULL; + df_error_t *df_err = NULL; + + QString dfilter, read_filter; +#ifdef HAVE_LIBPCAP + int caps_queries = 0; +#endif + /* Start time in microseconds */ + guint64 start_time = g_get_monotonic_time(); + static const struct report_message_routines wireshark_report_routines = { + vfailure_alert_box, + vwarning_alert_box, + open_failure_alert_box, + read_failure_alert_box, + write_failure_alert_box, + cfile_open_failure_alert_box, + cfile_dump_open_failure_alert_box, + cfile_read_failure_alert_box, + cfile_write_failure_alert_box, + cfile_close_failure_alert_box + }; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + /* + * See: + * + * issue #16908; + * + * https://doc.qt.io/qt-5/qvector.html#maximum-size-and-out-of-memory-conditions + * + * https://forum.qt.io/topic/114950/qvector-realloc-throwing-sigsegv-when-very-large-surface3d-is-rendered + * + * for why we're doing this; the widget we use for the packet list + * uses QVector, so those limitations apply to it. + * + * Apparently, this will be fixed in Qt 6: + * + * https://github.com/qt/qtbase/commit/215ca735341b9487826023a7983382851ce8bf26 + * + * https://github.com/qt/qtbase/commit/2a6cdec718934ca2cc7f6f9c616ebe62f6912123#diff-724f419b0bb0487c2629bb16cf534c4b268ddcee89b5177189b607f940cfd83dR192 + * + * Hopefully QList won't cause any performance hits relative to + * QVector. + * + * We pick 53 million records as a value that should avoid the problem; + * see the Wireshark issue for why that value was chosen. + */ + cf_set_max_records(53000000); +#endif + +#ifdef Q_OS_MAC + macos_enable_layer_backing(); +#endif + + cmdarg_err_init(wireshark_cmdarg_err, wireshark_cmdarg_err_cont); + + /* Initialize log handler early so we can have proper logging during startup. */ + ws_log_init("wireshark", vcmdarg_err); + /* For backward compatibility with GLib logging and Wireshark 3.4. */ + ws_log_console_writer_set_use_stdout(TRUE); + + qInstallMessageHandler(qt_log_message_handler); + +#ifdef _WIN32 + restore_pipes(); +#endif + +#ifdef DEBUG_STARTUP_TIME + prefs.gui_console_open = console_open_always; +#endif /* DEBUG_STARTUP_TIME */ + +#if defined(Q_OS_MAC) + /* Disable automatic addition of tab menu entries in view menu */ + CocoaBridge::cleanOSGeneratedMenuItems(); +#endif + + /* + * Set the C-language locale to the native environment and set the + * code page to UTF-8 on Windows. + */ +#ifdef _WIN32 + setlocale(LC_ALL, ".UTF-8"); +#else + setlocale(LC_ALL, ""); +#endif + + ws_tzset(); + +#ifdef _WIN32 + // + // On Windows, QCoreApplication has its own WinMain(), which gets the + // command line using GetCommandLineW(), breaks it into individual + // arguments using CommandLineToArgvW(), and then "helpfully" + // converts those UTF-16LE arguments into strings in the local code + // page. + // + // We don't want that, because not all file names can be represented + // in the local code page, so we do the same, but we convert the + // strings into UTF-8. + // + wc_argv = CommandLineToArgvW(GetCommandLineW(), &wc_argc); + if (wc_argv) { + argc = wc_argc; + argv = arg_list_utf_16to8(wc_argc, wc_argv); + LocalFree(wc_argv); + } /* XXX else bail because something is horribly, horribly wrong? */ + + create_app_running_mutex(); +#endif /* _WIN32 */ + + /* Early logging command-line initialization. */ + ws_log_parse_args(&argc, argv, vcmdarg_err, WS_EXIT_INVALID_OPTION); + ws_noisy("Finished log init and parsing command line log arguments"); + + /* + * Get credential information for later use, and drop privileges + * before doing anything else. + * Let the user know if anything happened. + */ + init_process_policies(); + relinquish_special_privs_perm(); + + /* + * Attempt to get the pathname of the directory containing the + * executable file. + */ + /* configuration_init_error = */ configuration_init(argv[0], NULL); + /* ws_log(NULL, LOG_LEVEL_DEBUG, "progfile_dir: %s", get_progfile_dir()); */ + +#ifdef _WIN32 + ws_init_dll_search_path(); + /* Load wpcap if possible. Do this before collecting the run-time version information */ + load_wpcap(); + +#ifdef HAVE_AIRPCAP + /* Load the airpcap.dll. This must also be done before collecting + * run-time version information. */ + load_airpcap(); +#if 0 + airpcap_dll_ret_val = load_airpcap(); + + switch (airpcap_dll_ret_val) { + case AIRPCAP_DLL_OK: + /* load the airpcap interfaces */ + g_airpcap_if_list = get_airpcap_interface_list(&err, &err_str); + + if (g_airpcap_if_list == NULL || g_list_length(g_airpcap_if_list) == 0) { + if (err == CANT_GET_AIRPCAP_INTERFACE_LIST && err_str != NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", "Failed to open Airpcap Adapters."); + g_free(err_str); + } + airpcap_if_active = NULL; + + } else { + + /* select the first as default (THIS SHOULD BE CHANGED) */ + airpcap_if_active = airpcap_get_default_if(airpcap_if_list); + } + break; + /* + * XXX - Maybe we need to warn the user if one of the following happens??? + */ + case AIRPCAP_DLL_OLD: + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_OLD\n"); + break; + + case AIRPCAP_DLL_ERROR: + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_ERROR\n"); + break; + + case AIRPCAP_DLL_NOT_FOUND: + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DDL_NOT_FOUND\n"); + break; + } +#endif +#endif /* HAVE_AIRPCAP */ +#endif /* _WIN32 */ + + /* Get the compile-time version information string */ + ws_init_version_info("Wireshark", gather_wireshark_qt_compiled_info, + gather_wireshark_runtime_info); + + init_report_message("Wireshark", &wireshark_report_routines); + + /* Create the user profiles directory */ + if (create_profiles_dir(&rf_path) == -1) { + simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, + "Could not create profiles directory\n\"%s\": %s.", + rf_path, g_strerror(errno)); + g_free (rf_path); + } + + profile_store_persconffiles(TRUE); + recent_init(); + + /* Read the profile independent recent file. We have to do this here so we can */ + /* set the profile before it can be set from the command line parameter */ + if (!recent_read_static(&rf_path, &rf_open_errno)) { + simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, + "Could not open common recent file\n\"%s\": %s.", + rf_path, g_strerror(rf_open_errno)); + g_free(rf_path); + } + + commandline_early_options(argc, argv); + +#if defined(_WIN32) && !defined(__MINGW32__) + win32_reset_library_path(); +#endif + + // Handle DPI scaling on Windows. This causes problems in at least + // one case on X11 and we don't yet support Android. + // We do the equivalent on macOS by setting NSHighResolutionCapable + // in Info.plist. + // Note that this enables Windows 8.1-style Per-monitor DPI + // awareness but not Windows 10-style Per-monitor v2 awareness. + // https://doc.qt.io/qt-5/scalability.html + // https://doc.qt.io/qt-5/highdpi.html + // https://bugreports.qt.io/browse/QTBUG-53022 - The device pixel ratio is pretty much bogus on Windows. + // https://bugreports.qt.io/browse/QTBUG-55510 - Windows have wrong size + // + // Deprecated in Qt6. + // warning: 'Qt::AA_EnableHighDpiScaling' is deprecated: High-DPI scaling is always enabled. + // This attribute no longer has any effect. +#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + /* Create The Wireshark app */ + WiresharkApplication ws_app(argc, qt_argv); + + // Default value is 400ms = "quickly typing" when searching in Preferences->Protocols + // 1000ms allows a more "hunt/peck" typing speed. 2000ms tested - too long. + QApplication::setKeyboardInputInterval(1000); + + /* initialize the funnel mini-api */ + // xxx qtshark + //initialize_funnel_ops(); + + Dot11DecryptInitContext(&dot11decrypt_ctx); + + QString cf_name; + unsigned int in_file_type = WTAP_TYPE_AUTO; + + err_msg = ws_init_sockets(); + if (err_msg != NULL) + { + cmdarg_err("%s", err_msg); + g_free(err_msg); + cmdarg_err_cont("%s", please_report_bug()); + ret_val = WS_EXIT_INIT_FAILED; + goto clean_exit; + } + + /* Read the profile dependent (static part) of the recent file. */ + /* Only the static part of it will be read, as we don't have the gui now to fill the */ + /* recent lists which is done in the dynamic part. */ + /* We have to do this already here, so command line parameters can overwrite these values. */ + if (!recent_read_profile_static(&rf_path, &rf_open_errno)) { + simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, + "Could not open recent file\n\"%s\": %s.", + rf_path, g_strerror(rf_open_errno)); + g_free(rf_path); + } + wsApp->applyCustomColorsFromRecent(); + + // Initialize our language + read_language_prefs(); + wsApp->loadLanguage(language); + + /* ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_DEBUG, "Translator %s", language); */ + + // Init the main window (and splash) + main_w = new(WiresharkMainWindow); + main_w->show(); + // Setup GLib mainloop on Qt event loop to enable GLib and GIO watches + GLibMainloopOnQEventLoop::setup(main_w); + // We may not need a queued connection here but it would seem to make sense + // to force the issue. + main_w->connect(&ws_app, SIGNAL(openCaptureFile(QString,QString,unsigned int)), + main_w, SLOT(openCaptureFile(QString,QString,unsigned int))); + main_w->connect(&ws_app, &WiresharkApplication::openCaptureOptions, + main_w, &WiresharkMainWindow::showCaptureOptionsDialog); + + /* + * If we have a saved "last directory in which a file was opened" + * in the recent file, set it as the one for the app. + * + * (do this after the path settings are processed) + */ + if (recent.gui_fileopen_remembered_dir && + test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR) { + set_last_open_dir(recent.gui_fileopen_remembered_dir); + } + +#ifdef DEBUG_STARTUP_TIME + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "set_console_log_handler, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time); +#endif + +#ifdef HAVE_LIBPCAP + /* Set the initial values in the capture options. This might be overwritten + by preference settings and then again by the command line parameters. */ + capture_opts_init(&global_capture_opts, capture_opts_get_interface_list); +#endif + + /* + * Libwiretap must be initialized before libwireshark is, so that + * dissection-time handlers for file-type-dependent blocks can + * register using the file type/subtype value for the file type. + */ + wtap_init(TRUE); + + splash_update(RA_DISSECTORS, NULL, NULL); +#ifdef DEBUG_STARTUP_TIME + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling epan init, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time); +#endif + /* Register all dissectors; we must do this before checking for the + "-G" flag, as the "-G" flag dumps information registered by the + dissectors, and we must do it before we read the preferences, in + case any dissectors register preferences. */ + if (!epan_init(splash_update, NULL, TRUE)) { + SimpleDialog::displayQueuedMessages(main_w); + ret_val = WS_EXIT_INIT_FAILED; + goto clean_exit; + } +#ifdef DEBUG_STARTUP_TIME + /* epan_init resets the preferences */ + prefs.gui_console_open = console_open_always; + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "epan done, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time); +#endif + + /* Register all audio codecs. */ + codecs_init(); + + // Read the dynamic part of the recent file. This determines whether or + // not the recent list appears in the main window so the earlier we can + // call this the better. + if (!recent_read_dynamic(&rf_path, &rf_open_errno)) { + simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, + "Could not open recent file\n\"%s\": %s.", + rf_path, g_strerror(rf_open_errno)); + g_free(rf_path); + } + wsApp->refreshRecentCaptures(); + + splash_update(RA_LISTENERS, NULL, NULL); +#ifdef DEBUG_STARTUP_TIME + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Register all tap listeners, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time); +#endif + /* Register all tap listeners; we do this before we parse the arguments, + as the "-z" argument can specify a registered tap. */ + + register_all_tap_listeners(tap_reg_listener); + + conversation_table_set_gui_info(init_conversation_table); + endpoint_table_set_gui_info(init_endpoint_table); + srt_table_iterate_tables(register_service_response_tables, NULL); + rtd_table_iterate_tables(register_response_time_delay_tables, NULL); + stat_tap_iterate_tables(register_simple_stat_tables, NULL); + + if (ex_opt_count("read_format") > 0) { + in_file_type = open_info_name_to_type(ex_opt_get_next("read_format")); + } + +#ifdef DEBUG_STARTUP_TIME + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling extcap_register_preferences, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time); +#endif + splash_update(RA_EXTCAP, NULL, NULL); + extcap_register_preferences(); + splash_update(RA_PREFERENCES, NULL, NULL); +#ifdef DEBUG_STARTUP_TIME + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling module preferences, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time); +#endif + + /* Read the preferences, but don't apply them yet. */ + global_commandline_info.prefs_p = ws_app.readConfigurationFiles(false); + + /* Now let's see if any of preferences were overridden at the command + * line, and store them. We have to do this before applying the + * preferences to the capture options. + */ + commandline_override_prefs(argc, argv, TRUE); + + /* Some of the preferences affect the capture options. Apply those + * before getting the other command line arguments, which can also + * affect the capture options. The command line arguments should be + * applied last to take precedence (at least until the user saves + * preferences, or switches profiles.) + */ + prefs_to_capture_opts(); + + /* Now get our remaining args */ + commandline_other_options(argc, argv, TRUE); + + /* Convert some command-line parameters to QStrings */ + if (global_commandline_info.cf_name != NULL) + cf_name = QString(global_commandline_info.cf_name); + if (global_commandline_info.rfilter != NULL) + read_filter = QString(global_commandline_info.rfilter); + if (global_commandline_info.dfilter != NULL) + dfilter = QString(global_commandline_info.dfilter); + + timestamp_set_type(recent.gui_time_format); + timestamp_set_precision(recent.gui_time_precision); + timestamp_set_seconds_type (recent.gui_seconds_format); + +#ifdef HAVE_LIBPCAP +#ifdef DEBUG_STARTUP_TIME + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling fill_in_local_interfaces, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time); +#endif + splash_update(RA_INTERFACES, NULL, NULL); + + if (!global_commandline_info.cf_name && !prefs.capture_no_interface_load) + fill_in_local_interfaces(main_window_update); + + if (global_commandline_info.list_link_layer_types) + caps_queries |= CAPS_QUERY_LINK_TYPES; + if (global_commandline_info.list_timestamp_types) + caps_queries |= CAPS_QUERY_TIMESTAMP_TYPES; + + if (global_commandline_info.start_capture || caps_queries) { + /* We're supposed to do a live capture or get a list of link-layer/timestamp + types for a live capture device; if the user didn't specify an + interface to use, pick a default. */ + ret_val = capture_opts_default_iface_if_necessary(&global_capture_opts, + ((global_commandline_info.prefs_p->capture_device) && (*global_commandline_info.prefs_p->capture_device != '\0')) ? get_if_name(global_commandline_info.prefs_p->capture_device) : NULL); + if (ret_val != 0) { + goto clean_exit; + } + } + + /* + * If requested, list the link layer types and/or time stamp types + * and exit. + */ + if (caps_queries) { + guint i; + +#ifdef _WIN32 + create_console(); +#endif /* _WIN32 */ + /* Get the list of link-layer types for the capture devices. */ + ret_val = EXIT_SUCCESS; + for (i = 0; i < global_capture_opts.ifaces->len; i++) { + interface_options *interface_opts; + if_capabilities_t *caps; + char *auth_str = NULL; + + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, i); +#ifdef HAVE_PCAP_REMOTE + if (interface_opts->auth_type == CAPTURE_AUTH_PWD) { + auth_str = ws_strdup_printf("%s:%s", interface_opts->auth_username, interface_opts->auth_password); + } +#endif + caps = capture_get_if_capabilities(interface_opts->name, interface_opts->monitor_mode, + auth_str, &err_str, &err_str_secondary, NULL); + g_free(auth_str); + if (caps == NULL) { + cmdarg_err("%s%s%s", err_str, err_str_secondary ? "\n" : "", err_str_secondary ? err_str_secondary : ""); + g_free(err_str); + g_free(err_str_secondary); + ret_val = WS_EXIT_INVALID_CAPABILITY; + break; + } + ret_val = capture_opts_print_if_capabilities(caps, interface_opts, + caps_queries); + free_if_capabilities(caps); + if (ret_val != EXIT_SUCCESS) { + break; + } + } +#ifdef _WIN32 + destroy_console(); +#endif /* _WIN32 */ + goto clean_exit; + } + + capture_opts_trim_snaplen(&global_capture_opts, MIN_PACKET_SIZE); + capture_opts_trim_ring_num_files(&global_capture_opts); +#endif /* HAVE_LIBPCAP */ + + /* Notify all registered modules that have had any of their preferences + changed either from one of the preferences file or from the command + line that their preferences have changed. */ +#ifdef DEBUG_STARTUP_TIME + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling prefs_apply_all, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time); +#endif + splash_update(RA_PREFERENCES_APPLY, NULL, NULL); + prefs_apply_all(); + wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged); + +#ifdef HAVE_LIBPCAP + if ((global_capture_opts.num_selected == 0) && + (prefs.capture_device != NULL)) { + guint i; + interface_t *device; + for (i = 0; i < global_capture_opts.all_ifaces->len; i++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (!device->hidden && strcmp(device->display_name, prefs.capture_device) == 0) { + device->selected = TRUE; + global_capture_opts.num_selected++; + break; + } + } + } +#endif + + /* + * Enabled and disabled protocols and heuristic dissectors as per + * command-line options. + */ + if (!setup_enabled_and_disabled_protocols()) { + ret_val = WS_EXIT_INVALID_OPTION; + goto clean_exit; + } + + build_column_format_array(&CaptureFile::globalCapFile()->cinfo, global_commandline_info.prefs_p->num_cols, TRUE); + wsApp->emitAppSignal(WiresharkApplication::ColumnsChanged); // We read "recent" widths above. + wsApp->emitAppSignal(WiresharkApplication::RecentPreferencesRead); // Must be emitted after PreferencesChanged. + + wsApp->setMonospaceFont(prefs.gui_font_name); + + /* For update of WindowTitle (When use gui.window_title preference) */ + main_w->setWSWindowTitle(); + + if (!color_filters_init(&err_msg, color_filter_add_cb)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); + g_free(err_msg); + } + + /* allSystemsGo() emits appInitialized(), which signals the WelcomePage to + * delete the splash overlay. However, it doesn't get redrawn until + * processEvents() is called. If we're opening a capture file that happens + * either when we finish reading the file or when the progress bar appears. + * It's probably better to leave the splash overlay up until that happens + * rather than showing the user the welcome page, so we don't call + * processEvents() here. + */ + wsApp->allSystemsGo(); + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Wireshark is up and ready to go, elapsed time %.3fs", (float) (g_get_monotonic_time() - start_time) / 1000000); + SimpleDialog::displayQueuedMessages(main_w); + + /* User could specify filename, or display filter, or both */ + if (!dfilter.isEmpty()) + main_w->filterPackets(dfilter, false); + if (!cf_name.isEmpty()) { + if (main_w->openCaptureFile(cf_name, read_filter, in_file_type)) { + + /* Open stat windows; we do so after creating the main window, + to avoid Qt warnings, and after successfully opening the + capture file, so we know we have something to compute stats + on, and after registering all dissectors, so that MATE will + have registered its field array and we can have a tap filter + with one of MATE's late-registered fields as part of the + filter. */ + start_requested_stats(); + + if (global_commandline_info.go_to_packet != 0) { + /* Jump to the specified frame number, kept for backward + compatibility. */ + cf_goto_frame(CaptureFile::globalCapFile(), global_commandline_info.go_to_packet); + } else if (global_commandline_info.jfilter != NULL) { + dfilter_t *jump_to_filter = NULL; + /* try to compile given filter */ + if (!dfilter_compile(global_commandline_info.jfilter, &jump_to_filter, &df_err)) { + // Similar code in MainWindow::mergeCaptureFile(). + QMessageBox::warning(main_w, QObject::tr("Invalid Display Filter"), + QObject::tr("The filter expression %1 isn't a valid display filter. (%2).") + .arg(global_commandline_info.jfilter, df_err->msg), + QMessageBox::Ok); + df_error_free(&df_err); + } else { + /* Filter ok, jump to the first packet matching the filter + conditions. Default search direction is forward, but if + option d was given, search backwards */ + cf_find_packet_dfilter(CaptureFile::globalCapFile(), jump_to_filter, global_commandline_info.jump_backwards); + } + } + } + } +#ifdef HAVE_LIBPCAP + else { + if (global_commandline_info.start_capture) { + if (global_capture_opts.save_file != NULL) { + /* Save the directory name for future file dialogs. */ + /* (get_dirname overwrites filename) */ + gchar *s = g_strdup(global_capture_opts.save_file); + set_last_open_dir(get_dirname(s)); + g_free(s); + } + /* "-k" was specified; start a capture. */ + check_and_warn_user_startup(); + + /* If no user interfaces were specified on the command line, + copy the list of selected interfaces to the set of interfaces + to use for this capture. */ + if (global_capture_opts.ifaces->len == 0) + collect_ifaces(&global_capture_opts); + CaptureFile::globalCapFile()->window = main_w; + if (capture_start(&global_capture_opts, global_commandline_info.capture_comments, + main_w->captureSession(), main_w->captureInfoData(), + main_window_update)) { + /* The capture started. Open stat windows; we do so after creating + the main window, to avoid GTK warnings, and after successfully + opening the capture file, so we know we have something to compute + stats on, and after registering all dissectors, so that MATE will + have registered its field array and we can have a tap filter with + one of MATE's late-registered fields as part of the filter. */ + start_requested_stats(); + } + } + /* if the user didn't supply a capture filter, use the one to filter out remote connections like SSH */ + if (!global_commandline_info.start_capture && !global_capture_opts.default_options.cfilter) { + global_capture_opts.default_options.cfilter = g_strdup(get_conn_cfilter()); + } + } +#endif /* HAVE_LIBPCAP */ + + // UAT and UI settings files used in configuration profiles which are used + // in Qt dialogs are not registered during startup because they only get + // loaded when the dialog is shown. Register them here. + profile_register_persconffile("io_graphs"); + profile_register_persconffile("import_hexdump.json"); + + profile_store_persconffiles(FALSE); + + // If the wsApp->exec() event loop exits cleanly, we call + // WiresharkApplication::cleanup(). + ret_val = wsApp->exec(); + wsApp = NULL; + + // Many widgets assume that they always have valid epan data, so this + // must be called before epan_cleanup(). + // XXX We need to clean up the Lua GUI here. We currently paper over + // this in FunnelStatistics::~FunnelStatistics, which leaks memory. + delete main_w; + + recent_cleanup(); + epan_cleanup(); + + extcap_cleanup(); + + Dot11DecryptDestroyContext(&dot11decrypt_ctx); + + ws_cleanup_sockets(); + +#ifdef _WIN32 + /* For some unknown reason, the "atexit()" call in "create_console()" + doesn't arrange that "destroy_console()" be called when we exit, + so we call it here if a console was created. */ + destroy_console(); +#endif /* _WIN32 */ + +clean_exit: +#ifdef HAVE_LIBPCAP + capture_opts_cleanup(&global_capture_opts); +#endif + col_cleanup(&CaptureFile::globalCapFile()->cinfo); + codecs_cleanup(); + wtap_cleanup(); + free_progdirs(); + commandline_options_free(); + exit_application(ret_val); +} diff --git a/ui/qt/main_application.cpp b/ui/qt/main_application.cpp new file mode 100644 index 00000000..bc52b6cf --- /dev/null +++ b/ui/qt/main_application.cpp @@ -0,0 +1,1383 @@ +/* main_application.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +// warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4267) +#endif + +#define WS_LOG_DOMAIN LOG_DOMAIN_MAIN + +#include "main_application.h" + +#include +#include + +#include "wsutil/filesystem.h" + +#include "epan/addr_resolv.h" +#include "epan/column-utils.h" +#include "epan/disabled_protos.h" +#include "epan/ftypes/ftypes.h" +#include "epan/prefs.h" +#include "epan/proto.h" +#include "epan/tap.h" +#include "epan/timestamp.h" +#include "epan/decode_as.h" + +#include "ui/decode_as_utils.h" +#include "ui/preference_utils.h" +#include "ui/iface_lists.h" +#include "ui/language.h" +#include "ui/recent.h" +#include "ui/simple_dialog.h" +#include "ui/util.h" + +#include +#include +#include "coloring_rules_dialog.h" + +#include "epan/color_filters.h" +#include "recent_file_status.h" + +#include "extcap.h" +#ifdef HAVE_LIBPCAP +#include +#endif + +#include "wsutil/filter_files.h" +#include "ui/capture_globals.h" +#include "ui/software_update.h" +#include "ui/file_dialog.h" +#include "ui/recent_utils.h" + +#ifdef HAVE_LIBPCAP +#include "ui/capture.h" +#endif + +#include "wsutil/utf8_entities.h" + +#ifdef _WIN32 +# include "wsutil/file_util.h" +# include +# include +#endif /* _WIN32 */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) +#include +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) && defined(Q_OS_WIN) +#include +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +MainApplication *mainApp = NULL; + +// XXX - Copied from ui/gtk/file_dlg.c + +static QList recent_captures_; +static QHash > dynamic_menu_groups_; +static QHash > added_menu_groups_; +static QHash > removed_menu_groups_; + +QString MainApplication::window_title_separator_ = QString::fromUtf8(" " UTF8_MIDDLE_DOT " "); + +// QMimeDatabase parses a large-ish XML file and can be slow to initialize. +// Do so in a worker thread as early as possible. +// https://github.com/lxde/pcmanfm-qt/issues/415 +class MimeDatabaseInitThread : public QRunnable +{ +private: + void run() + { + QMimeDatabase mime_db; + mime_db.mimeTypeForData(QByteArray()); + } +}; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +// Populating the font database can be slow as well. +class FontDatabaseInitThread : public QRunnable +{ +private: + void run() + { + QFontDatabase font_db; + } +}; +#endif + +void +topic_action(topic_action_e action) +{ + if (mainApp) mainApp->helpTopicAction(action); +} + +/* + * Add the capture filename to the application-wide "Recent Files" list. + * Contrary to the name this isn't limited to the "recent" menu. + */ +/* + * XXX - We might want to call SHAddToRecentDocs under Windows 7: + * https://stackoverflow.com/questions/437212/how-do-you-register-a-most-recently-used-list-with-windows-in-preparation-for-win + */ +extern "C" void +add_menu_recent_capture_file(const gchar *cf_name) { + QString normalized_cf_name = QString::fromUtf8(cf_name); + QDir cf_path; + + cf_path.setPath(normalized_cf_name); + normalized_cf_name = cf_path.absolutePath(); + normalized_cf_name = QDir::cleanPath(normalized_cf_name); + normalized_cf_name = QDir::toNativeSeparators(normalized_cf_name); + + /* Iterate through the recent items list, removing duplicate entries and every + * item above count_max + */ + unsigned int cnt = 1; + QMutableListIterator rii(recent_captures_); + while (rii.hasNext()) { + recent_item_status *ri = rii.next(); + /* if this element string is one of our special items (separator, ...) or + * already in the list or + * this element is above maximum count (too old), remove it + */ + if (ri->filename.length() < 1 || +#ifdef _WIN32 + /* do a case insensitive compare on win32 */ + ri->filename.compare(normalized_cf_name, Qt::CaseInsensitive) == 0 || +#else /* _WIN32 */ + /* + * Do a case sensitive compare on UN*Xes. + * + * XXX - on UN*Xes such as macOS, where you can use pathconf() + * to check whether a given file system is case-sensitive or + * not, we should check whether this particular file system + * is case-sensitive and do the appropriate comparison. + */ + ri->filename.compare(normalized_cf_name) == 0 || +#endif + cnt >= prefs.gui_recent_files_count_max) { + rii.remove(); + delete(ri); + cnt--; + } + cnt++; + } + mainApp->addRecentItem(normalized_cf_name, 0, false); +} + +/* write all capture filenames of the menu to the user's recent file */ +extern "C" void menu_recent_file_write_all(FILE *rf) { + + /* we have to iterate backwards through the children's list, + * so we get the latest item last in the file. + */ + QListIterator rii(recent_captures_); + rii.toBack(); + while (rii.hasPrevious()) { + QString cf_name; + /* get capture filename from the menu item label */ + cf_name = rii.previous()->filename; + if (!cf_name.isNull()) { + fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", qUtf8Printable(cf_name)); + } + } +} + +#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) +/** Check to see if Wireshark can shut down safely (e.g. offer to save the + * current capture). + */ +extern "C" int software_update_can_shutdown_callback(void) { + return mainApp->softwareUpdateCanShutdown(); +} + +/** Shut down Wireshark in preparation for an upgrade. + */ +extern "C" void software_update_shutdown_request_callback(void) { + mainApp->softwareUpdateShutdownRequest(); +} +#endif // HAVE_SOFTWARE_UPDATE && Q_OS_WIN + +// Check each recent item in a separate thread so that we don't hang while +// calling stat(). This is called periodically because files and entire +// volumes can disappear and reappear at any time. +void MainApplication::refreshRecentCaptures() { + recent_item_status *ri; + RecentFileStatus *rf_status; + + // We're in the middle of a capture. Don't create traffic. + if (active_captures_ > 0) return; + + foreach (ri, recent_captures_) { + if (ri->in_thread) { + continue; + } + rf_status = new RecentFileStatus(ri->filename, this); + QThreadPool::globalInstance()->start(rf_status); + } +} + +void MainApplication::refreshPacketData() +{ + if (host_name_lookup_process()) { + emit addressResolutionChanged(); + } else if (col_data_changed()) { + emit columnDataChanged(); + } +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) && defined(Q_OS_WIN) +void MainApplication::colorSchemeChanged() { + if (ColorUtils::themeIsDark()) { + setStyle(QStyleFactory::create("fusion")); + } else { + setStyle(QStyleFactory::create("windowsvista")); + } +} +#endif + +void MainApplication::updateTaps() +{ + draw_tap_listeners(FALSE); +} + +QDir MainApplication::openDialogInitialDir() { + return QDir(get_open_dialog_initial_dir()); +} + +void MainApplication::setLastOpenDirFromFilename(const QString file_name) +{ + QString directory = QFileInfo(file_name).absolutePath(); + /* XXX - printable? */ + set_last_open_dir(qUtf8Printable(directory)); +} + +void MainApplication::helpTopicAction(topic_action_e action) +{ + QString url = gchar_free_to_qstring(topic_action_url(action)); + + if (!url.isEmpty()) { + QDesktopServices::openUrl(QUrl(url)); + } +} + +const QFont MainApplication::monospaceFont(bool zoomed) const +{ + if (zoomed) { + return zoomed_font_; + } + return mono_font_; +} + +void MainApplication::setMonospaceFont(const char *font_string) { + + if (font_string && strlen(font_string) > 0) { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + // Qt 6's QFont::toString returns a value with 16 or 17 fields, e.g. + // Consolas,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1 + // Corbel,10,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular + // Qt 5's QFont::fromString expects a value with 10 or 11 fields, e.g. + // Consolas,10,-1,5,50,0,0,0,0,0 + // Corbel,10,-1,5,50,0,0,0,0,0,Regular + // It looks like Qt6's QFont::fromString can read both forms: + // https://github.com/qt/qtbase/blob/6.0/src/gui/text/qfont.cpp#L2146 + // but Qt5's cannot: + // https://github.com/qt/qtbase/blob/5.15/src/gui/text/qfont.cpp#L2151 + const char *fs_ptr = font_string; + int field_count = 1; + while ((fs_ptr = strchr(fs_ptr, ',')) != NULL) { + fs_ptr++; + field_count++; + } + if (field_count <= 11) { +#endif + mono_font_.fromString(font_string); + + // Only accept the font name if it actually exists. + if (mono_font_.family() == QFontInfo(mono_font_).family()) { + return; + } else { + ws_warning("Monospace font family %s differs from its fontinfo: %s", + qUtf8Printable(mono_font_.family()), qUtf8Printable(QFontInfo(mono_font_).family())); + } +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + } else { + ws_warning("Monospace font %s appears to be from Qt6 and we're running Qt5.", font_string); + } +#endif + } + + // https://en.wikipedia.org/wiki/Category:Monospaced_typefaces + const char *win_default_font = "Consolas"; + const char *win_alt_font = "Lucida Console"; + // SF Mono might be a system font someday. Right now (Oct 2016) it appears + // to be limited to Xcode and Terminal. + // http://www.openradar.me/26790072 + // http://www.openradar.me/26862220 + const char *osx_default_font = "SF Mono"; + const QStringList osx_alt_fonts = QStringList() << "Menlo" << "Monaco"; + // XXX Detect Ubuntu systems (e.g. via /etc/os-release and/or + // /etc/lsb_release) and add "Ubuntu Mono Regular" there. + // https://design.ubuntu.com/font/ + const char *x11_default_font = "Liberation Mono"; + const QStringList x11_alt_fonts = QStringList() << "DejaVu Sans Mono" << "Bitstream Vera Sans Mono"; + const QStringList fallback_fonts = QStringList() << "Lucida Sans Typewriter" << "Inconsolata" << "Droid Sans Mono" << "Andale Mono" << "Courier New" << "monospace"; + QStringList substitutes; + int font_size_adjust = 0; + + // Try to pick the latest, shiniest fixed-width font for our OS. +#if defined(Q_OS_WIN) + const char *default_font = win_default_font; + substitutes << win_alt_font << osx_default_font << osx_alt_fonts << x11_default_font << x11_alt_fonts << fallback_fonts; +# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + font_size_adjust = 1; +# else // QT_VERSION + font_size_adjust = 2; +# endif // QT_VERSION +#elif defined(Q_OS_MAC) + const char *default_font = osx_default_font; + substitutes << osx_alt_fonts << win_default_font << win_alt_font << x11_default_font << x11_alt_fonts << fallback_fonts; +#else // Q_OS + const char *default_font = x11_default_font; + substitutes << x11_alt_fonts << win_default_font << win_alt_font << osx_default_font << osx_alt_fonts << fallback_fonts; +#endif // Q_OS + + mono_font_ = QFont(default_font, mainApp->font().pointSize() + font_size_adjust); + mono_font_.insertSubstitutions(default_font, substitutes); + mono_font_.setBold(false); + + // Retrieve the effective font and apply it. + mono_font_.setFamily(QFontInfo(mono_font_).family()); + + g_free(prefs.gui_font_name); + prefs.gui_font_name = qstring_strdup(mono_font_.toString()); +} + +int MainApplication::monospaceTextSize(const char *str) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return QFontMetrics(mono_font_).horizontalAdvance(str); +#else + return QFontMetrics(mono_font_).width(str); +#endif +} + +void MainApplication::setConfigurationProfile(const gchar *profile_name, bool write_recent_file) +{ + char *rf_path; + int rf_open_errno; + gchar *err_msg = NULL; + + gboolean prev_capture_no_interface_load; + gboolean prev_capture_no_extcap; + + /* First check if profile exists */ + if (!profile_exists(profile_name, FALSE)) { + if (profile_exists(profile_name, TRUE)) { + char *pf_dir_path, *pf_dir_path2, *pf_filename; + /* Copy from global profile */ + if (create_persconffile_profile(profile_name, &pf_dir_path) == -1) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Can't create directory\n\"%s\":\n%s.", + pf_dir_path, g_strerror(errno)); + + g_free(pf_dir_path); + } + + if (copy_persconffile_profile(profile_name, profile_name, TRUE, &pf_filename, + &pf_dir_path, &pf_dir_path2) == -1) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.", + pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno)); + + g_free(pf_filename); + g_free(pf_dir_path); + g_free(pf_dir_path2); + } + } else { + /* No personal and no global profile exists */ + return; + } + } + + /* Then check if changing to another profile */ + if (profile_name && strcmp (profile_name, get_profile_name()) == 0) { + return; + } + + prev_capture_no_interface_load = prefs.capture_no_interface_load; + prev_capture_no_extcap = prefs.capture_no_extcap; + + /* Get the current geometry, before writing it to disk */ + emit profileChanging(); + + if (write_recent_file && profile_exists(get_profile_name(), FALSE)) + { + /* Write recent file for profile we are leaving, if it still exists */ + write_profile_recent(); + } + + /* Set profile name and update the status bar */ + set_profile_name (profile_name); + emit profileNameChanged(profile_name); + + /* Apply new preferences */ + readConfigurationFiles(true); + + if (!recent_read_profile_static(&rf_path, &rf_open_errno)) { + simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, + "Could not open common recent file\n\"%s\": %s.", + rf_path, g_strerror(rf_open_errno)); + g_free(rf_path); + } + if (recent.gui_fileopen_remembered_dir && + test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR) { + set_last_open_dir(recent.gui_fileopen_remembered_dir); + } + timestamp_set_type(recent.gui_time_format); + timestamp_set_precision(recent.gui_time_precision); + timestamp_set_seconds_type (recent.gui_seconds_format); + tap_update_timer_.setInterval(prefs.tap_update_interval); + + prefs_to_capture_opts(); + prefs_apply_all(); +#ifdef HAVE_LIBPCAP + update_local_interfaces(); +#endif + + setMonospaceFont(prefs.gui_font_name); + + // Freeze the packet list early to avoid updating column data before doing a + // full redissection. The packet list will be thawed when redissection is done. + emit freezePacketList(true); + + emit columnsChanged(); + emit preferencesChanged(); + emit recentPreferencesRead(); + emit filterExpressionsChanged(); + emit checkDisplayFilter(); + emit captureFilterListChanged(); + emit displayFilterListChanged(); + + /* Reload color filters */ + if (!color_filters_reload(&err_msg, color_filter_add_cb)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); + g_free(err_msg); + } + + /* Load interfaces if settings have changed */ + if (!prefs.capture_no_interface_load && + ((prefs.capture_no_interface_load != prev_capture_no_interface_load) || + (prefs.capture_no_extcap != prev_capture_no_extcap))) { + refreshLocalInterfaces(); + } + + emit localInterfaceListChanged(); + emit packetDissectionChanged(); + + /* Write recent_common file to ensure last used profile setting is stored. */ + write_recent(); +} + +void MainApplication::reloadLuaPluginsDelayed() +{ + QTimer::singleShot(0, this, SIGNAL(reloadLuaPlugins())); +} + +const QIcon &MainApplication::normalIcon() +{ + if (normal_icon_.isNull()) { + initializeIcons(); + } + return normal_icon_; +} + +const QIcon &MainApplication::captureIcon() +{ + if (capture_icon_.isNull()) { + initializeIcons(); + } + return capture_icon_; +} + +const QString MainApplication::windowTitleString(QStringList title_parts) +{ + QMutableStringListIterator tii(title_parts); + while (tii.hasNext()) { + QString ti = tii.next(); + if (ti.isEmpty()) tii.remove(); + } + title_parts.prepend(applicationName()); + return title_parts.join(window_title_separator_); +} + +void MainApplication::applyCustomColorsFromRecent() +{ + int i = 0; + bool ok; + for (GList *custom_color = recent.custom_colors; custom_color; custom_color = custom_color->next) { + QRgb rgb = QString((const char *)custom_color->data).toUInt(&ok, 16); + if (ok) { + QColorDialog::setCustomColor(i++, QColor(rgb)); + } + } +} + +// Return the first top-level QMainWindow. +QWidget *MainApplication::mainWindow() +{ + foreach (QWidget *tlw, topLevelWidgets()) { + QMainWindow *tlmw = qobject_cast(tlw); + if (tlmw && tlmw->isVisible()) { + return tlmw; + } + } + return 0; +} + +void MainApplication::storeCustomColorsInRecent() +{ + if (QColorDialog::customCount()) { + prefs_clear_string_list(recent.custom_colors); + recent.custom_colors = NULL; + for (int i = 0; i < QColorDialog::customCount(); i++) { + QRgb rgb = QColorDialog::customColor(i).rgb(); + recent.custom_colors = g_list_append(recent.custom_colors, ws_strdup_printf("%08x", rgb)); + } + } +} + +bool MainApplication::event(QEvent *event) +{ + QString display_filter = NULL; + if (event->type() == QEvent::FileOpen) { + QFileOpenEvent *foe = static_cast(event); + if (foe && foe->file().length() > 0) { + QString cf_path(foe->file()); + if (initialized_) { + emit openCaptureFile(cf_path, display_filter, WTAP_TYPE_AUTO); + } else { + pending_open_files_.append(cf_path); + } + } + return true; + } + return QApplication::event(event); +} + +void MainApplication::clearRecentCaptures() { + qDeleteAll(recent_captures_); + recent_captures_.clear(); + emit updateRecentCaptureStatus(NULL, 0, false); +} + +void MainApplication::cleanup() +{ + software_update_cleanup(); + storeCustomColorsInRecent(); + // Write the user's recent file(s) to disk. + write_profile_recent(); + write_recent(); + + qDeleteAll(recent_captures_); + recent_captures_.clear(); + // We might end up here via exit_application. + QThreadPool::globalInstance()->waitForDone(); +} + +void MainApplication::itemStatusFinished(const QString filename, qint64 size, bool accessible) { + recent_item_status *ri; + + foreach (ri, recent_captures_) { + if (filename == ri->filename && (size != ri->size || accessible != ri->accessible)) { + ri->size = size; + ri->accessible = accessible; + ri->in_thread = false; + + emit updateRecentCaptureStatus(filename, size, accessible); + } + } +} + +MainApplication::MainApplication(int &argc, char **argv) : + QApplication(argc, argv), + initialized_(false), + is_reloading_lua_(false), + if_notifier_(NULL), + active_captures_(0) +{ + mainApp = this; + + MimeDatabaseInitThread *mime_db_init_thread = new(MimeDatabaseInitThread); + QThreadPool::globalInstance()->start(mime_db_init_thread); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + FontDatabaseInitThread *font_db_init_thread = new (FontDatabaseInitThread); + QThreadPool::globalInstance()->start(font_db_init_thread); +#endif + + Q_INIT_RESOURCE(about); + Q_INIT_RESOURCE(i18n); + Q_INIT_RESOURCE(layout); + Q_INIT_RESOURCE(stock_icons); + Q_INIT_RESOURCE(languages); + +#ifdef Q_OS_WIN + /* RichEd20.DLL is needed for native file dialog filter entries. */ + ws_load_library("riched20.dll"); +#endif // Q_OS_WIN + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && defined(Q_OS_WIN) + setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + setAttribute(Qt::AA_DisableWindowContextHelpButton); +#endif + + // Throw various settings at the wall with the hope that one of them will + // enable context menu shortcuts QTBUG-69452, QTBUG-109590 +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + setAttribute(Qt::AA_DontShowShortcutsInContextMenus, false); +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + styleHints()->setShowShortcutsInContextMenus(true); +#endif + + // + // XXX - this means we try to check for the existence of all files + // in the recent list every 2 seconds; that causes noticeable network + // traffic if any of them are stored on file servers. + // + // QFileSystemWatcher should allow us to watch for files being + // removed or renamed. It uses kqueues and EVFILT_VNODE on FreeBSD, + // NetBSD, FSEvents on macOS, inotify on Linux if available, and + // FindFirstChagneNotification() on Windows. On all other platforms, + // it just periodically polls, as we're doing now. + // + // For unmounts: + // + // macOS and FreeBSD deliver NOTE_REVOKE notes for EVFILT_VNODE, and + // QFileSystemWatcher delivers signals for them, just as it does for + // NOTE_DELETE and NOTE_RENAME. + // + // On Linux, inotify: + // + // http://man7.org/linux/man-pages/man7/inotify.7.html + // + // appears to deliver "filesystem containing watched object was + // unmounted" events. It looks as if Qt turns them into "changed" + // events. + // + // On Windows, it's not clearly documented what happens on a handle + // opened with FindFirstChangeNotification() if the volume on which + // the path handed to FindFirstChangeNotification() is removed, or + // ejected, or whatever the Windowsese is for "unmounted". The + // handle obviously isn't valid any more, but whether it just hangs + // around and never delivers any notifications or delivers an + // event that turns into an error indication doesn't seem to be + // documented. If it just hangs around, I think our main loop will + // receive a WM_DEVICECHANGE Windows message with DBT_DEVICEREMOVECOMPLETE + // if an unmount occurs - even for network devices. If we need to watch + // for those, we can use the winEvent method of the QWidget for the + // top-level window to get Windows messages. + // + // Note also that remote file systems might not report file + // removal or renames if they're done on the server or done by + // another client. At least on macOS, they *will* get reported + // if they're done on the machine running the program doing the + // kqueue stuff, and, at least in newer versions, should get + // reported on SMB-mounted (and AFP-mounted?) file systems + // even if done on the server or another client. + // + // But, when push comes to shove, the file manager(s) on the + // OSes in question probably use the same mechanisms to + // monitor folders in folder windows or open/save dialogs or..., + // so my inclination is just to use QFileSystemWatcher. + // + // However, that wouldn't catch files that become *re*-accessible + // by virtue of a file system being re-mounted. The only way to + // catch *that* would be to watch for mounts and re-check all + // marked-as-inaccessible files. + // + // macOS and FreeBSD also support EVFILT_FS events, which notify you + // of file system mounts and unmounts. We'd need to add our own + // kqueue for that, if we can check those with QSocketNotifier. + // + // On Linux, at least as of 2006, you're supposed to poll /proc/mounts: + // + // https://lkml.org/lkml/2006/2/22/169 + // + // to discover mounts. + // + // On Windows, you'd probably have to watch for WM_DEVICECHANGE events. + // + // Then again, with an automounter, a file system containing a + // recent capture might get unmounted automatically if you haven't + // referred to anything on that file system for a while, and get + // treated as inaccessible. However, if you try to access it, + // the automounter will attempt to re-mount it, so the access *will* + // succeed if the automounter can remount the file. + // + // (Speaking of automounters, repeatedly polling recent files will + // keep the file system from being unmounted, for what that's worth.) + // + // At least on macOS, you can determine whether a file is on an + // automounted file system by calling statfs() on its path and + // checking whether MNT_AUTOMOUNTED is set in f_flags. FreeBSD + // appears to support that flag as well, but no other *BSD appears + // to. + // + // I'm not sure what can be done on Linux. + // + recent_timer_.setParent(this); + connect(&recent_timer_, SIGNAL(timeout()), this, SLOT(refreshRecentCaptures())); + recent_timer_.start(2000); + + packet_data_timer_.setParent(this); + connect(&packet_data_timer_, SIGNAL(timeout()), this, SLOT(refreshPacketData())); + packet_data_timer_.start(1000); + + tap_update_timer_.setParent(this); + tap_update_timer_.setInterval(TAP_UPDATE_DEFAULT_INTERVAL); + connect(this, SIGNAL(appInitialized()), &tap_update_timer_, SLOT(start())); + connect(&tap_update_timer_, SIGNAL(timeout()), this, SLOT(updateTaps())); + + // Application-wide style sheet + QString app_style_sheet = qApp->styleSheet(); + qApp->setStyleSheet(app_style_sheet); + + // If our window text is lighter than the window background, assume the theme is dark. + prefs_set_gui_theme_is_dark(ColorUtils::themeIsDark()); + +#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) + connect(this, SIGNAL(softwareUpdateQuit()), this, SLOT(quit()), Qt::QueuedConnection); +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) && defined(Q_OS_WIN) + colorSchemeChanged(); + connect(styleHints(), &QStyleHints::colorSchemeChanged, this, &MainApplication::colorSchemeChanged); +#endif + + connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(cleanup())); +} + +MainApplication::~MainApplication() +{ + mainApp = NULL; + clearDynamicMenuGroupItems(); + free_filter_lists(); +} + +void MainApplication::registerUpdate(register_action_e action, const char *message) +{ + emit splashUpdate(action, message); +} + +void MainApplication::emitAppSignal(AppSignal signal) +{ + switch (signal) { + case ColumnsChanged: + emit columnsChanged(); + break; + case CaptureFilterListChanged: + emit captureFilterListChanged(); + break; + case DisplayFilterListChanged: + emit displayFilterListChanged(); + break; + case FilterExpressionsChanged: + emit filterExpressionsChanged(); + break; + case LocalInterfacesChanged: + emit localInterfaceListChanged(); + break; + case NameResolutionChanged: + emit addressResolutionChanged(); + break; + case PreferencesChanged: + emit preferencesChanged(); + break; + case PacketDissectionChanged: + emit packetDissectionChanged(); + break; + case ProfileChanging: + emit profileChanging(); + break; + case RecentCapturesChanged: + emit updateRecentCaptureStatus(NULL, 0, false); + break; + case RecentPreferencesRead: + emit recentPreferencesRead(); + break; + case FieldsChanged: + emit fieldsChanged(); + break; + case FreezePacketList: + emit freezePacketList(false); + break; + default: + break; + } +} + +// Flush any collected app signals. +// +// On macOS emitting PacketDissectionChanged from a dialog can +// render the application unusable: +// https://gitlab.com/wireshark/wireshark/-/issues/11361 +// https://gitlab.com/wireshark/wireshark/-/issues/11448 +// Work around the problem by queueing up app signals and emitting them +// after the dialog is closed. +// +// The following bugs might be related although they don't describe the +// exact behavior we're working around here: +// https://bugreports.qt.io/browse/QTBUG-38512 +// https://bugreports.qt.io/browse/QTBUG-38600 +void MainApplication::flushAppSignals() +{ + while (!app_signals_.isEmpty()) { + mainApp->emitAppSignal(app_signals_.takeFirst()); + } +} + +void MainApplication::emitStatCommandSignal(const QString &menu_path, const char *arg, void *userdata) +{ + emit openStatCommandDialog(menu_path, arg, userdata); +} + +void MainApplication::emitTapParameterSignal(const QString cfg_abbr, const QString arg, void *userdata) +{ + emit openTapParameterDialog(cfg_abbr, arg, userdata); +} + +// XXX Combine statistics and funnel routines into addGroupItem + groupItems? +void MainApplication::addDynamicMenuGroupItem(int group, QAction *sg_action) +{ + if (!dynamic_menu_groups_.contains(group)) { + dynamic_menu_groups_[group] = QList(); + } + dynamic_menu_groups_[group] << sg_action; +} + +void MainApplication::appendDynamicMenuGroupItem(int group, QAction *sg_action) +{ + if (!added_menu_groups_.contains(group)) { + added_menu_groups_[group] = QList(); + } + added_menu_groups_[group] << sg_action; + addDynamicMenuGroupItem(group, sg_action); +} + +void MainApplication::removeDynamicMenuGroupItem(int group, QAction *sg_action) +{ + if (!removed_menu_groups_.contains(group)) { + removed_menu_groups_[group] = QList(); + } + removed_menu_groups_[group] << sg_action; + dynamic_menu_groups_[group].removeAll(sg_action); +} + +void MainApplication::clearDynamicMenuGroupItems() +{ + foreach (int group, dynamic_menu_groups_.keys()) { + dynamic_menu_groups_[group].clear(); + } +} + +QList MainApplication::dynamicMenuGroupItems(int group) +{ + if (!dynamic_menu_groups_.contains(group)) { + return QList(); + } + + QList sgi_list = dynamic_menu_groups_[group]; + std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan); + return sgi_list; +} + +QList MainApplication::addedMenuGroupItems(int group) +{ + if (!added_menu_groups_.contains(group)) { + return QList(); + } + + QList sgi_list = added_menu_groups_[group]; + std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan); + return sgi_list; +} + +QList MainApplication::removedMenuGroupItems(int group) +{ + if (!removed_menu_groups_.contains(group)) { + return QList(); + } + + QList sgi_list = removed_menu_groups_[group]; + std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan); + return sgi_list; +} + +void MainApplication::clearAddedMenuGroupItems() +{ + foreach (int group, added_menu_groups_.keys()) { + added_menu_groups_[group].clear(); + } +} + +void MainApplication::clearRemovedMenuGroupItems() +{ + foreach (int group, removed_menu_groups_.keys()) { + foreach (QAction *action, removed_menu_groups_[group]) { + delete action; + } + removed_menu_groups_[group].clear(); + } +} + +#ifdef HAVE_LIBPCAP + +static void +iface_mon_event_cb(const char *iface, int added, int up) +{ + int present = 0; + guint ifs, j; + interface_t *device; + interface_options *interface_opts; + + for (ifs = 0; ifs < global_capture_opts.all_ifaces->len; ifs++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, ifs); + if (strcmp(device->name, iface) == 0) { + present = 1; + if (!up) { + /* + * Interface went down or disappeared; remove all instances + * of it from the current list of interfaces selected + * for capturing. + */ + for (j = 0; j < global_capture_opts.ifaces->len; j++) { + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, j); + if (strcmp(interface_opts->name, device->name) == 0) { + capture_opts_del_iface(&global_capture_opts, j); + } + } + } + } + } + + mainApp->emitLocalInterfaceEvent(iface, added, up); + if (present != up) { + /* + * We've been told that there's a new interface or that an old + * interface is gone; reload the local interface list. + */ + mainApp->refreshLocalInterfaces(); + } +} + +#endif + +void MainApplication::ifChangeEventsAvailable() +{ +#ifdef HAVE_LIBPCAP + /* + * Something's readable from the descriptor for interface + * monitoring. + * + * Have the interface-monitoring code Read whatever interface-change + * events are available, and call the callback for them. + */ + iface_mon_event(); +#endif +} + +void MainApplication::emitLocalInterfaceEvent(const char *ifname, int added, int up) +{ + emit localInterfaceEvent(ifname, added, up); +} + +void MainApplication::refreshLocalInterfaces() +{ + extcap_clear_interfaces(); + +#ifdef HAVE_LIBPCAP + /* + * Reload the local interface list. + */ + scan_local_interfaces(main_window_update); + + /* + * Now emit a signal to indicate that the list changed, so that all + * places displaying the list will get updated. + * + * XXX - only if it *did* change. + */ + emit localInterfaceListChanged(); +#endif +} + +void MainApplication::allSystemsGo() +{ + QString display_filter = NULL; + initialized_ = true; + emit appInitialized(); + while (pending_open_files_.length() > 0) { + emit openCaptureFile(pending_open_files_.front(), display_filter, WTAP_TYPE_AUTO); + pending_open_files_.pop_front(); + } + software_update_init(); + +#ifdef HAVE_LIBPCAP + int err; + err = iface_mon_start(&iface_mon_event_cb); + if (err == 0) { + if_notifier_ = new QSocketNotifier(iface_mon_get_sock(), + QSocketNotifier::Read, this); + connect(if_notifier_, SIGNAL(activated(int)), SLOT(ifChangeEventsAvailable())); + } +#endif +} + +_e_prefs *MainApplication::readConfigurationFiles(bool reset) +{ + e_prefs *prefs_p; + + if (reset) { + // + // Reset current preferences and enabled/disabled protocols and + // heuristic dissectors before reading. + // (Needed except when this is called at startup.) + // + prefs_reset(); + proto_reenable_all(); + } + + /* Load libwireshark settings from the current profile. */ + prefs_p = epan_load_settings(); + + /* Read the capture filter file. */ + read_filter_list(CFILTER_LIST); + + return prefs_p; +} + +QList MainApplication::recentItems() const { + return recent_captures_; +} + +void MainApplication::addRecentItem(const QString filename, qint64 size, bool accessible) { + recent_item_status *ri = new(recent_item_status); + + ri->filename = filename; + ri->size = size; + ri->accessible = accessible; + ri->in_thread = false; + recent_captures_.prepend(ri); + + itemStatusFinished(filename, size, accessible); +} + +void MainApplication::removeRecentItem(const QString &filename) +{ + QMutableListIterator rii(recent_captures_); + + while (rii.hasNext()) { + recent_item_status *ri = rii.next(); +#ifdef _WIN32 + /* Do a case insensitive compare on win32 */ + if (ri->filename.compare(filename, Qt::CaseInsensitive) == 0) { +#else + /* Do a case sensitive compare on UN*Xes. + * + * XXX - on UN*Xes such as macOS, where you can use pathconf() + * to check whether a given file system is case-sensitive or + * not, we should check whether this particular file system + * is case-sensitive and do the appropriate comparison. + */ + if (ri->filename.compare(filename) == 0) { +#endif + rii.remove(); + delete(ri); + } + } + + emit updateRecentCaptureStatus(NULL, 0, false); +} + +static void switchTranslator(QTranslator& myTranslator, const QString& filename, + const QString& searchPath) +{ + mainApp->removeTranslator(&myTranslator); + + if (myTranslator.load(filename, searchPath)) + mainApp->installTranslator(&myTranslator); +} + +void MainApplication::loadLanguage(const QString newLanguage) +{ + QLocale locale; + QString localeLanguage; + + if (newLanguage.isEmpty() || newLanguage == USE_SYSTEM_LANGUAGE) { + locale = QLocale::system(); + localeLanguage = locale.name(); + } else { + localeLanguage = newLanguage; + locale = QLocale(localeLanguage); + } + + QLocale::setDefault(locale); + switchTranslator(mainApp->translator, + QString("wireshark_%1.qm").arg(localeLanguage), QString(":/i18n/")); + if (QFile::exists(QString("%1/%2/wireshark_%3.qm") + .arg(get_datafile_dir()).arg("languages").arg(localeLanguage))) + switchTranslator(mainApp->translator, + QString("wireshark_%1.qm").arg(localeLanguage), QString(get_datafile_dir()) + QString("/languages")); + if (QFile::exists(QString("%1/wireshark_%3.qm") + .arg(gchar_free_to_qstring(get_persconffile_path("languages", FALSE))).arg(localeLanguage))) + switchTranslator(mainApp->translator, + QString("wireshark_%1.qm").arg(localeLanguage), gchar_free_to_qstring(get_persconffile_path("languages", FALSE))); + if (QFile::exists(QString("%1/qt_%2.qm") + .arg(get_datafile_dir()).arg(localeLanguage))) { + switchTranslator(mainApp->translatorQt, + QString("qt_%1.qm").arg(localeLanguage), QString(get_datafile_dir())); + } else if (QFile::exists(QString("%1/qt_%2.qm") + .arg(get_datafile_dir()).arg(localeLanguage.left(localeLanguage.lastIndexOf('_'))))) { + switchTranslator(mainApp->translatorQt, + QString("qt_%1.qm").arg(localeLanguage.left(localeLanguage.lastIndexOf('_'))), QString(get_datafile_dir())); + } else { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QString translationPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); +#else + QString translationPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); +#endif + switchTranslator(mainApp->translatorQt, QString("qt_%1.qm").arg(localeLanguage), translationPath); + } +} + +void MainApplication::doTriggerMenuItem(MainMenuItem menuItem) +{ + switch (menuItem) + { + case FileOpenDialog: + emit openCaptureFile(QString(), QString(), WTAP_TYPE_AUTO); + break; + case CaptureOptionsDialog: + emit openCaptureOptions(); + break; + } +} + +void MainApplication::zoomTextFont(int zoomLevel) +{ + // 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. + qreal zoom_size = mono_font_.pointSize() * 2 * qPow(qreal(1.1), zoomLevel); + zoom_size = qRound(zoom_size) / qreal(2.0); + zoom_size = qMax(zoom_size, qreal(1.0)); + + zoomed_font_ = mono_font_; + zoomed_font_.setPointSizeF(zoom_size); + 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) +bool MainApplication::softwareUpdateCanShutdown() { + software_update_ok_ = true; + // At this point the update is ready to install, but WinSparkle has + // not yet run the installer. We need to close our "Wireshark is + // running" mutexes since the IsWiresharkRunning NSIS macro checks + // for them. + // + // We must not exit the Qt main event loop here, which means we must + // not close the main window. + + // Step 1: See if we have any open files. + emit softwareUpdateRequested(); + if (software_update_ok_ == true) { + + // Step 2: Close the "running" mutexes. + close_app_running_mutex(); + } + return software_update_ok_; +} + +void MainApplication::softwareUpdateShutdownRequest() { + // At this point the installer has been launched. Neither Wireshark nor + // its children should have any "Wireshark is running" mutexes open. + // The main window should still be open as noted above in + // softwareUpdateCanShutdown and it's safe to exit the Qt main + // event loop. + + // Step 3: Quit. + emit softwareUpdateQuit(); +} +#endif + +void MainApplication::captureEventHandler(CaptureEvent ev) +{ + switch(ev.captureContext()) + { +#ifdef HAVE_LIBPCAP + case CaptureEvent::Update: + case CaptureEvent::Fixed: + switch (ev.eventType()) + { + case CaptureEvent::Started: + active_captures_++; + emit captureActive(active_captures_); + break; + case CaptureEvent::Finished: + active_captures_--; + emit captureActive(active_captures_); + break; + default: + break; + } + break; +#endif + case CaptureEvent::File: + case CaptureEvent::Reload: + case CaptureEvent::Rescan: + switch (ev.eventType()) + { + case CaptureEvent::Started: + QTimer::singleShot(TAP_UPDATE_DEFAULT_INTERVAL / 5, this, SLOT(updateTaps())); + QTimer::singleShot(TAP_UPDATE_DEFAULT_INTERVAL / 2, this, SLOT(updateTaps())); + break; + case CaptureEvent::Finished: + updateTaps(); + break; + default: + break; + } + break; + default: + break; + } +} + +void MainApplication::pushStatus(StatusInfo status, const QString &message, const QString &messagetip) +{ + if (! mainWindow() || ! qobject_cast(mainWindow())) + return; + + MainWindow * mw = qobject_cast(mainWindow()); + if (! mw->statusBar()) + return; + + MainStatusBar * bar = mw->statusBar(); + + switch(status) + { + case FilterSyntax: + bar->pushGenericStatus(MainStatusBar::STATUS_CTX_FILTER, message); + break; + case FieldStatus: + bar->pushGenericStatus(MainStatusBar::STATUS_CTX_FIELD, message); + break; + case FileStatus: + bar->pushGenericStatus(MainStatusBar::STATUS_CTX_FILE, message, messagetip); + break; + case ByteStatus: + bar->pushGenericStatus(MainStatusBar::STATUS_CTX_BYTE, message); + break; + case BusyStatus: + bar->pushGenericStatus(MainStatusBar::STATUS_CTX_PROGRESS, message, messagetip); + break; + case TemporaryStatus: + bar->pushGenericStatus(MainStatusBar::STATUS_CTX_TEMPORARY, message); + break; + } +} + +void MainApplication::popStatus(StatusInfo status) +{ + if (! mainWindow() || ! qobject_cast(mainWindow())) + return; + + MainWindow * mw = qobject_cast(mainWindow()); + if (! mw->statusBar()) + return; + + MainStatusBar * bar = mw->statusBar(); + + switch(status) + { + case FilterSyntax: + bar->popGenericStatus(MainStatusBar::STATUS_CTX_FILTER); + break; + case FieldStatus: + bar->popGenericStatus(MainStatusBar::STATUS_CTX_FIELD); + break; + case FileStatus: + bar->popGenericStatus(MainStatusBar::STATUS_CTX_FILE); + break; + case ByteStatus: + bar->popGenericStatus(MainStatusBar::STATUS_CTX_BYTE); + break; + case BusyStatus: + bar->popGenericStatus(MainStatusBar::STATUS_CTX_PROGRESS); + break; + case TemporaryStatus: + bar->popGenericStatus(MainStatusBar::STATUS_CTX_TEMPORARY); + break; + } +} + +void MainApplication::gotoFrame(int frame) +{ + if (! mainWindow() || ! qobject_cast(mainWindow())) + return; + + MainWindow * mw = qobject_cast(mainWindow()); + mw->gotoFrame(frame); +} diff --git a/ui/qt/main_application.h b/ui/qt/main_application.h new file mode 100644 index 00000000..dd5b0bab --- /dev/null +++ b/ui/qt/main_application.h @@ -0,0 +1,244 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MAIN_APPLICATION_H +#define MAIN_APPLICATION_H + +#include + +#include + +#include "wsutil/feature_list.h" + +#include "epan/register.h" + +#include "ui/help_url.h" + +#include +#include +#include +#include +#include +#include + +#include "capture_event.h" + +struct _e_prefs; + +class QAction; +class QSocketNotifier; + +// Recent items: +// - Read from prefs +// - Add from open file +// - Check current list +// - Signal updated item +// - +typedef struct _recent_item_status { + QString filename; + qint64 size; + bool accessible; + bool in_thread; +} recent_item_status; + +class MainApplication : public QApplication +{ + Q_OBJECT +public: + explicit MainApplication(int &argc, char **argv); + ~MainApplication(); + + enum AppSignal { + CaptureFilterListChanged, + ColumnsChanged, + DisplayFilterListChanged, + FieldsChanged, + FilterExpressionsChanged, + LocalInterfacesChanged, + NameResolutionChanged, + PacketDissectionChanged, + PreferencesChanged, + ProfileChanging, + RecentCapturesChanged, + RecentPreferencesRead, + FreezePacketList + }; + + enum MainMenuItem { + FileOpenDialog, + CaptureOptionsDialog + }; + + enum StatusInfo { + FilterSyntax, + FieldStatus, + FileStatus, + BusyStatus, + ByteStatus, + TemporaryStatus + }; + + void registerUpdate(register_action_e action, const char *message); + void emitAppSignal(AppSignal signal); + // Emitting app signals (PacketDissectionChanged in particular) from + // dialogs on macOS can be problematic. Dialogs should call queueAppSignal + // instead. + void queueAppSignal(AppSignal signal) { app_signals_ << signal; } + void emitStatCommandSignal(const QString &menu_path, const char *arg, void *userdata); + void emitTapParameterSignal(const QString cfg_abbr, const QString arg, void *userdata); + void addDynamicMenuGroupItem(int group, QAction *sg_action); + void appendDynamicMenuGroupItem(int group, QAction *sg_action); + void removeDynamicMenuGroupItem(int group, QAction *sg_action); + QList dynamicMenuGroupItems(int group); + QList addedMenuGroupItems(int group); + QList removedMenuGroupItems(int group); + void clearAddedMenuGroupItems(); + void clearRemovedMenuGroupItems(); + + void allSystemsGo(); + void emitLocalInterfaceEvent(const char *ifname, int added, int up); + + virtual void refreshLocalInterfaces(); + + struct _e_prefs * readConfigurationFiles(bool reset); + QList recentItems() const; + void addRecentItem(const QString filename, qint64 size, bool accessible); + void removeRecentItem(const QString &filename); + QDir openDialogInitialDir(); + void setLastOpenDirFromFilename(QString file_name); + void helpTopicAction(topic_action_e action); + const QFont monospaceFont(bool zoomed = false) const; + void setMonospaceFont(const char *font_string); + int monospaceTextSize(const char *str); + void setConfigurationProfile(const gchar *profile_name, bool write_recent_file = true); + void reloadLuaPluginsDelayed(); + bool isInitialized() { return initialized_; } + void setReloadingLua(bool is_reloading) { is_reloading_lua_ = is_reloading; } + bool isReloadingLua() { return is_reloading_lua_; } + const QIcon &normalIcon(); + const QIcon &captureIcon(); + const QString &windowTitleSeparator() const { return window_title_separator_; } + const QString windowTitleString(QStringList title_parts); + const QString windowTitleString(QString title_part) { return windowTitleString(QStringList() << title_part); } + void applyCustomColorsFromRecent(); +#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) + void rejectSoftwareUpdate() { software_update_ok_ = false; } + bool softwareUpdateCanShutdown(); + void softwareUpdateShutdownRequest(); +#endif + QWidget *mainWindow(); + + QTranslator translator; + QTranslator translatorQt; + void loadLanguage(const QString language); + + void doTriggerMenuItem(MainMenuItem menuItem); + + void zoomTextFont(int zoomLevel); + + void pushStatus(StatusInfo sinfo, const QString &message, const QString &messagetip = QString()); + void popStatus(StatusInfo sinfo); + + void gotoFrame(int frameNum); + +private: + bool initialized_; + bool is_reloading_lua_; + QFont mono_font_; + QFont zoomed_font_; + QTimer recent_timer_; + QTimer packet_data_timer_; + QTimer tap_update_timer_; + QList pending_open_files_; + QSocketNotifier *if_notifier_; + static QString window_title_separator_; + QList app_signals_; + int active_captures_; +#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) + bool software_update_ok_; +#endif + + void storeCustomColorsInRecent(); + void clearDynamicMenuGroupItems(); + +protected: + bool event(QEvent *event); + virtual void initializeIcons() = 0; + + QIcon normal_icon_; + QIcon capture_icon_; + +signals: + void appInitialized(); + void localInterfaceEvent(const char *ifname, int added, int up); + void localInterfaceListChanged(); + void openCaptureFile(QString cf_path, QString display_filter, unsigned int type); + void openCaptureOptions(); + void recentPreferencesRead(); + void updateRecentCaptureStatus(const QString &filename, qint64 size, bool accessible); + void splashUpdate(register_action_e action, const char *message); + void profileChanging(); + void profileNameChanged(const gchar *profile_name); + + void freezePacketList(bool changing_profile); + void columnsChanged(); // XXX This recreates the packet list. We might want to rename it accordingly. + void captureFilterListChanged(); + void displayFilterListChanged(); + void filterExpressionsChanged(); + void packetDissectionChanged(); + void preferencesChanged(); + void addressResolutionChanged(); + void columnDataChanged(); + void checkDisplayFilter(); + void fieldsChanged(); + void reloadLuaPlugins(); +#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) + // Each of these are called from a separate thread. + void softwareUpdateRequested(); + void softwareUpdateQuit(); +#endif + + void openStatCommandDialog(const QString &menu_path, const char *arg, void *userdata); + void openTapParameterDialog(const QString cfg_str, const QString arg, void *userdata); + + /* Signals activation and stop of a capture. The value provides the number of active captures */ + void captureActive(int); + + void zoomRegularFont(const QFont & font); + void zoomMonospaceFont(const QFont & font); + +public slots: + void clearRecentCaptures(); + void refreshRecentCaptures(); + + void captureEventHandler(CaptureEvent); + + // Flush queued app signals. Should be called from the main window after + // each dialog that calls queueAppSignal closes. + void flushAppSignals(); + +private slots: + void updateTaps(); + + void cleanup(); + void ifChangeEventsAvailable(); + void itemStatusFinished(const QString filename = "", qint64 size = 0, bool accessible = false); + void refreshPacketData(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) && defined(Q_OS_WIN) + void colorSchemeChanged(); +#endif +}; + +extern MainApplication *mainApp; + +/** Global compile time version info */ +extern void gather_wireshark_qt_compiled_info(feature_list l); +/** Global runtime version info */ +extern void gather_wireshark_runtime_info(feature_list l); +#endif // MAIN_APPLICATION_H diff --git a/ui/qt/main_status_bar.cpp b/ui/qt/main_status_bar.cpp new file mode 100644 index 00000000..a504aa7a --- /dev/null +++ b/ui/qt/main_status_bar.cpp @@ -0,0 +1,697 @@ +/* main_status_bar.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include "file.h" + +#include +#include + +#include +#include + +#include "ui/main_statusbar.h" +#include +#include + +#include "capture_file.h" +#include "main_status_bar.h" +#include "profile_dialog.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// To do: +// - Use the CaptureFile class. + +// XXX - The GTK+ code assigns priorities to these and pushes/pops accordingly. + +Q_DECLARE_METATYPE(ProfileDialog::ProfileAction) + +// If we ever add support for multiple windows this will need to be replaced. +// See also: main_window.cpp +static MainStatusBar *cur_main_status_bar_ = NULL; + +/* + * Push a formatted temporary message onto the statusbar. + */ +void +statusbar_push_temporary_msg(const gchar *msg_format, ...) +{ + va_list ap; + QString push_msg; + + if (!cur_main_status_bar_) return; + + va_start(ap, msg_format); + push_msg = QString::vasprintf(msg_format, ap); + va_end(ap); + + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, push_msg); +} + +/* + * Update the packets statusbar to the current values + */ +void +packets_bar_update(void) +{ + if (!cur_main_status_bar_) return; + + cur_main_status_bar_->updateCaptureStatistics(NULL); +} + +static const int icon_size = 14; // px + +MainStatusBar::MainStatusBar(QWidget *parent) : + QStatusBar(parent), + cap_file_(NULL), + #ifdef HAVE_LIBPCAP + ready_msg_(tr("Ready to load or capture")), + #else + ready_msg_(tr("Ready to load file")), + #endif + cs_fixed_(false), + cs_count_(0) +{ + QSplitter *splitter = new QSplitter(this); + QWidget *info_progress = new QWidget(this); + QHBoxLayout *info_progress_hb = new QHBoxLayout(info_progress); + +#if defined(Q_OS_WIN) + // Handles are the same color as widgets, at least on Windows 7. + splitter->setHandleWidth(3); + splitter->setStyleSheet(QString( + "QSplitter::handle {" + " border-left: 1px solid palette(mid);" + " border-right: 1px solid palette(mid);" + "}" + )); +#endif + +#ifdef Q_OS_MAC + profile_status_.setAttribute(Qt::WA_MacSmallSize, true); +#endif + + QString button_ss = + "QToolButton {" + " border: none;" + " background: transparent;" // Disables platform style on Windows. + " padding: 0px;" + " margin: 0px;" + "}"; + + expert_button_ = new QToolButton(this); + expert_button_->setIconSize(QSize(icon_size, icon_size)); + expert_button_->setStyleSheet(button_ss); + expert_button_->hide(); + + // We just want a clickable image. Using a QPushButton or QToolButton would require + // a lot of adjustment. + StockIcon comment_icon("x-capture-comment-update"); + comment_button_ = new QToolButton(this); + comment_button_->setIcon(comment_icon); + comment_button_->setIconSize(QSize(icon_size, icon_size)); + comment_button_->setStyleSheet(button_ss); + + comment_button_->setToolTip(tr("Open the Capture File Properties dialog")); + comment_button_->setEnabled(false); + connect(expert_button_, SIGNAL(clicked(bool)), this, SIGNAL(showExpertInfo())); + connect(comment_button_, SIGNAL(clicked(bool)), this, SIGNAL(editCaptureComment())); + + info_progress_hb->setContentsMargins(icon_size / 2, 0, 0, 0); + + info_status_.setTemporaryContext(STATUS_CTX_TEMPORARY); + info_status_.setShrinkable(true); + + info_progress_hb->addWidget(expert_button_); + info_progress_hb->addWidget(comment_button_); + info_progress_hb->addWidget(&info_status_); + info_progress_hb->addWidget(&progress_frame_); + info_progress_hb->addStretch(10); + + splitter->addWidget(info_progress); + splitter->addWidget(&packet_status_); + splitter->addWidget(&profile_status_); + + splitter->setStretchFactor(0, 3); + splitter->setStretchFactor(1, 3); + splitter->setStretchFactor(2, 1); + + addWidget(splitter, 1); + + cur_main_status_bar_ = this; + + splitter->hide(); + info_status_.pushText(ready_msg_, STATUS_CTX_MAIN); + packets_bar_update(); + +#ifdef QWINTASKBARPROGRESS_H + progress_frame_.enableTaskbarUpdates(true); +#endif + + connect(mainApp, SIGNAL(appInitialized()), splitter, SLOT(show())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(appInitialized())); + connect(&info_status_, SIGNAL(toggleTemporaryFlash(bool)), + this, SLOT(toggleBackground(bool))); + connect(mainApp, SIGNAL(profileNameChanged(const gchar *)), + this, SLOT(setProfileName())); + connect(&profile_status_, SIGNAL(clickedAt(QPoint,Qt::MouseButton)), + this, SLOT(showProfileMenu(QPoint,Qt::MouseButton))); + + connect(&progress_frame_, SIGNAL(stopLoading()), + this, SIGNAL(stopLoading())); +} + +void MainStatusBar::showExpert() { + expertUpdate(); +} + +void MainStatusBar::captureFileClosing() { + expert_button_->hide(); + progress_frame_.captureFileClosing(); + popGenericStatus(STATUS_CTX_FIELD); +} + +void MainStatusBar::expertUpdate() { + // won't load @2x versions in Qt versions earlier than 5.4. + // https://bugreports.qt.io/browse/QTBUG-36383 + // We might have to switch to a QPushButton. + QString stock_name = "x-expert-"; + QString tt_text = tr(" is the highest expert information level"); + + switch(expert_get_highest_severity()) { + case(PI_ERROR): + stock_name.append("error"); + tt_text.prepend(tr("ERROR")); + break; + case(PI_WARN): + stock_name.append("warn"); + tt_text.prepend(tr("WARNING")); + break; + case(PI_NOTE): + stock_name.append("note"); + tt_text.prepend(tr("NOTE")); + break; + case(PI_CHAT): + stock_name.append("chat"); + tt_text.prepend(tr("CHAT")); + break; +// case(PI_COMMENT): +// m_expertStatus.setText(""); +// break; + default: + stock_name.append("none"); + tt_text = tr("No expert information"); + break; + } + + StockIcon expert_icon(stock_name); + expert_button_->setIcon(expert_icon); + expert_button_->setToolTip(tt_text); + expert_button_->show(); +} + +// ui/gtk/main_statusbar.c +void MainStatusBar::setFileName(CaptureFile &cf) +{ + if (cf.isValid()) { + popGenericStatus(STATUS_CTX_FILE); + QString msgtip = QString("%1 (%2)") + .arg(cf.capFile()->filename) + .arg(file_size_to_qstring(cf.capFile()->f_datalen)); + pushGenericStatus(STATUS_CTX_FILE, cf.fileName(), msgtip); + } +} + +void MainStatusBar::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) { + info_status_.popText(STATUS_CTX_MAIN); + info_status_.pushText(ready_msg_, STATUS_CTX_MAIN); + setStatusbarForCaptureFile(); + showCaptureStatistics(); + setProfileName(); + } + QStatusBar::changeEvent(event); +} + +void MainStatusBar::setCaptureFile(capture_file *cf) +{ + cap_file_ = cf; + comment_button_->setEnabled(cap_file_ != NULL); +} + +void MainStatusBar::setStatusbarForCaptureFile() +{ + if (cap_file_ && cap_file_->filename && (cap_file_->state != FILE_CLOSED)) { + popGenericStatus(STATUS_CTX_FILE); + QString msgtip = QString("%1 (%2)") + .arg(cap_file_->filename) + .arg(file_size_to_qstring(cap_file_->f_datalen)); + pushGenericStatus(STATUS_CTX_FILE, + gchar_free_to_qstring(cf_get_display_name(cap_file_)), msgtip); + } +} + +void MainStatusBar::selectedFieldChanged(FieldInformation * finfo) +{ + QString item_info; + + if (! finfo) { + pushGenericStatus(STATUS_CTX_FIELD, item_info); + return; + } + + FieldInformation::HeaderInfo hInfo = finfo->headerInfo(); + + if (hInfo.isValid) + { + if (hInfo.description.length() > 0) { + item_info.append(hInfo.description); + } else { + item_info.append(hInfo.name); + } + } + + if (!item_info.isEmpty()) { + int finfo_length; + if (hInfo.isValid) + item_info.append(" (" + hInfo.abbreviation + ")"); + + finfo_length = finfo->position().length + finfo->appendix().length; + if (finfo_length > 0) { + item_info.append(", " + tr("%Ln byte(s)", "", finfo_length)); + } + } + + pushGenericStatus(STATUS_CTX_FIELD, item_info); +} + +void MainStatusBar::highlightedFieldChanged(FieldInformation * finfo) +{ + QString hint; + + if (finfo) + { + FieldInformation::Position pos = finfo->position(); + QString field_str; + + if (pos.length < 2) { + hint = QString(tr("Byte %1")).arg(pos.start); + } else { + hint = QString(tr("Bytes %1-%2")).arg(pos.start).arg(pos.start + pos.length - 1); + } + hint += QString(": %1 (%2)") + .arg(finfo->headerInfo().name) + .arg(finfo->headerInfo().abbreviation); + } + + pushGenericStatus(STATUS_CTX_BYTE, hint); +} + +void MainStatusBar::pushGenericStatus(StatusContext status, const QString &message, const QString &messagetip) +{ + LabelStack * stack = &info_status_; + + if (status == STATUS_CTX_MAIN) + stack = &packet_status_; + + if (message.isEmpty() && status != STATUS_CTX_FILE && status != STATUS_CTX_TEMPORARY && status != STATUS_CTX_PROGRESS) + popGenericStatus(status); + else + stack->pushText(message, status); + + stack->setToolTip(messagetip); + + if (status == STATUS_CTX_FILTER || status == STATUS_CTX_FILE) + expertUpdate(); +} + +void MainStatusBar::popGenericStatus(StatusContext status) +{ + LabelStack * stack = &info_status_; + + if (status == STATUS_CTX_MAIN) + stack = &packet_status_; + + stack->setToolTip(QString()); + + stack->popText(status); +} + +void MainStatusBar::setProfileName() +{ + profile_status_.setText(tr("Profile: %1").arg(get_profile_name())); +} + +void MainStatusBar::appInitialized() +{ + setProfileName(); + connect(mainApp->mainWindow(), SIGNAL(framesSelected(QList)), this, SLOT(selectedFrameChanged(QList))); +} + +void MainStatusBar::selectedFrameChanged(QList) +{ + showCaptureStatistics(); +} + +void MainStatusBar::showCaptureStatistics() +{ + QString packets_str; + + QList rows; + MainWindow * mw = qobject_cast(mainApp->mainWindow()); + if (mw) + rows = mw->selectedRows(true); + +#ifdef HAVE_LIBPCAP + if (cap_file_) { + /* Do we have any packets? */ + if (!cs_fixed_) { + cs_count_ = cap_file_->count; + } + if (cs_count_ > 0) { + if (prefs.gui_show_selected_packet && rows.count() == 1) { + packets_str.append(QString(tr("Selected Packet: %1 %2 ")) + .arg(rows.at(0)) + .arg(UTF8_MIDDLE_DOT)); + } + packets_str.append(QString(tr("Packets: %1 %4 Displayed: %2 (%3%)")) + .arg(cs_count_) + .arg(cap_file_->displayed_count) + .arg((100.0*cap_file_->displayed_count)/cs_count_, 0, 'f', 1) + .arg(UTF8_MIDDLE_DOT)); + if (rows.count() > 1) { + packets_str.append(QString(tr(" %1 Selected: %2 (%3%)")) + .arg(UTF8_MIDDLE_DOT) + .arg(rows.count()) + .arg((100.0*rows.count())/cs_count_, 0, 'f', 1)); + } + if (cap_file_->marked_count > 0) { + packets_str.append(QString(tr(" %1 Marked: %2 (%3%)")) + .arg(UTF8_MIDDLE_DOT) + .arg(cap_file_->marked_count) + .arg((100.0*cap_file_->marked_count)/cs_count_, 0, 'f', 1)); + } + if (cap_file_->drops_known) { + packets_str.append(QString(tr(" %1 Dropped: %2 (%3%)")) + .arg(UTF8_MIDDLE_DOT) + .arg(cap_file_->drops) + .arg((100.0*cap_file_->drops)/cs_count_, 0, 'f', 1)); + } + if (cap_file_->ignored_count > 0) { + packets_str.append(QString(tr(" %1 Ignored: %2 (%3%)")) + .arg(UTF8_MIDDLE_DOT) + .arg(cap_file_->ignored_count) + .arg((100.0*cap_file_->ignored_count)/cs_count_, 0, 'f', 1)); + } + if (cap_file_->packet_comment_count > 0) { + packets_str.append(QString(tr(" %1 Comments: %2")) + .arg(UTF8_MIDDLE_DOT) + .arg(cap_file_->packet_comment_count)); + } + if (prefs.gui_show_file_load_time && !cap_file_->is_tempfile) { + /* Loading an existing file */ + gulong computed_elapsed = cf_get_computed_elapsed(cap_file_); + packets_str.append(QString(tr(" %1 Load time: %2:%3.%4")) + .arg(UTF8_MIDDLE_DOT) + .arg(computed_elapsed/60000, 2, 10, QLatin1Char('0')) + .arg(computed_elapsed%60000/1000, 2, 10, QLatin1Char('0')) + .arg(computed_elapsed%1000, 3, 10, QLatin1Char('0'))); + } + } + } else if (cs_fixed_ && cs_count_ > 0) { + /* There shouldn't be any rows without a cap_file_ but this is benign */ + if (prefs.gui_show_selected_packet && rows.count() == 1) { + packets_str.append(QString(tr("Selected Packet: %1 %2 ")) + .arg(rows.at(0)) + .arg(UTF8_MIDDLE_DOT)); + } + packets_str.append(QString(tr("Packets: %1")) + .arg(cs_count_)); + } +#endif // HAVE_LIBPCAP + + if (packets_str.isEmpty()) { + packets_str = tr("No Packets"); + } + + popGenericStatus(STATUS_CTX_MAIN); + pushGenericStatus(STATUS_CTX_MAIN, packets_str); +} + +void MainStatusBar::updateCaptureStatistics(capture_session *cap_session) +{ + cs_fixed_ = false; + +#ifndef HAVE_LIBPCAP + Q_UNUSED(cap_session) +#else + if ((!cap_session || cap_session->cf == cap_file_) && cap_file_ && cap_file_->count) { + cs_count_ = cap_file_->count; + } else { + cs_count_ = 0; + } +#endif // HAVE_LIBPCAP + + showCaptureStatistics(); +} + +void MainStatusBar::updateCaptureFixedStatistics(capture_session *cap_session) +{ + cs_fixed_ = true; + +#ifndef HAVE_LIBPCAP + Q_UNUSED(cap_session) +#else + if (cap_session && cap_session->count) { + cs_count_ = cap_session->count; + } else { + cs_count_ = 0; + } +#endif // HAVE_LIBPCAP + + showCaptureStatistics(); +} + +void MainStatusBar::showProfileMenu(const QPoint &global_pos, Qt::MouseButton button) +{ + ProfileModel model; + + QMenu * ctx_menu_; + QMenu * profile_menu; + if (button == Qt::LeftButton) { + ctx_menu_ = nullptr; + profile_menu = new QMenu(this); + profile_menu->setAttribute(Qt::WA_DeleteOnClose); + } else { + ctx_menu_ = new QMenu(this); + ctx_menu_->setAttribute(Qt::WA_DeleteOnClose); + profile_menu = new QMenu(ctx_menu_); + } + QActionGroup * global = new QActionGroup(profile_menu); + QActionGroup * user = new QActionGroup(profile_menu); + + for (int cnt = 0; cnt < model.rowCount(); cnt++) + { + QModelIndex idx = model.index(cnt, ProfileModel::COL_NAME); + if (! idx.isValid()) + continue; + + + QAction * pa = Q_NULLPTR; + QString name = idx.data().toString(); + + // An ampersand in the menu item's text sets Alt+F as a shortcut for this menu. + // Use "&&" to get a real ampersand in the menu bar. + name.replace('&', "&&"); + + if (idx.data(ProfileModel::DATA_IS_DEFAULT).toBool()) + { + pa = profile_menu->addAction(name); + } + else if (idx.data(ProfileModel::DATA_IS_GLOBAL).toBool()) + { + /* Check if this profile does not exist as user */ + if (cnt == model.findByName(name)) + pa = global->addAction(name); + } + else + pa = user->addAction(name); + + if (! pa) + continue; + + pa->setCheckable(true); + if (idx.data(ProfileModel::DATA_IS_SELECTED).toBool()) + pa->setChecked(true); + + pa->setFont(idx.data(Qt::FontRole).value()); + pa->setProperty("profile_name", idx.data()); + pa->setProperty("profile_is_global", idx.data(ProfileModel::DATA_IS_GLOBAL)); + + connect(pa, &QAction::triggered, this, &MainStatusBar::switchToProfile); + } + + profile_menu->addActions(user->actions()); + profile_menu->addSeparator(); + profile_menu->addActions(global->actions()); + + if (button == Qt::LeftButton) { + profile_menu->popup(global_pos); + } else { + + bool enable_edit = false; + + QModelIndex idx = model.activeProfile(); + if (! idx.data(ProfileModel::DATA_IS_DEFAULT).toBool() && ! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool()) + enable_edit = true; + + profile_menu->setTitle(tr("Switch to")); + QAction * action = ctx_menu_->addAction(tr("Manage Profiles…"), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::ShowProfiles); + + ctx_menu_->addSeparator(); + action = ctx_menu_->addAction(tr("New…"), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::NewProfile); + action = ctx_menu_->addAction(tr("Edit…"), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::EditCurrentProfile); + action->setEnabled(enable_edit); + action = ctx_menu_->addAction(tr("Delete"), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::DeleteCurrentProfile); + action->setEnabled(enable_edit); + ctx_menu_->addSeparator(); + +#ifdef HAVE_MINIZIP + QMenu * importMenu = new QMenu(tr("Import"), ctx_menu_); + action = importMenu->addAction(tr("From Zip File..."), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::ImportZipProfile); + action = importMenu->addAction(tr("From Directory..."), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::ImportDirProfile); + ctx_menu_->addMenu(importMenu); + + if (model.userProfilesExist()) + { + QMenu * exportMenu = new QMenu(tr("Export"), ctx_menu_); + if (enable_edit) + { + action = exportMenu->addAction(tr("Selected Personal Profile..."), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::ExportSingleProfile); + action->setEnabled(enable_edit); + } + action = exportMenu->addAction(tr("All Personal Profiles..."), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::ExportAllProfiles); + ctx_menu_->addMenu(exportMenu); + } + +#else + action = ctx_menu_->addAction(tr("Import"), this, SLOT(manageProfile())); + action->setProperty("dialog_action_", (int)ProfileDialog::ImportDirProfile); +#endif + ctx_menu_->addSeparator(); + + ctx_menu_->addMenu(profile_menu); + ctx_menu_->popup(global_pos); + } +} + +void MainStatusBar::toggleBackground(bool enabled) +{ + if (enabled) { + setStyleSheet(QString( + "QStatusBar {" + " background-color: %2;" + "}" + ) + .arg(ColorUtils::warningBackground().name())); + } else { + setStyleSheet(QString()); + } +} + +void MainStatusBar::switchToProfile() +{ + QAction *pa = qobject_cast(sender()); + + if (pa && pa->property("profile_name").isValid()) { + QString profile = pa->property("profile_name").toString(); + mainApp->setConfigurationProfile(profile.toUtf8().constData()); + } +} + +void MainStatusBar::manageProfile() +{ + QAction *pa = qobject_cast(sender()); + + if (pa) { + ProfileDialog * cp_dialog = new ProfileDialog(this); + cp_dialog->setAttribute(Qt::WA_DeleteOnClose); + + int profileAction = pa->property("dialog_action_").toInt(); + cp_dialog->execAction(static_cast(profileAction)); + } +} + +void MainStatusBar::captureEventHandler(CaptureEvent ev) +{ + switch(ev.captureContext()) + { +#ifdef HAVE_LIBPCAP + case CaptureEvent::Update: + switch (ev.eventType()) + { + case CaptureEvent::Continued: + updateCaptureStatistics(ev.capSession()); + break; + case CaptureEvent::Finished: + updateCaptureStatistics(ev.capSession()); + break; + default: + break; + } + break; + case CaptureEvent::Fixed: + switch (ev.eventType()) + { + case CaptureEvent::Continued: + updateCaptureFixedStatistics(ev.capSession()); + break; + default: + break; + } + break; +#endif + case CaptureEvent::Save: + switch (ev.eventType()) + { + case CaptureEvent::Finished: + case CaptureEvent::Failed: + case CaptureEvent::Stopped: + popGenericStatus(STATUS_CTX_FILE); + break; + default: + break; + } + break; + default: + break; + } +} diff --git a/ui/qt/main_status_bar.h b/ui/qt/main_status_bar.h new file mode 100644 index 00000000..611bc017 --- /dev/null +++ b/ui/qt/main_status_bar.h @@ -0,0 +1,103 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MAIN_STATUS_BAR_H +#define MAIN_STATUS_BAR_H + +#include "config.h" + +#include "cfile.h" + +#include "capture/capture_session.h" + +#include +#include +#include +#include "progress_frame.h" +#include "wireshark_application.h" + +#include +#include +#include + +class CaptureFile; +class QToolButton; + +class MainStatusBar : public QStatusBar +{ + Q_OBJECT +public: + explicit MainStatusBar(QWidget *parent = 0); + void showExpert(); + void captureFileClosing(); + void expertUpdate(); + void setFileName(CaptureFile &cf); + +protected: + + enum StatusContext { + STATUS_CTX_MAIN, + STATUS_CTX_FILE, + STATUS_CTX_FIELD, + STATUS_CTX_BYTE, + STATUS_CTX_FILTER, + STATUS_CTX_PROGRESS, + STATUS_CTX_TEMPORARY + }; + + virtual void changeEvent(QEvent* event); + +private: + QToolButton *expert_button_; + QToolButton *comment_button_; + LabelStack info_status_; + ProgressFrame progress_frame_; + LabelStack packet_status_; + ClickableLabel profile_status_; + capture_file *cap_file_; + QString ready_msg_; + + // Capture statistics + bool cs_fixed_; + guint32 cs_count_; + + void showCaptureStatistics(); + void setStatusbarForCaptureFile(); + + void pushGenericStatus(StatusContext status, const QString &message, const QString &messagetip = QString()); + void popGenericStatus(StatusContext status); + +signals: + void showExpertInfo(); + void editCaptureComment(); + void stopLoading(); + +public slots: + void setCaptureFile(capture_file *cf); + void selectedFieldChanged(FieldInformation *); + void highlightedFieldChanged(FieldInformation *); + void selectedFrameChanged(QList); + + void updateCaptureStatistics(capture_session * cap_session); + void updateCaptureFixedStatistics(capture_session * cap_session); + + void captureEventHandler(CaptureEvent ev); + +private slots: + void appInitialized(); + void toggleBackground(bool enabled); + void setProfileName(); + void switchToProfile(); + void manageProfile(); + void showProfileMenu(const QPoint &global_pos, Qt::MouseButton button); + + friend MainApplication; +}; + +#endif // MAIN_STATUS_BAR_H diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp new file mode 100644 index 00000000..0f54995f --- /dev/null +++ b/ui/qt/main_window.cpp @@ -0,0 +1,205 @@ +/* main_window.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include "ui/preference_utils.h" + +#include "main_window.h" + +#include "funnel_statistics.h" +#include "packet_list.h" +#include "widgets/display_filter_combo.h" + +// Packet Menu actions +static QList dynamic_packet_menu_actions; + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + main_stack_(nullptr), + welcome_page_(nullptr), + cur_layout_(QVector()), + packet_list_(nullptr), + proto_tree_(nullptr), + byte_view_tab_(nullptr), + packet_diagram_(nullptr), + df_combo_box_(nullptr), + main_status_bar_(nullptr) +{ + +} + +MainWindow::~MainWindow() +{ + clearAddedPacketMenus(); +} + +bool MainWindow::hasSelection() +{ + if (packet_list_) + return packet_list_->multiSelectActive(); + return false; +} + +/* + * As hasSelection() is not looking for one single packet + * selection, but at least 2, this method returns TRUE in + * this specific case. + */ +bool MainWindow::hasUniqueSelection() +{ + if (packet_list_) + return packet_list_->uniqueSelectActive(); + return false; +} + +QList MainWindow::selectedRows(bool useFrameNum) +{ + if (packet_list_) + return packet_list_->selectedRows(useFrameNum); + return QList(); +} + +frame_data* MainWindow::frameDataForRow(int row) const +{ + if (packet_list_) + return packet_list_->getFDataForRow(row); + + return Q_NULLPTR; +} + +void MainWindow::insertColumn(QString name, QString abbrev, gint pos) +{ + gint colnr = 0; + if (name.length() > 0 && abbrev.length() > 0) + { + colnr = column_prefs_add_custom(COL_CUSTOM, name.toStdString().c_str(), abbrev.toStdString().c_str(), pos); + packet_list_->columnsChanged(); + packet_list_->resizeColumnToContents(colnr); + prefs_main_write(); + } +} + +void MainWindow::gotoFrame(int packet_num) +{ + if (packet_num > 0) { + packet_list_->goToPacket(packet_num); + } +} + +QString MainWindow::getFilter() +{ + return df_combo_box_->currentText(); +} + +MainStatusBar *MainWindow::statusBar() +{ + return main_status_bar_; +} + +void MainWindow::setDisplayFilter(QString filter, FilterAction::Action action, FilterAction::ActionType filterType) +{ + emit filterAction(filter, action, filterType); +} + +/* + * Used for registering custom packet menus + * + * @param funnel_action a custom packet menu action + */ +void MainWindow::appendPacketMenu(QAction* funnel_action) +{ + dynamic_packet_menu_actions.append(funnel_action); + connect(funnel_action, SIGNAL(triggered(bool)), funnel_action, SLOT(triggerPacketCallback())); +} + +/* + * Returns the list of registered packet menu actions + * + * After ensuring that all stored custom packet menu actions + * are registered with the Wireshark GUI, it returns them as a list + * so that they can potentially be displayed to a user. + * + * @return the list of registered packet menu actions + */ +QList MainWindow::getPacketMenuActions() +{ + if (funnel_statistics_packet_menus_modified()) { + // If the packet menus were modified, we need to clear the already + // loaded packet menus to avoid duplicates + this->clearAddedPacketMenus(); + funnel_statistics_load_packet_menus(); + } + return dynamic_packet_menu_actions; +} + +/* + * Clears the list of registered packet menu actions + * + * Clears the list of registered packet menu actions + * and frees all associated memory. + */ +void MainWindow::clearAddedPacketMenus() +{ + for( int i=0; i myPacketMenuActions = this->getPacketMenuActions(); + if (myPacketMenuActions.isEmpty()) { + return insertedPacketMenu; + } + + // Build a set of fields present for efficient lookups + QSet fieldsPresent = QSet(); + for (guint fieldInfoIndex = 0; fieldInfoIndex < finfo_array->len; fieldInfoIndex++) { + field_info *fi = (field_info *)g_ptr_array_index (finfo_array, fieldInfoIndex); + fieldsPresent.insert(QString(fi->hfinfo->abbrev)); + } + + // Place actions in the relevant (sub)menu + // The 'root' menu is the ctx_menu, so map NULL to that + QHash menuTextToMenus; + menuTextToMenus.insert(NULL, ctx_menu); + foreach (QAction * action, myPacketMenuActions) { + if (! qobject_cast(action)) { + continue; + } + FunnelAction * packetAction = qobject_cast(action); + + // Only display a menu if all required fields are present + if (!fieldsPresent.contains(packetAction->getPacketRequiredFields())) { + continue; + } + + packetAction->setPacketData(finfo_array); + packetAction->addToMenu(ctx_menu, menuTextToMenus); + insertedPacketMenu = true; + } + return insertedPacketMenu; +} diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h new file mode 100644 index 00000000..ac5ddcf4 --- /dev/null +++ b/ui/qt/main_window.h @@ -0,0 +1,109 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include + +// frame_data also available with this include in the original wireshark_main_window code +//#include "follow_stream_dialog.h" + + +#include "filter_action.h" + +#include +#include + +class QSplitter; +class QStackedWidget; +class ByteViewTab; +class DisplayFilterCombo; +class FieldInformation; +class MainStatusBar; +class PacketDiagram; +class PacketList; +class ProtoTree; +class WelcomePage; + +typedef struct _capture_file capture_file; + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + bool hasSelection(); + bool hasUniqueSelection(); + QList selectedRows(bool useFrameNum = false); + void insertColumn(QString name, QString abbrev, gint pos = -1); + void gotoFrame(int packet_num); + frame_data* frameDataForRow(int) const; + + QString getFilter(); + MainStatusBar *statusBar(); + + // Used for managing custom packet menus + void appendPacketMenu(QAction* funnel_action); + QList getPacketMenuActions(); + void clearAddedPacketMenus(); + bool addPacketMenus(QMenu * ctx_menu, GPtrArray *finfo_array); + +public slots: + void setDisplayFilter(QString filter, FilterAction::Action action, FilterAction::ActionType filterType); + virtual void filterPackets(QString, bool) = 0; + virtual void showPreferencesDialog(QString module_name) = 0; + void layoutPanes(); + void applyRecentPaneGeometry(); + +protected: + enum CopySelected { + CopyAllVisibleItems, + CopyAllVisibleSelectedTreeItems, + CopySelectedDescription, + CopySelectedFieldName, + CopySelectedValue, + CopyListAsText, + CopyListAsCSV, + CopyListAsYAML + }; + + void showWelcome(); + void showCapture(); + + QList menu_groups_; + QWidget* getLayoutWidget(layout_pane_content_e type); + + QStackedWidget *main_stack_; + WelcomePage *welcome_page_; + QSplitter master_split_; + QSplitter extra_split_; + QWidget empty_pane_; + QVector cur_layout_; + + PacketList *packet_list_; + ProtoTree *proto_tree_; + ByteViewTab *byte_view_tab_; + PacketDiagram *packet_diagram_; + DisplayFilterCombo *df_combo_box_; + MainStatusBar *main_status_bar_; + +signals: + void setCaptureFile(capture_file *cf); + void fieldSelected(FieldInformation *); + void framesSelected(QList); + void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + void displayFilterSuccess(bool success); + +}; + +#endif // MAINWINDOW_H diff --git a/ui/qt/main_window_layout.cpp b/ui/qt/main_window_layout.cpp new file mode 100644 index 00000000..b65b267f --- /dev/null +++ b/ui/qt/main_window_layout.cpp @@ -0,0 +1,235 @@ +/* main_window_layout.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include + +#include "ui/recent.h" + +#include "epan/prefs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +void MainWindow::showWelcome() +{ + main_stack_->setCurrentWidget(welcome_page_); +} + +void MainWindow::showCapture() +{ + main_stack_->setCurrentWidget(&master_split_); +} + +QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) { + switch (type) { + case layout_pane_content_none: + return &empty_pane_; + case layout_pane_content_plist: + return packet_list_; + case layout_pane_content_pdetails: + return proto_tree_; + case layout_pane_content_pbytes: + return byte_view_tab_; + case layout_pane_content_pdiagram: + return packet_diagram_; + default: + ws_assert_not_reached(); + return NULL; + } +} + + +// A new layout should be applied when it differs from the old layout AND +// at the following times: +// - At startup +// - When the preferences change +// - When the profile changes +void MainWindow::layoutPanes() +{ + QVector new_layout = QVector() << prefs.gui_layout_type + << prefs.gui_layout_content_1 + << prefs.gui_layout_content_2 + << prefs.gui_layout_content_3 + << recent.packet_list_show + << recent.tree_view_show + << recent.byte_view_show + << recent.packet_diagram_show; + + if (cur_layout_ == new_layout) return; + + QSplitter *parents[3]; + + // Reparent all widgets and add them back in the proper order below. + // This hides each widget as well. + bool frozen = packet_list_->freeze(); // Clears tree, byte view tabs, and diagram. + packet_list_->setParent(main_stack_); + proto_tree_->setParent(main_stack_); + byte_view_tab_->setParent(main_stack_); + packet_diagram_->setParent(main_stack_); + empty_pane_.setParent(main_stack_); + extra_split_.setParent(main_stack_); + + // XXX We should try to preserve geometries if we can, e.g. by + // checking to see if the layout type is the same. + switch(prefs.gui_layout_type) { + case(layout_type_2): + case(layout_type_1): + extra_split_.setOrientation(Qt::Horizontal); + /* Fall Through */ + case(layout_type_5): + master_split_.setOrientation(Qt::Vertical); + break; + + case(layout_type_4): + case(layout_type_3): + extra_split_.setOrientation(Qt::Vertical); + /* Fall Through */ + case(layout_type_6): + master_split_.setOrientation(Qt::Horizontal); + break; + + default: + ws_assert_not_reached(); + } + + switch(prefs.gui_layout_type) { + case(layout_type_5): + case(layout_type_6): + parents[0] = &master_split_; + parents[1] = &master_split_; + parents[2] = &master_split_; + break; + case(layout_type_2): + case(layout_type_4): + parents[0] = &master_split_; + parents[1] = &extra_split_; + parents[2] = &extra_split_; + break; + case(layout_type_1): + case(layout_type_3): + parents[0] = &extra_split_; + parents[1] = &extra_split_; + parents[2] = &master_split_; + break; + default: + ws_assert_not_reached(); + } + + if (parents[0] == &extra_split_) { + master_split_.addWidget(&extra_split_); + } + + parents[0]->addWidget(getLayoutWidget(prefs.gui_layout_content_1)); + + if (parents[2] == &extra_split_) { + master_split_.addWidget(&extra_split_); + } + + parents[1]->addWidget(getLayoutWidget(prefs.gui_layout_content_2)); + parents[2]->addWidget(getLayoutWidget(prefs.gui_layout_content_3)); + + if (frozen) { + // 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. + packet_list_->show(); + } + + const QList ms_children = master_split_.findChildren(); + + extra_split_.setVisible(ms_children.contains(&extra_split_)); + packet_list_->setVisible(ms_children.contains(packet_list_) && recent.packet_list_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); + packet_diagram_->setVisible(ms_children.contains(packet_diagram_) && recent.packet_diagram_show); + + if (frozen) { + packet_list_->thaw(true); + } + cur_layout_ = new_layout; +} + +// The recent layout geometry should be applied after the layout has been +// applied AND at the following times: +// - At startup +// - When the profile changes +void MainWindow::applyRecentPaneGeometry() +{ + // XXX This shrinks slightly each time the application is run. For some + // reason the master_split_ geometry is two pixels shorter when + // saveWindowGeometry is invoked. + + // This is also an awful lot of trouble to go through to reuse the GTK+ + // pane settings. We might want to add gui.geometry_main_master_sizes + // and gui.geometry_main_extra_sizes and save QSplitter::saveState in + // each. + + // Force a geometry recalculation + QWidget *cur_w = main_stack_->currentWidget(); + showCapture(); + QRect geom = main_stack_->geometry(); + QList master_sizes = master_split_.sizes(); + QList extra_sizes = extra_split_.sizes(); + main_stack_->setCurrentWidget(cur_w); + + int master_last_size = master_split_.orientation() == Qt::Vertical ? geom.height() : geom.width(); + master_last_size -= master_split_.handleWidth() * (master_sizes.length() - 1); + + int extra_last_size = extra_split_.orientation() == Qt::Vertical ? geom.height() : geom.width(); + extra_last_size -= extra_split_.handleWidth(); + + if (recent.gui_geometry_main_upper_pane > 0) { + master_sizes[0] = recent.gui_geometry_main_upper_pane; + master_last_size -= recent.gui_geometry_main_upper_pane; + } else { + master_sizes[0] = master_last_size / master_sizes.length(); + master_last_size -= master_last_size / master_sizes.length(); + } + + if (recent.gui_geometry_main_lower_pane > 0) { + if (master_sizes.length() > 2) { + master_sizes[1] = recent.gui_geometry_main_lower_pane; + master_last_size -= recent.gui_geometry_main_lower_pane; + } else if (extra_sizes.length() > 0) { + extra_sizes[0] = recent.gui_geometry_main_lower_pane; + extra_last_size -= recent.gui_geometry_main_lower_pane; + extra_sizes.last() = extra_last_size; + } + } else { + if (master_sizes.length() > 2) { + master_sizes[1] = master_last_size / 2; + master_last_size -= master_last_size / 2; + } else if (extra_sizes.length() > 0) { + extra_sizes[0] = extra_last_size / 2; + extra_last_size -= extra_last_size / 2; + extra_sizes.last() = extra_last_size; + } + } + + master_sizes.last() = master_last_size; + + master_split_.setSizes(master_sizes); + extra_split_.setSizes(extra_sizes); +} diff --git a/ui/qt/main_window_preferences_frame.cpp b/ui/qt/main_window_preferences_frame.cpp new file mode 100644 index 00000000..9659c928 --- /dev/null +++ b/ui/qt/main_window_preferences_frame.cpp @@ -0,0 +1,222 @@ +/* main_window_preferences_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "main_window_preferences_frame.h" +#include + +#include +#include "ui/language.h" + +#include +#include +#include +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include + +MainWindowPreferencesFrame::MainWindowPreferencesFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::MainWindowPreferencesFrame) +{ + ui->setupUi(this); + + pref_geometry_save_position_ = prefFromPrefPtr(&prefs.gui_geometry_save_position); + pref_geometry_save_size_ = prefFromPrefPtr(&prefs.gui_geometry_save_size); + pref_geometry_save_maximized_ = prefFromPrefPtr(&prefs.gui_geometry_save_maximized); + pref_fileopen_style_ = prefFromPrefPtr(&prefs.gui_fileopen_style); + pref_fileopen_dir_ = prefFromPrefPtr(&prefs.gui_fileopen_dir); + pref_recent_df_entries_max_ = prefFromPrefPtr(&prefs.gui_recent_df_entries_max); + pref_recent_files_count_max_ = prefFromPrefPtr(&prefs.gui_recent_files_count_max); + pref_ask_unsaved_ = prefFromPrefPtr(&prefs.gui_ask_unsaved); + pref_autocomplete_filter_ = prefFromPrefPtr(&prefs.gui_autocomplete_filter); + pref_toolbar_main_style_ = prefFromPrefPtr(&prefs.gui_toolbar_main_style); + pref_window_title_ = prefFromPrefPtr(&prefs.gui_window_title); + pref_prepend_window_title_ = prefFromPrefPtr(&prefs.gui_prepend_window_title); + + QStyleOption style_opt; + QString indent_ss = QString( + "QRadioButton, QLineEdit, QLabel {" + " margin-left: %1px;" + "}" + ).arg(ui->geometryCheckBox->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left()); + ui->foStyleLastOpenedRadioButton->setStyleSheet(indent_ss); + ui->foStyleSpecifiedRadioButton->setStyleSheet(indent_ss); + ui->maxFilterLineEdit->setStyleSheet(indent_ss); + ui->maxRecentLineEdit->setStyleSheet(indent_ss); + + int num_entry_width = ui->maxFilterLineEdit->fontMetrics().height() * 3; + int num_entry_height = ui->maxFilterLineEdit->fontMetrics().height(); + // Some styles (e.g., adwaita) add some extra space around the contents. + // Find the actual maximum size to set the widget. + QStyleOptionFrame opt; + initStyleOption(&opt); + QSize num_entry_size = ui->maxRecentLineEdit->style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(num_entry_width, num_entry_height)); + ui->maxFilterLineEdit->setMaximumWidth(num_entry_size.width()); + ui->maxRecentLineEdit->setMaximumWidth(num_entry_size.width()); + + QString li_path = QString(":/languages/language%1.svg").arg(ColorUtils::themeIsDark() ? ".dark" : ""); + QIcon language_icon = QIcon(li_path); + ui->languageComboBox->setItemIcon(0, language_icon); + + QString globalLanguagesPath(QString(get_datafile_dir()) + "/languages/"); + QString userLanguagesPath(gchar_free_to_qstring(get_persconffile_path("languages/", FALSE))); + + QStringList filenames = QDir(":/i18n/").entryList(QStringList("wireshark_*.qm")); + filenames += QDir(globalLanguagesPath).entryList(QStringList("wireshark_*.qm")); + filenames += QDir(userLanguagesPath).entryList(QStringList("wireshark_*.qm")); + + for (int i = 0; i < filenames.size(); i += 1) { + QString locale; + locale = filenames[i]; + locale.truncate(locale.lastIndexOf('.')); + locale.remove(0, locale.indexOf('_') + 1); + + QString lang = QLocale::languageToString(QLocale(locale).language()); + + ui->languageComboBox->addItem(lang, locale); + } + + ui->languageComboBox->setItemData(0, USE_SYSTEM_LANGUAGE); + ui->languageComboBox->model()->sort(0); + + for (int i = 0; i < ui->languageComboBox->count(); i += 1) { + if (QString(language) == ui->languageComboBox->itemData(i).toString()) { + ui->languageComboBox->setCurrentIndex(i); + break; + } + } + +} + +MainWindowPreferencesFrame::~MainWindowPreferencesFrame() +{ + delete ui; +} + +void MainWindowPreferencesFrame::showEvent(QShowEvent *) +{ + updateWidgets(); +} + +void MainWindowPreferencesFrame::updateWidgets() +{ + // Yes, this means we're potentially clobbering two prefs in favor of one. + if (prefs_get_bool_value(pref_geometry_save_position_, pref_stashed) || prefs_get_bool_value(pref_geometry_save_size_, pref_stashed) || prefs_get_bool_value(pref_geometry_save_maximized_, pref_stashed)) { + ui->geometryCheckBox->setChecked(true); + } else { + ui->geometryCheckBox->setChecked(false); + } + + if (prefs_get_enum_value(pref_fileopen_style_, pref_stashed) == FO_STYLE_LAST_OPENED) { + ui->foStyleLastOpenedRadioButton->setChecked(true); + } else { + ui->foStyleSpecifiedRadioButton->setChecked(true); + } + + ui->foStyleSpecifiedLineEdit->setText(prefs_get_string_value(pref_fileopen_dir_, pref_stashed)); + + ui->maxFilterLineEdit->setText(QString::number(prefs_get_uint_value_real(pref_recent_df_entries_max_, pref_stashed))); + ui->maxRecentLineEdit->setText(QString::number(prefs_get_uint_value_real(pref_recent_files_count_max_, pref_stashed))); + + ui->confirmUnsavedCheckBox->setChecked(prefs_get_bool_value(pref_ask_unsaved_, pref_stashed)); + ui->displayAutoCompleteCheckBox->setChecked(prefs_get_bool_value(pref_autocomplete_filter_, pref_stashed)); + + ui->mainToolbarComboBox->setCurrentIndex(prefs_get_enum_value(pref_toolbar_main_style_, pref_stashed)); + + for (int i = 0; i < ui->languageComboBox->count(); i += 1) { + if (QString(language) == ui->languageComboBox->itemData(i).toString()) { + ui->languageComboBox->setCurrentIndex(i); + break; + } + } + + ui->windowTitle->setText(prefs_get_string_value(pref_window_title_, pref_stashed)); + ui->prependWindowTitle->setText(prefs_get_string_value(pref_prepend_window_title_, pref_stashed)); +} + +void MainWindowPreferencesFrame::on_geometryCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_geometry_save_position_, checked, pref_stashed); + prefs_set_bool_value(pref_geometry_save_size_, checked, pref_stashed); + prefs_set_bool_value(pref_geometry_save_maximized_, checked, pref_stashed); +} + +void MainWindowPreferencesFrame::on_foStyleLastOpenedRadioButton_toggled(bool checked) +{ + if (checked) { + prefs_set_enum_value(pref_fileopen_style_, FO_STYLE_LAST_OPENED, pref_stashed); + } +} + +void MainWindowPreferencesFrame::on_foStyleSpecifiedRadioButton_toggled(bool checked) +{ + if (checked) { + prefs_set_enum_value(pref_fileopen_style_, FO_STYLE_SPECIFIED, pref_stashed); + } +} + +void MainWindowPreferencesFrame::on_foStyleSpecifiedLineEdit_textEdited(const QString &new_dir) +{ + prefs_set_string_value(pref_fileopen_dir_, new_dir.toStdString().c_str(), pref_stashed); + ui->foStyleSpecifiedRadioButton->setChecked(true); +} + +void MainWindowPreferencesFrame::on_foStyleSpecifiedPushButton_clicked() +{ + QString specified_dir = WiresharkFileDialog::getExistingDirectory(this, tr("Open Files In")); + + if (specified_dir.isEmpty()) return; + + ui->foStyleSpecifiedLineEdit->setText(specified_dir); + prefs_set_string_value(pref_fileopen_dir_, specified_dir.toStdString().c_str(), pref_stashed); + ui->foStyleSpecifiedRadioButton->setChecked(true); +} + +void MainWindowPreferencesFrame::on_maxFilterLineEdit_textEdited(const QString &new_max) +{ + prefs_set_uint_value(pref_recent_df_entries_max_, new_max.toUInt(), pref_stashed); +} + +void MainWindowPreferencesFrame::on_maxRecentLineEdit_textEdited(const QString &new_max) +{ + prefs_set_uint_value(pref_recent_files_count_max_, new_max.toUInt(), pref_stashed); +} + +void MainWindowPreferencesFrame::on_confirmUnsavedCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_ask_unsaved_, checked, pref_stashed); +} + +void MainWindowPreferencesFrame::on_displayAutoCompleteCheckBox_toggled(bool checked) +{ + prefs_set_bool_value(pref_autocomplete_filter_, checked, pref_stashed); +} + +void MainWindowPreferencesFrame::on_mainToolbarComboBox_currentIndexChanged(int index) +{ + prefs_set_enum_value(pref_toolbar_main_style_, index, pref_stashed); +} + +void MainWindowPreferencesFrame::on_languageComboBox_currentIndexChanged(int index) +{ + g_free(language); + + language = qstring_strdup(ui->languageComboBox->itemData(index).toString()); +} + +void MainWindowPreferencesFrame::on_windowTitle_textEdited(const QString &new_title) +{ + prefs_set_string_value(pref_window_title_, new_title.toStdString().c_str(), pref_stashed); +} + +void MainWindowPreferencesFrame::on_prependWindowTitle_textEdited(const QString &new_prefix) +{ + prefs_set_string_value(pref_prepend_window_title_, new_prefix.toStdString().c_str(), pref_stashed); +} diff --git a/ui/qt/main_window_preferences_frame.h b/ui/qt/main_window_preferences_frame.h new file mode 100644 index 00000000..52d01049 --- /dev/null +++ b/ui/qt/main_window_preferences_frame.h @@ -0,0 +1,65 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MAIN_WINDOW_PREFERENCES_FRAME_H +#define MAIN_WINDOW_PREFERENCES_FRAME_H + +#include + +#include + +namespace Ui { +class MainWindowPreferencesFrame; +} + +class MainWindowPreferencesFrame : public QFrame +{ + Q_OBJECT + +public: + explicit MainWindowPreferencesFrame(QWidget *parent = 0); + ~MainWindowPreferencesFrame(); + +protected: + void showEvent(QShowEvent *evt); + +private: + Ui::MainWindowPreferencesFrame *ui; + + pref_t *pref_geometry_save_position_; + pref_t *pref_geometry_save_size_; + pref_t *pref_geometry_save_maximized_; + pref_t *pref_fileopen_style_; + pref_t *pref_fileopen_dir_; + pref_t *pref_recent_df_entries_max_; + pref_t *pref_recent_files_count_max_; + pref_t *pref_ask_unsaved_; + pref_t *pref_autocomplete_filter_; + pref_t *pref_toolbar_main_style_; + pref_t *pref_window_title_; + pref_t *pref_prepend_window_title_; + void updateWidgets(); + +private slots: + void on_geometryCheckBox_toggled(bool checked); + void on_foStyleLastOpenedRadioButton_toggled(bool checked); + void on_foStyleSpecifiedRadioButton_toggled(bool checked); + void on_foStyleSpecifiedLineEdit_textEdited(const QString &new_dir); + void on_foStyleSpecifiedPushButton_clicked(); + void on_maxFilterLineEdit_textEdited(const QString &new_max); + void on_maxRecentLineEdit_textEdited(const QString &new_max); + void on_confirmUnsavedCheckBox_toggled(bool checked); + void on_displayAutoCompleteCheckBox_toggled(bool checked); + void on_mainToolbarComboBox_currentIndexChanged(int index); + void on_languageComboBox_currentIndexChanged(int index); + void on_windowTitle_textEdited(const QString &new_title); + void on_prependWindowTitle_textEdited(const QString &new_prefix); +}; + +#endif // MAIN_WINDOW_PREFERENCES_FRAME_H diff --git a/ui/qt/main_window_preferences_frame.ui b/ui/qt/main_window_preferences_frame.ui new file mode 100644 index 00000000..745e78b3 --- /dev/null +++ b/ui/qt/main_window_preferences_frame.ui @@ -0,0 +1,307 @@ + + + MainWindowPreferencesFrame + + + + 0 + 0 + 405 + 445 + + + + + 0 + 0 + + + + + 0 + 384 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + Checking this will save the size, position, and maximized state of the main window. + + + Remember main window size and placement + + + + + + + Open files in + + + + + + + + + This folder: + + + openInButtonGroup + + + + + + + + + + Browse… + + + + + + + The most recently used folder + + + openInButtonGroup + + + + + + + + + Show up to + + + + + + + + + + + + filter entries + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + recent files + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Confirm unsaved capture files + + + + + + + Display autocompletion for filter text + + + + + + + + + Main toolbar style: + + + + + + + + Icons only + + + + + Text only + + + + + Icons & Text + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Window title + + + + + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + + + + + + + + Prepend window title + + + + + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + + + + + + + + + Language: + + + + + + + true + + + + 16777215 + 16777215 + + + + + Use system setting + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + + + diff --git a/ui/qt/manage_interfaces_dialog.cpp b/ui/qt/manage_interfaces_dialog.cpp new file mode 100644 index 00000000..a8529e26 --- /dev/null +++ b/ui/qt/manage_interfaces_dialog.cpp @@ -0,0 +1,640 @@ +/* manage_interfaces_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "manage_interfaces_dialog.h" +#include + +#include "epan/prefs.h" +#include "epan/to_str.h" +#include "capture_opts.h" +#include "ui/capture_globals.h" +#include "ui/qt/capture_options_dialog.h" +#include +#include +#ifdef HAVE_PCAP_REMOTE +#include "ui/qt/remote_capture_dialog.h" +#include "ui/qt/remote_settings_dialog.h" +#include "capture/capture-pcap-util.h" +#include "ui/recent.h" +#endif +#include "ui/iface_lists.h" +#include "ui/preference_utils.h" +#include "ui/ws_ui_util.h" +#include + +#include + +#include "main_application.h" + +#include + +#include "ui/capture_ui_utils.h" + +#include + +#include +#include +#include +#include +#include +#include + +// To do: +// - Check the validity of pipes and remote interfaces and provide feedback +// via hintLabel. +// - We might want to move PathSelectionDelegate to its own module and use it in +// other parts of the application such as the general preferences and UATs. +// Qt Creator has a much more elaborate version from which we might want +// to draw inspiration. + +enum { + col_p_pipe_ +}; + + +enum +{ + col_r_show_, + col_r_host_dev_ +}; + +enum { + tab_local_, + tab_pipe_, + tab_remote_ +}; + +#ifdef HAVE_PCAP_REMOTE +static void populateExistingRemotes(gpointer key, gpointer value, gpointer user_data) +{ + ManageInterfacesDialog *dialog = (ManageInterfacesDialog*)user_data; + const gchar *host = (const gchar *)key; + struct remote_host *remote_host = (struct remote_host *)value; + remote_options global_remote_opts; + int err; + gchar *err_str; + + global_remote_opts.src_type = CAPTURE_IFREMOTE; + global_remote_opts.remote_host_opts.remote_host = g_strdup(host); + global_remote_opts.remote_host_opts.remote_port = g_strdup(remote_host->remote_port); + global_remote_opts.remote_host_opts.auth_type = remote_host->auth_type; + global_remote_opts.remote_host_opts.auth_username = g_strdup(remote_host->auth_username); + global_remote_opts.remote_host_opts.auth_password = g_strdup(remote_host->auth_password); + global_remote_opts.remote_host_opts.datatx_udp = FALSE; + global_remote_opts.remote_host_opts.nocap_rpcap = TRUE; + global_remote_opts.remote_host_opts.nocap_local = FALSE; +#ifdef HAVE_PCAP_SETSAMPLING + global_remote_opts.sampling_method = CAPTURE_SAMP_NONE; + global_remote_opts.sampling_param = 0; +#endif + GList *rlist = get_remote_interface_list(global_remote_opts.remote_host_opts.remote_host, + global_remote_opts.remote_host_opts.remote_port, + global_remote_opts.remote_host_opts.auth_type, + global_remote_opts.remote_host_opts.auth_username, + global_remote_opts.remote_host_opts.auth_password, + &err, &err_str); + if (rlist == NULL) { + switch (err) { + case 0: + QMessageBox::warning(dialog, QObject::tr("Error"), QObject::tr("No remote interfaces found.")); + break; + case CANT_GET_INTERFACE_LIST: + QMessageBox::critical(dialog, QObject::tr("Error"), err_str); + break; + case DONT_HAVE_PCAP: + QMessageBox::critical(dialog, QObject::tr("Error"), QObject::tr("PCAP not found")); + break; + default: + QMessageBox::critical(dialog, QObject::tr("Error"), QObject::tr("Unknown error")); + break; + } + return; + } + + emit dialog->remoteAdded(rlist, &global_remote_opts); +} +#endif /* HAVE_PCAP_REMOTE */ + +ManageInterfacesDialog::ManageInterfacesDialog(QWidget *parent) : + GeometryStateDialog(parent), + ui(new Ui::ManageInterfacesDialog) +{ + ui->setupUi(this); + loadGeometry(); + setAttribute(Qt::WA_DeleteOnClose, true); + + ui->addPipe->setStockIcon("list-add"); + ui->delPipe->setStockIcon("list-remove"); + ui->addRemote->setStockIcon("list-add"); + ui->delRemote->setStockIcon("list-remove"); + +#ifdef Q_OS_MAC + ui->addPipe->setAttribute(Qt::WA_MacSmallSize, true); + ui->delPipe->setAttribute(Qt::WA_MacSmallSize, true); + ui->addRemote->setAttribute(Qt::WA_MacSmallSize, true); + ui->delRemote->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + sourceModel = new InterfaceTreeCacheModel(this); + + proxyModel = new InterfaceSortFilterModel(this); + QList columns; + columns.append(IFTREE_COL_HIDDEN); + columns.append(IFTREE_COL_DESCRIPTION); + columns.append(IFTREE_COL_NAME); + columns.append(IFTREE_COL_COMMENT); + proxyModel->setColumns(columns); + proxyModel->setSourceModel(sourceModel); + proxyModel->setFilterHidden(false); +#ifdef HAVE_PCAP_REMOTE + proxyModel->setRemoteDisplay(false); +#endif + proxyModel->setFilterByType(false); + + ui->localView->setModel(proxyModel); + ui->localView->resizeColumnToContents(proxyModel->mapSourceToColumn(IFTREE_COL_HIDDEN)); + ui->localView->resizeColumnToContents(proxyModel->mapSourceToColumn(IFTREE_COL_NAME)); + + pipeProxyModel = new InterfaceSortFilterModel(this); + columns.clear(); + columns.append(IFTREE_COL_PIPE_PATH); + pipeProxyModel->setColumns(columns); + pipeProxyModel->setSourceModel(sourceModel); + pipeProxyModel->setFilterHidden(true); +#ifdef HAVE_PCAP_REMOTE + pipeProxyModel->setRemoteDisplay(false); +#endif + pipeProxyModel->setFilterByType(true, true); + pipeProxyModel->setInterfaceTypeVisible(IF_PIPE, false); + ui->pipeView->setModel(pipeProxyModel); + ui->delPipe->setEnabled(pipeProxyModel->rowCount() > 0); + + ui->pipeView->setItemDelegateForColumn(pipeProxyModel->mapSourceToColumn(IFTREE_COL_PIPE_PATH), new PathSelectionDelegate()); + connect(ui->pipeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [=](const QItemSelection &sel, const QItemSelection &) { + ui->delPipe->setEnabled(sel.count() > 0); + }); + +#if defined(HAVE_PCAP_REMOTE) + // The default indentation (20) means our checkboxes are shifted too far on Windows. + // Assume that our disclosure and checkbox controls are square, or at least fit within an em. + int one_em = fontMetrics().height(); + ui->remoteList->setIndentation(one_em); + ui->remoteList->setColumnWidth(col_r_show_, one_em * 4); + ui->remoteSettings->setEnabled(false); + showRemoteInterfaces(); +#else + ui->tabWidget->removeTab(tab_remote_); +#endif + + connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(updateWidgets())); + connect(this, SIGNAL(ifsChanged()), parent, SIGNAL(ifsChanged())); + +#ifdef HAVE_PCAP_REMOTE + connect(this, SIGNAL(remoteAdded(GList*, remote_options*)), this, SLOT(addRemoteInterfaces(GList*, remote_options*))); + connect(this, SIGNAL(remoteSettingsChanged(interface_t *)), this, SLOT(setRemoteSettings(interface_t *))); + connect(ui->remoteList, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(remoteSelectionChanged(QTreeWidgetItem*, int))); + recent_remote_host_list_foreach(populateExistingRemotes, this); +#endif + + ui->tabWidget->setCurrentIndex(tab_local_); + updateWidgets(); +} + +ManageInterfacesDialog::~ManageInterfacesDialog() +{ + if (result() == QDialog::Accepted) { +#ifdef HAVE_LIBPCAP + sourceModel->save(); +#endif +#ifdef HAVE_PCAP_REMOTE + remoteAccepted(); +#endif + prefs_main_write(); + mainApp->refreshLocalInterfaces(); + emit ifsChanged(); + } + + delete ui; +} + +void ManageInterfacesDialog::updateWidgets() +{ + QString hint; + +#ifdef HAVE_PCAP_REMOTE + bool enable_del_remote = false; + bool enable_remote_settings = false; + QTreeWidgetItem *item = ui->remoteList->currentItem(); + + if (item) { + if (item->childCount() < 1) { // Leaf + enable_remote_settings = true; + } else { + enable_del_remote = true; + } + } + ui->delRemote->setEnabled(enable_del_remote); + ui->remoteSettings->setEnabled(enable_remote_settings); +#endif + + switch (ui->tabWidget->currentIndex()) { + case tab_pipe_: + hint = tr("This version of Wireshark does not save pipe settings."); + break; + case tab_remote_: +#ifdef HAVE_PCAP_REMOTE + hint = tr("This version of Wireshark does not save remote settings."); +#else + hint = tr("This version of Wireshark does not support remote interfaces."); +#endif + break; + default: + break; + } + + hint.prepend(""); + hint.append(""); + ui->hintLabel->setText(hint); +} + +#ifdef HAVE_LIBPCAP +void ManageInterfacesDialog::on_addPipe_clicked() +{ + interface_t device; + + memset(&device, 0, sizeof(device)); + device.name = qstring_strdup(tr("New Pipe")); + device.display_name = g_strdup(device.name); + device.hidden = FALSE; + device.selected = TRUE; + device.pmode = global_capture_opts.default_options.promisc_mode; + device.has_snaplen = global_capture_opts.default_options.has_snaplen; + device.snaplen = global_capture_opts.default_options.snaplen; + device.cfilter = g_strdup(global_capture_opts.default_options.cfilter); + device.timestamp_type = g_strdup(global_capture_opts.default_options.timestamp_type); +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + device.buffer = DEFAULT_CAPTURE_BUFFER_SIZE; +#endif + device.active_dlt = -1; + device.if_info.name = g_strdup(device.name); + device.if_info.type = IF_PIPE; + + sourceModel->addDevice(&device); + + updateWidgets(); +} + +void ManageInterfacesDialog::on_delPipe_clicked() +{ + /* Get correct selection and tell the source model to delete the itm. pipe view only + * displays IF_PIPE devices, therefore this will only delete pipes, and this is set + * to only select single items. */ + QModelIndex selIndex = ui->pipeView->selectionModel()->selectedIndexes().at(0); + + sourceModel->deleteDevice(pipeProxyModel->mapToSource(selIndex)); + updateWidgets(); +} +#endif + +void ManageInterfacesDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_CAPTURE_MANAGE_INTERFACES_DIALOG); +} + +#ifdef HAVE_PCAP_REMOTE +void ManageInterfacesDialog::remoteSelectionChanged(QTreeWidgetItem*, int) +{ + updateWidgets(); +} + +void ManageInterfacesDialog::updateRemoteInterfaceList(GList* rlist, remote_options* roptions) +{ + GList *if_entry, *lt_entry; + if_info_t *if_info; + char *if_string = NULL; + gchar *descr, *auth_str; + if_capabilities_t *caps; + gint linktype_count; + bool monitor_mode, found = false; + GSList *curr_addr; + int ips = 0; + guint i; + if_addr_t *addr; + data_link_info_t *data_link_info; + GString *ip_str; + link_row *linkr = NULL; + interface_t device; + guint num_interfaces; + + num_interfaces = global_capture_opts.all_ifaces->len; + for (if_entry = g_list_first(rlist); if_entry != NULL; if_entry = gxx_list_next(if_entry)) { + auth_str = NULL; + if_info = gxx_list_data(if_info_t *, if_entry); +#if 0 + add_interface_to_remote_list(if_info); +#endif + for (i = 0; i < num_interfaces; i++) { + device = g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (device.hidden) + continue; + if (strcmp(device.name, if_info->name) == 0) { + found = TRUE; + break; + } + } + if (found) { + found = FALSE; + continue; + } + ip_str = g_string_new(""); + ips = 0; + memset(&device, 0, sizeof(device)); + device.name = g_strdup(if_info->name); + device.if_info.name = g_strdup("Don't crash on bug 13448"); + /* Is this interface hidden and, if so, should we include it + anyway? */ + descr = capture_dev_user_descr_find(if_info->name); + if (descr != NULL) { + /* Yes, we have a user-supplied description; use it. */ + if_string = ws_strdup_printf("%s: %s", descr, if_info->name); + g_free(descr); + } else { + /* No, we don't have a user-supplied description; did we get + one from the OS or libpcap? */ + if (if_info->vendor_description != NULL) { + /* Yes - use it. */ + if_string = ws_strdup_printf("%s: %s", if_info->vendor_description, if_info->name); + } else { + /* No. */ + if_string = g_strdup(if_info->name); + } + } /* else descr != NULL */ + if (if_info->loopback) { + device.display_name = ws_strdup_printf("%s (loopback)", if_string); + } else { + device.display_name = g_strdup(if_string); + } +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + if ((device.buffer = capture_dev_user_buffersize_find(if_string)) == -1) { + device.buffer = global_capture_opts.default_options.buffer_size; + } +#endif + if (!capture_dev_user_pmode_find(if_string, &device.pmode)) { + device.pmode = global_capture_opts.default_options.promisc_mode; + } + if (!capture_dev_user_snaplen_find(if_string, &device.has_snaplen, + &device.snaplen)) { + device.has_snaplen = global_capture_opts.default_options.has_snaplen; + device.snaplen = global_capture_opts.default_options.snaplen; + } + device.cfilter = g_strdup(global_capture_opts.default_options.cfilter); + device.timestamp_type = g_strdup(global_capture_opts.default_options.timestamp_type); + monitor_mode = prefs_capture_device_monitor_mode(if_string); + if (roptions->remote_host_opts.auth_type == CAPTURE_AUTH_PWD) { + auth_str = ws_strdup_printf("%s:%s", roptions->remote_host_opts.auth_username, + roptions->remote_host_opts.auth_password); + } + caps = capture_get_if_capabilities(if_string, monitor_mode, auth_str, NULL, NULL, main_window_update); + g_free(auth_str); + for (; (curr_addr = g_slist_nth(if_info->addrs, ips)) != NULL; ips++) { + address addr_str; + char* temp_addr_str = NULL; + if (ips != 0) { + g_string_append(ip_str, "\n"); + } + addr = (if_addr_t *)curr_addr->data; + switch (addr->ifat_type) { + case IF_AT_IPv4: + set_address(&addr_str, AT_IPv4, 4, &addr->addr.ip4_addr); + temp_addr_str = (char*)address_to_str(NULL, &addr_str); + g_string_append(ip_str, temp_addr_str); + break; + case IF_AT_IPv6: + set_address(&addr_str, AT_IPv6, 16, addr->addr.ip6_addr); + temp_addr_str = (char*)address_to_str(NULL, &addr_str); + g_string_append(ip_str, temp_addr_str); + break; + default: + /* In case we add non-IP addresses */ + break; + } + wmem_free(NULL, temp_addr_str); + } /* for curr_addr */ + linktype_count = 0; + device.links = NULL; + if (caps != NULL) { +#ifdef HAVE_PCAP_CREATE + device.monitor_mode_enabled = monitor_mode; + device.monitor_mode_supported = caps->can_set_rfmon; +#endif + for (lt_entry = caps->data_link_types; lt_entry != NULL; lt_entry = gxx_list_next(lt_entry)) { + data_link_info = gxx_list_data(data_link_info_t *, lt_entry); + linkr = new link_row; + /* + * For link-layer types libpcap/WinPcap/Npcap doesn't know + * about, the name will be "DLT n", and the description will + * be null. + * We mark those as unsupported, and don't allow them to be + * used. + */ + if (data_link_info->description != NULL) { + linkr->name = g_strdup(data_link_info->description); + linkr->dlt = data_link_info->dlt; + } else { + linkr->name = ws_strdup_printf("%s (not supported)", data_link_info->name); + linkr->dlt = -1; + } + if (linktype_count == 0) { + device.active_dlt = data_link_info->dlt; + } + device.links = g_list_append(device.links, linkr); + linktype_count++; + } /* for link_types */ + } else { +#if defined(HAVE_PCAP_CREATE) + device.monitor_mode_enabled = FALSE; + device.monitor_mode_supported = FALSE; +#endif + device.active_dlt = -1; + } + device.addresses = g_strdup(ip_str->str); + device.no_addresses = ips; + device.remote_opts.src_type= roptions->src_type; + if (device.remote_opts.src_type == CAPTURE_IFREMOTE) { + device.local = FALSE; + } + device.remote_opts.remote_host_opts.remote_host = g_strdup(roptions->remote_host_opts.remote_host); + device.remote_opts.remote_host_opts.remote_port = g_strdup(roptions->remote_host_opts.remote_port); + device.remote_opts.remote_host_opts.auth_type = roptions->remote_host_opts.auth_type; + device.remote_opts.remote_host_opts.auth_username = g_strdup(roptions->remote_host_opts.auth_username); + device.remote_opts.remote_host_opts.auth_password = g_strdup(roptions->remote_host_opts.auth_password); + device.remote_opts.remote_host_opts.datatx_udp = roptions->remote_host_opts.datatx_udp; + device.remote_opts.remote_host_opts.nocap_rpcap = roptions->remote_host_opts.nocap_rpcap; + device.remote_opts.remote_host_opts.nocap_local = roptions->remote_host_opts.nocap_local; +#ifdef HAVE_PCAP_SETSAMPLING + device.remote_opts.sampling_method = roptions->sampling_method; + device.remote_opts.sampling_param = roptions->sampling_param; +#endif + device.selected = TRUE; + global_capture_opts.num_selected++; + g_array_append_val(global_capture_opts.all_ifaces, device); + g_string_free(ip_str, TRUE); + } /*for*/ +} + +void ManageInterfacesDialog::addRemoteInterfaces(GList* rlist, remote_options *roptions) +{ + updateRemoteInterfaceList(rlist, roptions); + showRemoteInterfaces(); +} + +// We don't actually store these. When we do we should make sure they're stored +// securely using CryptProtectData, the macOS Keychain, GNOME Keyring, KWallet, etc. +void ManageInterfacesDialog::remoteAccepted() +{ + QTreeWidgetItemIterator it(ui->remoteList); + + while (*it) { + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if ((*it)->text(col_r_host_dev_).compare(device->name)) + continue; + device->hidden = ((*it)->checkState(col_r_show_) == Qt::Checked ? false : true); + } + ++it; + } +} + +void ManageInterfacesDialog::on_remoteList_currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *) +{ + updateWidgets(); +} + +void ManageInterfacesDialog::on_remoteList_itemClicked(QTreeWidgetItem *item, int column) +{ + if (!item || column != col_r_show_) { + return; + } + + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (!device->local) { + if (item->text(col_r_host_dev_).compare(device->name)) + continue; + device->hidden = (item->checkState(col_r_show_) == Qt::Checked ? false : true); + } + } +} + +void ManageInterfacesDialog::on_delRemote_clicked() +{ + QTreeWidgetItem* item = ui->remoteList->currentItem(); + if (!item) { + return; + } + + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (item->text(col_r_host_dev_).compare(device->remote_opts.remote_host_opts.remote_host)) + continue; + capture_opts_free_interface_t(device); + global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, i); + } + delete item; + fflush(stdout); // ??? +} + +void ManageInterfacesDialog::on_addRemote_clicked() +{ + RemoteCaptureDialog *dlg = new RemoteCaptureDialog(this); + dlg->show(); +} + +void ManageInterfacesDialog::showRemoteInterfaces() +{ + guint i; + interface_t *device; + QTreeWidgetItem * item = nullptr; + + // We assume that remote interfaces are grouped by host. + for (i = 0; i < global_capture_opts.all_ifaces->len; i++) { + QTreeWidgetItem * child = nullptr; + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (!device->local) { + + // check if the QTreeWidgetItem for that interface already exists + QList items = ui->remoteList->findItems(QString(device->name), Qt::MatchCaseSensitive | Qt::MatchFixedString, col_r_host_dev_); + if (items.count() > 0) + continue; + + // create or find the QTreeWidgetItem for the remote host configuration + QString parentName = QString(device->remote_opts.remote_host_opts.remote_host); + items = ui->remoteList->findItems(parentName, Qt::MatchCaseSensitive | Qt::MatchFixedString, col_r_host_dev_); + if (items.count() == 0) { + item = new QTreeWidgetItem(ui->remoteList); + item->setText(col_r_host_dev_, parentName); + item->setExpanded(true); + } + else { + item = items.at(0); + } + + items = ui->remoteList->findItems(QString(device->name), Qt::MatchCaseSensitive | Qt::MatchFixedString | Qt::MatchRecursive, col_r_host_dev_); + if (items.count() == 0) + { + child = new QTreeWidgetItem(item); + child->setCheckState(col_r_show_, device->hidden ? Qt::Unchecked : Qt::Checked); + child->setText(col_r_host_dev_, QString(device->name)); + } + } + } +} + +void ManageInterfacesDialog::on_remoteSettings_clicked() +{ + guint i = 0; + interface_t *device; + QTreeWidgetItem* item = ui->remoteList->currentItem(); + if (!item) { + return; + } + + for (i = 0; i < global_capture_opts.all_ifaces->len; i++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (!device->local) { + if (item->text(col_r_host_dev_).compare(device->name)) { + continue; + } else { + RemoteSettingsDialog *dlg = new RemoteSettingsDialog(this, device); + dlg->show(); + break; + } + } + } +} + +void ManageInterfacesDialog::setRemoteSettings(interface_t *iface) +{ + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (!device->local) { + if (strcmp(iface->name, device->name)) { + continue; + } + device->remote_opts.remote_host_opts.nocap_rpcap = iface->remote_opts.remote_host_opts.nocap_rpcap; + device->remote_opts.remote_host_opts.datatx_udp = iface->remote_opts.remote_host_opts.datatx_udp; +#ifdef HAVE_PCAP_SETSAMPLING + device->remote_opts.sampling_method = iface->remote_opts.sampling_method; + device->remote_opts.sampling_param = iface->remote_opts.sampling_param; +#endif //HAVE_PCAP_SETSAMPLING + } + } +} +#endif // HAVE_PCAP_REMOTE diff --git a/ui/qt/manage_interfaces_dialog.h b/ui/qt/manage_interfaces_dialog.h new file mode 100644 index 00000000..79e9d0d9 --- /dev/null +++ b/ui/qt/manage_interfaces_dialog.h @@ -0,0 +1,82 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MANAGE_INTERFACES_DIALOG_H +#define MANAGE_INTERFACES_DIALOG_H + +#include + +#include +#include "capture_opts.h" + +#include +#include + +#include "geometry_state_dialog.h" +#include + +class QTreeWidget; +class QTreeWidgetItem; +class QStandardItemModel; + +class QLineEdit; + + +namespace Ui { +class ManageInterfacesDialog; +} + +class ManageInterfacesDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit ManageInterfacesDialog(QWidget *parent = 0); + ~ManageInterfacesDialog(); + +private: + Ui::ManageInterfacesDialog *ui; + + InterfaceTreeCacheModel * sourceModel; + InterfaceSortFilterModel * proxyModel; + InterfaceSortFilterModel * pipeProxyModel; + + void showRemoteInterfaces(); + +signals: + void ifsChanged(); +#ifdef HAVE_PCAP_REMOTE + void remoteAdded(GList *rlist, remote_options *roptions); + void remoteSettingsChanged(interface_t *iface); +#endif + +private slots: + void updateWidgets(); + +#ifdef HAVE_LIBPCAP + void on_addPipe_clicked(); + void on_delPipe_clicked(); +#endif + +#ifdef HAVE_PCAP_REMOTE + void on_addRemote_clicked(); + void on_delRemote_clicked(); + void remoteAccepted(); + void on_remoteList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); + void on_remoteList_itemClicked(QTreeWidgetItem *item, int column); + void addRemoteInterfaces(GList *rlist, remote_options *roptions); + void updateRemoteInterfaceList(GList *rlist, remote_options *roptions); + void setRemoteSettings(interface_t *iface); + void remoteSelectionChanged(QTreeWidgetItem* item, int col); + void on_remoteSettings_clicked(); +#endif + void on_buttonBox_helpRequested(); +}; + +#endif // MANAGE_INTERFACES_DIALOG_H diff --git a/ui/qt/manage_interfaces_dialog.ui b/ui/qt/manage_interfaces_dialog.ui new file mode 100644 index 00000000..fa93023c --- /dev/null +++ b/ui/qt/manage_interfaces_dialog.ui @@ -0,0 +1,266 @@ + + + ManageInterfacesDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 750 + 425 + + + + Manage Interfaces + + + false + + + + + + 0 + + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + + + Local Interfaces + + + + + + Qt::NoFocus + + + 0 + + + false + + + true + + + false + + + + + + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + + + Pipes + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAlwaysOff + + + Qt::ElideMiddle + + + false + + + false + + + + + + + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + + + + + + + + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Remote Interfaces + + + + + + true + + + + Show + + + + + Host / Device URL + + + + + + + + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + + + + + + + + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + + + + + + + + + + Qt::Horizontal + + + + 328 + 20 + + + + + + + + Remote Settings + + + + + + + + + + + + + <small><i></i></small> + + + true + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+
+ + + + buttonBox + accepted() + ManageInterfacesDialog + accept() + + + 374 + 404 + + + 374 + 212 + + + + + buttonBox + rejected() + ManageInterfacesDialog + reject() + + + 374 + 404 + + + 374 + 212 + + + + +
diff --git a/ui/qt/manager/preference_manager.cpp b/ui/qt/manager/preference_manager.cpp new file mode 100644 index 00000000..28cfe42a --- /dev/null +++ b/ui/qt/manager/preference_manager.cpp @@ -0,0 +1,70 @@ +/* preference_manager.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include +#include + +#include + +PreferenceFactory::~PreferenceFactory() {} + +QMap & PreferenceManager::factories() +{ + static QMap inst = QMap(); + return inst; +} + +PreferenceManager::PreferenceManager(QObject * parent) + : QObject(parent) +{} + +PreferenceManager::~PreferenceManager() +{ + /* As this is a singleton, this is the point, where we can clear the registry */ + PreferenceManager::factories().clear(); +} + +PreferenceManager * PreferenceManager::instance() +{ + static PreferenceManager* _inst = 0; + if (! _inst) + _inst = new PreferenceManager(); + + return _inst; +} + +void PreferenceManager::registerType(int pref, PreferenceFactory * factory) +{ + Q_ASSERT(pref >= 0); + + if (PreferenceManager::factories().contains(pref) || ! factory) + return; + + PreferenceManager::factories()[pref] = factory; +} + +WiresharkPreference * PreferenceManager::getPreference(PrefsItem * pref) +{ + if (! pref) + return Q_NULLPTR; + + int key = pref->getPrefType(); + if (! PreferenceManager::factories().contains(key)) + return Q_NULLPTR; + + /* All actions are parented with this manager, to clear the objects together with the manager */ +// WiresharkPreference * wspref = qobject_cast(PreferenceManager::factories()[key]->create(this)); + WiresharkPreference * wspref = PreferenceManager::factories()[key]->create(this); + if (wspref) + wspref->setPrefsItem(pref); + + return wspref; +} diff --git a/ui/qt/manager/preference_manager.h b/ui/qt/manager/preference_manager.h new file mode 100644 index 00000000..030e0fae --- /dev/null +++ b/ui/qt/manager/preference_manager.h @@ -0,0 +1,64 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PREFERENCE_MANAGER_H +#define PREFERENCE_MANAGER_H + +#include + +#include +#include +#include +#include + +#include +#include + +class PreferenceFactory; +class WiresharkPreference; + +class PreferenceManager : public QObject +{ +public: + static PreferenceManager* instance(); + virtual ~PreferenceManager(); + + void registerType(int pref, PreferenceFactory * factory); + void reuseType(int pref, int reuseFor); + WiresharkPreference * getPreference(PrefsItem * item); + +protected: + explicit PreferenceManager(QObject * parent = Q_NULLPTR); + +private: + static QMap & factories(); +}; + +class PreferenceFactory : public QObject +{ +public: + virtual ~PreferenceFactory(); + virtual WiresharkPreference * create(QObject * parent = Q_NULLPTR) = 0; +}; + +#define REGISTER_PREFERENCE_TYPE(pref_id, preference_class) \ + class preference_class##pref_id##Factory : public PreferenceFactory { \ + public: \ + preference_class##pref_id##Factory() \ + { \ + PreferenceManager::instance()->registerType(pref_id, this); \ + } \ + virtual WiresharkPreference *create(QObject * parent) { \ + WiresharkPreference * newPrefHandler = new preference_class(parent); \ + return newPrefHandler; \ + } \ + }; \ + static preference_class##pref_id##Factory global_##preference_class##pref_id##Factory; + +#endif // PREFERENCE_MANAGER_H diff --git a/ui/qt/manager/wireshark_preference.cpp b/ui/qt/manager/wireshark_preference.cpp new file mode 100644 index 00000000..cca87c7b --- /dev/null +++ b/ui/qt/manager/wireshark_preference.cpp @@ -0,0 +1,248 @@ +/* wireshark_preference.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include +#include + +#include +#include +#include +#include + +WiresharkPreference::WiresharkPreference(QObject * parent) : QObject(parent), _prefsItem(NULL) +{} + +QWidget * WiresharkPreference::editor(QWidget * /*parent*/, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) +{ + return Q_NULLPTR; +} + +void WiresharkPreference::setData(QWidget * /*editor*/, const QModelIndex &/*index*/) {} +void WiresharkPreference::setModelData(QWidget * /*editor*/, QAbstractItemModel * /*model*/, const QModelIndex &/*index*/) {} + +void WiresharkPreference::setPrefsItem(PrefsItem * item) +{ + _prefsItem = item; +} + +PrefsItem * WiresharkPreference::prefsItem() const +{ + return _prefsItem; +} + +class BoolPreference : public WiresharkPreference +{ +public: + BoolPreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) + { + const_cast(index.model())->setData(index, QString("BOOL"), Qt::EditRole); + return WiresharkPreference::editor(parent, option, index); + } +}; +REGISTER_PREFERENCE_TYPE(PREF_BOOL, BoolPreference) + +class StringPreference : public WiresharkPreference +{ +public: + StringPreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) + { + return new QLineEdit(parent); + } + + virtual void setData(QWidget *editor, const QModelIndex &index) + { + QLineEdit* line = static_cast(editor); + line->setText(index.model()->data(index, Qt::DisplayRole).toString()); + } + + virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) + { + QLineEdit* line = static_cast(editor); + model->setData(index, line->text(), Qt::EditRole); + } +}; +REGISTER_PREFERENCE_TYPE(PREF_STRING, StringPreference) +REGISTER_PREFERENCE_TYPE(PREF_CUSTOM, StringPreference) + +class PasswordPreference : public StringPreference +{ +public: + PasswordPreference(QObject * parent = Q_NULLPTR) : StringPreference(parent) {} + virtual QWidget * editor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) + { + QLineEdit *le = static_cast(StringPreference::editor(parent, option, index)); + + le->setEchoMode(QLineEdit::PasswordEchoOnEdit); + return le; + } +}; +REGISTER_PREFERENCE_TYPE(PREF_PASSWORD, PasswordPreference) + +class UIntPreference : public StringPreference +{ +public: + UIntPreference(QObject * parent = Q_NULLPTR) : StringPreference(parent) {} +}; +REGISTER_PREFERENCE_TYPE(PREF_UINT, UIntPreference) +REGISTER_PREFERENCE_TYPE(PREF_DECODE_AS_UINT, UIntPreference) + +class EnumPreference : public WiresharkPreference +{ +public: + EnumPreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) + { + return new QComboBox(parent); + } + + virtual void setData(QWidget *editor, const QModelIndex &index) + { + QComboBox* combo = static_cast(editor); + const enum_val_t *ev; + PrefsItem* pref = VariantPointer::asPtr(index.model()->data(index, Qt::UserRole)); + for (ev = prefs_get_enumvals(pref->getPref()); ev && ev->description; ev++) { + combo->addItem(ev->description, QVariant(ev->value)); + if (prefs_get_enum_value(pref->getPref(), pref_stashed) == ev->value) + combo->setCurrentIndex(combo->count() - 1); + } + } + + virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) + { + QComboBox* combo = static_cast(editor); + model->setData(index, combo->itemData(combo->currentIndex()), Qt::EditRole); + } +}; +REGISTER_PREFERENCE_TYPE(PREF_ENUM, EnumPreference) + +class RangePreference : public WiresharkPreference +{ +public: + RangePreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) + { + return new RangeSyntaxLineEdit(parent); + } + + virtual void setData(QWidget *editor, const QModelIndex &index) + { + RangeSyntaxLineEdit* syntax = static_cast(editor); + syntax->setText(index.model()->data(index, Qt::DisplayRole).toString()); + } + + virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) + { + RangeSyntaxLineEdit* syntax = static_cast(editor); + model->setData(index, syntax->text(), Qt::EditRole); + } +}; +REGISTER_PREFERENCE_TYPE(PREF_RANGE, RangePreference) +REGISTER_PREFERENCE_TYPE(PREF_DECODE_AS_RANGE, RangePreference) + +class ColorPreference : public WiresharkPreference +{ +public: + ColorPreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget * parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) + { + QColorDialog* color_dlg = new QColorDialog(parent); + color_dlg->setWindowModality(Qt::ApplicationModal); + color_dlg->show(); + return color_dlg; + } + + virtual void setData(QWidget *editor, const QModelIndex &index) + { + QColorDialog* color_dlg = static_cast(editor); + QColor color = QColor("#" + index.model()->data(index, Qt::DisplayRole).toString()); + color_dlg->setCurrentColor(color); + } + + virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) + { + QColorDialog* color_dlg = static_cast(editor); + if (color_dlg->result() == QDialog::Accepted) { + model->setData(index, color_dlg->currentColor().name(), Qt::EditRole); + } + } +}; +REGISTER_PREFERENCE_TYPE(PREF_COLOR, ColorPreference) + +class SaveFilePreference : public WiresharkPreference +{ +public: + SaveFilePreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index) + { + QString filename = WiresharkFileDialog::getSaveFileName(parent, mainApp->windowTitleString(prefs_get_title(prefsItem()->getPref())), + index.model()->data(index, Qt::DisplayRole).toString()); + if (!filename.isEmpty()) { + const_cast(index.model())->setData(index, QDir::toNativeSeparators(filename), Qt::EditRole); + } + return WiresharkPreference::editor(parent, option, index); + } +}; +REGISTER_PREFERENCE_TYPE(PREF_SAVE_FILENAME, SaveFilePreference) + +class OpenFilePreference : public WiresharkPreference +{ +public: + OpenFilePreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index) + { + QString filename = WiresharkFileDialog::getOpenFileName(parent, mainApp->windowTitleString(prefs_get_title(prefsItem()->getPref())), + index.model()->data(index, Qt::DisplayRole).toString()); + if (!filename.isEmpty()) { + const_cast(index.model())->setData(index, QDir::toNativeSeparators(filename), Qt::EditRole); + } + return WiresharkPreference::editor(parent, option, index); + } +}; +REGISTER_PREFERENCE_TYPE(PREF_OPEN_FILENAME, OpenFilePreference) + +class DirNamePreference : public WiresharkPreference +{ +public: + DirNamePreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index) + { + QString filename = WiresharkFileDialog::getExistingDirectory(parent, mainApp->windowTitleString(prefs_get_title(prefsItem()->getPref())), + index.model()->data(index, Qt::DisplayRole).toString()); + if (!filename.isEmpty()) { + const_cast(index.model())->setData(index, QDir::toNativeSeparators(filename), Qt::EditRole); + } + return WiresharkPreference::editor(parent, option, index); + } +}; +REGISTER_PREFERENCE_TYPE(PREF_DIRNAME, DirNamePreference) + +class UatPreference : public WiresharkPreference +{ +public: + UatPreference(QObject * parent = Q_NULLPTR) : WiresharkPreference(parent) {} + virtual QWidget * editor(QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index) + { + UatDialog uat_dlg(parent, prefs_get_uat_value(prefsItem()->getPref())); + uat_dlg.exec(); + + return WiresharkPreference::editor(parent, option, index); + } +}; +REGISTER_PREFERENCE_TYPE(PREF_UAT, UatPreference) diff --git a/ui/qt/manager/wireshark_preference.h b/ui/qt/manager/wireshark_preference.h new file mode 100644 index 00000000..a71939d1 --- /dev/null +++ b/ui/qt/manager/wireshark_preference.h @@ -0,0 +1,38 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WIRESHARK_PREFERENCE_H +#define WIRESHARK_PREFERENCE_H + +#include + +#include +#include +#include + +class WiresharkPreference : public QObject +{ +public: + explicit Q_INVOKABLE WiresharkPreference(QObject * parent = Q_NULLPTR); + + virtual QWidget * editor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index); + virtual void setData(QWidget *editor, const QModelIndex &index); + virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index); + + void setPrefsItem(PrefsItem *); + +protected: + PrefsItem * prefsItem() const; + +private: + PrefsItem * _prefsItem; + +}; + +#endif // WIRESHARK_PREFERENCE_H diff --git a/ui/qt/manuf_dialog.cpp b/ui/qt/manuf_dialog.cpp new file mode 100644 index 00000000..0c383af3 --- /dev/null +++ b/ui/qt/manuf_dialog.cpp @@ -0,0 +1,210 @@ +/* + * manuf_dialog.c + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "manuf_dialog.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main_application.h" +#include +#include +#include +#include + +#define PLACEHOLDER_SEARCH_ADDR "Search address" +#define PLACEHOLDER_SEARCH_NAME "Search name" + +ManufDialog::ManufDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::ManufDialog) +{ + ui->setupUi(this); + loadGeometry(); + + model_ = new ManufTableModel(this); + proxy_model_ = new ManufSortFilterProxyModel(this); + proxy_model_->setSourceModel(model_); + + ui->manufTableView->setModel(proxy_model_); + ui->manufTableView->setContextMenuPolicy(Qt::ActionsContextMenu); + ui->manufTableView->setColumnHidden(ManufTableModel::COL_SHORT_NAME, true); + + QAction *select_action = new QAction(tr("Select all"), ui->manufTableView); + ui->manufTableView->addAction(select_action); + connect(select_action, &QAction::triggered, ui->manufTableView, &QTableView::selectAll); + + QAction *copy_action = new QAction(tr("Copy"), ui->manufTableView); + ui->manufTableView->addAction(copy_action); + connect(copy_action, &QAction::triggered, this, &ManufDialog::copyToClipboard); + + QPushButton *find_button = ui->buttonBox->addButton(tr("Find"), QDialogButtonBox::ActionRole); + find_button->setDefault(true); + connect(find_button, &QPushButton::clicked, this, &ManufDialog::on_editingFinished); + + QPushButton *clear_button = ui->buttonBox->addButton(tr("Clear"), QDialogButtonBox::ActionRole); + connect(clear_button, &QPushButton::clicked, this, &ManufDialog::clearFilter); + + QPushButton *copy_button = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ApplyRole); + connect(copy_button, &QPushButton::clicked, this, &ManufDialog::copyToClipboard); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + connect(ui->radioButtonGroup, &QButtonGroup::buttonClicked, this, &ManufDialog::on_searchToggled); + connect(ui->radioButtonGroup, &QButtonGroup::buttonClicked, this, &ManufDialog::on_editingFinished); +#else + connect(ui->radioButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ManufDialog::on_searchToggled); + connect(ui->radioButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ManufDialog::on_editingFinished); +#endif + connect(ui->checkShortNameButton, &QCheckBox::stateChanged, this, &ManufDialog::on_shortNameStateChanged); + + ui->manufLineEdit->setPlaceholderText(tr(PLACEHOLDER_SEARCH_ADDR)); + + ui->hintLabel->clear(); +} + +ManufDialog::~ManufDialog() +{ + delete ui; +} + +void ManufDialog::searchVendor(QString &text) +{ + QRegularExpression name_re; + + name_re = QRegularExpression(text, QRegularExpression::CaseInsensitiveOption); + if (!name_re.isValid()) { + ui->hintLabel->setText(QString("Invalid regular expression: %1").arg(name_re.errorString())); + return; + } + + proxy_model_->setFilterName(name_re); + ui->hintLabel->setText(QString("Found %1 matches for \"%2\"").arg(proxy_model_->rowCount()).arg(text)); +} + +static QByteArray convertMacAddressToByteArray(const QString &bytesString) +{ + GByteArray *bytes = g_byte_array_new(); + + if (!hex_str_to_bytes(qUtf8Printable(bytesString), bytes, FALSE) + || bytes->len == 0 || bytes->len > 6) { + g_byte_array_free(bytes, TRUE); + return QByteArray(); + } + + /* Mask out multicast/locally administered flags. */ + bytes->data[0] &= 0xFC; + + return gbytearray_free_to_qbytearray(bytes); +} + +QString convertToMacAddress(const QByteArray& byteArray) { + QString macAddress; + for (int i = 0; i < byteArray.size(); ++i) { + macAddress += QString("%1").arg(static_cast(byteArray[i]), 2, 16, QChar('0')); + if (i != byteArray.size() - 1) { + macAddress += ":"; + } + } + return macAddress.toUpper(); +} + +void ManufDialog::searchPrefix(QString &text) +{ + QByteArray addr; + + addr = convertMacAddressToByteArray(text); + if (addr.isEmpty()) { + ui->hintLabel->setText(QString("\"%1\" is not a valid MAC address").arg(text)); + return; + } + + proxy_model_->setFilterAddress(addr); + ui->hintLabel->setText(QString("Found %1 matches for \"%2\"").arg(proxy_model_->rowCount()).arg(convertToMacAddress(addr))); +} + +void ManufDialog::on_searchToggled(void) +{ + if (ui->ouiRadioButton->isChecked()) + ui->manufLineEdit->setPlaceholderText(tr(PLACEHOLDER_SEARCH_ADDR)); + else if (ui->vendorRadioButton->isChecked()) + ui->manufLineEdit->setPlaceholderText(tr(PLACEHOLDER_SEARCH_NAME)); + else + ws_assert_not_reached(); +} + +void ManufDialog::on_editingFinished(void) +{ + QString text = ui->manufLineEdit->text(); + + if (text.isEmpty()) + return; + + if (ui->ouiRadioButton->isChecked()) + searchPrefix(text); + else if (ui->vendorRadioButton->isChecked()) + searchVendor(text); + else + ws_assert_not_reached(); +} + +void ManufDialog::on_shortNameStateChanged(int state) +{ + ui->manufTableView->setColumnHidden(ManufTableModel::COL_SHORT_NAME, state ? false : true); +} + +void ManufDialog::clearFilter() +{ + proxy_model_->clearFilter(); + ui->manufLineEdit->clear(); + ui->hintLabel->clear(); +} + +void ManufDialog::copyToClipboard() { + QModelIndexList selectedIndexes = ui->manufTableView->selectionModel()->selectedIndexes(); + + std::sort(selectedIndexes.begin(), selectedIndexes.end(), [](const QModelIndex &a, const QModelIndex &b) { + return a.row() < b.row() || (a.row() == b.row() && a.column() < b.column()); + }); + + QAbstractItemModel *model = ui->manufTableView->model(); + QString copiedData; + + int previousRow = -1; + + for (const QModelIndex& selectedIndex : selectedIndexes) { + // If the row changed, add a newline character + if (selectedIndex.row() != previousRow) { + if (!copiedData.isEmpty()) { + copiedData += "\n"; + } + previousRow = selectedIndex.row(); + } + else { + // If not the first column in the row, add a tab character + copiedData += "\t"; + } + + // Add the cell data to the string + copiedData += model->data(selectedIndex).toString(); + } + + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(copiedData); +} diff --git a/ui/qt/manuf_dialog.h b/ui/qt/manuf_dialog.h new file mode 100644 index 00000000..fa3b1caf --- /dev/null +++ b/ui/qt/manuf_dialog.h @@ -0,0 +1,44 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MANUF_DIALOG_H +#define MANUF_DIALOG_H + +#include +#include + +namespace Ui { +class ManufDialog; +} + +class ManufDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit ManufDialog(QWidget &parent, CaptureFile &cf); + ~ManufDialog(); + +private slots: + void on_searchToggled(void); + void on_editingFinished(void); + void on_shortNameStateChanged(int state); + void copyToClipboard(void); + void clearFilter(void); + +private: + void searchPrefix(QString &text); + void searchVendor(QString &text); + + Ui::ManufDialog *ui; + ManufTableModel *model_; + ManufSortFilterProxyModel *proxy_model_; +}; + +#endif // MANUF_DIALOG_H diff --git a/ui/qt/manuf_dialog.ui b/ui/qt/manuf_dialog.ui new file mode 100644 index 00000000..49138c5d --- /dev/null +++ b/ui/qt/manuf_dialog.ui @@ -0,0 +1,146 @@ + + + ManufDialog + + + + 0 + 0 + 503 + 394 + + + + MAC Address Blocks + + + + + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + MAC Address + + + true + + + radioButtonGroup + + + + + + + Search vendor name using a case-insentitive regular expression. + + + Vendor Name + + + radioButtonGroup + + + + + + + Show short name column. + + + Short name + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + + + + 140 + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + ManufDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ManufDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + + + diff --git a/ui/qt/models/astringlist_list_model.cpp b/ui/qt/models/astringlist_list_model.cpp new file mode 100644 index 00000000..a0b5fc38 --- /dev/null +++ b/ui/qt/models/astringlist_list_model.cpp @@ -0,0 +1,305 @@ +/* astringlist_list_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include + +#include + +#include + +AStringListListModel::AStringListListModel(QObject * parent): +QAbstractTableModel(parent) +{} + +AStringListListModel::~AStringListListModel() { display_data_.clear(); } + +void AStringListListModel::appendRow(const QStringList & display_strings, const QString & row_tooltip, const QModelIndex &parent) +{ + QStringList columns = headerColumns(); + if (display_strings.count() != columns.count()) + return; + + emit beginInsertRows(parent, rowCount(), rowCount()); + display_data_ << display_strings; + tooltip_data_ << row_tooltip; + emit endInsertRows(); +} + +int AStringListListModel::rowCount(const QModelIndex &) const +{ + return static_cast(display_data_.count()); +} + +int AStringListListModel::columnCount(const QModelIndex &parent) const +{ + if (rowCount(parent) == 0) + return 0; + + return static_cast(headerColumns().count()); +} + +QVariant AStringListListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) + return QVariant(); + + QStringList columns = headerColumns(); + if (role == Qt::DisplayRole && section < columns.count()) + return QVariant::fromValue(columns[section]); + + return QVariant(); +} + +QVariant AStringListListModel::data(const QModelIndex &index, int role) const +{ + if (! index.isValid() || index.row() >= rowCount()) + return QVariant(); + + if (role == Qt::DisplayRole) + { + QStringList data = display_data_.at(index.row()); + + if (index.column() < columnCount()) + return QVariant::fromValue(data.at(index.column())); + } + else if (role == Qt::ToolTipRole) + { + QString tooltip = tooltip_data_.at(index.row()); + if (!tooltip.isEmpty()) { + return tooltip; + } + } + + return QVariant(); +} + +AStringListListSortFilterProxyModel::AStringListListSortFilterProxyModel(QObject * parent) +: QSortFilterProxyModel(parent) +{ + filter_ = QString(); + types_[-1] = FilterByContains; +} + +bool AStringListListSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + QString leftData = left.data().toString(); + QString rightData = right.data().toString(); + + if (numericColumns_.contains(left.column()) || numericColumns_.contains(right.column()) ) + { + float leftD = leftData.toFloat(); + float rightD = rightData.toFloat(); + + return leftD < rightD; + } + + return leftData.compare(rightData, sortCaseSensitivity()) < 0; +} + +void AStringListListSortFilterProxyModel::setFilter(const QString & filter) +{ + filter_ = filter; + invalidateFilter(); +} + +static bool AContainsB(const QVariant &a, const QVariant &b, Qt::CaseSensitivity cs) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (! a.canConvert() || ! b.canConvert()) +#else + if (! a.canConvert(QVariant::String) || ! b.canConvert(QVariant::String)) +#endif + return false; + return a.toString().contains(b.toString(), cs); +} + +static bool AStartsWithB(const QVariant &a, const QVariant &b, Qt::CaseSensitivity cs) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (! a.canConvert() || ! b.canConvert()) +#else + if (! a.canConvert(QVariant::String) || ! b.canConvert(QVariant::String)) +#endif + return false; + return a.toString().startsWith(b.toString(), cs); +} + +static bool AIsEquivalentToB(const QVariant &a, const QVariant &b, Qt::CaseSensitivity) +{ + return a == b; +} + +bool AStringListListSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + if (columnsToFilter_.count() == 0) + return true; + + foreach(int column, columnsToFilter_) + { + if (column >= columnCount()) + continue; + + QModelIndex chkIdx = sourceModel()->index(sourceRow, column, sourceParent); + QString dataString = chkIdx.data().toString(); + + /* Default is filter by string a contains string b */ + bool (*compareFunc)(const QVariant&, const QVariant&, Qt::CaseSensitivity) = AContainsB; + if (types_.keys().contains(column)) + { + switch (types_.value(column, FilterByContains)) + { + case FilterByStart: + compareFunc = AStartsWithB; + break; + case FilterByEquivalent: + compareFunc = AIsEquivalentToB; + break; + case FilterNone: + return true; + break; + default: + compareFunc = AContainsB; + break; + } + } + + if (compareFunc(dataString, filter_, filterCaseSensitivity())) + return true; + } + + return false; +} + +void AStringListListSortFilterProxyModel::setFilterType(AStringListListFilterType type, int column) +{ + if (column >= -1 && column < columnCount()) + { + if (! types_.keys().contains(column)) + { + types_.insert(column, type); + invalidateFilter(); + } + else if (types_.keys().contains(column) && type != types_[column]) + { + types_[column] = type; + invalidateFilter(); + } + } +} + +void AStringListListSortFilterProxyModel::setColumnToFilter(int column) +{ + if (column < columnCount() && ! columnsToFilter_.contains(column)) + { + columnsToFilter_.append(column); + invalidateFilter(); + } +} + +void AStringListListSortFilterProxyModel::setColumnsToFilter(QList columns) +{ + bool hasBeenAdded = false; + + foreach (int column, columns) { + if (column < columnCount() && ! columnsToFilter_.contains(column)) { + columnsToFilter_.append(column); + hasBeenAdded = true; + } + } + + if (hasBeenAdded) + invalidateFilter(); +} + +void AStringListListSortFilterProxyModel::clearColumnsToFilter() +{ + columnsToFilter_.clear(); + invalidateFilter(); +} + +void AStringListListSortFilterProxyModel::clearHiddenColumns() +{ + hiddenColumns_.clear(); + invalidateFilter(); +} + +void AStringListListSortFilterProxyModel::setColumnToHide(int col) +{ + if (! hiddenColumns_.contains(col) && col > -1 && sourceModel() && sourceModel()->columnCount() > col) + { + hiddenColumns_ << col; + invalidateFilter(); + } +} + +bool AStringListListSortFilterProxyModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const +{ + QModelIndex realIndex = sourceModel()->index(0, sourceColumn, sourceParent); + + if (! realIndex.isValid()) + return false; + + if (hiddenColumns_.contains(sourceColumn)) + return false; + + return true; +} + +void AStringListListSortFilterProxyModel::clearNumericColumns() +{ + numericColumns_.clear(); + invalidateFilter(); +} + +void AStringListListSortFilterProxyModel::setColumnAsNumeric(int col) +{ + if (! numericColumns_.contains(col) && col > -1 && sourceModel() && sourceModel()->columnCount() > col) + { + numericColumns_ << col; + invalidateFilter(); + } +} + +AStringListListUrlProxyModel::AStringListListUrlProxyModel(QObject * parent): + QIdentityProxyModel(parent) +{} + +void AStringListListUrlProxyModel::setUrlColumn(int column) +{ + if (column < columnCount() && ! urls_.contains(column)) + urls_ << column; +} + +bool AStringListListUrlProxyModel::isUrlColumn(int column) const +{ + return urls_.contains(column); +} + +QVariant AStringListListUrlProxyModel::data(const QModelIndex &index, int role) const +{ + QVariant result = QIdentityProxyModel::data(index, role); + + if (role == Qt::ForegroundRole && urls_.contains(index.column()) +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + && result.canConvert()) +#else + && result.canConvert(QVariant::Brush)) +#endif + { + QBrush selected = result.value(); + selected.setColor(ColorUtils::themeLinkBrush().color()); + return selected; + } + + return result; +} diff --git a/ui/qt/models/astringlist_list_model.h b/ui/qt/models/astringlist_list_model.h new file mode 100644 index 00000000..70acc97b --- /dev/null +++ b/ui/qt/models/astringlist_list_model.h @@ -0,0 +1,107 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ASTRINGLIST_LIST_MODEL_H +#define ASTRINGLIST_LIST_MODEL_H + +#include + +#include +#include +#include +#include +#include +#include + +class AStringListListModel : public QAbstractTableModel +{ +public: + explicit AStringListListModel(QObject * parent = Q_NULLPTR); + virtual ~AStringListListModel(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + // + // This is not protected because we may need to invoke it from + // a wmem_map_foreach() callback implemented as an extern "C" + // static member function of a subclass. wmem_map_foreach() is + // passed, as the user data, a pointer to the class instance to + // which we want to append rows. + // + virtual void appendRow(const QStringList &, const QString & row_tooltip = QString(), const QModelIndex &parent = QModelIndex()); + +protected: + virtual QStringList headerColumns() const = 0; + +private: + QList display_data_; + QStringList tooltip_data_; +}; + +class AStringListListSortFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + + enum AStringListListFilterType + { + FilterByContains = 0, + FilterByStart, + FilterByEquivalent, + FilterNone + }; + Q_ENUM(AStringListListFilterType) + + explicit AStringListListSortFilterProxyModel(QObject * parent = Q_NULLPTR); + + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + virtual bool filterAcceptsColumn(int column, const QModelIndex &sourceParent) const; + + void setFilterType(AStringListListFilterType type, int column = -1); + + void setColumnToFilter(int); + void setColumnsToFilter(QList); + void clearColumnsToFilter(); + + void clearHiddenColumns(); + void setColumnToHide(int col); + + void clearNumericColumns(); + void setColumnAsNumeric(int col); + +public slots: + void setFilter(const QString&); + +private: + QString filter_; + QMap types_; + QList columnsToFilter_; + QList hiddenColumns_; + QList numericColumns_; +}; + +class AStringListListUrlProxyModel : public QIdentityProxyModel +{ +public: + explicit AStringListListUrlProxyModel(QObject * parent = Q_NULLPTR); + + void setUrlColumn(int); + bool isUrlColumn(int) const; + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private: + QList urls_; +}; + +#endif // ASTRINGLIST_LIST_MODEL_H diff --git a/ui/qt/models/atap_data_model.cpp b/ui/qt/models/atap_data_model.cpp new file mode 100644 index 00000000..b7ff508d --- /dev/null +++ b/ui/qt/models/atap_data_model.cpp @@ -0,0 +1,850 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static QString formatString(qlonglong value) +{ + return QLocale().formattedDataSize(value, 0, QLocale::DataSizeSIFormat); +} + +ATapDataModel::ATapDataModel(dataModelType type, int protoId, QString filter, QObject *parent): + QAbstractListModel(parent) +{ + hash_.conv_array = nullptr; + hash_.hashtable = nullptr; + hash_.user_data = this; + + storage_ = nullptr; + _resolveNames = false; + _absoluteTime = false; + _nanoseconds = false; + + _protoId = protoId; + _filter = filter; + + _minRelStartTime = 0; + _maxRelStopTime = 0; + + _type = type; + _disableTap = true; + + QString _tap(proto_get_protocol_filter_name(protoId)); +} + +ATapDataModel::~ATapDataModel() +{ + /* Only remove the tap if we come from a enabled model */ + if (!_disableTap) + remove_tap_listener(hash()); + + if (_type == ATapDataModel::DATAMODEL_ENDPOINT) + reset_endpoint_table_data(&hash_); + else if (_type == ATapDataModel::DATAMODEL_CONVERSATION) + reset_conversation_table_data(&hash_); +} + +int ATapDataModel::protoId() const +{ + return _protoId; +} + +QString ATapDataModel::tap() const +{ + return proto_get_protocol_filter_name(_protoId); +} + +#ifdef HAVE_MAXMINDDB +bool ATapDataModel::hasGeoIPData() +{ + bool coordsFound = false; + int row = 0; + int count = rowCount(); + while (!coordsFound && row < count) + { + QModelIndex idx = index(row, 0); + if (_type == ATapDataModel::DATAMODEL_ENDPOINT) + coordsFound = qobject_cast(this)->data(idx, ATapDataModel::GEODATA_AVAILABLE).toBool(); + else if (_type == ATapDataModel::DATAMODEL_CONVERSATION) + coordsFound = qobject_cast(this)->data(idx, ATapDataModel::GEODATA_AVAILABLE).toBool(); + row++; + } + + return coordsFound; +} +#endif + +bool ATapDataModel::enableTap() +{ + /* We can't reenable a tap, so just return */ + if (! _disableTap) + return true; + + _disableTap = false; + + /* The errorString is ignored. If this is not working, there is nothing really the user may do about + * it, so the error is only interesting to the developer.*/ + GString * errorString = register_tap_listener(tap().toUtf8().constData(), hash(), _filter.toUtf8().constData(), + TL_IGNORE_DISPLAY_FILTER, &ATapDataModel::tapReset, conversationPacketHandler(), &ATapDataModel::tapDraw, nullptr); + if (errorString && errorString->len > 0) { + g_string_free(errorString, TRUE); + _disableTap = true; + emit tapListenerChanged(false); + return false; + } + + if (errorString) + g_string_free(errorString, TRUE); + + emit tapListenerChanged(true); + + return true; +} + +void ATapDataModel::disableTap() +{ + /* Only remove the tap if we come from a enabled model */ + if (!_disableTap) + remove_tap_listener(hash()); + _disableTap = true; + emit tapListenerChanged(false); +} + +int ATapDataModel::rowCount(const QModelIndex &parent) const +{ + return (storage_ && !parent.isValid()) ? (int) storage_->len : 0; +} + +void ATapDataModel::tapReset(void *tapdata) { + if (! tapdata) + return; + + conv_hash_t *hash = (conv_hash_t*)tapdata; + ATapDataModel * dataModel = qobject_cast((ATapDataModel *)hash->user_data); + + dataModel->resetData(); +} + +void ATapDataModel::tapDraw(void *tapdata) +{ + if (! tapdata) + return; + + conv_hash_t *hash = (conv_hash_t*)tapdata; + ATapDataModel * dataModel = qobject_cast((ATapDataModel *)hash->user_data); + + dataModel->updateData(hash->conv_array); +} + +conv_hash_t * ATapDataModel::hash() +{ + return &hash_; +} + +register_ct_t * ATapDataModel::registerTable() const +{ + if (_protoId > -1) + return get_conversation_by_proto_id(_protoId); + + return nullptr; +} + +tap_packet_cb ATapDataModel::conversationPacketHandler() +{ + register_ct_t* table = registerTable(); + if (table) { + if (_type == ATapDataModel::DATAMODEL_ENDPOINT) + return get_endpoint_packet_func(table); + else if (_type == ATapDataModel::DATAMODEL_CONVERSATION) + return get_conversation_packet_func(table); + } + + return nullptr; +} + +void ATapDataModel::resetData() +{ + if (_disableTap) + return; + + beginResetModel(); + storage_ = nullptr; + if (_type == ATapDataModel::DATAMODEL_ENDPOINT) + reset_endpoint_table_data(&hash_); + else if (_type == ATapDataModel::DATAMODEL_CONVERSATION) + reset_conversation_table_data(&hash_); + + _minRelStartTime = 0; + _maxRelStopTime = 0; + + endResetModel(); +} + +void ATapDataModel::updateData(GArray * newData) +{ + if (_disableTap) + return; + + beginResetModel(); + storage_ = newData; + endResetModel(); + + if (_type == ATapDataModel::DATAMODEL_CONVERSATION) + ((ConversationDataModel *)(this))->doDataUpdate(); +} + +bool ATapDataModel::resolveNames() const +{ + return _resolveNames; +} + +void ATapDataModel::setResolveNames(bool resolve) +{ + if (_resolveNames == resolve) + return; + + beginResetModel(); + _resolveNames = resolve; + endResetModel(); +} + +bool ATapDataModel::allowsNameResolution() const +{ + if (_protoId < 0) + return false; + + QStringList mac_protos = QStringList() << "bluetooth" << "eth" << "fddi" + << "sll" << "tr" << "wlan"; + QStringList net_protos = QStringList() << "dccp" << "ip" << "ipv6" + << "jxta" << "mptcp" << "ncp" + << "rsvp" << "sctp" << "sll" + << "tcp" << "udp"; + QStringList transport_protos = QStringList() << "dccp" << "mptcp" << "sctp" + << "tcp" << "udp"; + + QString table_proto = proto_get_protocol_filter_name(_protoId); + + if (mac_protos.contains(table_proto) && gbl_resolv_flags.mac_name) + return true; + if (net_protos.contains(table_proto) && gbl_resolv_flags.network_name) + return true; + if (transport_protos.contains(table_proto) && gbl_resolv_flags.transport_name) + return true; + + return false; +} + +void ATapDataModel::useAbsoluteTime(bool absolute) +{ + if (absolute == _absoluteTime) + return; + + beginResetModel(); + _absoluteTime = absolute; + endResetModel(); +} + +void ATapDataModel::useNanosecondTimestamps(bool nanoseconds) +{ + if (_nanoseconds == nanoseconds) + return; + + beginResetModel(); + _nanoseconds = nanoseconds; + endResetModel(); +} + +void ATapDataModel::setFilter(QString filter) +{ + if (_disableTap) + return; + + _filter = filter; + GString * errorString = set_tap_dfilter(&hash_, !_filter.isEmpty() ? _filter.toUtf8().constData() : nullptr); + if (errorString && errorString->len > 0) { + /* If this fails, chances are that the main system failed as well. Silently exiting as the + * user cannot react to it */ + disableTap(); + } + + if (errorString) + g_string_free(errorString, TRUE); +} + +QString ATapDataModel::filter() const +{ + return _filter; +} + +ATapDataModel::dataModelType ATapDataModel::modelType() const +{ + return _type; +} + +bool ATapDataModel::portsAreHidden() const +{ + return (get_conversation_hide_ports(registerTable())); +} + +bool ATapDataModel::showTotalColumn() const +{ + /* Implemented to ensure future changes may be done more easily */ + return _filter.length() > 0; +} + +EndpointDataModel::EndpointDataModel(int protoId, QString filter, QObject *parent) : + ATapDataModel(ATapDataModel::DATAMODEL_ENDPOINT, protoId, filter, parent) +{} + +int EndpointDataModel::columnCount(const QModelIndex &) const +{ +#ifdef HAVE_MAXMINDDB + int proto_ipv4 = proto_get_id_by_filter_name("ip"); + int proto_ipv6 = proto_get_id_by_filter_name("ipv6"); + if (protoId() == proto_ipv4 || protoId() == proto_ipv6) { + return ENDP_NUM_GEO_COLUMNS; + } +#endif + return ENDP_NUM_COLUMNS; +} + +QVariant EndpointDataModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) + return QVariant(); + + if (role == Qt::DisplayRole) { + switch (section) { + case ENDP_COLUMN_ADDR: + return tr("Address"); break; + case ENDP_COLUMN_PORT: + return tr("Port"); break; + case ENDP_COLUMN_PACKETS: + return tr("Packets"); break; + case ENDP_COLUMN_BYTES: + return tr("Bytes"); break; + case ENDP_COLUMN_PACKETS_TOTAL: + return tr("Total Packets"); break; + case ENDP_COLUMN_BYTES_TOTAL: + return tr("Percent Filtered"); break; + case ENDP_COLUMN_PKT_AB: + return tr("Tx Packets"); break; + case ENDP_COLUMN_BYTES_AB: + return tr("Tx Bytes"); break; + case ENDP_COLUMN_PKT_BA: + return tr("Rx Packets"); break; + case ENDP_COLUMN_BYTES_BA: + return tr("Rx Bytes"); break; + case ENDP_COLUMN_GEO_COUNTRY: + return tr("Country"); break; + case ENDP_COLUMN_GEO_CITY: + return tr("City"); break; + case ENDP_COLUMN_GEO_LATITUDE: + return tr("Latitude"); break; + case ENDP_COLUMN_GEO_LONGITUDE: + return tr("Longitude"); break; + case ENDP_COLUMN_GEO_AS_NUM: + return tr("AS Number"); break; + case ENDP_COLUMN_GEO_AS_ORG: + return tr("AS Organization"); break; + } + } else if (role == Qt::TextAlignmentRole) { + switch (section) { + case ENDP_COLUMN_ADDR: + case ENDP_COLUMN_GEO_COUNTRY: + case ENDP_COLUMN_GEO_CITY: + case ENDP_COLUMN_GEO_AS_ORG: + return Qt::AlignLeft; + default: + break; + } + return Qt::AlignRight; + } + + return QVariant(); +} + +QVariant EndpointDataModel::data(const QModelIndex &idx, int role) const +{ + if (! idx.isValid()) + return QVariant(); + + // Column text cooked representation. + endpoint_item_t *item = &g_array_index(storage_, endpoint_item_t, idx.row()); + const mmdb_lookup_t *mmdb_lookup = nullptr; +#ifdef HAVE_MAXMINDDB + char addr[WS_INET6_ADDRSTRLEN]; + if (item->myaddress.type == AT_IPv4) { + const ws_in4_addr * ip4 = (const ws_in4_addr *) item->myaddress.data; + mmdb_lookup = maxmind_db_lookup_ipv4(ip4); + ws_inet_ntop4(ip4, addr, sizeof(addr)); + } else if (item->myaddress.type == AT_IPv6) { + const ws_in6_addr * ip6 = (const ws_in6_addr *) item->myaddress.data; + mmdb_lookup = maxmind_db_lookup_ipv6(ip6); + ws_inet_ntop6(ip6, addr, sizeof(addr)); + } else { + addr[0] = '\0'; + } + QString ipAddress(addr); +#endif + + if (role == Qt::DisplayRole || role == ATapDataModel::UNFORMATTED_DISPLAYDATA) { + switch (idx.column()) { + case ENDP_COLUMN_ADDR: { + char* addr_str = get_conversation_address(NULL, &item->myaddress, _resolveNames); + QString q_addr_str(addr_str); + wmem_free(NULL, addr_str); + return q_addr_str; + } + case ENDP_COLUMN_PORT: + if (_resolveNames) { + char* port_str = get_endpoint_port(NULL, item, _resolveNames); + QString q_port_str(port_str); + wmem_free(NULL, port_str); + return q_port_str; + } else { + return quint32(item->port); + } + case ENDP_COLUMN_PACKETS: + { + qlonglong packets = (qlonglong)(item->tx_frames + item->rx_frames); + return role == Qt::DisplayRole ? QString("%L1").arg(packets) : (QVariant)packets; + } + case ENDP_COLUMN_BYTES: + return role == Qt::DisplayRole ? formatString((qlonglong)(item->tx_bytes + item->rx_bytes)) : + QVariant((qlonglong)(item->tx_bytes + item->rx_bytes)); + case ENDP_COLUMN_PACKETS_TOTAL: + { + qlonglong packets = 0; + if (showTotalColumn()) + packets = item->tx_frames_total + item->rx_frames_total; + return role == Qt::DisplayRole ? QString("%L1").arg(packets) : (QVariant)packets; + } + case ENDP_COLUMN_BYTES_TOTAL: + { + double percent = 0; + if (showTotalColumn()) { + qlonglong totalPackets = (qlonglong)(item->tx_frames_total + item->rx_frames_total); + qlonglong packets = (qlonglong)(item->tx_frames + item->rx_frames); + percent = totalPackets == 0 ? 0 : (double) packets * 100 / (double) totalPackets; + } + QString rounded = QString::number(percent, 'f', 2); + /* Qt guarantees that this roundtrip conversion compares equally, + * so filtering with equality will work as expected. + * Perhaps the UNFORMATTED_DISPLAYDATA role shoud be split + * into one used for raw data export, and one used for comparisons. + */ + return role == Qt::DisplayRole ? rounded + "%" : QVariant(rounded.toDouble()); + } + case ENDP_COLUMN_PKT_AB: + return role == Qt::DisplayRole ? QString("%L1").arg((qlonglong)item->tx_frames) : QVariant((qlonglong) item->tx_frames); + case ENDP_COLUMN_BYTES_AB: + return role == Qt::DisplayRole ? formatString((qlonglong)item->tx_bytes) : QVariant((qlonglong)item->tx_bytes); + case ENDP_COLUMN_PKT_BA: + return role == Qt::DisplayRole ? QString("%L1").arg((qlonglong)item->rx_frames) : QVariant((qlonglong) item->rx_frames); + case ENDP_COLUMN_BYTES_BA: + return role == Qt::DisplayRole ? formatString((qlonglong)item->rx_bytes) : QVariant((qlonglong)item->rx_bytes); + case ENDP_COLUMN_GEO_COUNTRY: + if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->country) { + return QVariant(mmdb_lookup->country); + } + return QVariant(); + case ENDP_COLUMN_GEO_CITY: + if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->city) { + return QVariant(mmdb_lookup->city); + } + return QVariant(); + case ENDP_COLUMN_GEO_LATITUDE: + if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->latitude >= -90.0 && mmdb_lookup->latitude <= 90.0) { + return role == Qt::DisplayRole ? QString("%L1" UTF8_DEGREE_SIGN).arg(mmdb_lookup->latitude) : QVariant(mmdb_lookup->latitude); + } + return QVariant(); + case ENDP_COLUMN_GEO_LONGITUDE: + if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->longitude >= -180.0 && mmdb_lookup->longitude <= 180.0) { + return role == Qt::DisplayRole ? QString("%L1" UTF8_DEGREE_SIGN).arg(mmdb_lookup->longitude) : QVariant(mmdb_lookup->longitude); + } + return QVariant(); + case ENDP_COLUMN_GEO_AS_NUM: + if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->as_number) { + return QVariant(mmdb_lookup->as_number); + } + return QVariant(); + case ENDP_COLUMN_GEO_AS_ORG: + if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->as_org) { + return QVariant(mmdb_lookup->as_org); + } + return QVariant(); + default: + return QVariant(); + } + } else if (role == Qt::TextAlignmentRole) { + switch (idx.column()) { + case ENDP_COLUMN_ADDR: + case ENDP_COLUMN_GEO_COUNTRY: + case ENDP_COLUMN_GEO_CITY: + case ENDP_COLUMN_GEO_AS_ORG: + return Qt::AlignLeft; + default: + break; + } + return Qt::AlignRight; + } else if (role == ATapDataModel::DISPLAY_FILTER) { + return QString(get_endpoint_filter(item)); + } else if (role == ATapDataModel::ROW_IS_FILTERED) { + return (bool)item->filtered && showTotalColumn(); + } +#ifdef HAVE_MAXMINDDB + else if (role == ATapDataModel::GEODATA_AVAILABLE) { + return (bool)(mmdb_lookup && maxmind_db_has_coords(mmdb_lookup)); + } else if (role == ATapDataModel::GEODATA_LOOKUPTABLE) { + return VariantPointer::asQVariant(mmdb_lookup); + } else if (role == ATapDataModel::GEODATA_ADDRESS) { + return ipAddress; + } +#endif + else if (role == ATapDataModel::PROTO_ID) { + return protoId(); + } else if (role == ATapDataModel::DATA_ADDRESS_TYPE) { + if (idx.column() == EndpointDataModel::ENDP_COLUMN_ADDR) + return (int)item->myaddress.type; + return (int) AT_NONE; + } else if (role == ATapDataModel::DATA_IPV4_INTEGER || role == ATapDataModel::DATA_IPV6_LIST) { + if (idx.column() == EndpointDataModel::ENDP_COLUMN_ADDR) { + if (role == ATapDataModel::DATA_IPV4_INTEGER && item->myaddress.type == AT_IPv4) { + const ws_in4_addr * ip4 = (const ws_in4_addr *) item->myaddress.data; + return (quint32) GUINT32_FROM_BE(*ip4); + } + else if (role == ATapDataModel::DATA_IPV6_LIST && item->myaddress.type == AT_IPv6) { + const ws_in6_addr * ip6 = (const ws_in6_addr *) item->myaddress.data; + QList result; + result.reserve(16); + std::copy(ip6->bytes + 0, ip6->bytes + 16, std::back_inserter(result)); + return QVariant::fromValue(result); + } + } + } + + return QVariant(); +} + +ConversationDataModel::ConversationDataModel(int protoId, QString filter, QObject *parent) : + ATapDataModel(ATapDataModel::DATAMODEL_CONVERSATION, protoId, filter, parent) +{} + +void ConversationDataModel::doDataUpdate() +{ + _minRelStartTime = 0; + _maxRelStopTime = 0; + + for (int row = 0; row < rowCount(); row ++) { + conv_item_t *conv_item = &g_array_index(storage_, conv_item_t, row); + + if (row == 0) { + _minRelStartTime = nstime_to_sec(&(conv_item->start_time)); + _maxRelStopTime = nstime_to_sec(&(conv_item->stop_time)); + } else { + double item_rel_start = nstime_to_sec(&(conv_item->start_time)); + if (item_rel_start < _minRelStartTime) { + _minRelStartTime = item_rel_start; + } + + double item_rel_stop = nstime_to_sec(&(conv_item->stop_time)); + if (item_rel_stop > _maxRelStopTime) { + _maxRelStopTime = item_rel_stop; + } + } + } +} + +int ConversationDataModel::columnCount(const QModelIndex &) const +{ + return CONV_NUM_COLUMNS; +} + +QVariant ConversationDataModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) + return QVariant(); + + if (role == Qt::DisplayRole) { + switch (section) { + case CONV_COLUMN_SRC_ADDR: + return tr("Address A"); break; + case CONV_COLUMN_SRC_PORT: + return tr("Port A"); break; + case CONV_COLUMN_DST_ADDR: + return tr("Address B"); break; + case CONV_COLUMN_DST_PORT: + return tr("Port B"); break; + case CONV_COLUMN_PACKETS: + return tr("Packets"); break; + case CONV_COLUMN_BYTES: + return tr("Bytes"); break; + case CONV_COLUMN_CONV_ID: + return tr("Stream ID"); break; + case CONV_COLUMN_PACKETS_TOTAL: + return tr("Total Packets"); break; + case CONV_COLUMN_BYTES_TOTAL: + return tr("Percent Filtered"); break; + case CONV_COLUMN_PKT_AB: + return tr("Packets A " UTF8_RIGHTWARDS_ARROW " B"); break; + case CONV_COLUMN_BYTES_AB: + return tr("Bytes A " UTF8_RIGHTWARDS_ARROW " B"); break; + case CONV_COLUMN_PKT_BA: + return tr("Packets B " UTF8_RIGHTWARDS_ARROW " A"); break; + case CONV_COLUMN_BYTES_BA: + return tr("Bytes B " UTF8_RIGHTWARDS_ARROW " A"); break; + case CONV_COLUMN_START: + return _absoluteTime ? tr("Abs Start") : tr("Rel Start"); break; + case CONV_COLUMN_DURATION: + return tr("Duration"); break; + case CONV_COLUMN_BPS_AB: + return tr("Bits/s A " UTF8_RIGHTWARDS_ARROW " B"); break; + case CONV_COLUMN_BPS_BA: + return tr("Bits/s B " UTF8_RIGHTWARDS_ARROW " A"); break; + } + } else if (role == Qt::TextAlignmentRole) { + if (section == CONV_COLUMN_SRC_ADDR || section == CONV_COLUMN_DST_ADDR) + return Qt::AlignLeft; + return Qt::AlignRight; + } + + return QVariant(); +} + +static const double min_bw_calc_duration_ = 5 / 1000.0; // seconds + +QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const +{ + if (! idx.isValid()) + return QVariant(); + + // Column text cooked representation. + conv_item_t *conv_item = (conv_item_t *)&g_array_index(storage_, conv_item_t,idx.row()); + + double duration = nstime_to_sec(&conv_item->stop_time) - nstime_to_sec(&conv_item->start_time); + double bps_ab = 0, bps_ba = 0; + bool bpsCalculated = false; + if (duration > min_bw_calc_duration_) { + bps_ab = conv_item->tx_bytes * 8 / duration; + bps_ba = conv_item->rx_bytes * 8 / duration; + bpsCalculated = true; + } + + if (role == Qt::DisplayRole || role == ATapDataModel::UNFORMATTED_DISPLAYDATA) { + switch(idx.column()) { + case CONV_COLUMN_SRC_ADDR: + { + char* addr_str = get_conversation_address(NULL, &conv_item->src_address, _resolveNames); + QString q_addr_str(addr_str); + wmem_free(NULL, addr_str); + return q_addr_str; + } + case CONV_COLUMN_SRC_PORT: + if (_resolveNames) { + char* port_str = get_conversation_port(NULL, conv_item->src_port, conv_item->ctype, _resolveNames); + QString q_port_str(port_str); + wmem_free(NULL, port_str); + return q_port_str; + } else { + return quint32(conv_item->src_port); + } + case CONV_COLUMN_DST_ADDR: + { + char* addr_str = get_conversation_address(NULL, &conv_item->dst_address, _resolveNames); + QString q_addr_str(addr_str); + wmem_free(NULL, addr_str); + return q_addr_str; + } + case CONV_COLUMN_DST_PORT: + if (_resolveNames) { + char* port_str = get_conversation_port(NULL, conv_item->dst_port, conv_item->ctype, _resolveNames); + QString q_port_str(port_str); + wmem_free(NULL, port_str); + return q_port_str; + } else { + return quint32(conv_item->dst_port); + } + case CONV_COLUMN_PACKETS: + { + qlonglong packets = conv_item->tx_frames + conv_item->rx_frames; + return role == Qt::DisplayRole ? QString("%L1").arg(packets) : (QVariant)packets; + } + case CONV_COLUMN_BYTES: + return role == Qt::DisplayRole ? formatString((qlonglong)conv_item->tx_bytes + conv_item->rx_bytes) : + QVariant((qlonglong)conv_item->tx_bytes + conv_item->rx_bytes); + case CONV_COLUMN_CONV_ID: + return (int) conv_item->conv_id; + case CONV_COLUMN_PACKETS_TOTAL: + { + qlonglong packets = 0; + if (showTotalColumn()) + packets = conv_item->tx_frames_total + conv_item->rx_frames_total; + + return role == Qt::DisplayRole ? QString("%L1").arg(packets) : (QVariant)packets; + } + case CONV_COLUMN_BYTES_TOTAL: + { + double percent = 0; + if (showTotalColumn()) { + qlonglong totalPackets = (qlonglong)(conv_item->tx_frames_total + conv_item->rx_frames_total); + qlonglong packets = (qlonglong)(conv_item->tx_frames + conv_item->rx_frames); + percent = totalPackets == 0 ? 0 : (double) packets * 100 / (double) totalPackets; + } + QString rounded = QString::number(percent, 'f', 2); + /* Qt guarantees that this roundtrip conversion compares equally, + * so filtering with equality will work as expected. + * XXX: Perhaps the UNFORMATTED_DISPLAYDATA role shoud be split + * into one used for raw data export and comparisions with each + * other, and another for comparing with filters? + */ + return role == Qt::DisplayRole ? rounded + "%" : QVariant(rounded.toDouble()); + } + case CONV_COLUMN_PKT_AB: + { + qlonglong packets = conv_item->tx_frames; + return role == Qt::DisplayRole ? QString("%L1").arg(packets) : (QVariant)packets; + } + case CONV_COLUMN_BYTES_AB: + return role == Qt::DisplayRole ? formatString((qlonglong)conv_item->tx_bytes) : QVariant((qlonglong)conv_item->tx_bytes); + case CONV_COLUMN_PKT_BA: + { + qlonglong packets = conv_item->rx_frames; + return role == Qt::DisplayRole ? QString("%L1").arg(packets) : (QVariant)packets; + } + case CONV_COLUMN_BYTES_BA: + return role == Qt::DisplayRole ? formatString((qlonglong)conv_item->rx_bytes) : QVariant((qlonglong)conv_item->rx_bytes); + case CONV_COLUMN_START: + { + int width = _nanoseconds ? 9 : 6; + + if (_absoluteTime) { + nstime_t *abs_time = &conv_item->start_abs_time; + /* XXX: QDateTime only supports millisecond resolution, + * and we have microseconds or nanoseconds. + * Should we use something else, particularly for exporting + * raw data? GDateTime handles microseconds. + */ + QDateTime abs_dt = QDateTime::fromMSecsSinceEpoch(nstime_to_msec(abs_time)); + if (role == Qt::DisplayRole) { + if (_maxRelStopTime >= 24*60*60) { + return abs_dt.toString(Qt::ISODateWithMs); + } else { + return abs_dt.time().toString(Qt::ISODateWithMs); + } + } else { + return QVariant(abs_dt); + } + } else { + return role == Qt::DisplayRole ? + QString::number(nstime_to_sec(&conv_item->start_time), 'f', width) : + (QVariant)((double) nstime_to_sec(&conv_item->start_time)); + } + } + case CONV_COLUMN_DURATION: + { + int width = _nanoseconds ? 6 : 4; + return role == Qt::DisplayRole ? QString::number(duration, 'f', width) : (QVariant)duration; + } + case CONV_COLUMN_BPS_AB: + return bpsCalculated ? (role == Qt::DisplayRole ? gchar_free_to_qstring(format_size((int64_t)bps_ab, FORMAT_SIZE_UNIT_BITS_S, FORMAT_SIZE_PREFIX_SI)) : QVariant((qlonglong)bps_ab)): QVariant(); + case CONV_COLUMN_BPS_BA: + return bpsCalculated ? (role == Qt::DisplayRole ? gchar_free_to_qstring(format_size((int64_t)bps_ba, FORMAT_SIZE_UNIT_BITS_S, FORMAT_SIZE_PREFIX_SI)) : QVariant((qlonglong)bps_ba)): QVariant(); + } + } else if (role == Qt::ToolTipRole) { + if (idx.column() == CONV_COLUMN_START || idx.column() == CONV_COLUMN_DURATION) + return QObject::tr("Bars show the relative timeline for each conversation."); + } else if (role == Qt::TextAlignmentRole) { + if (idx.column() == CONV_COLUMN_SRC_ADDR || idx.column() == CONV_COLUMN_DST_ADDR) + return Qt::AlignLeft; + return Qt::AlignRight; + } else if (role == ATapDataModel::TIMELINE_DATA) { + struct timeline_span span_data; + span_data.minRelTime = _minRelStartTime; + span_data.maxRelTime = _maxRelStopTime; + span_data.startTime = nstime_to_sec(&conv_item->start_time); + span_data.stopTime = nstime_to_sec(&conv_item->stop_time); + span_data.colStart = CONV_COLUMN_START; + span_data.colDuration = CONV_COLUMN_DURATION; + + if ((_maxRelStopTime - _minRelStartTime) > 0) { + return QVariant::fromValue(span_data); + } + } else if (role == ATapDataModel::ENDPOINT_DATATYPE) { + return (int)(conv_item->ctype); + } else if (role == ATapDataModel::PROTO_ID) { + return protoId(); + } else if (role == ATapDataModel::CONVERSATION_ID) { + return (int)(conv_item->conv_id); + } else if (role == ATapDataModel::ROW_IS_FILTERED) { + return (bool)conv_item->filtered && showTotalColumn(); + } else if (role == ATapDataModel::DATA_ADDRESS_TYPE) { + if (idx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR || idx.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) { + address tst_address = idx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR ? conv_item->src_address : conv_item->dst_address; + return (int)tst_address.type; + } + return (int) AT_NONE; + } else if (role == ATapDataModel::DATA_IPV4_INTEGER || role == ATapDataModel::DATA_IPV6_LIST) { + if (idx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR || idx.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) { + address tst_address = idx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR ? conv_item->src_address : conv_item->dst_address; + if (role == ATapDataModel::DATA_IPV4_INTEGER && tst_address.type == AT_IPv4) { + const ws_in4_addr * ip4 = (const ws_in4_addr *) tst_address.data; + return (quint32) GUINT32_FROM_BE(*ip4); + } + else if (role == ATapDataModel::DATA_IPV6_LIST && tst_address.type == AT_IPv6) { + const ws_in6_addr * ip6 = (const ws_in6_addr *) tst_address.data; + QList result; + result.reserve(16); + std::copy(ip6->bytes + 0, ip6->bytes + 16, std::back_inserter(result)); + return QVariant::fromValue(result); + } + } + } + + return QVariant(); +} + +conv_item_t * ConversationDataModel::itemForRow(int row) +{ + if (row < 0 || row >= rowCount()) + return nullptr; + return (conv_item_t *)&g_array_index(storage_, conv_item_t, row); +} + +bool ConversationDataModel::showConversationId(int row) const +{ + if (!storage_ || row < 0 || row >= (int) storage_->len) + return false; + + conv_item_t *conv_item = (conv_item_t *)&g_array_index(storage_, conv_item_t, row); + if (conv_item && (conv_item->ctype == CONVERSATION_TCP || conv_item->ctype == CONVERSATION_UDP)) + return true; + return false; +} diff --git a/ui/qt/models/atap_data_model.h b/ui/qt/models/atap_data_model.h new file mode 100644 index 00000000..38bdbd2f --- /dev/null +++ b/ui/qt/models/atap_data_model.h @@ -0,0 +1,329 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ATAP_DATA_MODEL_H +#define ATAP_DATA_MODEL_H + +#include "config.h" + +#include "glib.h" + +#include +#include +#include + +#include + +/** + * @brief DataModel for tap user data + * + * This datamodel provides the management for all tap data for the conversation + * and endpoint dialogs. It predominantly is implemented to work with conversation + * tap data. The management of displaying and correctly presenting the information + * is done in the corresponding type classes + * + * @see EndpointDataModel + * @see ConversationDataModel + */ +class ATapDataModel : public QAbstractListModel +{ + Q_OBJECT +public: + + enum { + DISPLAY_FILTER = Qt::UserRole, + UNFORMATTED_DISPLAYDATA, +#ifdef HAVE_MAXMINDDB + GEODATA_AVAILABLE, + GEODATA_LOOKUPTABLE, + GEODATA_ADDRESS, +#endif + TIMELINE_DATA, + ENDPOINT_DATATYPE, + PROTO_ID, + CONVERSATION_ID, + ROW_IS_FILTERED, + DATA_ADDRESS_TYPE, + DATA_IPV4_INTEGER, + DATA_IPV6_LIST, + }; + + typedef enum { + DATAMODEL_ENDPOINT, + DATAMODEL_CONVERSATION, + DATAMODEL_UNKNOWN + } dataModelType; + + /** + * @brief Construct a new ATapDataModel object + * + * The tap will not be created automatically, but must be enabled by calling enableTap + * + * @param type an element of dataModelType. Either DATAMODEL_ENDPOINT or DATAMODEL_CONVERSATION are supported + * at this time + * @param protoId the protocol id for which the tap is created + * @param filter a potential filter to be used for the tap + * @param parent the parent for the class + * + * @see enableTap + */ + explicit ATapDataModel(dataModelType type, int protoId, QString filter, QObject *parent = nullptr); + virtual ~ATapDataModel(); + + /** + * @brief Number of rows under the given parent in this model, which + * is the total number of rows for the empty QModelIndex, and 0 for + * any valid parent index (as no row has children; this is a flat table.) + * + * @param parent index of parent, QModelIndex() for the root + * @return int the number of rows under the parent + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const; + + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0; + virtual QVariant headerData(int section, Qt::Orientation orientation = Qt::Horizontal, int role = Qt::DisplayRole) const = 0; + virtual QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const = 0; + + /** + * @brief Returns the name for the tap being used + * + * @return QString the tap name, normally identical with the protocol name + */ + QString tap() const; + + /** + * @brief The protocol id for the tap + * + * @return int the id given in the constructor + */ + int protoId() const; + + /** + * @brief Set the filter string. + * + * A set filter can be reset by providing an empty string + * + * @param filter the filter for the tap + */ + void setFilter(QString filter); + + /** + * @brief Return a filter set for the model + * + * @return QString the filter string for the model + */ + QString filter() const; + + /** + * @brief Is the model set to resolve names in address and ports columns + * + * @return true yes, names will be resolved + * @return false no they won't + */ + bool resolveNames() const; + + /** + * @brief Enable or disable if names should be resolved + * + * @param resolve true if names should be resolved + */ + void setResolveNames(bool resolve); + + /** + * @brief Does the model allow names to be resolved + * + * @return true yes, names may be resolved (set via setResolveNames) + * @return false no, they won't be resolved + * + * @see setResolveNames + * @see resolveNames + */ + bool allowsNameResolution() const; + + /** + * @brief Use absolute time for any column supporting it + * + * @param absolute true to use absolute time values + */ + void useAbsoluteTime(bool absolute); + + /** + * @brief Use nanosecond timestamps if requested + * + * @param nanoseconds use nanosecond timestamps if required and requested + */ + void useNanosecondTimestamps(bool nanoseconds); + + /** + * @brief Are ports hidden for this model + * + * @return true the ports are hidden + * @return false the ports are not hidden + */ + bool portsAreHidden() const; + + /** + * @brief A total column is filled + * + * @return true if the column is filled + * @return false the column is empty + */ + bool showTotalColumn() const; + + /** + * @brief Enable tapping in this model. + * + * This will register the tap listener with the corresponding packet function. + * @note if the tap has not been disabled, this method will do nothing + * + * @return true the tap has been enabled + * @return false the tap has not been enabled + */ + bool enableTap(); + + /** + * @brief Disable the tapping for this model + */ + void disableTap(); + + /** + * @brief Return the model type + * + * @return dataModelType + */ + dataModelType modelType() const; + +#ifdef HAVE_MAXMINDDB + /** + * @brief Does this model have geoip data available + * + * @return true it has + * @return false it has not + */ + bool hasGeoIPData(); +#endif + +signals: + void tapListenerChanged(bool enable); + +protected: + + static void tapReset(void *tapdata); + static void tapDraw(void *tap_data); + + virtual tap_packet_cb conversationPacketHandler(); + + conv_hash_t * hash(); + + void resetData(); + void updateData(GArray * data); + + dataModelType _type; + GArray * storage_; + QString _filter; + + bool _absoluteTime; + bool _nanoseconds; + bool _resolveNames; + bool _disableTap; + + double _minRelStartTime; + double _maxRelStopTime; + + register_ct_t* registerTable() const; + +private: + int _protoId; + + conv_hash_t hash_; +}; + +class EndpointDataModel : public ATapDataModel +{ + Q_OBJECT +public: + + typedef enum + { + ENDP_COLUMN_ADDR, + ENDP_COLUMN_PORT, + ENDP_COLUMN_PACKETS, + ENDP_COLUMN_BYTES, + ENDP_COLUMN_PACKETS_TOTAL, + ENDP_COLUMN_BYTES_TOTAL, + ENDP_COLUMN_PKT_AB, + ENDP_COLUMN_BYTES_AB, + ENDP_COLUMN_PKT_BA, + ENDP_COLUMN_BYTES_BA, + ENDP_NUM_COLUMNS, + ENDP_COLUMN_GEO_COUNTRY = ENDP_NUM_COLUMNS, + ENDP_COLUMN_GEO_CITY, + ENDP_COLUMN_GEO_LATITUDE, + ENDP_COLUMN_GEO_LONGITUDE, + ENDP_COLUMN_GEO_AS_NUM, + ENDP_COLUMN_GEO_AS_ORG, + ENDP_NUM_GEO_COLUMNS + } endpoint_column_type_e; + + explicit EndpointDataModel(int protoId, QString filter, QObject *parent = nullptr); + + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation = Qt::Horizontal, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const; + +}; + +class ConversationDataModel : public ATapDataModel +{ + Q_OBJECT +public: + + typedef enum { + CONV_COLUMN_SRC_ADDR, + CONV_COLUMN_SRC_PORT, + CONV_COLUMN_DST_ADDR, + CONV_COLUMN_DST_PORT, + CONV_COLUMN_PACKETS, + CONV_COLUMN_BYTES, + CONV_COLUMN_CONV_ID, + CONV_COLUMN_PACKETS_TOTAL, + CONV_COLUMN_BYTES_TOTAL, + CONV_COLUMN_PKT_AB, + CONV_COLUMN_BYTES_AB, + CONV_COLUMN_PKT_BA, + CONV_COLUMN_BYTES_BA, + CONV_COLUMN_START, + CONV_COLUMN_DURATION, + CONV_COLUMN_BPS_AB, + CONV_COLUMN_BPS_BA, + CONV_NUM_COLUMNS, + CONV_INDEX_COLUMN = CONV_NUM_COLUMNS + } conversation_column_type_e; + + explicit ConversationDataModel(int protoId, QString filter, QObject *parent = nullptr); + + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation = Qt::Horizontal, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const; + + void doDataUpdate(); + + conv_item_t * itemForRow(int row); + + /** + * @brief Show the conversation id if available + * + * @return true a conversation id exists + * @return false none available + */ + bool showConversationId(int row = 0) const; + +}; + +#endif // ATAP_DATA_MODEL_H diff --git a/ui/qt/models/cache_proxy_model.cpp b/ui/qt/models/cache_proxy_model.cpp new file mode 100644 index 00000000..9f136882 --- /dev/null +++ b/ui/qt/models/cache_proxy_model.cpp @@ -0,0 +1,100 @@ +/* cache_proxy_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +CacheProxyModel::CacheProxyModel(QObject *parent) : QIdentityProxyModel(parent) +{ +} + +QVariant CacheProxyModel::data(const QModelIndex &index, int role) const +{ + QModelIndex dataIndex = cache.index(index.row(), index.column()); + if (!dataIndex.isValid()) { + // index is possibly outside columnCount or rowCount + return QVariant(); + } + + if (hasModel()) { + QVariant value = QIdentityProxyModel::data(index, role); + cache.setData(dataIndex, value, role); + return value; + } else { + return cache.data(dataIndex, role); + } +} + +Qt::ItemFlags CacheProxyModel::flags(const QModelIndex &index) const +{ + if (hasModel()) { + return QIdentityProxyModel::flags(index); + } else { + // Override default to prevent editing. + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } +} + +QVariant CacheProxyModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (hasModel()) { + QVariant value = QIdentityProxyModel::headerData(section, orientation, role); + cache.setHeaderData(section, orientation, value, role); + return value; + } else { + return cache.headerData(section, orientation, role); + } +} + +int CacheProxyModel::rowCount(const QModelIndex &parent) const +{ + if (hasModel()) { + int count = QIdentityProxyModel::rowCount(parent); + cache.setRowCount(count); + return count; + } else { + return cache.rowCount(parent); + } +} + +int CacheProxyModel::columnCount(const QModelIndex &parent) const +{ + if (hasModel()) { + int count = QIdentityProxyModel::columnCount(parent); + cache.setColumnCount(count); + return count; + } else { + return cache.columnCount(parent); + } +} + +/** + * Sets the source model from which data must be pulled. If newSourceModel is + * NULL, then the cache will be used. + */ +void CacheProxyModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (newSourceModel) { + cache.clear(); + QIdentityProxyModel::setSourceModel(newSourceModel); + connect(newSourceModel, &QAbstractItemModel::modelReset, + this, &CacheProxyModel::resetCacheModel); + } else { + if (sourceModel()) { + // Prevent further updates to source model from invalidating cache. + disconnect(sourceModel(), &QAbstractItemModel::modelReset, + this, &CacheProxyModel::resetCacheModel); + } + QIdentityProxyModel::setSourceModel(&cache); + } +} + +void CacheProxyModel::resetCacheModel() { + cache.clear(); +} diff --git a/ui/qt/models/cache_proxy_model.h b/ui/qt/models/cache_proxy_model.h new file mode 100644 index 00000000..70954b59 --- /dev/null +++ b/ui/qt/models/cache_proxy_model.h @@ -0,0 +1,47 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CACHE_PROXY_MODEL_H +#define CACHE_PROXY_MODEL_H + +#include + +#include +#include + +/** + * Caches any data read access to the source model, returning an older copy if + * the source model is invalidated. + * + * Only flat data is supported at the moment, tree models (with parents) are + * unsupported. + */ +class CacheProxyModel : public QIdentityProxyModel +{ + Q_OBJECT + +public: + CacheProxyModel(QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) 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 setSourceModel(QAbstractItemModel *newSourceModel); + +private: + mutable QStandardItemModel cache; + + bool hasModel() const { return sourceModel() != &cache; } + +private slots: + void resetCacheModel(); +}; +#endif diff --git a/ui/qt/models/coloring_rules_delegate.cpp b/ui/qt/models/coloring_rules_delegate.cpp new file mode 100644 index 00000000..b86ffa97 --- /dev/null +++ b/ui/qt/models/coloring_rules_delegate.cpp @@ -0,0 +1,123 @@ +/* coloring_rules_delegate.cpp + * Delegates for editing various coloring rule fields. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +ColoringRulesDelegate::ColoringRulesDelegate(QObject *parent) : QStyledItemDelegate(parent) +{ +} + +QWidget* ColoringRulesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, + const QModelIndex &index) const +{ + switch (index.column()) + { + case ColoringRulesModel::colName: + { + SyntaxLineEdit *editor = new SyntaxLineEdit(parent); + connect(editor, &SyntaxLineEdit::textChanged, this, &ColoringRulesDelegate::ruleNameChanged); + return editor; + } + + case ColoringRulesModel::colFilter: + return new DisplayFilterEdit(parent); + + default: + Q_ASSERT(FALSE); + return 0; + } + + return 0; +} + +void ColoringRulesDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + switch (index.column()) + { + case ColoringRulesModel::colName: + { + SyntaxLineEdit *syntaxEdit = static_cast(editor); + syntaxEdit->setText(index.model()->data(index, Qt::EditRole).toString()); + break; + } + case ColoringRulesModel::colFilter: + { + DisplayFilterEdit *displayEdit = static_cast(editor); + displayEdit->setText(index.model()->data(index, Qt::EditRole).toString()); + break; + } + default: + QStyledItemDelegate::setEditorData(editor, index); + break; + } +} + +void ColoringRulesDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + switch (index.column()) + { + case ColoringRulesModel::colName: + { + SyntaxLineEdit *syntaxEdit = static_cast(editor); + model->setData(index, syntaxEdit->text(), Qt::EditRole); + if (syntaxEdit->syntaxState() == SyntaxLineEdit::Invalid) { + QString error_text = tr("the \"@\" symbol will be ignored."); + emit invalidField(index, error_text); + } + else + { + emit validField(index); + } + break; + } + case ColoringRulesModel::colFilter: + { + DisplayFilterEdit *displayEdit = static_cast(editor); + model->setData(index, displayEdit->text(), Qt::EditRole); + if ((displayEdit->syntaxState() == SyntaxLineEdit::Invalid) && + (model->data(model->index(index.row(), ColoringRulesModel::colName), Qt::CheckStateRole) == Qt::Checked)) + { + model->setData(model->index(index.row(), ColoringRulesModel::colName), Qt::Unchecked, Qt::CheckStateRole); + emit invalidField(index, displayEdit->syntaxErrorMessage()); + } + else + { + emit validField(index); + } + break; + } + default: + QStyledItemDelegate::setModelData(editor, model, index); + break; + } +} + +void ColoringRulesDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex&) const +{ + editor->setGeometry(option.rect); +} + +void ColoringRulesDelegate::ruleNameChanged(const QString name) +{ + SyntaxLineEdit *name_edit = qobject_cast(QObject::sender()); + if (!name_edit) return; + + if (name.isEmpty()) { + name_edit->setSyntaxState(SyntaxLineEdit::Empty); + } else if (name.contains("@")) { + name_edit->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + name_edit->setSyntaxState(SyntaxLineEdit::Valid); + } +} diff --git a/ui/qt/models/coloring_rules_delegate.h b/ui/qt/models/coloring_rules_delegate.h new file mode 100644 index 00000000..24ba2f38 --- /dev/null +++ b/ui/qt/models/coloring_rules_delegate.h @@ -0,0 +1,43 @@ +/** @file + * + * Delegates for editing various coloring rule fields. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COLORING_RULE_DELEGATE_H +#define COLORING_RULE_DELEGATE_H + +#include + +#include +#include + +class ColoringRulesDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + ColoringRulesDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + +signals: + void invalidField(const QModelIndex &index, const QString& errMessage) const; + void validField(const QModelIndex &index) const; + +private slots: + void ruleNameChanged(const QString name); +}; +#endif // COLORING_RULE_DELEGATE_H diff --git a/ui/qt/models/coloring_rules_model.cpp b/ui/qt/models/coloring_rules_model.cpp new file mode 100644 index 00000000..dced9c37 --- /dev/null +++ b/ui/qt/models/coloring_rules_model.cpp @@ -0,0 +1,569 @@ +/* coloring_rules_model.cpp + * Data model for coloring rules. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "config.h" + +#include "coloring_rules_model.h" + +#include + +#include "ui/ws_ui_util.h" //for color_filter_add_cb + +#include +#include +#include +#include + +#include +#include +#include +#include + +ColoringRuleItem::ColoringRuleItem(bool disabled, QString name, QString filter, QColor foreground, QColor background, ColoringRuleItem* parent) + : ModelHelperTreeItem(parent), + disabled_(disabled), + name_(name), + filter_(filter), + foreground_(foreground), + background_(background) +{ +} + +ColoringRuleItem::~ColoringRuleItem() +{ + +} + +ColoringRuleItem::ColoringRuleItem(color_filter_t *colorf, ColoringRuleItem* parent) + : ModelHelperTreeItem(parent), + disabled_(colorf->disabled), + name_(colorf->filter_name), + filter_(colorf->filter_text), + foreground_(ColorUtils::fromColorT(colorf->fg_color)), + background_(ColorUtils::fromColorT(colorf->bg_color)) +{ +} + +ColoringRuleItem::ColoringRuleItem(const ColoringRuleItem& item) + : ModelHelperTreeItem(item.parent_), + disabled_(item.disabled_), + name_(item.name_), + filter_(item.filter_), + foreground_(item.foreground_), + background_(item.background_) +{ +} + +ColoringRuleItem& ColoringRuleItem::operator=(ColoringRuleItem& rhs) +{ + disabled_ = rhs.disabled_; + name_ = rhs.name_; + filter_ = rhs.filter_; + foreground_ = rhs.foreground_; + background_ = rhs.background_; + return *this; +} + +// Callback for color_filters_clone. +void +color_filter_add_cb(color_filter_t *colorf, gpointer user_data) +{ + ColoringRulesModel *model = (ColoringRulesModel*)user_data; + + if (model == NULL) + return; + + model->addColor(colorf); +} + +ColoringRulesModel::ColoringRulesModel(QColor defaultForeground, QColor defaultBackground, QObject *parent) : + QAbstractItemModel(parent), + root_(new ColoringRuleItem(false, "", "", QColor(), QColor(), NULL)), + conversation_colors_(NULL), + defaultForeground_(defaultForeground), + defaultBackground_(defaultBackground) + +{ + color_filters_clone(this, color_filter_add_cb); +} + +ColoringRulesModel::~ColoringRulesModel() +{ + delete root_; + color_filter_list_delete(&conversation_colors_); +} + +GSList *ColoringRulesModel::createColorFilterList() +{ + GSList *cfl = NULL; + for (int row = 0; row < root_->childCount(); row++) + { + ColoringRuleItem* rule = root_->child(row); + if (rule == NULL) + continue; + + color_t fg = ColorUtils::toColorT(rule->foreground_); + color_t bg = ColorUtils::toColorT(rule->background_); + color_filter_t *colorf = color_filter_new(rule->name_.toUtf8().constData(), + rule->filter_.toUtf8().constData(), + &bg, &fg, rule->disabled_); + cfl = g_slist_append(cfl, colorf); + } + + return cfl; +} + +void ColoringRulesModel::addColor(color_filter_t* colorf) +{ + if (!colorf) return; + + if (strstr(colorf->filter_name, CONVERSATION_COLOR_PREFIX) != NULL) { + conversation_colors_ = g_slist_append(conversation_colors_, colorf); + } else { + int count = root_->childCount(); + + beginInsertRows(QModelIndex(), count, count); + ColoringRuleItem* item = new ColoringRuleItem(colorf, root_); + color_filter_delete(colorf); + root_->appendChild(item); + endInsertRows(); + } +} + +void ColoringRulesModel::addColor(bool disabled, QString filter, QColor foreground, QColor background) +{ + //add rule to top of the list + beginInsertRows(QModelIndex(), 0, 0); + ColoringRuleItem* item = new ColoringRuleItem(disabled, tr("New coloring rule"), filter, foreground, background, root_); + root_->prependChild(item); + endInsertRows(); +} + + +bool ColoringRulesModel::importColors(QString filename, QString& err) +{ + bool success = true; + gchar* err_msg = NULL; + if (!color_filters_import(filename.toUtf8().constData(), this, &err_msg, color_filter_add_cb)) { + err = gchar_free_to_qstring(err_msg); + success = false; + } + + return success; +} + +bool ColoringRulesModel::exportColors(QString filename, QString& err) +{ + GSList *cfl = createColorFilterList(); + bool success = true; + gchar* err_msg = NULL; + if (!color_filters_export(filename.toUtf8().constData(), cfl, FALSE, &err_msg)) { + err = gchar_free_to_qstring(err_msg); + success = false; + } + color_filter_list_delete(&cfl); + + return success; +} + +bool ColoringRulesModel::writeColors(QString& err) +{ + GSList *cfl = createColorFilterList(); + bool success = true; + gchar* err_msg = NULL; + if (!color_filters_apply(conversation_colors_, cfl, &err_msg)) { + err = gchar_free_to_qstring(err_msg); + success = false; + } + if (!color_filters_write(cfl, &err_msg)) { + err = QString(tr("Unable to save coloring rules: %1").arg(g_strerror(errno))); + success = false; + g_free(err_msg); + } + color_filter_list_delete(&cfl); + + return success; +} + +bool ColoringRulesModel::insertRows(int row, int count, const QModelIndex& parent) +{ + // sanity check insertion + if (row < 0) + return false; + + beginInsertRows(parent, row, row+(count-1)); + + for (int i = row; i < row + count; i++) + { + ColoringRuleItem* item = new ColoringRuleItem(true, tr("New coloring rule"), "", defaultForeground_, defaultBackground_, root_); + root_->insertChild(i, item); + /* Automatically enable the new coloring rule */ + setData(index(i, colName, parent), Qt::Checked, Qt::CheckStateRole); + } + + endInsertRows(); + return true; +} + +bool ColoringRulesModel::removeRows(int row, int count, const QModelIndex& parent) +{ + if (row < 0) + return false; + + beginRemoveRows(parent, row, row+(count-1)); + for (int i = row; i < row + count; i++) + { + root_->removeChild(row); + } + endRemoveRows(); + + return true; +} + +bool ColoringRulesModel::copyRow(int dst_row, int src_row) +{ + if (src_row < 0 || src_row >= rowCount() || dst_row < 0 || dst_row >= rowCount()) { + return false; + } + + ColoringRuleItem* src_item = root_->child(src_row); + if (src_item == NULL) + return false; + + ColoringRuleItem* dst_item = new ColoringRuleItem(*src_item); + if (dst_item == NULL) + return false; + + beginInsertRows(QModelIndex(), dst_row, dst_row); + root_->insertChild(dst_row, dst_item); + endInsertRows(); + + return true; +} + +Qt::ItemFlags ColoringRulesModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + switch (index.column()) + { + case colName: + flags |= (Qt::ItemIsUserCheckable|Qt::ItemIsEditable); + break; + case colFilter: + flags |= Qt::ItemIsEditable; + break; + } + + if (index.isValid()) + flags |= (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); + else + flags |= Qt::ItemIsDropEnabled; + + return flags; +} + + +QVariant ColoringRulesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + ColoringRuleItem* rule = root_->child(index.row()); + if (rule == NULL) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + switch(index.column()) + { + case colName: + return rule->name_; + case colFilter: + return rule->filter_; + } + break; + case Qt::CheckStateRole: + switch(index.column()) + { + case colName: + return rule->disabled_ ? Qt::Unchecked : Qt::Checked; + } + break; + case Qt::BackgroundRole: + return rule->background_; + case Qt::ForegroundRole: + return rule->foreground_; + } + return QVariant(); +} + +bool ColoringRulesModel::setData(const QModelIndex &dataIndex, const QVariant &value, int role) +{ + if (!dataIndex.isValid()) + return false; + + if (data(dataIndex, role) == value) { + // Data appears unchanged, do not do additional checks. + return true; + } + + ColoringRuleItem* rule = root_->child(dataIndex.row()); + if (rule == NULL) + return false; + + QModelIndex topLeft = dataIndex, + bottomRight = dataIndex; + + switch (role) + { + case Qt::EditRole: + switch (dataIndex.column()) + { + case colName: + rule->name_ = value.toString(); + break; + case colFilter: + rule->filter_ = value.toString(); + break; + default: + return false; + } + break; + case Qt::CheckStateRole: + switch (dataIndex.column()) + { + case colName: + rule->disabled_ = (value.toInt() == Qt::Checked) ? false : true; + break; + default: + return false; + } + break; + case Qt::BackgroundRole: +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (!value.canConvert()) +#else + if (!value.canConvert(QVariant::Color)) +#endif + return false; + + rule->background_ = QColor(value.toString()); + break; + case Qt::ForegroundRole: +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (!value.canConvert()) +#else + if (!value.canConvert(QVariant::Color)) +#endif + return false; + + rule->foreground_ = QColor(value.toString()); + break; + case Qt::UserRole: + { + ColoringRuleItem* new_rule = VariantPointer::asPtr(value); + *rule = *new_rule; + topLeft = index(dataIndex.row(), colName); + bottomRight = index(dataIndex.row(), colFilter); + break; + } + default: + return false; + } + + QVector roles; + roles << role; + + emit dataChanged(topLeft, bottomRight, roles); + + return true; + +} + +QVariant ColoringRulesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + + switch ((ColoringRulesColumn)section) { + case colName: + return tr("Name"); + case colFilter: + return tr("Filter"); + default: + break; + } + + return QVariant(); +} + +Qt::DropActions ColoringRulesModel::supportedDropActions() const +{ + return Qt::MoveAction | Qt::CopyAction; +} + +QStringList ColoringRulesModel::mimeTypes() const +{ + return QStringList() << WiresharkMimeData::ColoringRulesMimeType; +} + +QMimeData* ColoringRulesModel::mimeData(const QModelIndexList &indexes) const +{ + //if the list is empty, don't return an empty list + if (indexes.count() == 0) + return NULL; + + QMimeData *mimeData = new QMimeData(); + + QJsonArray data; + foreach (const QModelIndex & index, indexes) + { + if (index.column() == 0) + { + ColoringRuleItem * item = root_->child(index.row()); + QJsonObject entry; + entry["disabled"] = item->disabled_; + entry["name"] = item->name_; + entry["filter"] = item->filter_; + entry["foreground"] = QVariant::fromValue(item->foreground_).toString(); + entry["background"] = QVariant::fromValue(item->background_).toString(); + data.append(entry); + } + } + + QJsonObject dataSet; + dataSet["coloringrules"] = data; + QByteArray encodedData = QJsonDocument(dataSet).toJson(); + + mimeData->setData(WiresharkMimeData::ColoringRulesMimeType, encodedData); + return mimeData; +} + +bool ColoringRulesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + //clear any previous dragDrop information + dragDropRows_.clear(); + + if (action == Qt::IgnoreAction) + return true; + + if (!data->hasFormat(WiresharkMimeData::ColoringRulesMimeType) || column > 0) + return false; + + int beginRow; + + if (row != -1) + beginRow = row; + else if (parent.isValid()) + beginRow = parent.row(); + else + beginRow = rowCount(); + + QList rules; + + QJsonDocument encodedData = QJsonDocument::fromJson(data->data(WiresharkMimeData::ColoringRulesMimeType)); + if (! encodedData.isObject() || ! encodedData.object().contains("coloringrules")) + return false; + + QJsonArray dataArray = encodedData.object()["coloringrules"].toArray(); + + for (int datarow = 0; datarow < dataArray.count(); datarow++) + { + QJsonObject entry = dataArray.at(datarow).toObject(); + + if (! entry.contains("foreground") || ! entry.contains("background") || ! entry.contains("filter")) + continue; + + QColor fgColor = entry["foreground"].toVariant().value(); + QColor bgColor = entry["background"].toVariant().value(); + + ColoringRuleItem * item = new ColoringRuleItem( + entry["disabled"].toVariant().toBool(), + entry["name"].toString(), + entry["filter"].toString(), + fgColor, + bgColor, + root_); + rules.append(VariantPointer::asQVariant(item)); + } + + insertRows(beginRow, static_cast(rules.count()), QModelIndex()); + for (int i = 0; i < rules.count(); i++) { + QModelIndex idx = index(beginRow, 0, QModelIndex()); + setData(idx, rules[i], Qt::UserRole); + beginRow++; + } + + return true; +} + +QModelIndex ColoringRulesModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + ColoringRuleItem *parent_item, *child_item; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + Q_ASSERT(parent_item); + + child_item = parent_item->child(row); + if (child_item) { + return createIndex(row, column, child_item); + } + + return QModelIndex(); +} + +QModelIndex ColoringRulesModel::parent(const QModelIndex& indexItem) const +{ + if (!indexItem.isValid()) + return QModelIndex(); + + ColoringRuleItem* item = static_cast(indexItem.internalPointer()); + if (item != NULL) { + ColoringRuleItem* parent_item = item->parentItem(); + if (parent_item != NULL) { + if (parent_item == root_) + return QModelIndex(); + + return createIndex(parent_item->row(), 0, parent_item); + } + } + + return QModelIndex(); +} + +int ColoringRulesModel::rowCount(const QModelIndex& parent) const +{ + ColoringRuleItem *parent_item; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + if (parent_item == NULL) + return 0; + + return parent_item->childCount(); +} + +int ColoringRulesModel::columnCount(const QModelIndex&) const +{ + return colColoringRulesMax; +} diff --git a/ui/qt/models/coloring_rules_model.h b/ui/qt/models/coloring_rules_model.h new file mode 100644 index 00000000..f51a1a30 --- /dev/null +++ b/ui/qt/models/coloring_rules_model.h @@ -0,0 +1,102 @@ +/** @file + * + * Data model for coloring rules. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COLORING_RULES_MODEL_H +#define COLORING_RULES_MODEL_H + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +class ColoringRuleItem : public ModelHelperTreeItem +{ +public: + ColoringRuleItem(bool disabled, QString name, QString filter, QColor foreground, QColor background, ColoringRuleItem* parent); + virtual ~ColoringRuleItem(); + + ColoringRuleItem(color_filter_t *colorf, ColoringRuleItem* parent); + ColoringRuleItem(const ColoringRuleItem& item); + + bool disabled_; + QString name_; + QString filter_; + QColor foreground_; + QColor background_; + + ColoringRuleItem& operator=(ColoringRuleItem& rhs); + +}; + +class ColoringRulesModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + ColoringRulesModel(QColor defaultForeground, QColor defaultBackground, QObject *parent); + virtual ~ColoringRulesModel(); + + enum ColoringRulesColumn { + colName = 0, + colFilter, + colColoringRulesMax + }; + + void addColor(color_filter_t* colorf); + void addColor(bool disabled, QString filter, QColor foreground, QColor background); + bool importColors(QString filename, QString& err); + bool exportColors(QString filename, QString& err); + bool writeColors(QString& err); + + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, + const QModelIndex & = QModelIndex()) const; + QModelIndex parent(const QModelIndex &) const; + + //Drag & drop functionality + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData* mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool copyRow(int dst_row, int src_row); + +private: + void populate(); + struct _GSList *createColorFilterList(); + + ColoringRuleItem* root_; + //Save off the conversation colors, do not include in dialog + struct _GSList *conversation_colors_; + + QColor defaultForeground_; + QColor defaultBackground_; + + QList dragDropRows_; +}; + +#endif // COLORING_RULES_MODEL_H diff --git a/ui/qt/models/column_list_model.cpp b/ui/qt/models/column_list_model.cpp new file mode 100644 index 00000000..a0e5c8ba --- /dev/null +++ b/ui/qt/models/column_list_model.cpp @@ -0,0 +1,518 @@ +/* column_list_models.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +struct ListElement +{ + QString title; + QString customFields; + int nr; + int type; + int originalType; + int occurrence; + bool displayed; + bool resolved; +}; + +static QList store_; + +ColumnProxyModel::ColumnProxyModel(QObject * parent) : + QSortFilterProxyModel(parent), + showDisplayedOnly_(false) +{} + +bool ColumnProxyModel::filterAcceptsRow(int source_row, const QModelIndex &/*source_parent*/) const +{ + bool displayed = false; + if (sourceModel() && + sourceModel()->index(source_row, ColumnListModel::COL_DISPLAYED).data(ColumnListModel::DisplayedState).toBool()) + displayed = true; + + if (showDisplayedOnly_ && ! displayed) + return false; + + return true; +} + +void ColumnProxyModel::setShowDisplayedOnly(bool set) +{ + showDisplayedOnly_ = set; + invalidateFilter(); +} + +ColumnTypeDelegate::ColumnTypeDelegate(QObject * parent) : + QStyledItemDelegate(parent) +{} + +QWidget *ColumnTypeDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *editor = nullptr; + + if (index.column() == ColumnListModel::COL_TYPE) + { + QComboBox *cb_editor = new QComboBox(parent); + + for (int i = 0; i < NUM_COL_FMTS; i++) + { + cb_editor->addItem(col_format_desc(i), QVariant(i)); + if (i == index.data().toInt()) + cb_editor->setCurrentIndex(i); + } + + cb_editor->setFrame(false); + editor = cb_editor; + } + else if (index.column() == ColumnListModel::COL_FIELDS) + { + FieldFilterEdit * ff_editor = new FieldFilterEdit(parent); + connect(ff_editor, &FieldFilterEdit::textChanged, ff_editor, &FieldFilterEdit::checkCustomColumn); + ff_editor->setText(index.data().toString()); + editor = ff_editor; + } + else if (index.column() == ColumnListModel::COL_OCCURRENCE) + { + SyntaxLineEdit * sl_editor = new SyntaxLineEdit(parent); + connect(sl_editor, &SyntaxLineEdit::textChanged, sl_editor, &SyntaxLineEdit::checkInteger); + sl_editor->setText(index.data().toString()); + editor = sl_editor; + } + + if (!editor) { + editor = QStyledItemDelegate::createEditor(parent, option, index); + } + editor->setAutoFillBackground(true); + return editor; +} + +void ColumnTypeDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + QVariant data = index.model()->data(index); + if (index.column() == ColumnListModel::COL_TYPE) + { + QComboBox *comboBox = static_cast(editor); + comboBox->setCurrentText(data.toString()); + } + else if (index.column() == ColumnListModel::COL_FIELDS) + { + if (qobject_cast(editor)) + qobject_cast(editor)->setText(data.toString()); + } + else if (index.column() == ColumnListModel::COL_OCCURRENCE) + { + if (qobject_cast(editor)) + qobject_cast(editor)->setText(data.toString()); + } + else + { + if (qobject_cast(editor)) + qobject_cast(editor)->setText(data.toString()); + } +} + +void ColumnTypeDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + if (index.column() == ColumnListModel::COL_TYPE) + { + QComboBox *comboBox = static_cast(editor); + bool ok = false; + int value = comboBox->currentData().toInt(&ok); + + if (ok) + model->setData(index, value, Qt::EditRole); + } + else if (index.column() == ColumnListModel::COL_FIELDS) + { + FieldFilterEdit * ffe = qobject_cast(editor); + if (ffe) + { + if (ffe->syntaxState() == SyntaxLineEdit::Valid) { + QModelIndex typeIndex = index.sibling(index.row(), ColumnListModel::COL_TYPE); + model->setData(typeIndex, COL_CUSTOM, Qt::EditRole); + model->setData(index, ffe->text(), Qt::EditRole); + } + else + { + ffe->setText(index.data().toString()); + } + } + + if (index.data().toString().length() == 0) + { + QModelIndex typeIndex = index.sibling(index.row(), ColumnListModel::COL_TYPE); + model->setData(typeIndex, index.data(ColumnListModel::OriginalType).toInt(), Qt::EditRole); + + } + } + else if (index.column() == ColumnListModel::COL_OCCURRENCE) + { + SyntaxLineEdit * sle = qobject_cast(editor); + bool ok = false; + if (sle) + { + sle->checkInteger(index.data().toString()); + if (sle->syntaxState() == SyntaxLineEdit::Valid) + ok = true; + } + + if (ok) + { + QModelIndex typeIndex = index.sibling(index.row(), ColumnListModel::COL_TYPE); + model->setData(typeIndex, COL_CUSTOM, Qt::EditRole); + model->setData(index, sle->text(), Qt::EditRole); + } + else if (sle) + { + sle->setText(index.data().toString()); + } + + if (index.data().toString().length() == 0) + { + QModelIndex typeIndex = index.sibling(index.row(), ColumnListModel::COL_TYPE); + model->setData(typeIndex, index.data(ColumnListModel::OriginalType).toInt(), Qt::EditRole); + + } + } + else + QStyledItemDelegate::setModelData(editor, model, index); +} + +void ColumnTypeDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, + const QModelIndex &/* index */) const +{ + editor->setGeometry(option.rect); +} + +ColumnListModel::ColumnListModel(QObject * parent): + QAbstractTableModel(parent) +{ + populate(); +} + +QVariant ColumnListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (section > ColumnListModel::COL_RESOLVED || orientation != Qt::Horizontal || + role != Qt::DisplayRole) + return QVariant(); + + return headerTitle(section); +} + +int ColumnListModel::rowCount(const QModelIndex &/*parent*/) const +{ + return static_cast(store_.count()); +} + +int ColumnListModel::columnCount(const QModelIndex &/*parent*/) const +{ + return ColumnListModel::COL_RESOLVED + 1; +} + +QString ColumnListModel::headerTitle(int section) const +{ + switch (section) + { + case ColumnListModel::COL_DISPLAYED: + return tr("Displayed"); + case ColumnListModel::COL_TITLE: + return tr("Title"); + case ColumnListModel::COL_TYPE: + return tr("Type"); + case ColumnListModel::COL_FIELDS: + return tr("Fields"); + case ColumnListModel::COL_OCCURRENCE: + return tr("Field Occurrence"); + case ColumnListModel::COL_RESOLVED: + return tr("Resolved"); + } + + return QString(); +} + +void ColumnListModel::populate() +{ + store_.clear(); + + int nr = 0; + + for (GList *cur = g_list_first(prefs.col_list); cur != NULL && cur->data != NULL; cur = cur->next) { + fmt_data *cfmt = (fmt_data *) cur->data; + ListElement ne; + ne.nr = nr; + ne.displayed = cfmt->visible; + ne.title = cfmt->title; + ne.type = ne.originalType = cfmt->fmt; + ne.customFields = cfmt->custom_fields; + ne.occurrence = cfmt->custom_occurrence; + ne.resolved = cfmt->resolved; + + nr++; + store_ << ne; + } +} + +QVariant ColumnListModel::data(const QModelIndex &index, int role) const +{ + if (! index.isValid() || index.column() >= store_.count()) + return QVariant(); + + ListElement ne = store_.at(index.row()); + + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case COL_DISPLAYED: + case COL_RESOLVED: + return QVariant(); + case ColumnListModel::COL_TITLE: + return ne.title; + case ColumnListModel::COL_TYPE: + return col_format_desc(ne.type); + case ColumnListModel::COL_FIELDS: + return ne.customFields; + case ColumnListModel::COL_OCCURRENCE: + return ne.customFields.length() > 0 ? QVariant::fromValue(ne.occurrence) : QVariant(); + } + } + else if (role == Qt::CheckStateRole) + { + if (index.column() == COL_DISPLAYED) + { + return ne.displayed ? Qt::Checked : Qt::Unchecked; + } + else if (index.column() == COL_RESOLVED) + { + QModelIndex fieldsIndex = index.sibling(index.row(), ColumnListModel::COL_FIELDS); + if (column_prefs_custom_resolve(fieldsIndex.data().toString().toUtf8().constData())) { + return ne.resolved ? Qt::Checked : Qt::Unchecked; + } + } + } + else if (role == Qt::ToolTipRole) + { + if (index.column() == COL_RESOLVED) + { + return tr("Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings."); + } + } + else if (role == OriginalType) + return QVariant::fromValue(ne.originalType); + else if (role == DisplayedState) + return QVariant::fromValue(ne.displayed); + + return QVariant(); +} + +Qt::ItemFlags ColumnListModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); + if (index.isValid() && index.row() < store_.count()) + { + ListElement ne = store_.at(index.row()); + + Qt::ItemFlags flags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + + if (index.column() == COL_DISPLAYED || index.column() == COL_RESOLVED) { + flags |= Qt::ItemIsUserCheckable; + } else { + flags |= Qt::ItemIsEditable; + } + + return flags; + } + else + return Qt::ItemIsDropEnabled | defaultFlags; +} + +QStringList ColumnListModel::mimeTypes() const +{ + return QStringList() << WiresharkMimeData::ColumnListMimeType; +} + +QMimeData *ColumnListModel::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData; + + int row = -1; + if (indexes.count() > 0) + row = indexes.at(0).row(); + + mimeData->setData(WiresharkMimeData::ColumnListMimeType, QString::number(row).toUtf8()); + return mimeData; +} + +bool ColumnListModel::canDropMimeData(const QMimeData *data, + Qt::DropAction /* action */, int /* row */, int /* column */, const QModelIndex &parent) const +{ + if (parent.isValid() || ! data->hasFormat(WiresharkMimeData::ColumnListMimeType)) + return false; + + return true; +} + +bool ColumnListModel::dropMimeData(const QMimeData *data, + Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + int moveTo; + + if (!canDropMimeData(data, action, row, column, parent)) + return false; + + if (action == Qt::IgnoreAction || parent.isValid()) + return true; + + if (row != -1) + moveTo = row; + else + moveTo = rowCount(QModelIndex()); + + bool ok = false; + int moveFrom = QString(data->data(WiresharkMimeData::ColumnListMimeType)).toInt(&ok); + if (! ok) + return false; + + if (moveFrom < moveTo) + moveTo = moveTo - 1; + + if (moveTo >= store_.count()) + moveTo = static_cast(store_.count()) - 1; + + beginResetModel(); + store_.move(moveFrom, moveTo); + endResetModel(); + + return true; +} + +Qt::DropActions ColumnListModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +bool ColumnListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || ! value.isValid()) + return false; + + bool change = false; + if (role == Qt::CheckStateRole && index.column() == ColumnListModel::COL_DISPLAYED) + { + store_[index.row()].displayed = value.toInt() == Qt::Checked ? true : false; + change = true; + } + else if (index.column() == ColumnListModel::COL_TYPE) + { + bool ok = false; + int val = value.toInt(&ok); + if (ok) + store_[index.row()].type = val; + } + else if (index.column() == ColumnListModel::COL_TITLE) + { + store_[index.row()].title = value.toString(); + } + else if (index.column() == ColumnListModel::COL_FIELDS) + { + store_[index.row()].customFields = value.toString(); + } + else if (index.column() == ColumnListModel::COL_OCCURRENCE) + { + bool ok = false; + int val = value.toInt(&ok); + if (ok) + store_[index.row()].occurrence = val; + } + else if (role == Qt::CheckStateRole && index.column() == ColumnListModel::COL_RESOLVED) + { + store_[index.row()].resolved = value.toInt() == Qt::Checked ? true : false; + } + + if (change) + emit dataChanged(index, index); + + return change; +} + +void ColumnListModel::saveColumns() +{ + GList *new_col_list = Q_NULLPTR; + + for (int row = 0; row < store_.count(); row++) + { + fmt_data * cfmt = g_new0(fmt_data, 1); + ListElement elem = store_.at(row); + + cfmt->title = qstring_strdup(elem.title); + cfmt->visible = elem.displayed; + cfmt->fmt = elem.type; + cfmt->resolved = TRUE; + if (cfmt->fmt == COL_CUSTOM) + { + cfmt->custom_fields = qstring_strdup(elem.customFields); + cfmt->custom_occurrence = elem.occurrence; + cfmt->resolved = elem.resolved; + } + + new_col_list = g_list_append(new_col_list, cfmt); + } + + while (prefs.col_list) + column_prefs_remove_link(prefs.col_list); + + prefs.col_list = new_col_list; +} + +void ColumnListModel::addEntry() +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + ListElement elem; + elem.nr = rowCount(); + elem.title = tr("New Column"); + elem.displayed = true; + elem.type = elem.originalType = COL_NUMBER; + elem.occurrence = 0; + elem.customFields = QString(); + elem.resolved = true; + store_ << elem; + endInsertRows(); +} + +void ColumnListModel::deleteEntry(int row) +{ + beginRemoveRows(QModelIndex(), row, row); + store_.removeAt(row); + endRemoveRows(); +} + +void ColumnListModel::reset() +{ + beginResetModel(); + populate(); + endResetModel(); +} diff --git a/ui/qt/models/column_list_model.h b/ui/qt/models/column_list_model.h new file mode 100644 index 00000000..c4739f96 --- /dev/null +++ b/ui/qt/models/column_list_model.h @@ -0,0 +1,96 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COLUMN_LIST_MODELS_H +#define COLUMN_LIST_MODELS_H + +#include +#include +#include +#include +#include + +class ColumnProxyModel : public QSortFilterProxyModel +{ +public: + ColumnProxyModel(QObject *parent = Q_NULLPTR); + + void setShowDisplayedOnly(bool set); + +protected: + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + +private: + bool showDisplayedOnly_; +}; + +class ColumnTypeDelegate : public QStyledItemDelegate +{ +public: + ColumnTypeDelegate(QObject * parent = Q_NULLPTR); + + QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const override; + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; +}; + +class ColumnListModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + ColumnListModel(QObject * parent = Q_NULLPTR); + + enum { + COL_DISPLAYED, + COL_TITLE, + COL_TYPE, + COL_FIELDS, + COL_OCCURRENCE, + COL_RESOLVED + }; + + enum { + OriginalType = Qt::UserRole, + DisplayedState + }; + + void saveColumns(); + + void addEntry(); + void deleteEntry(int row); + void reset(); + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + virtual QStringList mimeTypes() const; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + virtual Qt::DropActions supportedDropActions() const; + virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + +private: + QString headerTitle(int section) const; + + void populate(); +}; + +#endif // COLUMN_LIST_MODELS_H diff --git a/ui/qt/models/credentials_model.cpp b/ui/qt/models/credentials_model.cpp new file mode 100644 index 00000000..8bc649f1 --- /dev/null +++ b/ui/qt/models/credentials_model.cpp @@ -0,0 +1,155 @@ +/* + * credentials_model.h + * + * Copyright 2019 - Dario Lombardo + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "credentials_model.h" + +#include +#include + +CredentialsModel::CredentialsModel(QObject *parent) + :QAbstractListModel(parent) +{ +} + +CredentialsModel::~CredentialsModel() +{ + clear(); +} + +int CredentialsModel::rowCount(const QModelIndex &) const +{ + return static_cast(credentials_.count()); +} + +int CredentialsModel::columnCount(const QModelIndex &) const +{ + return COL_INFO + 1; +} + +QVariant CredentialsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + tap_credential_t * auth = credentials_.at(index.row()); + if (!auth) + return QVariant(); + + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case COL_NUM: + return QVariant::fromValue(auth->num); + case COL_PROTO: + return QString(auth->proto); + case COL_USERNAME: + return QString(auth->username); + case COL_INFO: + return QString(auth->info); + default: + return QVariant(); + } + } + + if (role == Qt::UserRole) { + switch (index.column()) { + case COL_NUM: + if (auth->num > 0) + return QVariant::fromValue(auth->num); + break; + case COL_USERNAME: + if (auth->username_num > 0) + return QVariant::fromValue(auth->username_num); + break; + default: + return QVariant(); + } + } + + if (role == CredentialsModel::ColumnHFID) + return QVariant::fromValue(auth->password_hf_id); + + if (role == Qt::ToolTipRole) { + const QString select_msg(tr("Click to select the packet")); + switch (index.column()) { + case COL_NUM: + if (auth->num > 0) + return select_msg; + break; + case COL_USERNAME: + if (auth->username_num > 0) { + if (auth->username_num != auth->num) + return QString(tr("Click to select the packet with username")); + else + return select_msg; + } else { + return QString(tr("Username not available")); + } + break; + default: + return QVariant(); + } + } + + return QVariant(); +} + +void CredentialsModel::addRecord(const tap_credential_t* auth) +{ + emit beginInsertRows(QModelIndex(), rowCount(), rowCount() + 1); + + tap_credential_t* clone = new tap_credential_t; + clone->num = auth->num; + clone->username_num = auth->username_num; + clone->password_hf_id = auth->password_hf_id; + clone->username = qstring_strdup(auth->username); + clone->proto = auth->proto; + clone->info = qstring_strdup(auth->info); + credentials_.append(clone); + + emit endInsertRows(); +} + +void CredentialsModel::clear() +{ + if (!credentials_.isEmpty()) { + emit beginRemoveRows(QModelIndex(), 0, rowCount() - 1); + for (QList::iterator itr = credentials_.begin(); itr != credentials_.end(); ++itr) { + g_free((*itr)->username); + g_free((*itr)->info); + delete *itr; + } + credentials_.clear(); + emit endRemoveRows(); + } +} + +QVariant CredentialsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + switch (section) { + case COL_NUM: + return QString(tr("Packet No.")); + case COL_PROTO: + return QString(tr("Protocol")); + case COL_USERNAME: + return QString(tr("Username")); + case COL_INFO: + return QString(tr("Additional Info")); + } + } + + return QVariant(); +} diff --git a/ui/qt/models/credentials_model.h b/ui/qt/models/credentials_model.h new file mode 100644 index 00000000..cc0f4d3d --- /dev/null +++ b/ui/qt/models/credentials_model.h @@ -0,0 +1,53 @@ +/** @file + * + * Copyright 2019 - Dario Lombardo + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CREDENTIALS_MODELS_H +#define CREDENTIALS_MODELS_H + +#include +#include + +#include +#include +#include + +class CredentialsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + CredentialsModel(QObject *parent); + ~CredentialsModel(); + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const ; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void addRecord(const tap_credential_t *rec); + void clear(); + + enum { + COL_NUM, + COL_PROTO, + COL_USERNAME, + COL_INFO + }; + + enum { + ColumnHFID = Qt::UserRole + 1 + }; + +private: + QList credentials_; + +}; + +#endif // CREDENTIALS_MODELS_H diff --git a/ui/qt/models/decode_as_delegate.cpp b/ui/qt/models/decode_as_delegate.cpp new file mode 100644 index 00000000..3c300a79 --- /dev/null +++ b/ui/qt/models/decode_as_delegate.cpp @@ -0,0 +1,390 @@ +/* decode_as_delegate.cpp + * Delegates for editing various field types in a Decode As record. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "decode_as_delegate.h" + +#include "epan/decode_as.h" +#include "epan/epan_dissect.h" + +#include + +#include +#include +#include +#include + +typedef struct _dissector_info_t { + QString proto_name; + dissector_handle_t dissector_handle; +} dissector_info_t; + +Q_DECLARE_METATYPE(dissector_info_t *) + +DecodeAsDelegate::DecodeAsDelegate(QObject *parent, capture_file *cf) + : QStyledItemDelegate(parent), + cap_file_(cf) +{ + cachePacketProtocols(); +} + +DecodeAsItem* DecodeAsDelegate::indexToField(const QModelIndex &index) const +{ + const QVariant v = index.model()->data(index, Qt::UserRole); + return static_cast(v.value()); +} + +void DecodeAsDelegate::cachePacketProtocols() +{ + //cache the list of potential decode as protocols in the current packet + if (cap_file_ && cap_file_->edt) { + + wmem_list_frame_t * protos = wmem_list_head(cap_file_->edt->pi.layers); + guint8 curr_layer_num = 1; + + while (protos != NULL) { + int proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); + const gchar * proto_name = proto_get_protocol_filter_name(proto_id); + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (g_strcmp0(proto_name, entry->name) == 0) { + packet_proto_data_t proto_data; + + proto_data.table_ui_name = get_dissector_table_ui_name(entry->table_name); + proto_data.proto_name = proto_name; + proto_data.curr_layer_num = curr_layer_num; + + packet_proto_list_.append(proto_data); + } + } + protos = wmem_list_frame_next(protos); + curr_layer_num++; + } + } +} + +void DecodeAsDelegate::collectDAProtocols(QSet& all_protocols, QList& current_list) const +{ + // If a packet is selected group its tables at the top in order + // from last-dissected to first. + + //gather the initial list + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + const char *table_name = get_dissector_table_ui_name(entry->table_name); + if (table_name) { + all_protocols.insert(get_dissector_table_ui_name(entry->table_name)); + } + } + + //filter out those in selected packet + foreach(packet_proto_data_t proto, packet_proto_list_) + { + current_list.append(proto.table_ui_name); + all_protocols.remove(proto.table_ui_name); + } +} + +//Determine if there are multiple values in the selector field that would +//correspond to using a combo box +bool DecodeAsDelegate::isSelectorCombo(DecodeAsItem* item) const +{ + const gchar *proto_name = NULL; + + foreach(packet_proto_data_t proto, packet_proto_list_) + { + if (g_strcmp0(proto.table_ui_name, item->tableUIName()) == 0) { + proto_name = proto.proto_name; + break; + } + } + + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if ((g_strcmp0(proto_name, entry->name) == 0) && + (g_strcmp0(item->tableName(), entry->table_name) == 0) && + (cap_file_ && cap_file_->edt)) { + return true; + } + } + + return false; +} + +void DecodeAsDelegate::decodeAddProtocol(const gchar *, const gchar *proto_name, gpointer value, gpointer user_data) +{ + QList* proto_list = (QList*)user_data; + + if (!proto_list) + return; + + dissector_info_t *dissector_info = new dissector_info_t(); + dissector_info->proto_name = proto_name; + dissector_info->dissector_handle = (dissector_handle_t) value; + + proto_list->append(dissector_info); +} + +QWidget* DecodeAsDelegate::createEditor(QWidget *parentWidget, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + DecodeAsItem* item = indexToField(index); + QWidget *editor = nullptr; + + switch(index.column()) + { + case DecodeAsModel::colTable: + { + QComboBox *cb_editor = new QComboBox(parentWidget); + QSet da_set; + QList packet_list; + QString table_ui_name; + + collectDAProtocols(da_set, packet_list); + + cb_editor->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + //put the protocols from the packet first in the combo box + foreach (table_ui_name, packet_list) { + cb_editor->addItem(table_ui_name, table_ui_name); + } + if (packet_list.count() > 0) { + cb_editor->insertSeparator(static_cast(packet_list.count())); + } + + //put the rest of the protocols in the combo box + QList da_list = da_set.values(); + std::sort(da_list.begin(), da_list.end()); + + foreach (table_ui_name, da_list) { + cb_editor->addItem(table_ui_name, table_ui_name); + } + + //Make sure the combo box is at least as wide as the column + QTreeView* parentTree = (QTreeView*)parent(); + int protoColWidth = parentTree->columnWidth(index.column()); + if (protoColWidth > cb_editor->size().width()) + cb_editor->setFixedWidth(protoColWidth); + + editor = cb_editor; + break; + } + case DecodeAsModel::colSelector: + { + QComboBox *cb_editor = NULL; + const gchar *proto_name = NULL; + bool edt_present = cap_file_ && cap_file_->edt; + gint8 curr_layer_num_saved = edt_present ? cap_file_->edt->pi.curr_layer_num : 0; + QList proto_layers; + + foreach(packet_proto_data_t proto, packet_proto_list_) + { + if (g_strcmp0(proto.table_ui_name, item->tableUIName()) == 0) { + if (edt_present) { + proto_layers << proto.curr_layer_num; + } + proto_name = proto.proto_name; + } + } + + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if ((g_strcmp0(proto_name, entry->name) == 0) && + (g_strcmp0(item->tableName(), entry->table_name) == 0)) { + if (edt_present) { + //create a combobox to add the entries from the packet + cb_editor = new QComboBox(parentWidget); + + //Don't limit user to just what's in combo box + cb_editor->setEditable(true); + + cb_editor->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + //add the current value of the column + const QString& current_value = index.model()->data(index, Qt::EditRole).toString(); + if (!current_value.isEmpty()) + cb_editor->addItem(current_value); + + //get the value(s) from the packet + foreach(guint8 current_layer, proto_layers) { + cap_file_->edt->pi.curr_layer_num = current_layer; + for (uint ni = 0; ni < entry->num_items; ni++) { + if (entry->values[ni].num_values == 1) { // Skip over multi-value ("both") entries + QString entryStr = DecodeAsModel::entryString(entry->table_name, + entry->values[ni].build_values[0](&cap_file_->edt->pi)); + //don't duplicate entries + if (cb_editor->findText(entryStr) < 0) + cb_editor->addItem(entryStr); + } + } + } + cap_file_->edt->pi.curr_layer_num = curr_layer_num_saved; + cb_editor->setCurrentIndex(entry->default_index_value); + + //Make sure the combo box is at least as wide as the column + QTreeView* parentTree = (QTreeView*)parent(); + int protoColWidth = parentTree->columnWidth(index.column()); + if (protoColWidth > cb_editor->size().width()) + cb_editor->setFixedWidth(protoColWidth); + } + break; + } + } + + //if there isn't a need for a combobox, just let user have a text box for direct edit + if (cb_editor) { + editor = cb_editor; + } else { + editor = QStyledItemDelegate::createEditor(parentWidget, option, index); + } + break; + } + + case DecodeAsModel::colProtocol: + { + QComboBox *cb_editor = new QComboBox(parentWidget); + QList protocols; + + cb_editor->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (g_strcmp0(item->tableName(), entry->table_name) == 0) { + entry->populate_list(entry->table_name, decodeAddProtocol, &protocols); + break; + } + } + + // Sort by description in a human-readable way (case-insensitive, etc) + std::sort(protocols.begin(), protocols.end(), [](dissector_info_t* d1, dissector_info_t* d2) { + return d1->proto_name.localeAwareCompare(d2->proto_name) < 0; + }); + + cb_editor->addItem(DECODE_AS_NONE); + cb_editor->insertSeparator(cb_editor->count()); + + for (dissector_info_t* protocol : protocols) + { + // Make it easy to reset to the default dissector + if (protocol->proto_name == item->defaultDissector()) { + cb_editor->insertItem(0, protocol->proto_name, VariantPointer::asQVariant(protocol)); + } else { + cb_editor->addItem(protocol->proto_name, VariantPointer::asQVariant(protocol)); + } + } + + //Make sure the combo box is at least as wide as the column + QTreeView* parentTree = (QTreeView*)parent(); + int protoColWidth = parentTree->columnWidth(index.column()); + if (protoColWidth > cb_editor->size().width()) + cb_editor->setFixedWidth(protoColWidth); + + editor = cb_editor; + break; + } + } + + if (editor) { + editor->setAutoFillBackground(true); + } + return editor; +} + +void DecodeAsDelegate::destroyEditor(QWidget *editor, const QModelIndex &index) const +{ + if (index.column() == DecodeAsModel::colProtocol) { + QComboBox *cb_editor = (QComboBox*)editor; + for (int i=0; i < cb_editor->count(); ++i) { + delete VariantPointer::asPtr(cb_editor->itemData(i)); + } + } + QStyledItemDelegate::destroyEditor(editor, index); +} + +void DecodeAsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + DecodeAsItem* item = indexToField(index); + + switch(index.column()) + { + case DecodeAsModel::colTable: + case DecodeAsModel::colProtocol: + { + QComboBox *combobox = static_cast(editor); + const QString &data = index.model()->data(index, Qt::EditRole).toString(); + combobox->setCurrentText(data); + } + break; + case DecodeAsModel::colSelector: + if (isSelectorCombo(item)) { + QComboBox *combobox = static_cast(editor); + const QString &data = index.model()->data(index, Qt::EditRole).toString(); + combobox->setCurrentText(data); + } + else { + QStyledItemDelegate::setEditorData(editor, index); + } + break; + default: + QStyledItemDelegate::setEditorData(editor, index); + break; + } +} + +void DecodeAsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + DecodeAsItem* item = indexToField(index); + + switch(index.column()) + { + case DecodeAsModel::colTable: + { + QComboBox *combobox = static_cast(editor); + const QString &data = combobox->currentText(); + model->setData(index, data, Qt::EditRole); + break; + } + case DecodeAsModel::colSelector: + if (isSelectorCombo(item)) { + QComboBox *combobox = static_cast(editor); + const QString &data = combobox->currentText(); + model->setData(index, data, Qt::EditRole); + } else { + QStyledItemDelegate::setModelData(editor, model, index); + } + break; + case DecodeAsModel::colProtocol: + { + QComboBox *combobox = static_cast(editor); + + //set the dissector handle + QVariant var = combobox->itemData(combobox->currentIndex()); + dissector_info_t* dissector_info = VariantPointer::asPtr(var); + if (dissector_info != NULL) { + model->setData(index, VariantPointer::asQVariant(dissector_info->dissector_handle), Qt::EditRole); + } else { + model->setData(index, QVariant(), Qt::EditRole); + } + break; + } + default: + QStyledItemDelegate::setModelData(editor, model, index); + break; + } +} + +#if 0 +// Qt docs suggest overriding updateEditorGeometry, but the defaults seem sane. +void UatDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyledItemDelegate::updateEditorGeometry(editor, option, index); +} +#endif diff --git a/ui/qt/models/decode_as_delegate.h b/ui/qt/models/decode_as_delegate.h new file mode 100644 index 00000000..d0457764 --- /dev/null +++ b/ui/qt/models/decode_as_delegate.h @@ -0,0 +1,59 @@ +/** @file + * + * Delegates for editing various field types in a Decode As record. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DECODE_AS_DELEGATE_H +#define DECODE_AS_DELEGATE_H + +#include +#include + +#include "cfile.h" + +#include +#include +#include +#include + +typedef struct _packet_proto_data_t { + const gchar* proto_name; + const gchar* table_ui_name; + guint8 curr_layer_num; +} packet_proto_data_t; + +class DecodeAsDelegate : public QStyledItemDelegate +{ +public: + DecodeAsDelegate(QObject *parent = 0, capture_file *cf = NULL); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + void destroyEditor(QWidget *editor, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const override; + +#if 0 + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; +#endif + +private: + DecodeAsItem *indexToField(const QModelIndex &index) const; + void collectDAProtocols(QSet& all_protocols, QList& current_list) const; + void cachePacketProtocols(); + bool isSelectorCombo(DecodeAsItem* item) const; + + static void decodeAddProtocol(const gchar *table_name, const gchar *proto_name, gpointer value, gpointer user_data); + + capture_file *cap_file_; + QList packet_proto_list_; +}; +#endif // DECODE_AS_DELEGATE_H diff --git a/ui/qt/models/decode_as_model.cpp b/ui/qt/models/decode_as_model.cpp new file mode 100644 index 00000000..67e98b75 --- /dev/null +++ b/ui/qt/models/decode_as_model.cpp @@ -0,0 +1,849 @@ +/* decode_as_model.cpp + * Data model for Decode As records. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "decode_as_model.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +static const char *DEFAULT_TABLE = "tcp.port"; // Arbitrary +static const char *DEFAULT_UI_TABLE = "TCP port"; // Arbitrary + +DecodeAsItem::DecodeAsItem(const char* table_name, gconstpointer selector) : + tableName_(DEFAULT_TABLE), + tableUIName_(DEFAULT_UI_TABLE), + selectorUint_(0), + selectorString_(""), + selectorDCERPC_(NULL), + default_dissector_(DECODE_AS_NONE), + current_dissector_(DECODE_AS_NONE), + dissector_handle_(NULL) +{ + if (table_name == nullptr) + return; + + init(table_name, selector); +} + +DecodeAsItem::DecodeAsItem(const decode_as_t *entry, gconstpointer selector) : + tableName_(DEFAULT_TABLE), + tableUIName_(DEFAULT_UI_TABLE), + selectorUint_(0), + selectorString_(""), + selectorDCERPC_(NULL), + default_dissector_(DECODE_AS_NONE), + current_dissector_(DECODE_AS_NONE), + dissector_handle_(NULL) +{ + if (entry == nullptr) + return; + + init(entry->table_name, selector); +} + +DecodeAsItem::~DecodeAsItem() +{ +} + +void DecodeAsItem::init(const char* table_name, gconstpointer selector) +{ + tableName_ = table_name; + tableUIName_ = get_dissector_table_ui_name(tableName_); + + dissector_handle_t default_handle = NULL; + ftenum_t selector_type = get_dissector_table_selector_type(tableName_); + if (FT_IS_STRING(selector_type)) { + if (selector != NULL) { + default_handle = dissector_get_default_string_handle(tableName_, (const gchar*)selector); + selectorString_ = QString((const char*)selector); + } + } else if (FT_IS_UINT(selector_type)) { + if (selector != NULL) { + selectorUint_ = GPOINTER_TO_UINT(selector); + default_handle = dissector_get_default_uint_handle(tableName_, selectorUint_); + } + } else if (selector_type == FT_NONE) { + // There is no default for an FT_NONE dissector table + } else if (selector_type == FT_GUID) { + /* Special handling for DCE/RPC dissectors */ + if (strcmp(tableName_, DCERPC_TABLE_NAME) == 0) { + selectorDCERPC_ = (decode_dcerpc_bind_values_t*)(selector); + } + } + + if (default_handle != NULL) { + default_dissector_ = dissector_handle_get_description(default_handle); + // When adding a new record, we set the "current" values equal to + // the default, so the user can easily reset the value. + // The existing value read from the prefs file should already + // be added to the table from reading the prefs file. + // When reading existing values the current dissector should be + // set explicitly to the actual current value. + current_dissector_ = default_dissector_; + dissector_handle_ = default_handle; + } +} + +void DecodeAsItem::setTable(const decode_as_t *entry) +{ + if (entry == nullptr) + return; + + tableName_ = entry->table_name; + tableUIName_ = get_dissector_table_ui_name(entry->table_name); + + /* XXX: Should the selector values be reset (e.g., to 0 and "") + * What if someone tries to change the table to the DCERPC table? + * That doesn't really work without the DCERPC special handling. + */ + + updateHandles(); +} + +void DecodeAsItem::setSelector(const QString &value) +{ + ftenum_t selector_type = get_dissector_table_selector_type(tableName_); + + if (FT_IS_STRING(selector_type)) { + selectorString_ = value; + } else if (FT_IS_UINT(selector_type)) { + selectorUint_ = value.toUInt(Q_NULLPTR, 0); + } + + updateHandles(); +} + +void DecodeAsItem::setDissectorHandle(dissector_handle_t handle) +{ + dissector_handle_ = handle; + if (handle == nullptr) { + current_dissector_ = DECODE_AS_NONE; + } else { + current_dissector_ = dissector_handle_get_description(handle); + } +} + +void DecodeAsItem::updateHandles() +{ + ftenum_t selector_type = get_dissector_table_selector_type(tableName_); + dissector_handle_t default_handle = nullptr; + + if (FT_IS_STRING(selector_type)) { + default_handle = dissector_get_default_string_handle(tableName_, qUtf8Printable(selectorString_)); + } else if (FT_IS_UINT(selector_type)) { + default_handle = dissector_get_default_uint_handle(tableName_, selectorUint_); + } + if (default_handle != nullptr) { + default_dissector_ = dissector_handle_get_description(default_handle); + } else { + default_dissector_ = DECODE_AS_NONE; + } +} + +DecodeAsModel::DecodeAsModel(QObject *parent, capture_file *cf) : + QAbstractTableModel(parent), + cap_file_(cf) +{ +} + +DecodeAsModel::~DecodeAsModel() +{ + foreach(DecodeAsItem* item, decode_as_items_) + delete item; + decode_as_items_.clear(); +} + +Qt::ItemFlags DecodeAsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemFlags(); + + DecodeAsItem* item = decode_as_items_[index.row()]; + + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + switch(index.column()) + { + case DecodeAsModel::colTable: + case DecodeAsModel::colProtocol: + flags |= Qt::ItemIsEditable; + break; + case DecodeAsModel::colSelector: + { + ftenum_t selector_type = get_dissector_table_selector_type(item->tableName()); + if ((selector_type != FT_NONE) && + (item->selectorDCERPC() == NULL)) + flags |= Qt::ItemIsEditable; + break; + } + } + + return flags; +} + +QVariant DecodeAsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + DecodeAsItem* item; + + switch (role) + { + case Qt::ToolTipRole: + switch (index.column()) + { + case colTable: + return tr("Match using this field"); + case colSelector: + return tr("Change behavior when the field matches this value"); + case colType: + return tr("Field value type (and base, if Integer)"); + case colDefault: + return tr("Default \"Decode As\" behavior"); + case colProtocol: + return tr("Current\"Decode As\" behavior"); + } + return QVariant(); + case Qt::DisplayRole: + case Qt::EditRole: + item = decode_as_items_[index.row()]; + if (item == NULL) + return QVariant(); + + switch (index.column()) + { + case colTable: + return item->tableUIName(); + case colSelector: + { + ftenum_t selector_type = get_dissector_table_selector_type(item->tableName()); + if (FT_IS_UINT(selector_type)) { + return entryString(item->tableName(), GUINT_TO_POINTER(item->selectorUint())); + } else if (FT_IS_STRING(selector_type)) { + return entryString(item->tableName(), (gconstpointer)item->selectorString().toUtf8().constData()); + } else if (selector_type == FT_GUID) { + if (item->selectorDCERPC() != NULL) { + return item->selectorDCERPC()->ctx_id; + } + } + + return DECODE_AS_NONE; + } + case colType: + { + ftenum_t selector_type = get_dissector_table_selector_type(item->tableName()); + + if (FT_IS_STRING(selector_type)) { + return tr("String"); + } else if (FT_IS_UINT(selector_type)) { + QString type_desc = tr("Integer, base "); + switch (get_dissector_table_param(item->tableName())) { + case BASE_OCT: + type_desc.append("8"); + break; + case BASE_DEC: + type_desc.append("10"); + break; + case BASE_HEX: + type_desc.append("16"); + break; + default: + type_desc.append(tr("unknown")); + } + return type_desc; + } else if (selector_type == FT_NONE) { + return tr(""); + } else if (selector_type == FT_GUID) { + if (item->selectorDCERPC() != NULL) { + return QString("ctx_id"); + } else { + return tr("GUID"); + } + } + break; + } + case colDefault: + return item->defaultDissector(); + case colProtocol: + return item->currentDissector(); + } + return QVariant(); + + case Qt::UserRole: + item = decode_as_items_[index.row()]; + return QVariant::fromValue(static_cast(item)); + } + + return QVariant(); +} + +QVariant DecodeAsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + + switch (section) { + case colTable: + return tr("Field"); + case colSelector: + return tr("Value"); + case colType: + return tr("Type"); + case colDefault: + return tr("Default"); + case colProtocol: + return tr("Current"); + default: + ws_assert_not_reached(); + } + + return QVariant(); +} + +int DecodeAsModel::rowCount(const QModelIndex &parent) const +{ + // there are no children + if (parent.isValid()) { + return 0; + } + + return static_cast(decode_as_items_.count()); +} + +int DecodeAsModel::columnCount(const QModelIndex &parent) const +{ + // there are no children + if (parent.isValid()) { + return 0; + } + + return colDecodeAsMax; +} + +bool DecodeAsModel::setData(const QModelIndex &cur_index, const QVariant &value, int role) +{ + if (!cur_index.isValid()) + return false; + + if (role != Qt::EditRole) + return false; + + if (data(cur_index, role) == value) { + // Data appears unchanged, do not do additional checks. + return true; + } + + DecodeAsItem* item = decode_as_items_[cur_index.row()]; + + switch(cur_index.column()) + { + case DecodeAsModel::colTable: + { + QString valueStr = value.toString(); + //grab the table values from the Decode As list because they are persistent + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (valueStr.compare(get_dissector_table_ui_name(entry->table_name)) == 0) { + item->setTable(entry); + //all other columns affected + emit dataChanged(index(cur_index.row(), colSelector), + index(cur_index.row(), colProtocol)); + break; + } + } + } + break; + case DecodeAsModel::colProtocol: + { + dissector_handle_t handle = VariantPointer::asPtr(value); + item->setDissectorHandle(handle); + break; + } + case DecodeAsModel::colSelector: + item->setSelector(value.toString()); + emit dataChanged(index(cur_index.row(), colDefault), + index(cur_index.row(), colProtocol)); + break; + } + + return true; +} + +bool DecodeAsModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + // support insertion of just one item for now. + if (count != 1 || row < 0 || row > rowCount()) + return false; + + beginInsertRows(QModelIndex(), row, row); + + DecodeAsItem* item = nullptr; + const decode_as_t *firstEntry = nullptr; + + if (cap_file_ && cap_file_->edt) { + // Populate the new Decode As item with the last protocol layer + // that can support Decode As and has a selector field for that + // present in the frame. + // + // XXX: This treats 0 (for UInts) and empty strings the same as + // the fields for the tables not being present at all. + + wmem_list_frame_t * protos = wmem_list_tail(cap_file_->edt->pi.layers); + gint8 curr_layer_num_saved = cap_file_->edt->pi.curr_layer_num; + guint8 curr_layer_num = wmem_list_count(cap_file_->edt->pi.layers); + + while (protos != NULL && item == nullptr) { + int proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); + const gchar * proto_name = proto_get_protocol_filter_name(proto_id); + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (g_strcmp0(proto_name, entry->name) == 0) { + if (firstEntry == nullptr) { + firstEntry = entry; + } + ftenum_t selector_type = get_dissector_table_selector_type(entry->table_name); + // Pick the first value in the packet for the current + // layer for the table + // XXX: What if the Decode As table supports multiple + // values, but the first possible one is 0/NULL? + cap_file_->edt->pi.curr_layer_num = curr_layer_num; + gpointer selector = entry->values[0].build_values[0](&cap_file_->edt->pi); + // FT_NONE tables don't need a value + if (selector != NULL || selector_type == FT_NONE) { + item = new DecodeAsItem(entry, selector); + break; + } + + } + } + protos = wmem_list_frame_prev(protos); + curr_layer_num--; + } + + cap_file_->edt->pi.curr_layer_num = curr_layer_num_saved; + } + + // If we didn't find an entry with a valid selector, create an entry + // from the last table with an empty selector, or an empty entry. + if (item == nullptr) { + item = new DecodeAsItem(firstEntry); + } + decode_as_items_ << item; + + endInsertRows(); + + return true; +} + +bool DecodeAsModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + if (count != 1 || row < 0 || row >= rowCount()) + return false; + + beginRemoveRows(QModelIndex(), row, row); + DecodeAsItem* item = decode_as_items_.takeAt(row); + delete item; + endRemoveRows(); + + return true; +} + +void DecodeAsModel::clearAll() +{ + if (rowCount() < 1) + return; + + beginResetModel(); + foreach(DecodeAsItem* item, decode_as_items_) + delete item; + decode_as_items_.clear(); + endResetModel(); +} + +bool DecodeAsModel::copyRow(int dst_row, int src_row) +{ + if (src_row < 0 || src_row >= rowCount() || dst_row < 0 || dst_row >= rowCount()) { + return false; + } + + DecodeAsItem* src = decode_as_items_[src_row]; + DecodeAsItem* dst = decode_as_items_[dst_row]; + + *dst = *src; + + QVector roles; + roles << Qt::EditRole << Qt::BackgroundRole; + emit dataChanged(index(dst_row, 0), index(dst_row, columnCount()), roles); + + return true; +} + +prefs_set_pref_e DecodeAsModel::readDecodeAsEntry(gchar *key, const gchar *value, void *private_data, gboolean) +{ + DecodeAsModel *model = (DecodeAsModel*)private_data; + if (model == NULL) + return PREFS_SET_OK; + + if (strcmp(key, DECODE_AS_ENTRY) != 0) { + return PREFS_SET_NO_SUCH_PREF; + } + + /* Parse into table, selector, initial, current */ + gchar **values = g_strsplit_set(value, ",", 4); + DecodeAsItem *item = nullptr; + + dissector_table_t dissector_table = find_dissector_table(values[0]); + + QString tableName(values[0]); + // Get the table values from the Decode As list because they are persistent + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (tableName.compare(entry->table_name) == 0) { + item = new DecodeAsItem(entry); + break; + } + } + + if (item == nullptr) { + g_strfreev(values); + return PREFS_SET_SYNTAX_ERR; + } + + QString selector(values[1]); + item->setSelector(selector); + + /* The value for the default dissector in the decode_as_entries file + * has no effect other than perhaps making the config file more + * informative when edited manually. + * We will actually display and reset to the programmatic default value. + */ + item->setDissectorHandle(dissector_table_get_dissector_handle(dissector_table, values[3])); + + model->decode_as_items_ << item; + g_strfreev(values); + + return PREFS_SET_OK; +} + +bool DecodeAsModel::copyFromProfile(QString filename, const gchar **err) +{ + FILE *fp = ws_fopen(filename.toUtf8().constData(), "r"); + + if (fp == NULL) { + *err = g_strerror(errno); + return false; + } + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + read_prefs_file(filename.toUtf8().constData(), fp, readDecodeAsEntry, this); + endInsertRows(); + + fclose(fp); + + return true; +} + +QString DecodeAsModel::entryString(const gchar *table_name, gconstpointer value) +{ + QString entry_str; + ftenum_t selector_type = get_dissector_table_selector_type(table_name); + + switch (selector_type) { + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + { + uint num_val = GPOINTER_TO_UINT(value); + switch (get_dissector_table_param(table_name)) { + + case BASE_DEC: + entry_str = QString::number(num_val); + break; + + case BASE_HEX: + int width; + switch (selector_type) { + case FT_UINT8: + width = 2; + break; + case FT_UINT16: + width = 4; + break; + case FT_UINT24: + width = 6; + break; + case FT_UINT32: + width = 8; + break; + + default: + ws_assert_not_reached(); + break; + } + entry_str = QString("%1").arg(int_to_qstring(num_val, width, 16)); + break; + + case BASE_OCT: + entry_str = "0" + QString::number(num_val, 8); + break; + } + break; + } + + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_STRINGZPAD: + case FT_STRINGZTRUNC: + entry_str = (const char *)value; + break; + + case FT_GUID: + //avoid the assert for now + break; + + case FT_NONE: + //doesn't really matter, just avoiding the assert + return "0"; + + default: + ws_assert_not_reached(); + break; + } + return entry_str; +} + +void DecodeAsModel::fillTable() +{ + decode_as_items_.clear(); + beginResetModel(); + + dissector_all_tables_foreach_changed(buildChangedList, this); + decode_dcerpc_add_show_list(buildDceRpcChangedList, this); + + endResetModel(); +} + +void DecodeAsModel::setDissectorHandle(const QModelIndex &index, dissector_handle_t dissector_handle) +{ + DecodeAsItem* item = decode_as_items_[index.row()]; + if (item != NULL) + item->setDissectorHandle(dissector_handle); +} + +void DecodeAsModel::buildChangedList(const gchar *table_name, ftenum_t, gpointer key, gpointer value, gpointer user_data) +{ + DecodeAsModel *model = (DecodeAsModel*)user_data; + if (model == NULL) + return; + + dissector_handle_t current_dh; + DecodeAsItem* item = new DecodeAsItem(table_name, key); + + current_dh = dtbl_entry_get_handle((dtbl_entry_t *)value); + item->setDissectorHandle(current_dh); + + model->decode_as_items_ << item; +} + +void DecodeAsModel::buildDceRpcChangedList(gpointer data, gpointer user_data) +{ + dissector_table_t sub_dissectors; + guid_key guid_val; + decode_dcerpc_bind_values_t *binding = (decode_dcerpc_bind_values_t *)data; + + DecodeAsModel *model = (DecodeAsModel*)user_data; + if (model == NULL) + return; + + DecodeAsItem* item = new DecodeAsItem(DCERPC_TABLE_NAME, binding); + + sub_dissectors = find_dissector_table(DCERPC_TABLE_NAME); + + guid_val.ver = binding->ver; + guid_val.guid = binding->uuid; + item->setDissectorHandle(dissector_get_guid_handle(sub_dissectors, &guid_val)); + + model->decode_as_items_ << item; +} + +typedef QPair UintPair; +typedef QPair CharPtrPair; + +void DecodeAsModel::gatherChangedEntries(const gchar *table_name, + ftenum_t selector_type, gpointer key, gpointer, gpointer user_data) +{ + DecodeAsModel *model = qobject_cast((DecodeAsModel*)user_data); + if (model == NULL) + return; + + switch (selector_type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + model->changed_uint_entries_ << UintPair(table_name, GPOINTER_TO_UINT(key)); + break; + case FT_NONE: + //need to reset dissector table, so this needs to be in a changed list, + //might as well be the uint one. + model->changed_uint_entries_ << UintPair(table_name, 0); + break; + + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_STRINGZPAD: + case FT_STRINGZTRUNC: + model->changed_string_entries_ << CharPtrPair(table_name, (const char *) key); + break; + default: + break; + } +} + +void DecodeAsModel::applyChanges() +{ + dissector_table_t sub_dissectors; + module_t *module; + pref_t* pref_value; + dissector_handle_t handle; + // Reset all dissector tables, then apply all rules from model. + + // We can't call g_hash_table_removed from g_hash_table_foreach, which + // means we can't call dissector_reset_{string,uint} from + // dissector_all_tables_foreach_changed. Collect changed entries in + // lists and remove them separately. + // + // If dissector_all_tables_remove_changed existed we could call it + // instead. + dissector_all_tables_foreach_changed(gatherChangedEntries, this); + foreach (UintPair uint_entry, changed_uint_entries_) { + /* Set "Decode As preferences" to default values */ + sub_dissectors = find_dissector_table(uint_entry.first); + handle = dissector_get_uint_handle(sub_dissectors, uint_entry.second); + if (handle != NULL) { + module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(handle))); + pref_value = prefs_find_preference(module, uint_entry.first); + if (pref_value != NULL) { + module->prefs_changed_flags |= prefs_get_effect_flags(pref_value); + reset_pref(pref_value); + } + } + + dissector_reset_uint(uint_entry.first, uint_entry.second); + } + changed_uint_entries_.clear(); + foreach (CharPtrPair char_ptr_entry, changed_string_entries_) { + dissector_reset_string(char_ptr_entry.first, char_ptr_entry.second); + } + changed_string_entries_.clear(); + + foreach(DecodeAsItem *item, decode_as_items_) { + decode_as_t *decode_as_entry; + + if (item->currentDissector().isEmpty()) { + continue; + } + + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_entry = (decode_as_t *) cur->data; + + if (!g_strcmp0(decode_as_entry->table_name, item->tableName())) { + + ftenum_t selector_type = get_dissector_table_selector_type(item->tableName()); + gconstpointer selector_value; + QByteArray byteArray; + + switch (selector_type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + selector_value = GUINT_TO_POINTER(item->selectorUint()); + break; + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_STRINGZPAD: + case FT_STRINGZTRUNC: + byteArray = item->selectorString().toUtf8(); + selector_value = (gconstpointer) byteArray.constData(); + break; + case FT_NONE: + //selector value is ignored, but dissector table needs to happen + selector_value = NULL; + break; + case FT_GUID: + if (item->selectorDCERPC() != NULL) { + selector_value = (gconstpointer)item->selectorDCERPC(); + } else { + //TODO: Support normal GUID dissector tables + selector_value = NULL; + } + break; + default: + continue; + } + + if ((item->currentDissector() == item->defaultDissector())) { + decode_as_entry->reset_value(decode_as_entry->table_name, selector_value); + sub_dissectors = find_dissector_table(decode_as_entry->table_name); + + /* For now, only numeric dissector tables can use preferences */ + if (FT_IS_UINT(dissector_table_get_type(sub_dissectors))) { + if (item->dissectorHandle() != NULL) { + module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(item->dissectorHandle()))); + pref_value = prefs_find_preference(module, decode_as_entry->table_name); + if (pref_value != NULL) { + module->prefs_changed_flags |= prefs_get_effect_flags(pref_value); + prefs_remove_decode_as_value(pref_value, item->selectorUint(), TRUE); + } + } + } + break; + } else { + decode_as_entry->change_value(decode_as_entry->table_name, selector_value, item->dissectorHandle(), item->currentDissector().toUtf8().constData()); + sub_dissectors = find_dissector_table(decode_as_entry->table_name); + + /* For now, only numeric dissector tables can use preferences */ + if (item->dissectorHandle() != NULL) { + if (FT_IS_UINT(dissector_table_get_type(sub_dissectors))) { + module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(item->dissectorHandle()))); + pref_value = prefs_find_preference(module, decode_as_entry->table_name); + if (pref_value != NULL) { + module->prefs_changed_flags |= prefs_get_effect_flags(pref_value); + prefs_add_decode_as_value(pref_value, item->selectorUint(), FALSE); + } + } + } + break; + } + } + } + } + prefs_apply_all(); +} diff --git a/ui/qt/models/decode_as_model.h b/ui/qt/models/decode_as_model.h new file mode 100644 index 00000000..cf280965 --- /dev/null +++ b/ui/qt/models/decode_as_model.h @@ -0,0 +1,120 @@ +/** @file + * + * Data model for Decode As records. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DECODE_AS_MODEL_H +#define DECODE_AS_MODEL_H + +#include +#include + +#include +#include + +#include "cfile.h" + +#include +#include +#include + +class DecodeAsItem +{ +public: + DecodeAsItem(const char *table_name = NULL, gconstpointer selector = NULL); + DecodeAsItem(const decode_as_t *entry, gconstpointer selector = NULL); + virtual ~DecodeAsItem(); + + const gchar* tableName() const { return tableName_; } + const gchar* tableUIName() const { return tableUIName_; } + uint selectorUint() const { return selectorUint_; } + QString selectorString() const { return selectorString_; } + decode_dcerpc_bind_values_t* selectorDCERPC() const { return selectorDCERPC_; } + QString defaultDissector() const { return default_dissector_; } + QString currentDissector() const { return current_dissector_; } + dissector_handle_t dissectorHandle() const { return dissector_handle_; } + void setTable(const decode_as_t *entry); + void setSelector(const QString &value); + void setDissectorHandle(dissector_handle_t handle); + + void updateHandles(); + +private: + void init(const char *table_name, gconstpointer selector = NULL); + + const gchar* tableName_; + const gchar* tableUIName_; + + //save our sanity and not have to worry about memory management + //between (lack of) persistent data in GUI and underlying data + uint selectorUint_; + QString selectorString_; + decode_dcerpc_bind_values_t* selectorDCERPC_; //for special handling of DCE/RPC + + QString default_dissector_; + QString current_dissector_; + dissector_handle_t dissector_handle_; +}; + +class DecodeAsModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + DecodeAsModel(QObject *parent, capture_file *cf = NULL); + virtual ~DecodeAsModel(); + + enum DecodeAsColumn { + colTable = 0, // aka "Field" (or dissector table like "TCP Port") + colSelector, // the actual table value (e.g., port number 80) + colType, // field type (e.g. "Integer, base 16") + colDefault, // aka "initial" protocol chosen by Wireshark + colProtocol, // aka "current" protocol selected by user + colDecodeAsMax //not used + }; + + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) 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; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + void fillTable(); + + void setDissectorHandle(const QModelIndex &index, dissector_handle_t dissector_handle); + + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + void clearAll(); + bool copyRow(int dst_row, int src_row); + bool copyFromProfile(QString filename, const gchar **err); + + static QString entryString(const gchar *table_name, gconstpointer value); + + void applyChanges(); + +protected: + static void buildChangedList(const gchar *table_name, ftenum_t selector_type, + gpointer key, gpointer value, gpointer user_data); + static void buildDceRpcChangedList(gpointer data, gpointer user_data); + static void gatherChangedEntries(const gchar *table_name, ftenum_t selector_type, + gpointer key, gpointer value, gpointer user_data); + static prefs_set_pref_e readDecodeAsEntry(gchar *key, const gchar *value, + void *user_data, gboolean return_range_errors); + +private: + capture_file *cap_file_; + QList decode_as_items_; + QList > changed_uint_entries_; + QList > changed_string_entries_; +}; + +#endif // DECODE_AS_MODEL_H diff --git a/ui/qt/models/dissector_tables_model.cpp b/ui/qt/models/dissector_tables_model.cpp new file mode 100644 index 00000000..73c757ec --- /dev/null +++ b/ui/qt/models/dissector_tables_model.cpp @@ -0,0 +1,434 @@ +/* dissector_tables_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +#include +#include "main_application.h" + +static const char* CUSTOM_TABLE_NAME = "Custom Tables"; +static const char* INTEGER_TABLE_NAME = "Integer Tables"; +static const char* STRING_TABLE_NAME = "String Tables"; +static const char* HEURISTIC_TABLE_NAME = "Heuristic Tables"; + +class IntegerTablesItem : public DissectorTablesItem +{ +public: + IntegerTablesItem(unsigned int value, QString dissectorDescription, DissectorTablesItem* parent); + virtual ~IntegerTablesItem(); + + virtual bool lessThan(DissectorTablesItem &right) const; + +protected: + unsigned int value_; +}; + + +DissectorTablesItem::DissectorTablesItem(QString tableName, QString dissectorDescription, DissectorTablesItem* parent) : + ModelHelperTreeItem(parent), + tableName_(tableName), + dissectorDescription_(dissectorDescription) +{ +} + +DissectorTablesItem::~DissectorTablesItem() +{ +} + +bool DissectorTablesItem::lessThan(DissectorTablesItem &right) const +{ + if (tableName().compare(right.tableName(), Qt::CaseInsensitive) < 0) + return true; + + return false; +} + + +IntegerTablesItem::IntegerTablesItem(unsigned int value, QString dissectorDescription, DissectorTablesItem* parent) + : DissectorTablesItem(QString("%1").arg(value), dissectorDescription, parent) + , value_(value) +{ +} + +IntegerTablesItem::~IntegerTablesItem() +{ +} + +bool IntegerTablesItem::lessThan(DissectorTablesItem &right) const +{ + if (value_ == ((IntegerTablesItem&)right).value_) { + return DissectorTablesItem::lessThan(right); + } + + if (value_ < ((IntegerTablesItem&)right).value_) { + return true; + } + + return false; +} + + + + + + + + +DissectorTablesModel::DissectorTablesModel(QObject *parent) : + QAbstractItemModel(parent), + root_(new DissectorTablesItem(QString("ROOT"), QString("ROOT"), NULL)) +{ + populate(); +} + +DissectorTablesModel::~DissectorTablesModel() +{ + delete root_; +} + +int DissectorTablesModel::rowCount(const QModelIndex &parent) const +{ + DissectorTablesItem *parent_item; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + if (parent_item == NULL) + return 0; + + return parent_item->childCount(); +} + +int DissectorTablesModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + +QModelIndex DissectorTablesModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return QModelIndex(); + + DissectorTablesItem* item = static_cast(index.internalPointer()); + if (item != NULL) { + DissectorTablesItem* parent_item = item->parentItem(); + if (parent_item != NULL) { + if (parent_item == root_) + return QModelIndex(); + + return createIndex(parent_item->row(), 0, parent_item); + } + } + + return QModelIndex(); +} + +QModelIndex DissectorTablesModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + DissectorTablesItem *parent_item, *child_item; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + Q_ASSERT(parent_item); + + child_item = parent_item->child(row); + if (child_item) { + return createIndex(row, column, child_item); + } + + return QModelIndex(); +} + +QVariant DissectorTablesModel::data(const QModelIndex &index, int role) const +{ + if ((!index.isValid()) || (role != Qt::DisplayRole)) + return QVariant(); + + DissectorTablesItem* item = static_cast(index.internalPointer()); + if (item == NULL) + return QVariant(); + + switch ((enum DissectorTablesColumn)index.column()) + { + case colTableName: + return item->tableName(); + case colDissectorDescription: + return item->dissectorDescription(); + default: + break; + } + + return QVariant(); +} + +static void gatherProtocolDecodes(const char *, ftenum_t selector_type, gpointer key, gpointer value, gpointer item_ptr) +{ + DissectorTablesItem* pdl_ptr = (DissectorTablesItem*)item_ptr; + if (pdl_ptr == NULL) + return; + + dtbl_entry_t *dtbl_entry = (dtbl_entry_t*)value; + dissector_handle_t handle = dtbl_entry_get_handle(dtbl_entry); + const QString dissector_description = dissector_handle_get_description(handle); + DissectorTablesItem *ti = NULL; + + switch (selector_type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + ti = new IntegerTablesItem(GPOINTER_TO_UINT(key), dissector_description, pdl_ptr); + pdl_ptr->prependChild(ti); + break; + + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_STRINGZPAD: + case FT_STRINGZTRUNC: + ti = new DissectorTablesItem((const char *)key, dissector_description, pdl_ptr); + pdl_ptr->prependChild(ti); + break; + + case FT_BYTES: + ti = new DissectorTablesItem(dissector_handle_get_description(handle), dissector_description, pdl_ptr); + pdl_ptr->prependChild(ti); + break; + + default: + break; + } +} + +struct tables_root +{ + DissectorTablesItem* custom_table; + DissectorTablesItem* integer_table; + DissectorTablesItem* string_table; +}; + +static void gatherTableNames(const char *short_name, const char *table_name, gpointer model_ptr) +{ + struct tables_root* tables = (struct tables_root*)model_ptr; + if (model_ptr == NULL) + return; + + ftenum_t selector_type = get_dissector_table_selector_type(short_name); + DissectorTablesItem *dt_ti = NULL; + + switch (selector_type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + dt_ti = new DissectorTablesItem(table_name, short_name, tables->integer_table); + tables->integer_table->prependChild(dt_ti); + break; + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_STRINGZPAD: + case FT_STRINGZTRUNC: + dt_ti = new DissectorTablesItem(table_name, short_name, tables->string_table); + tables->string_table->prependChild(dt_ti); + break; + case FT_BYTES: + dt_ti = new DissectorTablesItem(table_name, short_name, tables->custom_table); + tables->custom_table->prependChild(dt_ti); + break; + default: + // Assert? + return; + } + + dissector_table_foreach(short_name, gatherProtocolDecodes, dt_ti); +} + +static void gatherHeurProtocolDecodes(const char *, struct heur_dtbl_entry *dtbl_entry, gpointer list_ptr) +{ + DissectorTablesItem* hdl_ptr = (DissectorTablesItem*)list_ptr; + if (hdl_ptr == NULL) + return; + + if (dtbl_entry->protocol) { + QString longName = proto_get_protocol_long_name(dtbl_entry->protocol); + QString heurDisplayName = dtbl_entry->display_name; + if (! heurDisplayName.isEmpty()) + longName.append(QString(" (%1)").arg(heurDisplayName)); + + DissectorTablesItem *heur = new DissectorTablesItem(longName, proto_get_protocol_short_name(dtbl_entry->protocol), hdl_ptr); + hdl_ptr->prependChild(heur); + } +} + +static void gatherHeurTableNames(const char *table_name, heur_dissector_list *list, gpointer heur_tables) +{ + DissectorTablesItem* table = (DissectorTablesItem*)heur_tables; + if (table == NULL) + return; + + DissectorTablesItem *heur = new DissectorTablesItem(table_name, QString(""), table); + table->prependChild(heur); + + if (list) { + heur_dissector_table_foreach(table_name, gatherHeurProtocolDecodes, heur); + } +} + +void DissectorTablesModel::populate() +{ + beginResetModel(); + + struct tables_root tables; + + tables.custom_table = new DissectorTablesItem(tr(CUSTOM_TABLE_NAME), QString(""), root_); + root_->prependChild(tables.custom_table); + tables.integer_table = new DissectorTablesItem(tr(INTEGER_TABLE_NAME), QString(""), root_); + root_->prependChild(tables.integer_table); + tables.string_table = new DissectorTablesItem(tr(STRING_TABLE_NAME), QString(""), root_); + root_->prependChild(tables.string_table); + + dissector_all_tables_foreach_table(gatherTableNames, &tables, NULL); + + DissectorTablesItem* heuristic_table = new DissectorTablesItem(tr(HEURISTIC_TABLE_NAME), QString(""), root_); + root_->prependChild(heuristic_table); + + dissector_all_heur_tables_foreach_table(gatherHeurTableNames, heuristic_table, NULL); + + endResetModel(); +} + + + + + +DissectorTablesProxyModel::DissectorTablesProxyModel(QObject * parent) +: QSortFilterProxyModel(parent), +tableName_(tr("Table Type")), +dissectorDescription_(), +filter_() +{ +} + +QVariant DissectorTablesProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + + switch ((enum DissectorTablesModel::DissectorTablesColumn)section) { + case DissectorTablesModel::colTableName: + return tableName_; + case DissectorTablesModel::colDissectorDescription: + return dissectorDescription_; + default: + break; + } + } + return QVariant(); +} + +bool DissectorTablesProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + //Use DissectorTablesItem directly for better performance + DissectorTablesItem* left_item = static_cast(left.internalPointer()); + DissectorTablesItem* right_item = static_cast(right.internalPointer()); + + if ((left_item != NULL) && (right_item != NULL)) { + return left_item->lessThan(*right_item); + } + + return false; +} + +bool DissectorTablesProxyModel::filterAcceptItem(DissectorTablesItem& item) const +{ + if (filter_.isEmpty()) + return true; + + if (item.tableName().contains(filter_, Qt::CaseInsensitive) || item.dissectorDescription().contains(filter_, Qt::CaseInsensitive)) + return true; + + DissectorTablesItem *child_item; + for (int child_row = 0; child_row < item.childCount(); child_row++) + { + child_item = item.child(child_row); + if ((child_item != NULL) && (filterAcceptItem(*child_item))) + return true; + } + + return false; +} + +bool DissectorTablesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex nameIdx = sourceModel()->index(sourceRow, DissectorTablesModel::colTableName, sourceParent); + DissectorTablesItem* item = static_cast(nameIdx.internalPointer()); + if (item == NULL) + return false; + + if (filterAcceptItem(*item)) + return true; + + return false; +} + +void DissectorTablesProxyModel::setFilter(const QString& filter) +{ + filter_ = filter; + invalidateFilter(); +} + +void DissectorTablesProxyModel::adjustHeader(const QModelIndex ¤tIndex) +{ + tableName_ = tr("Table Type"); + dissectorDescription_ = QString(); + if (currentIndex.isValid() && currentIndex.parent().isValid()) { + QString table; + + if (currentIndex.parent().parent().isValid()) { + table = data(index(currentIndex.parent().parent().row(), DissectorTablesModel::colTableName), Qt::DisplayRole).toString(); + if ((table.compare(CUSTOM_TABLE_NAME) == 0) || + (table.compare(STRING_TABLE_NAME) == 0)) { + tableName_ = tr("String"); + dissectorDescription_ = tr("Dissector Description"); + } else if (table.compare(INTEGER_TABLE_NAME) == 0) { + tableName_ = tr("Integer"); + dissectorDescription_ = tr("Dissector Description"); + } else if (table.compare(HEURISTIC_TABLE_NAME) == 0) { + tableName_ = tr("Protocol"); + dissectorDescription_ = tr("Short Name"); + } + } else { + table = data(index(currentIndex.parent().row(), DissectorTablesModel::colTableName), Qt::DisplayRole).toString(); + if ((table.compare(CUSTOM_TABLE_NAME) == 0) || + (table.compare(INTEGER_TABLE_NAME) == 0) || + (table.compare(STRING_TABLE_NAME) == 0)) { + tableName_ = tr("Table Name"); + dissectorDescription_ = tr("Selector Name"); + } else if (table.compare(HEURISTIC_TABLE_NAME) == 0) { + tableName_ = tr("Protocol"); + dissectorDescription_ = tr("Short Name"); + } + } + } + + + emit headerDataChanged(Qt::Vertical, 0, 1); +} diff --git a/ui/qt/models/dissector_tables_model.h b/ui/qt/models/dissector_tables_model.h new file mode 100644 index 00000000..40d63f94 --- /dev/null +++ b/ui/qt/models/dissector_tables_model.h @@ -0,0 +1,89 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISSECTOR_TABLES_MODEL_H +#define DISSECTOR_TABLES_MODEL_H + +#include + +#include + +#include + +class DissectorTablesItem : public ModelHelperTreeItem +{ +public: + DissectorTablesItem(QString tableName, QString dissectorDescription, DissectorTablesItem* parent); + virtual ~DissectorTablesItem(); + + QString tableName() const {return tableName_;} + QString dissectorDescription() const {return dissectorDescription_;} + + virtual bool lessThan(DissectorTablesItem &right) const; + +protected: + QString tableName_; + QString dissectorDescription_; +}; + +class DissectorTablesModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit DissectorTablesModel(QObject * parent = Q_NULLPTR); + virtual ~DissectorTablesModel(); + + enum DissectorTablesColumn { + colTableName = 0, + colDissectorDescription, + colLast + }; + + QModelIndex index(int row, int column, + const QModelIndex & = QModelIndex()) const; + QModelIndex parent(const QModelIndex &) const; + QVariant data(const QModelIndex &index, int role) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + void populate(); + +private: + DissectorTablesItem* root_; +}; + +class DissectorTablesProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + explicit DissectorTablesProxyModel(QObject * parent = Q_NULLPTR); + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + void adjustHeader(const QModelIndex ¤tIndex); + void setFilter(const QString& filter); + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + bool filterAcceptItem(DissectorTablesItem& item) const; + +private: + + QString tableName_; + QString dissectorDescription_; + QString filter_; +}; + +#endif // DISSECTOR_TABLES_MODEL_H diff --git a/ui/qt/models/enabled_protocols_model.cpp b/ui/qt/models/enabled_protocols_model.cpp new file mode 100644 index 00000000..ecbc47a9 --- /dev/null +++ b/ui/qt/models/enabled_protocols_model.cpp @@ -0,0 +1,520 @@ +/* enabled_protocols_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include + +#include +#include "main_application.h" + +#include + +class ProtocolTreeItem : public EnabledProtocolItem +{ +public: + ProtocolTreeItem(protocol_t* proto, EnabledProtocolItem* parent) + : EnabledProtocolItem(proto_get_protocol_short_name(proto), proto_get_protocol_long_name(proto), proto_is_protocol_enabled(proto), parent), + proto_(proto) + { + + } + + virtual ~ProtocolTreeItem() {} + +protected: + virtual void applyValuePrivate(gboolean value) + { + if (! proto_can_toggle_protocol(proto_get_id(proto_))) { + return; + } + proto_set_decoding(proto_get_id(proto_), value); + } + +private: + protocol_t* proto_; +}; + +class HeuristicTreeItem : public EnabledProtocolItem +{ +public: + HeuristicTreeItem(heur_dtbl_entry_t *heuristic, EnabledProtocolItem* parent) + : EnabledProtocolItem(heuristic->short_name, heuristic->display_name, heuristic->enabled, parent), + heuristic_table_(heuristic) + { + type_ = EnabledProtocolItem::Heuristic; + } + + virtual ~HeuristicTreeItem() {} + +protected: + virtual void applyValuePrivate(gboolean value) + { + heuristic_table_->enabled = value; + } + +private: + heur_dtbl_entry_t *heuristic_table_; +}; + + +EnabledProtocolItem::EnabledProtocolItem(QString name, QString description, bool enabled, EnabledProtocolItem* parent) : + ModelHelperTreeItem(parent), + name_(name), + description_(description), + enabled_(enabled), + enabledInit_(enabled), + type_(EnabledProtocolItem::Standard) +{ +} + +EnabledProtocolItem::~EnabledProtocolItem() +{ +} + +EnabledProtocolItem::EnableProtocolType EnabledProtocolItem::type() const +{ + return type_; +} + +bool EnabledProtocolItem::applyValue() +{ + if (enabledInit_ != enabled_) { + applyValuePrivate(enabled_); + return true; + } + + return false; +} + + + + +EnabledProtocolsModel::EnabledProtocolsModel(QObject *parent) : + QAbstractItemModel(parent), + root_(new ProtocolTreeItem(NULL, NULL)) +{ +} + +EnabledProtocolsModel::~EnabledProtocolsModel() +{ + delete root_; +} + +int EnabledProtocolsModel::rowCount(const QModelIndex &parent) const +{ + EnabledProtocolItem *parent_item; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + if (parent_item == NULL) + return 0; + + return parent_item->childCount(); +} + +int EnabledProtocolsModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + +QVariant EnabledProtocolsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + + switch ((enum EnabledProtocolsColumn)section) { + case colProtocol: + return tr("Protocol"); + case colDescription: + return tr("Description"); + default: + break; + } + } + return QVariant(); +} + +QModelIndex EnabledProtocolsModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return QModelIndex(); + + EnabledProtocolItem* item = static_cast(index.internalPointer()); + if (item != NULL) { + EnabledProtocolItem* parent_item = item->parentItem(); + if (parent_item != NULL) { + if (parent_item == root_) + return QModelIndex(); + + return createIndex(parent_item->row(), 0, parent_item); + } + } + + return QModelIndex(); +} + +QModelIndex EnabledProtocolsModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + EnabledProtocolItem *parent_item, *child_item; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + Q_ASSERT(parent_item); + + child_item = parent_item->child(row); + if (child_item) { + return createIndex(row, column, child_item); + } + + return QModelIndex(); +} + +Qt::ItemFlags EnabledProtocolsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemFlags(); + + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + switch(index.column()) + { + case colProtocol: + flags |= Qt::ItemIsUserCheckable; + break; + default: + break; + } + + return flags; +} + +QVariant EnabledProtocolsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + EnabledProtocolItem* item = static_cast(index.internalPointer()); + if (item == NULL) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + switch ((enum EnabledProtocolsColumn)index.column()) + { + case colProtocol: + return item->name(); + case colDescription: + return item->description(); + default: + break; + } + break; + case Qt::CheckStateRole: + switch ((enum EnabledProtocolsColumn)index.column()) + { + case colProtocol: + return item->enabled() ? Qt::Checked : Qt::Unchecked; + default: + break; + } + break; + case DATA_PROTOCOL_TYPE: + return QVariant::fromValue(item->type()); + break; + default: + break; + } + + return QVariant(); +} + +bool EnabledProtocolsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + if ((role != Qt::EditRole) && + ((index.column() == colProtocol) && (role != Qt::CheckStateRole))) + return false; + + if (data(index, role) == value) { + // Data appears unchanged, do not do additional checks. + return true; + } + + EnabledProtocolItem* item = static_cast(index.internalPointer()); + if (item == NULL) + return false; + + item->setEnabled(value.toInt() == Qt::Checked ? true : false); + + return true; +} + +static void addHeuristicItem(gpointer data, gpointer user_data) +{ + heur_dtbl_entry_t* heur = (heur_dtbl_entry_t*)data; + ProtocolTreeItem* protocol_item = (ProtocolTreeItem*)user_data; + + HeuristicTreeItem* heuristic_row = new HeuristicTreeItem(heur, protocol_item); + protocol_item->prependChild(heuristic_row); +} + +void EnabledProtocolsModel::populate() +{ + void *cookie; + protocol_t *protocol; + + beginResetModel(); + + // Iterate over all the protocols + for (int i = proto_get_first_protocol(&cookie); i != -1; i = proto_get_next_protocol(&cookie)) + { + if (proto_can_toggle_protocol(i)) + { + protocol = find_protocol_by_id(i); + if (!proto_is_pino(protocol)) { + ProtocolTreeItem* protocol_row = new ProtocolTreeItem(protocol, root_); + root_->prependChild(protocol_row); + + proto_heuristic_dissector_foreach(protocol, addHeuristicItem, protocol_row); + } + } + } + + endResetModel(); +} + +void EnabledProtocolsModel::applyChanges(bool writeChanges) +{ + bool redissect = false; + + for (int proto_index = 0; proto_index < root_->childCount(); proto_index++) { + EnabledProtocolItem* proto = root_->child(proto_index); + redissect |= proto->applyValue(); + for (int heur_index = 0; heur_index < proto->childCount(); heur_index++) { + EnabledProtocolItem* heur = proto->child(heur_index); + redissect |= heur->applyValue(); + } + } + + if (redissect) { + saveChanges(writeChanges); + } +} + +void EnabledProtocolsModel::disableProtocol(struct _protocol *protocol) +{ + ProtocolTreeItem disabled_proto(protocol, NULL); + disabled_proto.setEnabled(false); + if (disabled_proto.applyValue()) { + saveChanges(); + } +} + +void EnabledProtocolsModel::saveChanges(bool writeChanges) +{ + if (writeChanges) { + save_enabled_and_disabled_lists(); + } + mainApp->emitAppSignal(MainApplication::PacketDissectionChanged); +} + + +EnabledProtocolsProxyModel::EnabledProtocolsProxyModel(QObject * parent) +: QSortFilterProxyModel(parent), +type_(EnabledProtocolsProxyModel::EveryWhere), +protocolType_(EnabledProtocolItem::Any), +filter_() +{} + +bool EnabledProtocolsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + //Use EnabledProtocolItem directly for better performance + EnabledProtocolItem* left_item = static_cast(left.internalPointer()); + EnabledProtocolItem* right_item = static_cast(right.internalPointer()); + + if ((left_item != NULL) && (right_item != NULL)) { + + int compare_ret = 0; + + if (left.column() == EnabledProtocolsModel::colProtocol) + compare_ret = left_item->name().compare(right_item->name(), Qt::CaseInsensitive); + else if (left.column() == EnabledProtocolsModel::colDescription) + compare_ret = left_item->description().compare(right_item->description(), Qt::CaseInsensitive); + + if (compare_ret < 0) + return true; + } + + return false; +} + +Qt::ItemFlags EnabledProtocolsProxyModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = Qt::NoItemFlags; + if (index.isValid()) + { + QModelIndex source = mapToSource(index); + if (filterAcceptsSelf(source.row(), source.parent()) ) + { + flags = Qt::ItemIsEnabled; + flags |= Qt::ItemIsSelectable; + flags |= Qt::ItemIsUserCheckable; + } + } + + return flags; +} + +bool EnabledProtocolsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + if (filterAcceptsSelf(sourceRow, sourceParent)) + return true; + +#if 0 + QModelIndex parent = sourceParent; + while (parent.isValid()) + { + if (filterAcceptsSelf(parent.row(), parent.parent())) + return true; + parent = parent.parent(); + } +#endif + + if (filterAcceptsChild(sourceRow, sourceParent)) + return true; + + return false; +} + +bool EnabledProtocolsProxyModel::filterAcceptsSelf(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex nameIdx = sourceModel()->index(sourceRow, EnabledProtocolsModel::colProtocol, sourceParent); + if (! nameIdx.isValid()) + return false; + EnabledProtocolItem* item = static_cast(nameIdx.internalPointer()); + if (! item) + return false; + + QRegularExpression regex(filter_, QRegularExpression::CaseInsensitiveOption); + if (! regex.isValid()) + return false; + + if (protocolType_ == EnabledProtocolItem::Any || protocolType_ == item->type()) + { + if (type_ != EnabledProtocolsProxyModel::EnabledItems && type_ != EnabledProtocolsProxyModel::DisabledItems) + { + if (! filter_.isEmpty()) + { + if (item->name().contains(regex) && type_ != OnlyDescription) + return true; + + if (item->description().contains(regex) && type_ != OnlyProtocol) + return true; + } + else + return true; + } + else if (filter_.isEmpty() || (! filter_.isEmpty() && (item->name().contains(regex) || item->description().contains(regex)))) + { + if (type_ == EnabledProtocolsProxyModel::EnabledItems && item->enabled()) + return true; + else if (type_ == EnabledProtocolsProxyModel::DisabledItems && ! item->enabled()) + return true; + } + } + + return false; +} + +bool EnabledProtocolsProxyModel::filterAcceptsChild(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex item = sourceModel()->index(sourceRow, EnabledProtocolsModel::colProtocol, sourceParent); + if (! item.isValid()) + return false; + + int childCount = item.model()->rowCount(item); + if (childCount == 0) + return false; + + for (int i = 0; i < childCount; i++) + { + if (filterAcceptsSelf(i, item)) + return true; +#if 0 + /* Recursive search disabled for performance reasons */ + if (filterAcceptsChild(i, item)) + return true; +#endif + } + + return false; +} + +void EnabledProtocolsProxyModel::setFilter(const QString& filter, EnabledProtocolsProxyModel::SearchType type, + EnabledProtocolItem::EnableProtocolType protocolType) +{ + filter_ = filter; + type_ = type; + protocolType_ = protocolType; + invalidateFilter(); +} + +void EnabledProtocolsProxyModel::setItemsEnable(EnabledProtocolsProxyModel::EnableType enableType, QModelIndex parent) +{ + if (! sourceModel()) + return; + + if (! parent.isValid()) + beginResetModel(); + + int rowcount = rowCount(parent); + for (int row = 0; row < rowcount; row++) + { + QModelIndex idx = index(row, EnabledProtocolsModel::colProtocol, parent); + + QModelIndex sIdx = mapToSource(idx); + if (sIdx.isValid()) + { + EnabledProtocolItem* item = static_cast(sIdx.internalPointer()); + if (item && (protocolType_ == EnabledProtocolItem::Any || protocolType_ == item->type()) ) + { + Qt::CheckState enable = idx.data(Qt::CheckStateRole).value(); + if (enableType == Enable) + enable = Qt::Checked; + else if (enableType == Disable) + enable = Qt::Unchecked; + else + enable = enable == Qt::Checked ? Qt::Unchecked : Qt::Checked; + + sourceModel()->setData(mapToSource(idx), QVariant::fromValue(enable), Qt::CheckStateRole); + } + } + + setItemsEnable(enableType, idx); + } + + + if (! parent.isValid()) + endResetModel(); +} diff --git a/ui/qt/models/enabled_protocols_model.h b/ui/qt/models/enabled_protocols_model.h new file mode 100644 index 00000000..23b2c1bb --- /dev/null +++ b/ui/qt/models/enabled_protocols_model.h @@ -0,0 +1,143 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ENABLED_PROTOCOLS_MODEL_H +#define ENABLED_PROTOCOLS_MODEL_H + +#include + +#include + +#include + +#include +#include + +class EnabledProtocolItem : public ModelHelperTreeItem +{ + Q_GADGET +public: + enum EnableProtocolType{ + Any, + Standard, + Heuristic + }; + Q_ENUM(EnableProtocolType) + + EnabledProtocolItem(QString name, QString description, bool enabled, EnabledProtocolItem* parent); + virtual ~EnabledProtocolItem(); + + QString name() const {return name_;} + QString description() const {return description_;} + bool enabled() const {return enabled_;} + void setEnabled(bool enable) {enabled_ = enable;} + + EnableProtocolType type() const; + + bool applyValue(); + +protected: + virtual void applyValuePrivate(gboolean value) = 0; + + QString name_; + QString description_; + bool enabled_; + bool enabledInit_; //value that model starts with to determine change + EnableProtocolType type_; +}; + +class EnabledProtocolsModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit EnabledProtocolsModel(QObject * parent = Q_NULLPTR); + virtual ~EnabledProtocolsModel(); + + enum EnabledProtocolsColumn { + colProtocol = 0, + colDescription, + colLast + }; + + enum EnableProtocolData { + DATA_ENABLE = Qt::UserRole, + DATA_PROTOCOL_TYPE + }; + + QModelIndex index(int row, int column, + const QModelIndex & = QModelIndex()) const; + QModelIndex parent(const QModelIndex &) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + 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 populate(); + + void applyChanges(bool writeChanges = true); + static void disableProtocol(struct _protocol *protocol); + +protected: + static void saveChanges(bool writeChanges = true); + +private: + EnabledProtocolItem* root_; +}; + +class EnabledProtocolsProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + enum SearchType + { + EveryWhere, + OnlyProtocol, + OnlyDescription, + EnabledItems, + DisabledItems + }; + Q_ENUM(SearchType) + + enum EnableType + { + Enable, + Disable, + Invert + }; + + explicit EnabledProtocolsProxyModel(QObject * parent = Q_NULLPTR); + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + + void setFilter(const QString& filter, EnabledProtocolsProxyModel::SearchType type, + EnabledProtocolItem::EnableProtocolType protocolType); + + void setItemsEnable(EnabledProtocolsProxyModel::EnableType enable, QModelIndex parent = QModelIndex()); + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + +private: + EnabledProtocolsProxyModel::SearchType type_; + EnabledProtocolItem::EnableProtocolType protocolType_; + QString filter_; + + bool filterAcceptsSelf(int sourceRow, const QModelIndex &sourceParent) const; + bool filterAcceptsChild(int sourceRow, const QModelIndex &sourceParent) const; +}; + +#endif // ENABLED_PROTOCOLS_MODEL_H diff --git a/ui/qt/models/expert_info_model.cpp b/ui/qt/models/expert_info_model.cpp new file mode 100644 index 00000000..017dba3d --- /dev/null +++ b/ui/qt/models/expert_info_model.cpp @@ -0,0 +1,424 @@ +/* expert_info_model.cpp + * Data model for Expert Info tap data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "expert_info_model.h" + +#include "file.h" +#include + +ExpertPacketItem::ExpertPacketItem(const expert_info_t& expert_info, column_info *cinfo, ExpertPacketItem* parent) : + packet_num_(expert_info.packet_num), + group_(expert_info.group), + severity_(expert_info.severity), + hf_id_(expert_info.hf_index), + protocol_(expert_info.protocol), + summary_(expert_info.summary), + parentItem_(parent) +{ + if (cinfo) { + info_ = col_get_text(cinfo, COL_INFO); + } +} + +ExpertPacketItem::~ExpertPacketItem() +{ + for (int row = 0; row < childItems_.count(); row++) + { + delete childItems_.value(row); + } + + childItems_.clear(); +} + +QString ExpertPacketItem::groupKey(bool group_by_summary, int severity, int group, QString protocol, int expert_hf) +{ + QString key = QString("%1|%2|%3") + .arg(severity) + .arg(group) + .arg(protocol); + if (group_by_summary) { + key += QString("|%1").arg(expert_hf); + } + return key; +} + +QString ExpertPacketItem::groupKey(bool group_by_summary) { + return groupKey(group_by_summary, severity_, group_, protocol_, hf_id_); +} + +void ExpertPacketItem::appendChild(ExpertPacketItem* child, QString hash) +{ + childItems_.append(child); + hashChild_[hash] = child; +} + +ExpertPacketItem* ExpertPacketItem::child(int row) +{ + return childItems_.value(row); +} + +ExpertPacketItem* ExpertPacketItem::child(QString hash) +{ + return hashChild_[hash]; +} + +int ExpertPacketItem::childCount() const +{ + return static_cast(childItems_.count()); +} + +int ExpertPacketItem::row() const +{ + if (parentItem_) + return static_cast(parentItem_->childItems_.indexOf(const_cast(this))); + + return 0; +} + +ExpertPacketItem* ExpertPacketItem::parentItem() +{ + return parentItem_; +} + + + + +ExpertInfoModel::ExpertInfoModel(CaptureFile& capture_file, QObject *parent) : + QAbstractItemModel(parent), + capture_file_(capture_file), + group_by_summary_(true), + root_(createRootItem()) +{ +} + +ExpertInfoModel::~ExpertInfoModel() +{ + delete root_; +} + +void ExpertInfoModel::clear() +{ + beginResetModel(); + + eventCounts_.clear(); + delete root_; + root_ = createRootItem(); + + endResetModel(); +} + +ExpertPacketItem* ExpertInfoModel::createRootItem() +{ + static const char* rootName = "ROOT"; +DIAG_OFF_CAST_AWAY_CONST + static expert_info_t root_expert = { 0, -1, -1, -1, rootName, (gchar*)rootName, NULL }; +DIAG_ON_CAST_AWAY_CONST + + return new ExpertPacketItem(root_expert, NULL, NULL); +} + + + +int ExpertInfoModel::numEvents(enum ExpertSeverity severity) +{ + return eventCounts_[severity]; +} + +QModelIndex ExpertInfoModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + ExpertPacketItem *parent_item, *child_item; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + Q_ASSERT(parent_item); + if (group_by_summary_) { + //don't allow group layer + if (parent_item == root_) { + int row_count = 0; + ExpertPacketItem *grandchild_item; + + for (int subrow = 0; subrow < parent_item->childCount(); subrow++) { + child_item = parent_item->child(subrow); + //summary children are always stored in first child of group + grandchild_item = child_item->child(0); + + if (row_count+grandchild_item->childCount() > row) { + return createIndex(row, column, grandchild_item->child(row-row_count)); + } + row_count += grandchild_item->childCount(); + } + + //shouldn't happen + return QModelIndex(); + } + + int root_level = 0; + ExpertPacketItem *item = parent_item; + while (item != root_) + { + root_level++; + item = item->parentItem(); + } + + if (root_level == 3) { + child_item = parent_item->child(row); + if (child_item) { + return createIndex(row, column, child_item); + } + } + + } else { + child_item = parent_item->child(row); + if (child_item) { + //only allow 2 levels deep + if (((parent_item == root_) || (parent_item->parentItem() == root_))) + return createIndex(row, column, child_item); + } + } + return QModelIndex(); +} + +QModelIndex ExpertInfoModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return QModelIndex(); + + ExpertPacketItem *item = static_cast(index.internalPointer()); + ExpertPacketItem *parent_item = item->parentItem(); + + if (group_by_summary_) + { + //don't allow group layer + int root_level = 0; + item = parent_item; + while ((item != root_) && (item != NULL)) + { + root_level++; + item = item->parentItem(); + } + + if (root_level == 3) + return createIndex(parent_item->row(), 0, parent_item); + + } else { + if (parent_item == root_) + return QModelIndex(); + + return createIndex(parent_item->row(), 0, parent_item); + } + + return QModelIndex(); +} + +#if 0 +Qt::ItemFlags ExpertInfoModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + ExpertPacketItem* item = static_cast(index.internalPointer()); + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + //collapse??? + return flags; +} +#endif + +QVariant ExpertInfoModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) + return QVariant(); + + ExpertPacketItem* item = static_cast(index.internalPointer()); + if (item == NULL) + return QVariant(); + + if (role == Qt::ToolTipRole) + { + QString filterName = proto_registrar_get_abbrev(item->hfId()); + return filterName; + } + else if (role == Qt::DisplayRole) + { + switch ((enum ExpertColumn)index.column()) { + case colSeverity: + return QString(val_to_str_const(item->severity(), expert_severity_vals, "Unknown")); + case colSummary: + if (index.parent().isValid()) + { + if (item->severity() == PI_COMMENT) + return item->summary().simplified(); + if (group_by_summary_) + return item->colInfo().simplified(); + + return item->summary().simplified(); + } + else + { + if (group_by_summary_) + { + if (item->severity() == PI_COMMENT) + return "Packet comments listed below."; + if (item->hfId() != -1) { + return proto_registrar_get_name(item->hfId()); + } else { + return item->summary().simplified(); + } + } + } + return QVariant(); + case colGroup: + return QString(val_to_str_const(item->group(), expert_group_vals, "Unknown")); + case colProtocol: + return item->protocol(); + case colCount: + if (!index.parent().isValid()) + { + return item->childCount(); + } + break; + case colPacket: + return item->packetNum(); + case colHf: + return item->hfId(); + default: + break; + } + } + + return QVariant(); +} + +//GUI helpers +void ExpertInfoModel::setGroupBySummary(bool group_by_summary) +{ + beginResetModel(); + group_by_summary_ = group_by_summary; + endResetModel(); +} + +int ExpertInfoModel::rowCount(const QModelIndex &parent) const +{ + ExpertPacketItem *parent_item; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + if (group_by_summary_) { + int row_count = 0; + + //don't allow group layer + if (parent_item == root_) { + ExpertPacketItem *child_item, *grandchild_item; + + for (int row = 0; row < parent_item->childCount(); row++) { + child_item = parent_item->child(row); + grandchild_item = child_item->child(0); + row_count += grandchild_item->childCount(); + } + + return row_count; + } + + return parent_item->childCount(); + + } else { + //only allow 2 levels deep + if ((parent_item == root_) || (parent_item->parentItem() == root_)) + return parent_item->childCount(); + } + + return 0; +} + +int ExpertInfoModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + +void ExpertInfoModel::addExpertInfo(const struct expert_info_s& expert_info) +{ + QString groupKey = ExpertPacketItem::groupKey(FALSE, expert_info.severity, expert_info.group, QString(expert_info.protocol), expert_info.hf_index); + QString summaryKey = ExpertPacketItem::groupKey(TRUE, expert_info.severity, expert_info.group, QString(expert_info.protocol), expert_info.hf_index); + + ExpertPacketItem* expert_root = root_->child(groupKey); + if (expert_root == NULL) { + ExpertPacketItem *new_item = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), root_); + + root_->appendChild(new_item, groupKey); + + expert_root = new_item; + } + + ExpertPacketItem *expert = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), expert_root); + expert_root->appendChild(expert, groupKey); + + //add the summary children off of the first child of the root children + ExpertPacketItem* summary_root = expert_root->child(0); + + //make a summary child + ExpertPacketItem* expert_summary_root = summary_root->child(summaryKey); + if (expert_summary_root == NULL) { + ExpertPacketItem *new_summary = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), summary_root); + + summary_root->appendChild(new_summary, summaryKey); + expert_summary_root = new_summary; + } + + ExpertPacketItem *expert_summary = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), expert_summary_root); + expert_summary_root->appendChild(expert_summary, summaryKey); +} + +void ExpertInfoModel::tapReset(void *eid_ptr) +{ + ExpertInfoModel *model = static_cast(eid_ptr); + if (!model) + return; + + model->clear(); +} + +tap_packet_status ExpertInfoModel::tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data, tap_flags_t) +{ + ExpertInfoModel *model = static_cast(eid_ptr); + const expert_info_t *expert_info = (const expert_info_t *) data; + tap_packet_status status = TAP_PACKET_DONT_REDRAW; + + if (!pinfo || !model || !expert_info) + return TAP_PACKET_DONT_REDRAW; + + model->addExpertInfo(*expert_info); + + status = TAP_PACKET_REDRAW; + + model->eventCounts_[(enum ExpertSeverity)expert_info->severity]++; + + return status; +} + +void ExpertInfoModel::tapDraw(void *eid_ptr) +{ + ExpertInfoModel *model = static_cast(eid_ptr); + if (!model) + return; + + model->beginResetModel(); + model->endResetModel(); +} diff --git a/ui/qt/models/expert_info_model.h b/ui/qt/models/expert_info_model.h new file mode 100644 index 00000000..ada10aea --- /dev/null +++ b/ui/qt/models/expert_info_model.h @@ -0,0 +1,128 @@ +/** @file + * + * Data model for Expert Info tap data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPERT_INFO_MODEL_H +#define EXPERT_INFO_MODEL_H + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +class ExpertPacketItem +{ +public: + ExpertPacketItem(const expert_info_t& expert_info, column_info *cinfo, ExpertPacketItem* parent); + virtual ~ExpertPacketItem(); + + unsigned int packetNum() const { return packet_num_; } + int group() const { return group_; } + int severity() const { return severity_; } + int hfId() const { return hf_id_; } + QString protocol() const { return protocol_; } + QString summary() const { return summary_; } + QString colInfo() const { return info_; } + + static QString groupKey(bool group_by_summary, int severity, int group, QString protocol, int expert_hf); + QString groupKey(bool group_by_summary); + + void appendChild(ExpertPacketItem* child, QString hash); + ExpertPacketItem* child(int row); + ExpertPacketItem* child(QString hash); + int childCount() const; + int row() const; + ExpertPacketItem* parentItem(); + +private: + unsigned int packet_num_; + int group_; + int severity_; + int hf_id_; + // Half-hearted attempt at conserving memory. If this isn't sufficient, + // PacketListRecord interns column strings in a GStringChunk. + QByteArray protocol_; + QByteArray summary_; + QByteArray info_; + + QList childItems_; + ExpertPacketItem* parentItem_; + QHash hashChild_; //optimization for insertion +}; + +class ExpertInfoModel : public QAbstractItemModel +{ +public: + ExpertInfoModel(CaptureFile& capture_file, QObject *parent = 0); + virtual ~ExpertInfoModel(); + + enum ExpertColumn { + colSeverity = 0, + colSummary, + colGroup, + colProtocol, + colCount, + colPacket, + colHf, + colLast + }; + + enum ExpertSeverity { + severityError = PI_ERROR, + severityWarn = PI_WARN, + severityNote = PI_NOTE, + severityChat = PI_CHAT, + severityComment = PI_COMMENT + }; + + QModelIndex index(int row, int column, + const QModelIndex & = QModelIndex()) const; + QModelIndex parent(const QModelIndex &) const; +#if 0 + Qt::ItemFlags flags(const QModelIndex &index) const; +#endif + QVariant data(const QModelIndex &index, int role) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + int numEvents(enum ExpertSeverity severity); + + void clear(); + + //GUI helpers + void setGroupBySummary(bool group_by_summary); + + // Called from tapPacket + void addExpertInfo(const struct expert_info_s& expert_info); + + // Callbacks for register_tap_listener + static void tapReset(void *eid_ptr); + static tap_packet_status tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data, tap_flags_t flags); + static void tapDraw(void *eid_ptr); + +private: + CaptureFile& capture_file_; + + ExpertPacketItem* createRootItem(); + + bool group_by_summary_; + ExpertPacketItem* root_; + + QHash eventCounts_; +}; +#endif // EXPERT_INFO_MODEL_H diff --git a/ui/qt/models/expert_info_proxy_model.cpp b/ui/qt/models/expert_info_proxy_model.cpp new file mode 100644 index 00000000..ac13eff5 --- /dev/null +++ b/ui/qt/models/expert_info_proxy_model.cpp @@ -0,0 +1,290 @@ +/* expert_info_model.cpp + * Data model for Expert Info tap data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +#include + +ExpertInfoProxyModel::ExpertInfoProxyModel(QObject *parent) : QSortFilterProxyModel(parent), + severityMode_(Group) +{ +} + +bool ExpertInfoProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + ExpertPacketItem *left_item, + *right_item; + QString leftStr, rightStr; + bool checkPacketNumber = false; + int compare_ret; + + if (source_left.parent().isValid() && source_right.parent().isValid()) { + left_item = static_cast(source_left.parent().internalPointer()); + right_item = static_cast(source_right.parent().internalPointer()); + } else { + left_item = static_cast(source_left.internalPointer()); + right_item = static_cast(source_right.internalPointer()); + } + + if ((left_item != NULL) && (right_item != NULL)) { + switch (source_left.column()) + { + case colProxySeverity: + if (left_item->severity() != right_item->severity()) { + return (left_item->severity() < right_item->severity()); + } + + checkPacketNumber = true; + break; + case colProxySummary: + compare_ret = left_item->summary().compare(right_item->summary()); + if (compare_ret < 0) + return true; + if (compare_ret > 0) + return false; + + checkPacketNumber = true; + break; + case colProxyGroup: + if (left_item->group() != right_item->group()) { + return (left_item->group() < right_item->group()); + } + + checkPacketNumber = true; + break; + case colProxyProtocol: + compare_ret = left_item->protocol().compare(right_item->protocol()); + if (compare_ret < 0) + return true; + if (compare_ret > 0) + return false; + + checkPacketNumber = true; + break; + case colProxyCount: + break; + default: + break; + } + + if (checkPacketNumber) { + return (left_item->packetNum() < right_item->packetNum()); + } + } + + // fallback to string cmp on other fields + return QSortFilterProxyModel::lessThan(source_left, source_right); +} + +QVariant ExpertInfoProxyModel::data(const QModelIndex &proxy_index, int role) const +{ + QModelIndex source_index; + + switch (role) + { + case Qt::BackgroundRole: + { + source_index = mapToSource(proxy_index); + + // only color base row + if (!source_index.isValid() || source_index.parent().isValid()) + return QVariant(); + + ExpertPacketItem* item = static_cast(source_index.internalPointer()); + if (item == NULL) + return QVariant(); + + // provide background color for groups + switch(item->severity()) { + case(PI_COMMENT): + return QBrush(ColorUtils::expert_color_comment); + case(PI_CHAT): + return QBrush(ColorUtils::expert_color_chat); + case(PI_NOTE): + return QBrush(ColorUtils::expert_color_note); + case(PI_WARN): + return QBrush(ColorUtils::expert_color_warn); + case(PI_ERROR): + return QBrush(ColorUtils::expert_color_error); + } + } + break; + case Qt::ForegroundRole: + { + source_index = mapToSource(proxy_index); + + // only color base row + if (!source_index.isValid() || source_index.parent().isValid()) + return QVariant(); + + ExpertPacketItem* item = static_cast(source_index.internalPointer()); + if (item == NULL) + return QVariant(); + + // provide foreground color for groups + switch(item->severity()) { + case(PI_COMMENT): + case(PI_CHAT): + case(PI_NOTE): + case(PI_WARN): + case(PI_ERROR): + return QBrush(ColorUtils::expert_color_foreground); + } + } + break; + case Qt::TextAlignmentRole: + switch (proxy_index.column()) + { + case colProxySeverity: + //packet number should be right aligned + if (source_index.parent().isValid()) + return Qt::AlignRight; + break; + case colProxyCount: + return Qt::AlignRight; + default: + break; + } + return Qt::AlignLeft; + + case Qt::DisplayRole: + source_index = mapToSource(proxy_index); + + switch (proxy_index.column()) + { + case colProxySeverity: + if (source_index.parent().isValid()) + return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colPacket), role); + + return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colSeverity), role); + case colProxySummary: + return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colSummary), role); + case colProxyGroup: + return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colGroup), role); + case colProxyProtocol: + return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colProtocol), role); + case colProxyCount: + //only show counts for parent + if (!source_index.parent().isValid()) { + //because of potential filtering, count is computed manually + unsigned int count = 0; + ExpertPacketItem *child_item, + *item = static_cast(source_index.internalPointer()); + for (int row = 0; row < item->childCount(); row++) { + child_item = item->child(row); + if (child_item == NULL) + continue; + if (filterAcceptItem(*child_item)) + count++; + } + + return count; + } + } + break; + } + + return QSortFilterProxyModel::data(proxy_index, role); +} + +QVariant ExpertInfoProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + + switch ((enum ExpertProxyColumn)section) { + case colProxySeverity: + if (severityMode_ == Packet) + return tr("Packet"); + else + return tr("Severity"); + case colProxySummary: + return tr("Summary"); + case colProxyGroup: + return tr("Group"); + case colProxyProtocol: + return tr("Protocol"); + case colProxyCount: + return tr("Count"); + default: + break; + } + } + return QVariant(); +} + +int ExpertInfoProxyModel::columnCount(const QModelIndex&) const +{ + return colProxyLast; +} + +bool ExpertInfoProxyModel::filterAcceptItem(ExpertPacketItem& item) const +{ + if (hidden_severities_.contains(item.severity())) + return false; + + if (!textFilter_.isEmpty()) { + QRegularExpression regex(textFilter_, QRegularExpression::CaseInsensitiveOption | + QRegularExpression::UseUnicodePropertiesOption); + if (! regex.isValid()) + return false; + + if (item.protocol().contains(regex)) + return true; + + if (item.summary().contains(regex)) + return true; + + if (item.colInfo().contains(regex)) + return true; + + return false; + } + + return true; +} + +bool ExpertInfoProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex severityIdx = sourceModel()->index(sourceRow, ExpertInfoModel::colSeverity, sourceParent); + ExpertPacketItem* item = static_cast(severityIdx.internalPointer()); + if (item == NULL) + return true; + + return filterAcceptItem(*item); +} + +//GUI helpers +void ExpertInfoProxyModel::setSeverityMode(enum SeverityMode mode) +{ + severityMode_ = mode; + emit headerDataChanged(Qt::Vertical, 0, 1); +} + +void ExpertInfoProxyModel::setSeverityFilter(int severity, bool hide) +{ + if (hide) + { + hidden_severities_ << severity; + } + else + { + hidden_severities_.removeOne(severity); + } + + invalidateFilter(); +} + +void ExpertInfoProxyModel::setSummaryFilter(const QString &filter) +{ + textFilter_ = filter; + invalidateFilter(); +} diff --git a/ui/qt/models/expert_info_proxy_model.h b/ui/qt/models/expert_info_proxy_model.h new file mode 100644 index 00000000..411d88d2 --- /dev/null +++ b/ui/qt/models/expert_info_proxy_model.h @@ -0,0 +1,61 @@ +/** @file + * + * Data model for Expert Info tap data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPERT_INFO_PROXY_MODEL_H +#define EXPERT_INFO_PROXY_MODEL_H + +#include + +#include + +class ExpertPacketItem; + +class ExpertInfoProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + ExpertInfoProxyModel(QObject *parent = 0); + + enum SeverityMode { Group, Packet }; + enum ExpertProxyColumn { + colProxySeverity = 0, + colProxySummary, + colProxyGroup, + colProxyProtocol, + colProxyCount, + colProxyLast + }; + + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + + //GUI helpers + void setSeverityMode(enum SeverityMode); + void setSeverityFilter(int severity, bool hide); + void setSummaryFilter(const QString &filter); + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + bool filterAcceptItem(ExpertPacketItem& item) const; + + enum SeverityMode severityMode_; + QList hidden_severities_; + + QString textFilter_; + +}; + +#endif // EXPERT_INFO_PROXY_MODEL_H diff --git a/ui/qt/models/export_objects_model.cpp b/ui/qt/models/export_objects_model.cpp new file mode 100644 index 00000000..3cf9ec63 --- /dev/null +++ b/ui/qt/models/export_objects_model.cpp @@ -0,0 +1,316 @@ +/* export_objects_model.cpp + * Data model for Export Objects. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "export_objects_model.h" + +#include +#include +#include +#include + +#include + +extern "C" { + +static void +object_list_add_entry(void *gui_data, export_object_entry_t *entry) { + export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data; + + if (object_list && object_list->model) + object_list->model->addObjectEntry(entry); +} + +static export_object_entry_t* +object_list_get_entry(void *gui_data, int row) { + export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data; + + if (object_list && object_list->model) + return object_list->model->objectEntry(row); + + return NULL; +} + +} // extern "C" + + + + +ExportObjectModel::ExportObjectModel(register_eo_t* eo, QObject *parent) : + QAbstractTableModel(parent), + eo_(eo) +{ + eo_gui_data_.model = this; + + export_object_list_.add_entry = object_list_add_entry; + export_object_list_.get_entry = object_list_get_entry; + export_object_list_.gui_data = (void*)&eo_gui_data_; +} + +ExportObjectModel::~ExportObjectModel() +{ + foreach (QVariant v, objects_) { + eo_free_entry(VariantPointer::asPtr(v)); + } +} + +QVariant ExportObjectModel::data(const QModelIndex &index, int role) const +{ + if ((!index.isValid()) || ((role != Qt::DisplayRole) && (role != Qt::UserRole))) { + return QVariant(); + } + + if (role == Qt::DisplayRole) + { + export_object_entry_t *entry = VariantPointer::asPtr(objects_.value(index.row())); + if (entry == NULL) + return QVariant(); + + switch(index.column()) + { + case colPacket: + return QString::number(entry->pkt_num); + case colHostname: + return QString::fromUtf8(entry->hostname); + case colContent: + return QString::fromUtf8(entry->content_type); + case colSize: + return file_size_to_qstring(entry->payload_len); + case colFilename: + return QString::fromUtf8(entry->filename); + } + } + else if (role == Qt::UserRole) + { + return objects_.value(index.row()); + } + + return QVariant(); +} + +QVariant ExportObjectModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + + switch (section) { + case colPacket: + return tr("Packet"); + case colHostname: + return tr("Hostname"); + case colContent: + return tr("Content Type"); + case colSize: + return tr("Size"); + case colFilename: + return tr("Filename"); + } + + return QVariant(); +} + +int ExportObjectModel::rowCount(const QModelIndex &parent) const +{ + // there are no children + if (parent.isValid()) { + return 0; + } + + return static_cast(objects_.count()); +} + +int ExportObjectModel::columnCount(const QModelIndex&) const +{ + return colExportObjectMax; +} + +void ExportObjectModel::addObjectEntry(export_object_entry_t *entry) +{ + if (entry == NULL) + return; + + int count = static_cast(objects_.count()); + beginInsertRows(QModelIndex(), count, count); + objects_.append(VariantPointer::asQVariant(entry)); + endInsertRows(); +} + +export_object_entry_t* ExportObjectModel::objectEntry(int row) +{ + return VariantPointer::asPtr(objects_.value(row)); +} + +bool ExportObjectModel::saveEntry(QModelIndex &index, QString filename) +{ + if (!index.isValid() || filename.isEmpty()) + return false; + + export_object_entry_t *entry = VariantPointer::asPtr(objects_.value(index.row())); + if (entry == NULL) + return false; + + if (filename.length() > 0) { + write_file_binary_mode(qUtf8Printable(filename), entry->payload_data, entry->payload_len); + } + + return true; +} + +void ExportObjectModel::saveAllEntries(QString path) +{ + if (path.isEmpty()) + return; + + QDir save_dir(path); + export_object_entry_t *entry; + + for (QList::iterator it = objects_.begin(); it != objects_.end(); ++it) + { + entry = VariantPointer::asPtr(*it); + if (entry == NULL) + continue; + + guint count = 0; + QString filename; + + do { + GString *safe_filename; + + if (entry->filename) + safe_filename = eo_massage_str(entry->filename, + EXPORT_OBJECT_MAXFILELEN, count); + else { + char generic_name[EXPORT_OBJECT_MAXFILELEN+1]; + const char *ext; + ext = eo_ct2ext(entry->content_type); + snprintf(generic_name, sizeof(generic_name), + "object%u%s%s", entry->pkt_num, ext ? "." : "", + ext ? ext : ""); + safe_filename = eo_massage_str(generic_name, + EXPORT_OBJECT_MAXFILELEN, count); + } + filename = QString::fromUtf8(safe_filename->str); + g_string_free(safe_filename, TRUE); + } while (save_dir.exists(filename) && ++count < prefs.gui_max_export_objects); + write_file_binary_mode(qUtf8Printable(save_dir.filePath(filename)), + entry->payload_data, entry->payload_len); + } +} + +void ExportObjectModel::resetObjects() +{ + export_object_gui_reset_cb reset_cb = get_eo_reset_func(eo_); + + beginResetModel(); + objects_.clear(); + endResetModel(); + + if (reset_cb) + reset_cb(); +} + +// Called by taps +/* Runs at the beginning of tapping only */ +void ExportObjectModel::resetTap(void *tapdata) +{ + export_object_list_t *tap_object = (export_object_list_t *)tapdata; + export_object_list_gui_t *object_list = (export_object_list_gui_t *)tap_object->gui_data; + if (object_list && object_list->model) + object_list->model->resetObjects(); +} + +const char* ExportObjectModel::getTapListenerName() +{ + return get_eo_tap_listener_name(eo_); +} + +void* ExportObjectModel::getTapData() +{ + return &export_object_list_; +} + +tap_packet_cb ExportObjectModel::getTapPacketFunc() +{ + return get_eo_packet_func(eo_); +} + +void ExportObjectModel::removeTap() +{ + eo_gui_data_.model = NULL; +} + + + +ExportObjectProxyModel::ExportObjectProxyModel(QObject * parent) + : QSortFilterProxyModel(parent) +{ + +} + +bool ExportObjectProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + export_object_entry_t *left_entry = VariantPointer::asPtr(sourceModel()->data(source_left, Qt::UserRole)), + *right_entry = VariantPointer::asPtr(sourceModel()->data(source_right, Qt::UserRole)); + + if ((left_entry != NULL) && (right_entry != NULL)) + { + switch (source_left.column()) + { + case ExportObjectModel::colPacket: + return left_entry->pkt_num < right_entry->pkt_num; + case ExportObjectModel::colSize: + return left_entry->payload_len < right_entry->payload_len; + case ExportObjectModel::colFilename: + break; + } + } + + return QSortFilterProxyModel::lessThan(source_left, source_right); +} + +void ExportObjectProxyModel::setContentFilterString(QString filter_) +{ + contentFilter_ = filter_; + invalidateFilter(); +} + +void ExportObjectProxyModel::setTextFilterString(QString filter_) +{ + textFilter_ = filter_; + invalidateFilter(); +} + +bool ExportObjectProxyModel::filterAcceptsRow(int source_row, const QModelIndex &/*source_parent*/) const +{ + if (contentFilter_.length() > 0) + { + QModelIndex idx = sourceModel()->index(source_row, ExportObjectModel::colContent); + if (!idx.isValid()) + return false; + + if (contentFilter_.compare(idx.data().toString()) != 0) + return false; + } + + if (textFilter_.length() > 0) + { + QModelIndex hostIdx = sourceModel()->index(source_row, ExportObjectModel::colHostname); + QModelIndex fileIdx = sourceModel()->index(source_row, ExportObjectModel::colFilename); + if (!hostIdx.isValid() || !fileIdx.isValid()) + return false; + + QString host = hostIdx.data().toString(); + QString file = fileIdx.data().toString(); + + if (!host.contains(textFilter_) && !file.contains(textFilter_)) + return false; + } + + return true; +} diff --git a/ui/qt/models/export_objects_model.h b/ui/qt/models/export_objects_model.h new file mode 100644 index 00000000..1aff4345 --- /dev/null +++ b/ui/qt/models/export_objects_model.h @@ -0,0 +1,91 @@ +/** @file + * + * Data model for Export Objects. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPORT_OBJECTS_MODEL_H +#define EXPORT_OBJECTS_MODEL_H + +#include + +#include +#include + +#include +#include +#include + +typedef struct export_object_list_gui_t { + class ExportObjectModel *model; +} export_object_list_gui_t; + +class ExportObjectModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + ExportObjectModel(register_eo_t* eo, QObject *parent); + virtual ~ExportObjectModel(); + + enum ExportObjectColumn { + colPacket = 0, + colHostname, + colContent, + colSize, + colFilename, + colExportObjectMax + }; + + void addObjectEntry(export_object_entry_t *entry); + export_object_entry_t *objectEntry(int row); + void resetObjects(); + + bool saveEntry(QModelIndex &index, QString filename); + void saveAllEntries(QString path); + + const char* getTapListenerName(); + void* getTapData(); + tap_packet_cb getTapPacketFunc(); + static void resetTap(void *tapdata); + void removeTap(); + + QVariant data(const QModelIndex &index, int role) 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; + +private: + QList objects_; + + export_object_list_t export_object_list_; + export_object_list_gui_t eo_gui_data_; + register_eo_t* eo_; +}; + +class ExportObjectProxyModel : public QSortFilterProxyModel +{ +public: + + explicit ExportObjectProxyModel(QObject * parent = Q_NULLPTR); + + void setContentFilterString(QString contentFilter); + void setTextFilterString(QString textFilter); + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + +private: + QString contentFilter_; + QString textFilter_; + +}; + +#endif // EXPORT_OBJECTS_MODEL_H diff --git a/ui/qt/models/fileset_entry_model.cpp b/ui/qt/models/fileset_entry_model.cpp new file mode 100644 index 00000000..8c9f504b --- /dev/null +++ b/ui/qt/models/fileset_entry_model.cpp @@ -0,0 +1,155 @@ +/* fileset_entry_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "wsutil/utf8_entities.h" + +#include + +#include + +FilesetEntryModel::FilesetEntryModel(QObject * parent) : + QAbstractItemModel(parent) +{} + +QModelIndex FilesetEntryModel::index(int row, int column, const QModelIndex &) const +{ + if (row >= entries_.count() || row < 0 || column > ColumnCount) { + return QModelIndex(); + } + + return createIndex(row, column, const_cast(entries_.at(row))); +} + +int FilesetEntryModel::rowCount(const QModelIndex &) const +{ + return static_cast(entries_.count()); +} + +QVariant FilesetEntryModel::data(const QModelIndex &index, int role) const +{ + if (! index.isValid() || index.row() >= rowCount()) + return QVariant(); + + const fileset_entry *entry = static_cast(index.internalPointer()); + if (role == Qt::DisplayRole && entry) { + switch (index.column()) { + case Name: + return QString(entry->name); + case Created: + { + QString created = nameToDate(entry->name); + if (created.length() < 1) { + /* if this file doesn't follow the file set pattern, */ + /* use the creation time of that file if available */ + /* https://en.wikipedia.org/wiki/ISO_8601 */ + /* + * macOS provides 0 if the file system doesn't support the + * creation time; FreeBSD provides -1. + * + * If this OS doesn't provide the creation time with stat(), + * it will be 0. + */ + if (entry->ctime > 0) { + created = time_tToString(entry->ctime); + } else { + created = UTF8_EM_DASH; + } + } + return created; + } + case Modified: + return time_tToString(entry->mtime); + case Size: + return file_size_to_qstring(entry->size); + default: + break; + } + } else if (role == Qt::ToolTipRole) { + return QString(tr("Open this capture file")); + } else if (role == Qt::TextAlignmentRole) { + switch (index.column()) { + case Size: + // Not perfect but better than nothing. + return Qt::AlignRight; + default: + return Qt::AlignLeft; + } + } + return QVariant(); +} + +QVariant FilesetEntryModel::headerData(int section, Qt::Orientation, int role) const +{ + if (role != Qt::DisplayRole) return QVariant(); + + switch (section) { + case Name: + return tr("Filename"); + case Created: + return tr("Created"); + case Modified: + return tr("Modified"); + case Size: + return tr("Size"); + default: + break; + } + return QVariant(); +} + +void FilesetEntryModel::appendEntry(const fileset_entry *entry) +{ + emit beginInsertRows(QModelIndex(), rowCount(), rowCount()); + entries_ << entry; + emit endInsertRows(); +} + +void FilesetEntryModel::clear() +{ + fileset_delete(); + beginResetModel(); + entries_.clear(); + endResetModel(); +} + +QString FilesetEntryModel::nameToDate(const char *name) const { + QString dn; + + if (!fileset_filename_match_pattern(name)) + return NULL; + + dn = name; + dn.remove(QRegularExpression(".*_")); + dn.truncate(14); + dn.insert(4, '-'); + dn.insert(7, '-'); + dn.insert(10, ' '); + dn.insert(13, ':'); + dn.insert(16, ':'); + return dn; +} + +QString FilesetEntryModel::time_tToString(time_t clock) const +{ + struct tm *local = localtime(&clock); + if (!local) return UTF8_EM_DASH; + + // yyyy-MM-dd HH:mm:ss + // The equivalent QDateTime call is pretty slow here, possibly related to QTBUG-21678 + // and/or QTBUG-41714. + return QString("%1-%2-%3 %4:%5:%6") + .arg(local->tm_year + 1900, 4, 10, QChar('0')) + .arg(local->tm_mon+1, 2, 10, QChar('0')) + .arg(local->tm_mday, 2, 10, QChar('0')) + .arg(local->tm_hour, 2, 10, QChar('0')) + .arg(local->tm_min, 2, 10, QChar('0')) + .arg(local->tm_sec, 2, 10, QChar('0')); +} diff --git a/ui/qt/models/fileset_entry_model.h b/ui/qt/models/fileset_entry_model.h new file mode 100644 index 00000000..aa812b0c --- /dev/null +++ b/ui/qt/models/fileset_entry_model.h @@ -0,0 +1,52 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FILESET_ENTRY_MODEL_H +#define FILESET_ENTRY_MODEL_H + +#include + +#include + +#include + +#include +#include +#include + +class FilesetEntryModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit FilesetEntryModel(QObject * parent = 0); + + QModelIndex index(int row, int column, const QModelIndex & = QModelIndex()) const; + // Everything is under the root. + virtual QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &) const { return ColumnCount; } + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation, int role = Qt::DisplayRole) const; + + virtual void appendEntry(const fileset_entry *entry); + const fileset_entry *getRowEntry(int row) const { return entries_.value(row, NULL); } + int entryCount() const { return static_cast(entries_.count()); } + // Calls fileset_delete and clears our model data. + void clear(); + +private: + QVector entries_; + enum Column { Name, Created, Modified, Size, ColumnCount }; + + QString nameToDate(const char *name) const ; + QString time_tToString(time_t clock) const; +}; + +#endif // FILESET_ENTRY_MODEL_H diff --git a/ui/qt/models/filter_list_model.cpp b/ui/qt/models/filter_list_model.cpp new file mode 100644 index 00000000..3ed25839 --- /dev/null +++ b/ui/qt/models/filter_list_model.cpp @@ -0,0 +1,314 @@ +/* filter_list_model.cpp + * Model for all filter types + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Old filter file name. + */ +#define FILTER_FILE_NAME "filters" + +/* + * Capture filter file name. + */ +#define CFILTER_FILE_NAME "cfilters" + +/* + * Display filter file name. + */ +#define DFILTER_FILE_NAME "dfilters" + +FilterListModel::FilterListModel(QObject * parent) : + QAbstractListModel(parent), + type_(FilterListModel::Display) +{ + reload(); +} + +FilterListModel::FilterListModel(FilterListModel::FilterListType type, QObject * parent) : + QAbstractListModel(parent), + type_(type) +{ + reload(); +} + +void FilterListModel::reload() +{ + storage.clear(); + + const char * cfile = (type_ == FilterListModel::Capture) ? CFILTER_FILE_NAME : DFILTER_FILE_NAME; + + /* Try personal config file first */ + QString fileName = gchar_free_to_qstring(get_persconffile_path(cfile, TRUE)); + if (fileName.length() <= 0 || ! QFileInfo::exists(fileName)) + fileName = gchar_free_to_qstring(get_persconffile_path(FILTER_FILE_NAME, TRUE)); + if (fileName.length() <= 0 || ! QFileInfo::exists(fileName)) + fileName = gchar_free_to_qstring(get_datafile_path(cfile)); + if (fileName.length() <= 0 || ! QFileInfo::exists(fileName)) + return; + + QFile file(fileName); + /* Still can use the model, just have to start from an empty set */ + if (! file.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + + QTextStream in(&file); + QRegularExpression rx("\\s*\\\"\\s*(.*?)\\s*\\\"\\s(.*)"); + while (!in.atEnd()) + { + QString data = in.readLine().trimmed(); + /* Filter out lines that do not contain content: + * - Starting with # is a comment + * - Does not start with a quoted string + */ + if (data.startsWith("#") || ! data.trimmed().startsWith("\"")) + continue; + + QStringList content = data.split(QChar('\n')); + foreach (QString line, content) + { + QRegularExpressionMatch match = rx.match(line); + if (match.hasMatch()) { + addFilter(match.captured(1).trimmed(), match.captured(2).trimmed()); + } + } + } +} + +void FilterListModel::setFilterType(FilterListModel::FilterListType type) +{ + type_ = type; + reload(); +} + +FilterListModel::FilterListType FilterListModel::filterType() const +{ + return type_; +} + +int FilterListModel::rowCount(const QModelIndex &/* parent */) const +{ + return static_cast(storage.count()); +} + +int FilterListModel::columnCount(const QModelIndex &/* parent */) const +{ + return 2; +} + +QVariant FilterListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (section >= columnCount() || section < 0 || orientation != Qt::Horizontal) + return QVariant(); + + if (role == Qt::DisplayRole) + { + switch (section) { + case ColumnName: + return tr("Filter Name"); + break; + case ColumnExpression: + return tr("Filter Expression"); + break; + } + } + + return QVariant(); +} + +QVariant FilterListModel::data(const QModelIndex &index, int role) const +{ + if (! index.isValid() || index.row() >= rowCount()) + return QVariant(); + + QStringList row = storage.at(index.row()).split("\n"); + if (role == Qt::DisplayRole) + return row.at(index.column()); + + return QVariant(); +} + +bool FilterListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (! index.isValid() || index.row() >= rowCount() || role != Qt::EditRole) + return false; + + QStringList row = storage.at(index.row()).split("\n"); + if (row.count() <= index.column()) + return false; + + if (index.column() == FilterListModel::ColumnName && value.toString().contains("\"")) + return false; + + row[index.column()] = value.toString(); + storage[index.row()] = row.join("\n"); + + return true; +} + +Qt::ItemFlags FilterListModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags fl = QAbstractListModel::flags(index); + fl |= Qt::ItemIsDropEnabled; + + if (! index.isValid() || index.row() >= rowCount()) + return fl; + + fl |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled; + + return fl; +} +QModelIndex FilterListModel::addFilter(QString name, QString expression) +{ + if (name.length() == 0 || expression.length() == 0) + return QModelIndex(); + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + storage << QString("%1\n%2").arg(name).arg(expression); + endInsertRows(); + + return index(rowCount() - 1, 0); +} + +QModelIndex FilterListModel::findByName(QString name) +{ + if (name.length() == 0) + return QModelIndex(); + + for (int cnt = 0; cnt < rowCount(); cnt++) + { + if (storage.at(cnt).startsWith(QString("%1\n").arg(name))) + return index(cnt, 0); + } + + return QModelIndex(); +} + +QModelIndex FilterListModel::findByExpression(QString expression) +{ + if (expression.length() == 0) + return QModelIndex(); + + for (int cnt = 0; cnt < rowCount(); cnt++) + { + if (storage.at(cnt).endsWith(QString("\n%1").arg(expression))) + return index(cnt, 0); + } + + return QModelIndex(); +} + +void FilterListModel::removeFilter(QModelIndex idx) +{ + if (! idx.isValid() || idx.row() >= rowCount()) + return; + + beginRemoveRows(QModelIndex(), idx.row(), idx.row()); + storage.removeAt(idx.row()); + endRemoveRows(); +} + +void FilterListModel::saveList() +{ + QString filename = (type_ == FilterListModel::Capture) ? CFILTER_FILE_NAME : DFILTER_FILE_NAME; + + filename = QString("%1%2%3").arg(ProfileModel::activeProfilePath()).arg("/").arg(filename); + QFile file(filename); + + if (! file.open(QIODevice::WriteOnly | QIODevice::Text)) + return; + + QTextStream out(&file); + for (int row = 0; row < rowCount(); row++) + { + QString line = QString("\"%1\"").arg(index(row, ColumnName).data().toString().trimmed()); + line.append(QString(" %1").arg(index(row, ColumnExpression).data().toString())); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + out << line << Qt::endl; +#else + out << line << endl; +#endif + } + + file.close(); +} + +Qt::DropActions FilterListModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +QStringList FilterListModel::mimeTypes() const +{ + return QStringList() << WiresharkMimeData::FilterListMimeType; +} + +QMimeData *FilterListModel::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData(); + QStringList rows; + + foreach (const QModelIndex &index, indexes) + { + if (! rows.contains(QString::number(index.row()))) + rows << QString::number(index.row()); + } + + mimeData->setData(WiresharkMimeData::FilterListMimeType, rows.join(",").toUtf8()); + return mimeData; +} + +bool FilterListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int /* column */, const QModelIndex & parent) +{ + if (action != Qt::MoveAction) + return true; + + if (! data->hasFormat(WiresharkMimeData::FilterListMimeType)) + return true; + + QStringList rows = QString(data->data(WiresharkMimeData::FilterListMimeType)).split(","); + + int insertRow = parent.isValid() ? parent.row() : row; + + /* for now, only single rows can be selected */ + if (rows.count() > 0) + { + bool ok = false; + int strow = rows[0].toInt(&ok); + if (ok) + { + int storeTo = insertRow; + if (storeTo < 0 || storeTo >= storage.count()) + { + storeTo = static_cast(storage.count()) - 1; + } + + beginResetModel(); + storage.move(strow, storeTo); + endResetModel(); + } + } + + return true; +} diff --git a/ui/qt/models/filter_list_model.h b/ui/qt/models/filter_list_model.h new file mode 100644 index 00000000..4f528d2f --- /dev/null +++ b/ui/qt/models/filter_list_model.h @@ -0,0 +1,71 @@ +/** @file + * + * Model for all filter types + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FILTER_LIST_MODEL_h +#define FILTER_LIST_MODEL_h + +#include + +#include +#include +#include + +class FilterListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum FilterListType { + Display, + Capture + }; + + explicit FilterListModel(FilterListType type = FilterListModel::Display, QObject * parent = Q_NULLPTR); + explicit FilterListModel(QObject * parent = Q_NULLPTR); + + enum { + ColumnName, + ColumnExpression + }; + + void setFilterType(FilterListModel::FilterListType type); + FilterListModel::FilterListType filterType() const; + + QModelIndex findByName(QString name); + QModelIndex findByExpression(QString expression); + + QModelIndex addFilter(QString name, QString expression); + void removeFilter(QModelIndex idx); + + void saveList(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + + virtual Qt::DropActions supportedDropActions() const override; + virtual QStringList mimeTypes() const override; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const override; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; + +private: + + FilterListModel::FilterListType type_; + + QStringList storage; + + void reload(); +}; + +#endif // FILTER_LIST_MODEL_h diff --git a/ui/qt/models/info_proxy_model.cpp b/ui/qt/models/info_proxy_model.cpp new file mode 100644 index 00000000..7b256b9d --- /dev/null +++ b/ui/qt/models/info_proxy_model.cpp @@ -0,0 +1,119 @@ +/* info_proxy_model.cpp + * Proxy model for displaying an info text at the end of any QAbstractListModel + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include + +InfoProxyModel::InfoProxyModel(QObject * parent) + : QIdentityProxyModel(parent), + column_(-1) +{ +} + +InfoProxyModel::~InfoProxyModel() +{ + infos_.clear(); +} + +void InfoProxyModel::appendInfo(QString info) +{ + if (! infos_.contains(info)) + infos_ << info; +} + +void InfoProxyModel::clearInfos() +{ + infos_.clear(); +} + +int InfoProxyModel::rowCount(const QModelIndex &parent) const +{ + return static_cast(sourceModel()->rowCount(parent) + infos_.count()); +} + +QVariant InfoProxyModel::data (const QModelIndex &index, int role) const +{ + if (! index.isValid()) + return QVariant(); + + if (index.row() < sourceModel()->rowCount()) + return sourceModel()->data(mapToSource(index), role); + + int ifIdx = index.row() - sourceModel()->rowCount(); + if (index.column() != column_ || ifIdx < 0 || ifIdx >= infos_.count()) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + return infos_.at(ifIdx); + break; + case Qt::FontRole: + QFont font = QIdentityProxyModel::data(index, Qt::FontRole).value(); + font.setItalic(true); + return font; + } + + return QIdentityProxyModel::data(index, role); +} + +Qt::ItemFlags InfoProxyModel::flags(const QModelIndex &index) const +{ + if (index.row() < sourceModel()->rowCount()) + return sourceModel()->flags(mapToSource(index)); + + return Qt::ItemFlags(); +} + +QModelIndex InfoProxyModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row >= sourceModel()->rowCount() && row < rowCount()) + return createIndex(row, column); + + return QIdentityProxyModel::index(row, column, parent); +} + +QModelIndex InfoProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (! proxyIndex.isValid()) + return QModelIndex(); + + if (proxyIndex.row() >= sourceModel()->rowCount()) + return QModelIndex(); + + return QIdentityProxyModel::mapToSource(proxyIndex); +} + +QModelIndex InfoProxyModel::mapFromSource(const QModelIndex &fromIndex) const +{ + return QIdentityProxyModel::mapFromSource(fromIndex); +} + +void InfoProxyModel::setColumn(int column) +{ + int old_column = column_; + column_ = column; + + QVector roles; + roles << Qt::DisplayRole; + + if (old_column >= 0) { + //Notify old column has changed + emit dataChanged(index(0, old_column), index(rowCount(), old_column), roles); + } + + if (column_ >= 0) { + //Notify new column has changed + emit dataChanged(index(0, column_), index(rowCount(), column_), roles); + } +} diff --git a/ui/qt/models/info_proxy_model.h b/ui/qt/models/info_proxy_model.h new file mode 100644 index 00000000..f9775b4d --- /dev/null +++ b/ui/qt/models/info_proxy_model.h @@ -0,0 +1,47 @@ +/** @file + * + * Proxy model for displaying an info text at the end of any QAbstractListModel + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INFO_PROXY_MODEL_H +#define INFO_PROXY_MODEL_H + +#include + +#include +#include + +class InfoProxyModel : public QIdentityProxyModel +{ +public: + explicit InfoProxyModel(QObject * parent = 0); + ~InfoProxyModel(); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const; + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + + virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + virtual QModelIndex mapFromSource(const QModelIndex &fromIndex) const; + + void appendInfo(QString info); + void clearInfos(); + + void setColumn(int column); + +private: + + int column_; + + QStringList infos_; +}; + +#endif // INFO_PROXY_MODEL_H diff --git a/ui/qt/models/interface_sort_filter_model.cpp b/ui/qt/models/interface_sort_filter_model.cpp new file mode 100644 index 00000000..9d9c5dc8 --- /dev/null +++ b/ui/qt/models/interface_sort_filter_model.cpp @@ -0,0 +1,396 @@ +/* interface_sort_filter_model.cpp + * Proxy model for the display of interface data for the interface tree + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include "main_application.h" + +#include + +InterfaceSortFilterModel::InterfaceSortFilterModel(QObject *parent) : + QSortFilterProxyModel(parent) +{ + resetAllFilter(); +} + +void InterfaceSortFilterModel::resetAllFilter() +{ + _filterHidden = true; + _filterTypes = true; + _invertTypeFilter = false; + _storeOnChange = false; + _sortByActivity = false; +#ifdef HAVE_PCAP_REMOTE + _remoteDisplay = true; +#endif + + /* Adding all columns, to have a default setting */ + for (int col = 0; col < IFTREE_COL_MAX; col++) + _columns.append((InterfaceTreeColumns)col); + + invalidateFilter(); + invalidate(); +} + +void InterfaceSortFilterModel::setStoreOnChange(bool storeOnChange) +{ + _storeOnChange = storeOnChange; + if (storeOnChange) + { + connect(mainApp, &MainApplication::preferencesChanged, this, &InterfaceSortFilterModel::resetPreferenceData); + resetPreferenceData(); + } +} + +void InterfaceSortFilterModel::setFilterHidden(bool filter) +{ + _filterHidden = filter; + + invalidate(); +} + + +void InterfaceSortFilterModel::setSortByActivity(bool sort) +{ + _sortByActivity = sort; + invalidate(); +} + +bool InterfaceSortFilterModel::sortByActivity() const +{ + return _sortByActivity; +} + +#ifdef HAVE_PCAP_REMOTE +void InterfaceSortFilterModel::setRemoteDisplay(bool remoteDisplay) +{ + _remoteDisplay = remoteDisplay; + + invalidate(); +} + +bool InterfaceSortFilterModel::remoteDisplay() +{ + return _remoteDisplay; +} + +void InterfaceSortFilterModel::toggleRemoteDisplay() +{ + _remoteDisplay = ! _remoteDisplay; + + if (_storeOnChange) + { + prefs.gui_interfaces_remote_display = ! _remoteDisplay; + + prefs_main_write(); + } + + invalidateFilter(); + invalidate(); +} + +bool InterfaceSortFilterModel::remoteInterfacesExist() +{ + bool exist = false; + + if (sourceModel()->rowCount() == 0) + return exist; + + for (int idx = 0; idx < sourceModel()->rowCount() && ! exist; idx++) + exist = ((InterfaceTreeModel *)sourceModel())->isRemote(idx); + + return exist; +} +#endif + +void InterfaceSortFilterModel::setFilterByType(bool filter, bool invert) +{ + _filterTypes = filter; + _invertTypeFilter = invert; + + invalidate(); +} + +void InterfaceSortFilterModel::resetPreferenceData() +{ + displayHiddenTypes.clear(); + QString stored_prefs(prefs.gui_interfaces_hide_types); + if (stored_prefs.length() > 0) + { + QStringList ifTypesStored = stored_prefs.split(','); + QStringList::const_iterator it = ifTypesStored.constBegin(); + while (it != ifTypesStored.constEnd()) + { + int i_val = (*it).toInt(); + if (! displayHiddenTypes.contains(i_val)) + displayHiddenTypes.append(i_val); + ++it; + } + } + +#if 0 + // Disabled until bug 13354 is fixed + _filterHidden = ! prefs.gui_interfaces_show_hidden; +#endif +#ifdef HAVE_PCAP_REMOTE + _remoteDisplay = prefs.gui_interfaces_remote_display; +#endif + + invalidate(); +} + +bool InterfaceSortFilterModel::filterHidden() const +{ + return _filterHidden; +} + +void InterfaceSortFilterModel::toggleFilterHidden() +{ + _filterHidden = ! _filterHidden; + + if (_storeOnChange) + { + prefs.gui_interfaces_show_hidden = ! _filterHidden; + + prefs_main_write(); + } + + invalidateFilter(); + invalidate(); +} + +bool InterfaceSortFilterModel::filterByType() const +{ + return _filterTypes; +} + +int InterfaceSortFilterModel::interfacesHidden() +{ +#ifdef HAVE_LIBPCAP + if (! global_capture_opts.all_ifaces) + return 0; +#endif + + return sourceModel()->rowCount() - rowCount(); +} + +QList InterfaceSortFilterModel::typesDisplayed() +{ + QList shownTypes; + + if (sourceModel()->rowCount() == 0) + return shownTypes; + + for (int idx = 0; idx < sourceModel()->rowCount(); idx++) + { + int type = ((InterfaceTreeModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_TYPE).toInt(); + bool hidden = ((InterfaceTreeModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_HIDDEN).toBool(); + + if (! hidden) + { + if (! shownTypes.contains(type)) + shownTypes.append(type); + } + } + + return shownTypes; +} + +void InterfaceSortFilterModel::setInterfaceTypeVisible(int ifType, bool visible) +{ + if (visible && displayHiddenTypes.contains(ifType)) + displayHiddenTypes.removeAll(ifType); + else if (! visible && ! displayHiddenTypes.contains(ifType)) + displayHiddenTypes.append(ifType); + else + /* Nothing should have changed */ + return; + + if (_storeOnChange) + { + QString new_pref; + QList::const_iterator it = displayHiddenTypes.constBegin(); + while (it != displayHiddenTypes.constEnd()) + { + new_pref.append(QString("%1,").arg(*it)); + ++it; + } + if (new_pref.length() > 0) + new_pref = new_pref.left(new_pref.length() - 1); + + prefs.gui_interfaces_hide_types = qstring_strdup(new_pref); + + prefs_main_write(); + } + + invalidateFilter(); + invalidate(); +} + +void InterfaceSortFilterModel::toggleTypeVisibility(int ifType) +{ + bool checked = isInterfaceTypeShown(ifType); + + setInterfaceTypeVisible(ifType, checked ? false : true); +} + +bool InterfaceSortFilterModel::isInterfaceTypeShown(int ifType) const +{ + bool result = false; + + if (displayHiddenTypes.size() == 0) + result = true; + else if (! displayHiddenTypes.contains(ifType)) + result = true; + + return ((_invertTypeFilter && ! result) || (! _invertTypeFilter && result) ); +} + +bool InterfaceSortFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const +{ + QModelIndex realIndex = sourceModel()->index(sourceRow, 0, sourceParent); + + if (! realIndex.isValid()) + return false; + +#ifdef HAVE_LIBPCAP + int idx = realIndex.row(); + + /* No data loaded, we do not display anything */ + if (sourceModel()->rowCount() == 0) + return false; + + int type = -1; + bool hidden = false; + + if (dynamic_cast(sourceModel()) != 0) + { + type = ((InterfaceTreeCacheModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_TYPE).toInt(); + hidden = ((InterfaceTreeCacheModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::UserRole).toBool(); + } + else if (dynamic_cast(sourceModel()) != 0) + { + type = ((InterfaceTreeModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_TYPE).toInt(); + hidden = ((InterfaceTreeModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::UserRole).toBool(); + } + else + return false; + + if (hidden && _filterHidden) + return false; + + if (_filterTypes && ! isInterfaceTypeShown(type)) + { +#ifdef HAVE_PCAP_REMOTE + /* Remote interfaces have the if type IF_WIRED, therefore would be filtered, if not explicitly checked here */ + if (type != IF_WIRED || ! ((InterfaceTreeModel *)sourceModel())->isRemote(idx)) +#endif + return false; + } + +#ifdef HAVE_PCAP_REMOTE + if (((InterfaceTreeModel *)sourceModel())->isRemote(idx)) + { + if (! _remoteDisplay) + return false; + } +#endif + +#endif + + return true; +} + +bool InterfaceSortFilterModel::filterAcceptsColumn(int sourceColumn, const QModelIndex & sourceParent) const +{ + QModelIndex realIndex = sourceModel()->index(0, sourceColumn, sourceParent); + + if (! realIndex.isValid()) + return false; + + if (! _columns.contains((InterfaceTreeColumns)sourceColumn)) + return false; + + return true; +} + +void InterfaceSortFilterModel::setColumns(QList columns) +{ + _columns.clear(); + _columns.append(columns); +} + +int InterfaceSortFilterModel::mapSourceToColumn(InterfaceTreeColumns mdlIndex) +{ + if (! _columns.contains(mdlIndex)) + return -1; + + return static_cast(_columns.indexOf(mdlIndex, 0)); +} + +QModelIndex InterfaceSortFilterModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (! proxyIndex.isValid()) + return QModelIndex(); + + if (! sourceModel()) + return QModelIndex(); + + QModelIndex baseIndex = QSortFilterProxyModel::mapToSource(proxyIndex); + QModelIndex newIndex = sourceModel()->index(baseIndex.row(), _columns.at(proxyIndex.column())); + + return newIndex; +} + +QModelIndex InterfaceSortFilterModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (! sourceIndex.isValid()) + return QModelIndex(); + else if (! _columns.contains((InterfaceTreeColumns) sourceIndex.column()) ) + return QModelIndex(); + + QModelIndex newIndex = QSortFilterProxyModel::mapFromSource(sourceIndex); + + return index(newIndex.row(), static_cast(_columns.indexOf((InterfaceTreeColumns) sourceIndex.column()))); +} + +QString InterfaceSortFilterModel::interfaceError() +{ + QString result; + + InterfaceTreeModel * sourceModel = dynamic_cast(this->sourceModel()); + if (sourceModel != NULL) + result = sourceModel->interfaceError(); + + if (result.size() == 0 && rowCount() == 0) + result = tr("No interfaces to be displayed. %1 interfaces hidden.").arg(interfacesHidden()); + + return result; +} + +bool InterfaceSortFilterModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + bool leftActive = source_left.sibling(source_left.row(), InterfaceTreeColumns::IFTREE_COL_ACTIVE).data(Qt::UserRole).toBool(); + bool rightActive = source_right.sibling(source_right.row(), InterfaceTreeColumns::IFTREE_COL_ACTIVE).data(Qt::UserRole).toBool(); + + if (_sortByActivity && rightActive && ! leftActive) + return true; + + return QSortFilterProxyModel::lessThan(source_left, source_right); +} + diff --git a/ui/qt/models/interface_sort_filter_model.h b/ui/qt/models/interface_sort_filter_model.h new file mode 100644 index 00000000..5bf69d9d --- /dev/null +++ b/ui/qt/models/interface_sort_filter_model.h @@ -0,0 +1,87 @@ +/** @file + * + * Proxy model for the display of interface data for the interface tree + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INTERFACE_SORT_FILTER_MODEL_H +#define INTERFACE_SORT_FILTER_MODEL_H + +#include + +#include + +#include + +#include + +class InterfaceSortFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + InterfaceSortFilterModel(QObject *parent); + + void setStoreOnChange(bool storeOnChange); + void resetAllFilter(); + + void setFilterHidden(bool filter); + bool filterHidden() const; + int interfacesHidden(); + void toggleFilterHidden(); + + void setSortByActivity(bool sort); + bool sortByActivity() const; + +#ifdef HAVE_PCAP_REMOTE + void setRemoteDisplay(bool remoteDisplay); + bool remoteDisplay(); + void toggleRemoteDisplay(); + bool remoteInterfacesExist(); +#endif + + void setInterfaceTypeVisible(int ifType, bool visible); + bool isInterfaceTypeShown(int ifType) const; + void setFilterByType(bool filter, bool invert = false); + bool filterByType() const; + void toggleTypeVisibility(int ifType); + + QList typesDisplayed(); + + void setColumns(QList columns); + int mapSourceToColumn(InterfaceTreeColumns mdlIndex); + + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + + QString interfaceError(); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const; + bool filterAcceptsColumn(int source_column, const QModelIndex & source_parent) const; + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + +private: + bool _filterHidden; + bool _filterTypes; + bool _invertTypeFilter; + bool _storeOnChange; + bool _sortByActivity; + +#ifdef HAVE_PCAP_REMOTE + bool _remoteDisplay; +#endif + + QList displayHiddenTypes; + + QList _columns; + +private slots: + void resetPreferenceData(); +}; + +#endif // INTERFACE_SORT_FILTER_MODEL_H diff --git a/ui/qt/models/interface_tree_cache_model.cpp b/ui/qt/models/interface_tree_cache_model.cpp new file mode 100644 index 00000000..71eda509 --- /dev/null +++ b/ui/qt/models/interface_tree_cache_model.cpp @@ -0,0 +1,584 @@ +/* interface_tree_cache_model.cpp + * Model caching interface changes before sending them to global storage + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include + +#include "epan/prefs.h" + +#include +#include "ui/capture_globals.h" +#include "wsutil/utf8_entities.h" + +#include "wiretap/wtap.h" + +#include "main_application.h" + +#include + +InterfaceTreeCacheModel::InterfaceTreeCacheModel(QObject *parent) : + QIdentityProxyModel(parent) +{ + /* ATTENTION: This cache model is not intended to be used with anything + * else then InterfaceTreeModel, and will break with anything else + * leading to unintended results. */ + sourceModel = new InterfaceTreeModel(parent); + + QIdentityProxyModel::setSourceModel(sourceModel); + storage = new QMap *>(); + + checkableColumns << IFTREE_COL_HIDDEN << IFTREE_COL_PROMISCUOUSMODE; +#ifdef HAVE_PCAP_CREATE + checkableColumns << IFTREE_COL_MONITOR_MODE; +#endif + + editableColumns << IFTREE_COL_COMMENT << IFTREE_COL_SNAPLEN << IFTREE_COL_PIPE_PATH; + +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + editableColumns << IFTREE_COL_BUFFERLEN; +#endif +} + +InterfaceTreeCacheModel::~InterfaceTreeCacheModel() +{ +#ifdef HAVE_LIBPCAP + /* This list should only exist, if the dialog is closed, without calling save first */ + newDevices.clear(); +#endif + + delete storage; + delete sourceModel; +} + +QVariant InterfaceTreeCacheModel::getColumnContent(int idx, int col, int role) +{ + return InterfaceTreeCacheModel::data(index(idx, col), role); +} + +#ifdef HAVE_LIBPCAP +void InterfaceTreeCacheModel::reset(int row) +{ + if (row < 0) + { + delete storage; + storage = new QMap *>(); + } + else + { + if (storage->count() > row) + storage->remove(storage->keys().at(row)); + } +} + +void InterfaceTreeCacheModel::saveNewDevices() +{ + QList::const_iterator it = newDevices.constBegin(); + /* idx is used for iterating only over the indices of the new devices. As all new + * devices are stored with an index higher then sourceModel->rowCount(), we start + * only with those storage indices. + * it is just the iterator over the new devices. A new device must not necessarily + * have storage, which will lead to that device not being stored in global_capture_opts */ + for (int idx = sourceModel->rowCount(); it != newDevices.constEnd(); ++it, idx++) + { + interface_t *device = const_cast(&(*it)); + bool useDevice = false; + + QMap * dataField = storage->value(idx, 0); + /* When devices are being added, they are added using generic values. So only devices + * whose data have been changed should be used from here on out. */ + if (dataField != 0) + { + if (device->if_info.type != IF_PIPE) + { + continue; + } + + if (device->if_info.type == IF_PIPE) + { + QVariant saveValue = dataField->value(IFTREE_COL_PIPE_PATH); + if (saveValue.isValid()) + { + g_free(device->if_info.name); + device->if_info.name = qstring_strdup(saveValue.toString()); + + g_free(device->name); + device->name = qstring_strdup(saveValue.toString()); + + g_free(device->display_name); + device->display_name = qstring_strdup(saveValue.toString()); + useDevice = true; + } + } + + if (useDevice) + g_array_append_val(global_capture_opts.all_ifaces, *device); + + } + + /* All entries of this new devices have been considered */ + storage->remove(idx); + delete dataField; + } + + newDevices.clear(); +} + +void InterfaceTreeCacheModel::save() +{ + if (storage->count() == 0) + return; + + QMap prefStorage; + + /* No devices are hidden until checking "Show" state */ + prefStorage[&prefs.capture_devices_hide] = QStringList(); + + /* Storing new devices first including their changed values */ + saveNewDevices(); + + + for (unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++) + { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + + if (! device->name) + continue; + + /* Try to load a saved value row for this index */ + QMap * dataField = storage->value(idx, 0); + + /* Handle the storing of values for this device here */ + if (dataField) + { + QMap::const_iterator it = dataField->constBegin(); + while (it != dataField->constEnd()) + { + InterfaceTreeColumns col = it.key(); + QVariant saveValue = it.value(); + + /* Setting the field values for each individual saved value cannot be generic, as the + * struct cannot be accessed in a generic way. Therefore below, each individually changed + * value has to be handled separately. Comments are stored only in the preference file + * and applied to the data name during loading. Therefore comments are not handled here */ + + if (col == IFTREE_COL_HIDDEN) + { + device->hidden = saveValue.toBool(); + } + else if (device->if_info.type == IF_EXTCAP) + { + /* extcap interfaces do not have the following columns. + * ATTENTION: all generic columns must be added, BEFORE this + * if-clause, or they will be ignored for extcap interfaces */ + } + else if (col == IFTREE_COL_PROMISCUOUSMODE) + { + device->pmode = saveValue.toBool(); + } +#ifdef HAVE_PCAP_CREATE + else if (col == IFTREE_COL_MONITOR_MODE) + { + device->monitor_mode_enabled = saveValue.toBool(); + } +#endif + else if (col == IFTREE_COL_SNAPLEN) + { + int iVal = saveValue.toInt(); + if (iVal != WTAP_MAX_PACKET_SIZE_STANDARD) + { + device->has_snaplen = true; + device->snaplen = iVal; + } + else + { + device->has_snaplen = false; + device->snaplen = WTAP_MAX_PACKET_SIZE_STANDARD; + } + } +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + else if (col == IFTREE_COL_BUFFERLEN) + { + device->buffer = saveValue.toInt(); + } +#endif + ++it; + } + } + + QVariant content = getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::CheckStateRole); + if (content.isValid() && static_cast(content.toInt()) == Qt::Unchecked) + prefStorage[&prefs.capture_devices_hide] << QString(device->name); + + content = getColumnContent(idx, IFTREE_COL_COMMENT); + if (content.isValid() && content.toString().size() > 0) + prefStorage[&prefs.capture_devices_descr] << QString("%1(%2)").arg(device->name).arg(content.toString()); + + bool allowExtendedColumns = true; + + if (device->if_info.type == IF_EXTCAP) + allowExtendedColumns = false; + + if (allowExtendedColumns) + { + content = getColumnContent(idx, IFTREE_COL_PROMISCUOUSMODE, Qt::CheckStateRole); + if (content.isValid()) + { + bool value = static_cast(content.toInt()) == Qt::Checked; + prefStorage[&prefs.capture_devices_pmode] << QString("%1(%2)").arg(device->name).arg(value ? 1 : 0); + } + +#ifdef HAVE_PCAP_CREATE + content = getColumnContent(idx, IFTREE_COL_MONITOR_MODE, Qt::CheckStateRole); + if (content.isValid() && static_cast(content.toInt()) == Qt::Checked) + prefStorage[&prefs.capture_devices_monitor_mode] << QString(device->name); +#endif + + content = getColumnContent(idx, IFTREE_COL_SNAPLEN); + if (content.isValid()) + { + int value = content.toInt(); + prefStorage[&prefs.capture_devices_snaplen] << + QString("%1:%2(%3)").arg(device->name). + arg(device->has_snaplen ? 1 : 0). + arg(device->has_snaplen ? value : WTAP_MAX_PACKET_SIZE_STANDARD); + } + +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + content = getColumnContent(idx, IFTREE_COL_BUFFERLEN); + if (content.isValid()) + { + int value = content.toInt(); + if (value != -1) + { + prefStorage[&prefs.capture_devices_buffersize] << + QString("%1(%2)").arg(device->name). + arg(value); + } + } +#endif + } + } + + QMap::const_iterator it = prefStorage.constBegin(); + while (it != prefStorage.constEnd()) + { + char ** key = it.key(); + + g_free(*key); + *key = qstring_strdup(it.value().join(",")); + + ++it; + } + + mainApp->emitAppSignal(MainApplication::LocalInterfacesChanged); +} +#endif + +int InterfaceTreeCacheModel::rowCount(const QModelIndex & parent) const +{ + int totalCount = sourceModel->rowCount(parent); +#ifdef HAVE_LIBPCAP + totalCount += newDevices.size(); +#endif + return totalCount; +} + +bool InterfaceTreeCacheModel::changeIsAllowed(InterfaceTreeColumns col) const +{ + if (editableColumns.contains(col) || checkableColumns.contains(col)) + return true; + return false; +} + +#ifdef HAVE_LIBPCAP +const interface_t * InterfaceTreeCacheModel::lookup(const QModelIndex &index) const +{ + const interface_t * result = 0; + + if (! index.isValid() || ! global_capture_opts.all_ifaces) + return result; + + int idx = index.row(); + + if ((unsigned int) idx >= global_capture_opts.all_ifaces->len) + { + idx = idx - global_capture_opts.all_ifaces->len; + if (idx < newDevices.size()) + result = &newDevices[idx]; + } + else + { + result = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + } + + return result; +} +#endif + +/* This checks if the column can be edited for the given index. This differs from + * isAvailableField in such a way, that it is only used in flags and not any + * other method.*/ +bool InterfaceTreeCacheModel::isAllowedToBeEdited(const QModelIndex &index) const +{ +#ifndef HAVE_LIBPCAP + Q_UNUSED(index); +#else + const interface_t * device = lookup(index); + if (device == 0) + return false; + + InterfaceTreeColumns col = (InterfaceTreeColumns) index.column(); + if (device->if_info.type == IF_EXTCAP) + { + /* extcap interfaces do not have those settings */ + if (col == IFTREE_COL_PROMISCUOUSMODE || col == IFTREE_COL_SNAPLEN) + return false; +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + if (col == IFTREE_COL_BUFFERLEN) + return false; +#endif + } +#endif + return true; +} + +// Whether this field is available for modification and display. +bool InterfaceTreeCacheModel::isAvailableField(const QModelIndex &index) const +{ +#ifndef HAVE_LIBPCAP + Q_UNUSED(index); +#else + const interface_t * device = lookup(index); + + if (device == 0) + return false; + + InterfaceTreeColumns col = (InterfaceTreeColumns) index.column(); + if (col == IFTREE_COL_HIDDEN) + { + // Do not allow default capture interface to be hidden. + if (! g_strcmp0(prefs.capture_device, device->display_name)) + return false; + } +#endif + + return true; +} + +Qt::ItemFlags InterfaceTreeCacheModel::flags(const QModelIndex &index) const +{ + if (! index.isValid()) + return Qt::ItemFlags(); + + Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + InterfaceTreeColumns col = (InterfaceTreeColumns) index.column(); + + if (changeIsAllowed(col) && isAvailableField(index) && isAllowedToBeEdited(index)) + { + if (checkableColumns.contains(col)) + { + flags |= Qt::ItemIsUserCheckable; + } + else + { + flags |= Qt::ItemIsEditable; + } + } + + return flags; +} + +bool InterfaceTreeCacheModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (! index.isValid()) + return false; + + if (! isAvailableField(index)) + return false; + + int row = index.row(); + InterfaceTreeColumns col = (InterfaceTreeColumns)index.column(); + + if (role == Qt::CheckStateRole || role == Qt::EditRole) + { + if (changeIsAllowed(col) ) + { + QVariant saveValue = value; + + QMap * dataField = 0; + /* obtain the list of already stored changes for this row. If none exist + * create a new storage row for this entry */ + if ((dataField = storage->value(row, 0)) == 0) + { + dataField = new QMap(); + storage->insert(row, dataField); + } + + dataField->insert(col, saveValue); + + return true; + } + } + + return false; +} + +QVariant InterfaceTreeCacheModel::data(const QModelIndex &index, int role) const +{ + if (! index.isValid()) + return QVariant(); + + int row = index.row(); + + InterfaceTreeColumns col = (InterfaceTreeColumns)index.column(); + + if (isAvailableField(index) && isAllowedToBeEdited(index)) + { + if (((role == Qt::DisplayRole || role == Qt::EditRole) && editableColumns.contains(col)) || + (role == Qt::CheckStateRole && checkableColumns.contains(col)) ) + { + QMap * dataField = 0; + if ((dataField = storage->value(row, 0)) != 0) + { + if (dataField->contains(col)) + { + return dataField->value(col, QVariant()); + } + } + } + } + else + { + if (role == Qt::CheckStateRole) + return QVariant(); + else if (role == Qt::DisplayRole) + return QString(UTF8_EM_DASH); + } + + if (row < sourceModel->rowCount()) + { + return sourceModel->data(index, role); + } +#ifdef HAVE_LIBPCAP + else + { + /* Handle all fields, which will have to be displayed for new devices. Only pipes + * are supported at the moment, so the information to be displayed is pretty limited. + * After saving, the devices are stored in global_capture_opts and no longer + * classify as new devices. */ + const interface_t * device = lookup(index); + + if (device != 0) + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + if (col == IFTREE_COL_PIPE_PATH || + col == IFTREE_COL_NAME || + col == IFTREE_COL_DESCRIPTION) + { + + QMap * dataField = 0; + if ((dataField = storage->value(row, 0)) != 0 && + dataField->contains(IFTREE_COL_PIPE_PATH)) + { + return dataField->value(IFTREE_COL_PIPE_PATH, QVariant()); + } + else + return QString(device->name); + } + else if (col == IFTREE_COL_TYPE) + { + return QVariant::fromValue((int)device->if_info.type); + } + } + else if (role == Qt::CheckStateRole) + { + if (col == IFTREE_COL_HIDDEN) + { + // Do not allow default capture interface to be hidden. + if (! g_strcmp0(prefs.capture_device, device->display_name)) + return QVariant(); + + /* Hidden is a de-selection, therefore inverted logic here */ + return device->hidden ? Qt::Unchecked : Qt::Checked; + } + } + } + } +#endif + + return QVariant(); +} + +#ifdef HAVE_LIBPCAP +QModelIndex InterfaceTreeCacheModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row >= sourceModel->rowCount() && (row - sourceModel->rowCount()) < newDevices.count()) + { + return createIndex(row, column, (void *)0); + } + + return QIdentityProxyModel::index(row, column, parent); +} + +void InterfaceTreeCacheModel::addDevice(const interface_t * newDevice) +{ + emit beginInsertRows(QModelIndex(), rowCount(), rowCount()); + newDevices << *newDevice; + emit endInsertRows(); +} + +void InterfaceTreeCacheModel::deleteDevice(const QModelIndex &index) +{ + if (! index.isValid()) + return; + + emit beginRemoveRows(QModelIndex(), index.row(), index.row()); + + int row = index.row(); + + /* device is in newDevices */ + if (row >= sourceModel->rowCount()) + { + int newDeviceIdx = row - sourceModel->rowCount(); + + newDevices.removeAt(newDeviceIdx); + if (storage->contains(index.row())) + storage->remove(index.row()); + + /* The storage array has to be resorted, if the index, that was removed + * had been in the middle of the array. Can't start at index.row(), as + * it may not be contained in storage + * We must iterate using a list, not an iterator, otherwise the change + * will fold on itself. */ + QList storageKeys = storage->keys(); + for (int i = 0; i < storageKeys.size(); ++i) + { + int key = storageKeys.at(i); + if (key > index.row()) + { + storage->insert(key - 1, storage->value(key)); + storage->remove(key); + } + } + + emit endRemoveRows(); + } + else + { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, row); + capture_opts_free_interface_t(device); + global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, row); + emit endRemoveRows(); + mainApp->emitAppSignal(MainApplication::LocalInterfacesChanged); + } +} +#endif diff --git a/ui/qt/models/interface_tree_cache_model.h b/ui/qt/models/interface_tree_cache_model.h new file mode 100644 index 00000000..9b51c10b --- /dev/null +++ b/ui/qt/models/interface_tree_cache_model.h @@ -0,0 +1,66 @@ +/** @file + * + * Model caching interface changes before sending them to global storage + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INTERFACE_TREE_CACHE_MODEL_H_ +#define INTERFACE_TREE_CACHE_MODEL_H_ + +#include + +#include +#include +#include + +class InterfaceTreeCacheModel : public QIdentityProxyModel +{ +public: + explicit InterfaceTreeCacheModel(QObject *parent); + ~InterfaceTreeCacheModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) const; + + QVariant getColumnContent(int idx, int col, int role = Qt::DisplayRole); + +#ifdef HAVE_LIBPCAP + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + + void reset(int row); + void save(); + + void addDevice(const interface_t * newDevice); + void deleteDevice(const QModelIndex &index); +#endif + +private: + InterfaceTreeModel * sourceModel; + +#ifdef HAVE_LIBPCAP + QList newDevices; + + void saveNewDevices(); +#endif + QMap *> * storage; + QList editableColumns; + QList checkableColumns; + +#ifdef HAVE_LIBPCAP + const interface_t * lookup(const QModelIndex &index) const; +#endif + + bool changeIsAllowed(InterfaceTreeColumns col) const; + bool isAvailableField(const QModelIndex &index) const; + bool isAllowedToBeEdited(const QModelIndex &index) const; + +}; +#endif /* INTERFACE_TREE_CACHE_MODEL_H_ */ diff --git a/ui/qt/models/interface_tree_model.cpp b/ui/qt/models/interface_tree_model.cpp new file mode 100644 index 00000000..547b9010 --- /dev/null +++ b/ui/qt/models/interface_tree_model.cpp @@ -0,0 +1,545 @@ +/* interface_tree_model.cpp + * Model for the interface data for display in the interface frame + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#ifdef HAVE_LIBPCAP +#include "ui/capture.h" +#include "capture/capture-pcap-util.h" +#include "capture_opts.h" +#include "ui/capture_ui_utils.h" +#include "ui/capture_globals.h" +#endif + +#include "wsutil/filesystem.h" + +#include +#include +#include "main_application.h" + +/* Needed for the meta type declaration of QList* */ +#include + +#include "extcap.h" + +const QString InterfaceTreeModel::DefaultNumericValue = QObject::tr("default"); + +/** + * This is the data model for interface trees. It implies, that the index within + * global_capture_opts.all_ifaces is identical to the row. This is always the case, even + * when interfaces are hidden by the proxy model. But for this to work, every access + * to the index from within the view, has to be filtered through the proxy model. + */ +InterfaceTreeModel::InterfaceTreeModel(QObject *parent) : + QAbstractTableModel(parent) +#ifdef HAVE_LIBPCAP + ,stat_cache_(NULL) +#endif +{ + connect(mainApp, &MainApplication::appInitialized, this, &InterfaceTreeModel::interfaceListChanged); + connect(mainApp, &MainApplication::localInterfaceListChanged, this, &InterfaceTreeModel::interfaceListChanged); +} + +InterfaceTreeModel::~InterfaceTreeModel(void) +{ +#ifdef HAVE_LIBPCAP + if (stat_cache_) { + capture_stat_stop(stat_cache_); + stat_cache_ = NULL; + } +#endif // HAVE_LIBPCAP +} + +QString InterfaceTreeModel::interfaceError() +{ +#ifdef HAVE_LIBPCAP + // + // First, see if there was an error fetching the interfaces. + // If so, report it. + // + if (global_capture_opts.ifaces_err != 0) + { + return tr(global_capture_opts.ifaces_err_info); + } + + // + // Otherwise, if there are no rows, there were no interfaces + // found. + // + if (rowCount() == 0) + { + return tr("No interfaces found."); + } + + // + // No error. Return an empty string. + // + return ""; +#else + // + // We were built without pcap support, so we have no notion of + // local interfaces. + // + return tr("This version of Wireshark was built without packet capture support."); +#endif +} + +int InterfaceTreeModel::rowCount(const QModelIndex &) const +{ +#ifdef HAVE_LIBPCAP + return (global_capture_opts.all_ifaces ? global_capture_opts.all_ifaces->len : 0); +#else + /* Currently no interfaces available for libpcap-less builds */ + return 0; +#endif +} + +int InterfaceTreeModel::columnCount(const QModelIndex &) const +{ + /* IFTREE_COL_MAX is not being displayed, it is the definition for the maximum numbers of columns */ + return ((int) IFTREE_COL_MAX); +} + +QVariant InterfaceTreeModel::data(const QModelIndex &index, int role) const +{ +#ifdef HAVE_LIBPCAP + bool interfacesLoaded = true; + if (! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len == 0) + interfacesLoaded = false; + + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + InterfaceTreeColumns col = (InterfaceTreeColumns) index.column(); + + if (interfacesLoaded) + { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, row); + + /* Data for display in cell */ + if (role == Qt::DisplayRole) + { + /* Only the name is being displayed */ + if (col == IFTREE_COL_NAME) + { + return QString(device->name); + } + else if (col == IFTREE_COL_DESCRIPTION) + { + return QString(device->friendly_name); + } + else if (col == IFTREE_COL_DISPLAY_NAME) + { + return QString(device->display_name); + } + else if (col == IFTREE_COL_PIPE_PATH) + { + return QString(device->if_info.name); + } + else if (col == IFTREE_COL_CAPTURE_FILTER) + { + if (device->cfilter && strlen(device->cfilter) > 0) + return html_escape(QString(device->cfilter)); + } + else if (col == IFTREE_COL_EXTCAP_PATH) + { + return QString(device->if_info.extcap); + } + else if (col == IFTREE_COL_SNAPLEN) + { + return device->has_snaplen ? QString::number(device->snaplen) : DefaultNumericValue; + } +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + else if (col == IFTREE_COL_BUFFERLEN) + { + return QString::number(device->buffer); + } +#endif + else if (col == IFTREE_COL_TYPE) + { + return QVariant::fromValue((int)device->if_info.type); + } + else if (col == IFTREE_COL_COMMENT) + { + QString comment = gchar_free_to_qstring(capture_dev_user_descr_find(device->name)); + if (comment.length() > 0) + return comment; + else + return QString(device->if_info.vendor_description); + } + else if (col == IFTREE_COL_DLT) + { + // XXX - this is duplicated in + // InterfaceTreeWidgetItem::updateInterfaceColumns; + // it should be done in common code somewhere. + QString linkname; + if (device->active_dlt == -1) + linkname = "Unknown"; + else { + linkname = QObject::tr("DLT %1").arg(device->active_dlt); + for (GList *list = device->links; list != Q_NULLPTR; list = gxx_list_next(list)) { + link_row *linkr = gxx_list_data(link_row *, list); + if (linkr->dlt == device->active_dlt) { + linkname = linkr->name; + break; + } + } + } + + return linkname; + } + else + { + /* Return empty string for every other DisplayRole */ + return QVariant(); + } + } + else if (role == Qt::CheckStateRole) + { + if (col == IFTREE_COL_HIDDEN) + { + /* Hidden is a de-selection, therefore inverted logic here */ + return device->hidden ? Qt::Unchecked : Qt::Checked; + } + else if (col == IFTREE_COL_PROMISCUOUSMODE) + { + return device->pmode ? Qt::Checked : Qt::Unchecked; + } +#ifdef HAVE_PCAP_CREATE + else if (col == IFTREE_COL_MONITOR_MODE) + { + return device->monitor_mode_enabled ? Qt::Checked : Qt::Unchecked; + } +#endif + } + /* Used by SparkLineDelegate for loading the data for the statistics line */ + else if (role == Qt::UserRole) + { + if (col == IFTREE_COL_STATS) + { + if ((active.contains(device->name) && active[device->name]) && points.contains(device->name)) + return QVariant::fromValue(points[device->name]); + } + else if (col == IFTREE_COL_ACTIVE) + { + if (active.contains(device->name)) + return QVariant::fromValue(active[device->name]); + } + else if (col == IFTREE_COL_HIDDEN) + { + return QVariant::fromValue((bool)device->hidden); + } + } + /* Displays the configuration icon for extcap interfaces */ + else if (role == Qt::DecorationRole) + { + if (col == IFTREE_COL_EXTCAP) + { + if (device->if_info.type == IF_EXTCAP) + return QIcon(StockIcon("x-capture-options")); + } + } + else if (role == Qt::TextAlignmentRole) + { + if (col == IFTREE_COL_EXTCAP) + { + return Qt::AlignRight; + } + } + /* Displays the tooltip for each row */ + else if (role == Qt::ToolTipRole) + { + return toolTipForInterface(row); + } + } +#else + Q_UNUSED(index) + Q_UNUSED(role) +#endif + + return QVariant(); +} + +QVariant InterfaceTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) + { + if (role == Qt::DisplayRole) + { + if (section == IFTREE_COL_HIDDEN) + { + return tr("Show"); + } + else if (section == IFTREE_COL_NAME) + { + return tr("Interface Name"); + } + else if (section == IFTREE_COL_DESCRIPTION) + { + return tr("Friendly Name"); + } + else if (section == IFTREE_COL_DISPLAY_NAME) + { + return tr("Friendly Name"); + } + else if (section == IFTREE_COL_PIPE_PATH) + { + return tr("Local Pipe Path"); + } + else if (section == IFTREE_COL_COMMENT) + { + return tr("Comment"); + } + else if (section == IFTREE_COL_DLT) + { + return tr("Link-Layer Header"); + } + else if (section == IFTREE_COL_PROMISCUOUSMODE) + { + return tr("Promiscuous"); + } + else if (section == IFTREE_COL_SNAPLEN) + { + return tr("Snaplen (B)"); + } +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + else if (section == IFTREE_COL_BUFFERLEN) + { + return tr("Buffer (MB)"); + } +#endif +#ifdef HAVE_PCAP_CREATE + else if (section == IFTREE_COL_MONITOR_MODE) + { + return tr("Monitor Mode"); + } +#endif + else if (section == IFTREE_COL_CAPTURE_FILTER) + { + return tr("Capture Filter"); + } + } + } + + return QVariant(); +} + +QVariant InterfaceTreeModel::getColumnContent(int idx, int col, int role) +{ + return InterfaceTreeModel::data(index(idx, col), role); +} + +#ifdef HAVE_PCAP_REMOTE +bool InterfaceTreeModel::isRemote(int idx) +{ + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + if (device->remote_opts.src_type == CAPTURE_IFREMOTE) + return true; + return false; +} +#endif + +/** + * The interface list has changed. global_capture_opts.all_ifaces may have been reloaded + * or changed with current data. beginResetModel() and endResetModel() will signalize the + * proxy model and the view, that the data has changed and the view has to reload + */ +void InterfaceTreeModel::interfaceListChanged() +{ + beginResetModel(); + + points.clear(); + active.clear(); + + endResetModel(); +} + +/* + * Displays the tooltip code for the given device index. + */ +QVariant InterfaceTreeModel::toolTipForInterface(int idx) const +{ +#ifdef HAVE_LIBPCAP + if (! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len <= (guint) idx) + return QVariant(); + + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + + QString tt_str = "

"; + if (device->no_addresses > 0) + { + tt_str += QString("%1: %2") + .arg(device->no_addresses > 1 ? tr("Addresses") : tr("Address")) + .arg(html_escape(device->addresses)) + .replace('\n', ", "); + } + else if (device->if_info.type == IF_EXTCAP) + { + tt_str = QString(tr("Extcap interface: %1")).arg(get_basename(device->if_info.extcap)); + } + else + { + tt_str = tr("No addresses"); + } + tt_str += "
"; + + QString cfilter = device->cfilter; + if (cfilter.isEmpty()) + { + tt_str += tr("No capture filter"); + } + else + { + tt_str += QString("%1: %2") + .arg(tr("Capture filter")) + .arg(html_escape(cfilter)); + } + tt_str += "

"; + + return tt_str; +#else + Q_UNUSED(idx) + + return QVariant(); +#endif +} + +#ifdef HAVE_LIBPCAP +void InterfaceTreeModel::stopStatistic() +{ + if (stat_cache_) + { + capture_stat_stop(stat_cache_); + stat_cache_ = NULL; + } +} +#endif + +void InterfaceTreeModel::updateStatistic(unsigned int idx) +{ +#ifdef HAVE_LIBPCAP + if (! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len <= (guint) idx) + return; + + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + + if (device->if_info.type == IF_PIPE || device->if_info.type == IF_EXTCAP) + return; + + if (!stat_cache_) + { + // Start gathering statistics using dumpcap + // We crash (on macOS at least) if we try to do this from ::showEvent. + stat_cache_ = capture_stat_start(&global_capture_opts); + } + + struct pcap_stat stats; + unsigned diff = 0; + bool isActive = false; + + if (capture_stats(stat_cache_, device->name, &stats)) + { + if ( (int) stats.ps_recv > 0 ) + isActive = true; + + if ((int)(stats.ps_recv - device->last_packets) >= 0) + { + diff = stats.ps_recv - device->last_packets; + device->packet_diff = diff; + } + device->last_packets = stats.ps_recv; + } + + points[device->name].append(diff); + + if (active[device->name] != isActive) + { + emit layoutAboutToBeChanged(); + active[device->name] = isActive; + emit layoutChanged(); + } + + emit dataChanged(index(idx, IFTREE_COL_STATS), index(idx, IFTREE_COL_STATS)); + +#else + Q_UNUSED(idx) +#endif +} + +QItemSelection InterfaceTreeModel::selectedDevices() +{ + QItemSelection mySelection; +#ifdef HAVE_LIBPCAP + for (int idx = 0; idx < rowCount(); idx++) + { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + + if (device->selected) + { + QModelIndex selectIndex = index(idx, 0); + mySelection.merge( + QItemSelection(selectIndex, index(selectIndex.row(), columnCount() - 1)), + QItemSelectionModel::SelectCurrent + ); + } + } +#endif + return mySelection; +} + +bool InterfaceTreeModel::updateSelectedDevices(QItemSelection sourceSelection) +{ + bool selectionHasChanged = false; +#ifdef HAVE_LIBPCAP + QList selectedIndices; + + QItemSelection::const_iterator it = sourceSelection.constBegin(); + while (it != sourceSelection.constEnd()) + { + QModelIndexList indeces = ((QItemSelectionRange) (*it)).indexes(); + + QModelIndexList::const_iterator cit = indeces.constBegin(); + while (cit != indeces.constEnd()) + { + QModelIndex index = (QModelIndex) (*cit); + if (! selectedIndices.contains(index.row())) + { + selectedIndices.append(index.row()); + } + ++cit; + } + ++it; + } + + global_capture_opts.num_selected = 0; + + for (unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++) + { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + if (selectedIndices.contains(idx)) + { + if (! device->selected) + selectionHasChanged = true; + device->selected = TRUE; + global_capture_opts.num_selected++; + } else { + if (device->selected) + selectionHasChanged = true; + device->selected = FALSE; + } + } +#else + Q_UNUSED(sourceSelection) +#endif + return selectionHasChanged; +} diff --git a/ui/qt/models/interface_tree_model.h b/ui/qt/models/interface_tree_model.h new file mode 100644 index 00000000..cdf2ac89 --- /dev/null +++ b/ui/qt/models/interface_tree_model.h @@ -0,0 +1,99 @@ +/** @file + * + * Model for the interface data for display in the interface frame + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INTERFACE_TREE_MODEL_H +#define INTERFACE_TREE_MODEL_H + +#include +#include + +#ifdef HAVE_LIBPCAP +#include "ui/capture.h" +#include "ui/capture_globals.h" +#endif + +#include +#include +#include +#include + +typedef QList PointList; + +enum InterfaceTreeColumns +{ + IFTREE_COL_EXTCAP, + IFTREE_COL_EXTCAP_PATH, + IFTREE_COL_NAME, + IFTREE_COL_DESCRIPTION, + IFTREE_COL_DISPLAY_NAME, + IFTREE_COL_COMMENT, + IFTREE_COL_HIDDEN, + IFTREE_COL_DLT, + IFTREE_COL_PROMISCUOUSMODE, + IFTREE_COL_TYPE, + IFTREE_COL_STATS, + IFTREE_COL_ACTIVE, + IFTREE_COL_SNAPLEN, +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + IFTREE_COL_BUFFERLEN, +#endif +#ifdef HAVE_PCAP_CREATE + IFTREE_COL_MONITOR_MODE, +#endif + IFTREE_COL_CAPTURE_FILTER, + IFTREE_COL_PIPE_PATH, + IFTREE_COL_MAX /* is not being displayed, it is the definition for the maximum numbers of columns */ +}; + +class InterfaceTreeModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + InterfaceTreeModel(QObject *parent); + ~InterfaceTreeModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + + void updateStatistic(unsigned int row); +#ifdef HAVE_LIBPCAP + void stopStatistic(); +#endif + + QString interfaceError(); + QItemSelection selectedDevices(); + bool updateSelectedDevices(QItemSelection sourceSelection); + + QVariant getColumnContent(int idx, int col, int role = Qt::DisplayRole); + +#ifdef HAVE_PCAP_REMOTE + bool isRemote(int idx); +#endif + + static const QString DefaultNumericValue; + +public slots: + void interfaceListChanged(); + +private: + QVariant toolTipForInterface(int idx) const; + QMap points; + QMap active; + +#ifdef HAVE_LIBPCAP + if_stat_cache_t *stat_cache_; +#endif // HAVE_LIBPCAP +}; + +#endif // INTERFACE_TREE_MODEL_H diff --git a/ui/qt/models/manuf_table_model.cpp b/ui/qt/models/manuf_table_model.cpp new file mode 100644 index 00000000..35792273 --- /dev/null +++ b/ui/qt/models/manuf_table_model.cpp @@ -0,0 +1,220 @@ +/* + * manuf_table_model.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "manuf_table_model.h" + +ManufTableItem::ManufTableItem(struct ws_manuf *ptr) : + short_name_(QString::fromUtf8(ptr->short_name)), + long_name_(QString::fromUtf8(ptr->long_name)) +{ + qsizetype size; + switch (ptr->mask) { + case 24: + size = 3; + break; + case 28: + size = 4; + break; + case 36: + size = 5; + break; + default: + ws_assert_not_reached(); + } + // Note: since 'ptr' is not stable, a deep copy is needed. +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + block_bytes_ = QByteArray(reinterpret_cast(ptr->block), size); +#else + block_bytes_ = QByteArray(reinterpret_cast(ptr->block), static_cast(size)); +#endif + + char buf[64]; + block_name_ = QString::fromUtf8(ws_manuf_block_str(buf, sizeof(buf), ptr)); +} + +ManufTableItem::~ManufTableItem() +{ +} + +ManufTableModel::ManufTableModel(QObject *parent) : QAbstractTableModel(parent) +{ + ws_manuf_iter_t iter; + struct ws_manuf item; + + ws_manuf_iter_init(&iter); + while (ws_manuf_iter_next(&iter, &item)) { + rows_.append(new ManufTableItem(&item)); + } +} + +ManufTableModel::~ManufTableModel() +{ + clear(); +} + +int ManufTableModel::rowCount(const QModelIndex &) const +{ + return static_cast(rows_.count()); +} + +int ManufTableModel::columnCount(const QModelIndex &) const +{ + return NUM_COLS; +} + +QVariant ManufTableModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rows_.size()) + return QVariant(); + + ManufTableItem *item = rows_.at(index.row()); + if (index.column() >= NUM_COLS) + return QVariant(); + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case COL_MAC_PREFIX: + return item->block_name_; + case COL_SHORT_NAME: + return item->short_name_; + case COL_VENDOR_NAME: + return item->long_name_; + default: + return QVariant(); + } + } + + if (role == Qt::UserRole) { + switch (index.column()) { + case COL_MAC_PREFIX: + return item->block_bytes_; + case COL_SHORT_NAME: + return item->short_name_; + case COL_VENDOR_NAME: + return item->long_name_; + default: + return QVariant(); + } + } + + return QVariant(); +} + +void ManufTableModel::addRecord(struct ws_manuf *ptr) +{ + emit beginInsertRows(QModelIndex(), rowCount(), rowCount()); + ManufTableItem *item = new ManufTableItem(ptr); + rows_.append(item); + emit endInsertRows(); +} + +void ManufTableModel::clear() +{ + if (!rows_.isEmpty()) { + emit beginRemoveRows(QModelIndex(), 0, rowCount() - 1); + qDeleteAll(rows_.begin(), rows_.end()); + rows_.clear(); + emit endRemoveRows(); + } +} + +QVariant ManufTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + switch (section) { + case COL_MAC_PREFIX: + return QString(tr("Address Block")); + case COL_SHORT_NAME: + return QString(tr("Short Name")); + case COL_VENDOR_NAME: + return QString(tr("Vendor Name")); + } + } + + return QVariant(); +} + + +ManufSortFilterProxyModel::ManufSortFilterProxyModel(QObject* parent) : + QSortFilterProxyModel(parent), + filter_type_(FilterEmpty) +{ +} + +void ManufSortFilterProxyModel::clearFilter() +{ + if (filter_type_ == FilterEmpty) + return; + filter_type_ = FilterEmpty; + invalidateFilter(); +} + +void ManufSortFilterProxyModel::setFilterAddress(const QByteArray &bytes) +{ + filter_type_ = FilterByAddress; + filter_bytes_ = bytes; + invalidateFilter(); +} + +void ManufSortFilterProxyModel::setFilterName(QRegularExpression &name) +{ + filter_type_ = FilterByName; + filter_name_ = name; + invalidateFilter(); +} + +static bool match_filter(const QByteArray &bytes, const QByteArray &mac_block) +{ + if (bytes.size() < mac_block.size()) + return mac_block.startsWith(bytes); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QByteArray prefix = bytes.first(mac_block.size()); +#else + QByteArray prefix = bytes.left(mac_block.size()); +#endif + // Blocks are 3, 4 or 5 bytes wide + if (mac_block.size() > 3) { + // Mask out the last nibble of the bytes for 28 and 36 bit block lengths + // (but not 24 bit OUIs) + prefix[prefix.size() - 1] = prefix[prefix.size() - 1] & 0xF0; + } + return prefix == mac_block; +} + +bool ManufSortFilterProxyModel::filterAddressAcceptsRow(int source_row, const QModelIndex& source_parent) const +{ + QModelIndex chkIdx = sourceModel()->index(source_row, ManufTableModel::COL_MAC_PREFIX, source_parent); + QByteArray mac_block = chkIdx.data(Qt::UserRole).toByteArray(); + return match_filter(filter_bytes_, mac_block); +} + +bool ManufSortFilterProxyModel::filterNameAcceptsRow(int source_row, const QModelIndex& source_parent) const +{ + QModelIndex chkIdx = sourceModel()->index(source_row, ManufTableModel::COL_VENDOR_NAME, source_parent); + QString vendor_name = chkIdx.data(Qt::UserRole).toString(); + return filter_name_.match(vendor_name).hasMatch(); +} + +bool ManufSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const +{ + switch (filter_type_) { + case FilterEmpty: return true; + case FilterByAddress: return filterAddressAcceptsRow(source_row, source_parent); + case FilterByName: return filterNameAcceptsRow(source_row, source_parent); + } + ws_error("unknown filter type %d", filter_type_); +} diff --git a/ui/qt/models/manuf_table_model.h b/ui/qt/models/manuf_table_model.h new file mode 100644 index 00000000..9f850774 --- /dev/null +++ b/ui/qt/models/manuf_table_model.h @@ -0,0 +1,90 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MANUF_TABLE_MODEL_H +#define MANUF_TABLE_MODEL_H + +#include +#include +#include + +#include +#include + +class ManufTableItem +{ +public: + ManufTableItem(struct ws_manuf *ptr); + ~ManufTableItem(); + + QByteArray block_bytes_; + QString block_name_; + QString short_name_; + QString long_name_; +}; + +class ManufTableModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + ManufTableModel(QObject *parent); + ~ManufTableModel(); + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const ; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void addRecord(struct ws_manuf *ptr); + + void clear(); + + enum { + COL_MAC_PREFIX, + COL_SHORT_NAME, + COL_VENDOR_NAME, + NUM_COLS, + }; + +private: + QList rows_; +}; + +class ManufSortFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + enum ManufProxyFilterType + { + FilterEmpty = 0, + FilterByAddress, + FilterByName, + }; + Q_ENUM(ManufProxyFilterType) + + ManufSortFilterProxyModel(QObject *parent); + + virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; + +public slots: + void setFilterAddress(const QByteArray&); + void setFilterName(QRegularExpression&); + void clearFilter(); + +private: + ManufProxyFilterType filter_type_; + QByteArray filter_bytes_; + QRegularExpression filter_name_; + + bool filterAddressAcceptsRow(int source_row, const QModelIndex& source_parent) const; + bool filterNameAcceptsRow(int source_row, const QModelIndex& source_parent) const; +}; + +#endif diff --git a/ui/qt/models/numeric_value_chooser_delegate.cpp b/ui/qt/models/numeric_value_chooser_delegate.cpp new file mode 100644 index 00000000..9bed91b2 --- /dev/null +++ b/ui/qt/models/numeric_value_chooser_delegate.cpp @@ -0,0 +1,93 @@ +/* numeric_value_chooser_delegate.cpp + * Delegate to select a numeric value for a treeview entry + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include +#include + +NumericValueChooserDelegate::NumericValueChooserDelegate(int min, int max, QObject *parent) + : QStyledItemDelegate(parent) +{ + _min = min; + _max = max; + _default = min; +} + +NumericValueChooserDelegate::~NumericValueChooserDelegate() +{ +} + +void NumericValueChooserDelegate::setMinMaxRange(int min, int max) +{ + _min = qMin(min, max); + _max = qMax(min, max); + /* ensure, that the default value is within the new min<->max */ + _default = qMin(_max, qMax(_min, _default)); + _defReturn = QVariant::fromValue(_default); +} + +void NumericValueChooserDelegate::setDefaultValue(int defValue, QVariant defaultReturn) +{ + /* ensure, that the new default value is within min<->max */ + _default = qMin(_max, qMax(_min, defValue)); + _defReturn = defaultReturn; +} + +QWidget* NumericValueChooserDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (!index.isValid()) { + return QStyledItemDelegate::createEditor(parent, option, index); + } + + QSpinBox * editor = new QSpinBox(parent); + editor->setMinimum(_min); + editor->setMaximum(_max); + editor->setWrapping(true); + + connect(editor, static_cast(&QSpinBox::valueChanged), this, + &NumericValueChooserDelegate::onValueChanged); + + return editor; +} + +void NumericValueChooserDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + if (index.isValid()) + { + bool canConvert = false; + int val = index.data().toInt(&canConvert); + if (! canConvert) + val = _default; + + QSpinBox * spinBox = qobject_cast(editor); + spinBox->setValue(val); + } + else + QStyledItemDelegate::setEditorData(editor, index); +} + +void NumericValueChooserDelegate::setModelData(QWidget *editor, QAbstractItemModel * model, const QModelIndex &index) const +{ + if (index.isValid()) { + QSpinBox * spinBox = qobject_cast(editor); + model->setData(index, _default == spinBox->value() ? _defReturn : QVariant::fromValue(spinBox->value())); + } else { + QStyledItemDelegate::setModelData(editor, model, index); + } +} + +void NumericValueChooserDelegate::onValueChanged(int) +{ + QSpinBox * spinBox = qobject_cast(sender()); + emit commitData(spinBox); +} diff --git a/ui/qt/models/numeric_value_chooser_delegate.h b/ui/qt/models/numeric_value_chooser_delegate.h new file mode 100644 index 00000000..ceda8746 --- /dev/null +++ b/ui/qt/models/numeric_value_chooser_delegate.h @@ -0,0 +1,45 @@ +/** @file + * + * Delegate to select a numeric value for a treeview entry + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef NUMERIC_VALUE_CHOOSER_DELEGATE_H_ +#define NUMERIC_VALUE_CHOOSER_DELEGATE_H_ + + +#include + +class NumericValueChooserDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + NumericValueChooserDelegate(int min = 0, int max = 0, QObject *parent = 0); + ~NumericValueChooserDelegate(); + + void setMinMaxRange(int min, int max); + void setDefaultValue(int defValue, QVariant defaultReturn); + +protected: + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; + +private: + + int _min; + int _max; + int _default; + QVariant _defReturn; + +private slots: + void onValueChanged(int i); +}; + +#endif /* NUMERIC_VALUE_CHOOSER_DELEGATE_H_ */ diff --git a/ui/qt/models/packet_list_model.cpp b/ui/qt/models/packet_list_model.cpp new file mode 100644 index 00000000..0ed61d74 --- /dev/null +++ b/ui/qt/models/packet_list_model.cpp @@ -0,0 +1,1014 @@ +/* packet_list_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include + +#include "packet_list_model.h" + +#include "file.h" + +#include +#include +#include +#include + +#include "ui/packet_list_utils.h" +#include "ui/recent.h" + +#include +#include "frame_tvbuff.h" + +#include +#include +#include "main_application.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +// Print timing information +//#define DEBUG_PACKET_LIST_MODEL 1 + +#ifdef DEBUG_PACKET_LIST_MODEL +#include +#endif + +class SortAbort : public std::runtime_error +{ + using std::runtime_error::runtime_error; +}; + +static PacketListModel * glbl_plist_model = Q_NULLPTR; +static const int reserved_packets_ = 100000; + +guint +packet_list_append(column_info *, frame_data *fdata) +{ + if (!glbl_plist_model) + return 0; + + /* fdata should be filled with the stuff we need + * strings are built at display time. + */ + return glbl_plist_model->appendPacket(fdata); +} + +void +packet_list_recreate_visible_rows(void) +{ + if (glbl_plist_model) + glbl_plist_model->recreateVisibleRows(); +} + +PacketListModel::PacketListModel(QObject *parent, capture_file *cf) : + QAbstractItemModel(parent), + number_to_row_(QVector()), + max_row_height_(0), + max_line_count_(1), + idle_dissection_row_(0) +{ + Q_ASSERT(glbl_plist_model == Q_NULLPTR); + glbl_plist_model = this; + setCaptureFile(cf); + + physical_rows_.reserve(reserved_packets_); + visible_rows_.reserve(reserved_packets_); + new_visible_rows_.reserve(1000); + number_to_row_.reserve(reserved_packets_); + + if (qobject_cast(mainApp->mainWindow())) + { + MainWindow *mw = qobject_cast(mainApp->mainWindow()); + QWidget * wtWidget = mw->findChild(); + if (wtWidget && qobject_cast(wtWidget)) + { + WirelessTimeline * wt = qobject_cast(wtWidget); + connect(this, &PacketListModel::bgColorizationProgress, + wt, &WirelessTimeline::bgColorizationProgress); + } + + } + + connect(this, &PacketListModel::maxLineCountChanged, + this, &PacketListModel::emitItemHeightChanged, + Qt::QueuedConnection); + idle_dissection_timer_ = new QElapsedTimer(); +} + +PacketListModel::~PacketListModel() +{ + delete idle_dissection_timer_; +} + +void PacketListModel::setCaptureFile(capture_file *cf) +{ + cap_file_ = cf; +} + +// Packet list records have no children (for now, at least). +QModelIndex PacketListModel::index(int row, int column, const QModelIndex &) const +{ + if (row >= visible_rows_.count() || row < 0 || !cap_file_ || column >= prefs.num_cols) + return QModelIndex(); + + PacketListRecord *record = visible_rows_[row]; + + return createIndex(row, column, record); +} + +// Everything is under the root. +QModelIndex PacketListModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +int PacketListModel::packetNumberToRow(int packet_num) const +{ + // map 1-based values to 0-based row numbers. Invisible rows are stored as + // the default value (0) and should map to -1. + return number_to_row_.value(packet_num) - 1; +} + +guint PacketListModel::recreateVisibleRows() +{ + beginResetModel(); + visible_rows_.resize(0); + number_to_row_.fill(0); + endResetModel(); + + foreach (PacketListRecord *record, physical_rows_) { + frame_data *fdata = record->frameData(); + + if (fdata->passed_dfilter || fdata->ref_time) { + visible_rows_ << record; + if (static_cast(number_to_row_.size()) <= fdata->num) { + number_to_row_.resize(fdata->num + 10000); + } + number_to_row_[fdata->num] = static_cast(visible_rows_.count()); + } + } + if (!visible_rows_.isEmpty()) { + beginInsertRows(QModelIndex(), 0, static_cast(visible_rows_.count()) - 1); + endInsertRows(); + } + idle_dissection_row_ = 0; + return static_cast(visible_rows_.count()); +} + +void PacketListModel::clear() { + beginResetModel(); + qDeleteAll(physical_rows_); + PacketListRecord::invalidateAllRecords(); + physical_rows_.resize(0); + visible_rows_.resize(0); + new_visible_rows_.resize(0); + number_to_row_.resize(0); + endResetModel(); + max_row_height_ = 0; + max_line_count_ = 1; + idle_dissection_timer_->invalidate(); + idle_dissection_row_ = 0; +} + +void PacketListModel::invalidateAllColumnStrings() +{ + PacketListRecord::invalidateAllRecords(); + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1), + QVector() << Qt::DisplayRole); +} + +void PacketListModel::resetColumns() +{ + if (cap_file_) { + PacketListRecord::resetColumns(&cap_file_->cinfo); + } + + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); + emit headerDataChanged(Qt::Horizontal, 0, columnCount() - 1); +} + +void PacketListModel::resetColorized() +{ + PacketListRecord::resetColorization(); + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole); +} + +void PacketListModel::toggleFrameMark(const QModelIndexList &indeces) +{ + if (!cap_file_ || indeces.count() <= 0) + return; + + int sectionMax = columnCount() - 1; + + foreach (QModelIndex index, indeces) { + if (! index.isValid()) + continue; + + PacketListRecord *record = static_cast(index.internalPointer()); + if (!record) + continue; + + frame_data *fdata = record->frameData(); + if (!fdata) + continue; + + if (fdata->marked) + cf_unmark_frame(cap_file_, fdata); + else + cf_mark_frame(cap_file_, fdata); + + emit dataChanged(index.sibling(index.row(), 0), index.sibling(index.row(), sectionMax), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole); + } +} + +void PacketListModel::setDisplayedFrameMark(gboolean set) +{ + foreach (PacketListRecord *record, visible_rows_) { + if (set) { + cf_mark_frame(cap_file_, record->frameData()); + } else { + cf_unmark_frame(cap_file_, record->frameData()); + } + } + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole); +} + +void PacketListModel::toggleFrameIgnore(const QModelIndexList &indeces) +{ + if (!cap_file_ || indeces.count() <= 0) + return; + + int sectionMax = columnCount() - 1; + + foreach (QModelIndex index, indeces) { + if (! index.isValid()) + continue; + + PacketListRecord *record = static_cast(index.internalPointer()); + if (!record) + continue; + + frame_data *fdata = record->frameData(); + if (!fdata) + continue; + + if (fdata->ignored) + cf_unignore_frame(cap_file_, fdata); + else + cf_ignore_frame(cap_file_, fdata); + + emit dataChanged(index.sibling(index.row(), 0), index.sibling(index.row(), sectionMax), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole << Qt::DisplayRole); + } +} + +void PacketListModel::setDisplayedFrameIgnore(gboolean set) +{ + foreach (PacketListRecord *record, visible_rows_) { + if (set) { + cf_ignore_frame(cap_file_, record->frameData()); + } else { + cf_unignore_frame(cap_file_, record->frameData()); + } + } + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole << Qt::DisplayRole); +} + +void PacketListModel::toggleFrameRefTime(const QModelIndex &rt_index) +{ + if (!cap_file_ || !rt_index.isValid()) return; + + PacketListRecord *record = static_cast(rt_index.internalPointer()); + if (!record) return; + + frame_data *fdata = record->frameData(); + if (!fdata) return; + + if (fdata->ref_time) { + fdata->ref_time=0; + cap_file_->ref_time_count--; + } else { + fdata->ref_time=1; + cap_file_->ref_time_count++; + } + cf_reftime_packets(cap_file_); + if (!fdata->ref_time && !fdata->passed_dfilter) { + cap_file_->displayed_count--; + } + record->resetColumns(&cap_file_->cinfo); + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); +} + +void PacketListModel::unsetAllFrameRefTime() +{ + if (!cap_file_) return; + + /* XXX: we might need a progressbar here */ + + foreach (PacketListRecord *record, physical_rows_) { + frame_data *fdata = record->frameData(); + if (fdata->ref_time) { + fdata->ref_time = 0; + } + } + cap_file_->ref_time_count = 0; + cf_reftime_packets(cap_file_); + PacketListRecord::resetColumns(&cap_file_->cinfo); + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); +} + +void PacketListModel::addFrameComment(const QModelIndexList &indices, const QByteArray &comment) +{ + int sectionMax = columnCount() - 1; + frame_data *fdata; + if (!cap_file_) return; + + for (const auto &index : indices) { + if (!index.isValid()) continue; + + PacketListRecord *record = static_cast(index.internalPointer()); + if (!record) continue; + + fdata = record->frameData(); + wtap_block_t pkt_block = cf_get_packet_block(cap_file_, fdata); + wtap_block_add_string_option(pkt_block, OPT_COMMENT, comment.data(), comment.size()); + + if (!cf_set_modified_block(cap_file_, fdata, pkt_block)) { + cap_file_->packet_comment_count++; + expert_update_comment_count(cap_file_->packet_comment_count); + } + + // In case there are coloring rules or columns related to comments. + // (#12519) + // + // XXX: "Does any active coloring rule relate to frame data" + // could be an optimization. For columns, note that + // "col_based_on_frame_data" only applies to built in columns, + // not custom columns based on frame data. (Should we prevent + // custom columns based on frame data from being created, + // substituting them with the other columns?) + // + // Note that there are not currently any fields that depend on + // whether other frames have comments, unlike with time references + // and time shifts ("frame.time_relative", "frame.offset_shift", etc.) + // If there were, then we'd need to reset data for all frames instead + // of just the frames changed. + record->invalidateColorized(); + record->invalidateRecord(); + emit dataChanged(index.sibling(index.row(), 0), index.sibling(index.row(), sectionMax), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole << Qt::DisplayRole); + } +} + +void PacketListModel::setFrameComment(const QModelIndex &index, const QByteArray &comment, guint c_number) +{ + int sectionMax = columnCount() - 1; + frame_data *fdata; + if (!cap_file_) return; + + if (!index.isValid()) return; + + PacketListRecord *record = static_cast(index.internalPointer()); + if (!record) return; + + fdata = record->frameData(); + + wtap_block_t pkt_block = cf_get_packet_block(cap_file_, fdata); + if (comment.isEmpty()) { + wtap_block_remove_nth_option_instance(pkt_block, OPT_COMMENT, c_number); + if (!cf_set_modified_block(cap_file_, fdata, pkt_block)) { + cap_file_->packet_comment_count--; + expert_update_comment_count(cap_file_->packet_comment_count); + } + } else { + wtap_block_set_nth_string_option_value(pkt_block, OPT_COMMENT, c_number, comment.data(), comment.size()); + cf_set_modified_block(cap_file_, fdata, pkt_block); + } + + record->invalidateColorized(); + record->invalidateRecord(); + emit dataChanged(index.sibling(index.row(), 0), index.sibling(index.row(), sectionMax), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole << Qt::DisplayRole); +} + +void PacketListModel::deleteFrameComments(const QModelIndexList &indices) +{ + int sectionMax = columnCount() - 1; + frame_data *fdata; + if (!cap_file_) return; + + for (const auto &index : indices) { + if (!index.isValid()) continue; + + PacketListRecord *record = static_cast(index.internalPointer()); + if (!record) continue; + + fdata = record->frameData(); + wtap_block_t pkt_block = cf_get_packet_block(cap_file_, fdata); + guint n_comments = wtap_block_count_option(pkt_block, OPT_COMMENT); + + if (n_comments) { + for (guint i = 0; i < n_comments; i++) { + wtap_block_remove_nth_option_instance(pkt_block, OPT_COMMENT, 0); + } + if (!cf_set_modified_block(cap_file_, fdata, pkt_block)) { + cap_file_->packet_comment_count -= n_comments; + expert_update_comment_count(cap_file_->packet_comment_count); + } + + record->invalidateColorized(); + record->invalidateRecord(); + emit dataChanged(index.sibling(index.row(), 0), index.sibling(index.row(), sectionMax), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole << Qt::DisplayRole); + } + } +} + +void PacketListModel::deleteAllFrameComments() +{ + int row; + int sectionMax = columnCount() - 1; + if (!cap_file_) return; + + /* XXX: we might need a progressbar here */ + + foreach (PacketListRecord *record, physical_rows_) { + frame_data *fdata = record->frameData(); + wtap_block_t pkt_block = cf_get_packet_block(cap_file_, fdata); + guint n_comments = wtap_block_count_option(pkt_block, OPT_COMMENT); + + if (n_comments) { + for (guint i = 0; i < n_comments; i++) { + wtap_block_remove_nth_option_instance(pkt_block, OPT_COMMENT, 0); + } + cf_set_modified_block(cap_file_, fdata, pkt_block); + + record->invalidateColorized(); + record->invalidateRecord(); + row = packetNumberToRow(fdata->num); + if (row > -1) { + emit dataChanged(index(row, 0), index(row, sectionMax), + QVector() << Qt::BackgroundRole << Qt::ForegroundRole << Qt::DisplayRole); + } + } + } + cap_file_->packet_comment_count = 0; + expert_update_comment_count(cap_file_->packet_comment_count); +} + +void PacketListModel::setMaximumRowHeight(int height) +{ + max_row_height_ = height; + // As the QTreeView uniformRowHeights documentation says, + // "The height is obtained from the first item in the view. It is + // updated when the data changes on that item." + emit dataChanged(index(0, 0), index(0, columnCount() - 1)); +} + +int PacketListModel::sort_column_; +int PacketListModel::sort_column_is_numeric_; +int PacketListModel::text_sort_column_; +Qt::SortOrder PacketListModel::sort_order_; +capture_file *PacketListModel::sort_cap_file_; +gboolean PacketListModel::stop_flag_; +ProgressFrame *PacketListModel::progress_frame_; +double PacketListModel::comps_; +double PacketListModel::exp_comps_; + +QElapsedTimer busy_timer_; +const int busy_timeout_ = 65; // ms, approximately 15 fps +void PacketListModel::sort(int column, Qt::SortOrder order) +{ + if (!cap_file_ || visible_rows_.count() < 1) return; + if (column < 0) return; + + if (physical_rows_.count() < 1) + return; + + sort_column_ = column; + text_sort_column_ = PacketListRecord::textColumn(column); + sort_order_ = order; + sort_cap_file_ = cap_file_; + + QString col_title = get_column_title(column); + + if (text_sort_column_ >= 0 && (guint)visible_rows_.count() > prefs.gui_packet_list_cached_rows_max) { + /* Column not based on frame data but by column text that requires + * dissection, so to sort in a reasonable amount of time the column + * text needs to be cached. + */ + /* If the sort is being triggered because the columns were already + * sorted and the filter is being cleared (or changed to something + * else with more rows than fit in the cache), then the temporary + * message will be immediately overwritten with the standard capture + * statistics by the packets_bar_update() call after thawing the rows. + * It will still blink yellow, and the user will get the message if + * they then click on the header file (wondering why it didn't sort.) + */ + if (col_title.isEmpty()) { + col_title = tr("Column"); + } + QString temp_msg = tr("%1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences").arg(col_title).arg(prefs.gui_packet_list_cached_rows_max); + mainApp->pushStatus(MainApplication::TemporaryStatus, temp_msg); + return; + } + + /* If we are currently in the middle of reading the capture file, don't + * sort. PacketList::captureFileReadFinished invalidates all the cached + * column strings and then tries to sort again. + * Similarly, claim the read lock because we don't want the file to + * change out from under us while sorting, which can segfault. (Previously + * we ignored user input, but now in order to cancel sorting we don't.) + */ + if (sort_cap_file_->read_lock) { + ws_info("Refusing to sort because capture file is being read"); + /* We shouldn't have to tell the user because we're just deferring + * the sort until PacketList::captureFileReadFinished + */ + return; + } + sort_cap_file_->read_lock = TRUE; + + QString busy_msg; + if (!col_title.isEmpty()) { + busy_msg = tr("Sorting \"%1\"…").arg(col_title); + } else { + busy_msg = tr("Sorting …"); + } + stop_flag_ = FALSE; + comps_ = 0; + /* XXX: The expected number of comparisons is O(N log N), but this could + * be a pretty significant overestimate of the amount of time it takes, + * if there are lots of identical entries. (Especially with string + * comparisons, some comparisons are faster than others.) Better to + * overestimate? + */ + exp_comps_ = log2(visible_rows_.count()) * visible_rows_.count(); + progress_frame_ = nullptr; + if (qobject_cast(mainApp->mainWindow())) { + MainWindow *mw = qobject_cast(mainApp->mainWindow()); + progress_frame_ = mw->findChild(); + if (progress_frame_) { + progress_frame_->showProgress(busy_msg, true, false, &stop_flag_, 0); + connect(progress_frame_, &ProgressFrame::stopLoading, + this, &PacketListModel::stopSorting); + } + } + + busy_timer_.start(); + sort_column_is_numeric_ = isNumericColumn(sort_column_); + QVector sorted_visible_rows_ = visible_rows_; + try { + std::sort(sorted_visible_rows_.begin(), sorted_visible_rows_.end(), recordLessThan); + + beginResetModel(); + visible_rows_.resize(0); + number_to_row_.fill(0); + foreach (PacketListRecord *record, sorted_visible_rows_) { + frame_data *fdata = record->frameData(); + + if (fdata->passed_dfilter || fdata->ref_time) { + visible_rows_ << record; + if (number_to_row_.size() <= (int)fdata->num) { + number_to_row_.resize(fdata->num + 10000); + } + number_to_row_[fdata->num] = static_cast(visible_rows_.count()); + } + } + endResetModel(); + } catch (const SortAbort& e) { + mainApp->pushStatus(MainApplication::TemporaryStatus, e.what()); + } + + if (progress_frame_ != nullptr) { + progress_frame_->hide(); + disconnect(progress_frame_, &ProgressFrame::stopLoading, + this, &PacketListModel::stopSorting); + } + sort_cap_file_->read_lock = FALSE; + + if (cap_file_->current_frame) { + emit goToPacket(cap_file_->current_frame->num); + } +} + +void PacketListModel::stopSorting() +{ + stop_flag_ = TRUE; +} + +bool PacketListModel::isNumericColumn(int column) +{ + if (column < 0) { + return false; + } + switch (sort_cap_file_->cinfo.columns[column].col_fmt) { + case COL_CUMULATIVE_BYTES: /**< 3) Cumulative number of bytes */ + case COL_DELTA_TIME: /**< 5) Delta time */ + case COL_DELTA_TIME_DIS: /**< 8) Delta time displayed*/ + case COL_UNRES_DST_PORT: /**< 10) Unresolved dest port */ + case COL_FREQ_CHAN: /**< 15) IEEE 802.11 (and WiMax?) - Channel */ + case COL_RSSI: /**< 22) IEEE 802.11 - received signal strength */ + case COL_TX_RATE: /**< 23) IEEE 802.11 - TX rate in Mbps */ + case COL_NUMBER: /**< 32) Packet list item number */ + case COL_PACKET_LENGTH: /**< 33) Packet length in bytes */ + case COL_UNRES_SRC_PORT: /**< 41) Unresolved source port */ + return true; + + /* + * Try to sort port numbers as number, if the numeric comparison fails (due + * to name resolution), it will fallback to string comparison. + * */ + case COL_RES_DST_PORT: /**< 10) Resolved dest port */ + case COL_DEF_DST_PORT: /**< 12) Destination port */ + case COL_DEF_SRC_PORT: /**< 37) Source port */ + case COL_RES_SRC_PORT: /**< 40) Resolved source port */ + return true; + + case COL_CUSTOM: + /* handle custom columns below. */ + break; + + default: + return false; + } + + guint num_fields = g_slist_length(sort_cap_file_->cinfo.columns[column].col_custom_fields_ids); + for (guint i = 0; i < num_fields; i++) { + guint *field_idx = (guint *) g_slist_nth_data(sort_cap_file_->cinfo.columns[column].col_custom_fields_ids, i); + header_field_info *hfi = proto_registrar_get_nth(*field_idx); + + /* + * Reject a field when there is no numeric field type or when: + * - there are (value_string) "strings" + * (but do accept fields which have a unit suffix). + * - BASE_HEX or BASE_HEX_DEC (these have a constant width, string + * comparison is faster than conversion to double). + * - BASE_CUSTOM (these can be formatted in any way). + */ + if (!hfi || + (hfi->strings != NULL && !(hfi->display & BASE_UNIT_STRING)) || + !(((FT_IS_INT(hfi->type) || FT_IS_UINT(hfi->type)) && + ((FIELD_DISPLAY(hfi->display) == BASE_DEC) || + (FIELD_DISPLAY(hfi->display) == BASE_OCT) || + (FIELD_DISPLAY(hfi->display) == BASE_DEC_HEX))) || + (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) || + (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) || + (hfi->type == FT_RELATIVE_TIME))) { + return false; + } + } + + return true; +} + +bool PacketListModel::recordLessThan(PacketListRecord *r1, PacketListRecord *r2) +{ + int cmp_val = 0; + comps_++; + + // Wherein we try to cram the logic of packet_list_compare_records, + // _packet_list_compare_records, and packet_list_compare_custom from + // gtk/packet_list_store.c into one function + + if (busy_timer_.elapsed() > busy_timeout_) { + if (progress_frame_) { + progress_frame_->setValue(static_cast(comps_/exp_comps_ * 100)); + } + // What's the least amount of processing that we can do which will draw + // the busy indicator? + mainApp->processEvents(QEventLoop::ExcludeSocketNotifiers, 1); + if (stop_flag_) { + throw SortAbort("Sorting aborted"); + } + busy_timer_.restart(); + } + if (sort_column_ < 0) { + // No column. + cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), COL_NUMBER); + } else if (text_sort_column_ < 0) { + // Column comes directly from frame data + cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), sort_cap_file_->cinfo.columns[sort_column_].col_fmt); + } else { + QString r1String = r1->columnString(sort_cap_file_, sort_column_); + QString r2String = r2->columnString(sort_cap_file_, sort_column_); + // XXX: The naive string comparison compares Unicode code points. + // Proper collation is more expensive + cmp_val = r1String.compare(r2String); + if (cmp_val != 0 && sort_column_is_numeric_) { + // Custom column with numeric data (or something like a port number). + // Attempt to convert to numbers. + // XXX This is slow. Can we avoid doing this? Perhaps the actual + // values used for sorting should be cached too as QVariant[List]. + // If so, we could consider using QCollatorSortKeys or similar + // for strings as well. + bool ok_r1, ok_r2; + double num_r1 = parseNumericColumn(r1String, &ok_r1); + double num_r2 = parseNumericColumn(r2String, &ok_r2); + + if (!ok_r1 && !ok_r2) { + cmp_val = 0; + } else if (!ok_r1 || (ok_r2 && num_r1 < num_r2)) { + // either r1 is invalid (and sort it before others) or both + // r1 and r2 are valid (sort normally) + cmp_val = -1; + } else if (!ok_r2 || (num_r1 > num_r2)) { + cmp_val = 1; + } + } + + if (cmp_val == 0) { + // All else being equal, compare column numbers. + cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), COL_NUMBER); + } + } + + if (sort_order_ == Qt::AscendingOrder) { + return cmp_val < 0; + } else { + return cmp_val > 0; + } +} + +// Parses a field as a double. Handle values with suffixes ("12ms"), negative +// values ("-1.23") and fields with multiple occurrences ("1,2"). Marks values +// that do not contain any numeric value ("Unknown") as invalid. +double PacketListModel::parseNumericColumn(const QString &val, bool *ok) +{ + QByteArray ba = val.toUtf8(); + const char *strval = ba.constData(); + gchar *end = NULL; + double num = g_ascii_strtod(strval, &end); + *ok = strval != end; + return num; +} + +// ::data is const so we have to make changes here. +void PacketListModel::emitItemHeightChanged(const QModelIndex &ih_index) +{ + if (!ih_index.isValid()) return; + + PacketListRecord *record = static_cast(ih_index.internalPointer()); + if (!record) return; + + if (record->lineCount() > max_line_count_) { + max_line_count_ = record->lineCount(); + emit itemHeightChanged(ih_index); + } +} + +int PacketListModel::rowCount(const QModelIndex &) const +{ + return static_cast(visible_rows_.count()); +} + +int PacketListModel::columnCount(const QModelIndex &) const +{ + return prefs.num_cols; +} + +QVariant PacketListModel::data(const QModelIndex &d_index, int role) const +{ + if (!d_index.isValid()) + return QVariant(); + + PacketListRecord *record = static_cast(d_index.internalPointer()); + if (!record) + return QVariant(); + const frame_data *fdata = record->frameData(); + if (!fdata) + return QVariant(); + + switch (role) { + case Qt::TextAlignmentRole: + switch(recent_get_column_xalign(d_index.column())) { + case COLUMN_XALIGN_RIGHT: + return Qt::AlignRight; + break; + case COLUMN_XALIGN_CENTER: + return Qt::AlignCenter; + break; + case COLUMN_XALIGN_LEFT: + return Qt::AlignLeft; + break; + case COLUMN_XALIGN_DEFAULT: + default: + if (right_justify_column(d_index.column(), cap_file_)) { + return Qt::AlignRight; + } + break; + } + return Qt::AlignLeft; + + case Qt::BackgroundRole: + const color_t *color; + if (fdata->ignored) { + color = &prefs.gui_ignored_bg; + } else if (fdata->marked) { + color = &prefs.gui_marked_bg; + } else if (fdata->color_filter && recent.packet_list_colorize) { + const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter; + color = &color_filter->bg_color; + } else { + return QVariant(); + } + return ColorUtils::fromColorT(color); + case Qt::ForegroundRole: + if (fdata->ignored) { + color = &prefs.gui_ignored_fg; + } else if (fdata->marked) { + color = &prefs.gui_marked_fg; + } else if (fdata->color_filter && recent.packet_list_colorize) { + const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter; + color = &color_filter->fg_color; + } else { + return QVariant(); + } + return ColorUtils::fromColorT(color); + case Qt::DisplayRole: + { + int column = d_index.column(); + QString column_string = record->columnString(cap_file_, column, true); + // We don't know an item's sizeHint until we fetch its text here. + // Assume each line count is 1. If the line count changes, emit + // itemHeightChanged which triggers another redraw (including a + // fetch of SizeHintRole and DisplayRole) in the next event loop. + if (column == 0 && record->lineCountChanged() && record->lineCount() > max_line_count_) { + emit maxLineCountChanged(d_index); + } + return column_string; + } + case Qt::SizeHintRole: + { + // If this is the first row and column, return the maximum row height... + if (d_index.row() < 1 && d_index.column() < 1 && max_row_height_ > 0) { + QSize size = QSize(-1, max_row_height_); + return size; + } + // ...otherwise punt so that the item delegate can correctly calculate the item width. + return QVariant(); + } + default: + return QVariant(); + } +} + +QVariant PacketListModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (!cap_file_) return QVariant(); + + if (orientation == Qt::Horizontal && section < prefs.num_cols) { + switch (role) { + case Qt::DisplayRole: + return QVariant::fromValue(QString(get_column_title(section))); + case Qt::ToolTipRole: + return QVariant::fromValue(gchar_free_to_qstring(get_column_tooltip(section))); + case PacketListModel::HEADER_CAN_RESOLVE: + return (bool)resolve_column(section, cap_file_); + default: + break; + } + } + + return QVariant(); +} + +void PacketListModel::flushVisibleRows() +{ + int pos = static_cast(visible_rows_.count()); + + if (new_visible_rows_.count() > 0) { + beginInsertRows(QModelIndex(), pos, pos + static_cast(new_visible_rows_.count())); + foreach (PacketListRecord *record, new_visible_rows_) { + frame_data *fdata = record->frameData(); + + visible_rows_ << record; + if (static_cast(number_to_row_.size()) <= fdata->num) { + number_to_row_.resize(fdata->num + 10000); + } + number_to_row_[fdata->num] = static_cast(visible_rows_.count()); + } + endInsertRows(); + new_visible_rows_.resize(0); + } +} + +// Fill our column string and colorization cache while the application is +// idle. Try to be as conservative with the CPU and disk as possible. +static const int idle_dissection_interval_ = 5; // ms +void PacketListModel::dissectIdle(bool reset) +{ + if (reset) { +// qDebug() << "=di reset" << idle_dissection_row_; + idle_dissection_row_ = 0; + } else if (!idle_dissection_timer_->isValid()) { + return; + } + + idle_dissection_timer_->restart(); + + int first = idle_dissection_row_; + while (idle_dissection_timer_->elapsed() < idle_dissection_interval_ + && idle_dissection_row_ < physical_rows_.count()) { + ensureRowColorized(idle_dissection_row_); + idle_dissection_row_++; +// if (idle_dissection_row_ % 1000 == 0) qDebug() << "=di row" << idle_dissection_row_; + } + + if (idle_dissection_row_ < physical_rows_.count()) { + QTimer::singleShot(0, this, [=]() { dissectIdle(); }); + } else { + idle_dissection_timer_->invalidate(); + } + + // report colorization progress + emit bgColorizationProgress(first+1, idle_dissection_row_+1); +} + +// XXX Pass in cinfo from packet_list_append so that we can fill in +// line counts? +gint PacketListModel::appendPacket(frame_data *fdata) +{ + PacketListRecord *record = new PacketListRecord(fdata); + qsizetype pos = -1; + +#ifdef DEBUG_PACKET_LIST_MODEL + if (fdata->num % 10000 == 1) { + log_resource_usage(fdata->num == 1, "%u packets", fdata->num); + } +#endif + + physical_rows_ << record; + + if (fdata->passed_dfilter || fdata->ref_time) { + new_visible_rows_ << record; + if (new_visible_rows_.count() < 2) { + // This is the first queued packet. Schedule an insertion for + // the next UI update. + QTimer::singleShot(0, this, &PacketListModel::flushVisibleRows); + } + pos = static_cast( visible_rows_.count() + new_visible_rows_.count() ) - 1; + } + + return static_cast(pos); +} + +frame_data *PacketListModel::getRowFdata(QModelIndex idx) +{ + if (!idx.isValid()) + return Q_NULLPTR; + return getRowFdata(idx.row()); +} + +frame_data *PacketListModel::getRowFdata(int row) { + if (row < 0 || row >= visible_rows_.count()) + return NULL; + PacketListRecord *record = visible_rows_[row]; + if (!record) + return NULL; + return record->frameData(); +} + +void PacketListModel::ensureRowColorized(int row) +{ + if (row < 0 || row >= visible_rows_.count()) + return; + PacketListRecord *record = visible_rows_[row]; + if (!record) + return; + if (!record->colorized()) { + record->ensureColorized(cap_file_); + } +} + +int PacketListModel::visibleIndexOf(frame_data *fdata) const +{ + int row = 0; + foreach (PacketListRecord *record, visible_rows_) { + if (record->frameData() == fdata) { + return row; + } + row++; + } + + return -1; +} diff --git a/ui/qt/models/packet_list_model.h b/ui/qt/models/packet_list_model.h new file mode 100644 index 00000000..807d8847 --- /dev/null +++ b/ui/qt/models/packet_list_model.h @@ -0,0 +1,130 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PACKET_LIST_MODEL_H +#define PACKET_LIST_MODEL_H + +#include + +#include + +#include + +#include + +#include +#include +#include + +#include + +#include "packet_list_record.h" + +#include "cfile.h" + +class QElapsedTimer; + +class PacketListModel : public QAbstractItemModel +{ + Q_OBJECT +public: + + enum { + HEADER_CAN_RESOLVE = Qt::UserRole, + }; + + explicit PacketListModel(QObject *parent = 0, capture_file *cf = NULL); + ~PacketListModel(); + void setCaptureFile(capture_file *cf); + QModelIndex index(int row, int column, + const QModelIndex & = QModelIndex()) const; + QModelIndex parent(const QModelIndex &) const; + int packetNumberToRow(int packet_num) const; + guint recreateVisibleRows(); + void clear(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex & = QModelIndex()) const; + QVariant data(const QModelIndex &d_index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + gint appendPacket(frame_data *fdata); + frame_data *getRowFdata(QModelIndex idx); + frame_data *getRowFdata(int row); + void ensureRowColorized(int row); + int visibleIndexOf(frame_data *fdata) const; + /** + * @brief Invalidate any cached column strings. + */ + void invalidateAllColumnStrings(); + /** + * @brief Rebuild columns from settings. + */ + void resetColumns(); + void resetColorized(); + void toggleFrameMark(const QModelIndexList &indeces); + void setDisplayedFrameMark(gboolean set); + void toggleFrameIgnore(const QModelIndexList &indeces); + void setDisplayedFrameIgnore(gboolean set); + void toggleFrameRefTime(const QModelIndex &rt_index); + void unsetAllFrameRefTime(); + void addFrameComment(const QModelIndexList &indices, const QByteArray &comment); + void setFrameComment(const QModelIndex &index, const QByteArray &comment, guint c_number); + void deleteFrameComments(const QModelIndexList &indices); + void deleteAllFrameComments(); + + void setMaximumRowHeight(int height); + +signals: + void goToPacket(int); + void maxLineCountChanged(const QModelIndex &ih_index) const; + void itemHeightChanged(const QModelIndex &ih_index); + + void bgColorizationProgress(int first, int last); + +public slots: + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + void stopSorting(); + void flushVisibleRows(); + void dissectIdle(bool reset = false); + +private: + capture_file *cap_file_; + QList col_names_; + QVector physical_rows_; + QVector visible_rows_; + QVector new_visible_rows_; + QVector number_to_row_; + + int max_row_height_; // px + int max_line_count_; + + static int sort_column_; + static int sort_column_is_numeric_; + static int text_sort_column_; + static Qt::SortOrder sort_order_; + static capture_file *sort_cap_file_; + static bool recordLessThan(PacketListRecord *r1, PacketListRecord *r2); + static double parseNumericColumn(const QString &val, bool *ok); + + static gboolean stop_flag_; + static ProgressFrame *progress_frame_; + static double exp_comps_; + static double comps_; + + QElapsedTimer *idle_dissection_timer_; + int idle_dissection_row_; + + bool isNumericColumn(int column); + +private slots: + void emitItemHeightChanged(const QModelIndex &ih_index); +}; + +#endif // PACKET_LIST_MODEL_H diff --git a/ui/qt/models/packet_list_record.cpp b/ui/qt/models/packet_list_record.cpp new file mode 100644 index 00000000..9c2c66d1 --- /dev/null +++ b/ui/qt/models/packet_list_record.cpp @@ -0,0 +1,246 @@ +/* packet_list_record.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "packet_list_record.h" + +#include + +#include +#include +#include +#include + +#include + +#include "frame_tvbuff.h" + +#include + +#include + +QCache PacketListRecord::col_text_cache_(500); +QMap PacketListRecord::cinfo_column_; +unsigned PacketListRecord::rows_color_ver_ = 1; + +PacketListRecord::PacketListRecord(frame_data *frameData) : + fdata_(frameData), + lines_(1), + line_count_changed_(false), + color_ver_(0), + colorized_(false), + conv_index_(0), + read_failed_(false) +{ +} + +PacketListRecord::~PacketListRecord() +{ +} + +void PacketListRecord::ensureColorized(capture_file *cap_file) +{ + // packet_list_store.c:packet_list_get_value + Q_ASSERT(fdata_); + + if (!cap_file) { + return; + } + + bool dissect_color = !colorized_ || ( color_ver_ != rows_color_ver_ ); + if (dissect_color) { + /* Dissect columns only if it won't evict anything from cache */ + bool dissect_columns = col_text_cache_.totalCost() < col_text_cache_.maxCost(); + dissect(cap_file, dissect_columns, dissect_color); + } +} + +// We might want to return a const char * instead. This would keep us from +// creating excessive QByteArrays, e.g. in PacketListModel::recordLessThan. +const QString PacketListRecord::columnString(capture_file *cap_file, int column, bool colorized) +{ + // packet_list_store.c:packet_list_get_value + Q_ASSERT(fdata_); + + if (!cap_file || column < 0 || column >= cap_file->cinfo.num_cols) { + return QString(); + } + + // + // XXX - do we still need to check the colorization, given that we now + // have the ensureColorized() method to ensure that the record is + // properly colorized? + // + bool dissect_color = ( colorized && !colorized_ ) || ( color_ver_ != rows_color_ver_ ); + QStringList *col_text = nullptr; + if (!dissect_color) { + col_text = col_text_cache_.object(fdata_->num); + } + if (col_text == nullptr || column >= col_text->count() || col_text->at(column).isNull()) { + dissect(cap_file, true, dissect_color); + col_text = col_text_cache_.object(fdata_->num); + } + + return col_text ? col_text->at(column) : QString(); +} + +void PacketListRecord::resetColumns(column_info *cinfo) +{ + invalidateAllRecords(); + + if (!cinfo) { + return; + } + + cinfo_column_.clear(); + int i, j; + for (i = 0, j = 0; i < cinfo->num_cols; i++) { + if (!col_based_on_frame_data(cinfo, i)) { + cinfo_column_[i] = j; + j++; + } + } +} + +void PacketListRecord::dissect(capture_file *cap_file, bool dissect_columns, bool dissect_color) +{ + // packet_list_store.c:packet_list_dissect_and_cache_record + epan_dissect_t edt; + column_info *cinfo = NULL; + gboolean create_proto_tree; + wtap_rec rec; /* Record metadata */ + Buffer buf; /* Record data */ + + if (!cap_file) { + return; + } + + if (dissect_columns) { + cinfo = &cap_file->cinfo; + } + + wtap_rec_init(&rec); + ws_buffer_init(&buf, 1514); + if (read_failed_) { + read_failed_ = !cf_read_record_no_alert(cap_file, fdata_, &rec, &buf); + } else { + read_failed_ = !cf_read_record(cap_file, fdata_, &rec, &buf); + } + + if (read_failed_) { + /* + * Error reading the record. + * + * Don't set the color filter for now (we might want + * to colorize it in some fashion to warn that the + * row couldn't be filled in or colorized), and + * set the columns to placeholder values, except + * for the Info column, where we'll put in an + * error message. + */ + if (dissect_columns) { + col_fill_in_error(cinfo, fdata_, FALSE, FALSE /* fill_fd_columns */); + + cacheColumnStrings(cinfo); + } + if (dissect_color) { + fdata_->color_filter = NULL; + colorized_ = true; + } + ws_buffer_free(&buf); + wtap_rec_cleanup(&rec); + return; /* error reading the record */ + } + + /* + * Determine whether we need to create a protocol tree. + * We do if: + * + * we're going to apply a color filter to this packet; + * + * we're need to fill in the columns and we have custom columns + * (which require field values, which currently requires that + * we build a protocol tree). + * + * XXX - field extractors? (Not done for GTK+....) + */ + create_proto_tree = ((dissect_color && color_filters_used()) || + (dissect_columns && (have_custom_cols(cinfo) || + have_field_extractors()))); + + epan_dissect_init(&edt, cap_file->epan, + create_proto_tree, + FALSE /* proto_tree_visible */); + + /* Re-color when the coloring rules are changed via the UI. */ + if (dissect_color) { + color_filters_prime_edt(&edt); + fdata_->need_colorize = 1; + } + if (dissect_columns) + col_custom_prime_edt(&edt, cinfo); + + /* + * XXX - need to catch an OutOfMemoryError exception and + * attempt to recover from it. + */ + epan_dissect_run(&edt, cap_file->cd_t, &rec, + frame_tvbuff_new_buffer(&cap_file->provider, fdata_, &buf), + fdata_, cinfo); + + if (dissect_columns) { + /* "Stringify" non frame_data vals */ + epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */); + cacheColumnStrings(cinfo); + } + + if (dissect_color) { + colorized_ = true; + color_ver_ = rows_color_ver_; + } + + struct conversation * conv = find_conversation_pinfo(&edt.pi, 0); + conv_index_ = ! conv ? 0 : conv->conv_index; + + epan_dissect_cleanup(&edt); + ws_buffer_free(&buf); + wtap_rec_cleanup(&rec); +} + +void PacketListRecord::cacheColumnStrings(column_info *cinfo) +{ + // packet_list_store.c:packet_list_change_record(PacketList *packet_list, PacketListRecord *record, gint col, column_info *cinfo) + if (!cinfo) { + return; + } + + QStringList *col_text = new QStringList(); + + lines_ = 1; + line_count_changed_ = false; + + for (int column = 0; column < cinfo->num_cols; ++column) { + int col_lines = 1; + + QString col_str; + int text_col = cinfo_column_.value(column, -1); + if (text_col < 0) { + col_fill_in_frame_data(fdata_, cinfo, column, FALSE); + } + + col_str = QString(get_column_text(cinfo, column)); + *col_text << col_str; + col_lines = static_cast(col_str.count('\n')); + if (col_lines > lines_) { + lines_ = col_lines; + line_count_changed_ = true; + } + } + + col_text_cache_.insert(fdata_->num, col_text); +} diff --git a/ui/qt/models/packet_list_record.h b/ui/qt/models/packet_list_record.h new file mode 100644 index 00000000..47aa5621 --- /dev/null +++ b/ui/qt/models/packet_list_record.h @@ -0,0 +1,84 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PACKET_LIST_RECORD_H +#define PACKET_LIST_RECORD_H + +#include + +#include + +#include "cfile.h" + +#include +#include + +#include +#include +#include +#include + +struct conversation; +struct _GStringChunk; + +class PacketListRecord +{ +public: + PacketListRecord(frame_data *frameData); + virtual ~PacketListRecord(); + + // Ensure that the record is colorized. + void ensureColorized(capture_file *cap_file); + // Return the string value for a column. Data is cached if possible. + const QString columnString(capture_file *cap_file, int column, bool colorized = false); + frame_data *frameData() const { return fdata_; } + // packet_list->col_to_text in gtk/packet_list_store.c + static int textColumn(int column) { return cinfo_column_.value(column, -1); } + bool colorized() { return colorized_ && (color_ver_ == rows_color_ver_); } + unsigned int conversation() { return conv_index_; } + + int columnTextSize(const char *str); + + void invalidateColorized() { colorized_ = false; } + void invalidateRecord() { col_text_cache_.remove(fdata_->num); } + static void invalidateAllRecords() { col_text_cache_.clear(); } + /* In Qt 6, QCache maxCost is a qsizetype, but the QAbstractItemModel + * number of rows is still an int, so we're limited to INT_MAX anyway. + */ + static void setMaxCache(int cost) { col_text_cache_.setMaxCost(cost); } + static void resetColumns(column_info *cinfo); + static void resetColorization() { rows_color_ver_++; } + + inline int lineCount() { return lines_; } + inline int lineCountChanged() { return line_count_changed_; } + +private: + /** The column text for some columns */ + static QCache col_text_cache_; + + frame_data *fdata_; + int lines_; + bool line_count_changed_; + static QMap cinfo_column_; + + /** Has this record been colorized? */ + static unsigned int rows_color_ver_; + unsigned int color_ver_; + bool colorized_; + + /** Conversation. Used by RelatedPacketDelegate */ + unsigned int conv_index_; + + bool read_failed_; + + void dissect(capture_file *cap_file, bool dissect_columns, bool dissect_color = false); + void cacheColumnStrings(column_info *cinfo); +}; + +#endif // PACKET_LIST_RECORD_H diff --git a/ui/qt/models/path_selection_delegate.cpp b/ui/qt/models/path_selection_delegate.cpp new file mode 100644 index 00000000..df614ef3 --- /dev/null +++ b/ui/qt/models/path_selection_delegate.cpp @@ -0,0 +1,63 @@ +/* path_chooser_delegate.cpp + * Delegate to select a file path for a treeview entry + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +PathSelectionDelegate::PathSelectionDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +QWidget* PathSelectionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const +{ + PathSelectionEdit * editor = new PathSelectionEdit(tr("Open a pipe"), QString(), true, parent); + + connect(editor, &PathSelectionEdit::pathChanged, this, &PathSelectionDelegate::pathHasChanged); + + return editor; +} + +void PathSelectionDelegate::pathHasChanged(QString) +{ + PathSelectionEdit * editor = qobject_cast(sender()); + if (editor) + emit commitData(editor); +} + +void PathSelectionDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const +{ + editor->setGeometry(option.rect); +} + +void PathSelectionDelegate::setEditorData(QWidget *editor, const QModelIndex &idx) const +{ + if (idx.isValid() && qobject_cast(editor) != nullptr) + { + PathSelectionEdit * edit = qobject_cast(editor); + edit->setPath(idx.data().toString()); + } + else + QStyledItemDelegate::setEditorData(editor, idx); +} + +void PathSelectionDelegate::setModelData(QWidget *editor, QAbstractItemModel * model, const QModelIndex &idx) const +{ + if (idx.isValid() && qobject_cast(editor) != nullptr) + { + PathSelectionEdit * edit = qobject_cast(editor); + model->setData(idx, edit->path()); + } + else + { + QStyledItemDelegate::setModelData(editor, model, idx); + } +} + diff --git a/ui/qt/models/path_selection_delegate.h b/ui/qt/models/path_selection_delegate.h new file mode 100644 index 00000000..6d33f116 --- /dev/null +++ b/ui/qt/models/path_selection_delegate.h @@ -0,0 +1,35 @@ +/** @file + * + * Delegate to select a file path for a treeview entry + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PATH_SELECTION_DELEGATE_H_ +#define PATH_SELECTION_DELEGATE_H_ + +#include + +class PathSelectionDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + PathSelectionDelegate(QObject *parent = 0); + +protected: + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &idx) const override; + void updateEditorGeometry (QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & idx) const override; + void setEditorData(QWidget *editor, const QModelIndex &idx) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &idx) const override; + +protected slots: + void pathHasChanged(QString newPath); + +}; + +#endif /* PATH_SELECTION_DELEGATE_H_ */ diff --git a/ui/qt/models/percent_bar_delegate.cpp b/ui/qt/models/percent_bar_delegate.cpp new file mode 100644 index 00000000..091c5fb0 --- /dev/null +++ b/ui/qt/models/percent_bar_delegate.cpp @@ -0,0 +1,91 @@ +/* percent_bar_delegate.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include + +static const int bar_em_width_ = 8; +static const double bar_blend_ = 0.15; + +void PercentBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionViewItem option_vi = option; + QStyledItemDelegate::initStyleOption(&option_vi, index); + + // Paint our rect with no text using the current style, then draw our + // bar and text over it. + QStyledItemDelegate::paint(painter, option, index); + + bool ok = false; + double value = index.data(Qt::UserRole).toDouble(&ok); + + if (!ok || !index.data(Qt::DisplayRole).toString().isEmpty()) { + // We don't have a valid value or the item has visible text. + return; + } + + // If our value is out range our caller has a bug. Clamp the graph and + // Print the numeric value so that the bug is obvious. + QString pct_str = QString::number(value, 'f', 1); + if (value < 0) { + value = 0; + } + if (value > 100.0) { + value = 100.0; + } + + if (QApplication::style()->objectName().contains("vista")) { + // QWindowsVistaStyle::drawControl does this internally. Unfortunately there + // doesn't appear to be a more general way to do this. + option_vi.palette.setColor(QPalette::All, QPalette::HighlightedText, + option_vi.palette.color(QPalette::Active, QPalette::Text)); + } + + QPalette::ColorGroup cg = option_vi.state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + QColor text_color = option_vi.palette.color(cg, QPalette::Text); + QColor bar_color = ColorUtils::alphaBlend(option_vi.palette.windowText(), + option_vi.palette.window(), bar_blend_); + + if (cg == QPalette::Normal && !(option_vi.state & QStyle::State_Active)) + cg = QPalette::Inactive; + if (option_vi.state & QStyle::State_Selected) { + text_color = option_vi.palette.color(cg, QPalette::HighlightedText); + bar_color = ColorUtils::alphaBlend(option_vi.palette.color(cg, QPalette::Window), + option_vi.palette.color(cg, QPalette::Highlight), + bar_blend_); + } + + painter->save(); + int border_radius = 3; // We use 3 px elsewhere, e.g. filter combos. + QRect pct_rect = option.rect; + pct_rect.adjust(1, 1, -1, -1); + pct_rect.setWidth(((pct_rect.width() * value) / 100.0) + 0.5); + painter->setPen(Qt::NoPen); + painter->setBrush(bar_color); + painter->drawRoundedRect(pct_rect, border_radius, border_radius); + painter->restore(); + + painter->save(); + painter->setPen(text_color); + painter->drawText(option.rect, Qt::AlignCenter, pct_str); + painter->restore(); +} + +QSize PercentBarDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + return QSize(option.fontMetrics.height() * bar_em_width_, + QStyledItemDelegate::sizeHint(option, index).height()); +} diff --git a/ui/qt/models/percent_bar_delegate.h b/ui/qt/models/percent_bar_delegate.h new file mode 100644 index 00000000..4f0d53df --- /dev/null +++ b/ui/qt/models/percent_bar_delegate.h @@ -0,0 +1,52 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PERCENTBARDELEGATE_H +#define PERCENTBARDELEGATE_H + +/* + * @file Percent bar delegate. + * + * QStyledItemDelegate subclass that will draw a percentage value and a + * single-item bar chart for the specified value. + * + * This is intended to be used in QTreeWidgets to show percentage values. + * To use it, first call setItemDelegate: + * + * myTreeWidget()->setItemDelegateForColumn(col_pct_, new PercentBarDelegate()); + * + * Then, for each QTreeWidgetItem, set a double value using setData: + * + * setData(col_pct_, Qt::UserRole, QVariant::fromValue(packets_ * 100.0 / num_packets)); + * + * If the item data cannot be converted to a valid double value or if its + * text string is non-empty then it will be rendered normally (i.e. the + * percent text and bar will not be drawn). This lets you mix normal and + * percent bar rendering between rows. + */ + +#include + +class PercentBarDelegate : public QStyledItemDelegate +{ +public: + PercentBarDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) { } + + // Make sure QStyledItemDelegate::paint doesn't draw any text. + virtual QString displayText(const QVariant &, const QLocale &) const { return QString(); } + +protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const; + +}; + +#endif // PERCENTBARDELEGATE_H diff --git a/ui/qt/models/pref_delegate.cpp b/ui/qt/models/pref_delegate.cpp new file mode 100644 index 00000000..e33bb13f --- /dev/null +++ b/ui/qt/models/pref_delegate.cpp @@ -0,0 +1,85 @@ +/* pref_delegate.cpp + * Delegates for editing prefereneces. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include +#include + +AdvancedPrefDelegate::AdvancedPrefDelegate(QObject *parent) : QStyledItemDelegate(parent) +{ +} + +PrefsItem* AdvancedPrefDelegate::indexToPref(const QModelIndex &index) const +{ + const QVariant v = index.model()->data(index, Qt::UserRole); + return VariantPointer::asPtr(v); +} + +QWidget *AdvancedPrefDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + PrefsItem* pref; + QString filename; + + switch(index.column()) + { + case AdvancedPrefsModel::colName: + case AdvancedPrefsModel::colStatus: + case AdvancedPrefsModel::colType: + //If user clicks on any of these columns, reset preference back to default + //There is no need to launch an editor + const_cast(index.model())->setData(index, QVariant(), Qt::EditRole); + break; + case AdvancedPrefsModel::colValue: + pref = indexToPref(index); + WiresharkPreference * wspref = PreferenceManager::instance()->getPreference(pref); + if (wspref) { + QWidget *editor = wspref->editor(parent, option, index); + if (editor) { + editor->setAutoFillBackground(true); + } + return editor; + } + break; + } + + return Q_NULLPTR; +} + +void AdvancedPrefDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + PrefsItem* pref = indexToPref(index); + + WiresharkPreference * wspref = PreferenceManager::instance()->getPreference(pref); + if (wspref) + { + wspref->setData(editor, index); + return; + } + + Q_ASSERT(FALSE); +} + +void AdvancedPrefDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + PrefsItem* pref = indexToPref(index); + + WiresharkPreference * wspref = PreferenceManager::instance()->getPreference(pref); + if (wspref) + { + wspref->setModelData(editor, model, index); + return; + } + + Q_ASSERT(FALSE); +} diff --git a/ui/qt/models/pref_delegate.h b/ui/qt/models/pref_delegate.h new file mode 100644 index 00000000..91054e9a --- /dev/null +++ b/ui/qt/models/pref_delegate.h @@ -0,0 +1,37 @@ +/** @file + * + * Delegates for editing prefereneces. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PREF_DELEGATE_H +#define PREF_DELEGATE_H + +#include + +#include + +#include +#include + +class AdvancedPrefDelegate : public QStyledItemDelegate +{ +public: + AdvancedPrefDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + +private: + PrefsItem* indexToPref(const QModelIndex &index) const; +}; + +#endif // PREF_DELEGATE_H diff --git a/ui/qt/models/pref_models.cpp b/ui/qt/models/pref_models.cpp new file mode 100644 index 00000000..b86230f0 --- /dev/null +++ b/ui/qt/models/pref_models.cpp @@ -0,0 +1,774 @@ +/* pref_models.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +#ifdef HAVE_LIBPCAP +#ifdef _WIN32 +#include "capture/capture-wpcap.h" +#endif /* _WIN32 */ +#endif /* HAVE_LIBPCAP */ + +#include +#include +#include +#include + +// XXX Should we move this to ui/preference_utils? +static GHashTable * pref_ptr_to_pref_ = NULL; +pref_t *prefFromPrefPtr(void *pref_ptr) +{ + return (pref_t *)g_hash_table_lookup(pref_ptr_to_pref_, (gpointer) pref_ptr); +} + +static void prefInsertPrefPtr(void * pref_ptr, pref_t * pref) +{ + if (! pref_ptr_to_pref_) + pref_ptr_to_pref_ = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); + + gpointer key = (gpointer) pref_ptr; + gpointer val = (gpointer) pref; + + /* Already existing entries will be ignored */ + if ((void *)g_hash_table_lookup(pref_ptr_to_pref_, key) == NULL) + g_hash_table_insert(pref_ptr_to_pref_, key, val); +} + +PrefsItem::PrefsItem(module_t *module, pref_t *pref, PrefsItem* parent) + : ModelHelperTreeItem(parent), + pref_(pref), + module_(module), + name_(module->name ? module->name : module->parent->name), + changed_(false) +{ + if (pref_ != NULL) { + name_ += QString(".%1").arg(prefs_get_name(pref_)); + } +} + +PrefsItem::PrefsItem(const QString name, PrefsItem* parent) + : ModelHelperTreeItem(parent), + pref_(NULL), + module_(NULL), + name_(name), + changed_(false) +{ + +} + +PrefsItem::~PrefsItem() +{ +} + +int PrefsItem::getPrefType() const +{ + if (pref_ == NULL) + return 0; + + return prefs_get_type(pref_); +} + +bool PrefsItem::isPrefDefault() const +{ + if (pref_ == NULL) + return true; + + if (changed_ == false) + return prefs_pref_is_default(pref_) ? true : false; + + return false; +} + +QString PrefsItem::getPrefTypeName() const +{ + if (pref_ == NULL) + return ""; + + return QString(prefs_pref_type_name(pref_)); +} + +QString PrefsItem::getModuleName() const +{ + if (module_ == NULL) + return name_; + + return QString(module_->name); +} + +QString PrefsItem::getModuleTitle() const +{ + if ((module_ == NULL) && (pref_ == NULL)) + return name_; + + Q_ASSERT(module_); + + return QString(module_->title); +} + +void PrefsItem::setChanged(bool changed) +{ + changed_ = changed; +} + +PrefsModel::PrefsModel(QObject *parent) : + QAbstractItemModel(parent), + root_(new PrefsItem(QString("ROOT"), NULL)) +{ + populate(); +} + +PrefsModel::~PrefsModel() +{ + delete root_; +} + +int PrefsModel::rowCount(const QModelIndex &parent) const +{ + PrefsItem *parent_item; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + if (parent_item == NULL) + return 0; + + return static_cast(parent_item->childCount()); +} + +int PrefsModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + + +QModelIndex PrefsModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return QModelIndex(); + + PrefsItem* item = static_cast(index.internalPointer()); + if (item != NULL) { + PrefsItem* parent_item = item->parentItem(); + if (parent_item != NULL) { + if (parent_item == root_) + return QModelIndex(); + + return createIndex(parent_item->row(), 0, parent_item); + } + } + + return QModelIndex(); +} + +QModelIndex PrefsModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + PrefsItem *parent_item, *child_item; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + Q_ASSERT(parent_item); + + child_item = parent_item->child(row); + if (child_item) { + return createIndex(row, column, child_item); + } + + return QModelIndex(); +} + +QVariant PrefsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::UserRole)) + return QVariant(); + + PrefsItem* item = static_cast(index.internalPointer()); + if (item == NULL) + return QVariant(); + + if (role == Qt::UserRole) + return VariantPointer::asQVariant(item); + + switch ((enum PrefsModelColumn)index.column()) { + case colName: + return item->getName(); + + case colStatus: + if (item->getPrefType() == PREF_UAT || item->getPrefType() == PREF_CUSTOM) + return QObject::tr("Unknown"); + + if (item->isPrefDefault()) + return QObject::tr("Default"); + + return QObject::tr("Changed"); + case colType: + return item->getPrefTypeName(); + case colValue: + if (item->getPref() == NULL) + return QVariant(); + + return QString(gchar_free_to_qstring(prefs_pref_to_str(item->getPref(), pref_stashed)).remove(QRegularExpression("\n\t"))); + default: + break; + } + + return QVariant(); +} + +static guint +fill_prefs(module_t *module, gpointer root_ptr) +{ + PrefsItem* root_item = static_cast(root_ptr); + + if ((module == NULL) || (root_item == NULL)) + return 1; + + if (module->numprefs < 1 && !prefs_module_has_submodules(module)) + return 0; + + PrefsItem* module_item = new PrefsItem(module, NULL, root_item); + root_item->prependChild(module_item); + + for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = gxx_list_next(pref_l)) { + pref_t *pref = gxx_list_data(pref_t *, pref_l); + + if (prefs_get_type(pref) == PREF_OBSOLETE || prefs_get_type(pref) == PREF_STATIC_TEXT) + continue; + + const char *type_name = prefs_pref_type_name(pref); + if (!type_name) + continue; + + pref_stash(pref, NULL); + + PrefsItem* item = new PrefsItem(module, pref, module_item); + module_item->prependChild(item); + + // .uat is a void * so it wins the "useful key value" prize. + if (prefs_get_uat_value(pref)) { + prefInsertPrefPtr(prefs_get_uat_value(pref), pref); + } + } + + if (prefs_module_has_submodules(module)) + return prefs_modules_foreach_submodules(module, fill_prefs, module_item); + + return 0; +} + +void PrefsModel::populate() +{ + prefs_modules_foreach_submodules(NULL, fill_prefs, (gpointer)root_); + + //Add the "specially handled" preferences + PrefsItem *appearance_item, *appearance_subitem, *special_item; + + appearance_item = new PrefsItem(typeToString(PrefsModel::Appearance), root_); + root_->prependChild(appearance_item); + + appearance_subitem = new PrefsItem(typeToString(PrefsModel::Layout), appearance_item); + appearance_item->prependChild(appearance_subitem); + appearance_subitem = new PrefsItem(typeToString(PrefsModel::Columns), appearance_item); + appearance_item->prependChild(appearance_subitem); + appearance_subitem = new PrefsItem(typeToString(PrefsModel::FontAndColors), appearance_item); + appearance_item->prependChild(appearance_subitem); + + special_item = new PrefsItem(typeToString(PrefsModel::Capture), root_); + root_->prependChild(special_item); + special_item = new PrefsItem(typeToString(PrefsModel::Expert), root_); + root_->prependChild(special_item); + special_item = new PrefsItem(typeToString(PrefsModel::FilterButtons), root_); + root_->prependChild(special_item); +#ifdef HAVE_LIBGNUTLS + special_item = new PrefsItem(typeToString(PrefsModel::RSAKeys), root_); + root_->prependChild(special_item); +#endif + special_item = new PrefsItem(typeToString(PrefsModel::Advanced), root_); + root_->prependChild(special_item); +} + +QString PrefsModel::typeToString(int type) +{ + QString typeStr; + + switch(type) + { + case Advanced: typeStr = tr("Advanced"); break; + case Appearance: typeStr = tr("Appearance"); break; + case Layout: typeStr = tr("Layout"); break; + case Columns: typeStr = tr("Columns"); break; + case FontAndColors: typeStr = tr("Font and Colors"); break; + case Capture: typeStr = tr("Capture"); break; + case Expert: typeStr = tr("Expert"); break; + case FilterButtons: typeStr = tr("Filter Buttons"); break; + case RSAKeys: typeStr = tr("RSA Keys"); break; + } + + return typeStr; +} + +AdvancedPrefsModel::AdvancedPrefsModel(QObject * parent) +: QSortFilterProxyModel(parent), +filter_(), +show_changed_values_(false), +passwordChar_(QApplication::style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter)) +{ +} + +QVariant AdvancedPrefsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + + switch (section) { + case colName: + return tr("Name"); + case colStatus: + return tr("Status"); + case colType: + return tr("Type"); + case colValue: + return tr("Value"); + default: + break; + } + } + return QVariant(); +} + +QVariant AdvancedPrefsModel::data(const QModelIndex &dataindex, int role) const +{ + if (!dataindex.isValid()) + return QVariant(); + + QModelIndex modelIndex = mapToSource(dataindex); + + PrefsItem* item = static_cast(modelIndex.internalPointer()); + if (item == NULL) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + switch ((AdvancedPrefsModelColumn)dataindex.column()) + { + case colName: + if (item->getPref() == NULL) + return item->getModule()->title; + + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colName, modelIndex.parent()), role); + case colStatus: + if (item->getPref() == NULL) + return QVariant(); + + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colStatus, modelIndex.parent()), role); + case colType: + if (item->getPref() == NULL) + return QVariant(); + + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colType, modelIndex.parent()), role); + case colValue: + if (item->getPref() == NULL) + return QVariant(); + + if (PREF_PASSWORD == item->getPrefType()) + { + return QString(sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colValue, modelIndex.parent()), role).toString().size(), passwordChar_); + } else { + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colValue, modelIndex.parent()), role); + } + default: + break; + } + break; + case Qt::ToolTipRole: + switch ((AdvancedPrefsModelColumn)dataindex.column()) + { + case colName: + if (item->getPref() == NULL) + return QString("%1").arg(item->getModule()->description); + + return QString("%1").arg(prefs_get_description(item->getPref())); + case colStatus: + if (item->getPref() == NULL) + return QVariant(); + + return QObject::tr("Has this preference been changed?"); + case colType: + if (item->getPref() == NULL) { + return QVariant(); + } else { + QString type_desc = gchar_free_to_qstring(prefs_pref_type_description(item->getPref())); + return QString("%1").arg(type_desc); + } + break; + case colValue: + if (item->getPref() == NULL) { + return QVariant(); + } else { + QString default_value = gchar_free_to_qstring(prefs_pref_to_str(item->getPref(), pref_stashed)); + return QString("%1").arg( + default_value.isEmpty() ? default_value : QObject::tr("Default value is empty")); + } + default: + break; + } + break; + case Qt::FontRole: + if (item->getPref() == NULL) + return QVariant(); + + if (!item->isPrefDefault() && + /* UATs and custom preferences are "unknown", that shouldn't mean that they are always bolded */ + item->getPrefType() != PREF_UAT && item->getPrefType() != PREF_CUSTOM) { + QFont font; + font.setBold(true); + return font; + } + break; + case Qt::UserRole: + return sourceModel()->data(modelIndex, role); + default: + break; + } + + return QVariant(); +} + +bool AdvancedPrefsModel::setData(const QModelIndex &dataindex, const QVariant &value, int role) +{ + if ((!dataindex.isValid()) || (role != Qt::EditRole)) + return false; + + QModelIndex modelIndex = mapToSource(dataindex); + + PrefsItem* item = static_cast(modelIndex.internalPointer()); + if (item == NULL) + return false; + + if (value.isNull()) { + //reset preference to default + reset_stashed_pref(item->getPref()); + item->setChanged(false); + } else { + item->setChanged(true); + switch (item->getPrefType()) + { + case PREF_DECODE_AS_UINT: + case PREF_UINT: + { + bool ok; + guint new_val = value.toString().toUInt(&ok, prefs_get_uint_base(item->getPref())); + + if (ok) + prefs_set_uint_value(item->getPref(), new_val, pref_stashed); + } + break; + case PREF_BOOL: + prefs_invert_bool_value(item->getPref(), pref_stashed); + break; + case PREF_ENUM: + prefs_set_enum_value(item->getPref(), value.toInt(), pref_stashed); + break; + case PREF_STRING: + prefs_set_string_value(item->getPref(), value.toString().toStdString().c_str(), pref_stashed); + break; + case PREF_PASSWORD: + prefs_set_password_value(item->getPref(), value.toString().toStdString().c_str(), pref_stashed); + break; + case PREF_DECODE_AS_RANGE: + case PREF_RANGE: + prefs_set_stashed_range_value(item->getPref(), value.toString().toUtf8().constData()); + break; + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + prefs_set_string_value(item->getPref(), value.toString().toStdString().c_str(), pref_stashed); + break; + case PREF_COLOR: + { + QColor qc(value.toString()); + color_t color; + + color.red = qc.red() << 8 | qc.red(); + color.green = qc.green() << 8 | qc.green(); + color.blue = qc.blue() << 8 | qc.blue(); + + prefs_set_color_value(item->getPref(), color, pref_stashed); + break; + } + case PREF_CUSTOM: + prefs_set_custom_value(item->getPref(), value.toString().toStdString().c_str(), pref_stashed); + break; + } + } + + QVector roles; + roles << role; + + // The status field may change as well as the value, so mark them for update + emit dataChanged(index(dataindex.row(), 0, dataindex.parent()), + index(dataindex.row(), columnCount() - 1, dataindex.parent()), + roles); + + return true; +} + +Qt::ItemFlags AdvancedPrefsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemFlags(); + + QModelIndex modelIndex = mapToSource(index); + + PrefsItem* item = static_cast(modelIndex.internalPointer()); + if (item == NULL) + return Qt::ItemFlags(); + + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + if (item->getPref() == NULL) { + /* Base modules aren't changable */ + flags &= ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + } else { + flags |= Qt::ItemIsEditable; + } + + return flags; +} + + +int AdvancedPrefsModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + +void AdvancedPrefsModel::setFirstColumnSpanned(QTreeView* tree, const QModelIndex& mIndex) +{ + int childCount, row; + PrefsItem* item; + if (mIndex.isValid()) { + item = VariantPointer::asPtr(data(mIndex, Qt::UserRole)); + if (item != NULL) { + childCount = item->childCount(); + if (childCount > 0) { + tree->setFirstColumnSpanned(mIndex.row(), mIndex.parent(), true); + for (row = 0; row < childCount; row++) { + setFirstColumnSpanned(tree, index(row, 0, mIndex)); + } + } + } + } else { + for (row = 0; row < rowCount(); row++) { + setFirstColumnSpanned(tree, index(row, 0)); + } + } +} + +bool AdvancedPrefsModel::filterAcceptItem(PrefsItem& item) const +{ + if (filter_.isEmpty() && !show_changed_values_) + return true; + + QString name, tooltip; + if (item.getPref() == NULL) { + name = item.getModule()->title; + tooltip = item.getModule()->description; + } else { + name = QString(item.getModule()->name ? item.getModule()->name : item.getModule()->parent->name); + name += QString(".%1").arg(prefs_get_name(item.getPref())); + tooltip = prefs_get_description(item.getPref()); + } + + if (show_changed_values_ && item.getPref()) { + // UATs and custom preferences are "unknown", do not show when show_changed_only. + if (item.isPrefDefault() || item.getPrefType() == PREF_UAT || item.getPrefType() == PREF_CUSTOM) { + return false; + } else if (filter_.isEmpty()) { + return true; + } + } + + // Do not match module title or description when having show_changed_only. + if (!(filter_.isEmpty() || (show_changed_values_ && !item.getPref())) && + (name.contains(filter_, Qt::CaseInsensitive) || tooltip.contains(filter_, Qt::CaseInsensitive))) + return true; + + PrefsItem *child_item; + for (int child_row = 0; child_row < item.childCount(); child_row++) + { + child_item = item.child(child_row); + if ((child_item != NULL) && (filterAcceptItem(*child_item))) + return true; + } + + return false; +} + +bool AdvancedPrefsModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex nameIdx = sourceModel()->index(sourceRow, PrefsModel::colName, sourceParent); + PrefsItem* item = static_cast(nameIdx.internalPointer()); + if (item == NULL) + return true; + + //filter out the "special" preferences + if ((item->getModule() == NULL) && (item->getPref() == NULL)) + return false; + + if (filterAcceptItem(*item)) + return true; + + return false; +} + +void AdvancedPrefsModel::setFilter(const QString& filter) +{ + filter_ = filter; + invalidateFilter(); +} + +void AdvancedPrefsModel::setShowChangedValues(bool show_changed_values) +{ + show_changed_values_ = show_changed_values; + invalidateFilter(); +} + + + +ModulePrefsModel::ModulePrefsModel(QObject* parent) + : QSortFilterProxyModel(parent) + , advancedPrefName_(PrefsModel::typeToString(PrefsModel::Advanced)) +{ +} + +QVariant ModulePrefsModel::data(const QModelIndex &dataindex, int role) const +{ + if (!dataindex.isValid()) + return QVariant(); + + QModelIndex modelIndex = mapToSource(dataindex); + + PrefsItem* item = static_cast(modelIndex.internalPointer()); + if (item == NULL) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + switch ((ModulePrefsModelColumn)dataindex.column()) + { + case colName: + return item->getModuleTitle(); + default: + break; + } + break; + case Qt::UserRole: + return sourceModel()->data(modelIndex, role); + case ModuleName: + return item->getModuleName(); + default: + break; + } + + return QVariant(); +} +Qt::ItemFlags ModulePrefsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemFlags(); + + bool disable_capture = true; +#ifdef HAVE_LIBPCAP +#ifdef _WIN32 + /* Is WPcap loaded? */ + if (has_wpcap) { +#endif /* _WIN32 */ + disable_capture = false; +#ifdef _WIN32 + } +#endif /* _WIN32 */ +#endif /* HAVE_LIBPCAP */ + + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + if (disable_capture) { + QModelIndex modelIndex = mapToSource(index); + + PrefsItem* item = static_cast(modelIndex.internalPointer()); + if (item == NULL) + return flags; + + if (item->getName().compare(PrefsModel::typeToString(PrefsModel::Capture)) == 0) { + flags &= (~Qt::ItemIsEnabled); + } + } + + return flags; +} + +int ModulePrefsModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + +bool ModulePrefsModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + PrefsItem* left_item = static_cast(source_left.internalPointer()); + PrefsItem* right_item = static_cast(source_right.internalPointer()); + + if ((left_item != NULL) && (right_item != NULL)) { + QString left_name = left_item->getModuleTitle(), + right_name = right_item->getModuleTitle(); + + //Force "Advanced" preferences to be at bottom of model + if (source_left.isValid() && !source_left.parent().isValid() && + source_right.isValid() && !source_right.parent().isValid()) { + if (left_name.compare(advancedPrefName_) == 0) { + return false; + } + if (right_name.compare(advancedPrefName_) == 0) { + return true; + } + } + + if (left_name.compare(right_name, Qt::CaseInsensitive) < 0) + return true; + } + + return false; +} + +bool ModulePrefsModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex nameIdx = sourceModel()->index(sourceRow, PrefsModel::colName, sourceParent); + PrefsItem* item = static_cast(nameIdx.internalPointer()); + if (item == NULL) + return true; + + if (item->getPref() != NULL) + return false; + + if (item->getModule() != NULL) { + if (!item->getModule()->use_gui) { + return false; + } + } + + return true; +} diff --git a/ui/qt/models/pref_models.h b/ui/qt/models/pref_models.h new file mode 100644 index 00000000..775da0d0 --- /dev/null +++ b/ui/qt/models/pref_models.h @@ -0,0 +1,165 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PREF_MODELS_H +#define PREF_MODELS_H + +#include + +#include + +#include + +#include +#include + +class PrefsItem : public ModelHelperTreeItem +{ +public: + PrefsItem(module_t *module, pref_t *pref, PrefsItem* parent); + PrefsItem(const QString name, PrefsItem* parent); + virtual ~PrefsItem(); + + QString getName() const {return name_;} + pref_t* getPref() const {return pref_;} + int getPrefType() const; + bool isPrefDefault() const; + QString getPrefTypeName() const; + module_t* getModule() const {return module_;} + QString getModuleName() const; + QString getModuleTitle() const; + void setChanged(bool changed = true); + +private: + pref_t *pref_; + module_t *module_; + QString name_; + //set to true if changed during module manipulation + //Used to determine proper "default" for comparison + bool changed_; +}; + + +class PrefsModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit PrefsModel(QObject * parent = Q_NULLPTR); + virtual ~PrefsModel(); + + enum PrefsModelType { + Advanced = Qt::UserRole, + Appearance, + Layout, + Columns, + FontAndColors, + Capture, + Expert, + FilterButtons, + RSAKeys + }; + + enum PrefsModelColumn { + colName = 0, + colStatus, + colType, + colValue, + colLast + }; + + QModelIndex index(int row, int column, + const QModelIndex & = QModelIndex()) const; + QModelIndex parent(const QModelIndex &) const; + QVariant data(const QModelIndex &index, int role) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + static QString typeToString(int type); + +private: + void populate(); + + PrefsItem* root_; +}; + +class AdvancedPrefsModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + explicit AdvancedPrefsModel(QObject * parent = Q_NULLPTR); + + enum AdvancedPrefsModelColumn { + colName = 0, + colStatus, + colType, + colValue, + colLast + }; + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + + void setFilter(const QString& filter); + void setShowChangedValues(bool show_changed_values); + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + //Keep the internals of model hidden from tree + void setFirstColumnSpanned(QTreeView* tree, const QModelIndex &index = QModelIndex()); + +protected: + bool filterAcceptItem(PrefsItem& item) const; + +private: + + QString filter_; + bool show_changed_values_; + const QChar passwordChar_; +}; + +class ModulePrefsModel : public QSortFilterProxyModel +{ +public: + + explicit ModulePrefsModel(QObject * parent = Q_NULLPTR); + + enum ModulePrefsModelColumn { + colName = 0, + colLast + }; + + enum ModulePrefsRoles { + ModuleName = Qt::UserRole + 1 + }; + + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + +private: + //cache of the translated "Advanced" preference name + QString advancedPrefName_; +}; + +extern pref_t *prefFromPrefPtr(void *pref_ptr); + +#endif // PREF_MODELS_H diff --git a/ui/qt/models/profile_model.cpp b/ui/qt/models/profile_model.cpp new file mode 100644 index 00000000..206ef389 --- /dev/null +++ b/ui/qt/models/profile_model.cpp @@ -0,0 +1,1299 @@ +/* profile_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include "glib.h" +#include "ui/profile.h" +#include "ui/recent.h" +#include "wsutil/filesystem.h" +#include "epan/prefs.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(profileLogger, "wireshark.profiles") + +ProfileSortModel::ProfileSortModel(QObject * parent): + QSortFilterProxyModel (parent), + ft_(ProfileSortModel::AllProfiles), + ftext_(QString()) +{} + +bool ProfileSortModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + QModelIndex left = source_left; + if (source_left.column() != ProfileModel::COL_NAME) + left = source_left.sibling(source_left.row(), ProfileModel::COL_NAME); + + QModelIndex right = source_right; + if (source_right.column() != ProfileModel::COL_NAME) + right = source_right.sibling(source_right.row(), ProfileModel::COL_NAME); + + bool igL = left.data(ProfileModel::DATA_IS_GLOBAL).toBool(); + bool igR = right.data(ProfileModel::DATA_IS_GLOBAL).toBool(); + + if (left.data(ProfileModel::DATA_STATUS).toInt() == PROF_STAT_DEFAULT) + igL = true; + if (right.data(ProfileModel::DATA_STATUS).toInt() == PROF_STAT_DEFAULT) + igR = true; + + if (igL && ! igR) + return true; + else if (! igL && igR) + return false; + else if (igL && igR) + { + if (left.data(ProfileModel::DATA_STATUS) == PROF_STAT_DEFAULT) + return true; + } + + if (left.data().toString().compare(right.data().toString()) <= 0) + return true; + + return false; +} + +void ProfileSortModel::setFilterType(FilterType ft) +{ + ft_ = ft; + invalidateFilter(); +} + +void ProfileSortModel::setFilterString(QString txt) +{ + ftext_ = ! txt.isEmpty() ? txt : ""; + invalidateFilter(); +} + +QStringList ProfileSortModel::filterTypes() +{ + QMap filter_types_; + filter_types_.insert(ProfileSortModel::AllProfiles, tr("All profiles")); + filter_types_.insert(ProfileSortModel::PersonalProfiles, tr("Personal profiles")); + filter_types_.insert(ProfileSortModel::GlobalProfiles, tr("Global profiles")); + + return filter_types_.values(); +} + +bool ProfileSortModel::filterAcceptsRow(int source_row, const QModelIndex &) const +{ + bool accept = true; + QModelIndex idx = sourceModel()->index(source_row, ProfileModel::COL_NAME); + + if (ft_ != ProfileSortModel::AllProfiles) + { + bool gl = idx.data(ProfileModel::DATA_IS_GLOBAL).toBool(); + if (ft_ == ProfileSortModel::PersonalProfiles && gl) + accept = false; + else if (ft_ == ProfileSortModel::GlobalProfiles && ! gl) + accept = false; + } + + if (ftext_.length() > 0) + { + QString name = idx.data().toString(); + if (! name.contains(ftext_, Qt::CaseInsensitive)) + accept = false; + } + + return accept; +} + +ProfileModel::ProfileModel(QObject * parent) : + QAbstractTableModel(parent) +{ + /* Store preset profile name */ + set_profile_ = get_profile_name(); + + reset_default_ = false; + profiles_imported_ = false; + + last_set_row_ = 0; + + /* Set filenames for profiles */ + GList *files, *file; + files = g_hash_table_get_keys(const_cast(allowed_profile_filenames())); + file = g_list_first(files); + while (file) { + profile_files_ << static_cast(file->data); + file = gxx_list_next(file); + } + g_list_free(files); + + loadProfiles(); +} + +void ProfileModel::loadProfiles() +{ + beginResetModel(); + + bool refresh = profiles_.count() > 0; + + if (refresh) + profiles_.clear(); + else + init_profile_list(); + + GList *fl_entry = edited_profile_list(); + while (fl_entry && fl_entry->data) + { + profiles_ << reinterpret_cast(fl_entry->data); + fl_entry = gxx_list_next(fl_entry); + } + + endResetModel(); +} + +GList * ProfileModel::entry(profile_def *ref) const +{ + GList *fl_entry = edited_profile_list(); + while (fl_entry && fl_entry->data) + { + profile_def *profile = reinterpret_cast(fl_entry->data); + if (QString(ref->name).compare(profile->name) == 0 && + QString(ref->reference).compare(profile->reference) == 0 && + ref->is_global == profile->is_global && + ref->status == profile->status) + { + return fl_entry; + } + + fl_entry = gxx_list_next(fl_entry); + } + + return Q_NULLPTR; +} + +GList *ProfileModel::at(int row) const +{ + if (row < 0 || row >= profiles_.count()) + return Q_NULLPTR; + + profile_def * prof = profiles_.at(row); + return entry(prof); +} + +bool ProfileModel::changesPending() const +{ + if (reset_default_) + return true; + + if (g_list_length(edited_profile_list()) != g_list_length(current_profile_list())) + return true; + + bool pending = false; + GList *fl_entry = edited_profile_list(); + while (fl_entry && fl_entry->data && ! pending) { + profile_def *profile = reinterpret_cast(fl_entry->data); + pending = (profile->status == PROF_STAT_NEW || profile->status == PROF_STAT_CHANGED || profile->status == PROF_STAT_COPY); + fl_entry = gxx_list_next(fl_entry); + } + + return pending; +} + +bool ProfileModel::importPending() const +{ + return profiles_imported_; +} + +bool ProfileModel::userProfilesExist() const +{ + bool user_exists = false; + for (int cnt = 0; cnt < rowCount() && ! user_exists; cnt++) + { + QModelIndex idx = index(cnt, ProfileModel::COL_NAME); + if (! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! idx.data(ProfileModel::DATA_IS_DEFAULT).toBool()) + user_exists = true; + } + + return user_exists; +} + +int ProfileModel::rowCount(const QModelIndex &) const +{ + return static_cast(profiles_.count()); +} + +int ProfileModel::columnCount(const QModelIndex &) const +{ + return static_cast(_LAST_ENTRY); +} + +profile_def * ProfileModel::guard(const QModelIndex &index) const +{ + if (! index.isValid()) + return Q_NULLPTR; + + return guard(index.row()); +} + +profile_def * ProfileModel::guard(int row) const +{ + if (row < 0 || profiles_.count() <= row) + return Q_NULLPTR; + + if (! edited_profile_list()) + { + static_cast>(profiles_).clear(); + return Q_NULLPTR; + } + + return profiles_.value(row, Q_NULLPTR); +} + +QVariant ProfileModel::dataDisplay(const QModelIndex &index) const +{ + profile_def * prof = guard(index); + if (! prof) + return QVariant(); + + switch (index.column()) + { + case COL_NAME: + return QString(prof->name); + case COL_TYPE: + if (prof->status == PROF_STAT_DEFAULT) + return tr("Default"); + else if (prof->is_global) + return tr("Global"); + else + return tr("Personal"); + default: + break; + } + + return QVariant(); +} + +QVariant ProfileModel::dataFontRole(const QModelIndex &index) const +{ + if (! index.isValid() || profiles_.count() <= index.row()) + return QVariant(); + + profile_def * prof = guard(index.row()); + if (! prof) + return QVariant(); + + QFont font; + + if (prof->is_global) + font.setItalic(true); + + if (! prof->is_global && ! checkDuplicate(index)) + { + if ((set_profile_.compare(prof->name) == 0 && prof->status == PROF_STAT_EXISTS) || + (set_profile_.compare(prof->reference) == 0 && prof->status == PROF_STAT_CHANGED) ) + font.setBold(true); + } + + if (prof->status == PROF_STAT_DEFAULT && reset_default_) + font.setStrikeOut(true); + + return font; +} + +bool ProfileModel::checkIfDeleted(int row) const +{ + QModelIndex idx = index(row, ProfileModel::COL_NAME); + return checkIfDeleted(idx); +} + +bool ProfileModel::checkIfDeleted(const QModelIndex &index) const +{ + profile_def * prof = guard(index); + if (! prof) + return false; + + QStringList deletedNames; + + GList * current = current_profile_list(); + + /* search the current list as long as we have not found anything */ + while (current) + { + bool found = false; + GList * edited = edited_profile_list(); + profile_def * profcurr = static_cast(current->data); + + if (! profcurr->is_global && profcurr->status != PROF_STAT_DEFAULT) + { + while (edited && ! found) + { + profile_def * profed = static_cast(edited->data); + if (! profed->is_global && profed->status != PROF_STAT_DEFAULT) + { + if (g_strcmp0(profcurr->name, profed->name) == 0 || g_strcmp0(profcurr->name, profed->reference) == 0) + { + if (profed->status == profcurr->status && prof->status != PROF_STAT_NEW && prof->status != PROF_STAT_COPY) + found = true; + } + } + + edited = gxx_list_next(edited); + } + + /* profile has been deleted, check if it has the name we ask for */ + if (! found) + deletedNames << profcurr->name; + } + + if (profcurr->is_global && deletedNames.contains(profcurr->name)) + deletedNames.removeAll(profcurr->name); + + current = gxx_list_next(current); + } + + if (deletedNames.contains(prof->name)) + return true; + + return false; +} + +bool ProfileModel::checkInvalid(const QModelIndex &index) const +{ + profile_def * prof = guard(index); + if (! prof) + return false; + + int ref = this->findAsReference(prof->name); + if (ref == index.row()) + return false; + + profile_def * pg = guard(ref); + if (pg && pg->status == PROF_STAT_CHANGED && g_strcmp0(pg->name, pg->reference) != 0 && ! prof->is_global) + return true; + + return false; +} + +bool ProfileModel::checkDuplicate(const QModelIndex &index, bool isOriginalToDuplicate) const +{ + profile_def * prof = guard(index); + if (! prof || (! isOriginalToDuplicate && prof->status == PROF_STAT_EXISTS) ) + return false; + + QList rows = this->findAllByNameAndVisibility(prof->name, prof->is_global, false); + int found = 0; + profile_def * check = Q_NULLPTR; + for (int cnt = 0; cnt < rows.count(); cnt++) + { + int row = rows.at(cnt); + + if (row == index.row()) + continue; + + check = guard(row); + if (! check || (isOriginalToDuplicate && check->status == PROF_STAT_EXISTS) ) + continue; + + found++; + } + + if (found > 0) + return true; + return false; +} + +QVariant ProfileModel::dataBackgroundRole(const QModelIndex &index) const +{ + if (! index.isValid() || profiles_.count() <= index.row()) + return QVariant(); + + profile_def * prof = guard(index.row()); + if (! prof) + return QVariant(); + + if (prof->status == PROF_STAT_DEFAULT && reset_default_) + return ColorUtils::fromColorT(&prefs.gui_text_deprecated); + + if (prof->status != PROF_STAT_DEFAULT && ! prof->is_global) + { + /* Highlights errorneous line */ + if (checkInvalid(index) || checkIfDeleted(index) || checkDuplicate(index) || ! checkNameValidity(prof->name)) + return ColorUtils::fromColorT(&prefs.gui_text_invalid); + + /* Highlights line, which has been duplicated by another index */ + if (checkDuplicate(index, true)) + return ColorUtils::fromColorT(&prefs.gui_text_valid); + } + + return QVariant(); +} + +QVariant ProfileModel::dataToolTipRole(const QModelIndex &idx) const +{ + if (! idx.isValid() || profiles_.count() <= idx.row()) + return QVariant(); + + profile_def * prof = guard(idx.row()); + if (! prof) + return QVariant(); + + if (prof->is_global) + return tr("This is a system provided profile"); + else + return dataPath(idx); +} + +QVariant ProfileModel::dataPath(const QModelIndex &index) const +{ + if (! index.isValid() || profiles_.count() <= index.row()) + return QVariant(); + + profile_def * prof = guard(index.row()); + if (! prof) + return QVariant(); + + if (checkInvalid(index)) + { + int ref = this->findAsReference(prof->name); + if (ref != index.row() && ref >= 0) + { + profile_def * prof = guard(ref); + QString msg = tr("A profile change for this name is pending"); + if (prof) + msg.append(tr(" (See: %1)").arg(prof->name)); + return msg; + } + + return tr("This is an invalid profile definition"); + } + + if ((prof->status == PROF_STAT_NEW || prof->status == PROF_STAT_CHANGED || prof->status == PROF_STAT_COPY) && checkDuplicate(index)) + return tr("A profile already exists with this name"); + + if (checkIfDeleted(index)) + { + return tr("A profile with this name is being deleted"); + } + + if (prof->is_import) + return tr("Imported profile"); + + switch (prof->status) + { + case PROF_STAT_DEFAULT: + if (!reset_default_) + return gchar_free_to_qstring(get_persconffile_path("", FALSE)); + else + return tr("Resetting to default"); + case PROF_STAT_EXISTS: + { + QString profile_path; + if (prof->is_global) { + profile_path = gchar_free_to_qstring(get_global_profiles_dir()); + } else { + profile_path = gchar_free_to_qstring(get_profiles_dir()); + } + profile_path.append("/").append(prof->name); + return QDir::toNativeSeparators(profile_path); + } + case PROF_STAT_NEW: + { + QString errMsg; + + if (! checkNameValidity(prof->name, &errMsg)) + return errMsg; + else + return tr("Created from default settings"); + } + case PROF_STAT_CHANGED: + { + QString msg; + if (! ProfileModel::checkNameValidity(QString(prof->name), &msg)) + return msg; + + if (prof->reference) + return tr("Renamed from: %1").arg(prof->reference); + + return QVariant(); + } + case PROF_STAT_COPY: + { + QString msg; + + /* this should always be the case, but just as a precaution it is checked */ + if (prof->reference) + { + msg = tr("Copied from: %1").arg(prof->reference); + QString appendix; + + /* A global profile is neither deleted or removed, only system provided is allowed as appendix */ + if (profile_exists(prof->reference, TRUE) && prof->from_global) + appendix = tr("system provided"); + /* A default model as reference can neither be deleted or renamed, so skip if the reference was one */ + else if (! index.data(ProfileModel::DATA_IS_DEFAULT).toBool()) + { + /* find a non-global, non-default profile which could be referenced by this one. Those are the only + * ones which could be renamed or deleted */ + int row = this->findByNameAndVisibility(prof->reference, false, true); + profile_def * ref = guard(row); + + /* The reference is itself a copy of the original, therefore it is not accepted */ + if (ref && (ref->status == PROF_STAT_COPY || ref->status == PROF_STAT_NEW) && QString(ref->name).compare(prof->reference) != 0) + ref = Q_NULLPTR; + + /* found no other profile, original one had to be deleted */ + if (! ref || row == index.row() || checkIfDeleted(row)) + { + appendix = tr("deleted"); + } + /* found another profile, so the reference had been renamed, it the status is changed */ + else if (ref && ref->status == PROF_STAT_CHANGED) + { + appendix = tr("renamed to %1").arg(ref->name); + } + } + + if (appendix.length() > 0) + msg.append(QString(" (%1)").arg(appendix)); + } + + return msg; + } + } + + return QVariant(); +} + +QVariant ProfileModel::data(const QModelIndex &index, int role) const +{ + profile_def * prof = guard(index); + if (! prof) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + return dataDisplay(index); + case Qt::FontRole: + return dataFontRole(index); + case Qt::BackgroundRole: + return dataBackgroundRole(index); + case Qt::ToolTipRole: + return dataToolTipRole(index); + case ProfileModel::DATA_STATUS: + return QVariant::fromValue(prof->status); + case ProfileModel::DATA_IS_DEFAULT: + return QVariant::fromValue(prof->status == PROF_STAT_DEFAULT); + case ProfileModel::DATA_IS_GLOBAL: + return QVariant::fromValue(prof->is_global); + case ProfileModel::DATA_IS_SELECTED: + { + QModelIndex selected = activeProfile(); + profile_def * selprof = guard(selected); + if (selprof) + { + if (selprof && selprof->is_global != prof->is_global) + return QVariant::fromValue(false); + + if (selprof && strcmp(selprof->name, prof->name) == 0) + return QVariant::fromValue(true); + } + return QVariant::fromValue(false); + } + case ProfileModel::DATA_PATH: + return dataPath(index); + case ProfileModel::DATA_INDEX_VALUE_IS_URL: + if (index.column() <= ProfileModel::COL_TYPE) + return QVariant::fromValue(false); + return QVariant::fromValue(true); + case ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION: + if (prof->status == PROF_STAT_NEW || prof->status == PROF_STAT_COPY + || (prof->status == PROF_STAT_DEFAULT && reset_default_) + || prof->status == PROF_STAT_CHANGED || prof->is_import) + return QVariant::fromValue(false); + else + return QVariant::fromValue(true); + + default: + break; + } + + return QVariant(); +} + +QVariant ProfileModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch (section) + { + case COL_NAME: + return tr("Profile"); + case COL_TYPE: + return tr("Type"); + default: + break; + } + } + + return QVariant(); +} + +Qt::ItemFlags ProfileModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags fl = QAbstractTableModel::flags(index); + + profile_def * prof = guard(index); + if (! prof) + return fl; + + if (index.column() == ProfileModel::COL_NAME && prof->status != PROF_STAT_DEFAULT && ! prof->is_global) + fl |= Qt::ItemIsEditable; + + return fl; +} + +int ProfileModel::findByName(QString name) +{ + int row = findByNameAndVisibility(name, false); + if (row < 0) + row = findByNameAndVisibility(name, true); + + return row; +} + +int ProfileModel::findAsReference(QString reference) const +{ + int found = -1; + if (reference.length() <= 0) + return found; + + for (int cnt = 0; cnt < profiles_.count() && found < 0; cnt++) + { + profile_def * prof = guard(cnt); + if (prof && reference.compare(prof->reference) == 0) + found = cnt; + } + + return found; +} + +int ProfileModel::findByNameAndVisibility(QString name, bool isGlobal, bool searchReference) const +{ + QList result = findAllByNameAndVisibility(name, isGlobal, searchReference); + return result.count() == 0 ? -1 : result.at(0); +} + +QList ProfileModel::findAllByNameAndVisibility(QString name, bool isGlobal, bool searchReference) const +{ + QList result; + + for (int cnt = 0; cnt < profiles_.count(); cnt++) + { + profile_def * prof = guard(cnt); + if (prof && static_cast(prof->is_global) == isGlobal) + { + if (name.compare(prof->name) == 0 || (searchReference && name.compare(prof->reference) == 0) ) + result << cnt; + } + } + + return result; + +} + +QModelIndex ProfileModel::addNewProfile(QString name) +{ + int cnt = 1; + QString newName = name; + while (findByNameAndVisibility(newName) >= 0) + { + newName = QString("%1 %2").arg(name).arg(QString::number(cnt)); + cnt++; + } + + add_to_profile_list(newName.toUtf8().constData(), newName.toUtf8().constData(), PROF_STAT_NEW, FALSE, FALSE, FALSE); + loadProfiles(); + + return index(findByName(newName), COL_NAME); +} + +QModelIndex ProfileModel::duplicateEntry(QModelIndex idx, int new_status) +{ + profile_def * prof = guard(idx); + if (! prof) + return QModelIndex(); + + /* only new and copied stati can be set */ + if (new_status != PROF_STAT_NEW && new_status != PROF_STAT_COPY) + new_status = PROF_STAT_COPY; + + /* this is a copy from a personal profile, check if the original has been a + * new profile or a preexisting one. In the case of a new profile, restart + * with the state PROF_STAT_NEW */ + if (prof->status == PROF_STAT_COPY && ! prof->from_global) + { + int row = findByNameAndVisibility(prof->reference, false); + profile_def * copyParent = guard(row); + if (copyParent && copyParent->status == PROF_STAT_NEW) + return duplicateEntry(index(row, ProfileModel::COL_NAME), PROF_STAT_NEW); + } + + /* Rules for figuring out the name to copy from: + * + * General, use copy name + * If status of copy is new or changed => use copy reference + * If copy is non global and status of copy is != changed, use original parent name + */ + QString parent = prof->name; + if (prof->status == PROF_STAT_CHANGED) + parent = prof->reference; + else if (! prof->is_global && prof->status != PROF_STAT_NEW && prof->status != PROF_STAT_CHANGED) + parent = get_profile_parent (prof->name); + + if (parent.length() == 0) + return QModelIndex(); + + /* parent references the parent profile to be used, parentName is the base for the new name */ + QString parentName = parent; + /* the user has changed the profile name, therefore this is also the name to be used */ + if (prof->status != PROF_STAT_EXISTS) + parentName = prof->name; + + /* check to ensure we do not end up with (copy) (copy) (copy) ... */ + QRegularExpression rx("\\s+(\\(\\s*" + tr("copy", "noun") + "\\s*\\d*\\))"); + parentName.replace(rx, ""); + + QString new_name; + /* if copy is global and name has not been used before, use that, else create first copy */ + if (prof->is_global && findByNameAndVisibility(parentName) < 0) + new_name = QString(prof->name); + else + new_name = QString("%1 (%2)").arg(parentName).arg(tr("copy", "noun")); + + /* check if copy already exists and iterate, until an unused version is found */ + int cnt = 1; + while (findByNameAndVisibility(new_name) >= 0) + { + new_name = QString("%1 (%2 %3)").arg(parentName).arg(tr("copy", "noun")).arg(QString::number(cnt)); + cnt++; + } + + /* if this would be a copy, but the original is already a new one, this is a copy as well */ + if (new_status == PROF_STAT_COPY && prof->status == PROF_STAT_NEW) + new_status = PROF_STAT_NEW; + + /* add element */ + add_to_profile_list(new_name.toUtf8().constData(), parent.toUtf8().constData(), new_status, FALSE, prof->from_global ? prof->from_global : prof->is_global, FALSE); + + /* reload profile list in model */ + loadProfiles(); + + int row = findByNameAndVisibility(new_name, false); + /* sanity check, if adding the profile went correctly */ + if (row < 0 || row == idx.row()) + return QModelIndex(); + + /* return the index of the profile */ + return index(row, COL_NAME); +} + +void ProfileModel::deleteEntry(QModelIndex idx) +{ + if (! idx.isValid()) + return; + + QModelIndexList temp; + temp << idx; + deleteEntries(temp); +} + +void ProfileModel::deleteEntries(QModelIndexList idcs) +{ + bool changes = false; + + QList indeces; + foreach (QModelIndex idx, idcs) + { + if (! indeces.contains(idx.row()) && ! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool()) + indeces << idx.row(); + } + /* Security blanket. This ensures, that we start deleting from the end and do not get any issues iterating the list */ + std::sort(indeces.begin(), indeces.end(), std::less()); + + foreach (int row, indeces) + { + profile_def * prof = guard(row); + if (! prof) + continue; + + if (prof->is_global) + continue; + + if (prof->status == PROF_STAT_DEFAULT) + { + reset_default_ = ! reset_default_; + } + else + { + GList * fl_entry = entry(prof); + if (fl_entry) + { + changes = true; + remove_from_profile_list(fl_entry); + } + } + } + + if (changes) + loadProfiles(); + + if (reset_default_) + { + emit layoutAboutToBeChanged(); + emit dataChanged(index(0, 0), index(rowCount(), columnCount())); + emit layoutChanged(); + } +} + +bool ProfileModel::resetDefault() const +{ + return reset_default_; +} + +void ProfileModel::doResetModel(bool reset_import) +{ + reset_default_ = false; + if (reset_import) + profiles_imported_ = false; + + loadProfiles(); +} + +QModelIndex ProfileModel::activeProfile() const +{ + QList rows = this->findAllByNameAndVisibility(set_profile_, false, true); + foreach (int row, rows) + { + profile_def * prof = profiles_.at(row); + if (prof->is_global || checkDuplicate(index(row, ProfileModel::COL_NAME)) ) + return QModelIndex(); + + if ((set_profile_.compare(prof->name) == 0 && (prof->status == PROF_STAT_EXISTS || prof->status == PROF_STAT_DEFAULT) ) || + (set_profile_.compare(prof->reference) == 0 && prof->status == PROF_STAT_CHANGED) ) + return index(row, ProfileModel::COL_NAME); + } + + return QModelIndex(); +} + +bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int role) +{ + last_set_row_ = -1; + + if (role != Qt::EditRole || ! value.isValid() || value.toString().isEmpty()) + return false; + + QString newValue = value.toString(); + profile_def * prof = guard(idx); + if (! prof || prof->status == PROF_STAT_DEFAULT) + return false; + + last_set_row_ = idx.row(); + + QString current(prof->name); + if (current.compare(newValue) != 0) + { + g_free(prof->name); + prof->name = qstring_strdup(newValue); + + if (prof->reference && g_strcmp0(prof->name, prof->reference) == 0 && ! (prof->status == PROF_STAT_NEW || prof->status == PROF_STAT_COPY)) { + prof->status = PROF_STAT_EXISTS; + } else if (prof->status == PROF_STAT_EXISTS) { + prof->status = PROF_STAT_CHANGED; + } + emit itemChanged(idx); + } + + loadProfiles(); + + return true; +} + +int ProfileModel::lastSetRow() const +{ + return last_set_row_; +} + +bool ProfileModel::copyTempToProfile(QString tempPath, QString profilePath, bool * wasEmpty) +{ + bool was_empty = true; + + QDir profileDir(profilePath); + if (! profileDir.mkpath(profilePath) || ! QFile::exists(tempPath)) + return false; + + QDir tempProfile(tempPath); + tempProfile.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks); + QFileInfoList files = tempProfile.entryInfoList(); + if (files.count() <= 0) + return false; + + int created = 0; + foreach (QFileInfo finfo, files) + { + QString tempFile = finfo.absoluteFilePath(); + QString profileFile = profilePath + "/" + finfo.fileName(); + + if (! profile_files_.contains(finfo.fileName())) + { + was_empty = false; + continue; + } + + if (! QFile::exists(tempFile) || QFile::exists(profileFile)) + continue; + + if (QFile::copy(tempFile, profileFile)) + created++; + } + + if (wasEmpty) + *wasEmpty = was_empty; + + if (created > 0) + return true; + + return false; +} + +QFileInfoList ProfileModel::uniquePaths(QFileInfoList lst) +{ + QStringList files; + QFileInfoList newLst; + + foreach (QFileInfo entry, lst) + { + if (! files.contains(entry.absoluteFilePath())) + { + if (entry.exists() && entry.isDir()) + { + newLst << QFileInfo(entry.absoluteFilePath()); + files << entry.absoluteFilePath(); + } + } + } + + return newLst; +} + +QFileInfoList ProfileModel::filterProfilePath(QString path, QFileInfoList ent, bool fromZip) +{ + QFileInfoList result = ent; + QDir temp(path); + temp.setSorting(QDir::Name); + temp.setFilter(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); + QFileInfoList entries = temp.entryInfoList(); + if (! fromZip) + entries << QFileInfo(path); + foreach (QFileInfo entry, entries) + { + QDir fPath(entry.absoluteFilePath()); + fPath.setSorting(QDir::Name); + fPath.setFilter(QDir::Files | QDir::NoSymLinks); + QFileInfoList fEntries = fPath.entryInfoList(); + bool found = false; + for (int cnt = 0; cnt < fEntries.count() && ! found; cnt++) + { + if (config_file_exists_with_entries(fEntries[cnt].absoluteFilePath().toUtf8().constData(), '#')) + found = true; + } + + if (found) + { + result.append(entry); + } + else + { + if (path.compare(entry.absoluteFilePath()) != 0) + result.append(filterProfilePath(entry.absoluteFilePath(), result, fromZip)); + } + } + + return result; +} + +#ifdef HAVE_MINIZIP +QStringList ProfileModel::exportFileList(QModelIndexList items) +{ + QStringList result; + + foreach(QModelIndex idx, items) + { + profile_def * prof = guard(idx); + if (! prof || prof->is_global || QString(prof->name).compare(DEFAULT_PROFILE) == 0) + continue; + + if (! idx.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool()) + continue; + + QString path = idx.data(ProfileModel::DATA_PATH).toString(); + QDir temp(path); + temp.setSorting(QDir::Name); + temp.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot); + QFileInfoList entries = temp.entryInfoList(); + foreach (QFileInfo fi, entries) + result << fi.absoluteFilePath(); + } + + return result; +} + +bool ProfileModel::exportProfiles(QString filename, QModelIndexList items, QString *err) +{ + if (changesPending()) + { + if (err) + err->append(tr("Exporting profiles while changes are pending is not allowed")); + return false; + } + + /* Write recent file for current profile before exporting */ + write_profile_recent(); + + QStringList files = exportFileList(items); + if (files.count() == 0) + { + if (err) + err->append((tr("No profiles found to export"))); + return false; + } + + if (WiresharkZipHelper::zip(filename, files, gchar_free_to_qstring(get_profiles_dir()) + "/") ) + return true; + + return false; +} + +/* This check runs BEFORE the file has been unzipped! */ +bool ProfileModel::acceptFile(QString fileName, int fileSize) +{ + if (fileName.toLower().endsWith(".zip")) + return false; + + /* Arbitrary maximum config file size accepted: 256MB */ + if (fileSize > 1024 * 1024 * 256) + return false; + + return true; +} + +QString ProfileModel::cleanName(QString fileName) +{ + QStringList parts = fileName.split("/"); + QString temp = parts[parts.count() - 1].replace(QRegularExpression("[" + QRegularExpression::escape(illegalCharacters()) + "]"), QString("_") ); + temp = parts.join("/"); + return temp; +} + +int ProfileModel::importProfilesFromZip(QString filename, int * skippedCnt, QStringList *result) +{ + QTemporaryDir dir; +#if 0 + dir.setAutoRemove(false); + g_printerr("Temp dir for unzip: %s\n", dir.path().toUtf8().constData()); +#endif + + int cnt = 0; + if (dir.isValid()) + { + WiresharkZipHelper::unzip(filename, dir.path(), &ProfileModel::acceptFile, &ProfileModel::cleanName); + cnt = importProfilesFromDir(dir.path(), skippedCnt, true, result); + } + + return cnt; +} +#endif + +int ProfileModel::importProfilesFromDir(QString dirname, int * skippedCnt, bool fromZip, QStringList *result) +{ + int count = 0; + int skipped = 0; + QDir profileDir(gchar_free_to_qstring(get_profiles_dir())); + QDir dir(dirname); + + if (skippedCnt) + *skippedCnt = 0; + + if (dir.exists()) + { + QFileInfoList entries = uniquePaths(filterProfilePath(dirname, QFileInfoList(), fromZip)); + + foreach (QFileInfo fentry, entries) + { + if (fentry.fileName().length() <= 0) + continue; + + bool wasEmpty = true; + bool success = false; + + QString profilePath = profileDir.absolutePath() + "/" + fentry.fileName(); + QString tempPath = fentry.absoluteFilePath(); + + if (fentry.fileName().compare(DEFAULT_PROFILE, Qt::CaseInsensitive) == 0 || QFile::exists(profilePath)) + { + skipped++; + continue; + } + + if (result) + *result << fentry.fileName(); + + success = copyTempToProfile(tempPath, profilePath, &wasEmpty); + if (success) + { + count++; + add_to_profile_list(fentry.fileName().toUtf8().constData(), fentry.fileName().toUtf8().constData(), PROF_STAT_NEW, FALSE, FALSE, TRUE); + } + else if (! wasEmpty && QFile::exists(profilePath)) + { + QDir dh(profilePath); + dh.rmdir(profilePath); + } + } + + } + + if (count > 0) + { + profiles_imported_ = true; + loadProfiles(); + } + + if (skippedCnt) + *skippedCnt = skipped; + + return count; +} + +void ProfileModel::markAsImported(QStringList importedItems) +{ + if (importedItems.count() <= 0) + return; + + profiles_imported_ = true; + + foreach (QString item, importedItems) + { + int row = findByNameAndVisibility(item, false); + profile_def * prof = guard(row); + if (! prof) + continue; + + prof->is_import = true; + } +} + +bool ProfileModel::clearImported(QString *msg) +{ + QList rows; + bool result = true; + for (int cnt = 0; cnt < rowCount(); cnt++) + { + profile_def * prof = guard(cnt); + if (prof && prof->is_import && ! rows.contains(cnt)) + rows << cnt; + } + /* Security blanket. This ensures, that we start deleting from the end and do not get any issues iterating the list */ + std::sort(rows.begin(), rows.end(), std::less()); + + char * ret_path = Q_NULLPTR; + for (int cnt = 0; cnt < rows.count() && result; cnt++) + { + int row = rows.at(cnt); + if (delete_persconffile_profile (index(row, ProfileModel::COL_NAME).data().toString().toUtf8().constData(), &ret_path) != 0) + { + if (msg) + { + QString errmsg = QString("%1\n\"%2\":\n%3").arg(tr("Can't delete profile directory")).arg(ret_path).arg(g_strerror(errno)); + msg->append(errmsg); + } + + result = false; + } + } + + return result; +} + +QString ProfileModel::illegalCharacters() +{ +#ifdef _WIN32 + /* According to https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions */ + return QString("<>:\"/\\|?*"); +#else + return QDir::separator(); +#endif + +} + +bool ProfileModel::checkNameValidity(QString name, QString *msg) +{ + QString message; + bool invalid = false; + QString msgChars; + + QString invalid_dir_chars = illegalCharacters(); + + for (int cnt = 0; cnt < invalid_dir_chars.length() && ! invalid; cnt++) + { + msgChars += invalid_dir_chars[cnt]; + msgChars += ' '; + if (name.contains(invalid_dir_chars[cnt])) + invalid = true; + } +#ifdef _WIN32 + if (invalid) + { + message = tr("A profile name cannot contain the following characters: %1").arg(msgChars); + } + + if (message.isEmpty() && (name.startsWith('.') || name.endsWith('.')) ) + message = tr("A profile cannot start or end with a period (.)"); +#else + if (invalid) + message = tr("A profile name cannot contain the '/' character"); +#endif + + if (! message.isEmpty()) { + if (msg) + msg->append(message); + return false; + } + + return true; +} + +QString ProfileModel::activeProfileName() +{ + ProfileModel model; + QModelIndex idx = model.activeProfile(); + return idx.data(ProfileModel::COL_NAME).toString(); +} + +QString ProfileModel::activeProfilePath() +{ + ProfileModel model; + QModelIndex idx = model.activeProfile(); + return idx.data(ProfileModel::DATA_PATH).toString(); +} diff --git a/ui/qt/models/profile_model.h b/ui/qt/models/profile_model.h new file mode 100644 index 00000000..16febd70 --- /dev/null +++ b/ui/qt/models/profile_model.h @@ -0,0 +1,165 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PROFILE_MODEL_H +#define PROFILE_MODEL_H + +#include "config.h" +#include "glib.h" + +#include + +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(profileLogger) + +class ProfileSortModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + ProfileSortModel(QObject *parent = Q_NULLPTR); + + enum FilterType { + AllProfiles = 0, + PersonalProfiles, + GlobalProfiles + }; + + void setFilterType(FilterType ft); + void setFilterString(QString txt = QString()); + + static QStringList filterTypes(); + +protected: + virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + +private: + FilterType ft_; + QString ftext_; +}; + +class ProfileModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit ProfileModel(QObject * parent = Q_NULLPTR); + + enum { + COL_NAME, + COL_TYPE, + _LAST_ENTRY + } columns_; + + enum { + DATA_STATUS = Qt::UserRole, + DATA_IS_DEFAULT, + DATA_IS_GLOBAL, + DATA_IS_SELECTED, + DATA_PATH, + DATA_PATH_IS_NOT_DESCRIPTION, + DATA_INDEX_VALUE_IS_URL + } data_values_; + + // QAbstractItemModel interface + virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex & parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex & idx, int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role); + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + void deleteEntry(QModelIndex idx); + void deleteEntries(QModelIndexList idcs); + + int findByName(QString name); + QModelIndex addNewProfile(QString name); + QModelIndex duplicateEntry(QModelIndex idx, int new_status = PROF_STAT_COPY); + + void doResetModel(bool reset_import = false); + bool resetDefault() const; + + QModelIndex activeProfile() const; + static QString activeProfileName(); + static QString activeProfilePath(); + + GList * at(int row) const; + + bool changesPending() const; + bool importPending() const; + + bool userProfilesExist() const; + +#ifdef HAVE_MINIZIP + bool exportProfiles(QString filename, QModelIndexList items, QString * err = Q_NULLPTR); + int importProfilesFromZip(QString filename, int *skippedCnt = Q_NULLPTR, QStringList *result = Q_NULLPTR); +#endif + int importProfilesFromDir(QString filename, int *skippedCnt = Q_NULLPTR, bool fromZip = false, QStringList *result = Q_NULLPTR); + + static bool checkNameValidity(QString name, QString *msg = Q_NULLPTR); + QList findAllByNameAndVisibility(QString name, bool isGlobal = false, bool searchReference = false) const; + void markAsImported(QStringList importedItems); + bool clearImported(QString *msg = Q_NULLPTR); + + int lastSetRow() const; + + bool checkInvalid(const QModelIndex &index) const; + bool checkIfDeleted(const QModelIndex &index) const; + bool checkIfDeleted(int row) const; + bool checkDuplicate(const QModelIndex &index, bool isOriginalToDuplicate = false) const; + +signals: + void itemChanged(const QModelIndex &idx); + +protected: + static QString illegalCharacters(); + +private: + QList profiles_; + QStringList profile_files_; + QString set_profile_; + bool reset_default_; + bool profiles_imported_; + + int last_set_row_; + + void loadProfiles(); + profile_def * guard(const QModelIndex &index) const; + profile_def * guard(int row) const; + GList * entry(profile_def *) const; + + int findByNameAndVisibility(QString name, bool isGlobal = false, bool searchReference = false) const; + int findAsReference(QString reference) const; + +#ifdef HAVE_MINIZIP + static bool acceptFile(QString fileName, int fileSize); + static QString cleanName(QString fileName); +#endif + + QVariant dataDisplay(const QModelIndex & idx) const; + QVariant dataFontRole(const QModelIndex & idx) const; + QVariant dataBackgroundRole(const QModelIndex & idx) const; + QVariant dataToolTipRole(const QModelIndex & idx) const; + QVariant dataPath(const QModelIndex & idx) const; + +#ifdef HAVE_MINIZIP + QStringList exportFileList(QModelIndexList items); +#endif + bool copyTempToProfile(QString tempPath, QString profilePath, bool *wasEmpty = Q_NULLPTR); + QFileInfoList filterProfilePath(QString, QFileInfoList ent, bool fromZip); + QFileInfoList uniquePaths(QFileInfoList lst); + +}; + +#endif diff --git a/ui/qt/models/proto_tree_model.cpp b/ui/qt/models/proto_tree_model.cpp new file mode 100644 index 00000000..3ededffd --- /dev/null +++ b/ui/qt/models/proto_tree_model.cpp @@ -0,0 +1,252 @@ +/* proto_tree_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include + +#include + +#include +#include +#include + +// To do: +// - Add ProtoTreeDelegate +// - Add ProtoTreeModel to CaptureFile + +ProtoTreeModel::ProtoTreeModel(QObject * parent) : + QAbstractItemModel(parent) +{ + root_node_ = new ProtoNode(NULL); +} + +ProtoTreeModel::~ProtoTreeModel() +{ + delete root_node_; +} + +Qt::ItemFlags ProtoTreeModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags item_flags = QAbstractItemModel::flags(index); + if (rowCount(index) < 1) { + item_flags |= Qt::ItemNeverHasChildren; + } + + return item_flags; +} + +QModelIndex ProtoTreeModel::index(int row, int, const QModelIndex &parent) const +{ + ProtoNode *parent_node = root_node_; + + if (parent.isValid()) { + // index is not a top level item. + parent_node = protoNodeFromIndex(parent); + } + + if (! parent_node->isValid()) + return QModelIndex(); + + ProtoNode *child = parent_node->child(row); + if (! child) { + return QModelIndex(); + } + + return createIndex(row, 0, static_cast(child)); +} + +QModelIndex ProtoTreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + ProtoNode *parent_node = protoNodeFromIndex(index)->parentNode(); + return indexFromProtoNode(parent_node); +} + +int ProtoTreeModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return protoNodeFromIndex(parent)->childrenCount(); + } + return root_node_->childrenCount(); +} + +// The QItemDelegate documentation says +// "When displaying items from a custom model in a standard view, it is +// often sufficient to simply ensure that the model returns appropriate +// data for each of the roles that determine the appearance of items in +// views." +// We might want to move this to a delegate regardless. +QVariant ProtoTreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + ProtoNode *index_node = protoNodeFromIndex(index); + FieldInformation finfo(index_node); + if (!finfo.isValid()) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return index_node->labelText(); + case Qt::BackgroundRole: + { + switch(finfo.flag(PI_SEVERITY_MASK)) { + case(0): + break; + case(PI_COMMENT): + return ColorUtils::expert_color_comment; + case(PI_CHAT): + return ColorUtils::expert_color_chat; + case(PI_NOTE): + return ColorUtils::expert_color_note; + case(PI_WARN): + return ColorUtils::expert_color_warn; + case(PI_ERROR): + return ColorUtils::expert_color_error; + default: + ws_warning("Unhandled severity flag: %u", finfo.flag(PI_SEVERITY_MASK)); + } + if (finfo.headerInfo().type == FT_PROTOCOL) { + return QApplication::palette().window(); + } + return QApplication::palette().base(); + } + case Qt::ForegroundRole: + { + if (finfo.flag(PI_SEVERITY_MASK)) { + return ColorUtils::expert_color_foreground; + } + if (finfo.isLink()) { + return ColorUtils::themeLinkBrush(); + } + if (finfo.headerInfo().type == FT_PROTOCOL) { + return QApplication::palette().windowText(); + } + return QApplication::palette().text(); + } + case Qt::FontRole: + if (finfo.isLink()) { + QFont font; + font.setUnderline(true); + return font; + } + default: + break; + } + + return QVariant(); +} + +void ProtoTreeModel::setRootNode(proto_node *root_node) +{ + beginResetModel(); + delete root_node_; + root_node_ = new ProtoNode(root_node); + endResetModel(); + if (!root_node) return; + + int row_count = root_node_->childrenCount(); + if (row_count < 1) return; + beginInsertRows(QModelIndex(), 0, row_count - 1); + endInsertRows(); +} + +ProtoNode* ProtoTreeModel::protoNodeFromIndex(const QModelIndex &index) const +{ + return static_cast(index.internalPointer()); +} + +QModelIndex ProtoTreeModel::indexFromProtoNode(ProtoNode *index_node) const +{ + if (!index_node) { + return QModelIndex(); + } + + int row = index_node->row(); + + if (!index_node->isValid() || row < 0) { + return QModelIndex(); + } + + return createIndex(row, 0, static_cast(index_node)); +} + +struct find_hfid_ { + int hfid; + ProtoNode *node; +}; + +bool ProtoTreeModel::foreachFindHfid(ProtoNode *node, gpointer find_hfid_ptr) +{ + struct find_hfid_ *find_hfid = (struct find_hfid_ *) find_hfid_ptr; + if (PNODE_FINFO(node->protoNode()) && PNODE_FINFO(node->protoNode())->hfinfo->id == find_hfid->hfid) { + find_hfid->node = node; + return true; + } + for (int i = 0; i < node->childrenCount(); i++) { + if (foreachFindHfid(node->child(i), find_hfid)) { + return true; + } + } + return false; +} + +QModelIndex ProtoTreeModel::findFirstHfid(int hf_id) +{ + if (!root_node_ || hf_id < 0) return QModelIndex(); + + struct find_hfid_ find_hfid; + find_hfid.hfid = hf_id; + + if (foreachFindHfid(root_node_, &find_hfid) && find_hfid.node->isValid()) { + return indexFromProtoNode(find_hfid.node); + } + return QModelIndex(); +} + +struct find_field_info_ { + field_info *fi; + ProtoNode *node; +}; + +bool ProtoTreeModel::foreachFindField(ProtoNode *node, gpointer find_finfo_ptr) +{ + struct find_field_info_ *find_finfo = (struct find_field_info_ *) find_finfo_ptr; + if (PNODE_FINFO(node->protoNode()) == find_finfo->fi) { + find_finfo->node = node; + return true; + } + for (int i = 0; i < node->childrenCount(); i++) { + if (foreachFindField(node->child(i), find_finfo)) { + return true; + } + } + return false; +} + +QModelIndex ProtoTreeModel::findFieldInformation(FieldInformation *finfo) +{ + if (!root_node_ || !finfo) return QModelIndex(); + field_info * fi = finfo->fieldInfo(); + if (!fi) return QModelIndex(); + + struct find_field_info_ find_finfo; + find_finfo.fi = fi; + + if (foreachFindField(root_node_, &find_finfo) && find_finfo.node->isValid()) { + return indexFromProtoNode(find_finfo.node); + } + return QModelIndex(); +} diff --git a/ui/qt/models/proto_tree_model.h b/ui/qt/models/proto_tree_model.h new file mode 100644 index 00000000..df7cbba8 --- /dev/null +++ b/ui/qt/models/proto_tree_model.h @@ -0,0 +1,48 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PROTO_TREE_MODEL_H +#define PROTO_TREE_MODEL_H + +#include +#include + +#include +#include + +class ProtoTreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit ProtoTreeModel(QObject * parent = 0); + ~ProtoTreeModel(); + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + QModelIndex index(int row, int, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &) const { return 1; } + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + // root_node can be NULL. + void setRootNode(proto_node *root_node); + ProtoNode* protoNodeFromIndex(const QModelIndex &index) const; + QModelIndex indexFromProtoNode(ProtoNode *index_node) const; + + QModelIndex findFirstHfid(int hf_id); + QModelIndex findFieldInformation(FieldInformation *finfo); + +private: + ProtoNode *root_node_; + static bool foreachFindHfid(ProtoNode *node, gpointer find_hfid_ptr); + static bool foreachFindField(ProtoNode *node, gpointer find_finfo_ptr); +}; + +#endif // PROTO_TREE_MODEL_H diff --git a/ui/qt/models/related_packet_delegate.cpp b/ui/qt/models/related_packet_delegate.cpp new file mode 100644 index 00000000..885160f4 --- /dev/null +++ b/ui/qt/models/related_packet_delegate.cpp @@ -0,0 +1,373 @@ +/* related_packet_delegate.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "packet_list_record.h" + +#include + +#include + +#include + +#include +#include + +typedef enum { + CT_NONE, // Not within the selected conversation. + CT_STARTING, // First packet in a conversation. + CT_CONTINUING, // Part of the selected conversation. + CT_BYPASSING, // *Not* part of the selected conversation. + CT_ENDING, // Last packet in a conversation. + CT_NUM_TYPES, +} ct_conversation_trace_type_t; + +// To do: +// - Add other frame types and symbols. If `tshark -G fields | grep FT_FRAMENUM` +// is any indication, we should add "reassembly" and "reassembly error" +// fields. +// - Don't add *too* many frame types and symbols. The goal is context, not +// clutter. +// - Add tooltips. It looks like this needs to be done in ::helpEvent +// or PacketListModel::data. +// - Add "Go -> Next Related" and "Go -> Previous Related"? +// - Apply as filter? + +RelatedPacketDelegate::RelatedPacketDelegate(QWidget *parent) : + QStyledItemDelegate(parent), + conv_(NULL), + current_frame_(0) +{ + clear(); +} + +void RelatedPacketDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + + /* This prevents the drawing of related objects, if multiple lines are being selected */ + if (mainApp && mainApp->mainWindow()) + { + MainWindow * mw = qobject_cast(mainApp->mainWindow()); + if (mw && mw->hasSelection()) + { + QStyledItemDelegate::paint(painter, option, index); + return; + } + } + + QStyleOptionViewItem option_vi = option; + QStyledItemDelegate::initStyleOption(&option_vi, index); + int em_w = option_vi.fontMetrics.height(); + int en_w = (em_w + 1) / 2; + int line_w = (option_vi.fontMetrics.lineWidth()); + + option_vi.features |= QStyleOptionViewItem::HasDecoration; + option_vi.decorationSize.setHeight(1); + option_vi.decorationSize.setWidth(em_w); + QStyledItemDelegate::paint(painter, option_vi, index); + + guint32 setup_frame = 0, last_frame = 0; + if (conv_) { + setup_frame = (int) conv_->setup_frame; + last_frame = (int) conv_->last_frame; + } + + const frame_data *fd; + PacketListRecord *record = static_cast(index.internalPointer()); + if (!record || (fd = record->frameData()) == NULL) { + return; + } + + ct_conversation_trace_type_t conversation_trace_type = CT_NONE; + ft_framenum_type_t related_frame_type = + related_frames_.contains(fd->num) ? related_frames_[fd->num] : FT_FRAMENUM_NUM_TYPES; + + if (setup_frame > 0 && last_frame > 0 && setup_frame != last_frame) { + if (fd->num == setup_frame) { + conversation_trace_type = CT_STARTING; + } else if (fd->num > setup_frame && fd->num < last_frame) { + conversation_trace_type = + conv_->conv_index == record->conversation() ? CT_CONTINUING : CT_BYPASSING; + } else if (fd->num == last_frame) { + conversation_trace_type = CT_ENDING; + } + } + + painter->save(); + + if (QApplication::style()->objectName().contains("vista")) { + // QWindowsVistaStyle::drawControl does this internally. Unfortunately there + // doesn't appear to be a more general way to do this. + option_vi.palette.setColor(QPalette::All, QPalette::HighlightedText, option_vi.palette.color(QPalette::Active, QPalette::Text)); + } + + QPalette::ColorGroup cg = option_vi.state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + QColor fg; + if (cg == QPalette::Normal && !(option_vi.state & QStyle::State_Active)) + cg = QPalette::Inactive; +#if !defined(Q_OS_WIN) + if (option_vi.state & QStyle::State_MouseOver) { + fg = QApplication::palette().text().color(); + } else +#endif + if (option_vi.state & QStyle::State_Selected) { + fg = option_vi.palette.color(cg, QPalette::HighlightedText); + } else { + fg = option_vi.palette.color(cg, QPalette::Text); + } + + fg = ColorUtils::alphaBlend(fg, option_vi.palette.color(cg, QPalette::Base), 0.5); + QPen line_pen(fg); + line_pen.setWidth(line_w); + line_pen.setJoinStyle(Qt::RoundJoin); + + painter->setPen(line_pen); + painter->translate(option_vi.rect.x(), option_vi.rect.y()); + painter->translate(en_w + 0.5, 0.5); + painter->setRenderHint(QPainter::Antialiasing, true); + int height = option_vi.rect.height(); + + // Uncomment to make the boundary visible. +// painter->save(); +// painter->setPen(Qt::darkRed); +// painter->drawRect(QRectF(0.5, 0.5, en_w - 1, height - 1)); +// painter->restore(); + + // The current decorations are based on what looked good and were easy + // to code. + + // It might be useful to have a JACKPOT_MODE define that shows each + // decoration in sequence in order to make it easier to create + // screenshots for the User's Guide. + + // Vertical line. Lower and upper half for the start and end of the + // conversation respectively, solid for conversation member, dashed + // for other packets in the start-end range. + switch (conversation_trace_type) { + case CT_STARTING: + { + QPoint start_line[] = { + QPoint(en_w - 1, height / 2), + QPoint(0, height / 2), + QPoint(0, height) + }; + painter->drawPolyline(start_line, 3); + break; + } + case CT_CONTINUING: + case CT_BYPASSING: + { + painter->save(); + if (conversation_trace_type == CT_BYPASSING) { + // Dashed line as we bypass packets not part of the conv. + QPen other_pen(line_pen); + other_pen.setStyle(Qt::DashLine); + painter->setPen(other_pen); + } + + // analysis overriding mark (three horizontal lines) + if(fd->tcp_snd_manual_analysis) { + int wbound = (en_w - 1) / 2; + + painter->drawLine(-wbound, 1, wbound, 1); + painter->drawLine(-wbound, height / 2, wbound, height / 2); + painter->drawLine(-wbound, height - 2, wbound, height - 2); + } + + painter->drawLine(0, 0, 0, height); + painter->restore(); + break; + } + case CT_ENDING: + { + QPoint end_line[] = { + QPoint(en_w - 1, height / 2), + QPoint(0, height / 2), + QPoint(0, 0) + }; + painter->drawPolyline(end_line, 3); + /* analysis overriding on the last packet of the conversation, + * we mark it with an additional horizontal line only. + * See issue 10725 for example. + */ + // analysis overriding mark (three horizontal lines) + if(fd->tcp_snd_manual_analysis) { + int wbound = (en_w - 1) / 2; + + painter->drawLine(-wbound, 1, wbound, 1); + painter->drawLine(-wbound, height / 2, wbound, height / 2); + } + + break; + } + default: + break; + } + + // Related packet indicator. Rightward arrow for requests, leftward + // arrow for responses, circle for others. + // XXX These are comically oversized when we have multi-line rows. + if (related_frame_type != FT_FRAMENUM_NUM_TYPES) { + painter->setBrush(fg); + switch (related_frame_type) { + // Request and response arrows are moved forward one pixel in order to + // maximize white space between the heads and the conversation line. + case FT_FRAMENUM_REQUEST: + { + int hh = height / 2; + QPoint tail(2 - en_w, hh); + QPoint head(en_w, hh); + drawArrow(painter, tail, head, hh / 2); + break; + } + case FT_FRAMENUM_RESPONSE: + { + int hh = height / 2; + QPoint tail(en_w - 1, hh); + QPoint head(1 - en_w, hh); + drawArrow(painter, tail, head, hh / 2); + break; + } + case FT_FRAMENUM_ACK: + { + QRect bbox (2 - en_w, height / 3, em_w - 2, height / 2); + drawCheckMark(painter, bbox); + break; + } + case FT_FRAMENUM_DUP_ACK: + { + QRect bbox (2 - en_w, (height / 3) - (line_w * 2), em_w - 2, height / 2); + drawCheckMark(painter, bbox); + bbox.moveTop(bbox.top() + (line_w * 3)); + drawCheckMark(painter, bbox); + break; + } + case FT_FRAMENUM_RETRANS_PREV: + { + int hh = height / 2; + QPoint tail(2 - en_w, hh); + QPoint head(en_w, hh); + drawChevrons(painter, tail, head, hh / 2); + break; + } + case FT_FRAMENUM_RETRANS_NEXT: + { + int hh = height / 2; + QPoint tail(en_w - 1, hh); + QPoint head(1 - en_w, hh); + drawChevrons(painter, tail, head, hh / 2); + break; + } + case FT_FRAMENUM_NONE: + default: + painter->drawEllipse(QPointF(0.0, option_vi.rect.height() / 2), 2, 2); + } + } + + painter->restore(); +} + +QSize RelatedPacketDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + /* This prevents the sizeHint for the delegate, if multiple lines are being selected */ + if (mainApp && mainApp->mainWindow()) + { + MainWindow * mw = qobject_cast(mainApp->mainWindow()); + if (mw && mw->selectedRows().count() > 1) + return QStyledItemDelegate::sizeHint(option, index); + } + + return QSize(option.fontMetrics.height() + QStyledItemDelegate::sizeHint(option, index).width(), + QStyledItemDelegate::sizeHint(option, index).height()); +} + +void RelatedPacketDelegate::drawArrow(QPainter *painter, const QPoint tail, const QPoint head, int head_size) const +{ + int x_mul = head.x() > tail.x() ? -1 : 1; + QPoint head_points[] = { + head, + QPoint(head.x() + (head_size * x_mul), head.y() + (head_size / 2)), + QPoint(head.x() + (head_size * x_mul), head.y() - (head_size / 2)), + }; + + painter->drawLine(tail.x(), tail.y(), head.x() + (head_size * x_mul), head.y()); + painter->drawPolygon(head_points, 3); +} + +void RelatedPacketDelegate::drawChevrons(QPainter *painter, const QPoint tail, const QPoint head, int head_size) const +{ + int x_mul = head.x() > tail.x() ? -1 : 1; + QPoint head_points1[] = { + head, + QPoint(head.x() + (head_size * x_mul), head.y() + (head_size / 2)), + QPoint(head.x() + (head_size * x_mul), head.y() - (head_size / 2)), + }; + QPoint head2(head.x() + (head_size * x_mul), head.y()); + QPoint head_points2[] = { + head2, + QPoint(head2.x() + (head_size * x_mul), head2.y() + (head_size / 2)), + QPoint(head2.x() + (head_size * x_mul), head2.y() - (head_size / 2)), + }; + + painter->drawPolygon(head_points1, 3); + painter->drawPolygon(head_points2, 3); +} + +void RelatedPacketDelegate::drawCheckMark(QPainter *painter, const QRect bbox) const +{ + QPoint cm_points[] = { + QPoint(bbox.x(), bbox.y() + (bbox.height() / 2)), + QPoint(bbox.x() + (bbox.width() / 4), bbox.y() + (bbox.height() * 3 / 4)), + bbox.topRight() + }; + painter->drawPolyline(cm_points, 3); +} + +void RelatedPacketDelegate::clear() +{ + related_frames_.clear(); + current_frame_ = 0; + conv_ = NULL; +} + +void RelatedPacketDelegate::setCurrentFrame(guint32 current_frame) + { + current_frame_ = current_frame; + foreach (ft_framenum_type_t framenum_type, related_frames_) { + addRelatedFrame(-1, framenum_type); /* No need to check if this element belongs to the hash... */ + } + } + +void RelatedPacketDelegate::addRelatedFrame(int frame_num, ft_framenum_type_t framenum_type) +{ + if (frame_num != -1 && !related_frames_.contains(frame_num)) + related_frames_[frame_num] = framenum_type; + + // Last match wins. Last match might not make sense, however. + if (current_frame_ > 0) { + switch (framenum_type) { + case FT_FRAMENUM_REQUEST: + related_frames_[current_frame_] = FT_FRAMENUM_RESPONSE; + break; + case FT_FRAMENUM_RESPONSE: + related_frames_[current_frame_] = FT_FRAMENUM_REQUEST; + break; + default: + break; + } + } +} + +void RelatedPacketDelegate::setConversation(conversation *conv) +{ + conv_ = conv; +} diff --git a/ui/qt/models/related_packet_delegate.h b/ui/qt/models/related_packet_delegate.h new file mode 100644 index 00000000..927129a0 --- /dev/null +++ b/ui/qt/models/related_packet_delegate.h @@ -0,0 +1,51 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RELATED_PACKET_DELEGATE_H +#define RELATED_PACKET_DELEGATE_H + +#include + +#include "epan/conversation.h" + +#include +#include + +class QPainter; +struct conversation; + +class RelatedPacketDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + RelatedPacketDelegate(QWidget *parent = 0); + void clear(); + void setCurrentFrame(guint32 current_frame); + void setConversation(struct conversation *conv); + +public slots: + void addRelatedFrame(int frame_num, ft_framenum_type_t framenum_type = FT_FRAMENUM_NONE); + +protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const; + +private: + QHash related_frames_; + struct conversation *conv_; + guint32 current_frame_; + + void drawArrow(QPainter *painter, const QPoint tail, const QPoint head, int head_size) const; + void drawChevrons(QPainter *painter, const QPoint tail, const QPoint head, int head_size) const; + void drawCheckMark(QPainter *painter, const QRect bbox) const; +}; + +#endif // RELATED_PACKET_DELEGATE_H diff --git a/ui/qt/models/resolved_addresses_models.cpp b/ui/qt/models/resolved_addresses_models.cpp new file mode 100644 index 00000000..48dd2f09 --- /dev/null +++ b/ui/qt/models/resolved_addresses_models.cpp @@ -0,0 +1,210 @@ +/* resolved_addresses_models.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include "file.h" + +#include "epan/addr_resolv.h" +#include + +extern "C" +{ + +static void +serv_port_hash_to_qstringlist(gpointer key, gpointer value, gpointer member_ptr) +{ + PortsModel *model = static_cast(member_ptr); + serv_port_t *serv_port = (serv_port_t *)value; + guint port = GPOINTER_TO_UINT(key); + + if (serv_port->tcp_name) { + QStringList entries; + + entries << serv_port->tcp_name; + entries << QString::number(port); + entries << "tcp"; + model->appendRow(entries); + } + if (serv_port->udp_name) { + QStringList entries; + + entries << serv_port->udp_name; + entries << QString::number(port); + entries << "udp"; + model->appendRow(entries); + } + if (serv_port->sctp_name) { + QStringList entries; + + entries << serv_port->sctp_name; + entries << QString::number(port); + entries << "sctp"; + model->appendRow(entries); + } + if (serv_port->dccp_name) { + QStringList entries; + + entries << serv_port->dccp_name; + entries << QString::number(port); + entries << "dccp"; + model->appendRow(entries); + } +} + +static void +ipv4_hash_table_resolved_to_list(gpointer, gpointer value, gpointer sl_ptr) +{ + QList *hosts = (QList *) sl_ptr; + hashipv4_t *ipv4_hash_table_entry = (hashipv4_t *) value; + + if ((ipv4_hash_table_entry->flags & NAME_RESOLVED)) { + *hosts << (QStringList() << QString(ipv4_hash_table_entry->ip) << QString(ipv4_hash_table_entry->name)); + } +} + +static void +ipv6_hash_table_resolved_to_list(gpointer, gpointer value, gpointer sl_ptr) +{ + QList *hosts = (QList *) sl_ptr; + hashipv6_t *ipv6_hash_table_entry = (hashipv6_t *) value; + + if ((ipv6_hash_table_entry->flags & NAME_RESOLVED)) { + *hosts << (QStringList() << QString(ipv6_hash_table_entry->ip6) << QString(ipv6_hash_table_entry->name)); + } +} + +static void +eth_hash_to_qstringlist(gpointer, gpointer value, gpointer sl_ptr) +{ + QList *values = (QList *) sl_ptr; + hashether_t* tp = (hashether_t*)value; + + *values << (QStringList() << QString(get_hash_ether_hexaddr(tp)) << QString(get_hash_ether_resolved_name(tp))); +} + +static void +manuf_hash_to_qstringlist(gpointer key, gpointer value, gpointer sl_ptr) +{ + QList *values = (QList *) sl_ptr; + hashmanuf_t *manuf = (hashmanuf_t*)value; + guint eth_as_guint = GPOINTER_TO_UINT(key); + + QString entry = QString("%1:%2:%3") + .arg((eth_as_guint >> 16 & 0xff), 2, 16, QChar('0')) + .arg((eth_as_guint >> 8 & 0xff), 2, 16, QChar('0')) + .arg((eth_as_guint & 0xff), 2, 16, QChar('0')); + + *values << (QStringList() << entry << QString(get_hash_manuf_resolved_name(manuf))); +} + +static void +wka_hash_to_qstringlist(gpointer key, gpointer value, gpointer sl_ptr) +{ + QList *values = (QList *) sl_ptr; + gchar *name = (gchar *)value; + guint8 *eth_addr = (guint8*)key; + + QString entry = QString("%1:%2:%3:%4:%5:%6") + .arg(eth_addr[0], 2, 16, QChar('0')) + .arg(eth_addr[1], 2, 16, QChar('0')) + .arg(eth_addr[2], 2, 16, QChar('0')) + .arg(eth_addr[3], 2, 16, QChar('0')) + .arg(eth_addr[4], 2, 16, QChar('0')) + .arg(eth_addr[5], 2, 16, QChar('0')); + + // We should filter on only those actually resolved, not display + // everything in wka + *values << (QStringList() << entry << QString(name)); +} + +} + +EthernetAddressModel::EthernetAddressModel(QObject * parent): + AStringListListModel(parent) +{ + populate(); +} + +QStringList EthernetAddressModel::headerColumns() const +{ + return QStringList() << tr("Type") << tr("Address") << tr("Name"); +} + +QStringList EthernetAddressModel::filterValues() const +{ + return QStringList() + << tr("All entries") + << tr("Hosts") + << tr("Ethernet Addresses") << tr("Ethernet Manufacturers") + << tr("Ethernet Well-Known Addresses"); +} + +void EthernetAddressModel::populate() +{ + QList hosts; // List of (address, names) + if (wmem_map_t *ipv4_hash_table = get_ipv4_hash_table()) { + wmem_map_foreach(ipv4_hash_table, ipv4_hash_table_resolved_to_list, &hosts); + } + if (wmem_map_t *ipv6_hash_table = get_ipv6_hash_table()) { + wmem_map_foreach(ipv6_hash_table, ipv6_hash_table_resolved_to_list, &hosts); + } + const QString &hosts_label = tr("Hosts"); + foreach (const QStringList &addr_name, hosts) + appendRow(QStringList() << hosts_label << addr_name); + + QList values; + if (wmem_map_t *eth_hashtable = get_eth_hashtable()) { + wmem_map_foreach(eth_hashtable, eth_hash_to_qstringlist, &values); + } + const QString ð_label = tr("Ethernet Addresses"); + foreach (const QStringList &line, values) + appendRow(QStringList() << eth_label << line); + values.clear(); + if (wmem_map_t *eth_hashtable = get_manuf_hashtable()) { + wmem_map_foreach(eth_hashtable, manuf_hash_to_qstringlist, &values); + } + const QString &manuf_label = tr("Ethernet Manufacturers"); + foreach (const QStringList &line, values) + appendRow(QStringList() << manuf_label << line); + values.clear(); + if (wmem_map_t *eth_hashtable = get_wka_hashtable()) { + wmem_map_foreach(eth_hashtable, wka_hash_to_qstringlist, &values); + } + const QString &wka_label = tr("Ethernet Well-Known Addresses"); + foreach (const QStringList &line, values) + appendRow(QStringList() << wka_label << line); +} + +PortsModel::PortsModel(QObject * parent): + AStringListListModel(parent) +{ + populate(); +} + +QStringList PortsModel::filterValues() const +{ + return QStringList() + << tr("All entries") << tr("tcp") << tr("udp") << tr("sctp") << tr("dccp"); +} + +QStringList PortsModel::headerColumns() const +{ + return QStringList() << tr("Name") << tr("Port") << tr("Type"); +} + +void PortsModel::populate() +{ + wmem_map_t *serv_port_hashtable = get_serv_port_hashtable(); + if (serv_port_hashtable) { + wmem_map_foreach(serv_port_hashtable, serv_port_hash_to_qstringlist, this); + } +} diff --git a/ui/qt/models/resolved_addresses_models.h b/ui/qt/models/resolved_addresses_models.h new file mode 100644 index 00000000..32ca1b15 --- /dev/null +++ b/ui/qt/models/resolved_addresses_models.h @@ -0,0 +1,48 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RESOLVED_ADDRESSES_MODELS_H +#define RESOLVED_ADDRESSES_MODELS_H + +#include + +#include +#include + +class EthernetAddressModel : public AStringListListModel +{ + Q_OBJECT + +public: + EthernetAddressModel(QObject * parent = Q_NULLPTR); + + QStringList filterValues() const; + +protected: + QStringList headerColumns() const override; + void populate(); + +}; + +class PortsModel : public AStringListListModel +{ + Q_OBJECT + +public: + PortsModel(QObject * parent = Q_NULLPTR); + + QStringList filterValues() const; + +protected: + QStringList headerColumns() const override; + void populate(); + +}; + +#endif // RESOLVED_ADDRESSES_MODELS_H diff --git a/ui/qt/models/sparkline_delegate.cpp b/ui/qt/models/sparkline_delegate.cpp new file mode 100644 index 00000000..457c8295 --- /dev/null +++ b/ui/qt/models/sparkline_delegate.cpp @@ -0,0 +1,109 @@ +/* sparkline_delegate.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include + +#define SPARKLINE_MIN_EM_WIDTH 10 + +void SparkLineDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QList points = qvariant_cast >(index.data(Qt::UserRole)); + int max = 1; + // We typically draw a sparkline alongside some text. Size our + // drawing area based on an Em width. and a bit of eyballing on + // Linux, macOS, and Windows. + int em_w = option.fontMetrics.height(); + int content_w = option.rect.width() - (em_w / 4); + int content_h = option.fontMetrics.ascent() - 1; + int val; + qreal idx = 0.0; + qreal step_w = em_w / 10.0; + qreal steps = content_w / step_w; + QVector fpoints; + + QStyledItemDelegate::paint(painter, option, index); + + if (points.isEmpty() || steps < 1.0 || content_h <= 0) { + return; + } + + while ((qreal) points.length() > steps) { + points.removeFirst(); + } + + foreach (val, points) { + if (val > max) max = val; + } + + foreach (val, points) { + fpoints.append(QPointF(idx, (qreal) content_h - (val * content_h / max))); + idx = idx + step_w; + } + + QStyleOptionViewItem option_vi = option; + QStyledItemDelegate::initStyleOption(&option_vi, index); + + painter->save(); + + if (QApplication::style()->objectName().contains("vista")) { + // QWindowsVistaStyle::drawControl does this internally. Unfortunately there + // doesn't appear to be a more general way to do this. + option_vi.palette.setColor(QPalette::All, QPalette::HighlightedText, option_vi.palette.color(QPalette::Active, QPalette::Text)); + } + + QPalette::ColorGroup cg = option_vi.state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(option_vi.state & QStyle::State_Active)) + cg = QPalette::Inactive; +#if defined(Q_OS_WIN) + if (option_vi.state & QStyle::State_Selected) { +#else + if ((option_vi.state & QStyle::State_Selected) && !(option_vi.state & QStyle::State_MouseOver)) { +#endif + painter->setPen(option_vi.palette.color(cg, QPalette::HighlightedText)); + } else { + painter->setPen(option_vi.palette.color(cg, QPalette::Text)); + } + + // As a general rule, aliased painting renders to pixels and + // antialiased painting renders to mathematical coordinates: + // https://doc.qt.io/qt-5/coordsys.html + // Shift our coordinates by 0.5 pixels, otherwise our lines end + // up blurry. + painter->setRenderHint(QPainter::Antialiasing, true); + painter->translate( + option.rect.x() + (em_w / 8) + 0.5, + option.rect.y() + ((option.rect.height() - option.fontMetrics.height()) / 2) + 1 + 0.5); + painter->drawPolyline(QPolygonF(fpoints)); + + // Some sparklines are decorated with dots at the beginning and end. + // Ours look better without in my (gcc) opinion. +// painter->setPen(Qt::NoPen); +// painter->setBrush(option.palette.foreground()); +// painter->drawEllipse(fpoints.first(), 2, 2); + +// painter->setBrush(Qt::red); +// painter->drawEllipse(fpoints.last(), 2, 2); + + painter->restore(); +} + +QSize SparkLineDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const { + return QSize(option.fontMetrics.height() * SPARKLINE_MIN_EM_WIDTH, QStyledItemDelegate::sizeHint(option, index).height()); +} + +QWidget *SparkLineDelegate::createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const +{ + return NULL; +} diff --git a/ui/qt/models/sparkline_delegate.h b/ui/qt/models/sparkline_delegate.h new file mode 100644 index 00000000..ddbe947a --- /dev/null +++ b/ui/qt/models/sparkline_delegate.h @@ -0,0 +1,35 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SPARKLINE_DELEGATE_H +#define SPARKLINE_DELEGATE_H + +#include + +class SparkLineDelegate : public QStyledItemDelegate +{ +public: + SparkLineDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {} + +protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; + +signals: + +public slots: + +}; + +Q_DECLARE_METATYPE(QList) + +#endif // SPARKLINE_DELEGATE_H diff --git a/ui/qt/models/supported_protocols_model.cpp b/ui/qt/models/supported_protocols_model.cpp new file mode 100644 index 00000000..04e62572 --- /dev/null +++ b/ui/qt/models/supported_protocols_model.cpp @@ -0,0 +1,261 @@ +/* supported_protocols_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +#include + +SupportedProtocolsItem::SupportedProtocolsItem(protocol_t* proto, const char *name, const char* filter, ftenum_t ftype, const char* descr, SupportedProtocolsItem* parent) + : ModelHelperTreeItem(parent), + proto_(proto), + name_(name), + filter_(filter), + ftype_(ftype), + descr_(descr) +{ +} + +SupportedProtocolsItem::~SupportedProtocolsItem() +{ +} + + +SupportedProtocolsModel::SupportedProtocolsModel(QObject *parent) : + QAbstractItemModel(parent), + root_(new SupportedProtocolsItem(NULL, NULL, NULL, FT_NONE, NULL, NULL)), + field_count_(0) +{ +} + +SupportedProtocolsModel::~SupportedProtocolsModel() +{ + delete root_; +} + +int SupportedProtocolsModel::rowCount(const QModelIndex &parent) const +{ + SupportedProtocolsItem *parent_item; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + if (parent_item == NULL) + return 0; + + return parent_item->childCount(); +} + +int SupportedProtocolsModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + +QVariant SupportedProtocolsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + + switch ((enum SupportedProtocolsColumn)section) { + case colName: + return tr("Name"); + case colFilter: + return tr("Filter"); + case colType: + return tr("Type"); + case colDescription: + return tr("Description"); + default: + break; + } + } + return QVariant(); +} + +QModelIndex SupportedProtocolsModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return QModelIndex(); + + SupportedProtocolsItem* item = static_cast(index.internalPointer()); + if (item != NULL) { + SupportedProtocolsItem* parent_item = item->parentItem(); + if (parent_item != NULL) { + if (parent_item == root_) + return QModelIndex(); + + return createIndex(parent_item->row(), 0, parent_item); + } + } + + return QModelIndex(); +} + +QModelIndex SupportedProtocolsModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + SupportedProtocolsItem *parent_item, *child_item; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast(parent.internalPointer()); + + Q_ASSERT(parent_item); + + child_item = parent_item->child(row); + if (child_item) { + return createIndex(row, column, child_item); + } + + return QModelIndex(); +} + +QVariant SupportedProtocolsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || role != Qt::DisplayRole) + return QVariant(); + + SupportedProtocolsItem* item = static_cast(index.internalPointer()); + if (item == NULL) + return QVariant(); + + switch ((enum SupportedProtocolsColumn)index.column()) { + case colName: + return item->name(); + case colFilter: + return item->filter(); + case colType: + if (index.parent().isValid()) + return QString(ftype_pretty_name(item->type())); + + return QVariant(); + case colDescription: + return item->description(); + default: + break; + } + + return QVariant(); +} + +void SupportedProtocolsModel::populate() +{ + void *proto_cookie; + void *field_cookie; + + beginResetModel(); + + SupportedProtocolsItem *protoItem, *fieldItem; + protocol_t *protocol; + + for (int proto_id = proto_get_first_protocol(&proto_cookie); proto_id != -1; + proto_id = proto_get_next_protocol(&proto_cookie)) { + + protocol = find_protocol_by_id(proto_id); + protoItem = new SupportedProtocolsItem(protocol, proto_get_protocol_short_name(protocol), proto_get_protocol_filter_name(proto_id), FT_PROTOCOL, proto_get_protocol_long_name(protocol), root_); + root_->prependChild(protoItem); + + for (header_field_info *hfinfo = proto_get_first_protocol_field(proto_id, &field_cookie); hfinfo != NULL; + hfinfo = proto_get_next_protocol_field(proto_id, &field_cookie)) { + if (hfinfo->same_name_prev_id != -1) + continue; + + fieldItem = new SupportedProtocolsItem(protocol, hfinfo->name, hfinfo->abbrev, hfinfo->type, hfinfo->blurb, protoItem); + protoItem->prependChild(fieldItem); + field_count_++; + } + } + + endResetModel(); +} + + + +SupportedProtocolsProxyModel::SupportedProtocolsProxyModel(QObject * parent) +: QSortFilterProxyModel(parent), +filter_() +{ +} + +bool SupportedProtocolsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + //Use SupportedProtocolsItem directly for better performance + SupportedProtocolsItem* left_item = static_cast(left.internalPointer()); + SupportedProtocolsItem* right_item = static_cast(right.internalPointer()); + + if ((left_item != NULL) && (right_item != NULL)) { + int compare_ret = left_item->name().compare(right_item->name()); + if (compare_ret < 0) + return true; + } + + return false; +} + +bool SupportedProtocolsProxyModel::filterAcceptItem(SupportedProtocolsItem& item) const +{ + QRegularExpression regex(filter_, QRegularExpression::CaseInsensitiveOption); + if (! regex.isValid()) + return false; + + if (item.name().contains(regex)) + return true; + + if (item.filter().contains(regex)) + return true; + + if (item.description().contains(regex)) + return true; + + return false; +} + +bool SupportedProtocolsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex nameIdx = sourceModel()->index(sourceRow, SupportedProtocolsModel::colName, sourceParent); + SupportedProtocolsItem* item = static_cast(nameIdx.internalPointer()); + if (item == NULL) + return true; + + if (!filter_.isEmpty()) { + if (filterAcceptItem(*item)) + return true; + + if (!nameIdx.parent().isValid()) + { + SupportedProtocolsItem* child_item; + for (int row = 0; row < item->childCount(); row++) + { + child_item = item->child(row); + if ((child_item != NULL) && (filterAcceptItem(*child_item))) + return true; + } + } + + return false; + } + + return true; +} + +void SupportedProtocolsProxyModel::setFilter(const QString& filter) +{ + filter_ = filter; + invalidateFilter(); +} diff --git a/ui/qt/models/supported_protocols_model.h b/ui/qt/models/supported_protocols_model.h new file mode 100644 index 00000000..d964ef68 --- /dev/null +++ b/ui/qt/models/supported_protocols_model.h @@ -0,0 +1,97 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SUPPORTED_PROTOCOLS_MODEL_H +#define SUPPORTED_PROTOCOLS_MODEL_H + +#include + +#include + +#include + +#include + +class SupportedProtocolsItem : public ModelHelperTreeItem +{ +public: + SupportedProtocolsItem(protocol_t* proto, const char *name, const char* filter, ftenum_t ftype, const char* descr, SupportedProtocolsItem* parent); + virtual ~SupportedProtocolsItem(); + + protocol_t* protocol() const {return proto_; } + QString name() const { return name_; } + ftenum_t type() const {return ftype_; } + QString filter() const { return filter_; } + QString description() const { return descr_; } + +private: + protocol_t* proto_; + QString name_; + QString filter_; + ftenum_t ftype_; + QString descr_; +}; + + +class SupportedProtocolsModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit SupportedProtocolsModel(QObject * parent = Q_NULLPTR); + virtual ~SupportedProtocolsModel(); + + enum SupportedProtocolsColumn { + colName = 0, + colFilter, + colType, + colDescription, + colLast + }; + + int fieldCount() {return field_count_;} + + QModelIndex index(int row, int column, + const QModelIndex & = QModelIndex()) const; + QModelIndex parent(const QModelIndex &) const; + QVariant data(const QModelIndex &index, int role) 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 populate(); + +private: + SupportedProtocolsItem* root_; + int field_count_; +}; + +class SupportedProtocolsProxyModel : public QSortFilterProxyModel +{ +public: + + explicit SupportedProtocolsProxyModel(QObject * parent = Q_NULLPTR); + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + + void setFilter(const QString& filter); + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + bool filterAcceptItem(SupportedProtocolsItem& item) const; + +private: + + QString filter_; +}; + +#endif // SUPPORTED_PROTOCOLS_MODEL_H diff --git a/ui/qt/models/timeline_delegate.cpp b/ui/qt/models/timeline_delegate.cpp new file mode 100644 index 00000000..07a7409c --- /dev/null +++ b/ui/qt/models/timeline_delegate.cpp @@ -0,0 +1,128 @@ +/* timeline_delegate.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include + +#include +#include +#include +#include + +// XXX We might want to move this to conversation_dialog.cpp. + +// PercentBarDelegate uses a stronger blend value, but its bars are also +// more of a prominent feature. Make the blend weaker here so that we don't +// obscure our text. +static const double bar_blend_ = 0.08; + +TimelineDelegate::TimelineDelegate(QWidget *parent) : + QStyledItemDelegate(parent) +{ + _dataRole = Qt::UserRole; +} + +void TimelineDelegate::setDataRole(int dataRole) +{ + _dataRole = dataRole; +} + +void TimelineDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionViewItem option_vi = option; + QStyledItemDelegate::initStyleOption(&option_vi, index); + + bool drawBar = false; + struct timeline_span span_px = index.data(_dataRole).value(); + if (_dataRole == ATapDataModel::TIMELINE_DATA) { + double span_s = span_px.maxRelTime - span_px.minRelTime; + QTreeView * tree = qobject_cast(parent()); + if (tree) { + QAbstractProxyModel * proxy = qobject_cast(tree->model()); + if (proxy && proxy->sourceModel()) { + QModelIndex indexStart = proxy->mapFromSource(proxy->sourceModel()->index(0, span_px.colStart)); + int colStart = -1; + int start_px = 0; + if (indexStart.isValid()) { + colStart = indexStart.column(); + start_px = tree->columnWidth(colStart); + } + int colDuration = -1; + int column_px = start_px; + QModelIndex indexDuration = proxy->mapFromSource(proxy->sourceModel()->index(0, span_px.colDuration)); + if (indexDuration.isValid()) { + colDuration = indexDuration.column(); + column_px += tree->columnWidth(colDuration); + } + + span_px.start = ((span_px.startTime - span_px.minRelTime) * column_px) / span_s; + span_px.width = ((span_px.stopTime - span_px.startTime) * column_px) / span_s; + + if (index.column() == colStart) { + drawBar = true; + } else if (index.column() == colDuration) { + drawBar = true; + span_px.start -= start_px; + } + } + } + } + + if (!drawBar) { + QStyledItemDelegate::paint(painter, option, index); + return; + } + + // Paint our rect with no text using the current style, then draw our + // bar and text over it. + option_vi.text = QString(); + QStyle *style = option_vi.widget ? option_vi.widget->style() : QApplication::style(); + style->drawControl(QStyle::CE_ItemViewItem, &option_vi, painter, option_vi.widget); + + if (QApplication::style()->objectName().contains("vista")) { + // QWindowsVistaStyle::drawControl does this internally. Unfortunately there + // doesn't appear to be a more general way to do this. + option_vi.palette.setColor(QPalette::All, QPalette::HighlightedText, + option_vi.palette.color(QPalette::Active, QPalette::Text)); + } + + QPalette::ColorGroup cg = option_vi.state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + QColor text_color = option_vi.palette.color(cg, QPalette::Text); + QColor bar_color = ColorUtils::alphaBlend(option_vi.palette.windowText(), + option_vi.palette.window(), bar_blend_); + + if (cg == QPalette::Normal && !(option_vi.state & QStyle::State_Active)) + cg = QPalette::Inactive; + if (option_vi.state & QStyle::State_Selected) { + text_color = option_vi.palette.color(cg, QPalette::HighlightedText); + bar_color = ColorUtils::alphaBlend(option_vi.palette.color(cg, QPalette::Window), + option_vi.palette.color(cg, QPalette::Highlight), + bar_blend_); + } + + painter->save(); + int border_radius = 3; // We use 3 px elsewhere, e.g. filter combos. + QRect timeline_rect = option.rect; + timeline_rect.adjust(span_px.start, 1, 0, -1); + timeline_rect.setWidth(span_px.width); + painter->setClipRect(option.rect); + painter->setPen(Qt::NoPen); + painter->setBrush(bar_color); + painter->drawRoundedRect(timeline_rect, border_radius, border_radius); + painter->restore(); + + painter->save(); + painter->setPen(text_color); + painter->drawText(option.rect, Qt::AlignCenter, index.data(Qt::DisplayRole).toString()); + painter->restore(); +} diff --git a/ui/qt/models/timeline_delegate.h b/ui/qt/models/timeline_delegate.h new file mode 100644 index 00000000..06218b87 --- /dev/null +++ b/ui/qt/models/timeline_delegate.h @@ -0,0 +1,66 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TIMELINE_DELEGATE_H +#define TIMELINE_DELEGATE_H + +/* + * @file Timeline delegate. + * + * QStyledItemDelegate subclass that will draw a timeline indicator for + * the specified value. + * + * This is intended to be used in QTreeWidgets to show timelines, e.g. for + * conversations. + * To use it, first call setItemDelegate: + * + * myTreeWidget()->setItemDelegateForColumn(col_time_start_, new TimelineDelegate()); + * + * Then, for each QTreeWidgetItem, set or return a timeline_span for the start and end + * of the timeline in pixels relative to the column width. + * + * setData(col_start_, Qt::UserRole, start_span); + * setData(col_end_, Qt::UserRole, end_span); + * + */ + +#include + +// Pixels are relative to item rect and will be clipped. +struct timeline_span { + int start; + int width; + + double startTime; + double stopTime; + double minRelTime; + double maxRelTime; + + int colStart; + int colDuration; +}; + +Q_DECLARE_METATYPE(timeline_span) + +class TimelineDelegate : public QStyledItemDelegate +{ +public: + TimelineDelegate(QWidget *parent = 0); + + void setDataRole(int role); + +protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +private: + + int _dataRole; +}; + +#endif // TIMELINE_DELEGATE_H diff --git a/ui/qt/models/tree_model_helpers.h b/ui/qt/models/tree_model_helpers.h new file mode 100644 index 00000000..5734b660 --- /dev/null +++ b/ui/qt/models/tree_model_helpers.h @@ -0,0 +1,89 @@ +/** @file + * + * Utility template classes for basic tree model functionality + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TREE_MODEL_HELPERS_H +#define TREE_MODEL_HELPERS_H + +#include +#include + +#include + +//Base class to inherit basic tree item from +template +class ModelHelperTreeItem +{ +public: + ModelHelperTreeItem(Item* parent) + : parent_(parent) + { + } + + virtual ~ModelHelperTreeItem() + { + for (int row = 0; row < childItems_.count(); row++) + { + delete VariantPointer::asPtr(childItems_.value(row)); + } + + childItems_.clear(); + } + + void appendChild(Item* child) + { + childItems_.append(VariantPointer::asQVariant(child)); + } + + void prependChild(Item* child) + { + childItems_.prepend(VariantPointer::asQVariant(child)); + } + + + void insertChild(int row, Item* child) + { + childItems_.insert(row, VariantPointer::asQVariant(child)); + } + + void removeChild(int row) + { + delete VariantPointer::asPtr(childItems_.value(row)); + childItems_.removeAt(row); + } + + Item* child(int row) + { + return VariantPointer::asPtr(childItems_.value(row)); + } + + int childCount() const + { + return static_cast(childItems_.count()); + } + + int row() + { + if (parent_) + { + return static_cast(parent_->childItems_.indexOf(VariantPointer::asQVariant((Item *)this))); + } + + return 0; + } + + Item* parentItem() {return parent_; } + +protected: + Item* parent_; + QList childItems_; +}; + +#endif // TREE_MODEL_HELPERS_H diff --git a/ui/qt/models/uat_delegate.cpp b/ui/qt/models/uat_delegate.cpp new file mode 100644 index 00000000..4e7d25b3 --- /dev/null +++ b/ui/qt/models/uat_delegate.cpp @@ -0,0 +1,221 @@ +/* uat_delegate.cpp + * Delegates for editing various field types in a UAT record. + * + * Copyright 2016 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include // for get_dissector_names() +#include "epan/value_string.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// The Qt docs suggest overriding updateEditorGeometry, but the +// defaults seem sane. + +UatDelegate::UatDelegate(QObject *parent) : QStyledItemDelegate(parent) +{ +} + +uat_field_t *UatDelegate::indexToField(const QModelIndex &index) const +{ + const QVariant v = index.model()->data(index, Qt::UserRole); + return static_cast(v.value()); +} + +QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + uat_field_t *field = indexToField(index); + QWidget *editor = nullptr; + + switch (field->mode) { + case PT_TXTMOD_DIRECTORYNAME: + case PT_TXTMOD_FILENAME: + if (index.isValid()) { + QString filename_old = index.model()->data(index, Qt::EditRole).toString(); + PathSelectionEdit * pathEdit = new PathSelectionEdit(field->title, QString(), field->mode != PT_TXTMOD_DIRECTORYNAME, parent); + connect(pathEdit, &PathSelectionEdit::pathChanged, this, &UatDelegate::pathHasChanged); + return pathEdit; + } + break; + case PT_TXTMOD_COLOR: + if (index.isValid()) { + QColor color(index.model()->data(index, Qt::DecorationRole).toString()); + QColorDialog * colorDialog = new QColorDialog(color, parent); + // Don't fall through and set setAutoFillBackground(true) + return colorDialog; + } + break; + + case PT_TXTMOD_ENUM: + { + // Note: the string repr. is written, not the integer value. + QComboBox *cb_editor = new QComboBox(parent); + const value_string *enum_vals = (const value_string *)field->fld_data; + for (int i = 0; enum_vals[i].strptr != nullptr; i++) { + cb_editor->addItem(enum_vals[i].strptr); + } + editor = cb_editor; + cb_editor->setMinimumWidth(cb_editor->minimumSizeHint().width()); + break; + } + + case PT_TXTMOD_DISSECTOR: + { + editor = new DissectorSyntaxLineEdit(parent); + break; + } + + case PT_TXTMOD_STRING: + // TODO add a live validator? Should SyntaxLineEdit be used? + editor = QStyledItemDelegate::createEditor(parent, option, index); + break; + + case PT_TXTMOD_DISPLAY_FILTER: + editor = new DisplayFilterEdit(parent); + break; + + case PT_TXTMOD_PROTO_FIELD: + editor = new FieldFilterEdit(parent); + break; + + case PT_TXTMOD_HEXBYTES: + { + // Requires input of the form "ab cd ef" (with possibly no or a colon + // separator instead of a single whitespace) for the editor to accept. + QRegularExpression hexbytes_regex("([0-9a-f]{2}[ :]?)*"); + hexbytes_regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption); + // QString types from QStyledItemDelegate are documented to return a + // QLineEdit. Note that Qt returns a subclass from QLineEdit which + // automatically adapts the width to the typed contents. + QLineEdit *le_editor = static_cast( + QStyledItemDelegate::createEditor(parent, option, index)); + le_editor->setValidator(new QRegularExpressionValidator(hexbytes_regex, le_editor)); + editor = le_editor; + break; + } + + case PT_TXTMOD_BOOL: + // model will handle creating checkbox + break; + + case PT_TXTMOD_NONE: + break; + + default: + ws_assert_not_reached(); + break; + } + + if (editor) { + editor->setAutoFillBackground(true); + } + return editor; +} + +void UatDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + uat_field_t *field = indexToField(index); + + switch (field->mode) { + case PT_TXTMOD_DIRECTORYNAME: + case PT_TXTMOD_FILENAME: + if (index.isValid() && qobject_cast(editor)) + qobject_cast(editor)->setPath(index.model()->data(index, Qt::EditRole).toString()); + break; + case PT_TXTMOD_ENUM: + { + QComboBox *combobox = static_cast(editor); + const QString &data = index.model()->data(index, Qt::EditRole).toString(); + combobox->setCurrentText(data); + + break; + } + case PT_TXTMOD_COLOR: + { + if (qobject_cast(editor)) + { + QColor color(index.model()->data(index, Qt::DecorationRole).toString()); + qobject_cast(editor)->setCurrentColor(color); + } + break; + } + + default: + QStyledItemDelegate::setEditorData(editor, index); + } +} + +void UatDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + uat_field_t *field = indexToField(index); + + switch (field->mode) { + case PT_TXTMOD_DIRECTORYNAME: + case PT_TXTMOD_FILENAME: + if (index.isValid() && qobject_cast(editor)) + const_cast(index.model())->setData(index, qobject_cast(editor)->path(), Qt::EditRole); + break; + case PT_TXTMOD_ENUM: + { + QComboBox *combobox = static_cast(editor); + const QString &data = combobox->currentText(); + model->setData(index, data, Qt::EditRole); + break; + } + case PT_TXTMOD_COLOR: + //do nothing, dialog signals will update table + if (qobject_cast(editor)) + { + QColor newColor = qobject_cast(editor)->currentColor(); + const_cast(index.model())->setData(index, newColor.name(), Qt::EditRole); + } + break; + + default: + QStyledItemDelegate::setModelData(editor, model, index); + } +} + +void UatDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + uat_field_t *field = indexToField(index); + + switch (field->mode) { + case PT_TXTMOD_DIRECTORYNAME: + case PT_TXTMOD_FILENAME: + editor->setGeometry(option.rect); + break; + default: + QStyledItemDelegate::updateEditorGeometry(editor, option, index); + } +} + +void UatDelegate::pathHasChanged(QString) +{ + PathSelectionEdit * editor = qobject_cast(sender()); + if (editor) + emit commitData(editor); +} diff --git a/ui/qt/models/uat_delegate.h b/ui/qt/models/uat_delegate.h new file mode 100644 index 00000000..e57e533d --- /dev/null +++ b/ui/qt/models/uat_delegate.h @@ -0,0 +1,42 @@ +/** @file + * + * Delegates for editing various field types in a UAT record. + * + * Copyright 2016 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UAT_DELEGATE_H +#define UAT_DELEGATE_H + +#include +#include +#include + +#include +#include + +class UatDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + UatDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +protected slots: + void pathHasChanged(QString newPath); + +private: + uat_field_t *indexToField(const QModelIndex &index) const; +}; +#endif // UAT_DELEGATE_H diff --git a/ui/qt/models/uat_model.cpp b/ui/qt/models/uat_model.cpp new file mode 100644 index 00000000..a841ca8c --- /dev/null +++ b/ui/qt/models/uat_model.cpp @@ -0,0 +1,545 @@ +/* uat_model.cpp + * Data model for UAT records. + * + * Copyright 2016 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "uat_model.h" +#include +#include +#include + +UatModel::UatModel(QObject *parent, epan_uat *uat) : + QAbstractTableModel(parent), + uat_(0) +{ + loadUat(uat); +} + +UatModel::UatModel(QObject * parent, QString tableName) : + QAbstractTableModel(parent), + uat_(0) +{ + loadUat(uat_get_table_by_name(tableName.toStdString().c_str())); +} + +void UatModel::loadUat(epan_uat * uat) +{ + uat_ = uat; + + dirty_records.reserve(uat_->raw_data->len); + // Validate existing data such that they can be marked as invalid if necessary. + record_errors.reserve(uat_->raw_data->len); + for (int i = 0; i < (int)uat_->raw_data->len; i++) { + record_errors.push_back(QMap()); + checkRow(i); + // Assume that records are initially not modified. + dirty_records.push_back(false); + } +} + +void UatModel::reloadUat() +{ + beginResetModel(); + loadUat(uat_); + endResetModel(); +} + +bool UatModel::applyChanges(QString &error) +{ + if (uat_->changed) { + gchar *err = NULL; + + if (!uat_save(uat_, &err)) { + error = QString("Error while saving %1: %2").arg(uat_->name).arg(err); + g_free(err); + } + + if (uat_->post_update_cb) { + uat_->post_update_cb(); + } + return true; + } + + return false; +} + +bool UatModel::revertChanges(QString &error) +{ + // Ideally this model should remember the changes made and try to undo them + // to avoid calling post_update_cb. Calling uat_clear + uat_load is a lazy + // option and might fail (e.g. when the UAT file is removed). + if (uat_->changed) { + gchar *err = NULL; + uat_clear(uat_); + if (!uat_load(uat_, NULL, &err)) { + error = QString("Error while loading %1: %2").arg(uat_->name).arg(err); + g_free(err); + } + return true; + } + + return false; +} + +Qt::ItemFlags UatModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemFlags(); + + uat_field_t *field = &uat_->fields[index.column()]; + + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + if (field->mode == PT_TXTMOD_BOOL) + { + flags |= Qt::ItemIsUserCheckable; + } + flags |= Qt::ItemIsEditable; + return flags; +} + +QVariant UatModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + void *rec = UAT_INDEX_PTR(uat_, index.row()); + uat_field_t *field = &uat_->fields[index.column()]; + if (role == Qt::DisplayRole || role == Qt::EditRole) { + char *str = NULL; + guint length = 0; + field->cb.tostr(rec, &str, &length, field->cbdata.tostr, field->fld_data); + + switch (field->mode) { + case PT_TXTMOD_HEXBYTES: + { + char* temp_str = bytes_to_str(NULL, (const guint8 *) str, length); + g_free(str); + QString qstr(temp_str); + wmem_free(NULL, temp_str); + return qstr; + } + case PT_TXTMOD_BOOL: + case PT_TXTMOD_COLOR: + return QVariant(); + default: + { + QString qstr(str); + g_free(str); + return qstr; + } + } + } + + if ((role == Qt::CheckStateRole) && (field->mode == PT_TXTMOD_BOOL)) + { + char *str = NULL; + guint length = 0; + enum Qt::CheckState state = Qt::Unchecked; + field->cb.tostr(rec, &str, &length, field->cbdata.tostr, field->fld_data); + if ((g_strcmp0(str, "TRUE") == 0) || + (g_strcmp0(str, "Enabled") == 0)) + state = Qt::Checked; + + g_free(str); + return state; + } + + if (role == Qt::UserRole) { + return QVariant::fromValue(static_cast(field)); + } + + const QMap &errors = record_errors[index.row()]; + // mark fields that fail the validation. + if (role == Qt::BackgroundRole) { + if (errors.contains(index.column())) { + // TODO is it OK to color cells like this? Maybe some other marker is better? + return QBrush("pink"); + } + return QVariant(); + } + + if ((role == Qt::DecorationRole) && (field->mode == PT_TXTMOD_COLOR)) { + char *str = NULL; + guint length = 0; + field->cb.tostr(rec, &str, &length, field->cbdata.tostr, field->fld_data); + + return QColor(QString(str)); + } + + // expose error message if any. + if (role == Qt::UserRole + 1) { + if (errors.contains(index.column())) { + return errors[index.column()]; + } + return QVariant(); + } + + return QVariant(); +} + +QModelIndex UatModel::findRowForColumnContent(QVariant columnContent, int columnToCheckAgainst, int role) +{ + if (! columnContent.isValid()) + return QModelIndex(); + + for (int i = 0; i < rowCount(); i++) + { + QVariant r_expr = data(index(i, columnToCheckAgainst), role); + if (r_expr == columnContent) + return index(i, columnToCheckAgainst); + } + + return QModelIndex(); +} + +QVariant UatModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal) { + return QVariant(); + } + + if (role == Qt::ToolTipRole && uat_->fields[section].desc) { + return uat_->fields[section].desc; + } + + if (role == Qt::DisplayRole) { + return uat_->fields[section].title; + } + + return QVariant(); +} + +int UatModel::rowCount(const QModelIndex &parent) const +{ + // there are no children + if (parent.isValid()) { + return 0; + } + + return uat_->raw_data->len; +} + +int UatModel::columnCount(const QModelIndex &parent) const +{ + // there are no children + if (parent.isValid()) { + return 0; + } + + return uat_->ncols; +} + +QModelIndex UatModel::appendEntry(QVariantList rowData) +{ + // Don't add an empty row, or a row with more entries than we have columns, + // but a row with fewer can be added, where the remaining entries are empty + if (rowData.count() == 0 || rowData.count() > columnCount()) + return QModelIndex(); + + QModelIndex newIndex; + int row = rowCount(); + emit beginInsertRows(QModelIndex(), row, row); + + // Initialize with given values + void *record = g_malloc0(uat_->record_size); + for (int col = 0; col < columnCount(); col++) { + uat_field_t *field = &uat_->fields[col]; + + QString data; + if (rowData.count() > col) { + if (field->mode != PT_TXTMOD_BOOL) { + data = rowData[col].toString(); + } else { + if (rowData[col].toInt() == Qt::Checked) { + data = QString("TRUE"); + } else { + data = QString("FALSE"); + } + } + } + + QByteArray bytes = field->mode == PT_TXTMOD_HEXBYTES ? QByteArray::fromHex(data.toUtf8()) : data.toUtf8(); + field->cb.set(record, bytes.constData(), (unsigned) bytes.size(), field->cbdata.set, field->fld_data); + } + uat_insert_record_idx(uat_, row, record); + if (uat_->free_cb) { + uat_->free_cb(record); + } + g_free(record); + + record_errors.insert(row, QMap()); + // a new row is created. For the moment all fields are empty, so validation + // will likely mark everything as invalid. Ideally validation should be + // postponed until the row (in the view) is not selected anymore + checkRow(row); + dirty_records.insert(row, true); + uat_->changed = TRUE; + + emit endInsertRows(); + + newIndex = index(row, 0, QModelIndex()); + + return newIndex; +} + +bool UatModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + uat_field_t *field = &uat_->fields[index.column()]; + + if ((role != Qt::EditRole) && + ((field->mode == PT_TXTMOD_BOOL) && (role != Qt::CheckStateRole))) + return false; + + if (data(index, role) == value) { + // Data appears unchanged, do not do additional checks. + return true; + } + + const int row = index.row(); + void *rec = UAT_INDEX_PTR(uat_, row); + + //qDebug() << "Changing (" << row << "," << index.column() << ") from " << data(index, Qt::EditRole) << " to " << value; + if (field->mode != PT_TXTMOD_BOOL) { + const QByteArray &str = value.toString().toUtf8(); + const QByteArray &bytes = field->mode == PT_TXTMOD_HEXBYTES ? QByteArray::fromHex(str) : str; + field->cb.set(rec, bytes.constData(), (unsigned) bytes.size(), field->cbdata.set, field->fld_data); + } else { + if (value.toInt() == Qt::Checked) { + field->cb.set(rec, "TRUE", 4, field->cbdata.set, field->fld_data); + } else { + field->cb.set(rec, "FALSE", 5, field->cbdata.set, field->fld_data); + } + } + + QVector roles; + roles << role; + + // Check validity of all rows, obtaining a list of columns where the + // validity status has changed. + const QList &updated_cols = checkRow(row); + if (!updated_cols.isEmpty()) { + roles << Qt::BackgroundRole; + //qDebug() << "validation status changed:" << updated_cols; + } + + if (record_errors[row].isEmpty()) { + // If all individual fields are valid, invoke the update callback. This + // might detect additional issues in either individual fields, or the + // combination of them. + if (uat_->update_cb) { + char *err = NULL; + if (!uat_->update_cb(rec, &err)) { + // TODO the error is not exactly on the first column, but we need a way to show the error. + record_errors[row].insert(0, err); + g_free(err); + } + } + } + uat_update_record(uat_, rec, record_errors[row].isEmpty()); + dirty_records[row] = true; + uat_->changed = TRUE; + + if (updated_cols.size() > updated_cols.count(index.column())) { + // The validation status for other columns were also affected by + // changing this field, mark those as dirty! + emit dataChanged(this->index(row, updated_cols.first()), + this->index(row, updated_cols.last()), roles); + } else { + + emit dataChanged(index, index, roles); + } + return true; +} + +bool UatModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + // support insertion of just one item for now. + if (count != 1 || row < 0 || row > rowCount()) + return false; + + beginInsertRows(QModelIndex(), row, row); + + // Initialize with empty values, caller should use setData to populate it. + void *record = g_malloc0(uat_->record_size); + for (int col = 0; col < columnCount(); col++) { + uat_field_t *field = &uat_->fields[col]; + field->cb.set(record, "", 0, field->cbdata.set, field->fld_data); + } + uat_insert_record_idx(uat_, row, record); + if (uat_->free_cb) { + uat_->free_cb(record); + } + g_free(record); + + record_errors.insert(row, QMap()); + // a new row is created. For the moment all fields are empty, so validation + // will likely mark everything as invalid. Ideally validation should be + // postponed until the row (in the view) is not selected anymore + checkRow(row); + dirty_records.insert(row, true); + uat_->changed = TRUE; + endInsertRows(); + return true; +} + +bool UatModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + if (count != 1 || row < 0 || row >= rowCount()) + return false; + + beginRemoveRows(QModelIndex(), row, row); + uat_remove_record_idx(uat_, row); + record_errors.removeAt(row); + dirty_records.removeAt(row); + uat_->changed = TRUE; + endRemoveRows(); + return true; +} + +void UatModel::clearAll() +{ + if (rowCount() < 1) + return; + + beginResetModel(); + uat_clear(uat_); + record_errors.clear(); + dirty_records.clear(); + uat_->changed = TRUE; + endResetModel(); +} + +QModelIndex UatModel::copyRow(QModelIndex original) +{ + if (! original.isValid()) + return QModelIndex(); + + int newRow = rowCount(); + + beginInsertRows(QModelIndex(), newRow, newRow); + + // Initialize with empty values, caller should use setData to populate it. + void *record = g_malloc0(uat_->record_size); + for (int col = 0; col < columnCount(); col++) { + uat_field_t *field = &uat_->fields[col]; + field->cb.set(record, "", 0, field->cbdata.set, field->fld_data); + } + uat_insert_record_idx(uat_, newRow, record); + if (uat_->free_cb) { + uat_->free_cb(record); + } + g_free(record); + + record_errors.insert(newRow, QMap()); + // a new row is created. For the moment all fields are empty, so validation + // will likely mark everything as invalid. Ideally validation should be + // postponed until the row (in the view) is not selected anymore + checkRow(newRow); + dirty_records.insert(newRow, true); + + // the UAT record has been created, now it is filled with the infromation + const void *src_record = UAT_INDEX_PTR(uat_, original.row()); + void *dst_record = UAT_INDEX_PTR(uat_, newRow); + // insertRows always initializes the record with empty value. Before copying + // over the new values, be sure to clear the old fields. + if (uat_->free_cb) { + uat_->free_cb(dst_record); + } + if (uat_->copy_cb) { + uat_->copy_cb(dst_record, src_record, uat_->record_size); + } else { + /* According to documentation of uat_copy_cb_t memcpy should be used if uat_->copy_cb is NULL */ + memcpy(dst_record, src_record, uat_->record_size); + } + gboolean src_valid = g_array_index(uat_->valid_data, gboolean, original.row()); + uat_update_record(uat_, dst_record, src_valid); + record_errors[newRow] = record_errors[original.row()]; + dirty_records[newRow] = true; + + uat_->changed = TRUE; + + endInsertRows(); + + return index(newRow, 0, QModelIndex()); +} + +bool UatModel::moveRow(int src_row, int dst_row) +{ + if (src_row < 0 || src_row >= rowCount() || dst_row < 0 || dst_row >= rowCount()) + return false; + + int dst = src_row < dst_row ? dst_row + 1 : dst_row; + + beginMoveRows(QModelIndex(), src_row, src_row, QModelIndex(), dst); + uat_move_index(uat_, src_row, dst_row); + record_errors.move(src_row, dst_row); + dirty_records.move(src_row, dst_row); + uat_->changed = TRUE; + endMoveRows(); + + return true; +} + +bool UatModel::hasErrors() const +{ + for (int i = 0; i < rowCount(); i++) { + // Ignore errors on unmodified records, these should not prevent the OK + // button from saving modifications to other entries. + if (dirty_records[i] && !record_errors[i].isEmpty()) { + return true; + } + } + return false; +} + +bool UatModel::checkField(int row, int col, char **error) const +{ + uat_field_t *field = &uat_->fields[col]; + void *rec = UAT_INDEX_PTR(uat_, row); + + if (!field->cb.chk) { + return true; + } + + char *str = NULL; + guint length; + field->cb.tostr(rec, &str, &length, field->cbdata.tostr, field->fld_data); + + bool ok = field->cb.chk(rec, str, length, field->cbdata.chk, field->fld_data, error); + g_free(str); + return ok; +} + +// Validates all fields in the given record, setting error messages as needed. +// Returns the columns that have changed (not the columns with errors). +QList UatModel::checkRow(int row) +{ + Q_ASSERT(0 <= row && row < rowCount()); + + QList changed; + QMap &errors = record_errors[row]; + for (int col = 0; col < columnCount(); col++) { + char *err; + bool error_changed = errors.remove(col) > 0; + if (!checkField(row, col, &err)) { + errors.insert(col, err); + g_free(err); + error_changed = !error_changed; + } + if (error_changed) { + changed << col; + } + } + return changed; +} diff --git a/ui/qt/models/uat_model.h b/ui/qt/models/uat_model.h new file mode 100644 index 00000000..5da647a5 --- /dev/null +++ b/ui/qt/models/uat_model.h @@ -0,0 +1,82 @@ +/** @file + * + * Data model for UAT records. + * + * Copyright 2016 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UAT_MODEL_H +#define UAT_MODEL_H + +#include +#include + +#include +#include +#include +#include + +class UatModel : public QAbstractTableModel +{ +public: + UatModel(QObject *parent, uat_t *uat = 0); + UatModel(QObject *parent, QString tableName); + + Qt::ItemFlags flags(const QModelIndex &index) const; + 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; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + + QModelIndex appendEntry(QVariantList row); + + QModelIndex copyRow(QModelIndex original); + bool moveRow(int src_row, int dst_row); + + bool moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild); + + void reloadUat(); + bool hasErrors() const; + void clearAll(); + + /** + * If the UAT has changed, save the contents to file and invoke the UAT + * post_update_cb. + * + * @param error An error while saving changes, if any. + * @return true if anything changed, false otherwise. + */ + bool applyChanges(QString &error); + + /** + * Undo any changes to the UAT. + * + * @param error An error while restoring the original UAT, if any. + * @return true if anything changed, false otherwise. + */ + bool revertChanges(QString &error); + + QModelIndex findRowForColumnContent(QVariant columnContent, int columnToCheckAgainst, int role = Qt::DisplayRole); + +private: + bool checkField(int row, int col, char **error) const; + QList checkRow(int row); + void loadUat(uat_t * uat = 0); + + epan_uat *uat_; + QList dirty_records; + QList > record_errors; +}; +#endif // UAT_MODEL_H diff --git a/ui/qt/models/url_link_delegate.cpp b/ui/qt/models/url_link_delegate.cpp new file mode 100644 index 00000000..a2c32bef --- /dev/null +++ b/ui/qt/models/url_link_delegate.cpp @@ -0,0 +1,51 @@ +/* url_link_delegate.cpp + * Delegates for displaying links as links, including elide model + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include + +UrlLinkDelegate::UrlLinkDelegate(QObject *parent) + : QStyledItemDelegate(parent), + re_col_(-1), + url_re_(new QRegularExpression()) +{} + +UrlLinkDelegate::~UrlLinkDelegate() +{ + delete url_re_; +} + +void UrlLinkDelegate::setColCheck(int column, QString &pattern) +{ + re_col_ = column; + url_re_->setPattern(pattern); +} + +void UrlLinkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + if (re_col_ >= 0 && url_re_) { + QModelIndex re_idx = index.model()->index(index.row(), re_col_); + QString col_text = index.model()->data(re_idx).toString(); + if (!url_re_->match(col_text).hasMatch()) { + QStyledItemDelegate::paint(painter, option, index); + return; + } + } + + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + + opt.font.setUnderline(true); + opt.palette.setColor(QPalette::Text, ColorUtils::themeLinkBrush().color()); + + QStyledItemDelegate::paint(painter, opt, index); +} diff --git a/ui/qt/models/url_link_delegate.h b/ui/qt/models/url_link_delegate.h new file mode 100644 index 00000000..c3539284 --- /dev/null +++ b/ui/qt/models/url_link_delegate.h @@ -0,0 +1,36 @@ +/** @file + * + * Delegates for displaying links as links, including elide model + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef URL_LINK_DELEGATE_H +#define URL_LINK_DELEGATE_H + +#include +#include +#include +#include + +class UrlLinkDelegate : public QStyledItemDelegate +{ +public: + explicit UrlLinkDelegate(QObject *parent = Q_NULLPTR); + ~UrlLinkDelegate(); + // If pattern matches the string in column, render as a URL. + // Otherwise render as plain text. + void setColCheck(int column, QString &pattern); + +protected: + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + int re_col_; + QRegularExpression *url_re_; +}; +#endif // URL_LINK_DELEGATE_H diff --git a/ui/qt/models/voip_calls_info_model.cpp b/ui/qt/models/voip_calls_info_model.cpp new file mode 100644 index 00000000..23ba46f1 --- /dev/null +++ b/ui/qt/models/voip_calls_info_model.cpp @@ -0,0 +1,249 @@ +/* voip_calls_info_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "voip_calls_info_model.h" +#include +#include +#include + +#include + +VoipCallsInfoModel::VoipCallsInfoModel(QObject *parent) : + QAbstractTableModel(parent), + mTimeOfDay_(false) +{ +} + +voip_calls_info_t *VoipCallsInfoModel::indexToCallInfo(const QModelIndex &index) +{ + return VariantPointer::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(callinfos_[index.row()]); + + if (role == Qt::UserRole) { + return VariantPointer::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 QString::fromUtf8(call_info->from_identity); + case To: + return QString::fromUtf8(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 QString::fromUtf8(call_info->call_comment); + } + case ColumnCount: + ws_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: + ws_assert_not_reached(); + } + } + return QVariant(); +} + +int VoipCallsInfoModel::rowCount(const QModelIndex &parent) const +{ + // there are no children + if (parent.isValid()) { + return 0; + } + + return static_cast(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::fromMSecsSinceEpoch(nstime_to_msec(abs_ts), 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)); + } +} + +bool VoipCallsInfoModel::timeOfDay() const +{ + return mTimeOfDay_; +} + +void VoipCallsInfoModel::updateCalls(GQueue *callsinfos) +{ + if (callsinfos) { + qsizetype calls = callinfos_.count(); + int cnt = 0; + GList *cur_call; + + // Iterate new callsinfos and replace data in mode if required + cur_call = g_queue_peek_nth_link(callsinfos, 0); + while (cur_call && (cnt < calls)) { + if (callinfos_.at(cnt) != cur_call->data) { + // Data changed, use it + callinfos_.replace(cnt, cur_call->data); + } + cur_call = gxx_list_next(cur_call); + cnt++; + } + + // Add new rows + 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 = gxx_list_data(voip_calls_info_t*, cur_call); + callinfos_.push_back(call_info); + cur_call = gxx_list_next(cur_call); + } + endInsertRows(); + } + } +} + +void VoipCallsInfoModel::removeAllCalls() +{ + beginRemoveRows(QModelIndex(), 0, rowCount() - 1); + callinfos_.clear(); + endRemoveRows(); +} + +// 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); +} diff --git a/ui/qt/models/voip_calls_info_model.h b/ui/qt/models/voip_calls_info_model.h new file mode 100644 index 00000000..2f8d4007 --- /dev/null +++ b/ui/qt/models/voip_calls_info_model.h @@ -0,0 +1,71 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef VOIP_CALLS_INFO_MODEL_H +#define VOIP_CALLS_INFO_MODEL_H + +#include +#include + +#include "ui/voip_calls.h" +#include + +#include +#include + +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); + bool timeOfDay() const; + void updateCalls(GQueue *callsinfos); + void removeAllCalls(); + + 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 callinfos_; + bool mTimeOfDay_; + + QVariant timeData(nstime_t *abs_ts, nstime_t *rel_ts) const; +}; + +class VoipCallsInfoSortedModel : public QSortFilterProxyModel +{ +public: + VoipCallsInfoSortedModel(QObject *parent = 0); + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; +}; + +#endif // VOIP_CALLS_INFO_MODEL_H diff --git a/ui/qt/module_preferences_scroll_area.cpp b/ui/qt/module_preferences_scroll_area.cpp new file mode 100644 index 00000000..56503e0a --- /dev/null +++ b/ui/qt/module_preferences_scroll_area.cpp @@ -0,0 +1,659 @@ +/* module_preferences_scroll_area.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "module_preferences_scroll_area.h" +#include +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include +#include "uat_dialog.h" +#include "main_application.h" +#include "ui/qt/main_window.h" + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *pref_prop_ = "pref_ptr"; + +// Escape our ampersands so that Qt won't try to interpret them as +// mnemonics. +static const QString title_to_shortcut(const char *title) { + QString shortcut_str(title); + shortcut_str.replace('&', "&&"); + return shortcut_str; +} + +typedef struct +{ + QVBoxLayout *layout; + QString moduleName; +} prefSearchData; + +extern "C" { +// Callbacks prefs routines + +/* Add a single preference to the QVBoxLayout of a preference page */ +static guint +pref_show(pref_t *pref, gpointer user_data) +{ + prefSearchData * data = static_cast(user_data); + + if (!pref || !data) return 0; + + QVBoxLayout *vb = data->layout; + + // Convert the pref description from plain text to rich text. + QString description = html_escape(prefs_get_description(pref)); + QString name = QString("%1.%2").arg(data->moduleName).arg(prefs_get_name(pref)); + description.replace('\n', "
"); + QString tooltip = QString("%1

%2").arg(description).arg(name); + + switch (prefs_get_type(pref)) { + case PREF_UINT: + case PREF_DECODE_AS_UINT: + { + QHBoxLayout *hb = new QHBoxLayout(); + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + hb->addWidget(label); + QLineEdit *uint_le = new QLineEdit(); + uint_le->setToolTip(tooltip); + uint_le->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + uint_le->setMinimumWidth(uint_le->fontMetrics().height() * 8); + hb->addWidget(uint_le); + hb->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + vb->addLayout(hb); + break; + } + case PREF_BOOL: + { + QCheckBox *bool_cb = new QCheckBox(title_to_shortcut(prefs_get_title(pref))); + bool_cb->setToolTip(tooltip); + bool_cb->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + vb->addWidget(bool_cb); + break; + } + case PREF_ENUM: + { + const enum_val_t *ev; + ev = prefs_get_enumvals(pref); + if (!ev || !ev->description) + return 0; + + if (prefs_get_enum_radiobuttons(pref)) { + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + vb->addWidget(label); + QButtonGroup *enum_bg = new QButtonGroup(vb); + while (ev->description) { + QRadioButton *enum_rb = new QRadioButton(title_to_shortcut(ev->description)); + enum_rb->setToolTip(tooltip); + QStyleOption style_opt; + enum_rb->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + enum_rb->setStyleSheet(QString( + "QRadioButton {" + " margin-left: %1px;" + "}" + ) + .arg(enum_rb->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left())); + enum_bg->addButton(enum_rb, ev->value); + vb->addWidget(enum_rb); + ev++; + } + } else { + QHBoxLayout *hb = new QHBoxLayout(); + QComboBox *enum_cb = new QComboBox(); + enum_cb->setToolTip(tooltip); + enum_cb->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + for (ev = prefs_get_enumvals(pref); ev && ev->description; ev++) { + enum_cb->addItem(ev->description, QVariant(ev->value)); + } + QLabel * lbl = new QLabel(prefs_get_title(pref)); + lbl->setToolTip(tooltip); + hb->addWidget(lbl); + hb->addWidget(enum_cb); + hb->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + vb->addLayout(hb); + } + break; + } + case PREF_STRING: + { + QHBoxLayout *hb = new QHBoxLayout(); + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + hb->addWidget(label); + QLineEdit *string_le = new QLineEdit(); + string_le->setToolTip(tooltip); + string_le->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + string_le->setMinimumWidth(string_le->fontMetrics().height() * 20); + hb->addWidget(string_le); + hb->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + vb->addLayout(hb); + break; + } + case PREF_PASSWORD: + { + QHBoxLayout *hb = new QHBoxLayout(); + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + hb->addWidget(label); + QLineEdit *string_le = new QLineEdit(); + string_le->setToolTip(tooltip); + string_le->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + string_le->setMinimumWidth(string_le->fontMetrics().height() * 20); + string_le->setEchoMode(QLineEdit::PasswordEchoOnEdit); + hb->addWidget(string_le); + hb->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + vb->addLayout(hb); + break; + } + case PREF_DECODE_AS_RANGE: + case PREF_RANGE: + { + QHBoxLayout *hb = new QHBoxLayout(); + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + hb->addWidget(label); + SyntaxLineEdit *range_se = new SyntaxLineEdit(); + range_se->setToolTip(tooltip); + range_se->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + range_se->setMinimumWidth(range_se->fontMetrics().height() * 20); + hb->addWidget(range_se); + hb->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + vb->addLayout(hb); + break; + } + case PREF_STATIC_TEXT: + { + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + label->setWordWrap(true); + vb->addWidget(label); + break; + } + case PREF_UAT: + { + QHBoxLayout *hb = new QHBoxLayout(); + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + hb->addWidget(label); + QPushButton *uat_pb = new QPushButton(QObject::tr("Edit…")); + uat_pb->setToolTip(tooltip); + uat_pb->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + hb->addWidget(uat_pb); + hb->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + vb->addLayout(hb); + break; + } + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + { + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + vb->addWidget(label); + QHBoxLayout *hb = new QHBoxLayout(); + QLineEdit *path_le = new QLineEdit(); + path_le->setToolTip(tooltip); + QStyleOption style_opt; + path_le->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + path_le->setMinimumWidth(path_le->fontMetrics().height() * 20); + path_le->setStyleSheet(QString( + "QLineEdit {" + " margin-left: %1px;" + "}" + ) + .arg(path_le->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left())); + hb->addWidget(path_le); + QPushButton *path_pb = new QPushButton(QObject::tr("Browse…")); + path_pb->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + hb->addWidget(path_pb); + hb->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + vb->addLayout(hb); + break; + } + case PREF_COLOR: + { + // XXX - Not needed yet. When it is needed we can add a label + QFrame which pops up a + // color picker similar to the Font and Colors prefs. + break; + } + case PREF_PROTO_TCP_SNDAMB_ENUM: + { + const enum_val_t *ev; + ev = prefs_get_enumvals(pref); + if (!ev || !ev->description) + return 0; + + if (prefs_get_enum_radiobuttons(pref)) { + QLabel *label = new QLabel(prefs_get_title(pref)); + label->setToolTip(tooltip); + vb->addWidget(label); + QButtonGroup *enum_bg = new QButtonGroup(vb); + while (ev->description) { + QRadioButton *enum_rb = new QRadioButton(title_to_shortcut(ev->description)); + enum_rb->setToolTip(tooltip); + QStyleOption style_opt; + enum_rb->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + enum_rb->setStyleSheet(QString( + "QRadioButton {" + " margin-left: %1px;" + "}" + ) + .arg(enum_rb->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left())); + enum_bg->addButton(enum_rb, ev->value); + vb->addWidget(enum_rb); + ev++; + } + } else { + QHBoxLayout *hb = new QHBoxLayout(); + QComboBox *enum_cb = new QComboBox(); + enum_cb->setToolTip(tooltip); + enum_cb->setProperty(pref_prop_, VariantPointer::asQVariant(pref)); + for (ev = prefs_get_enumvals(pref); ev && ev->description; ev++) { + enum_cb->addItem(ev->description, QVariant(ev->value)); + } + QLabel * lbl = new QLabel(prefs_get_title(pref)); + lbl->setToolTip(tooltip); + hb->addWidget(lbl); + hb->addWidget(enum_cb); + hb->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + vb->addLayout(hb); + } + break; + } + default: + break; + } + return 0; +} + +} // extern "C" + +ModulePreferencesScrollArea::ModulePreferencesScrollArea(module_t *module, QWidget *parent) : + QScrollArea(parent), + ui(new Ui::ModulePreferencesScrollArea), + module_(module) +{ + ui->setupUi(this); + + if (!module) return; + + /* Show the preference's description at the top of the page */ + QFont font; + font.setBold(TRUE); + QLabel *label = new QLabel(module->description); + label->setFont(font); + ui->verticalLayout->addWidget(label); + + prefSearchData searchData; + searchData.layout = ui->verticalLayout; + searchData.moduleName = module->name; + + /* Add items for each of the preferences */ + prefs_pref_foreach(module, pref_show, &searchData); + + foreach (QLineEdit *le, findChildren()) { + pref_t *pref = VariantPointer::asPtr(le->property(pref_prop_)); + if (!pref) continue; + + switch (prefs_get_type(pref)) { + case PREF_DECODE_AS_UINT: + connect(le, &QLineEdit::textEdited, this, &ModulePreferencesScrollArea::uintLineEditTextEdited); + break; + case PREF_UINT: + connect(le, &QLineEdit::textEdited, this, &ModulePreferencesScrollArea::uintLineEditTextEdited); + break; + case PREF_STRING: + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + case PREF_PASSWORD: + connect(le, &QLineEdit::textEdited, this, &ModulePreferencesScrollArea::stringLineEditTextEdited); + break; + case PREF_RANGE: + case PREF_DECODE_AS_RANGE: + connect(le, &QLineEdit::textEdited, this, &ModulePreferencesScrollArea::rangeSyntaxLineEditTextEdited); + break; + default: + break; + } + } + + foreach (QCheckBox *cb, findChildren()) { + pref_t *pref = VariantPointer::asPtr(cb->property(pref_prop_)); + if (!pref) continue; + + if (prefs_get_type(pref) == PREF_BOOL) { + connect(cb, &QCheckBox::toggled, this, &ModulePreferencesScrollArea::boolCheckBoxToggled); + } + } + + foreach (QRadioButton *rb, findChildren()) { + pref_t *pref = VariantPointer::asPtr(rb->property(pref_prop_)); + if (!pref) continue; + + if (prefs_get_type(pref) == PREF_ENUM && prefs_get_enum_radiobuttons(pref)) { + connect(rb, &QRadioButton::toggled, this, &ModulePreferencesScrollArea::enumRadioButtonToggled); + } + } + + foreach (QComboBox *combo, findChildren()) { + pref_t *pref = VariantPointer::asPtr(combo->property(pref_prop_)); + if (!pref) continue; + + if (prefs_get_type(pref) == PREF_ENUM && !prefs_get_enum_radiobuttons(pref)) { + connect(combo, static_cast(&QComboBox::currentIndexChanged), + this, &ModulePreferencesScrollArea::enumComboBoxCurrentIndexChanged); + } + } + + foreach (QComboBox *combo, findChildren()) { + pref_t *pref = VariantPointer::asPtr(combo->property(pref_prop_)); + if (!pref) continue; + + if (prefs_get_type(pref) == PREF_PROTO_TCP_SNDAMB_ENUM && !prefs_get_enum_radiobuttons(pref)) { + connect(combo, static_cast(&QComboBox::currentIndexChanged), + this, &ModulePreferencesScrollArea::enumComboBoxCurrentIndexChanged_PROTO_TCP); + } + } + + foreach (QPushButton *pb, findChildren()) { + pref_t *pref = VariantPointer::asPtr(pb->property(pref_prop_)); + if (!pref) continue; + + switch (prefs_get_type(pref)) { + case PREF_UAT: + connect(pb, &QPushButton::clicked, this, &ModulePreferencesScrollArea::uatPushButtonClicked); + break; + case PREF_SAVE_FILENAME: + connect(pb, &QPushButton::clicked, this, &ModulePreferencesScrollArea::saveFilenamePushButtonClicked); + break; + case PREF_OPEN_FILENAME: + connect(pb, &QPushButton::clicked, this, &ModulePreferencesScrollArea::openFilenamePushButtonClicked); + break; + case PREF_DIRNAME: + connect(pb, &QPushButton::clicked, this, &ModulePreferencesScrollArea::dirnamePushButtonClicked); + break; + } + } + + ui->verticalLayout->addSpacerItem(new QSpacerItem(10, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); +} + +ModulePreferencesScrollArea::~ModulePreferencesScrollArea() +{ + delete ui; +} + +void ModulePreferencesScrollArea::showEvent(QShowEvent *) +{ + updateWidgets(); +} + +void ModulePreferencesScrollArea::resizeEvent(QResizeEvent *evt) +{ + QScrollArea::resizeEvent(evt); + + if (verticalScrollBar()->isVisible()) { + setFrameStyle(QFrame::StyledPanel); + } else { + setFrameStyle(QFrame::NoFrame); + } +} + +void ModulePreferencesScrollArea::updateWidgets() +{ + foreach (QLineEdit *le, findChildren()) { + pref_t *pref = VariantPointer::asPtr(le->property(pref_prop_)); + if (!pref) continue; + + le->setText(gchar_free_to_qstring(prefs_pref_to_str(pref, pref_stashed)).remove(QRegularExpression("\n\t"))); + } + + foreach (QCheckBox *cb, findChildren()) { + pref_t *pref = VariantPointer::asPtr(cb->property(pref_prop_)); + if (!pref) continue; + + if (prefs_get_type(pref) == PREF_BOOL) { + cb->setChecked(prefs_get_bool_value(pref, pref_stashed)); + } + } + + foreach (QRadioButton *enum_rb, findChildren()) { + pref_t *pref = VariantPointer::asPtr(enum_rb->property(pref_prop_)); + if (!pref) continue; + + QButtonGroup *enum_bg = enum_rb->group(); + if (!enum_bg) continue; + + if (prefs_get_type(pref) == PREF_ENUM && prefs_get_enum_radiobuttons(pref)) { + if (prefs_get_enum_value(pref, pref_stashed) == enum_bg->id(enum_rb)) { + enum_rb->setChecked(true); + } + } + } + + foreach (QComboBox *enum_cb, findChildren()) { + pref_t *pref = VariantPointer::asPtr(enum_cb->property(pref_prop_)); + if (!pref) continue; + + if (prefs_get_type(pref) == PREF_ENUM && !prefs_get_enum_radiobuttons(pref)) { + for (int i = 0; i < enum_cb->count(); i++) { + if (prefs_get_enum_value(pref, pref_stashed) == enum_cb->itemData(i).toInt()) { + enum_cb->setCurrentIndex(i); + } + } + } + + if (prefs_get_type(pref) == PREF_PROTO_TCP_SNDAMB_ENUM && !prefs_get_enum_radiobuttons(pref)) { + MainWindow* topWidget = dynamic_cast (mainApp->mainWindow()); + /* Ensure there is one unique or multiple selections. See issue 18642 */ + if (topWidget->hasSelection() || topWidget->hasUniqueSelection()) { + frame_data * fdata = topWidget->frameDataForRow((topWidget->selectedRows()).at(0)); + enum_cb->setCurrentIndex(fdata->tcp_snd_manual_analysis); + } + } + } +} + +void ModulePreferencesScrollArea::uintLineEditTextEdited(const QString &new_str) +{ + QLineEdit *uint_le = qobject_cast(sender()); + if (!uint_le) return; + + pref_t *pref = VariantPointer::asPtr(uint_le->property(pref_prop_)); + if (!pref) return; + + bool ok; + uint new_uint = new_str.toUInt(&ok, 0); + if (ok) { + prefs_set_uint_value(pref, new_uint, pref_stashed); + } +} + +void ModulePreferencesScrollArea::boolCheckBoxToggled(bool checked) +{ + QCheckBox *bool_cb = qobject_cast(sender()); + if (!bool_cb) return; + + pref_t *pref = VariantPointer::asPtr(bool_cb->property(pref_prop_)); + if (!pref) return; + + prefs_set_bool_value(pref, checked, pref_stashed); +} + +void ModulePreferencesScrollArea::enumRadioButtonToggled(bool checked) +{ + if (!checked) return; + QRadioButton *enum_rb = qobject_cast(sender()); + if (!enum_rb) return; + + QButtonGroup *enum_bg = enum_rb->group(); + if (!enum_bg) return; + + pref_t *pref = VariantPointer::asPtr(enum_rb->property(pref_prop_)); + if (!pref) return; + + if (enum_bg->checkedId() >= 0) { + prefs_set_enum_value(pref, enum_bg->checkedId(), pref_stashed); + } +} + +void ModulePreferencesScrollArea::enumComboBoxCurrentIndexChanged(int index) +{ + QComboBox *enum_cb = qobject_cast(sender()); + if (!enum_cb) return; + + pref_t *pref = VariantPointer::asPtr(enum_cb->property(pref_prop_)); + if (!pref) return; + + prefs_set_enum_value(pref, enum_cb->itemData(index).toInt(), pref_stashed); +} + +void ModulePreferencesScrollArea::stringLineEditTextEdited(const QString &new_str) +{ + QLineEdit *string_le = qobject_cast(sender()); + if (!string_le) return; + + pref_t *pref = VariantPointer::asPtr(string_le->property(pref_prop_)); + if (!pref) return; + + prefs_set_string_value(pref, new_str.toStdString().c_str(), pref_stashed); +} + +void ModulePreferencesScrollArea::rangeSyntaxLineEditTextEdited(const QString &new_str) +{ + SyntaxLineEdit *range_se = qobject_cast(sender()); + if (!range_se) return; + + pref_t *pref = VariantPointer::asPtr(range_se->property(pref_prop_)); + if (!pref) return; + + if (prefs_set_stashed_range_value(pref, new_str.toUtf8().constData())) { + if (new_str.isEmpty()) { + range_se->setSyntaxState(SyntaxLineEdit::Empty); + } else { + range_se->setSyntaxState(SyntaxLineEdit::Valid); + } + } else { + range_se->setSyntaxState(SyntaxLineEdit::Invalid); + } +} + +void ModulePreferencesScrollArea::uatPushButtonClicked() +{ + QPushButton *uat_pb = qobject_cast(sender()); + if (!uat_pb) return; + + pref_t *pref = VariantPointer::asPtr(uat_pb->property(pref_prop_)); + if (!pref) return; + + UatDialog *uat_dlg = new UatDialog(this, prefs_get_uat_value(pref)); + uat_dlg->setWindowModality(Qt::ApplicationModal); + uat_dlg->setAttribute(Qt::WA_DeleteOnClose); + uat_dlg->show(); +} + +void ModulePreferencesScrollArea::saveFilenamePushButtonClicked() +{ + QPushButton *filename_pb = qobject_cast(sender()); + if (!filename_pb) return; + + pref_t *pref = VariantPointer::asPtr(filename_pb->property(pref_prop_)); + if (!pref) return; + + QString filename = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(prefs_get_title(pref)), + prefs_get_string_value(pref, pref_stashed)); + + if (!filename.isEmpty()) { + prefs_set_string_value(pref, QDir::toNativeSeparators(filename).toStdString().c_str(), pref_stashed); + updateWidgets(); + } +} + +void ModulePreferencesScrollArea::openFilenamePushButtonClicked() +{ + QPushButton *filename_pb = qobject_cast(sender()); + if (!filename_pb) return; + + pref_t *pref = VariantPointer::asPtr(filename_pb->property(pref_prop_)); + if (!pref) return; + + QString filename = WiresharkFileDialog::getOpenFileName(this, mainApp->windowTitleString(prefs_get_title(pref)), + prefs_get_string_value(pref, pref_stashed)); + if (!filename.isEmpty()) { + prefs_set_string_value(pref, QDir::toNativeSeparators(filename).toStdString().c_str(), pref_stashed); + updateWidgets(); + } +} + +void ModulePreferencesScrollArea::dirnamePushButtonClicked() +{ + QPushButton *dirname_pb = qobject_cast(sender()); + if (!dirname_pb) return; + + pref_t *pref = VariantPointer::asPtr(dirname_pb->property(pref_prop_)); + if (!pref) return; + + QString dirname = WiresharkFileDialog::getExistingDirectory(this, mainApp->windowTitleString(prefs_get_title(pref)), + prefs_get_string_value(pref, pref_stashed)); + + if (!dirname.isEmpty()) { + prefs_set_string_value(pref, QDir::toNativeSeparators(dirname).toStdString().c_str(), pref_stashed); + updateWidgets(); + } +} + +/* + * Dedicated event handling for TCP SEQ Analysis overriding. + */ +void ModulePreferencesScrollArea::enumComboBoxCurrentIndexChanged_PROTO_TCP(int index) +{ + QComboBox *enum_cb = qobject_cast(sender()); + if (!enum_cb) return; + + pref_t *pref = VariantPointer::asPtr(enum_cb->property(pref_prop_)); + if (!pref) return; + + MainWindow* topWidget = dynamic_cast (mainApp->mainWindow()); + + // method 1 : apply to one single packet + /* frame_data * fdata = topWidget->frameDataForRow((topWidget->selectedRows()).at(0)); + fdata->tcp_snd_manual_analysis = enum_cb->itemData(index).toInt();*/ + + // method 2 : we can leverage the functionality by allowing multiple selections + QList rows = topWidget->selectedRows(); + foreach (int row, rows) { + frame_data * fdata = topWidget->frameDataForRow(row); + fdata->tcp_snd_manual_analysis = enum_cb->itemData(index).toInt(); + } + + prefs_set_enum_value(pref, enum_cb->itemData(index).toInt(), pref_current); + //prefs_set_enum_value(pref, enum_cb->itemData(index).toInt(), pref_stashed); + updateWidgets(); +} diff --git a/ui/qt/module_preferences_scroll_area.h b/ui/qt/module_preferences_scroll_area.h new file mode 100644 index 00000000..f3da516b --- /dev/null +++ b/ui/qt/module_preferences_scroll_area.h @@ -0,0 +1,59 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MODULE_PREFERENCES_SCROLL_AREA_H +#define MODULE_PREFERENCES_SCROLL_AREA_H + +#include + +#include + +#include +#include + +#include + +namespace Ui { +class ModulePreferencesScrollArea; +} + +class ModulePreferencesScrollArea : public QScrollArea +{ + Q_OBJECT + +public: + explicit ModulePreferencesScrollArea(module_t *module, QWidget *parent = 0); + ~ModulePreferencesScrollArea(); + const QString name() const { return QString(module_->name); } + +protected: + void showEvent(QShowEvent *); + void resizeEvent(QResizeEvent *evt); + +private: + Ui::ModulePreferencesScrollArea *ui; + + module_t *module_; + void updateWidgets(); + +private slots: + void uintLineEditTextEdited(const QString &new_str); + void boolCheckBoxToggled(bool checked); + void enumRadioButtonToggled(bool checked); + void enumComboBoxCurrentIndexChanged(int index); + void stringLineEditTextEdited(const QString &new_str); + void rangeSyntaxLineEditTextEdited(const QString &new_str); + void uatPushButtonClicked(); + void saveFilenamePushButtonClicked(); + void openFilenamePushButtonClicked(); + void dirnamePushButtonClicked(); + void enumComboBoxCurrentIndexChanged_PROTO_TCP(int index); +}; + +#endif // MODULE_PREFERENCES_SCROLL_AREA_H diff --git a/ui/qt/module_preferences_scroll_area.ui b/ui/qt/module_preferences_scroll_area.ui new file mode 100644 index 00000000..0286534f --- /dev/null +++ b/ui/qt/module_preferences_scroll_area.ui @@ -0,0 +1,41 @@ + + + ModulePreferencesScrollArea + + + + 0 + 0 + 400 + 300 + + + + ScrollArea + + + QFrame::NoFrame + + + QFrame::Plain + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 400 + 300 + + + + + + + diff --git a/ui/qt/mtp3_summary_dialog.cpp b/ui/qt/mtp3_summary_dialog.cpp new file mode 100644 index 00000000..e5460fd9 --- /dev/null +++ b/ui/qt/mtp3_summary_dialog.cpp @@ -0,0 +1,387 @@ +/* mtp3_summary_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "mtp3_summary_dialog.h" +#include + +#include "config.h" + +#include + +#include + +#include + +#include "wsutil/utf8_entities.h" + +#include "ui/capture_globals.h" +#include "ui/simple_dialog.h" +#include "ui/summary.h" + +#include + +#include + +typedef struct _mtp3_stat_si_code_t { + int num_msus; + int size; +} mtp3_stat_si_code_t; + +typedef struct _mtp3_stat_t { + mtp3_addr_pc_t addr_opc; + mtp3_addr_pc_t addr_dpc; + mtp3_stat_si_code_t mtp3_si_code[MTP3_NUM_SI_CODE]; +} mtp3_stat_t; + +#define MTP3_MAX_NUM_OPC_DPC 50 + +static mtp3_stat_t mtp3_stat[MTP3_MAX_NUM_OPC_DPC]; +static size_t mtp3_num_used; + +Mtp3SummaryDialog::Mtp3SummaryDialog(QWidget &parent, CaptureFile &capture_file) : + WiresharkDialog(parent, capture_file), + ui(new Ui::Mtp3SummaryDialog) +{ + ui->setupUi(this); + + setWindowSubtitle(tr("MTP3 Summary")); + updateWidgets(); +} + +Mtp3SummaryDialog::~Mtp3SummaryDialog() +{ + delete ui; +} + +QString Mtp3SummaryDialog::summaryToHtml() +{ + summary_tally summary; + memset(&summary, 0, sizeof(summary_tally)); + + QString section_tmpl; + QString table_begin, table_end; + QString table_row_begin, table_ul_row_begin, table_row_end; + QString table_vheader_tmpl, table_hheader15_tmpl, table_hheader25_tmpl; + QString table_data_tmpl; + + section_tmpl = "

%1

\n"; + table_begin = "

\n"; + table_end = "

\n"; + table_row_begin = "\n"; + table_ul_row_begin = "\n"; + table_row_end = "\n"; + table_vheader_tmpl = "%1:"; // looked odd + table_hheader15_tmpl = "%1"; + table_hheader25_tmpl = "%1"; + table_data_tmpl = "%1"; + + if (cap_file_.isValid()) { + /* initial computations */ + summary_fill_in(cap_file_.capFile(), &summary); +#ifdef HAVE_LIBPCAP + summary_fill_in_capture(cap_file_.capFile(), &global_capture_opts, &summary); +#endif + } + + QString summary_str; + QTextStream out(&summary_str); + + // File Section + out << section_tmpl.arg(tr("File")); + out << table_begin; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Name")) + << table_data_tmpl.arg(summary.filename) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Length")) + << table_data_tmpl.arg(file_size_to_qstring(summary.file_length)) + << table_row_end; + + QString format_str = wtap_file_type_subtype_description(summary.file_type); + const char *compression_type_description = wtap_compression_type_description(summary.compression_type); + if (compression_type_description != NULL) { + format_str += QString(" (%1)").arg(compression_type_description); + } + out << table_row_begin + << table_vheader_tmpl.arg(tr("Format")) + << table_data_tmpl.arg(format_str) + << table_row_end; + + if (summary.snap != 0) { + out << table_row_begin + << table_vheader_tmpl.arg(tr("Snapshot length")) + << table_data_tmpl.arg(summary.snap) + << table_row_end; + } + + out << table_end; + + // Data Section + out << section_tmpl.arg(tr("Data")); + out << table_begin; + + if (summary.packet_count_ts == summary.packet_count && + summary.packet_count >= 1) + { + // start time + out << table_row_begin + << table_vheader_tmpl.arg(tr("First packet")) + << table_data_tmpl.arg(time_t_to_qstring((time_t)summary.start_time)) + << table_row_end; + + // stop time + out << table_row_begin + << table_vheader_tmpl.arg(tr("Last packet")) + << table_data_tmpl.arg(time_t_to_qstring((time_t)summary.stop_time)) + << table_row_end; + + // elapsed seconds (capture duration) + if (summary.packet_count_ts > 1) + { + /* elapsed seconds */ + QString elapsed_str; + unsigned int elapsed_time = (unsigned int)summary.elapsed_time; + if (elapsed_time/86400) + { + elapsed_str = QString("%1 days ").arg(elapsed_time / 86400); + } + + elapsed_str += QString("%1:%2:%3") + .arg(elapsed_time % 86400 / 3600, 2, 10, QChar('0')) + .arg(elapsed_time % 3600 / 60, 2, 10, QChar('0')) + .arg(elapsed_time % 60, 2, 10, QChar('0')); + out << table_row_begin + << table_vheader_tmpl.arg(tr("Elapsed")) + << table_data_tmpl.arg(elapsed_str) + << table_row_end; + } + } + + // count + out << table_row_begin + << table_vheader_tmpl.arg(tr("Packets")) + << table_data_tmpl.arg(summary.packet_count) + << table_row_end; + + out << table_end; + + QString n_a = UTF8_EM_DASH; + int total_msus = 0; + int total_bytes = 0; + double seconds = summary.stop_time - summary.start_time; + + // SI Section + out << section_tmpl.arg(tr("Service Indicator (SI) Totals")); + out << table_begin; + + out << table_row_begin + << table_hheader25_tmpl.arg(tr("SI")) + << table_hheader15_tmpl.arg(tr("MSUs")) + << table_hheader15_tmpl.arg(tr("MSUs/s")) + << table_hheader15_tmpl.arg(tr("Bytes")) + << table_hheader15_tmpl.arg(tr("Bytes/MSU")) + << table_hheader15_tmpl.arg(tr("Bytes/s")) + << table_row_end; + + for (size_t ws_si_code = 0; ws_si_code < MTP3_NUM_SI_CODE; ws_si_code++) { + int si_msus = 0; + int si_bytes = 0; + QString msus_s_str = n_a; + QString bytes_msu_str = n_a; + QString bytes_s_str = n_a; + + for (size_t stat_idx = 0; stat_idx < mtp3_num_used; stat_idx++) { + si_msus += mtp3_stat[stat_idx].mtp3_si_code[ws_si_code].num_msus; + si_bytes += mtp3_stat[stat_idx].mtp3_si_code[ws_si_code].size; + } + total_msus += si_msus; + total_bytes += si_bytes; + + if (seconds > 0) { + msus_s_str = QString("%1").arg(si_msus / seconds, 1, 'f', 1); + bytes_s_str = QString("%1").arg(si_bytes / seconds, 1, 'f', 1); + } + + if (si_msus > 0) { + bytes_msu_str = QString("%1").arg((double) si_bytes / si_msus, 1, 'f', 1); + } + + out << table_row_begin + << table_data_tmpl.arg(mtp3_service_indicator_code_short_vals[ws_si_code].strptr) + << table_data_tmpl.arg(si_msus) + << table_data_tmpl.arg(msus_s_str) + << table_data_tmpl.arg(si_bytes) + << table_data_tmpl.arg(bytes_msu_str) + << table_data_tmpl.arg(bytes_s_str) + << table_row_end; + } + + out << table_end; + + // Totals Section + + QString total_msus_s_str = n_a; + QString total_bytes_msu_str = n_a; + QString total_bytes_s_str = n_a; + + if (seconds > 0) { + total_msus_s_str = QString("%1").arg(total_msus / seconds, 1, 'f', 1); + total_bytes_s_str = QString("%1").arg(total_bytes / seconds, 1, 'f', 1); + } + if (total_msus > 0) { + total_bytes_msu_str = QString("%1").arg((double) total_bytes / total_msus, 1, 'f', 1); + } + + out << section_tmpl.arg(tr("Totals")); + out << table_begin; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Total MSUs")) + << table_data_tmpl.arg(total_msus) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("MSUs/s")) + << table_data_tmpl.arg(total_msus_s_str) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Total Bytes")) + << table_data_tmpl.arg(total_bytes) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Average Bytes/MSU")) + << table_data_tmpl.arg(total_bytes_msu_str) + << table_row_end; + + out << table_row_begin + << table_vheader_tmpl.arg(tr("Average Bytes/s")) + << table_data_tmpl.arg(total_bytes_s_str) + << table_row_end; + + out << table_end; + + return summary_str; +} + +void Mtp3SummaryDialog::updateWidgets() +{ + ui->summaryTextEdit->setHtml(summaryToHtml()); + + WiresharkDialog::updateWidgets(); +} + +extern "C" { + +void register_tap_listener_qt_mtp3_summary(void); + +static void +mtp3_summary_reset( + void *tapdata) +{ + mtp3_stat_t (*stat_p)[MTP3_MAX_NUM_OPC_DPC] = (mtp3_stat_t(*)[MTP3_MAX_NUM_OPC_DPC])tapdata; + + mtp3_num_used = 0; + memset(stat_p, 0, MTP3_MAX_NUM_OPC_DPC * sizeof(mtp3_stat_t)); +} + + +static tap_packet_status +mtp3_summary_packet( + void *tapdata, + packet_info *, + epan_dissect_t *, + const void *data, + tap_flags_t) +{ + mtp3_stat_t (*stat_p)[MTP3_MAX_NUM_OPC_DPC] = (mtp3_stat_t(*)[MTP3_MAX_NUM_OPC_DPC])tapdata; + const mtp3_tap_rec_t *data_p = (const mtp3_tap_rec_t *)data; + size_t i; + + if (data_p->mtp3_si_code >= MTP3_NUM_SI_CODE) + { + /* + * we thought this si_code was not used ? + * is MTP3_NUM_SI_CODE out of date ? + * XXX - if this is an error, report it and return TAP_PACKET_FAILED. + */ + return(TAP_PACKET_DONT_REDRAW); + } + + /* + * look for opc/dpc pair + */ + i = 0; + while (i < mtp3_num_used) + { + if (memcmp(&data_p->addr_opc, &(*stat_p)[i].addr_opc, sizeof(mtp3_addr_pc_t)) == 0) + { + if (memcmp(&data_p->addr_dpc, &(*stat_p)[i].addr_dpc, sizeof(mtp3_addr_pc_t)) == 0) + { + break; + } + } + + i++; + } + + if (i == mtp3_num_used) + { + if (mtp3_num_used == MTP3_MAX_NUM_OPC_DPC) + { + /* + * too many + * XXX - report an error and return TAP_PACKET_FAILED? + */ + return(TAP_PACKET_DONT_REDRAW); + } + + mtp3_num_used++; + } + + (*stat_p)[i].addr_opc = data_p->addr_opc; + (*stat_p)[i].addr_dpc = data_p->addr_dpc; + (*stat_p)[i].mtp3_si_code[data_p->mtp3_si_code].num_msus++; + (*stat_p)[i].mtp3_si_code[data_p->mtp3_si_code].size += data_p->size; + + return(TAP_PACKET_REDRAW); +} + +void +register_tap_listener_qt_mtp3_summary(void) +{ + GString *err_p; + + memset((void *) &mtp3_stat, 0, sizeof(mtp3_stat)); + + err_p = + register_tap_listener("mtp3", &mtp3_stat, NULL, 0, + mtp3_summary_reset, + mtp3_summary_packet, + NULL, + NULL); + + if (err_p != NULL) + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_p->str); + g_string_free(err_p, TRUE); + + exit(1); + } +} + +} // extern "C" diff --git a/ui/qt/mtp3_summary_dialog.h b/ui/qt/mtp3_summary_dialog.h new file mode 100644 index 00000000..31380c97 --- /dev/null +++ b/ui/qt/mtp3_summary_dialog.h @@ -0,0 +1,40 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MTP3_SUMMARY_DIALOG_H +#define MTP3_SUMMARY_DIALOG_H + +#include "wireshark_dialog.h" + +namespace Ui { +class Mtp3SummaryDialog; +} + +class Mtp3SummaryDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit Mtp3SummaryDialog(QWidget &parent, CaptureFile& capture_file); + ~Mtp3SummaryDialog(); + +private: + Ui::Mtp3SummaryDialog *ui; + + QString summaryToHtml(); + +private slots: + void updateWidgets(); +}; + +#endif // MTP3_SUMMARY_DIALOG_H diff --git a/ui/qt/mtp3_summary_dialog.ui b/ui/qt/mtp3_summary_dialog.ui new file mode 100644 index 00000000..168a2841 --- /dev/null +++ b/ui/qt/mtp3_summary_dialog.ui @@ -0,0 +1,71 @@ + + + Mtp3SummaryDialog + + + + 0 + 0 + 640 + 420 + + + + Dialog + + + + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + Mtp3SummaryDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Mtp3SummaryDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/multicast_statistics_dialog.cpp b/ui/qt/multicast_statistics_dialog.cpp new file mode 100644 index 00000000..cca74783 --- /dev/null +++ b/ui/qt/multicast_statistics_dialog.cpp @@ -0,0 +1,510 @@ +/* multicast_statistics_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "multicast_statistics_dialog.h" + +#include +#include +#include +#include + +#include +#include +#include "ui/simple_dialog.h" +#include + +#include "main_application.h" + +enum { + col_src_addr_, + col_src_port_, + col_dst_addr_, + col_dst_port_, + col_packets_, + col_packets_s_, + col_avg_bw_, + col_max_bw_, + col_max_burst_, + col_burst_alarms_, + col_max_buffers_, + col_buffer_alarms_ +}; + +enum { + mcast_table_type_ = 1000 +}; + +class MulticastStatTreeWidgetItem : public QTreeWidgetItem +{ +public: + MulticastStatTreeWidgetItem(QTreeWidget *parent) : + QTreeWidgetItem (parent, mcast_table_type_) + { + clear_address(&src_addr_); + clear_address(&dst_addr_); + src_port_ = 0; + dst_port_ = 0; + num_packets_ = 0; + avg_pps_ = 0; + avg_bw_ = 0; + max_bw_ = 0; + top_burst_size_ = 0; + num_bursts_ = 0; + top_buff_usage_ = 0; + num_buff_alarms_ = 0; + } + + void updateStreamInfo(const mcast_stream_info_t *stream_info) { + copy_address(&src_addr_, &stream_info->src_addr); + src_port_ = stream_info->src_port; + copy_address(&dst_addr_, &stream_info->dest_addr); + dst_port_ = stream_info->dest_port; + num_packets_ = stream_info->npackets; + avg_pps_ = stream_info->apackets; + avg_bw_ = stream_info->average_bw; + max_bw_ = stream_info->element.maxbw; + top_burst_size_ = stream_info->element.topburstsize; + num_bursts_ = stream_info->element.numbursts; + top_buff_usage_ = stream_info->element.topbuffusage; + num_buff_alarms_ = stream_info->element.numbuffalarms; + + draw(); + } + + void draw() { + setText(col_src_addr_, address_to_qstring(&src_addr_)); + setText(col_src_port_, QString::number(src_port_)); + setText(col_dst_addr_, address_to_qstring(&dst_addr_)); + setText(col_dst_port_, QString::number(dst_port_)); + setText(col_packets_, QString::number(num_packets_)); + setText(col_packets_s_, QString::number(avg_pps_, 'f', 2)); + setText(col_avg_bw_, bits_s_to_qstring(avg_bw_)); + setText(col_max_bw_, bits_s_to_qstring(max_bw_)); + setText(col_max_burst_, QString("%1 / %2ms").arg(top_burst_size_).arg(mcast_stream_burstint)); + setText(col_burst_alarms_, QString::number(num_bursts_)); + setText(col_max_buffers_, bits_s_to_qstring(top_buff_usage_)); + setText(col_buffer_alarms_, QString::number(num_buff_alarms_)); + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != mcast_table_type_) return QTreeWidgetItem::operator< (other); + const MulticastStatTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case col_src_addr_: + return cmp_address(&src_addr_, &other_row->src_addr_) < 0; + case col_src_port_: + return src_port_ < other_row->src_port_; + case col_dst_addr_: + return cmp_address(&dst_addr_, &other_row->dst_addr_) < 0; + case col_dst_port_: + return dst_port_ < other_row->dst_port_; + case col_packets_: + return num_packets_ < other_row->num_packets_; + case col_packets_s_: + return avg_pps_ < other_row->avg_pps_; + case col_avg_bw_: + return avg_bw_ < other_row->avg_bw_; + case col_max_bw_: + return max_bw_ < other_row->max_bw_; + case col_max_burst_: + return top_burst_size_ < other_row->top_burst_size_; + case col_burst_alarms_: + return num_bursts_ < other_row->num_bursts_; + case col_max_buffers_: + return top_buff_usage_ < other_row->top_buff_usage_; + case col_buffer_alarms_: + return num_buff_alarms_ < other_row->num_buff_alarms_; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList rowData() { + return QList() + << address_to_qstring(&src_addr_) << src_port_ + << address_to_qstring(&dst_addr_) << dst_port_ + << num_packets_ << avg_pps_ + << avg_bw_ << max_bw_ + << top_burst_size_ << num_bursts_ + << top_buff_usage_ << num_buff_alarms_; + } + const QString filterExpression() { + QString ip_version; + + if (src_addr_.type == AT_IPv6) ip_version = "v6"; + + const QString filter_expr = QString("(ip%1.src==%2 && udp.srcport==%3 && ip%1.dst==%4 && udp.dstport==%5)") + .arg(ip_version) + .arg(address_to_qstring(&src_addr_)) + .arg(src_port_) + .arg(address_to_qstring(&dst_addr_)) + .arg(dst_port_); + return filter_expr; + } + +private: + address src_addr_; + guint16 src_port_; + address dst_addr_; + guint16 dst_port_; + unsigned num_packets_; + double avg_pps_; + double avg_bw_; + double max_bw_; + int top_burst_size_; + int num_bursts_; + int top_buff_usage_; + int num_buff_alarms_; +}; + +MulticastStatisticsDialog::MulticastStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) : + TapParameterDialog(parent, cf) +{ + setWindowSubtitle(tr("UDP Multicast Streams")); + loadGeometry(parent.width() * 4 / 5, parent.height() * 3 / 4, "MulticastStatisticsDialog"); + + tapinfo_ = new mcaststream_tapinfo_t(); + tapinfo_->user_data = this; + tapinfo_->tap_reset = tapReset; + tapinfo_->tap_draw = tapDraw; + + QStringList header_names = QStringList() + << tr("Source Address") << tr("Source Port") + << tr("Destination Address") << tr("Destination Port") + << tr("Packets") << tr("Packets/s") + << tr("Avg BW (bps)") << tr("Max BW (bps)") + << tr("Max Burst") << tr("Burst Alarms") + << tr("Max Buffers (B)") << tr("Buffer Alarms"); + + statsTreeWidget()->setHeaderLabels(header_names); + + for (int col = 0; col < statsTreeWidget()->columnCount(); col++) { + if (col == col_src_addr_ || col == col_dst_addr_) continue; + statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); + } + + burst_measurement_interval_le_ = new SyntaxLineEdit(this); + burst_alarm_threshold_le_ = new SyntaxLineEdit(this); + buffer_alarm_threshold_le_ = new SyntaxLineEdit(this); + stream_empty_speed_le_ = new SyntaxLineEdit(this); + total_empty_speed_le_ = new SyntaxLineEdit(this); + + int filter_layout_idx = verticalLayout()->indexOf(filterLayout()->widget()); + QGridLayout *param_grid = new QGridLayout(); + int one_em = fontMetrics().height(); + verticalLayout()->insertLayout(filter_layout_idx, param_grid); + + // Label | LineEdit | | Label | LineEdit | | Label | LineEdit + // 0 1 2 3 4 5 6 7 + param_grid->setColumnMinimumWidth(2, one_em * 2); + param_grid->setColumnStretch(2, 1); + param_grid->setColumnMinimumWidth(5, one_em * 2); + param_grid->setColumnStretch(5, 1); + param_grid->addWidget(new QLabel(tr("Burst measurement interval (ms):")), 0, 0, Qt::AlignRight); + param_grid->addWidget(burst_measurement_interval_le_, 0, 1); + param_grid->addWidget(new QLabel(tr("Burst alarm threshold (packets):")), 0, 3, Qt::AlignRight); + param_grid->addWidget(burst_alarm_threshold_le_, 0, 4); + param_grid->addWidget(new QLabel(tr("Buffer alarm threshold (B):")), 0, 6, Qt::AlignRight); + param_grid->addWidget(buffer_alarm_threshold_le_, 0, 7); + + param_grid->addWidget(new QLabel(tr("Stream empty speed (Kb/s):")), 1, 0, Qt::AlignRight); + param_grid->addWidget(stream_empty_speed_le_, 1, 1); + param_grid->addWidget(new QLabel(tr("Total empty speed (Kb/s):")), 1, 3, Qt::AlignRight); + param_grid->addWidget(total_empty_speed_le_, 1, 4); + + burst_measurement_interval_le_->setText(QString::number(mcast_stream_burstint)); + burst_alarm_threshold_le_->setText(QString::number(mcast_stream_trigger)); + buffer_alarm_threshold_le_->setText(QString::number(mcast_stream_bufferalarm)); + stream_empty_speed_le_->setText(QString::number(mcast_stream_emptyspeed)); + total_empty_speed_le_->setText(QString::number(mcast_stream_cumulemptyspeed)); + + line_edits_ = QList() + << burst_measurement_interval_le_ << burst_alarm_threshold_le_ + << buffer_alarm_threshold_le_ << stream_empty_speed_le_ + << total_empty_speed_le_; + + foreach (QWidget *w, line_edits_) { + QLineEdit *line_edit = qobject_cast(w); + line_edit->setMinimumWidth(one_em * 5); + connect(line_edit, &QLineEdit::textEdited, this, &MulticastStatisticsDialog::updateWidgets); + } + + addFilterActions(); + + if (filter) { + setDisplayFilter(filter); + } + + connect(this, &MulticastStatisticsDialog::updateFilter, + this, &MulticastStatisticsDialog::updateMulticastParameters); + + /* Register the tap listener */ + GString * error_string = register_tap_listener_mcast_stream(tapinfo_); + if (error_string != NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "%s", error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + updateWidgets(); +} + +MulticastStatisticsDialog::~MulticastStatisticsDialog() +{ + /* Remove the stream tap listener */ + remove_tap_listener_mcast_stream(tapinfo_); + + /* Clean up memory used by stream tap */ + mcaststream_reset(tapinfo_); + + delete tapinfo_; +} + +void MulticastStatisticsDialog::tapReset(mcaststream_tapinfo_t *tapinfo) +{ + MulticastStatisticsDialog *ms_dlg = dynamic_cast((MulticastStatisticsDialog*)tapinfo->user_data); + if (!ms_dlg || !ms_dlg->statsTreeWidget()) return; + + ms_dlg->statsTreeWidget()->clear(); +} + +void MulticastStatisticsDialog::tapDraw(mcaststream_tapinfo_t *tapinfo) +{ + MulticastStatisticsDialog *ms_dlg = dynamic_cast((MulticastStatisticsDialog*)tapinfo->user_data); + if (!ms_dlg || !ms_dlg->statsTreeWidget()) return; + + //Clear the tree because the list always starts from the beginning + ms_dlg->statsTreeWidget()->clear(); + + // Add missing rows and update stats + int cur_row = 0; + for (GList *cur = g_list_first(tapinfo->strinfo_list); cur; cur = gxx_list_next(cur)) { + mcast_stream_info_t *stream_info = gxx_list_data(mcast_stream_info_t *, cur); + if (!stream_info) continue; + + MulticastStatTreeWidgetItem *ms_ti; + QTreeWidgetItem *ti = ms_dlg->statsTreeWidget()->topLevelItem(cur_row); + if (!ti) { + ms_ti = new MulticastStatTreeWidgetItem(ms_dlg->statsTreeWidget()); + for (int col = 0; col < ms_dlg->statsTreeWidget()->columnCount(); col++) { + if (col == col_src_addr_ || col == col_dst_addr_) continue; + ms_ti->setTextAlignment(col, Qt::AlignRight); + } + } else { + ms_ti = static_cast(ti); + } + + ms_ti->updateStreamInfo(stream_info); + cur_row++; + } +} + +QList MulticastStatisticsDialog::treeItemData(QTreeWidgetItem *ti) const +{ + MulticastStatTreeWidgetItem *ms_ti = dynamic_cast(ti); + if (ms_ti) { + return ms_ti->rowData(); + } + else { + return QList(); + } +} + +const QString MulticastStatisticsDialog::filterExpression() +{ + QString filter_expr; + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + + MulticastStatTreeWidgetItem *ms_ti = static_cast(ti); + filter_expr = ms_ti->filterExpression(); + } + return filter_expr; +} + +void MulticastStatisticsDialog::updateWidgets() +{ + QString hint; + bool enable_apply = true; + bool enable_edits = cap_file_.isValid(); + bool ok = false; + int param; + + param = burst_measurement_interval_le_->text().toUInt(&ok); + if (!ok || param < 1 || param > 1000) { + hint += tr("The burst interval must be between 1 and 1000. "); + enable_apply = false; + burst_measurement_interval_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + burst_measurement_interval_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + param = burst_alarm_threshold_le_->text().toInt(&ok); + if (!ok || param < 1) { + hint += tr("The burst alarm threshold isn't valid. "); + enable_apply = false; + burst_alarm_threshold_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + burst_alarm_threshold_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + param = buffer_alarm_threshold_le_->text().toInt(&ok); + if (!ok || param < 1) { + hint += tr("The buffer alarm threshold isn't valid. "); + enable_apply = false; + buffer_alarm_threshold_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + buffer_alarm_threshold_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + param = stream_empty_speed_le_->text().toInt(&ok); + if (!ok || param < 1 || param > 10000000) { + hint += tr("The stream empty speed should be between 1 and 10000000. "); + enable_apply = false; + stream_empty_speed_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + stream_empty_speed_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + param = total_empty_speed_le_->text().toInt(&ok); + if (!ok || param < 1 || param > 10000000) { + hint += tr("The total empty speed should be between 1 and 10000000. "); + enable_apply = false; + total_empty_speed_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + total_empty_speed_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + foreach (QWidget *line_edit, line_edits_) { + line_edit->setEnabled(enable_edits); + } + + applyFilterButton()->setEnabled(enable_apply); + + if (hint.isEmpty() && tapinfo_->allstreams) { + const QString stats = tr("%1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B") + .arg(statsTreeWidget()->topLevelItemCount()) + .arg(bits_s_to_qstring(tapinfo_->allstreams->average_bw)) + .arg(bits_s_to_qstring(tapinfo_->allstreams->element.maxbw)) + .arg(tapinfo_->allstreams->element.topburstsize) + .arg(mcast_stream_burstint) + .arg(bits_s_to_qstring(tapinfo_->allstreams->element.topbuffusage)); + hint.append(stats); + } + hint.prepend(""); + hint.append(""); + setHint(hint); + TapParameterDialog::updateWidgets(); +} + +void MulticastStatisticsDialog::updateMulticastParameters() +{ + bool ok = false; + int param; + + param = burst_measurement_interval_le_->text().toUInt(&ok); + if (ok && param > 0 && param <= 1000) { + mcast_stream_burstint = (guint16) param; + } + + param = burst_alarm_threshold_le_->text().toInt(&ok); + if (ok) { + mcast_stream_trigger = param; + } + + param = buffer_alarm_threshold_le_->text().toInt(&ok); + if (ok && param > 0) { + mcast_stream_bufferalarm = param; + } + + param = stream_empty_speed_le_->text().toInt(&ok); + if (ok && param > 0 && param <= 10000000) { + mcast_stream_emptyspeed = param; + } + + param = total_empty_speed_le_->text().toInt(&ok); + if (ok && param > 0 && param <= 10000000) { + mcast_stream_cumulemptyspeed = param; + } +} + +void MulticastStatisticsDialog::fillTree() +{ + QList disable_widgets = QList() + << line_edits_ << displayFilterLineEdit() << applyFilterButton(); + + foreach (QWidget *w, disable_widgets) w->setEnabled(false); + + rescan(); + + foreach (QWidget *w, disable_widgets) w->setEnabled(true); + for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) { + statsTreeWidget()->resizeColumnToContents(col); + } + updateWidgets(); +} + +void MulticastStatisticsDialog::rescan() +{ + gboolean was_registered = tapinfo_->is_registered; + if (!tapinfo_->is_registered) + register_tap_listener_mcast_stream(tapinfo_); + + cf_retap_packets(cap_file_.capFile()); + + if (!was_registered) + remove_tap_listener_mcast_stream(tapinfo_); + + tapDraw(tapinfo_); +} + +void MulticastStatisticsDialog::captureFileClosing() +{ + /* Remove the stream tap listener */ + remove_tap_listener_mcast_stream(tapinfo_); + + WiresharkDialog::captureFileClosing(); +} + +// Stat command + args + +static void +multicast_statistics_init(const char *args, void*) { + QStringList args_l = QString(args).split(','); + QByteArray filter; + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(",").toUtf8(); + } + mainApp->emitStatCommandSignal("MulticastStatistics", filter.constData(), NULL); +} + +static stat_tap_ui multicast_statistics_ui = { + REGISTER_STAT_GROUP_GENERIC, + NULL, + "multicast,stat", + multicast_statistics_init, + 0, + NULL +}; + +extern "C" { + +void register_tap_listener_qt_multicast_statistics(void); + +void +register_tap_listener_qt_multicast_statistics(void) +{ + register_stat_tap_ui(&multicast_statistics_ui, NULL); +} + +} diff --git a/ui/qt/multicast_statistics_dialog.h b/ui/qt/multicast_statistics_dialog.h new file mode 100644 index 00000000..88217b50 --- /dev/null +++ b/ui/qt/multicast_statistics_dialog.h @@ -0,0 +1,53 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef MULTICASTSTATISTICSDIALOG_H +#define MULTICASTSTATISTICSDIALOG_H + +#include "tap_parameter_dialog.h" +#include "ui/mcast_stream.h" + +class SyntaxLineEdit; + +class MulticastStatisticsDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + MulticastStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter = NULL); + ~MulticastStatisticsDialog(); + +protected: + void captureFileClosing(); + +private: + struct _mcaststream_tapinfo *tapinfo_; + SyntaxLineEdit *burst_measurement_interval_le_; + SyntaxLineEdit *burst_alarm_threshold_le_; + SyntaxLineEdit *buffer_alarm_threshold_le_; + SyntaxLineEdit *stream_empty_speed_le_; + SyntaxLineEdit *total_empty_speed_le_; + QList line_edits_; + + // Callbacks for register_tap_listener + static void tapReset(mcaststream_tapinfo_t *tapinfo); + static void tapDraw(mcaststream_tapinfo_t *tapinfo); + + void rescan(); + + virtual QList treeItemData(QTreeWidgetItem *ti) const; + virtual const QString filterExpression(); + +private slots: + void updateWidgets(); + void updateMulticastParameters(); + virtual void fillTree(); +}; + +#endif // MULTICASTSTATISTICSDIALOG_H diff --git a/ui/qt/packet_comment_dialog.cpp b/ui/qt/packet_comment_dialog.cpp new file mode 100644 index 00000000..d9dfc7dd --- /dev/null +++ b/ui/qt/packet_comment_dialog.cpp @@ -0,0 +1,44 @@ +/* packet_comment_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "packet_comment_dialog.h" +#include + +#include "main_application.h" + +PacketCommentDialog::PacketCommentDialog(bool isEdit, QWidget *parent, QString comment) : + GeometryStateDialog(parent), + pc_ui_(new Ui::PacketCommentDialog) +{ + + QString title = isEdit + ? tr("Edit Packet Comment") + : tr("Add Packet Comment"); + + pc_ui_->setupUi(this); + loadGeometry(); + setWindowTitle(mainApp->windowTitleString(title)); + + pc_ui_->commentTextEdit->setPlainText(comment); +} + +PacketCommentDialog::~PacketCommentDialog() +{ + delete pc_ui_; +} + +QString PacketCommentDialog::text() +{ + return pc_ui_->commentTextEdit->toPlainText(); +} + +void PacketCommentDialog::on_buttonBox_helpRequested() +{ +// mainApp->helpTopicAction(HELP_PACKET_COMMENT_DIALOG); +} diff --git a/ui/qt/packet_comment_dialog.h b/ui/qt/packet_comment_dialog.h new file mode 100644 index 00000000..114e8345 --- /dev/null +++ b/ui/qt/packet_comment_dialog.h @@ -0,0 +1,37 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PACKET_COMMENT_DIALOG_H +#define PACKET_COMMENT_DIALOG_H + +#include + +#include "geometry_state_dialog.h" + +namespace Ui { +class PacketCommentDialog; +} + +class PacketCommentDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit PacketCommentDialog(bool isEdit, QWidget *parent = 0, QString comment = QString()); + ~PacketCommentDialog(); + QString text(); + +private slots: + void on_buttonBox_helpRequested(); + +private: + Ui::PacketCommentDialog *pc_ui_; +}; + +#endif // PACKET_COMMENT_DIALOG_H diff --git a/ui/qt/packet_comment_dialog.ui b/ui/qt/packet_comment_dialog.ui new file mode 100644 index 00000000..db14c1ad --- /dev/null +++ b/ui/qt/packet_comment_dialog.ui @@ -0,0 +1,67 @@ + + + PacketCommentDialog + + + + 0 + 0 + 400 + 300 + + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PacketCommentDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PacketCommentDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/packet_diagram.cpp b/ui/qt/packet_diagram.cpp new file mode 100644 index 00000000..a5bbc106 --- /dev/null +++ b/ui/qt/packet_diagram.cpp @@ -0,0 +1,828 @@ +/* packet_diagram.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 "main_application.h" + +#include "ui/qt/main_window.h" +#include "ui/qt/capture_file_dialog.h" +#include "ui/qt/utils/proto_node.h" +#include "ui/qt/utils/variant_pointer.h" +#include "ui/recent.h" + +#include +#include +#include +#include +#include + +#if defined(QT_SVG_LIB) && 0 +#include +#include +#include +#endif + +// Item offsets and lengths +//#define DEBUG_PACKET_DIAGRAM 1 + +#ifdef DEBUG_PACKET_DIAGRAM +#include +#endif + +// "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) + { + setFont(mainApp->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) { recent.gui_packet_diagram_field_values = 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 = recent.gui_packet_diagram_field_values ? 2 : 1; + return ((lineHeight() * rows) + (vPadding() * 2)); + } + bool showFields() const { return recent.gui_packet_diagram_field_values; } +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? + 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)), + representation_("Unknown"), + 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::asQVariant(finfo_->fieldInfo())); + representation_ = fi->rep->representation; + } else { + setToolTip(QObject::tr("Gap in dissection")); + } + } + + ~FieldInformationGraphicsItem() + { + delete finfo_; + } + + 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; + if (finfo_->headerInfo().type == FT_NONE) { + label = representation_; + } else { + 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_; + QString representation_; + 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(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(mainApp, &MainApplication::appInitialized, this, &PacketDiagram::connectToMainWindow); + connect(mainApp, &MainApplication::zoomRegularFont, this, &PacketDiagram::setFont); + + resetScene(); +} + +PacketDiagram::~PacketDiagram() +{ + delete layout_; +} + +void PacketDiagram::setRootNode(proto_node *root_node) +{ + // As https://doc.qt.io/qt-5/qgraphicsscene.html#clear says, this + // "Removes and deletes all items from the scene, but otherwise leaves + // the state of the scene unchanged." + // This means that the scene rect grows but doesn't shrink, which is + // useful in our case because it gives us a cheap way to retain our + // scroll position between packets. + scene()->clear(); + selected_field_ = nullptr; + y_pos_ = 0; + + root_node_ = root_node; + if (!isVisible() || !root_node) { + return; + } + + 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; + + if (!cf) { + resetScene(); + } +} + +void PacketDiagram::setFont(const QFont &font) +{ + layout_->setFont(font); + resetScene(false); +} + +void PacketDiagram::selectedFieldChanged(FieldInformation *finfo) +{ + setSelectedField(finfo ? finfo->fieldInfo() : nullptr); +} + +void PacketDiagram::selectedFrameChanged(QList 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); + } +} + +bool PacketDiagram::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + resetScene(false); + break; + default: + break; + + } + return QGraphicsView::event(event); +} + +void PacketDiagram::contextMenuEvent(QContextMenuEvent *event) +{ + if (!event) { + return; + } + + QAction *action; + QMenu *ctx_menu = new QMenu(this); + ctx_menu->setAttribute(Qt::WA_DeleteOnClose); + + 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…")); + 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("…as SVG")); + connect(action, &QAction::triggered, this, &PacketDiagram::copyAsSvgTriggered); +#endif + + ctx_menu->popup(event->globalPos()); +} + +void PacketDiagram::connectToMainWindow() +{ + MainWindow *main_window = qobject_cast(mainApp->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::asPtr(scene()->selectedItems().first()->data(Qt::UserRole)); + } + + if (sel_fi) { + FieldInformation finfo(sel_fi, this); + emit fieldSelected(&finfo); + } else { + emit fieldSelected(nullptr); + } +} + +void PacketDiagram::resetScene(bool reset_root) +{ + // As noted in setRootNode, scene()->clear() doesn't clear everything. + // Do a "hard" clear, which resets our various rects and scroll position. + if (scene()) { + delete scene(); + } + viewport()->update(); + QGraphicsScene *new_scene = new QGraphicsScene(); + setScene(new_scene); + connect(new_scene, &QGraphicsScene::selectionChanged, this, &PacketDiagram::sceneSelectionChanged); + setRootNode(reset_root ? nullptr : root_node_); +} + +struct DiagramItemSpan { + field_info *finfo; + 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 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); + QFontMetrics sfm = QFontMetrics(layout_->smallFont()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + int space_w = sfm.horizontalAdvance(' '); +#else + int space_w = sfm.width(' '); +#endif +#ifdef Q_OS_WIN + // t_item->boundingRect() has a pixel of space on the left on my (gcc) + // Windows VM. + int tl_adjust = 1; +#else + int tl_adjust = 0; +#endif + + 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()); + if (tick_n % 2 == 0) { + t_item->setPos(x + space_w - tl_adjust, y_pos_); + } else { + t_item->setPos(x + bit_width - space_w - t_item->boundingRect().width() - tl_adjust, y_pos_); + } + // Does the placement above look funny on your system? Try + // uncommenting the lines below. + // QGraphicsRectItem *br_item = scene()->addRect(t_item->boundingRect(), QPen(palette().highlight().color())); + // br_item->setPos(t_item->pos()); + } + } + y_pos_ = y_bottom; + x = layout_->hPadding(); + + // Collect our top-level fields + int last_start_bit = -1; + int max_l_y = y_bottom; + QListitem_spans; + 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) { +#ifdef DEBUG_PACKET_DIAGRAM + qDebug() << "Skipping item" << fi->hfinfo->abbrev << start_bit << last_start_bit << length; +#endif + continue; + } + last_start_bit = start_bit; + + if (item_spans.size() > 0) { + DiagramItemSpan prev_span = item_spans.last(); + // Get rid of overlaps. + if (prev_span.start_bit + prev_span.length > start_bit) { +#ifdef DEBUG_PACKET_DIAGRAM + qDebug() << "Resized prev" << prev_span.finfo->hfinfo->abbrev << prev_span.start_bit << prev_span.length << "->" << start_bit - prev_span.start_bit; +#endif + prev_span.length = start_bit - prev_span.start_bit; + } + if (prev_span.length < 1) { +#ifdef DEBUG_PACKET_DIAGRAM + qDebug() << "Removed prev" << prev_span.finfo->hfinfo->abbrev << prev_span.start_bit << prev_span.length; + item_spans.removeLast(); + if (item_spans.size() < 1) { + continue; + } + prev_span = item_spans.last(); +#endif + } + // Fill in gaps. + if (prev_span.start_bit + prev_span.length < start_bit) { +#ifdef DEBUG_PACKET_DIAGRAM + qDebug() << "Adding gap" << prev_span.finfo->hfinfo->abbrev << prev_span.start_bit << prev_span.length << start_bit; +#endif + int gap_start = prev_span.start_bit + prev_span.length; + DiagramItemSpan gap_span = { nullptr, gap_start, start_bit - gap_start }; + item_spans << gap_span; + } + } + + DiagramItemSpan item_span = { cur_item->finfo, start_bit, length }; + item_spans << item_span; + } + + qreal z_value = tl_item->zValue(); + int start_bit = 0; + for (int idx = 0; idx < item_spans.size(); idx++) { + DiagramItemSpan *item_span = &item_spans[idx]; + + int y_off = (start_bit / bits_per_row) * layout_->rowHeight(); + // Stack each item behind the previous one. + z_value -= .01; + FieldInformationGraphicsItem *fi_item = new FieldInformationGraphicsItem(item_span->finfo, start_bit, item_span->length, layout_); + start_bit += fi_item->collapsedLength(); + 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(item); + if (t_item) { + t_item->setBrush(palette().text().color()); + } + QGraphicsLineItem *l_item = qgraphicsitem_cast(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); + } + if (fi && VariantPointer::asPtr(item->data(Qt::UserRole)) == fi) { + fi_item = qgraphicsitem_cast(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_); + /* Viewport needs to be update to avoid residues being shown */ + viewport()->update(); +} + +// 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(mainApp->openDialogInitialDir()); + 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, mainApp->windowTitleString(tr("Save Graph As…")), + 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) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } +} + +void PacketDiagram::copyAsRasterTriggered() +{ + QImage raster_diagram = exportToImage(); + mainApp->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); + mainApp->clipboard()->setMimeData(md); +} +#endif diff --git a/ui/qt/packet_diagram.h b/ui/qt/packet_diagram.h new file mode 100644 index 00000000..9b2373fd --- /dev/null +++ b/ui/qt/packet_diagram.h @@ -0,0 +1,74 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PACKET_DIAGRAM_H +#define PACKET_DIAGRAM_H + +#include + +#include + +#include "cfile.h" + +#include + +#include + +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 frames); + +protected: + virtual bool event(QEvent *event) override; + virtual void contextMenuEvent(QContextMenuEvent *event) override; + +private slots: + void connectToMainWindow(); + void sceneSelectionChanged(); + +private: + void resetScene(bool reset_root = true); + 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 diff --git a/ui/qt/packet_dialog.cpp b/ui/qt/packet_dialog.cpp new file mode 100644 index 00000000..8bf7a3bf --- /dev/null +++ b/ui/qt/packet_dialog.cpp @@ -0,0 +1,211 @@ +/* packet_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "packet_dialog.h" +#include + +#include "file.h" + +#include "epan/column.h" +#include "epan/ftypes/ftypes.h" +#include "epan/prefs.h" +#include "ui/preference_utils.h" + +#include "frame_tvbuff.h" + +#include + +#include "byte_view_tab.h" +#include "proto_tree.h" +#include "main_application.h" + +#include +#include + +// To do: +// - Copy over experimental packet editing code. +// - Fix ElidedText width. + +PacketDialog::PacketDialog(QWidget &parent, CaptureFile &cf, frame_data *fdata) : + WiresharkDialog(parent, cf), + ui(new Ui::PacketDialog), + proto_tree_(NULL), + byte_view_tab_(NULL) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 4 / 5); + ui->hintLabel->setSmallText(); + + wtap_rec_init(&rec_); + ws_buffer_init(&buf_, 1514); + + edt_.session = NULL; + edt_.tvb = NULL; + edt_.tree = NULL; + + memset(&edt_.pi, 0x0, sizeof(edt_.pi)); + + setWindowSubtitle(tr("Packet %1").arg(fdata->num)); + + if (!cf_read_record(cap_file_.capFile(), fdata, &rec_, &buf_)) { + reject(); + return; + } + + /* proto tree, visible. We need a proto tree if there are custom columns */ + epan_dissect_init(&edt_, cap_file_.capFile()->epan, TRUE, TRUE); + col_custom_prime_edt(&edt_, &(cap_file_.capFile()->cinfo)); + + epan_dissect_run(&edt_, cap_file_.capFile()->cd_t, &rec_, + frame_tvbuff_new_buffer(&cap_file_.capFile()->provider, fdata, &buf_), + fdata, &(cap_file_.capFile()->cinfo)); + epan_dissect_fill_in_columns(&edt_, TRUE, TRUE); + + proto_tree_ = new ProtoTree(ui->packetSplitter, &edt_); + // Do not call proto_tree_->setCaptureFile, ProtoTree only needs the + // dissection context. + proto_tree_->setRootNode(edt_.tree); + + byte_view_tab_ = new ByteViewTab(ui->packetSplitter, &edt_); + byte_view_tab_->setCaptureFile(cap_file_.capFile()); + byte_view_tab_->selectedFrameChanged(QList() << 0); + + ui->packetSplitter->setStretchFactor(1, 0); + + QStringList col_parts; + for (int i = 0; i < cap_file_.capFile()->cinfo.num_cols; ++i) { + // ElidedLabel doesn't support rich text / HTML + col_parts << QString("%1: %2") + .arg(get_column_title(i)) + .arg(get_column_text(&cap_file_.capFile()->cinfo, i)); + } + col_info_ = col_parts.join(" " UTF8_MIDDLE_DOT " "); + + ui->hintLabel->setText(col_info_); + + /* Handle preference value correctly */ + Qt::CheckState state = Qt::Checked; + if (!prefs.gui_packet_details_show_byteview) { + state = Qt::Unchecked; + byte_view_tab_->setVisible(false); + } + ui->chkShowByteView->setCheckState(state); + + connect(mainApp, SIGNAL(zoomMonospaceFont(QFont)), + proto_tree_, SLOT(setMonospaceFont(QFont))); + + connect(byte_view_tab_, SIGNAL(fieldSelected(FieldInformation *)), + proto_tree_, SLOT(selectedFieldChanged(FieldInformation *))); + connect(proto_tree_, SIGNAL(fieldSelected(FieldInformation *)), + byte_view_tab_, SLOT(selectedFieldChanged(FieldInformation *))); + + connect(byte_view_tab_, SIGNAL(fieldHighlight(FieldInformation *)), + this, SLOT(setHintText(FieldInformation *))); + connect(byte_view_tab_, &ByteViewTab::fieldSelected, + this, &PacketDialog::setHintTextSelected); + connect(proto_tree_, &ProtoTree::fieldSelected, + this, &PacketDialog::setHintTextSelected); + + connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)), + this, SIGNAL(showProtocolPreferences(QString))); + connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)), + this, SIGNAL(editProtocolPreference(preference*,pref_module*))); + + connect(ui->chkShowByteView, &QCheckBox::stateChanged, this, &PacketDialog::viewVisibilityStateChanged); +} + +PacketDialog::~PacketDialog() +{ + delete ui; + epan_dissect_cleanup(&edt_); + wtap_rec_cleanup(&rec_); + ws_buffer_free(&buf_); +} + +void PacketDialog::captureFileClosing() +{ + QString closed_title = tr("[%1 closed] " UTF8_MIDDLE_DOT " %2") + .arg(cap_file_.fileName()) + .arg(col_info_); + ui->hintLabel->setText(closed_title); + byte_view_tab_->captureFileClosing(); + WiresharkDialog::captureFileClosing(); +} + +void PacketDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_NEW_PACKET_DIALOG); +} + +void PacketDialog::setHintText(FieldInformation * finfo) +{ + QString hint; + + if (finfo) + { + FieldInformation::Position pos = finfo->position(); + QString field_str; + + if (pos.length < 2) { + hint = QString(tr("Byte %1")).arg(pos.start); + } else { + hint = QString(tr("Bytes %1-%2")).arg(pos.start).arg(pos.start + pos.length - 1); + } + hint += QString(": %1 (%2)") + .arg(finfo->headerInfo().name) + .arg(finfo->headerInfo().abbreviation); + } + else { + hint = col_info_; + } + ui->hintLabel->setText(hint); +} + +void PacketDialog::setHintTextSelected(FieldInformation* finfo) +{ + QString hint; + + if (finfo) + { + FieldInformation::HeaderInfo hInfo = finfo->headerInfo(); + + if (hInfo.isValid) + { + if (hInfo.description.length() > 0) { + hint.append(hInfo.description); + } + else { + hint.append(hInfo.name); + } + } + + if (!hint.isEmpty()) { + int finfo_length; + if (hInfo.isValid) + hint.append(" (" + hInfo.abbreviation + ")"); + + finfo_length = finfo->position().length + finfo->appendix().length; + if (finfo_length > 0) { + hint.append(", " + tr("%Ln byte(s)", "", finfo_length)); + } + } + } + else { + hint = col_info_; + } + ui->hintLabel->setText(hint); +} + +void PacketDialog::viewVisibilityStateChanged(int state) +{ + byte_view_tab_->setVisible(state == Qt::Checked); + + prefs.gui_packet_details_show_byteview = (state == Qt::Checked ? TRUE : FALSE); + prefs_main_write(); +} diff --git a/ui/qt/packet_dialog.h b/ui/qt/packet_dialog.h new file mode 100644 index 00000000..c2119b74 --- /dev/null +++ b/ui/qt/packet_dialog.h @@ -0,0 +1,61 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PACKET_DIALOG_H +#define PACKET_DIALOG_H + +#include "wireshark_dialog.h" + +#include "epan/epan_dissect.h" +#include "wiretap/wtap.h" +#include "wsutil/buffer.h" + +#include + +class ByteViewTab; +class ProtoTree; + +namespace Ui { +class PacketDialog; +} + +class PacketDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit PacketDialog(QWidget &parent, CaptureFile &cf, frame_data *fdata); + ~PacketDialog(); + +protected: + void captureFileClosing(); + +signals: + void showProtocolPreferences(const QString module_name); + void editProtocolPreference(struct preference *pref, struct pref_module *module); + +private slots: + void on_buttonBox_helpRequested(); + void viewVisibilityStateChanged(int); + + void setHintText(FieldInformation *); + void setHintTextSelected(FieldInformation*); + +private: + Ui::PacketDialog *ui; + + QString col_info_; + ProtoTree *proto_tree_; + ByteViewTab *byte_view_tab_; + wtap_rec rec_; + Buffer buf_; + epan_dissect_t edt_; +}; + +#endif // PACKET_DIALOG_H diff --git a/ui/qt/packet_dialog.ui b/ui/qt/packet_dialog.ui new file mode 100644 index 00000000..0473fa12 --- /dev/null +++ b/ui/qt/packet_dialog.ui @@ -0,0 +1,101 @@ + + + PacketDialog + + + + 0 + 0 + 641 + 450 + + + + Dialog + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + Qt::Vertical + + + + + + + <small><i></i></small> + + + + + + + Show packet bytes + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+
+ + + + buttonBox + accepted() + PacketDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PacketDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/packet_format_group_box.cpp b/ui/qt/packet_format_group_box.cpp new file mode 100644 index 00000000..18f2259d --- /dev/null +++ b/ui/qt/packet_format_group_box.cpp @@ -0,0 +1,148 @@ +/* packet_format_group_box.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "packet_format_group_box.h" +#include + +#include + +#include +#include + +PacketFormatGroupBox::PacketFormatGroupBox(QWidget *parent) : + QGroupBox(parent), + pf_ui_(new Ui::PacketFormatGroupBox) +{ + pf_ui_->setupUi(this); + setFlat(true); + + QStyleOption style_opt; + int cb_label_offset = pf_ui_->detailsCheckBox->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left(); + + // Indent the checkbox under the "Packet summary" checkbox + pf_ui_->includeColumnHeadingsCheckBox->setStyleSheet(QString( + "QCheckBox {" + " padding-left: %1px;" + "}" + ).arg(cb_label_offset)); + + // Indent the radio buttons under the "Packet details" checkbox + pf_ui_->allCollapsedButton->setStyleSheet(QString( + "QRadioButton {" + " padding-left: %1px;" + "}" + ).arg(cb_label_offset)); + pf_ui_->asDisplayedButton->setStyleSheet(QString( + "QRadioButton {" + " padding-left: %1px;" + "}" + ).arg(cb_label_offset)); + pf_ui_->allExpandedButton->setStyleSheet(QString( + "QRadioButton {" + " padding-left: %1px;" + "}" + ).arg(cb_label_offset)); + + // Indent the checkbox under the "Bytes" checkbox + pf_ui_->includeDataSourcesCheckBox->setStyleSheet(QString( + "QCheckBox {" + " padding-left: %1px;" + "}" + ).arg(cb_label_offset)); +} + +PacketFormatGroupBox::~PacketFormatGroupBox() +{ + delete pf_ui_; +} + +bool PacketFormatGroupBox::summaryEnabled() +{ + return pf_ui_->summaryCheckBox->isChecked(); +} + +bool PacketFormatGroupBox::detailsEnabled() +{ + return pf_ui_->detailsCheckBox->isChecked(); +} + +bool PacketFormatGroupBox::bytesEnabled() +{ + return pf_ui_->bytesCheckBox->isChecked(); +} + +bool PacketFormatGroupBox::includeColumnHeadingsEnabled() +{ + return pf_ui_->includeColumnHeadingsCheckBox->isChecked(); +} + +bool PacketFormatGroupBox::allCollapsedEnabled() +{ + return pf_ui_->allCollapsedButton->isChecked(); +} + +bool PacketFormatGroupBox::asDisplayedEnabled() +{ + return pf_ui_->asDisplayedButton->isChecked(); +} + +bool PacketFormatGroupBox::allExpandedEnabled() +{ + return pf_ui_->allExpandedButton->isChecked(); +} + +uint PacketFormatGroupBox::getHexdumpOptions() +{ + return pf_ui_->includeDataSourcesCheckBox->isChecked() ? HEXDUMP_SOURCE_MULTI : HEXDUMP_SOURCE_PRIMARY; +} + +void PacketFormatGroupBox::on_summaryCheckBox_toggled(bool checked) +{ + pf_ui_->includeColumnHeadingsCheckBox->setEnabled(checked); + emit formatChanged(); +} + +void PacketFormatGroupBox::on_detailsCheckBox_toggled(bool checked) +{ + pf_ui_->allCollapsedButton->setEnabled(checked); + pf_ui_->asDisplayedButton->setEnabled(checked); + pf_ui_->allExpandedButton->setEnabled(checked); + emit formatChanged(); +} + +void PacketFormatGroupBox::on_bytesCheckBox_toggled(bool checked) +{ + pf_ui_->includeDataSourcesCheckBox->setEnabled(checked); + emit formatChanged(); +} + +void PacketFormatGroupBox::on_includeColumnHeadingsCheckBox_toggled(bool) +{ + emit formatChanged(); +} + +void PacketFormatGroupBox::on_allCollapsedButton_toggled(bool checked) +{ + if (checked) emit formatChanged(); +} + +void PacketFormatGroupBox::on_asDisplayedButton_toggled(bool checked) +{ + if (checked) emit formatChanged(); +} + +void PacketFormatGroupBox::on_allExpandedButton_toggled(bool checked) +{ + if (checked) emit formatChanged(); +} + +void PacketFormatGroupBox::on_includeDataSourcesCheckBox_toggled(bool) +{ + emit formatChanged(); +} diff --git a/ui/qt/packet_format_group_box.h b/ui/qt/packet_format_group_box.h new file mode 100644 index 00000000..30459071 --- /dev/null +++ b/ui/qt/packet_format_group_box.h @@ -0,0 +1,60 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#ifndef PACKET_FORMAT_GROUP_BOX_H +#define PACKET_FORMAT_GROUP_BOX_H + +#include + +namespace Ui { +class PacketFormatGroupBox; +} + +class PacketFormatGroupBox : public QGroupBox +{ + Q_OBJECT + +public: + explicit PacketFormatGroupBox(QWidget *parent = 0); + ~PacketFormatGroupBox(); + + bool summaryEnabled(); + bool detailsEnabled(); + bool bytesEnabled(); + + bool includeColumnHeadingsEnabled(); + + bool allCollapsedEnabled(); + bool asDisplayedEnabled(); + bool allExpandedEnabled(); + + uint getHexdumpOptions(); + +signals: + void formatChanged(); + +private slots: + void on_summaryCheckBox_toggled(bool checked); + void on_detailsCheckBox_toggled(bool checked); + void on_bytesCheckBox_toggled(bool checked); + + void on_includeColumnHeadingsCheckBox_toggled(bool checked); + + void on_allCollapsedButton_toggled(bool checked); + void on_asDisplayedButton_toggled(bool checked); + void on_allExpandedButton_toggled(bool checked); + + void on_includeDataSourcesCheckBox_toggled(bool checked); + +private: + Ui::PacketFormatGroupBox *pf_ui_; +}; + +#endif // PACKET_FORMAT_GROUP_BOX_H diff --git a/ui/qt/packet_format_group_box.ui b/ui/qt/packet_format_group_box.ui new file mode 100644 index 00000000..fcf350f0 --- /dev/null +++ b/ui/qt/packet_format_group_box.ui @@ -0,0 +1,136 @@ + + + PacketFormatGroupBox + + + + 0 + 0 + 400 + 199 + + + + GroupBox + + + Packet Format + + + + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + + + Summary line + + + true + + + + + + + Include column headings + + + true + + + + + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + + + Details: + + + true + + + + + + + + 1 + 0 + + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + + + All co&llapsed + + + + + + + + 1 + 0 + + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + + + As displa&yed + + + true + + + + + + + + 1 + 0 + + + + <html><head/><body><p>Export all packet detail items</p></body></html> + + + All e&xpanded + + + + + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + + + Bytes + + + + + + + Include secondary data sources + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + + + true + + + false + + + + + + + diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp new file mode 100644 index 00000000..e74f1517 --- /dev/null +++ b/ui/qt/packet_list.cpp @@ -0,0 +1,2164 @@ +/* packet_list.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "config.h" + +#include + +#include "file.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ui/main_statusbar.h" +#include "ui/packet_list_utils.h" +#include "ui/preference_utils.h" +#include "ui/recent.h" +#include "ui/recent_utils.h" +#include "ui/ws_ui_util.h" +#include "ui/simple_dialog.h" +#include +#include "ui/util.h" + +#include "wiretap/wtap_opttypes.h" +#include "wsutil/str_util.h" +#include + +#include +#include "frame_tvbuff.h" + +#include +#include +#include "proto_tree.h" +#include +#include "main_application.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include "wsutil/file_util.h" +#include +#include +#endif + +// To do: +// - Fix "apply as filter" behavior. +// - Add colorize conversation. +// - Use a timer to trigger automatic scrolling. + +// If we ever add the ability to open multiple capture files we might be +// able to use something like QMap to match +// capture files against packet lists and models. +static PacketList *gbl_cur_packet_list = NULL; + +const int max_comments_to_fetch_ = 20000000; // Arbitrary +const int overlay_update_interval_ = 100; // 250; // Milliseconds. + + +/* + * Given a frame_data structure, scroll to and select the row in the + * packet list corresponding to that frame. If there is no such + * row, return FALSE, otherwise return TRUE. + */ +gboolean +packet_list_select_row_from_data(frame_data *fdata_needle) +{ + if (! gbl_cur_packet_list || ! gbl_cur_packet_list->model()) + return FALSE; + + PacketListModel * model = qobject_cast(gbl_cur_packet_list->model()); + + if (! model) + return FALSE; + + model->flushVisibleRows(); + int row = -1; + if (!fdata_needle) + row = 0; + else + row = model->visibleIndexOf(fdata_needle); + + if (row >= 0) { + /* Calling ClearAndSelect with setCurrentIndex clears the "current" + * item, but doesn't clear the "selected" item. We want to clear + * the "selected" item as well so that selectionChanged() will be + * emitted in order to force an update of the packet details and + * packet bytes after a search. + */ + gbl_cur_packet_list->selectionModel()->clearSelection(); + gbl_cur_packet_list->selectionModel()->setCurrentIndex(model->index(row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + gbl_cur_packet_list->scrollTo(gbl_cur_packet_list->currentIndex(), PacketList::PositionAtCenter); + return TRUE; + } + + return FALSE; +} + +void +packet_list_clear(void) +{ + if (gbl_cur_packet_list) { + gbl_cur_packet_list->clear(); + } +} + +void +packet_list_freeze(void) +{ + if (gbl_cur_packet_list) { + gbl_cur_packet_list->freeze(); + } +} + +void +packet_list_thaw(void) +{ + if (gbl_cur_packet_list) { + gbl_cur_packet_list->thaw(); + } + + packets_bar_update(); +} + +/* Redraw the packet list *and* currently-selected detail */ +void +packet_list_queue_draw(void) +{ + if (gbl_cur_packet_list) + gbl_cur_packet_list->redrawVisiblePackets(); +} + +void +packet_list_recent_write_all(FILE *rf) { + if (!gbl_cur_packet_list) + return; + + gbl_cur_packet_list->writeRecent(rf); +} + +gboolean +packet_list_multi_select_active(void) +{ + if (gbl_cur_packet_list) { + return gbl_cur_packet_list->multiSelectActive(); + } + return FALSE; +} + +#define MIN_COL_WIDTH_STR "MMMMMM" + +PacketList::PacketList(QWidget *parent) : + QTreeView(parent), + proto_tree_(NULL), + cap_file_(NULL), + ctx_column_(-1), + overlay_timer_id_(0), + create_near_overlay_(true), + create_far_overlay_(true), + mouse_pressed_at_(QModelIndex()), + capture_in_progress_(false), + tail_at_end_(0), + columns_changed_(false), + set_column_visibility_(false), + frozen_current_row_(QModelIndex()), + frozen_selected_rows_(QModelIndexList()), + cur_history_(-1), + in_history_(false), + finfo_array(NULL) +{ + setItemsExpandable(false); + setRootIsDecorated(false); + setSortingEnabled(prefs.gui_packet_list_sortable); + setUniformRowHeights(true); + setAccessibleName("Packet list"); + + proto_prefs_menus_.setTitle(tr("Protocol Preferences")); + + packet_list_header_ = new PacketListHeader(header()->orientation()); + connect(packet_list_header_, &PacketListHeader::resetColumnWidth, this, &PacketList::setRecentColumnWidth); + connect(packet_list_header_, &PacketListHeader::updatePackets, this, &PacketList::updatePackets); + connect(packet_list_header_, &PacketListHeader::showColumnPreferences, this, &PacketList::showProtocolPreferences); + connect(packet_list_header_, &PacketListHeader::editColumn, this, &PacketList::editColumn); + connect(packet_list_header_, &PacketListHeader::columnsChanged, this, &PacketList::columnsChanged); + setHeader(packet_list_header_); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + header()->setFirstSectionMovable(true); +#endif + + setSelectionMode(QAbstractItemView::ExtendedSelection); + + // Shrink down to a small but nonzero size in the main splitter. + int one_em = fontMetrics().height(); + setMinimumSize(one_em, one_em); + + overlay_sb_ = new OverlayScrollBar(Qt::Vertical, this); + setVerticalScrollBar(overlay_sb_); + + header()->setSortIndicator(-1, Qt::AscendingOrder); + + packet_list_model_ = new PacketListModel(this, cap_file_); + setModel(packet_list_model_); + + Q_ASSERT(gbl_cur_packet_list == Q_NULLPTR); + gbl_cur_packet_list = this; + + connect(packet_list_model_, SIGNAL(goToPacket(int)), this, SLOT(goToPacket(int))); + connect(packet_list_model_, SIGNAL(itemHeightChanged(const QModelIndex&)), this, SLOT(updateRowHeights(const QModelIndex&))); + connect(mainApp, SIGNAL(addressResolutionChanged()), this, SLOT(redrawVisiblePacketsDontSelectCurrent())); + connect(mainApp, SIGNAL(columnDataChanged()), this, SLOT(redrawVisiblePacketsDontSelectCurrent())); + connect(mainApp, &MainApplication::preferencesChanged, this, [=]() { + /* The pref is a uint but QCache maxCost is a signed int (/ + * qsizetype in Qt 6). Note that QAbstractItemModel row numbers + * are ints, not unsigned ints, so we're limited to INT_MAX + * rows anyway. + */ + PacketListRecord::setMaxCache(prefs.gui_packet_list_cached_rows_max > INT_MAX ? INT_MAX : prefs.gui_packet_list_cached_rows_max); + if ((bool) (prefs.gui_packet_list_sortable) != isSortingEnabled()) { + setSortingEnabled(prefs.gui_packet_list_sortable); + } + }); + + connect(header(), SIGNAL(sectionResized(int,int,int)), + this, SLOT(sectionResized(int,int,int))); + connect(header(), SIGNAL(sectionMoved(int,int,int)), + this, SLOT(sectionMoved(int,int,int))); + + connect(verticalScrollBar(), SIGNAL(actionTriggered(int)), this, SLOT(vScrollBarActionTriggered(int))); +} + +PacketList::~PacketList() +{ + if (finfo_array) + { + g_ptr_array_free(finfo_array, TRUE); + } +} + +void PacketList::colorsChanged() +{ + const QString c_active = "active"; + const QString c_inactive = "!active"; + + QString flat_style_format = + "QTreeView::item:selected:%1 {" + " color: %2;" + " background-color: %3;" + "}"; + + QString gradient_style_format = + "QTreeView::item:selected:%1 {" + " color: %2;" + " background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1 stop: 0 %4, stop: 0.5 %3, stop: 1 %4);" + "}"; + + QString hover_style; +#if !defined(Q_OS_WIN) + hover_style = QString( + "QTreeView:item:hover {" + " background-color: %1;" + " color: palette(text);" + "}").arg(ColorUtils::hoverBackground().name(QColor::HexArgb)); +#endif + + QString active_style = QString(); + QString inactive_style = QString(); + + if (prefs.gui_active_style == COLOR_STYLE_DEFAULT) { + // ACTIVE = Default + } else if (prefs.gui_active_style == COLOR_STYLE_FLAT) { + // ACTIVE = Flat + QColor foreground = ColorUtils::fromColorT(prefs.gui_active_fg); + QColor background = ColorUtils::fromColorT(prefs.gui_active_bg); + + active_style = flat_style_format.arg( + c_active, + foreground.name(), + background.name()); + } else if (prefs.gui_active_style == COLOR_STYLE_GRADIENT) { + // ACTIVE = Gradient + QColor foreground = ColorUtils::fromColorT(prefs.gui_active_fg); + QColor background1 = ColorUtils::fromColorT(prefs.gui_active_bg); + QColor background2 = QColor::fromRgb(ColorUtils::alphaBlend(foreground, background1, COLOR_STYLE_ALPHA)); + + active_style = gradient_style_format.arg( + c_active, + foreground.name(), + background1.name(), + background2.name()); + } + + // INACTIVE style sheet settings + if (prefs.gui_inactive_style == COLOR_STYLE_DEFAULT) { + // INACTIVE = Default + } else if (prefs.gui_inactive_style == COLOR_STYLE_FLAT) { + // INACTIVE = Flat + QColor foreground = ColorUtils::fromColorT(prefs.gui_inactive_fg); + QColor background = ColorUtils::fromColorT(prefs.gui_inactive_bg); + + inactive_style = flat_style_format.arg( + c_inactive, + foreground.name(), + background.name()); + } else if (prefs.gui_inactive_style == COLOR_STYLE_GRADIENT) { + // INACTIVE = Gradient + QColor foreground = ColorUtils::fromColorT(prefs.gui_inactive_fg); + QColor background1 = ColorUtils::fromColorT(prefs.gui_inactive_bg); + QColor background2 = QColor::fromRgb(ColorUtils::alphaBlend(foreground, background1, COLOR_STYLE_ALPHA)); + + inactive_style = gradient_style_format.arg( + c_inactive, + foreground.name(), + background1.name(), + background2.name()); + } + + // Set the style sheet + if(prefs.gui_packet_list_hover_style) { + setStyleSheet(active_style + inactive_style + hover_style); + } else { + setStyleSheet(active_style + inactive_style); + } +} + +QString PacketList::joinSummaryRow(QStringList col_parts, int row, SummaryCopyType type) +{ + QString copy_text; + switch (type) { + case CopyAsCSV: + copy_text = "\""; + copy_text += col_parts.join("\",\""); + copy_text += "\""; + break; + case CopyAsYAML: + copy_text = "----\n"; + copy_text += QString("# Packet %1 from %2\n").arg(row).arg(cap_file_->filename); + copy_text += "- "; + copy_text += col_parts.join("\n- "); + copy_text += "\n"; + break; + case CopyAsText: + default: + copy_text = col_parts.join("\t"); + } + + return copy_text; +} + +void PacketList::drawRow (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QTreeView::drawRow(painter, option, index); + + if (prefs.gui_packet_list_separator) { + QRect rect = visualRect(index); + + painter->setPen(QColor(Qt::white)); + painter->drawLine(0, rect.y() + rect.height() - 1, width(), rect.y() + rect.height() - 1); + } +} + +void PacketList::setProtoTree (ProtoTree *proto_tree) { + proto_tree_ = proto_tree; + + connect(proto_tree_, SIGNAL(goToPacket(int)), this, SLOT(goToPacket(int))); + connect(proto_tree_, SIGNAL(relatedFrame(int,ft_framenum_type_t)), + &related_packet_delegate_, SLOT(addRelatedFrame(int,ft_framenum_type_t))); +} + +bool PacketList::uniqueSelectActive() +{ + return selectionModel()->selectedRows(0).count() == 1 ? true : false; +} + +bool PacketList::multiSelectActive() +{ + return selectionModel()->selectedRows(0).count() > 1 ? true : false; +} + +QList PacketList::selectedRows(bool useFrameNum) +{ + QList rows; + if (selectionModel() && selectionModel()->hasSelection()) + { + foreach (QModelIndex idx, selectionModel()->selectedRows(0)) + { + if (idx.isValid()) + { + if (! useFrameNum) + rows << idx.row(); + else if (useFrameNum) + { + frame_data * frame = getFDataForRow(idx.row()); + if (frame) + rows << frame->num; + } + } + } + } + else if (currentIndex().isValid()) + { + // + // XXX - will we ever have a current index but not a selection + // model? + // + if (! useFrameNum) + rows << currentIndex().row(); + else + { + frame_data *frame = getFDataForRow(currentIndex().row()); + if (frame) + rows << frame->num; + } + } + + return rows; +} + +void PacketList::selectionChanged (const QItemSelection & selected, const QItemSelection & deselected) +{ + QTreeView::selectionChanged(selected, deselected); + + if (!cap_file_) return; + + int row = -1; + static bool multiSelect = false; + + if (selectionModel()) + { + QModelIndexList selRows = selectionModel()->selectedRows(0); + if (selRows.count() > 1) + { + QList rows; + foreach (QModelIndex idx, selRows) + { + if (idx.isValid()) + rows << idx.row(); + } + + emit framesSelected(rows); + emit fieldSelected(0); + cf_unselect_packet(cap_file_); + + /* We have to repaint the content while changing state, as some delegates react to multi-select */ + if (! multiSelect) + { + related_packet_delegate_.clear(); + viewport()->update(); + } + + multiSelect = true; + + return; + } + else if (selRows.count() > 0 && selRows.at(0).isValid()) + { + multiSelect = false; + row = selRows.at(0).row(); + } + + /* Handling empty selection */ + if (selRows.count() <= 0) + { + /* Nothing selected, but multiSelect is still active */ + if (multiSelect) + { + multiSelect = false; + if (currentIndex().isValid()) + { + selectionModel()->select(currentIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); + return; + } + } + /* Nothing selected, so in WS <= 3.0 nothing was indicated as well */ + else if (currentIndex().isValid()) + { + setCurrentIndex(QModelIndex()); + } + } + } + + if (row < 0 || !packet_list_model_) + cf_unselect_packet(cap_file_); + else { + frame_data * fdata = packet_list_model_->getRowFdata(row); + cf_select_packet(cap_file_, fdata); + } + + if (!in_history_ && cap_file_->current_frame) { + cur_history_++; + selection_history_.resize(cur_history_); + selection_history_.append(cap_file_->current_frame->num); + } + in_history_ = false; + + related_packet_delegate_.clear(); + + // The previous dissection state has been invalidated by cf_select_packet + // above, receivers must clear the previous state and apply the updated one. + emit framesSelected(QList() << row); + + if (!cap_file_->edt) { + viewport()->update(); + emit fieldSelected(0); + return; + } + + if (cap_file_->edt->tree) { + packet_info *pi = &cap_file_->edt->pi; + related_packet_delegate_.setCurrentFrame(pi->num); + conversation_t *conv = find_conversation_pinfo(pi, 0); + if (conv) { + related_packet_delegate_.setConversation(conv); + } + viewport()->update(); + } + + if (cap_file_->search_in_progress) { + field_info *fi = NULL; + + if (cap_file_->string && cap_file_->decode_data) { + // The tree where the target string matched one of the labels was discarded in + // match_protocol_tree() so we have to search again in the latest tree. + fi = cf_find_string_protocol_tree(cap_file_, cap_file_->edt->tree); + } else if (cap_file_->search_pos != 0) { + // Find the finfo that corresponds to our byte. + fi = proto_find_field_from_offset(cap_file_->edt->tree, cap_file_->search_pos, + cap_file_->edt->tvb); + } + + if (fi) { + FieldInformation finfo(fi, this); + emit fieldSelected(&finfo); + } else { + emit fieldSelected(0); + } + } else if (proto_tree_) { + proto_tree_->restoreSelectedField(); + } +} + +void PacketList::contextMenuEvent(QContextMenuEvent *event) +{ + const char *module_name = NULL; + + proto_prefs_menus_.clear(); + + if (finfo_array) + { + g_ptr_array_free(finfo_array, TRUE); + finfo_array = NULL; + } + if (cap_file_ && cap_file_->edt && cap_file_->edt->tree) { + finfo_array = proto_all_finfos(cap_file_->edt->tree); + QList added_proto_prefs; + + for (guint i = 0; i < finfo_array->len; i++) { + field_info *fi = (field_info *)g_ptr_array_index (finfo_array, i); + header_field_info *hfinfo = fi->hfinfo; + + if (prefs_is_registered_protocol(hfinfo->abbrev)) { + if (hfinfo->parent == -1) { + module_name = hfinfo->abbrev; + } else { + module_name = proto_registrar_get_abbrev(hfinfo->parent); + } + + if (added_proto_prefs.contains(module_name)) { + continue; + } + + ProtocolPreferencesMenu *proto_prefs_menu = new ProtocolPreferencesMenu(hfinfo->name, module_name, &proto_prefs_menus_); + + connect(proto_prefs_menu, SIGNAL(showProtocolPreferences(QString)), + this, SIGNAL(showProtocolPreferences(QString))); + connect(proto_prefs_menu, SIGNAL(editProtocolPreference(preference*,pref_module*)), + this, SIGNAL(editProtocolPreference(preference*,pref_module*))); + + proto_prefs_menus_.addMenu(proto_prefs_menu); + added_proto_prefs << module_name; + } + } + } + + QModelIndex ctxIndex = indexAt(event->pos()); + + if (selectionModel() && selectionModel()->selectedRows(0).count() > 1) + selectionModel()->select(ctxIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + + // frameData will be owned by one of the submenus, see below. + FrameInformation * frameData = + new FrameInformation(new CaptureFile(this, cap_file_), packet_list_model_->getRowFdata(ctxIndex.row())); + + QMenu * ctx_menu = new QMenu(this); + ctx_menu->setAttribute(Qt::WA_DeleteOnClose); + // XXX We might want to reimplement setParent() and fill in the context + // menu there. + ctx_menu->addAction(window()->findChild("actionEditMarkPacket")); + ctx_menu->addAction(window()->findChild("actionEditIgnorePacket")); + ctx_menu->addAction(window()->findChild("actionEditSetTimeReference")); + ctx_menu->addAction(window()->findChild("actionEditTimeShift")); + ctx_menu->addMenu(window()->findChild("menuPacketComment")); + + ctx_menu->addSeparator(); + + // Code for custom context menus from Lua's register_packet_menu() + MainWindow * mainWindow = qobject_cast(mainApp->mainWindow()); + // N.B., Only want to call for a single frame selection. + // Testing finfo_array as a proxy, because mainWindow->hasUniqueSelection() doesn't detect multiple selections... + if (cap_file_ && cap_file_->edt && cap_file_->edt->tree && mainWindow && finfo_array) { + bool insertedPacketMenu = mainWindow->addPacketMenus(ctx_menu, finfo_array); + if (insertedPacketMenu) { + ctx_menu->addSeparator(); + } + } + + ctx_menu->addAction(window()->findChild("actionViewEditResolvedName")); + ctx_menu->addSeparator(); + + QString selectedfilter = getFilterFromRowAndColumn(currentIndex()); + + if (! hasFocus() && cap_file_ && cap_file_->finfo_selected) { + char *tmp_field = proto_construct_match_selected_string(cap_file_->finfo_selected, cap_file_->edt); + selectedfilter = QString(tmp_field); + wmem_free(NULL, tmp_field); + } + + bool have_filter_expr = !selectedfilter.isEmpty(); + ctx_menu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionApply, selectedfilter, have_filter_expr, ctx_menu)); + ctx_menu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionPrepare, selectedfilter, have_filter_expr, ctx_menu)); + + const char *conv_menu_name = "menuConversationFilter"; + QMenu * main_menu_item = window()->findChild(conv_menu_name); + conv_menu_.setTitle(main_menu_item->title()); + conv_menu_.setObjectName(conv_menu_name); + ctx_menu->addMenu(&conv_menu_); + + const char *colorize_menu_name = "menuColorizeConversation"; + main_menu_item = window()->findChild(colorize_menu_name); + colorize_menu_.setTitle(main_menu_item->title()); + colorize_menu_.setObjectName(colorize_menu_name); + ctx_menu->addMenu(&colorize_menu_); + + QMenu * submenu; + main_menu_item = window()->findChild("menuSCTP"); + if (main_menu_item) { + submenu = new QMenu(main_menu_item->title(), ctx_menu); + ctx_menu->addMenu(submenu); + submenu->addAction(window()->findChild("actionSCTPAnalyseThisAssociation")); + submenu->addAction(window()->findChild("actionSCTPShowAllAssociations")); + submenu->addAction(window()->findChild("actionSCTPFilterThisAssociation")); + } + + main_menu_item = window()->findChild("menuFollow"); + if (main_menu_item) { + submenu = new QMenu(main_menu_item->title(), ctx_menu); + ctx_menu->addMenu(submenu); + foreach (FollowStreamAction *follow_action, main_menu_item->findChildren()) { + /* XXX: We could, like the prefs above, walk the protocols/layers + * and add the follow actions in the order they appear in the packet. + */ + if (follow_action->isEnabled()) { + submenu->addAction(follow_action); + } + } + } + + ctx_menu->addSeparator(); + + main_menu_item = window()->findChild("menuEditCopy"); + submenu = new QMenu(main_menu_item->title(), ctx_menu); + ctx_menu->addMenu(submenu); + + QAction * action = submenu->addAction(tr("Summary as Text")); + action->setData(CopyAsText); + connect(action, SIGNAL(triggered()), this, SLOT(copySummary())); + action = submenu->addAction(tr("…as CSV")); + action->setData(CopyAsCSV); + connect(action, SIGNAL(triggered()), this, SLOT(copySummary())); + action = submenu->addAction(tr("…as YAML")); + action->setData(CopyAsYAML); + connect(action, SIGNAL(triggered()), this, SLOT(copySummary())); + submenu->addSeparator(); + + submenu->addAction(window()->findChild("actionEditCopyAsFilter")); + submenu->addSeparator(); + + QActionGroup * copyEntries = DataPrinter::copyActions(this, frameData); + submenu->addActions(copyEntries->actions()); + copyEntries->setParent(submenu); + frameData->setParent(submenu); + + ctx_menu->addSeparator(); + ctx_menu->addMenu(&proto_prefs_menus_); + action = ctx_menu->addAction(tr("Decode As…")); + action->setProperty("create_new", QVariant(true)); + connect(action, &QAction::triggered, this, &PacketList::ctxDecodeAsDialog); + // "Print" not ported intentionally + action = window()->findChild("actionViewShowPacketInNewWindow"); + ctx_menu->addAction(action); + + + // Set menu sensitivity for the current column and set action data. + if (frameData) + emit framesSelected(QList() << frameData->frameNum()); + else + emit framesSelected(QList()); + + ctx_menu->popup(event->globalPos()); +} + +void PacketList::ctxDecodeAsDialog() +{ + QAction *da_action = qobject_cast(sender()); + if (! da_action) + return; + bool create_new = da_action->property("create_new").toBool(); + + DecodeAsDialog *da_dialog = new DecodeAsDialog(this, cap_file_, create_new); + connect(da_dialog, SIGNAL(destroyed(QObject*)), mainApp, SLOT(flushAppSignals())); + da_dialog->setWindowModality(Qt::ApplicationModal); + da_dialog->setAttribute(Qt::WA_DeleteOnClose); + da_dialog->show(); +} + +void PacketList::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == overlay_timer_id_) { + if (!capture_in_progress_) { + if (create_near_overlay_) drawNearOverlay(); + if (create_far_overlay_) drawFarOverlay(); + } + } else { + QTreeView::timerEvent(event); + } +} + +void PacketList::paintEvent(QPaintEvent *event) +{ + // XXX This is overkill, but there are quite a few events that + // require a new overlay, e.g. page up/down, scrolling, column + // resizing, etc. + create_near_overlay_ = true; + QTreeView::paintEvent(event); +} + +void PacketList::mousePressEvent (QMouseEvent *event) +{ + setAutoScroll(false); + QTreeView::mousePressEvent(event); + setAutoScroll(true); + + QModelIndex curIndex = indexAt(event->pos()); + mouse_pressed_at_ = curIndex; + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + bool midButton = (event->buttons() & Qt::MiddleButton) == Qt::MiddleButton; +#else + bool midButton = (event->buttons() & Qt::MidButton) == Qt::MidButton; +#endif + if (midButton && cap_file_ && packet_list_model_) + { + packet_list_model_->toggleFrameMark(QModelIndexList() << curIndex); + + // Make sure the packet list's frame.marked related field text is updated. + redrawVisiblePackets(); + + create_far_overlay_ = true; + packets_bar_update(); + } +} + +void PacketList::mouseReleaseEvent(QMouseEvent *event) { + QTreeView::mouseReleaseEvent(event); + + mouse_pressed_at_ = QModelIndex(); +} + +void PacketList::mouseMoveEvent (QMouseEvent *event) +{ + QModelIndex curIndex = indexAt(event->pos()); + if (event->buttons() & Qt::LeftButton && curIndex.isValid() && curIndex == mouse_pressed_at_) + { + ctx_column_ = curIndex.column(); + QMimeData * mimeData = new QMimeData(); + QWidget * content = nullptr; + + QString filter = getFilterFromRowAndColumn(curIndex); + QList rows = selectedRows(); + if (rows.count() > 1) + { + QStringList content; + foreach (int row, rows) + { + QModelIndex idx = model()->index(row, 0); + if (! idx.isValid()) + continue; + + QString entry = createSummaryText(idx, CopyAsText); + content << entry; + } + + if (content.count() > 0) + mimeData->setText(content.join("\n")); + } + else if (! filter.isEmpty()) + { + QString abbrev; + QString name = model()->headerData(curIndex.column(), header()->orientation()).toString(); + + if (! filter.isEmpty()) + { + abbrev = filter.left(filter.indexOf(' ')); + } + else + { + filter = model()->data(curIndex).toString().toLower(); + abbrev = filter; + } + + mimeData->setText(filter); + + QJsonObject filterData; + filterData["filter"] = filter; + filterData["name"] = abbrev; + filterData["description"] = name; + + mimeData->setData(WiresharkMimeData::DisplayFilterMimeType, QJsonDocument(filterData).toJson()); + content = new DragLabel(QString("%1\n%2").arg(name, abbrev), this); + } + else + { + QString text = model()->data(curIndex).toString(); + if (! text.isEmpty()) + mimeData->setText(text); + } + + if (mimeData->hasText() || mimeData->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) + { + QDrag * drag = new QDrag(this); + drag->setMimeData(mimeData); + if (content) + { + qreal dpr = window()->windowHandle()->devicePixelRatio(); + QPixmap pixmap= QPixmap(content->size() * dpr); + pixmap.setDevicePixelRatio(dpr); + content->render(&pixmap); + drag->setPixmap(pixmap); + } + + drag->exec(Qt::CopyAction); + } + else + { + delete mimeData; + } + } +} + +void PacketList::keyPressEvent(QKeyEvent *event) +{ + bool handled = false; + // If scrolling up/down, want to preserve horizontal scroll extent. + if (event->key() == Qt::Key_Down || event->key() == Qt::Key_Up || + event->key() == Qt::Key_PageDown || event->key() == Qt::Key_PageUp || + event->key() == Qt::Key_End || event->key() == Qt::Key_Home ) + { + // XXX: Why allow jumping to the left if the first column is current? + if (currentIndex().isValid() && currentIndex().column() > 0) { + int pos = horizontalScrollBar()->value(); + QTreeView::keyPressEvent(event); + horizontalScrollBar()->setValue(pos); + handled = true; + } + } + + if (!handled) + QTreeView::keyPressEvent(event); + + if (event->matches(QKeySequence::Copy)) + { + QStringList content; + if (model() && selectionModel() && selectionModel()->hasSelection()) + { + QList rows; + QModelIndexList selRows = selectionModel()->selectedRows(0); + foreach(QModelIndex row, selRows) + rows.append(row.row()); + + foreach(int row, rows) + { + QModelIndex idx = model()->index(row, 0); + if (! idx.isValid()) + continue; + + QString entry = createSummaryText(idx, CopyAsText); + content << entry; + } + } + + if (content.count() > 0) + mainApp->clipboard()->setText(content.join('\n'), QClipboard::Clipboard); + } +} + +void PacketList::resizeEvent(QResizeEvent *event) +{ + create_near_overlay_ = true; + create_far_overlay_ = true; + QTreeView::resizeEvent(event); +} + +void PacketList::setColumnVisibility() +{ + set_column_visibility_ = true; + for (int i = 0; i < prefs.num_cols; i++) { + setColumnHidden(i, get_column_visible(i) ? false : true); + } + set_column_visibility_ = false; +} + +int PacketList::sizeHintForColumn(int column) const +{ + int size_hint = 0; + + // This is a bit hacky but Qt does a fine job of column sizing and + // reimplementing QTreeView::sizeHintForColumn seems like a worse idea. + if (itemDelegateForColumn(column)) { + QStyleOptionViewItem option; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + initViewItemOption(&option); +#else + option = viewOptions(); +#endif + // In my (gcc) testing this results in correct behavior on Windows but adds extra space + // on macOS and Linux. We might want to add Q_OS_... #ifdefs accordingly. + size_hint = itemDelegateForColumn(column)->sizeHint(option, QModelIndex()).width(); + } + size_hint += QTreeView::sizeHintForColumn(column); // Decoration padding + return size_hint; +} + +void PacketList::setRecentColumnWidth(int col) +{ + int col_width = recent_get_column_width(col); + + if (col_width < 1) { + int fmt = get_column_format(col); + const char *long_str = get_column_width_string(fmt, col); + + QFontMetrics fm = QFontMetrics(mainApp->monospaceFont()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + if (long_str) { + col_width = fm.horizontalAdvance(long_str); + } else { + col_width = fm.horizontalAdvance(MIN_COL_WIDTH_STR); + } +#else + if (long_str) { + col_width = fm.width(long_str); + } else { + col_width = fm.width(MIN_COL_WIDTH_STR); + } +#endif + // Custom delegate padding + if (itemDelegateForColumn(col)) { + QStyleOptionViewItem option; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + initViewItemOption(&option); +#else + option = viewOptions(); +#endif + col_width += itemDelegateForColumn(col)->sizeHint(option, QModelIndex()).width(); + } + } + + setColumnWidth(col, col_width); +} + +void PacketList::drawCurrentPacket() +{ + QModelIndex current_index = currentIndex(); + if (selectionModel() && current_index.isValid()) { + selectionModel()->clearSelection(); + selectionModel()->setCurrentIndex(current_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + } +} + +// Redraw the packet list and detail. Re-selects the current packet (causes +// the UI to scroll to that packet). +// Called from many places. +void PacketList::redrawVisiblePackets() { + redrawVisiblePacketsDontSelectCurrent(); + drawCurrentPacket(); +} + +// Redraw the packet list and detail. +// Does not scroll back to the selected packet. +void PacketList::redrawVisiblePacketsDontSelectCurrent() { + packet_list_model_->invalidateAllColumnStrings(); +} + +void PacketList::resetColumns() +{ + packet_list_model_->resetColumns(); +} + +// Return true if we have a visible packet further along in the history. +bool PacketList::haveNextHistory(bool update_cur) +{ + if (selection_history_.size() < 1 || cur_history_ >= selection_history_.size() - 1) { + return false; + } + + for (int i = cur_history_ + 1; i < selection_history_.size(); i++) { + if (packet_list_model_->packetNumberToRow(selection_history_.at(i)) >= 0) { + if (update_cur) { + cur_history_ = i; + } + return true; + } + } + return false; +} + +// Return true if we have a visible packet back in the history. +bool PacketList::havePreviousHistory(bool update_cur) +{ + if (selection_history_.size() < 1 || cur_history_ < 1) { + return false; + } + + for (int i = cur_history_ - 1; i >= 0; i--) { + if (packet_list_model_->packetNumberToRow(selection_history_.at(i)) >= 0) { + if (update_cur) { + cur_history_ = i; + } + return true; + } + } + return false; +} + +frame_data *PacketList::getFDataForRow(int row) const +{ + return packet_list_model_->getRowFdata(row); +} + +// prefs.col_list has changed. +void PacketList::columnsChanged() +{ + columns_changed_ = true; + column_register_fields(); + mainApp->emitAppSignal(MainApplication::FieldsChanged); + if (!cap_file_) { + // Keep columns_changed_ = true until we load a capture file. + return; + } + + prefs.num_cols = g_list_length(prefs.col_list); + col_cleanup(&cap_file_->cinfo); + build_column_format_array(&cap_file_->cinfo, prefs.num_cols, FALSE); + create_far_overlay_ = true; + resetColumns(); + applyRecentColumnWidths(); + setColumnVisibility(); + columns_changed_ = false; +} + +// Fields have changed, update custom columns +void PacketList::fieldsChanged(capture_file *cf) +{ + prefs.num_cols = g_list_length(prefs.col_list); + col_cleanup(&cf->cinfo); + build_column_format_array(&cf->cinfo, prefs.num_cols, FALSE); + resetColumns(); +} + +// Column widths should +// - Load from recent when we load a new profile (including at starting up). +// - Reapply when changing columns. +// - Persist across freezes and thaws. +// - Persist across file closing and opening. +// - Save to recent when we save our profile (including shutting down). +// - Not be affected by the behavior of stretchLastSection. (XXX: We +// still save the stretched value to recent, sectionResized doesn't +// distinguish between a resize from being stretched and a manual change.) +void PacketList::applyRecentColumnWidths() +{ + // Either we've just started up or a profile has changed. Read + // the recent settings, apply them, and save the header state. + + for (int col = 0; col < prefs.num_cols; col++) { + // The column must be shown before setting column width. + // Visibility will be updated in setColumnVisibility(). + setColumnHidden(col, false); + setRecentColumnWidth(col); + } + + column_state_ = header()->saveState(); +} + +void PacketList::preferencesChanged() +{ + // Update color style changes + colorsChanged(); + + // Related packet delegate + if (prefs.gui_packet_list_show_related) { + setItemDelegateForColumn(0, &related_packet_delegate_); + } else { + setItemDelegateForColumn(0, 0); + } + + // Intelligent scroll bar (minimap) + if (prefs.gui_packet_list_show_minimap) { + if (overlay_timer_id_ == 0) { + overlay_timer_id_ = startTimer(overlay_update_interval_); + } + } else { + if (overlay_timer_id_ != 0) { + killTimer(overlay_timer_id_); + overlay_timer_id_ = 0; + } + } + + // Elide mode. + // This sets the mode for the entire view. If we want to make this setting + // per-column we'll either have to generalize RelatedPacketDelegate so that + // we can set it for entire rows or create another delegate. + Qt::TextElideMode elide_mode = Qt::ElideRight; + switch (prefs.gui_packet_list_elide_mode) { + case ELIDE_LEFT: + elide_mode = Qt::ElideLeft; + break; + case ELIDE_MIDDLE: + elide_mode = Qt::ElideMiddle; + break; + case ELIDE_NONE: + elide_mode = Qt::ElideNone; + break; + default: + break; + } + setTextElideMode(elide_mode); +} + +void PacketList::freezePacketList(bool changing_profile) +{ + changing_profile_ = changing_profile; + freeze(true); +} + +void PacketList::recolorPackets() +{ + packet_list_model_->resetColorized(); + redrawVisiblePackets(); +} + +// Enable autoscroll. +void PacketList::setVerticalAutoScroll(bool enabled) +{ + tail_at_end_ = enabled; + if (enabled && capture_in_progress_) { + scrollToBottom(); + } +} + +// Called when we finish reading, reloading, rescanning, and retapping +// packets. +void PacketList::captureFileReadFinished() +{ + packet_list_model_->flushVisibleRows(); + packet_list_model_->dissectIdle(true); + // Invalidating the column strings picks up and request/response + // tracking changes. We might just want to call it from flushVisibleRows. + packet_list_model_->invalidateAllColumnStrings(); + // Sort *after* invalidating the column strings + if (isSortingEnabled()) { + sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder()); + } +} + +bool PacketList::freeze(bool keep_current_frame) +{ + if (!cap_file_ || model() == Q_NULLPTR) { + // No capture file or already frozen + return false; + } + + frame_data *current_frame = cap_file_->current_frame; + column_state_ = header()->saveState(); + setHeaderHidden(true); + frozen_current_row_ = currentIndex(); + frozen_selected_rows_ = selectionModel()->selectedRows(); + selectionModel()->clear(); + setModel(Q_NULLPTR); + // It looks like GTK+ sends a cursor-changed signal at this point but Qt doesn't + // call selectionChanged. + related_packet_delegate_.clear(); + + if (keep_current_frame) { + cap_file_->current_frame = current_frame; + } + + /* Clears packet list as well as byteview */ + emit framesSelected(QList()); + + return true; +} + +bool PacketList::thaw(bool restore_selection) +{ + if (!cap_file_ || model() != Q_NULLPTR) { + // No capture file or not frozen + return false; + } + + setHeaderHidden(false); + // Note that if we have a current sort status set in the header, + // this will automatically try to sort the model (we don't want + // that to happen if we're in the middle of reading the file). + setModel(packet_list_model_); + + if (changing_profile_) { + // When changing profile the new recent settings must be applied to the columns. + applyRecentColumnWidths(); + setColumnVisibility(); + changing_profile_ = false; + } else { + // Resetting the model resets our column widths so we restore them here. + // We don't reapply the recent settings because the user could have + // resized the columns manually since they were initially loaded. + header()->restoreState(column_state_); + } + + if (restore_selection && frozen_selected_rows_.length() > 0 && selectionModel()) { + /* This updates our selection, which redissects the current packet, + * which is needed when we're called from MainWindow::layoutPanes. + * Also, this resets all ProtoTree and ByteView data */ + clearSelection(); + setCurrentIndex(frozen_current_row_); + foreach (QModelIndex idx, frozen_selected_rows_) { + selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); + } + scrollTo(currentIndex(), PositionAtCenter); + } + frozen_current_row_ = QModelIndex(); + frozen_selected_rows_ = QModelIndexList(); + + return true; +} + +void PacketList::clear() { + related_packet_delegate_.clear(); + selectionModel()->clear(); + packet_list_model_->clear(); + proto_tree_->clear(); + selection_history_.clear(); + cur_history_ = -1; + in_history_ = false; + + QImage overlay; + overlay_sb_->setNearOverlayImage(overlay); + overlay_sb_->setMarkedPacketImage(overlay); + create_near_overlay_ = true; + create_far_overlay_ = true; +} + +void PacketList::writeRecent(FILE *rf) { + gint col, width, col_fmt; + gchar xalign; + + fprintf (rf, "%s:\n", RECENT_KEY_COL_WIDTH); + for (col = 0; col < prefs.num_cols; col++) { + if (col > 0) { + fprintf (rf, ",\n"); + } + col_fmt = get_column_format(col); + if (col_fmt == COL_CUSTOM) { + fprintf (rf, " \"%%Cus:%s\",", get_column_custom_fields(col)); + } else { + fprintf (rf, " %s,", col_format_to_string(col_fmt)); + } + width = recent_get_column_width (col); + xalign = recent_get_column_xalign (col); + fprintf (rf, " %d", width); + if (xalign != COLUMN_XALIGN_DEFAULT) { + fprintf (rf, ":%c", xalign); + } + } + fprintf (rf, "\n"); +} + +bool PacketList::contextMenuActive() +{ + return ctx_column_ >= 0 ? true : false; +} + +QString PacketList::getFilterFromRowAndColumn(QModelIndex idx) +{ + frame_data *fdata; + QString filter; + + if (! idx.isValid()) + return filter; + + int row = idx.row(); + int column = idx.column(); + + if (!cap_file_ || !packet_list_model_ || column < 0 || column >= cap_file_->cinfo.num_cols) + return filter; + + fdata = packet_list_model_->getRowFdata(row); + + if (fdata != NULL) { + epan_dissect_t edt; + wtap_rec rec; /* Record metadata */ + Buffer buf; /* Record data */ + + wtap_rec_init(&rec); + ws_buffer_init(&buf, 1514); + if (!cf_read_record(cap_file_, fdata, &rec, &buf)) { + wtap_rec_cleanup(&rec); + ws_buffer_free(&buf); + return filter; /* error reading the record */ + } + /* proto tree, visible. We need a proto tree if there's custom columns */ + epan_dissect_init(&edt, cap_file_->epan, have_custom_cols(&cap_file_->cinfo), FALSE); + col_custom_prime_edt(&edt, &cap_file_->cinfo); + + epan_dissect_run(&edt, cap_file_->cd_t, &rec, + frame_tvbuff_new_buffer(&cap_file_->provider, fdata, &buf), + fdata, &cap_file_->cinfo); + + if (cap_file_->cinfo.columns[column].col_fmt == COL_CUSTOM) { + filter.append(gchar_free_to_qstring(col_custom_get_filter(&edt, &cap_file_->cinfo, column))); + } else { + /* We don't need to fill in the custom columns, as we get their + * filters above. + */ + col_fill_in(&edt.pi, TRUE, TRUE); + if (strlen(cap_file_->cinfo.col_expr.col_expr[column]) != 0 && + strlen(cap_file_->cinfo.col_expr.col_expr_val[column]) != 0) { + gboolean is_string_value = FALSE; + header_field_info *hfi = proto_registrar_get_byname(cap_file_->cinfo.col_expr.col_expr[column]); + if (hfi && hfi->type == FT_STRING) { + /* Could be an address type such as usb.src which must be quoted. */ + is_string_value = TRUE; + } + + if (filter.isEmpty()) { + if (is_string_value) { + filter.append(QString("%1 == \"%2\"") + .arg(cap_file_->cinfo.col_expr.col_expr[column]) + .arg(cap_file_->cinfo.col_expr.col_expr_val[column])); + } else { + filter.append(QString("%1 == %2") + .arg(cap_file_->cinfo.col_expr.col_expr[column]) + .arg(cap_file_->cinfo.col_expr.col_expr_val[column])); + } + } + } + } + + epan_dissect_cleanup(&edt); + wtap_rec_cleanup(&rec); + ws_buffer_free(&buf); + } + + return filter; +} + +void PacketList::resetColorized() +{ + packet_list_model_->resetColorized(); + update(); +} + +QString PacketList::getPacketComment(guint c_number) +{ + int row = currentIndex().row(); + const frame_data *fdata; + char *pkt_comment; + wtap_opttype_return_val result; + QString ret_val = NULL; + + if (!cap_file_ || !packet_list_model_) return NULL; + + fdata = packet_list_model_->getRowFdata(row); + + if (!fdata) return NULL; + + wtap_block_t pkt_block = cf_get_packet_block(cap_file_, fdata); + result = wtap_block_get_nth_string_option_value(pkt_block, OPT_COMMENT, c_number, &pkt_comment); + if (result == WTAP_OPTTYPE_SUCCESS) { + ret_val = QString(pkt_comment); + } + wtap_block_unref(pkt_block); + return ret_val; +} + +void PacketList::addPacketComment(QString new_comment) +{ + if (!cap_file_ || !packet_list_model_) return; + if (new_comment.isEmpty()) return; + + QByteArray ba = new_comment.toUtf8(); + + /* + * Make sure this would fit in a pcapng option. + * + * XXX - 65535 is the maximum size for an option in pcapng; + * what if another capture file format supports larger + * comments? + */ + if (ba.size() > 65535) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "That comment is too large to save in a capture file."); + return; + } + + if (selectionModel() && selectionModel()->hasSelection()) { + packet_list_model_->addFrameComment(selectionModel()->selectedRows(), ba); + drawCurrentPacket(); + } +} + +void PacketList::setPacketComment(guint c_number, QString new_comment) +{ + QModelIndex curIndex = currentIndex(); + + if (!cap_file_ || !packet_list_model_) return; + + QByteArray ba = new_comment.toUtf8(); + /* + * Make sure this would fit in a pcapng option. + * + * XXX - 65535 is the maximum size for an option in pcapng; + * what if another capture file format supports larger + * comments? + */ + if (ba.size() > 65535) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "That comment is too large to save in a capture file."); + return; + } + + packet_list_model_->setFrameComment(curIndex, ba, c_number); + drawCurrentPacket(); +} + +QString PacketList::allPacketComments() +{ + guint32 framenum; + frame_data *fdata; + QString buf_str; + + if (!cap_file_) return buf_str; + + for (framenum = 1; framenum <= cap_file_->count ; framenum++) { + fdata = frame_data_sequence_find(cap_file_->provider.frames, framenum); + + wtap_block_t pkt_block = cf_get_packet_block(cap_file_, fdata); + + if (pkt_block) { + guint n_comments = wtap_block_count_option(pkt_block, OPT_COMMENT); + for (guint i = 0; i < n_comments; i++) { + char *comment_text; + if (WTAP_OPTTYPE_SUCCESS == wtap_block_get_nth_string_option_value(pkt_block, OPT_COMMENT, i, &comment_text)) { + buf_str.append(QString(tr("Frame %1: %2\n\n")).arg(framenum).arg(comment_text)); + if (buf_str.length() > max_comments_to_fetch_) { + buf_str.append(QString(tr("[ Comment text exceeds %1. Stopping. ]")) + .arg(format_size(max_comments_to_fetch_, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + return buf_str; + } + } + } + } + } + return buf_str; +} + +void PacketList::deleteCommentsFromPackets() +{ + if (!cap_file_ || !packet_list_model_) return; + + if (selectionModel() && selectionModel()->hasSelection()) { + packet_list_model_->deleteFrameComments(selectionModel()->selectedRows()); + drawCurrentPacket(); + } +} + +void PacketList::deleteAllPacketComments() +{ + if (!cap_file_ || !packet_list_model_) return; + + packet_list_model_->deleteAllFrameComments(); + drawCurrentPacket(); +} + + +// Slots + +void PacketList::setCaptureFile(capture_file *cf) +{ + cap_file_ = cf; + packet_list_model_->setCaptureFile(cf); + if (cf) { + if (columns_changed_) { + columnsChanged(); + } else { + // Restore columns widths and visibility. + header()->restoreState(column_state_); + setColumnVisibility(); + } + } + create_near_overlay_ = true; + changing_profile_ = false; + sortByColumn(-1, Qt::AscendingOrder); +} + +void PacketList::setMonospaceFont(const QFont &mono_font) +{ + setFont(mono_font); + header()->setFont(mainApp->font()); +} + +void PacketList::goNextPacket(void) +{ + if (QApplication::keyboardModifiers() & Qt::AltModifier) { + // Alt+toolbar + goNextHistoryPacket(); + return; + } + + if (selectionModel()->hasSelection()) { + selectionModel()->setCurrentIndex(moveCursor(MoveDown, Qt::NoModifier), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + } else { + // First visible packet. + selectionModel()->setCurrentIndex(indexAt(viewport()->rect().topLeft()), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + } + + scrollViewChanged(false); +} + +void PacketList::goPreviousPacket(void) +{ + if (QApplication::keyboardModifiers() & Qt::AltModifier) { + // Alt+toolbar + goPreviousHistoryPacket(); + return; + } + + if (selectionModel()->hasSelection()) { + selectionModel()->setCurrentIndex(moveCursor(MoveUp, Qt::NoModifier), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + } else { + // Last visible packet. + QModelIndex last_idx = indexAt(viewport()->rect().bottomLeft()); + if (last_idx.isValid()) { + selectionModel()->setCurrentIndex(last_idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + } else { + goLastPacket(); + } + } + + scrollViewChanged(false); +} + +void PacketList::goFirstPacket(void) { + if (packet_list_model_->rowCount() < 1) return; + selectionModel()->setCurrentIndex(packet_list_model_->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + scrollTo(currentIndex()); + + scrollViewChanged(false); +} + +void PacketList::goLastPacket(void) { + if (packet_list_model_->rowCount() < 1) return; + selectionModel()->setCurrentIndex(packet_list_model_->index(packet_list_model_->rowCount() - 1, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + scrollTo(currentIndex()); + + scrollViewChanged(false); +} + +// XXX We can jump to the wrong packet if a display filter is applied +void PacketList::goToPacket(int packet, int hf_id) +{ + if (!cf_goto_frame(cap_file_, packet)) + return; + + int row = packet_list_model_->packetNumberToRow(packet); + if (row >= 0) { + selectionModel()->setCurrentIndex(packet_list_model_->index(row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + scrollTo(currentIndex(), PositionAtCenter); + proto_tree_->goToHfid(hf_id); + } + + scrollViewChanged(false); +} + +void PacketList::goNextHistoryPacket() +{ + if (haveNextHistory(true)) { + in_history_ = true; + goToPacket(selection_history_.at(cur_history_)); + in_history_ = false; + } +} + +void PacketList::goPreviousHistoryPacket() +{ + if (havePreviousHistory(true)) { + in_history_ = true; + goToPacket(selection_history_.at(cur_history_)); + in_history_ = false; + } +} + +void PacketList::markFrame() +{ + if (!cap_file_ || !packet_list_model_) return; + + QModelIndexList frames; + + if (selectionModel() && selectionModel()->hasSelection()) + { + QModelIndexList selRows = selectionModel()->selectedRows(0); + foreach (QModelIndex idx, selRows) + { + if (idx.isValid()) + { + frames << idx; + } + } + } + else + frames << currentIndex(); + + packet_list_model_->toggleFrameMark(frames); + + // Make sure the packet list's frame.marked related field text is updated. + redrawVisiblePackets(); + + create_far_overlay_ = true; + packets_bar_update(); +} + +void PacketList::markAllDisplayedFrames(bool set) +{ + if (!cap_file_ || !packet_list_model_) return; + + packet_list_model_->setDisplayedFrameMark(set); + + // Make sure the packet list's frame.marked related field text is updated. + redrawVisiblePackets(); + + create_far_overlay_ = true; + packets_bar_update(); +} + +void PacketList::ignoreFrame() +{ + if (!cap_file_ || !packet_list_model_) return; + + QModelIndexList frames; + + if (selectionModel() && selectionModel()->hasSelection()) + { + foreach (QModelIndex idx, selectionModel()->selectedRows(0)) + { + if (idx.isValid()) + { + frames << idx; + } + } + } + else + frames << currentIndex(); + + + packet_list_model_->toggleFrameIgnore(frames); + create_far_overlay_ = true; + int sb_val = verticalScrollBar()->value(); // Surely there's a better way to keep our position? + setUpdatesEnabled(false); + emit packetDissectionChanged(); + setUpdatesEnabled(true); + verticalScrollBar()->setValue(sb_val); +} + +void PacketList::ignoreAllDisplayedFrames(bool set) +{ + if (!cap_file_ || !packet_list_model_) return; + + packet_list_model_->setDisplayedFrameIgnore(set); + create_far_overlay_ = true; + emit packetDissectionChanged(); +} + +void PacketList::setTimeReference() +{ + if (!cap_file_ || !packet_list_model_) return; + packet_list_model_->toggleFrameRefTime(currentIndex()); + create_far_overlay_ = true; +} + +void PacketList::unsetAllTimeReferences() +{ + if (!cap_file_ || !packet_list_model_) return; + packet_list_model_->unsetAllFrameRefTime(); + create_far_overlay_ = true; +} + +void PacketList::applyTimeShift() +{ + packet_list_model_->resetColumns(); + redrawVisiblePackets(); + emit packetDissectionChanged(); +} + +void PacketList::updatePackets(bool redraw) +{ + if (redraw) { + packet_list_model_->resetColumns(); + redrawVisiblePackets(); + } else { + update(); + } +} + +void PacketList::columnVisibilityTriggered() +{ + QAction *ha = qobject_cast(sender()); + if (!ha) return; + + int col = ha->data().toInt(); + set_column_visible(col, ha->isChecked()); + setColumnVisibility(); + if (ha->isChecked()) { + setRecentColumnWidth(col); + } + prefs_main_write(); +} + +void PacketList::sectionResized(int col, int, int new_width) +{ + if (isVisible() && !columns_changed_ && !set_column_visibility_ && new_width > 0) { + // Column 1 gets an invalid value (32 on macOS) when we're not yet + // visible. + // + // Don't set column width when columns changed or setting column + // visibility because we may get a sectionReized() from QTreeView + // with values from a old columns layout. + // + // Don't set column width when hiding a column. + + recent_set_column_width(col, new_width); + } +} + +// The user moved a column. Make sure prefs.col_list, the column format +// array, and the header's visual and logical indices all agree. +// gtk/packet_list.c:column_dnd_changed_cb +void PacketList::sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) +{ + GList *new_col_list = NULL; + QList saved_sizes; + int sort_idx; + + // Since we undo the move below, these should always stay in sync. + // Otherwise the order of columns can be unexpected after drag and drop. + if (logicalIndex != oldVisualIndex) { + ws_warning("Column moved from an unexpected state (%d, %d, %d)", + logicalIndex, oldVisualIndex, newVisualIndex); + } + + // Remember which column should be sorted. Use the visual index since this + // points to the current GUI state rather than the outdated column order + // (indicated by the logical index). + sort_idx = header()->sortIndicatorSection(); + if (sort_idx != -1) { + sort_idx = header()->visualIndex(sort_idx); + } + + // Build a new column list based on the header's logical order. + for (int vis_idx = 0; vis_idx < header()->count(); vis_idx++) { + int log_idx = header()->logicalIndex(vis_idx); + saved_sizes << header()->sectionSize(log_idx); + + void *pref_data = g_list_nth_data(prefs.col_list, log_idx); + if (!pref_data) continue; + + new_col_list = g_list_append(new_col_list, pref_data); + } + + // Undo move to ensure that the logical indices map to the visual indices, + // otherwise the column order is changed twice (once via the modified + // col_list, once because of the visual/logical index mismatch). + disconnect(header(), SIGNAL(sectionMoved(int,int,int)), + this, SLOT(sectionMoved(int,int,int))); + header()->moveSection(newVisualIndex, oldVisualIndex); + connect(header(), SIGNAL(sectionMoved(int,int,int)), + this, SLOT(sectionMoved(int,int,int))); + + // Clear and rebuild our (and the header's) model. There doesn't appear + // to be another way to reset the logical index. + freeze(); + + g_list_free(prefs.col_list); + prefs.col_list = new_col_list; + + thaw(true); + + for (int i = 0; i < saved_sizes.length(); i++) { + if (saved_sizes[i] < 1) continue; + header()->resizeSection(i, saved_sizes[i]); + } + + prefs_main_write(); + + mainApp->emitAppSignal(MainApplication::ColumnsChanged); + + // If the column with the sort indicator got shifted, mark the new column + // after updating the columns contents (via ColumnsChanged) to ensure that + // the columns are sorted using the intended column contents. + int left_col = MIN(oldVisualIndex, newVisualIndex); + int right_col = MAX(oldVisualIndex, newVisualIndex); + if (left_col <= sort_idx && sort_idx <= right_col) { + header()->setSortIndicator(sort_idx, header()->sortIndicatorOrder()); + } +} + +void PacketList::updateRowHeights(const QModelIndex &ih_index) +{ + QStyleOptionViewItem option; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + initViewItemOption(&option); +#else + option = viewOptions(); +#endif + int max_height = 0; + + // One of our columns increased the maximum row height. Find out which one. + for (int col = 0; col < packet_list_model_->columnCount(); col++) { + QSize size_hint = itemDelegate()->sizeHint(option, packet_list_model_->index(ih_index.row(), col)); + max_height = qMax(max_height, size_hint.height()); + } + + if (max_height > 0) { + packet_list_model_->setMaximumRowHeight(max_height); + } +} + +QString PacketList::createSummaryText(QModelIndex idx, SummaryCopyType type) +{ + if (! idx.isValid()) + return ""; + + QStringList col_parts; + int row = idx.row(); + for (int col = 0; col < packet_list_model_->columnCount(); col++) { + if (get_column_visible(col)) { + col_parts << packet_list_model_->data(packet_list_model_->index(row, col), Qt::DisplayRole).toString(); + } + } + return joinSummaryRow(col_parts, row, type); +} + +QString PacketList::createHeaderSummaryText(SummaryCopyType type) +{ + QStringList col_parts; + for (int col = 0; col < packet_list_model_->columnCount(); ++col) + { + if (get_column_visible(col)) { + col_parts << packet_list_model_->headerData(col, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); + } + } + return joinSummaryRow(col_parts, 0, type); +} + +void PacketList::copySummary() +{ + if (!currentIndex().isValid()) return; + + QAction *ca = qobject_cast(sender()); + if (!ca) return; + + QVariant type = ca->data(); + if (! type.canConvert()) + return; + SummaryCopyType copy_type = type.value(); + + QString copy_text = createSummaryText(currentIndex(), copy_type); + + mainApp->clipboard()->setText(copy_text); +} + +// We need to tell when the user has scrolled the packet list, either to +// the end or anywhere other than the end. +void PacketList::vScrollBarActionTriggered(int) +{ + // If we're scrolling with a mouse wheel or trackpad sliderPosition can end up + // past the end. + tail_at_end_ = (overlay_sb_->sliderPosition() >= overlay_sb_->maximum()); + + scrollViewChanged(tail_at_end_); +} + +void PacketList::scrollViewChanged(bool at_end) +{ + if (capture_in_progress_) { + // We want to start auto scrolling when the user scrolls to (or past) + // the end only if recent.capture_auto_scroll is set. + // We want to stop autoscrolling if the user scrolls up or uses + // Go to Packet regardless of the preference setting. + if (recent.capture_auto_scroll || !at_end) { + emit packetListScrolled(at_end); + } + } +} + +// Goal: Overlay the packet list scroll bar with the colors of all of the +// packets. +// Try 1: Average packet colors in each scroll bar raster line. This has +// two problems: It's easy to wash out colors and we dissect every packet. +// Try 2: Color across a 5000 or 10000 packet window. We still end up washing +// out colors. +// Try 3: One packet per vertical scroll bar pixel. This seems to work best +// but has the smallest window. +// Try 4: Use a multiple of the scroll bar heigh and scale the image down +// using Qt::SmoothTransformation. This gives us more packets per raster +// line. + +// Odd (prime?) numbers resulted in fewer scaling artifacts. A multiplier +// of 9 washed out colors a little too much. +//const int height_multiplier_ = 7; +void PacketList::drawNearOverlay() +{ + if (create_near_overlay_) { + create_near_overlay_ = false; + } + + if (!cap_file_ || cap_file_->state != FILE_READ_DONE) return; + + if (!prefs.gui_packet_list_show_minimap) return; + + qreal dp_ratio = overlay_sb_->devicePixelRatio(); + int o_height = overlay_sb_->height() * dp_ratio; + int o_rows = qMin(packet_list_model_->rowCount(), o_height); + QFontMetricsF fmf(mainApp->font()); + int o_width = ((static_cast(fmf.height())) * 2 * dp_ratio) + 2; // 2ems + 1-pixel border on either side. + + if (recent.packet_list_colorize && o_rows > 0) { + QImage overlay(o_width, o_height, QImage::Format_ARGB32_Premultiplied); + + QPainter painter(&overlay); + + overlay.fill(Qt::transparent); + + int cur_line = 0; + int start = 0; + + if (packet_list_model_->rowCount() > o_height && overlay_sb_->maximum() > 0) { + start += ((double) overlay_sb_->value() / overlay_sb_->maximum()) * (packet_list_model_->rowCount() - o_rows); + } + int end = start + o_rows; + for (int row = start; row < end; row++) { + packet_list_model_->ensureRowColorized(row); + + frame_data *fdata = packet_list_model_->getRowFdata(row); + const color_t *bgcolor = NULL; + if (fdata->color_filter) { + const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter; + bgcolor = &color_filter->bg_color; + } + + int next_line = (row - start + 1) * o_height / o_rows; + if (bgcolor) { + QColor color(ColorUtils::fromColorT(bgcolor)); + painter.fillRect(0, cur_line, o_width, next_line - cur_line, color); + } + cur_line = next_line; + } + + // If the selected packet is in the overlay set selected_pos + // accordingly. Otherwise, pin it to either the top or bottom. + QList positions; + if (selectionModel()->hasSelection()) { + + QModelIndexList selRows = selectionModel()->selectedRows(0); + int last_row = -1; + int last_pos = -1; + foreach (QModelIndex idx, selRows) + { + int selected_pos = -1; + int sel_row = idx.row(); + if (sel_row < start) { + selected_pos = 0; + } else if (sel_row >= end) { + selected_pos = overlay.height() - 1; + } else { + selected_pos = (sel_row - start) * o_height / o_rows; + } + + /* Due to the difference in the display height, we sometimes get empty positions + * inbetween consecutive valid rows. If those are detected, they are signaled as + * being selected as well */ + if (last_pos >= 0 && selected_pos > (last_pos + 1) && (last_row + 1) == sel_row) + { + for (int pos = (last_pos + 1); pos < selected_pos; pos++) + { + if (! positions.contains(pos)) + positions << pos; + } + } + else if (selected_pos != -1 && ! positions.contains(selected_pos)) + positions << selected_pos; + + last_row = sel_row; + last_pos = selected_pos; + } + } + + overlay_sb_->setNearOverlayImage(overlay, packet_list_model_->rowCount(), start, end, positions, (o_height / o_rows)); + } else { + QImage overlay; + overlay_sb_->setNearOverlayImage(overlay); + } +} + +void PacketList::drawFarOverlay() +{ + if (create_far_overlay_) { + create_far_overlay_ = false; + } + + if (!cap_file_ || cap_file_->state != FILE_READ_DONE) return; + + if (!prefs.gui_packet_list_show_minimap) return; + + QSize groove_size = overlay_sb_->grooveRect().size(); + qreal dp_ratio = overlay_sb_->devicePixelRatio(); + groove_size *= dp_ratio; + int o_width = groove_size.width(); + int o_height = groove_size.height(); + int pl_rows = packet_list_model_->rowCount(); + QImage overlay(o_width, o_height, QImage::Format_ARGB32_Premultiplied); + bool have_marked_image = false; + + // If only there were references from popular culture about getting into + // some sort of groove. + if (!overlay.isNull() && recent.packet_list_colorize && pl_rows > 0) { + + QPainter painter(&overlay); + + // Draw text-colored tick marks on a transparent background. + // Hopefully no themes use the text color for the groove color. + overlay.fill(Qt::transparent); + + QColor tick_color = palette().text().color(); + tick_color.setAlphaF(0.3f); + painter.setPen(tick_color); + + for (int row = 0; row < pl_rows; row++) { + + frame_data *fdata = packet_list_model_->getRowFdata(row); + if (fdata->marked || fdata->ref_time || fdata->ignored) { + int new_line = row * o_height / pl_rows; + int tick_width = o_width / 3; + // Marked or ignored: left side, time refs: right side. + // XXX Draw ignored ticks in the middle? + int x1 = fdata->ref_time ? o_width - tick_width : 1; + int x2 = fdata->ref_time ? o_width - 1 : tick_width; + + painter.drawLine(x1, new_line, x2, new_line); + have_marked_image = true; + } + } + + if (have_marked_image) { + overlay_sb_->setMarkedPacketImage(overlay); + return; + } + } + + if (!have_marked_image) { + QImage null_overlay; + overlay_sb_->setMarkedPacketImage(null_overlay); + } +} + +// Auto scroll if: +// - We are capturing +// - actionGoAutoScroll in the main UI is checked. + +// actionGoAutoScroll in the main UI: +// - Is set to the value of recent.capture_auto_scroll when beginning a capture +// - Can be triggered manually by the user +// - Is turned on if the last user-set vertical scrollbar position is at the +// end and recent.capture_auto_scroll is enabled +// - Is turned off if the last user-set vertical scrollbar is not at the end, +// or if one of the Go to Packet actions is used (XXX: Should keyboard +// navigation in keyPressEvent turn it off for similar reasons?) +void PacketList::rowsInserted(const QModelIndex &parent, int start, int end) +{ + QTreeView::rowsInserted(parent, start, end); + if (capture_in_progress_ && tail_at_end_) { + scrollToBottom(); + } +} + +void PacketList::resizeAllColumns(bool onlyTimeFormatted) +{ + if (!cap_file_ || cap_file_->state == FILE_CLOSED || cap_file_->state == FILE_READ_PENDING) + return; + + for (int col = 0; col < cap_file_->cinfo.num_cols; col++) { + if (! onlyTimeFormatted || col_has_time_fmt(&cap_file_->cinfo, col)) { + resizeColumnToContents(col); + } + } +} diff --git a/ui/qt/packet_list.h b/ui/qt/packet_list.h new file mode 100644 index 00000000..7b3fbe15 --- /dev/null +++ b/ui/qt/packet_list.h @@ -0,0 +1,209 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PACKET_LIST_H +#define PACKET_LIST_H + +#include "byte_view_tab.h" +#include +#include "proto_tree.h" +#include "protocol_preferences_menu.h" +#include +#include + +#include +#include +#include +#include + +class PacketListHeader; +class OverlayScrollBar; + +class QAction; +class QTimerEvent; + +// +// XXX - Wireshark supports up to 2^32-1 packets in a capture, but +// row numbers in a QAbstractItemModel are ints, not unsigned ints, +// so we can only have 2^31-1 rows on ILP32, LP64, and LLP64 platforms. +// Does that mean we're permanently stuck at a maximum of 2^31-1 packets +// per capture? +// +class PacketList : public QTreeView +{ + Q_OBJECT +public: + explicit PacketList(QWidget *parent = 0); + ~PacketList(); + + enum SummaryCopyType { + CopyAsText, + CopyAsCSV, + CopyAsYAML + }; + Q_ENUM(SummaryCopyType) + + QMenu *conversationMenu() { return &conv_menu_; } + QMenu *colorizeMenu() { return &colorize_menu_; } + void setProtoTree(ProtoTree *proto_tree); + + /** Disable and clear the packet list. + * + * @param keep_current_frame If true, keep the selected frame. + * Disable packet list widget updates, clear the detail and byte views, + * and disconnect the model. + */ + bool freeze(bool keep_current_frame = false); + /** Enable and restore the packet list. + * + * Enable packet list widget updates and reconnect the model. + * + * @param restore_selection If true, redissect the previously selected + * packet. This includes filling in the detail and byte views. + */ + bool thaw(bool restore_selection = false); + void clear(); + void writeRecent(FILE *rf); + bool contextMenuActive(); + QString getFilterFromRowAndColumn(QModelIndex idx); + void resetColorized(); + QString getPacketComment(guint c_number); + void addPacketComment(QString new_comment); + void setPacketComment(guint c_number, QString new_comment); + QString allPacketComments(); + void deleteCommentsFromPackets(); + void deleteAllPacketComments(); + void setVerticalAutoScroll(bool enabled = true); + void setCaptureInProgress(bool in_progress = false, bool auto_scroll = true) { capture_in_progress_ = in_progress; tail_at_end_ = in_progress && auto_scroll; } + void captureFileReadFinished(); + void resetColumns(); + bool haveNextHistory(bool update_cur = false); + bool havePreviousHistory(bool update_cur = false); + + frame_data * getFDataForRow(int row) const; + + bool uniqueSelectActive(); + bool multiSelectActive(); + QList selectedRows(bool useFrameNum = false); + + QString createSummaryText(QModelIndex idx, SummaryCopyType type); + QString createHeaderSummaryText(SummaryCopyType type); + + void resizeAllColumns(bool onlyTimeFormatted = false); + +protected: + + void selectionChanged(const QItemSelection & selected, const QItemSelection & deselected) override; + virtual void contextMenuEvent(QContextMenuEvent *event) override; + void timerEvent(QTimerEvent *event) override; + void paintEvent(QPaintEvent *event) override; + virtual void mousePressEvent (QMouseEvent *event) override; + virtual void mouseReleaseEvent (QMouseEvent *event) override; + virtual void mouseMoveEvent (QMouseEvent *event) override; + virtual void resizeEvent(QResizeEvent *event) override; + virtual void keyPressEvent(QKeyEvent *event) override; + +protected slots: + void rowsInserted(const QModelIndex &parent, int start, int end) override; + virtual void drawRow(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + +private: + PacketListModel *packet_list_model_; + PacketListHeader * packet_list_header_; + ProtoTree *proto_tree_; + capture_file *cap_file_; + QMenu conv_menu_; + QMenu colorize_menu_; + QMenu proto_prefs_menus_; + int ctx_column_; + QByteArray column_state_; + OverlayScrollBar *overlay_sb_; + int overlay_timer_id_; + bool create_near_overlay_; + bool create_far_overlay_; + QVector overlay_colors_; + bool changing_profile_; + + QModelIndex mouse_pressed_at_; + + RelatedPacketDelegate related_packet_delegate_; + QAction *show_hide_separator_; + QListshow_hide_actions_; + bool capture_in_progress_; + bool tail_at_end_; + bool columns_changed_; + bool set_column_visibility_; + QModelIndex frozen_current_row_; + QModelIndexList frozen_selected_rows_; + QVector selection_history_; + int cur_history_; + bool in_history_; + GPtrArray *finfo_array; // Packet data from the last selected packet entry + + void setFrameReftime(gboolean set, frame_data *fdata); + void setColumnVisibility(); + int sizeHintForColumn(int column) const override; + void setRecentColumnWidth(int column); + void drawCurrentPacket(); + void applyRecentColumnWidths(); + void scrollViewChanged(bool at_end); + void colorsChanged(); + QString joinSummaryRow(QStringList col_parts, int row, SummaryCopyType type); + +signals: + void packetDissectionChanged(); + void showColumnPreferences(QString pane_name); + void editColumn(int column); + void packetListScrolled(bool at_end); + void showProtocolPreferences(const QString module_name); + void editProtocolPreference(struct preference *pref, struct pref_module *module); + + void framesSelected(QList); + void fieldSelected(FieldInformation *); + +public slots: + void setCaptureFile(capture_file *cf); + void setMonospaceFont(const QFont &mono_font); + void goNextPacket(); + void goPreviousPacket(); + void goFirstPacket(); + void goLastPacket(); + void goToPacket(int packet, int hf_id = -1); + void goNextHistoryPacket(); + void goPreviousHistoryPacket(); + void markFrame(); + void markAllDisplayedFrames(bool set); + void ignoreFrame(); + void ignoreAllDisplayedFrames(bool set); + void setTimeReference(); + void unsetAllTimeReferences(); + void applyTimeShift(); + void recolorPackets(); + void redrawVisiblePackets(); + void redrawVisiblePacketsDontSelectCurrent(); + void columnsChanged(); + void fieldsChanged(capture_file *cf); + void preferencesChanged(); + void freezePacketList(bool changing_profile); + +private slots: + void columnVisibilityTriggered(); + void sectionResized(int col, int, int new_width); + void sectionMoved(int, int, int); + void updateRowHeights(const QModelIndex &ih_index); + void copySummary(); + void vScrollBarActionTriggered(int); + void drawFarOverlay(); + void drawNearOverlay(); + void updatePackets(bool redraw); + void ctxDecodeAsDialog(); +}; + +#endif // PACKET_LIST_H diff --git a/ui/qt/packet_range_group_box.cpp b/ui/qt/packet_range_group_box.cpp new file mode 100644 index 00000000..143da613 --- /dev/null +++ b/ui/qt/packet_range_group_box.cpp @@ -0,0 +1,424 @@ +/* packet_range_group_box.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "packet_range_group_box.h" +#include +#include + +PacketRangeGroupBox::PacketRangeGroupBox(QWidget *parent) : + QGroupBox(parent), + pr_ui_(new Ui::PacketRangeGroupBox), + range_(NULL), + syntax_state_(SyntaxLineEdit::Empty) +{ + pr_ui_->setupUi(this); + setFlat(true); + + pr_ui_->displayedButton->setChecked(true); + pr_ui_->allButton->setChecked(true); +} + +PacketRangeGroupBox::~PacketRangeGroupBox() +{ + delete pr_ui_; +} + +void PacketRangeGroupBox::initRange(packet_range_t *range, QString selRange) { + if (!range) return; + + range_ = nullptr; + // Set this before setting range_ so that on_dependedCheckBox_toggled + // doesn't trigger an extra updateCounts(). + // (We could instead manually connect and disconnect signals and slots + // after getting a range, instead of checking for range_ in all the + // slots.) + pr_ui_->dependedCheckBox->setChecked(range->include_dependents); + + range_ = range; + + if (range_->process_filtered) { + pr_ui_->displayedButton->setChecked(true); + } else { + pr_ui_->capturedButton->setChecked(true); + } + + if (selRange.length() > 0) + packet_range_convert_selection_str(range_, selRange.toUtf8().constData()); + + if (range_->user_range) { + char* tmp_str = range_convert_range(NULL, range_->user_range); + pr_ui_->rangeLineEdit->setText(tmp_str); + wmem_free(NULL, tmp_str); + } + updateCounts(); +} + +bool PacketRangeGroupBox::isValid() { + if (pr_ui_->rangeButton->isChecked() && syntax_state_ != SyntaxLineEdit::Empty) { + return false; + } + return true; +} + +void PacketRangeGroupBox::updateCounts() { + SyntaxLineEdit::SyntaxState orig_ss = syntax_state_; + bool displayed_checked = pr_ui_->displayedButton->isChecked(); + bool can_select; + bool selected_packets; + int ignored_cnt = 0, displayed_ignored_cnt = 0; + int depended_cnt = 0, displayed_depended_cnt = 0; + int label_count; + + if (!range_ || !range_->cf) return; + + if (range_->displayed_cnt != 0) { + pr_ui_->displayedButton->setEnabled(true); + } else { + displayed_checked = false; + pr_ui_->capturedButton->setChecked(true); + pr_ui_->displayedButton->setEnabled(false); + } + + // All / Captured + pr_ui_->allCapturedLabel->setEnabled(!displayed_checked); + label_count = range_->cf->count; + if (range_->remove_ignored) { + label_count -= range_->ignored_cnt; + } + pr_ui_->allCapturedLabel->setText(QString("%1").arg(label_count)); + + // All / Displayed + pr_ui_->allDisplayedLabel->setEnabled(displayed_checked); + if (range_->include_dependents) { + label_count = range_->displayed_plus_dependents_cnt; + } else { + label_count = range_->displayed_cnt; + } + if (range_->remove_ignored) { + label_count -= range_->displayed_ignored_cnt; + } + pr_ui_->allDisplayedLabel->setText(QString("%1").arg(label_count)); + + // Selected / Captured + Displayed + can_select = (range_->selection_range_cnt > 0 || range_->displayed_selection_range_cnt > 0); + if (can_select) { + pr_ui_->selectedButton->setEnabled(true); + pr_ui_->selectedCapturedLabel->setEnabled(!displayed_checked); + pr_ui_->selectedDisplayedLabel->setEnabled(displayed_checked); + + if (range_->include_dependents) { + pr_ui_->selectedCapturedLabel->setText(QString::number(range_->selected_plus_depends_cnt)); + pr_ui_->selectedDisplayedLabel->setText(QString::number(range_->displayed_selected_plus_depends_cnt)); + } else { + pr_ui_->selectedCapturedLabel->setText(QString::number(range_->selection_range_cnt)); + pr_ui_->selectedDisplayedLabel->setText(QString::number(range_->displayed_selection_range_cnt)); + } + } else { + if (range_->process == range_process_selected) { + pr_ui_->allButton->setChecked(true); + } + pr_ui_->selectedButton->setEnabled(false); + pr_ui_->selectedCapturedLabel->setEnabled(false); + pr_ui_->selectedDisplayedLabel->setEnabled(false); + + pr_ui_->selectedCapturedLabel->setText("0"); + pr_ui_->selectedDisplayedLabel->setText("0"); + } + + // Marked / Captured + Displayed + if (displayed_checked) { + selected_packets = (range_->displayed_marked_cnt != 0); + } else { + selected_packets = (range_->cf->marked_count > 0); + } + if (selected_packets) { + pr_ui_->markedButton->setEnabled(true); + pr_ui_->markedCapturedLabel->setEnabled(!displayed_checked); + pr_ui_->markedDisplayedLabel->setEnabled(displayed_checked); + } else { + if (range_->process == range_process_marked) { + pr_ui_->allButton->setChecked(true); + } + pr_ui_->markedButton->setEnabled(false); + pr_ui_->markedCapturedLabel->setEnabled(false); + pr_ui_->markedDisplayedLabel->setEnabled(false); + } + if (range_->include_dependents) { + label_count = range_->marked_plus_depends_cnt; + } else { + label_count = range_->cf->marked_count; + } + if (range_->remove_ignored) { + label_count -= range_->ignored_marked_cnt; + } + pr_ui_->markedCapturedLabel->setText(QString("%1").arg(label_count)); + if (range_->include_dependents) { + label_count = range_->displayed_marked_plus_depends_cnt; + } else { + label_count = range_->displayed_marked_cnt; + } + if (range_->remove_ignored) { + label_count -= range_->displayed_ignored_marked_cnt; + } + pr_ui_->markedDisplayedLabel->setText(QString("%1").arg(label_count)); + + // First to last marked / Captured + Displayed + if (displayed_checked) { + selected_packets = (range_->displayed_mark_range_cnt != 0); + } else { + selected_packets = (range_->mark_range_cnt != 0); + } + if (selected_packets) { + pr_ui_->ftlMarkedButton->setEnabled(true); + pr_ui_->ftlCapturedLabel->setEnabled(!displayed_checked); + pr_ui_->ftlDisplayedLabel->setEnabled(displayed_checked); + } else { + if (range_->process == range_process_marked_range) { + pr_ui_->allButton->setChecked(true); + } + pr_ui_->ftlMarkedButton->setEnabled(false); + pr_ui_->ftlCapturedLabel->setEnabled(false); + pr_ui_->ftlDisplayedLabel->setEnabled(false); + } + if (range_->include_dependents) { + label_count = range_->mark_range_plus_depends_cnt; + } else { + label_count = range_->mark_range_cnt; + } + if (range_->remove_ignored) { + label_count -= range_->ignored_mark_range_cnt; + } + pr_ui_->ftlCapturedLabel->setText(QString("%1").arg(label_count)); + if (range_->include_dependents) { + label_count = range_->displayed_mark_range_plus_depends_cnt; + } else { + label_count = range_->displayed_mark_range_cnt; + } + if (range_->remove_ignored) { + label_count -= range_->displayed_ignored_mark_range_cnt; + } + pr_ui_->ftlDisplayedLabel->setText(QString("%1").arg(label_count)); + + // User specified / Captured + Displayed + + pr_ui_->rangeButton->setEnabled(true); + pr_ui_->rangeCapturedLabel->setEnabled(!displayed_checked); + pr_ui_->rangeDisplayedLabel->setEnabled(displayed_checked); + + packet_range_convert_str(range_, pr_ui_->rangeLineEdit->text().toUtf8().constData()); + + switch (packet_range_check(range_)) { + + case CVT_NO_ERROR: + if (range_->include_dependents) { + label_count = range_->user_range_plus_depends_cnt; + } else { + label_count = range_->user_range_cnt; + } + if (range_->remove_ignored) { + label_count -= range_->ignored_user_range_cnt; + } + pr_ui_->rangeCapturedLabel->setText(QString("%1").arg(label_count)); + if (range_->include_dependents) { + label_count = range_->displayed_user_range_plus_depends_cnt; + } else { + label_count = range_->displayed_user_range_cnt; + } + if (range_->remove_ignored) { + label_count -= range_->displayed_ignored_user_range_cnt; + } + pr_ui_->rangeDisplayedLabel->setText(QString("%1").arg(label_count)); + syntax_state_ = SyntaxLineEdit::Empty; + break; + + case CVT_SYNTAX_ERROR: + pr_ui_->rangeCapturedLabel->setText("Bad range"); + pr_ui_->rangeDisplayedLabel->setText("-"); + syntax_state_ = SyntaxLineEdit::Invalid; + break; + + case CVT_NUMBER_TOO_BIG: + pr_ui_->rangeCapturedLabel->setText("Number too large"); + pr_ui_->rangeDisplayedLabel->setText("-"); + syntax_state_ = SyntaxLineEdit::Invalid; + break; + + default: + ws_assert_not_reached(); + return; + } + + // Ignored + switch(range_->process) { + case(range_process_all): + ignored_cnt = range_->ignored_cnt; + displayed_ignored_cnt = range_->displayed_ignored_cnt; + break; + case(range_process_selected): + ignored_cnt = range_->ignored_selection_range_cnt; + displayed_ignored_cnt = range_->displayed_ignored_selection_range_cnt; + break; + case(range_process_marked): + ignored_cnt = range_->ignored_marked_cnt; + displayed_ignored_cnt = range_->displayed_ignored_marked_cnt; + break; + case(range_process_marked_range): + ignored_cnt = range_->ignored_mark_range_cnt; + displayed_ignored_cnt = range_->displayed_ignored_mark_range_cnt; + break; + case(range_process_user_range): + ignored_cnt = range_->ignored_user_range_cnt; + displayed_ignored_cnt = range_->displayed_ignored_user_range_cnt; + break; + default: + ws_assert_not_reached(); + } + + if (displayed_checked) + selected_packets = (displayed_ignored_cnt != 0); + else + selected_packets = (ignored_cnt != 0); + + if (selected_packets) { + pr_ui_->ignoredCheckBox->setEnabled(true); + pr_ui_->ignoredCapturedLabel->setEnabled(!displayed_checked); + pr_ui_->ignoredDisplayedLabel->setEnabled(displayed_checked); + } else { + pr_ui_->ignoredCheckBox->setEnabled(false); + pr_ui_->ignoredCapturedLabel->setEnabled(false); + pr_ui_->ignoredDisplayedLabel->setEnabled(false); + } + pr_ui_->ignoredCapturedLabel->setText(QString("%1").arg(ignored_cnt)); + pr_ui_->ignoredDisplayedLabel->setText(QString("%1").arg(displayed_ignored_cnt)); + + // Depended upon / Displayed + Captured + switch(range_->process) { + case(range_process_all): + depended_cnt = 0; + displayed_depended_cnt = range_->displayed_plus_dependents_cnt - range_->displayed_cnt; + break; + case(range_process_selected): + depended_cnt = range_->selected_plus_depends_cnt - range_->selection_range_cnt; + displayed_depended_cnt = range_->displayed_selected_plus_depends_cnt - range_->displayed_selection_range_cnt; + break; + case(range_process_marked): + depended_cnt = range_->marked_plus_depends_cnt - range_->cf->marked_count; + displayed_depended_cnt = range_->displayed_marked_plus_depends_cnt - range_->displayed_marked_cnt; + break; + case(range_process_marked_range): + depended_cnt = range_->mark_range_plus_depends_cnt - range_->mark_range_cnt; + displayed_depended_cnt = range_->displayed_mark_range_plus_depends_cnt - range_->displayed_mark_range_cnt; + break; + case(range_process_user_range): + depended_cnt = range_->user_range_plus_depends_cnt - range_->user_range_cnt; + displayed_depended_cnt = range_->displayed_user_range_plus_depends_cnt - range_->displayed_user_range_cnt; + break; + default: + depended_cnt = 0; + displayed_depended_cnt = 0; + break; + } + + if (displayed_checked) { + selected_packets = (displayed_depended_cnt != 0); + } else { + selected_packets = (depended_cnt != 0); + } + + if (selected_packets) { + pr_ui_->dependedCheckBox->setEnabled(true); + pr_ui_->dependedCapturedLabel->setEnabled(!displayed_checked); + pr_ui_->dependedDisplayedLabel->setEnabled(displayed_checked); + } else { + pr_ui_->dependedCheckBox->setEnabled(false); + pr_ui_->dependedCapturedLabel->setEnabled(false); + pr_ui_->dependedDisplayedLabel->setEnabled(false); + } + pr_ui_->dependedCapturedLabel->setText(QString("%1").arg(depended_cnt)); + pr_ui_->dependedDisplayedLabel->setText(QString("%1").arg(displayed_depended_cnt)); + + if (orig_ss != syntax_state_) { + pr_ui_->rangeLineEdit->setSyntaxState(syntax_state_); + emit validityChanged(isValid()); + } + emit rangeChanged(); +} + +// Slots + +void PacketRangeGroupBox::on_rangeLineEdit_textChanged(const QString &) +{ + if (!pr_ui_->rangeButton->isChecked()) { + pr_ui_->rangeButton->setChecked(true); + } else { + updateCounts(); + } +} + +void PacketRangeGroupBox::processButtonToggled(bool checked, packet_range_e process) { + if (checked && range_) { + range_->process = process; + } + updateCounts(); +} + +void PacketRangeGroupBox::on_allButton_toggled(bool checked) +{ + processButtonToggled(checked, range_process_all); +} + +void PacketRangeGroupBox::on_selectedButton_toggled(bool checked) +{ + processButtonToggled(checked, range_process_selected); +} + +void PacketRangeGroupBox::on_markedButton_toggled(bool checked) +{ + processButtonToggled(checked, range_process_marked); +} + +void PacketRangeGroupBox::on_ftlMarkedButton_toggled(bool checked) +{ + processButtonToggled(checked, range_process_marked_range); +} + +void PacketRangeGroupBox::on_rangeButton_toggled(bool checked) +{ + processButtonToggled(checked, range_process_user_range); +} + +void PacketRangeGroupBox::on_capturedButton_toggled(bool checked) +{ + if (checked) { + if (range_) range_->process_filtered = FALSE; + updateCounts(); + } +} + +void PacketRangeGroupBox::on_displayedButton_toggled(bool checked) +{ + if (checked) { + if (range_) range_->process_filtered = TRUE; + updateCounts(); + } +} + +void PacketRangeGroupBox::on_ignoredCheckBox_toggled(bool checked) +{ + if (range_) range_->remove_ignored = checked ? TRUE : FALSE; + updateCounts(); +} + +void PacketRangeGroupBox::on_dependedCheckBox_toggled(bool checked) +{ + if (range_) { + range_->include_dependents = checked ? TRUE : FALSE; + updateCounts(); + } +} diff --git a/ui/qt/packet_range_group_box.h b/ui/qt/packet_range_group_box.h new file mode 100644 index 00000000..1912d71d --- /dev/null +++ b/ui/qt/packet_range_group_box.h @@ -0,0 +1,71 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PACKET_RANGE_GROUP_BOX_H +#define PACKET_RANGE_GROUP_BOX_H + +#include + +#include + +#include + +#include +#include + +namespace Ui { +class PacketRangeGroupBox; +} + +/** + * UI element for controlling a range selection. The range provided in + * "initRange" is not owned by this class but will be modified. + */ +class PacketRangeGroupBox : public QGroupBox +{ + Q_OBJECT + +public: + explicit PacketRangeGroupBox(QWidget *parent = 0); + ~PacketRangeGroupBox(); + void initRange(packet_range_t *range, QString selRange = QString()); + bool isValid(); + +signals: + void validityChanged(bool is_valid); + void rangeChanged(); + +private: + void updateCounts(); + void processButtonToggled(bool checked, packet_range_e process); + + Ui::PacketRangeGroupBox *pr_ui_; + packet_range_t *range_; + SyntaxLineEdit::SyntaxState syntax_state_; + +private slots: + void on_rangeLineEdit_textChanged(const QString &range_str); + + void on_allButton_toggled(bool checked); + + void on_selectedButton_toggled(bool checked); + + void on_markedButton_toggled(bool checked); + + void on_ftlMarkedButton_toggled(bool checked); + + void on_rangeButton_toggled(bool checked); + + void on_capturedButton_toggled(bool checked); + void on_displayedButton_toggled(bool checked); + void on_ignoredCheckBox_toggled(bool checked); + void on_dependedCheckBox_toggled(bool checked); +}; + +#endif // PACKET_RANGE_GROUP_BOX_H diff --git a/ui/qt/packet_range_group_box.ui b/ui/qt/packet_range_group_box.ui new file mode 100644 index 00000000..5cceffcd --- /dev/null +++ b/ui/qt/packet_range_group_box.ui @@ -0,0 +1,290 @@ + + + PacketRangeGroupBox + + + + 0 + 0 + 454 + 241 + + + + Form + + + Packet Range + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Displayed + + + true + + + capturedDisplayedButtonGroup + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + &Marked packets only + + + packetSelectionButtonGroup + + + + + + + &Range: + + + packetSelectionButtonGroup + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Remove &ignored packets + + + + + + + Include &depended upon packets + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + First &to last marked + + + packetSelectionButtonGroup + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + &All packets + + + packetSelectionButtonGroup + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + &Selected packets only + + + packetSelectionButtonGroup + + + + + + + Captured + + + true + + + capturedDisplayedButtonGroup + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 63 + 20 + + + + + + + + + 1 + 0 + + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + SyntaxLineEdit + QLineEdit +
widgets/syntax_line_edit.h
+
+
+ + + + + +
diff --git a/ui/qt/preference_editor_frame.cpp b/ui/qt/preference_editor_frame.cpp new file mode 100644 index 00000000..4ebce5c9 --- /dev/null +++ b/ui/qt/preference_editor_frame.cpp @@ -0,0 +1,296 @@ +/* preference_editor_frame.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include +#include + +#include "preference_editor_frame.h" +#include + +#include +#include +#include + +#include "main_application.h" + +#include +#include +#include + +PreferenceEditorFrame::PreferenceEditorFrame(QWidget *parent) : + AccordionFrame(parent), + ui(new Ui::PreferenceEditorFrame), + module_(NULL), + pref_(NULL), + new_uint_(0), + new_str_(""), + new_range_(NULL) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC + foreach (QWidget *w, findChildren()) { + w->setAttribute(Qt::WA_MacSmallSize, true); + } +#endif + + connect(ui->preferenceBrowseButton, &QPushButton::clicked, this, &PreferenceEditorFrame::browsePushButtonClicked); +} + +PreferenceEditorFrame::~PreferenceEditorFrame() +{ + delete ui; +} + +void PreferenceEditorFrame::editPreference(preference *pref, pref_module *module) +{ + pref_ = pref; + module_ = module; + + if (!pref || !module) { + hide(); + return; + } + + ui->modulePreferencesToolButton->setText(tr("Open %1 preferences…").arg(module_->title)); + + pref_stash(pref_, NULL); + ui->preferenceTitleLabel->setText(QString("%1:").arg(prefs_get_title(pref))); + + // Convert the pref description from plain text to rich text. + QString description = html_escape(prefs_get_description(pref)); + description.replace('\n', "
"); + QString tooltip = QString("%1").arg(description); + ui->preferenceTitleLabel->setToolTip(tooltip); + ui->preferenceLineEdit->setToolTip(tooltip); + + ui->preferenceLineEdit->clear(); + ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Empty); + disconnect(ui->preferenceLineEdit, 0, 0, 0); + + bool show = false; + bool browse_button = false; + + switch (prefs_get_type(pref_)) { + case PREF_UINT: + case PREF_DECODE_AS_UINT: + connect(ui->preferenceLineEdit, &SyntaxLineEdit::textChanged, + this, &PreferenceEditorFrame::uintLineEditTextEdited); + show = true; + break; + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + browse_button = true; + // Fallthrough + case PREF_STRING: + case PREF_PASSWORD: + connect(ui->preferenceLineEdit, &SyntaxLineEdit::textChanged, + this, &PreferenceEditorFrame::stringLineEditTextEdited); + show = true; + break; + case PREF_RANGE: + case PREF_DECODE_AS_RANGE: + connect(ui->preferenceLineEdit, &SyntaxLineEdit::textChanged, + this, &PreferenceEditorFrame::rangeLineEditTextEdited); + show = true; + break; + default: + break; + } + + if (show) { + ui->preferenceLineEdit->setText(gchar_free_to_qstring(prefs_pref_to_str(pref_, pref_stashed)).remove(QRegularExpression("\n\t"))); + ui->preferenceBrowseButton->setHidden(!browse_button); + animatedShow(); + } +} + +void PreferenceEditorFrame::uintLineEditTextEdited(const QString &new_str) +{ + if (new_str.isEmpty()) { + new_uint_ = prefs_get_uint_value_real(pref_, pref_stashed); + ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Empty); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + return; + } + + bool ok; + uint new_uint = new_str.toUInt(&ok, 0); + if (ok) { + new_uint_ = new_uint; + ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Valid); + } else { + new_uint_ = prefs_get_uint_value_real(pref_, pref_stashed); + ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Invalid); + } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok); +} + +void PreferenceEditorFrame::stringLineEditTextEdited(const QString &new_str) +{ + new_str_ = new_str; + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); +} + +void PreferenceEditorFrame::browsePushButtonClicked() +{ + QString caption = mainApp->windowTitleString(prefs_get_title(pref_)); + QString dir = prefs_get_string_value(pref_, pref_stashed); + QString filename; + + switch (prefs_get_type(pref_)) { + case PREF_SAVE_FILENAME: + filename = WiresharkFileDialog::getSaveFileName(this, caption, dir); + break; + case PREF_OPEN_FILENAME: + filename = WiresharkFileDialog::getOpenFileName(this, caption, dir); + break; + case PREF_DIRNAME: + filename = WiresharkFileDialog::getExistingDirectory(this, caption, dir); + break; + } + + if (!filename.isEmpty()) { + ui->preferenceLineEdit->setText(filename); + } +} + +void PreferenceEditorFrame::rangeLineEditTextEdited(const QString &new_str) +{ + range_t *new_range = NULL; + + convert_ret_t ret = range_convert_str(NULL, &new_range, new_str.toUtf8().constData(), prefs_get_max_value(pref_)); + wmem_free(NULL, new_range_); + new_range_ = new_range; + + if (ret == CVT_NO_ERROR) { + if (new_str.isEmpty()) { + ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Empty); + } else { + ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Valid); + } + } else { + ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Invalid); + } + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ret == CVT_NO_ERROR); +} + +void PreferenceEditorFrame::showEvent(QShowEvent *event) +{ + ui->preferenceLineEdit->setFocus(); + ui->preferenceLineEdit->selectAll(); + + AccordionFrame::showEvent(event); +} + +void PreferenceEditorFrame::on_modulePreferencesToolButton_clicked() +{ + if (module_) { + emit showProtocolPreferences(module_->name); + } + on_buttonBox_rejected(); +} + +void PreferenceEditorFrame::on_preferenceLineEdit_returnPressed() +{ + if (ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) { + on_buttonBox_accepted(); + } +} + +void PreferenceEditorFrame::on_buttonBox_accepted() +{ + unsigned int changed_flags = 0; + unsigned int apply = 0; + switch(prefs_get_type(pref_)) { + case PREF_UINT: + case PREF_DECODE_AS_UINT: + apply = prefs_set_uint_value(pref_, new_uint_, pref_stashed); + break; + case PREF_STRING: + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + apply = prefs_set_string_value(pref_, new_str_.toStdString().c_str(), pref_stashed); + break; + case PREF_PASSWORD: + apply = prefs_set_password_value(pref_, new_str_.toStdString().c_str(), pref_stashed); + break; + case PREF_RANGE: + case PREF_DECODE_AS_RANGE: + apply = prefs_set_range_value(pref_, new_range_, pref_stashed); + break; + default: + break; + } + + if (apply && module_) { + changed_flags = module_->prefs_changed_flags; + pref_unstash_data_t unstashed_data; + + unstashed_data.module = module_; + unstashed_data.handle_decode_as = TRUE; + + pref_unstash(pref_, &unstashed_data); + prefs_apply(module_); + prefs_main_write(); + + gchar* err = NULL; + if (save_decode_as_entries(&err) < 0) + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err); + g_free(err); + } + } + on_buttonBox_rejected(); + // Emit signals once UI is hidden + if (apply) { + if (changed_flags & PREF_EFFECT_FIELDS) { + mainApp->emitAppSignal(MainApplication::FieldsChanged); + } + mainApp->emitAppSignal(MainApplication::PacketDissectionChanged); + mainApp->emitAppSignal(MainApplication::PreferencesChanged); + } +} + +void PreferenceEditorFrame::on_buttonBox_rejected() +{ + pref_ = NULL; + module_ = NULL; + wmem_free(NULL, new_range_); + new_range_ = NULL; + animatedHide(); +} + +void PreferenceEditorFrame::keyPressEvent(QKeyEvent *event) +{ + if (event->modifiers() == Qt::NoModifier) { + if (event->key() == Qt::Key_Escape) { + on_buttonBox_rejected(); + } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + if (ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) { + on_buttonBox_accepted(); + } else if (ui->preferenceLineEdit->syntaxState() == SyntaxLineEdit::Invalid) { + mainApp->pushStatus(MainApplication::FilterSyntax, tr("Invalid value.")); + } + } + } + + AccordionFrame::keyPressEvent(event); +} diff --git a/ui/qt/preference_editor_frame.h b/ui/qt/preference_editor_frame.h new file mode 100644 index 00000000..e23524de --- /dev/null +++ b/ui/qt/preference_editor_frame.h @@ -0,0 +1,64 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PREFERENCE_EDITOR_FRAME_H +#define PREFERENCE_EDITOR_FRAME_H + +#include "accordion_frame.h" + +struct pref_module; +struct preference; +struct epan_range; + +namespace Ui { +class PreferenceEditorFrame; +} + +class PreferenceEditorFrame : public AccordionFrame +{ + Q_OBJECT + +public: + explicit PreferenceEditorFrame(QWidget *parent = 0); + ~PreferenceEditorFrame(); + +public slots: + void editPreference(struct preference *pref = NULL, struct pref_module *module = NULL); + +signals: + void showProtocolPreferences(const QString module_name); + +protected: + virtual void showEvent(QShowEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + +private slots: + // Similar to ModulePreferencesScrollArea + void uintLineEditTextEdited(const QString &new_str); + void stringLineEditTextEdited(const QString &new_str); + void rangeLineEditTextEdited(const QString &new_str); + void browsePushButtonClicked(); + + void on_modulePreferencesToolButton_clicked(); + void on_preferenceLineEdit_returnPressed(); + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + +private: + Ui::PreferenceEditorFrame *ui; + + struct pref_module *module_; + struct preference *pref_; + + unsigned int new_uint_; + QString new_str_; + struct epan_range *new_range_; +}; + +#endif // PREFERENCE_EDITOR_FRAME_H diff --git a/ui/qt/preference_editor_frame.ui b/ui/qt/preference_editor_frame.ui new file mode 100644 index 00000000..6b8900e2 --- /dev/null +++ b/ui/qt/preference_editor_frame.ui @@ -0,0 +1,116 @@ + + + PreferenceEditorFrame + + + + 0 + 0 + 458 + 34 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + + + + + + + + + + Qt::Horizontal + + + + 81 + 5 + + + + + + + + a preference + + + + + + + + 1 + 0 + + + + + + + + Browse… + + + + + + + Qt::Horizontal + + + + 20 + 13 + + + + + + + + + 16777215 + 27 + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + AccordionFrame + QFrame +
accordion_frame.h
+ 1 +
+ + SyntaxLineEdit + QLineEdit +
widgets/syntax_line_edit.h
+
+
+ + +
diff --git a/ui/qt/preferences_dialog.cpp b/ui/qt/preferences_dialog.cpp new file mode 100644 index 00000000..7f78350c --- /dev/null +++ b/ui/qt/preferences_dialog.cpp @@ -0,0 +1,337 @@ +/* preferences_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "preferences_dialog.h" +#include + +#include "module_preferences_scroll_area.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main_application.h" + +extern "C" { +// Callbacks prefs routines + +static guint +module_prefs_unstash(module_t *module, gpointer data) +{ + gboolean *must_redissect_p = static_cast(data); + pref_unstash_data_t unstashed_data; + + unstashed_data.handle_decode_as = TRUE; + + module->prefs_changed_flags = 0; /* assume none of them changed */ + for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = gxx_list_next(pref_l)) { + pref_t *pref = gxx_list_data(pref_t *, pref_l); + + if (prefs_get_type(pref) == PREF_OBSOLETE || prefs_get_type(pref) == PREF_STATIC_TEXT) continue; + + unstashed_data.module = module; + pref_unstash(pref, &unstashed_data); + commandline_options_drop(module->name, prefs_get_name(pref)); + } + + /* If any of them changed, indicate that we must redissect and refilter + the current capture (if we have one), as the preference change + could cause packets to be dissected differently. */ + *must_redissect_p |= module->prefs_changed_flags; + + if (prefs_module_has_submodules(module)) + return prefs_modules_foreach_submodules(module, module_prefs_unstash, data); + + return 0; /* Keep unstashing. */ +} + +static guint +module_prefs_clean_stash(module_t *module, gpointer) +{ + for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = gxx_list_next(pref_l)) { + pref_t *pref = gxx_list_data(pref_t *, pref_l); + + if (prefs_get_type(pref) == PREF_OBSOLETE || prefs_get_type(pref) == PREF_STATIC_TEXT) continue; + + pref_clean_stash(pref, Q_NULLPTR); + } + + if (prefs_module_has_submodules(module)) + return prefs_modules_foreach_submodules(module, module_prefs_clean_stash, Q_NULLPTR); + + return 0; /* Keep cleaning modules */ +} + +} // extern "C" + +// Preference tree items +const int APPEARANCE_ITEM = 0; + +//placeholder key to keep dynamically loaded preferences +static const char* MODULES_NAME = "Modules"; + +PreferencesDialog::PreferencesDialog(QWidget *parent) : + GeometryStateDialog(parent), + pd_ui_(new Ui::PreferencesDialog), + model_(this), + advancedPrefsModel_(this), + advancedPrefsDelegate_(this), + modulePrefsModel_(this) +{ + advancedPrefsModel_.setSourceModel(&model_); + modulePrefsModel_.setSourceModel(&model_); + saved_capture_no_extcap_ = prefs.capture_no_extcap; + + // Some classes depend on pref_ptr_to_pref_ so this MUST be called after + // model_.populate(). + pd_ui_->setupUi(this); + loadGeometry(); + + setWindowTitle(mainApp->windowTitleString(tr("Preferences"))); + + pd_ui_->advancedView->setModel(&advancedPrefsModel_); + pd_ui_->advancedView->setItemDelegate(&advancedPrefsDelegate_); + advancedPrefsModel_.setFirstColumnSpanned(pd_ui_->advancedView); + + pd_ui_->prefsView->setModel(&modulePrefsModel_); + + pd_ui_->splitter->setStretchFactor(0, 1); + pd_ui_->splitter->setStretchFactor(1, 5); + pd_ui_->prefsView->sortByColumn(ModulePrefsModel::colName, Qt::AscendingOrder); + + //Set the Appearance leaf to expanded + pd_ui_->prefsView->setExpanded(modulePrefsModel_.index(APPEARANCE_ITEM, 0), true); + + + // PreferencesPane, prefsView, and stackedWidget must all correspond to each other. + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::Appearance)] = pd_ui_->appearanceFrame; + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::Layout)] = pd_ui_->layoutFrame; + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::Columns)] = pd_ui_->columnFrame; + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::FontAndColors)] = pd_ui_->fontandcolorFrame; + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::Capture)] = pd_ui_->captureFrame; + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::Expert)] = pd_ui_->expertFrame; + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::FilterButtons)] = pd_ui_->filterExpressonsFrame; + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::RSAKeys)] = pd_ui_->rsaKeysFrame; + prefs_pane_to_item_[PrefsModel::typeToString(PrefsModel::Advanced)] = pd_ui_->advancedFrame; + prefs_pane_to_item_[MODULES_NAME] = NULL; + + pd_ui_->filterExpressonsFrame->setUat(uat_get_table_by_name("Display expressions")); + pd_ui_->expertFrame->setUat(uat_get_table_by_name("Expert Info Severity Level Configuration")); + + connect(pd_ui_->prefsView, &PrefModuleTreeView::goToPane, this, &PreferencesDialog::selectPane); + + /* Create a single-shot timer for debouncing calls to + * updateSearchLineEdit() */ + searchLineEditTimer = new QTimer(this); + searchLineEditTimer->setSingleShot(true); + connect(searchLineEditTimer, &QTimer::timeout, this, &PreferencesDialog::updateSearchLineEdit); +} + +PreferencesDialog::~PreferencesDialog() +{ + delete pd_ui_; + delete searchLineEditTimer; + prefs_modules_foreach_submodules(NULL, module_prefs_clean_stash, NULL); +} + +void PreferencesDialog::setPane(const QString module_name) +{ + pd_ui_->prefsView->setPane(module_name); +} + +void PreferencesDialog::showEvent(QShowEvent *) +{ + QStyleOption style_opt; + int new_prefs_tree_width = pd_ui_->prefsView->style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &style_opt).left(); + QList sizes = pd_ui_->splitter->sizes(); + +#ifdef Q_OS_WIN + new_prefs_tree_width *= 2; +#endif + pd_ui_->prefsView->resizeColumnToContents(ModulePrefsModel::colName); + new_prefs_tree_width += pd_ui_->prefsView->columnWidth(ModulePrefsModel::colName); + pd_ui_->prefsView->setMinimumWidth(new_prefs_tree_width); + + sizes[1] += sizes[0] - new_prefs_tree_width; + sizes[0] = new_prefs_tree_width; + pd_ui_->splitter->setSizes(sizes); + pd_ui_->splitter->setStretchFactor(0, 1); + + pd_ui_->advancedView->expandAll(); + pd_ui_->advancedView->setSortingEnabled(true); + pd_ui_->advancedView->sortByColumn(AdvancedPrefsModel::colName, Qt::AscendingOrder); + + int one_em = fontMetrics().height(); + pd_ui_->advancedView->setColumnWidth(AdvancedPrefsModel::colName, one_em * 12); // Don't let long items widen things too much + pd_ui_->advancedView->resizeColumnToContents(AdvancedPrefsModel::colStatus); + pd_ui_->advancedView->resizeColumnToContents(AdvancedPrefsModel::colType); + pd_ui_->advancedView->setColumnWidth(AdvancedPrefsModel::colValue, one_em * 30); +} + +void PreferencesDialog::selectPane(QString pane) +{ + if (prefs_pane_to_item_.contains(pane)) { + pd_ui_->stackedWidget->setCurrentWidget(prefs_pane_to_item_[pane]); + } else { + //If not found in prefs_pane_to_item_, it must be an individual module + module_t* module = prefs_find_module(pane.toStdString().c_str()); + if (module != NULL) { + QWidget* moduleWindow = prefs_pane_to_item_[MODULES_NAME]; + if (moduleWindow != NULL) { + pd_ui_->stackedWidget->removeWidget(moduleWindow); + delete moduleWindow; + } + + moduleWindow = new ModulePreferencesScrollArea(module); + prefs_pane_to_item_[MODULES_NAME] = moduleWindow; + pd_ui_->stackedWidget->addWidget(moduleWindow); + pd_ui_->stackedWidget->setCurrentWidget(moduleWindow); + } + } +} + +void PreferencesDialog::updateSearchLineEdit() +{ + advancedPrefsModel_.setFilter(searchLineEditText); + /* If items are filtered out, then filtered back in, the tree remains collapsed + Force an expansion */ + pd_ui_->advancedView->expandAll(); +} + +void PreferencesDialog::on_advancedSearchLineEdit_textEdited(const QString &text) +{ + /* As running pd_ui_->advancedView->expandAll() takes a noticeable amount + * of time and so would introduce significant lag while typing a string + * into the Search box, we instead debounce the call to + * updateSearchLineEdit(), so that it doesn't run until a set amount of + * time has elapsed with no updates to the Search field. + * + * If the user types something before the timer elapses, the timer restarts + * the countdown. + */ + searchLineEditText = text; + guint gui_debounce_timer = prefs_get_uint_value("gui", "debounce.timer"); + searchLineEditTimer->start(gui_debounce_timer); +} + +void PreferencesDialog::on_showChangedValuesCheckBox_toggled(bool checked) +{ + advancedPrefsModel_.setShowChangedValues(checked); + /* If items are filtered out, then filtered back in, the tree remains collapsed + Force an expansion */ + pd_ui_->advancedView->expandAll(); +} + +void PreferencesDialog::on_buttonBox_accepted() +{ + gchar* err = NULL; + unsigned int redissect_flags = 0; + + // XXX - We should validate preferences as the user changes them, not here. + // XXX - We're also too enthusiastic about setting must_redissect. + prefs_modules_foreach_submodules(NULL, module_prefs_unstash, (gpointer)&redissect_flags); + + if (redissect_flags & PREF_EFFECT_GUI_LAYOUT) { + // Layout type changed, reset sizes + recent.gui_geometry_main_upper_pane = 0; + recent.gui_geometry_main_lower_pane = 0; + } + + pd_ui_->columnFrame->unstash(); + pd_ui_->filterExpressonsFrame->acceptChanges(); + pd_ui_->expertFrame->acceptChanges(); +#ifdef HAVE_LIBGNUTLS + pd_ui_->rsaKeysFrame->acceptChanges(); +#endif + + //Filter expressions don't affect dissection, so there is no need to + //send any events to that effect. However, the app needs to know + //about any button changes. + mainApp->emitAppSignal(MainApplication::FilterExpressionsChanged); + + prefs_main_write(); + if (save_decode_as_entries(&err) < 0) + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err); + g_free(err); + } + + write_language_prefs(); + mainApp->loadLanguage(QString(language)); + +#ifdef HAVE_AIRPCAP + /* + * Load the Wireshark decryption keys (just set) and save + * the changes to the adapters' registry + */ + //airpcap_load_decryption_keys(airpcap_if_list); +#endif + + // gtk/prefs_dlg.c:prefs_main_apply_all + /* + * Apply the protocol preferences first - "gui_prefs_apply()" could + * cause redissection, and we have to make sure the protocol + * preference changes have been fully applied. + */ + prefs_apply_all(); + + /* Fill in capture options with values from the preferences */ + prefs_to_capture_opts(); + +#ifdef HAVE_AIRPCAP +// prefs_airpcap_update(); +#endif + + mainApp->setMonospaceFont(prefs.gui_font_name); + + if (redissect_flags & PREF_EFFECT_FIELDS) { + mainApp->queueAppSignal(MainApplication::FieldsChanged); + } + + if (redissect_flags & PREF_EFFECT_DISSECTION) { + // Freeze the packet list early to avoid updating column data before doing a + // full redissection. The packet list will be thawed when redissection is done. + mainApp->queueAppSignal(MainApplication::FreezePacketList); + + /* Redissect all the packets, and re-evaluate the display filter. */ + mainApp->queueAppSignal(MainApplication::PacketDissectionChanged); + } + mainApp->queueAppSignal(MainApplication::PreferencesChanged); + + if (redissect_flags & PREF_EFFECT_GUI_LAYOUT) { + mainApp->queueAppSignal(MainApplication::RecentPreferencesRead); + } + + if (prefs.capture_no_extcap != saved_capture_no_extcap_) + mainApp->refreshLocalInterfaces(); +} + +void PreferencesDialog::on_buttonBox_rejected() +{ + //handle frames that don't have their own OK/Cancel "buttons" + pd_ui_->filterExpressonsFrame->rejectChanges(); + pd_ui_->expertFrame->rejectChanges(); +#ifdef HAVE_LIBGNUTLS + pd_ui_->rsaKeysFrame->rejectChanges(); +#endif +} + +void PreferencesDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_PREFERENCES_DIALOG); +} diff --git a/ui/qt/preferences_dialog.h b/ui/qt/preferences_dialog.h new file mode 100644 index 00000000..51760699 --- /dev/null +++ b/ui/qt/preferences_dialog.h @@ -0,0 +1,78 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PREFERENCES_DIALOG_H +#define PREFERENCES_DIALOG_H + +#include + +#include + +#include +#include + +#include "geometry_state_dialog.h" + +class QComboBox; + +namespace Ui { +class PreferencesDialog; +} + +class PreferencesDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit PreferencesDialog(QWidget *parent = 0); + ~PreferencesDialog(); + + /** + * Show the preference pane corresponding to the a preference module name. + * @param module_name A preference module name, e.g. the "name" parameter passed + * to prefs_register_module or a protocol name. + */ + void setPane(const QString module_name); + +protected: + void showEvent(QShowEvent *evt); + +private: + Ui::PreferencesDialog *pd_ui_; + + QHash prefs_pane_to_item_; + + PrefsModel model_; + AdvancedPrefsModel advancedPrefsModel_; + AdvancedPrefDelegate advancedPrefsDelegate_; + ModulePrefsModel modulePrefsModel_; + gboolean saved_capture_no_extcap_; + + QTimer *searchLineEditTimer; + QString searchLineEditText; + +private slots: + void selectPane(QString pane); + void on_advancedSearchLineEdit_textEdited(const QString &search_re); + void on_showChangedValuesCheckBox_toggled(bool checked); + + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + void on_buttonBox_helpRequested(); + + /** + * Update search results from the advancedSearchLineEdit field + * + * This is performed separately from on_advancedSearchLineEdit_textEdited + * to support debouncing. + */ + void updateSearchLineEdit(); +}; + +#endif // PREFERENCES_DIALOG_H diff --git a/ui/qt/preferences_dialog.ui b/ui/qt/preferences_dialog.ui new file mode 100644 index 00000000..e6c625bb --- /dev/null +++ b/ui/qt/preferences_dialog.ui @@ -0,0 +1,205 @@ + + + PreferencesDialog + + + + 0 + 0 + 680 + 475 + + + + + + + Qt::Horizontal + + + + + 0 + 0 + + + + true + + + true + + + true + + + + + + 0 + 0 + + + + 6 + + + + + + + + + + + + + + + + + Search: + + + + + + + + + + Checking this will show only changed preferences. + + + Show changed values + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + true + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + MainWindowPreferencesFrame + QFrame +
main_window_preferences_frame.h
+ 1 +
+ + LayoutPreferencesFrame + QFrame +
layout_preferences_frame.h
+ 1 +
+ + ColumnPreferencesFrame + QFrame +
column_preferences_frame.h
+ 1 +
+ + FontColorPreferencesFrame + QFrame +
font_color_preferences_frame.h
+ 1 +
+ + CapturePreferencesFrame + QFrame +
capture_preferences_frame.h
+ 1 +
+ + UatFrame + QFrame +
uat_frame.h
+ 1 +
+ + PrefModuleTreeView + QTreeView +
widgets/pref_module_view.h
+ 1 +
+ + RsaKeysFrame + QFrame +
rsa_keys_frame.h
+ 1 +
+
+ + + + buttonBox + accepted() + PreferencesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PreferencesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/print_dialog.cpp b/ui/qt/print_dialog.cpp new file mode 100644 index 00000000..84c2d906 --- /dev/null +++ b/ui/qt/print_dialog.cpp @@ -0,0 +1,361 @@ +/* print_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "print_dialog.h" +#include + +#include + +#ifdef Q_OS_WIN +#include +#include "ui/packet_range.h" +#include "ui/win32/file_dlg_win32.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "main_application.h" + +extern "C" { + +// Page element callbacks + + +static gboolean +print_preamble_pd(print_stream_t *self, gchar *, const char *) +{ + if (!self) return FALSE; + PrintDialog *print_dlg = static_cast(self->data); + if (!print_dlg) return FALSE; + + return print_dlg->printHeader(); +} + +static gboolean +print_line_pd(print_stream_t *self, int indent, const char *line) +{ + if (!self) return FALSE; + PrintDialog *print_dlg = static_cast(self->data); + if (!print_dlg) return FALSE; + + return print_dlg->printLine(indent, line); +} + +static gboolean +new_page_pd(print_stream_t *self) +{ + if (!self) return FALSE; + PrintDialog *print_dlg = static_cast(self->data); + if (!print_dlg) return FALSE; + + return print_dlg->printHeader(); +} + +} // extern "C" + +PrintDialog::PrintDialog(QWidget *parent, capture_file *cf, QString selRange) : + QDialog(parent), + pd_ui_(new Ui::PrintDialog), + cur_printer_(NULL), + cur_painter_(NULL), + preview_(new QPrintPreviewWidget(&printer_)), + print_bt_(new QPushButton(tr("&Print…"))), + cap_file_(cf), + page_pos_(0), + in_preview_(FALSE) +{ + Q_ASSERT(cf); + + pd_ui_->setupUi(this); + setWindowTitle(mainApp->windowTitleString(tr("Print"))); + + pd_ui_->previewLayout->insertWidget(0, preview_, Qt::AlignTop); + + preview_->setMinimumWidth(preview_->height() / 2); + preview_->setToolTip(pd_ui_->zoomLabel->toolTip()); + + // XXX Make these configurable + header_font_.setFamily("Times"); + header_font_.setPointSizeF(header_font_.pointSizeF() * 0.8); + packet_font_ = mainApp->monospaceFont(); + packet_font_.setPointSizeF(packet_font_.pointSizeF() * 0.8); + + memset(&print_args_, 0, sizeof(print_args_)); + memset(&stream_ops_, 0, sizeof(stream_ops_)); + + /* Init the export range */ + packet_range_init(&print_args_.range, cap_file_); + /* Default to displayed packets */ + print_args_.range.process_filtered = TRUE; + + stream_ops_.print_preamble = print_preamble_pd; + stream_ops_.print_line = print_line_pd; + stream_ops_.new_page = new_page_pd; + + stream_.data = this; + stream_.ops = &stream_ops_; + print_args_.stream = &stream_; + + gchar *display_basename = g_filename_display_basename(cap_file_->filename); + printer_.setDocName(display_basename); + g_free(display_basename); + + pd_ui_->rangeGroupBox->initRange(&print_args_.range, selRange); + + pd_ui_->buttonBox->addButton(print_bt_, QDialogButtonBox::ActionRole); + pd_ui_->buttonBox->addButton(tr("Page &Setup…"), QDialogButtonBox::ResetRole); + print_bt_->setDefault(true); + + connect(preview_, SIGNAL(paintRequested(QPrinter*)), this, SLOT(paintPreview(QPrinter*))); + connect(pd_ui_->rangeGroupBox, SIGNAL(rangeChanged()), + this, SLOT(checkValidity())); + connect(pd_ui_->formatGroupBox, SIGNAL(formatChanged()), + this, SLOT(checkValidity())); + connect(pd_ui_->formFeedCheckBox, SIGNAL(toggled(bool)), + preview_, SLOT(updatePreview())); + connect(pd_ui_->bannerCheckBox, SIGNAL(toggled(bool)), + preview_, SLOT(updatePreview())); + + checkValidity(); +} + +PrintDialog::~PrintDialog() +{ + packet_range_cleanup(&print_args_.range); + delete pd_ui_; +} + +gboolean PrintDialog::printHeader() +{ + if (!cap_file_ || !cap_file_->filename || !cur_printer_ || !cur_painter_) return FALSE; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + int page_top = cur_printer_->pageLayout().paintRectPixels(cur_printer_->resolution()).top(); +#else + int page_top = cur_printer_->pageRect().top(); +#endif + + if (page_pos_ > page_top) { + if (in_preview_) { + // When generating a preview, only generate the first page; + // if we're past the first page, stop the printing process. + return FALSE; + } + // Second and subsequent pages only + cur_printer_->newPage(); + page_pos_ = page_top; + } + + if (pd_ui_->bannerCheckBox->isChecked()) { + QString banner = QString(tr("%1 %2 total packets, %3 shown")) + .arg(cap_file_->filename) + .arg(cap_file_->count) + .arg(cap_file_->displayed_count); + cur_painter_->setFont(header_font_); + cur_painter_->drawText(0, page_top, banner); + } + page_pos_ += cur_painter_->fontMetrics().height(); + cur_painter_->setFont(packet_font_); + return TRUE; +} + +gboolean PrintDialog::printLine(int indent, const char *line) +{ + QRect out_rect, page_rect; + QString out_line; + + if (!line || !cur_printer_ || !cur_painter_) return FALSE; + + /* Prepare the tabs for printing, depending on tree level */ + out_line.fill(' ', indent * 4); + out_line += line; + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + page_rect = cur_printer_->pageLayout().paintRectPixels(cur_printer_->resolution()); +#else + page_rect = cur_printer_->pageRect(); +#endif + + out_rect = cur_painter_->boundingRect(page_rect, Qt::TextWordWrap, out_line); + + if (page_rect.height() < page_pos_ + out_rect.height()) { + // + // We're past the end of the page, so this line will be on + // the next page. + // + if (in_preview_) { + // When generating a preview, only generate the first page; + // if we're past the first page, stop the printing process. + return FALSE; + } + if (*line == '\0') { + // This is an empty line, so it's a separator; no need to + // waste space printing it at the top of a page, as the + // page break suffices as a separator. + return TRUE; + } + printHeader(); + } + + out_rect.translate(0, page_pos_); + cur_painter_->drawText(out_rect, Qt::TextWordWrap, out_line); + page_pos_ += out_rect.height(); + return TRUE; +} + +// Protected + +void PrintDialog::keyPressEvent(QKeyEvent *event) +{ + // XXX - This differs from the main window but matches other applications (e.g. Mozilla and Safari) + switch(event->key()) { + case Qt::Key_Minus: + case Qt::Key_Underscore: // Shifted minus on U.S. keyboards + preview_->zoomOut(); + break; + case Qt::Key_Plus: + case Qt::Key_Equal: // Unshifted plus on U.S. keyboards + preview_->zoomIn(); + break; + case Qt::Key_0: + case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards + // fitInView doesn't grow (at least in Qt 4.8.2) so make sure it shrinks. + preview_->setUpdatesEnabled(false); + preview_->setZoomFactor(1.0); + preview_->fitInView(); + preview_->setUpdatesEnabled(true); + break; + } + + QDialog::keyPressEvent(event); +} + +// Private + +void PrintDialog::printPackets(QPrinter *printer, bool in_preview) +{ + QPainter painter; + + if (!printer) return; + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + page_pos_ = printer->pageLayout().paintRectPixels(printer->resolution()).top(); +#else + page_pos_ = printer->pageRect().top(); +#endif + in_preview_ = in_preview; + + /* Fill in our print args */ + + print_args_.format = PR_FMT_TEXT; + print_args_.print_summary = pd_ui_->formatGroupBox->summaryEnabled(); + print_args_.print_col_headings = pd_ui_->formatGroupBox->includeColumnHeadingsEnabled(); + print_args_.print_hex = pd_ui_->formatGroupBox->bytesEnabled(); + print_args_.hexdump_options = pd_ui_->formatGroupBox->getHexdumpOptions(); + print_args_.print_formfeed = pd_ui_->formFeedCheckBox->isChecked(); + + print_args_.print_dissections = print_dissections_none; + if (pd_ui_->formatGroupBox->detailsEnabled()) { + if (pd_ui_->formatGroupBox->allCollapsedEnabled()) + print_args_.print_dissections = print_dissections_collapsed; + else if (pd_ui_->formatGroupBox->asDisplayedEnabled()) + print_args_.print_dissections = print_dissections_as_displayed; + else if (pd_ui_->formatGroupBox->allExpandedEnabled()) + print_args_.print_dissections = print_dissections_expanded; + } + + // This should be identical to printer_. However, the QPrintPreviewWidget docs + // tell us to draw on the printer handed to us by the paintRequested() signal. + cur_printer_ = printer; + cur_painter_ = &painter; + if (!painter.begin(printer)) { + QMessageBox::warning(this, tr("Print Error"), + QString(tr("Unable to print to %1.")).arg(printer_.printerName()), + QMessageBox::Ok); + close(); + } + // Don't show a progress bar if we're previewing; if it takes a + // significant amount of time to generate a preview of the first + // page, We Have A Real Problem + cf_print_packets(cap_file_, &print_args_, in_preview ? FALSE : TRUE); + cur_printer_ = NULL; + cur_painter_ = NULL; + painter.end(); +} + +void PrintDialog::paintPreview(QPrinter *printer) +{ + printPackets(printer, true); +} + +void PrintDialog::checkValidity() +{ + bool enable = true; + + if (!pd_ui_->rangeGroupBox->isValid()) enable = false; + + if (!pd_ui_->formatGroupBox->summaryEnabled() && + !pd_ui_->formatGroupBox->detailsEnabled() && + !pd_ui_->formatGroupBox->bytesEnabled()) + { + enable = false; + } + + print_bt_->setEnabled(enable); + preview_->updatePreview(); +} + +void PrintDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_PRINT_DIALOG); +} + +void PrintDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + QPrintDialog *print_dlg; + QPageSetupDialog *ps_dlg; +#ifdef Q_OS_WIN + HANDLE da_ctx; +#endif + + switch (pd_ui_->buttonBox->buttonRole(button)) { + case QDialogButtonBox::ActionRole: + int result; +#ifdef Q_OS_WIN + da_ctx = set_thread_per_monitor_v2_awareness(); +#endif + print_dlg = new QPrintDialog(&printer_, this); + result = print_dlg->exec(); +#ifdef Q_OS_WIN + revert_thread_per_monitor_v2_awareness(da_ctx); +#endif + if (result == QDialog::Accepted) { + printPackets(&printer_, false); + done(result); + } + break; + case QDialogButtonBox::ResetRole: +#ifdef Q_OS_WIN + da_ctx = set_thread_per_monitor_v2_awareness(); +#endif + ps_dlg = new QPageSetupDialog(&printer_, this); + ps_dlg->exec(); +#ifdef Q_OS_WIN + revert_thread_per_monitor_v2_awareness(da_ctx); +#endif + preview_->updatePreview(); + break; + default: // Help, Cancel + break; + } +} diff --git a/ui/qt/print_dialog.h b/ui/qt/print_dialog.h new file mode 100644 index 00000000..88bda81e --- /dev/null +++ b/ui/qt/print_dialog.h @@ -0,0 +1,71 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PRINT_DIALOG_H +#define PRINT_DIALOG_H + +#include + +#include + +#include "file.h" + +#include +#include +#include +#include + +namespace Ui { + class PrintDialog; +} + +class PrintDialog : public QDialog +{ + Q_OBJECT + +public: + explicit PrintDialog(QWidget *parent = 0, capture_file *cf = NULL, QString selRange = QString()); + ~PrintDialog(); + + gboolean printHeader(); + gboolean printLine(int indent, const char *line); + +protected: + virtual void keyPressEvent(QKeyEvent *event) override; + +private: + Ui::PrintDialog *pd_ui_; + + QPrinter printer_; + QPrinter *cur_printer_; + QPainter *cur_painter_; + QPrintPreviewWidget *preview_; + QPushButton *print_bt_; + QFont header_font_; + QFont packet_font_; +public: + capture_file *cap_file_; +private: + print_args_t print_args_; + print_stream_ops_t stream_ops_; + print_stream_t stream_; + int page_pos_; + bool in_preview_; + + void printPackets(QPrinter *printer = NULL, bool in_preview = false); + +private slots: + void paintPreview(QPrinter *printer); + void checkValidity(); + void on_buttonBox_helpRequested(); + void on_buttonBox_clicked(QAbstractButton *button); +}; + + +#endif // PRINT_DIALOG_H diff --git a/ui/qt/print_dialog.ui b/ui/qt/print_dialog.ui new file mode 100644 index 00000000..60753245 --- /dev/null +++ b/ui/qt/print_dialog.ui @@ -0,0 +1,152 @@ + + + PrintDialog + + + + 0 + 0 + 496 + 328 + + + + + + + + + + + Packet Format + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Print each packet on a new page + + + + + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + + + Capture information header + + + true + + + + + + + Qt::Vertical + + + + 118 + 28 + + + + + + + + false + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + + + + + + + + + + + Packet Range + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help + + + + + + + + PacketRangeGroupBox + QGroupBox +
packet_range_group_box.h
+ 1 +
+ + PacketFormatGroupBox + QGroupBox +
packet_format_group_box.h
+ 1 +
+
+ + + + buttonBox + accepted() + PrintDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PrintDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/profile_dialog.cpp b/ui/qt/profile_dialog.cpp new file mode 100644 index 00000000..5b53a2cf --- /dev/null +++ b/ui/qt/profile_dialog.cpp @@ -0,0 +1,736 @@ +/* profile_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" +#include + +#include "wsutil/filesystem.h" +#include "wsutil/utf8_entities.h" +#include "epan/prefs.h" + +#include + +#include "ui/profile.h" +#include "ui/recent.h" + +#include +#include + +#include "profile_dialog.h" +#include +#include "main_application.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROFILE_EXPORT_PROPERTY "export" +#define PROFILE_EXPORT_ALL "all" +#define PROFILE_EXPORT_SELECTED "selected" + +ProfileDialog::ProfileDialog(QWidget *parent) : + GeometryStateDialog(parent), + pd_ui_(new Ui::ProfileDialog), + ok_button_(Q_NULLPTR), + import_button_(Q_NULLPTR), + model_(Q_NULLPTR), + sort_model_(Q_NULLPTR) +{ + pd_ui_->setupUi(this); + loadGeometry(); + setWindowTitle(mainApp->windowTitleString(tr("Configuration Profiles"))); + + ok_button_ = pd_ui_->buttonBox->button(QDialogButtonBox::Ok); + + // XXX - Use NSImageNameAddTemplate and NSImageNameRemoveTemplate to set stock + // icons on macOS. + // Are there equivalent stock icons on Windows? + pd_ui_->newToolButton->setStockIcon("list-add"); + pd_ui_->deleteToolButton->setStockIcon("list-remove"); + pd_ui_->copyToolButton->setStockIcon("list-copy"); +#ifdef Q_OS_MAC + pd_ui_->newToolButton->setAttribute(Qt::WA_MacSmallSize, true); + pd_ui_->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true); + pd_ui_->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true); + pd_ui_->hintLabel->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + import_button_ = pd_ui_->buttonBox->addButton(tr("Import", "noun"), QDialogButtonBox::ActionRole); + +#ifdef HAVE_MINIZIP + export_button_ = pd_ui_->buttonBox->addButton(tr("Export", "noun"), QDialogButtonBox::ActionRole); + + QMenu * importMenu = new QMenu(import_button_); + QAction * entry = importMenu->addAction(tr("From Zip File...")); + connect(entry, &QAction::triggered, this, &ProfileDialog::importFromZip, Qt::QueuedConnection); + entry = importMenu->addAction(tr("From Directory...")); + connect(entry, &QAction::triggered, this, &ProfileDialog::importFromDirectory, Qt::QueuedConnection); + import_button_->setMenu(importMenu); + + QMenu * exportMenu = new QMenu(export_button_); + export_selected_entry_ = exportMenu->addAction(tr("%Ln Selected Personal Profile(s)...", "", 0)); + export_selected_entry_->setProperty(PROFILE_EXPORT_PROPERTY, PROFILE_EXPORT_SELECTED); + connect(export_selected_entry_, &QAction::triggered, this, &ProfileDialog::exportProfiles, Qt::QueuedConnection); + entry = exportMenu->addAction(tr("All Personal Profiles...")); + entry->setProperty(PROFILE_EXPORT_PROPERTY, PROFILE_EXPORT_ALL); + connect(entry, &QAction::triggered, this, &ProfileDialog::exportProfiles, Qt::QueuedConnection); + export_button_->setMenu(exportMenu); +#else + connect(import_button_, &QPushButton::clicked, this, &ProfileDialog::importFromDirectory); +#endif + + resetTreeView(); + + /* Select the row for the currently selected profile or the first row if non is selected*/ + selectProfile(); + + pd_ui_->cmbProfileTypes->addItems(ProfileSortModel::filterTypes()); + + connect (pd_ui_->cmbProfileTypes, &QComboBox::currentTextChanged, + this, &ProfileDialog::filterChanged); + connect (pd_ui_->lineProfileFilter, &QLineEdit::textChanged, + this, &ProfileDialog::filterChanged); + + currentItemChanged(); + + pd_ui_->profileTreeView->setFocus(); +} + +ProfileDialog::~ProfileDialog() +{ + delete pd_ui_; + empty_profile_list (TRUE); +} + +void ProfileDialog::keyPressEvent(QKeyEvent *evt) +{ + if (pd_ui_->lineProfileFilter->hasFocus() && (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return)) + return; + QDialog::keyPressEvent(evt); +} + +void ProfileDialog::selectProfile(QString profile) +{ + if (profile.isEmpty()) + profile = QString(get_profile_name()); + + int row = model_->findByName(profile); + QModelIndex idx = sort_model_->mapFromSource(model_->index(row, ProfileModel::COL_NAME)); + if (idx.isValid()) + pd_ui_->profileTreeView->selectRow(idx.row()); +} + +int ProfileDialog::execAction(ProfileDialog::ProfileAction profile_action) +{ + int ret = QDialog::Accepted; + QModelIndex item; + + switch (profile_action) { + case ShowProfiles: + ret = exec(); + break; + case NewProfile: + on_newToolButton_clicked(); + ret = exec(); + break; + case ImportZipProfile: +#ifdef HAVE_MINIZIP + importFromZip(); +#endif + break; + case ImportDirProfile: + importFromDirectory(); + break; + case ExportSingleProfile: +#ifdef HAVE_MINIZIP + exportProfiles(); +#endif + break; + case ExportAllProfiles: +#ifdef HAVE_MINIZIP + exportProfiles(true); +#endif + break; + case EditCurrentProfile: + item = pd_ui_->profileTreeView->currentIndex(); + if (item.isValid()) { + pd_ui_->profileTreeView->edit(item); + } + ret = exec(); + break; + case DeleteCurrentProfile: + if (delete_current_profile()) { + mainApp->setConfigurationProfile (Q_NULLPTR); + } + break; + } + return ret; +} + +QModelIndexList ProfileDialog::selectedProfiles() +{ + QModelIndexList profiles; + + foreach (QModelIndex idx, pd_ui_->profileTreeView->selectionModel()->selectedIndexes()) + { + QModelIndex temp = sort_model_->mapToSource(idx); + if (! temp.isValid() || profiles.contains(temp) || temp.column() != ProfileModel::COL_NAME) + continue; + + profiles << temp; + } + + return profiles; +} + +void ProfileDialog::selectionChanged() +{ + if (selectedProfiles().count() == 0) + pd_ui_->profileTreeView->selectRow(0); + + updateWidgets(); +} + +void ProfileDialog::updateWidgets() +{ + bool enable_del = true; + bool enable_ok = true; + bool multiple = false; + bool enable_import = true; + int user_profiles = 0; + + QString msg; + QModelIndex index = sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex()); + QModelIndexList profiles = selectedProfiles(); + + /* Ensure that the index is always the name column */ + if (index.column() != ProfileModel::COL_NAME) + index = index.sibling(index.row(), ProfileModel::COL_NAME); + + /* check if more than one viable profile is selected, and inform the sorting model */ + if (profiles.count() > 1) + multiple = true; + + /* Check if user profiles have been selected and allow export if it is so */ + for (int cnt = 0; cnt < profiles.count(); cnt++) + { + if (! profiles[cnt].data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! profiles[cnt].data(ProfileModel::DATA_IS_DEFAULT).toBool()) + user_profiles++; + } + if (model_->changesPending()) + { + enable_import = false; + msg = tr("An import of profiles is not allowed, while changes are pending"); + } + else if (model_->importPending()) + { + enable_import = false; + msg = tr("An import is pending to be saved. Additional imports are not allowed"); + } + import_button_->setToolTip(msg); + import_button_->setEnabled(enable_import); + +#ifdef HAVE_MINIZIP + bool contains_user = false; + bool enable_export = false; + + if (user_profiles > 0) + contains_user = true; + + /* enable export if no changes are pending */ + if (! model_->changesPending()) + enable_export = true; + + export_button_->setEnabled(enable_export); + if (! enable_export) + { + if (! contains_user) + export_button_->setToolTip(tr("An export of profiles is only allowed for personal profiles")); + else + export_button_->setToolTip(tr("An export of profiles is not allowed, while changes are pending")); + } + export_selected_entry_->setVisible(contains_user); +#endif + + /* if the current profile is default with reset pending or a global one, deactivate delete */ + if (! multiple) + { + if (index.isValid()) + { + if (index.data(ProfileModel::DATA_IS_GLOBAL).toBool()) + enable_del = false; + else if (index.data(ProfileModel::DATA_IS_DEFAULT).toBool() && model_->resetDefault()) + enable_del = false; + } + else if (! index.isValid()) + enable_del = false; + } + + QString hintUrl; + msg.clear(); + if (multiple) + { + /* multiple profiles are being selected, copy is no longer allowed */ + pd_ui_->copyToolButton->setEnabled(false); + + msg = tr("%Ln Selected Personal Profile(s)...", "", user_profiles); + pd_ui_->hintLabel->setText(msg); +#ifdef HAVE_MINIZIP + export_selected_entry_->setText(msg); +#endif + } + else + { + /* if only one profile is selected, display it's path in the hint label and activate link (if allowed) */ + if (index.isValid()) + { + QString temp = index.data(ProfileModel::DATA_PATH).toString(); + if (index.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool() && QFileInfo(temp).isDir()) + hintUrl = QUrl::fromLocalFile(temp).toString(); + pd_ui_->hintLabel->setText(temp); + pd_ui_->hintLabel->setToolTip(index.data(Qt::ToolTipRole).toString()); + + if (! index.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! index.data(ProfileModel::DATA_IS_DEFAULT).toBool()) + msg = tr("%Ln Selected Personal Profile(s)...", "", 1); + } + + pd_ui_->copyToolButton->setEnabled(true); +#ifdef HAVE_MINIZIP + export_selected_entry_->setText(msg); +#endif + } + + /* Ensure, that the ok button is disabled, if an invalid name is used or if duplicate global profiles exist */ + if (model_ && model_->rowCount() > 0) + { + msg.clear(); + for (int row = 0; row < model_->rowCount() && enable_ok; row++) + { + QModelIndex idx = model_->index(row, ProfileModel::COL_NAME); + QString name = idx.data().toString(); + + if (! ProfileModel::checkNameValidity(name, &msg)) + { + if (idx == index || selectedProfiles().contains(idx)) + { + hintUrl.clear(); + pd_ui_->hintLabel->setText(msg); + } + + enable_ok = false; + continue; + } + + if (model_->checkInvalid(idx) || (! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() && model_->checkIfDeleted(idx)) ) + { + if (idx == index) + hintUrl.clear(); + enable_ok = false; + continue; + } + + if (idx != index && idx.data().toString().compare(index.data().toString()) == 0) + { + if (idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() == index.data(ProfileModel::DATA_IS_GLOBAL).toBool()) + enable_ok = false; + } + + QList rows = model_->findAllByNameAndVisibility(name, idx.data(ProfileModel::DATA_IS_GLOBAL).toBool()); + if (rows.count() > 1) + enable_ok = false; + } + + if (enable_ok && ! model_->checkIfDeleted(index) && index.data(ProfileModel::DATA_STATUS).toInt() == PROF_STAT_CHANGED) + hintUrl.clear(); + } + + pd_ui_->hintLabel->setUrl(hintUrl); + + /* ensure the name column is resized to it's content */ + pd_ui_->profileTreeView->resizeColumnToContents(ProfileModel::COL_NAME); + + pd_ui_->deleteToolButton->setEnabled(enable_del); + ok_button_->setEnabled(enable_ok); +} + +void ProfileDialog::currentItemChanged(const QModelIndex &, const QModelIndex &) +{ + updateWidgets(); +} + +void ProfileDialog::on_newToolButton_clicked() +{ + pd_ui_->lineProfileFilter->setText(""); + pd_ui_->cmbProfileTypes->setCurrentIndex(ProfileSortModel::AllProfiles); + sort_model_->setFilterString(); + + QModelIndex ridx = sort_model_->mapFromSource(model_->addNewProfile(tr("New profile"))); + if (ridx.isValid()) + { + pd_ui_->profileTreeView->setCurrentIndex(ridx); + pd_ui_->profileTreeView->scrollTo(ridx); + pd_ui_->profileTreeView->edit(ridx); + currentItemChanged(); + } + else + updateWidgets(); +} + +void ProfileDialog::on_deleteToolButton_clicked() +{ + QModelIndexList profiles = selectedProfiles(); + if (profiles.count() <= 0) + return; + + model_->deleteEntries(profiles); + + bool isGlobal = model_->activeProfile().data(ProfileModel::DATA_IS_GLOBAL).toBool(); + int row = model_->findByName(model_->activeProfile().data().toString()); + /* If the active profile is deleted, the default is selected next */ + if (row < 0) + row = 0; + QModelIndex newIdx = sort_model_->mapFromSource(model_->index(row, 0)); + if (newIdx.data(ProfileModel::DATA_IS_GLOBAL).toBool() != isGlobal) + newIdx = sort_model_->mapFromSource(model_->index(0, 0)); + + pd_ui_->profileTreeView->setCurrentIndex(newIdx); + + updateWidgets(); +} + +void ProfileDialog::on_copyToolButton_clicked() +{ + QModelIndexList profiles = selectedProfiles(); + if (profiles.count() > 1) + return; + + pd_ui_->lineProfileFilter->setText(""); + pd_ui_->cmbProfileTypes->setCurrentIndex(ProfileSortModel::AllProfiles); + sort_model_->setFilterString(); + + QModelIndex current = pd_ui_->profileTreeView->currentIndex(); + if (current.column() != ProfileModel::COL_NAME) + current = current.sibling(current.row(), ProfileModel::COL_NAME); + + QModelIndex source = sort_model_->mapToSource(current); + QModelIndex ridx = model_->duplicateEntry(source); + if (ridx.isValid()) + { + pd_ui_->profileTreeView->setCurrentIndex(sort_model_->mapFromSource(ridx)); + pd_ui_->profileTreeView->scrollTo(sort_model_->mapFromSource(ridx)); + pd_ui_->profileTreeView->edit(sort_model_->mapFromSource(ridx)); + currentItemChanged(); + } + else + updateWidgets(); +} + +void ProfileDialog::on_buttonBox_accepted() +{ + bool write_recent = true; + bool item_data_removed = false; + + QModelIndex index = sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex()); + + pd_ui_->buttonBox->setFocus(); + + QModelIndexList profiles = selectedProfiles(); + if (profiles.count() <= 0) + index = QModelIndex(); + + QModelIndex default_item = sort_model_->mapFromSource(model_->index(0, ProfileModel::COL_NAME)); + if (index.isValid() && index.column() != ProfileModel::COL_NAME) + index = index.sibling(index.row(), ProfileModel::COL_NAME); + + if (default_item.data(ProfileModel::DATA_STATUS).toInt() == PROF_STAT_DEFAULT && model_->resetDefault()) + { + // Reset Default profile. + GList *fl_entry = model_->at(0); + remove_from_profile_list(fl_entry); + + // Don't write recent file if leaving the Default profile after this has been reset. + write_recent = !is_default_profile(); + + // Don't fetch profile data if removed. + item_data_removed = (index.row() == 0); + } + + if (write_recent) { + /* Get the current geometry, before writing it to disk */ + mainApp->emitAppSignal(MainApplication::ProfileChanging); + + /* Write recent file for current profile now because + * the profile may be renamed in apply_profile_changes() */ + write_profile_recent(); + } + + gchar * err_msg = Q_NULLPTR; + if ((err_msg = apply_profile_changes()) != Q_NULLPTR) { + QMessageBox::critical(this, tr("Profile Error"), + err_msg, + QMessageBox::Ok); + g_free(err_msg); + + model_->doResetModel(); + return; + } + + model_->doResetModel(); + + QString profileName; + + if (! index.isValid() && model_->lastSetRow() >= 0) + { + QModelIndex original = model_->index(model_->lastSetRow(), ProfileModel::COL_NAME); + index = sort_model_->mapFromSource(original); + } + + /* If multiple profiles are selected, do not change the selected profile */ + if (index.isValid() && ! item_data_removed && profiles.count() <= 1) + { + profileName = model_->data(index).toString(); + } + + if (profileName.length() > 0 && model_->findByName(profileName) >= 0) { + // The new profile exists, change. + mainApp->setConfigurationProfile (profileName.toUtf8().constData(), FALSE); + } else if (!model_->activeProfile().isValid()) { + // The new profile does not exist, and the previous profile has + // been deleted. Change to the default profile. + mainApp->setConfigurationProfile (Q_NULLPTR, FALSE); + } +} + +void ProfileDialog::on_buttonBox_rejected() +{ + QString msg; + if (! model_->clearImported(&msg)) + QMessageBox::critical(this, tr("Error"), msg); +} + +void ProfileDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_CONFIG_PROFILES_DIALOG); +} + +void ProfileDialog::dataChanged(const QModelIndex &) +{ + pd_ui_->lineProfileFilter->setText(""); + pd_ui_->cmbProfileTypes->setCurrentIndex(ProfileSortModel::AllProfiles); + + pd_ui_->profileTreeView->setFocus(); + if (model_->lastSetRow() >= 0) + { + QModelIndex original = model_->index(model_->lastSetRow(), ProfileModel::COL_NAME); + pd_ui_->profileTreeView->setCurrentIndex(sort_model_->mapFromSource(original)); + pd_ui_->profileTreeView->selectRow(sort_model_->mapFromSource(original).row()); + } + + updateWidgets(); +} + +void ProfileDialog::filterChanged(const QString &text) +{ + if (qobject_cast(sender())) + { + QComboBox * cmb = qobject_cast(sender()); + sort_model_->setFilterType(static_cast(cmb->currentIndex())); + } + else if (qobject_cast(sender())) + sort_model_->setFilterString(text); + + pd_ui_->profileTreeView->resizeColumnToContents(ProfileModel::COL_NAME); + + QModelIndex active = sort_model_->mapFromSource(model_->activeProfile()); + if (active.isValid()) + pd_ui_->profileTreeView->setCurrentIndex(active); +} + +#ifdef HAVE_MINIZIP +void ProfileDialog::exportProfiles(bool exportAllPersonalProfiles) +{ + QAction * action = qobject_cast(sender()); + if (action && action->property(PROFILE_EXPORT_PROPERTY).isValid()) + exportAllPersonalProfiles = action->property(PROFILE_EXPORT_PROPERTY).toString().compare(PROFILE_EXPORT_ALL) == 0; + + QModelIndexList items; + int skipped = 0; + + if (! exportAllPersonalProfiles) + { + foreach (QModelIndex idx, selectedProfiles()) + { + QModelIndex baseIdx = sort_model_->index(idx.row(), ProfileModel::COL_NAME); + if (! baseIdx.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! baseIdx.data(ProfileModel::DATA_IS_DEFAULT).toBool()) + items << sort_model_->mapToSource(baseIdx); + else + skipped++; + } + } + else if (exportAllPersonalProfiles) + { + for (int cnt = 0; cnt < sort_model_->rowCount(); cnt++) + { + QModelIndex idx = sort_model_->index(cnt, ProfileModel::COL_NAME); + if (! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! idx.data(ProfileModel::DATA_IS_DEFAULT).toBool()) + items << sort_model_->mapToSource(idx); + } + } + if (items.count() == 0) + { + QString msg = tr("No profiles found for export"); + if (skipped > 0) + msg.append(tr(", %Ln profile(s) skipped", "", skipped)); + QMessageBox::critical(this, tr("Exporting profiles"), msg); + return; + } + + QString zipFile = QFileDialog::getSaveFileName(this, tr("Select zip file for export"), openDialogInitialDir(), tr("Zip File (*.zip)")); + + if (zipFile.length() > 0) + { + QFileInfo fi(zipFile); + if (fi.suffix().length() == 0 || fi.suffix().toLower().compare("zip") != 0) + zipFile += ".zip"; + + QString err; + if (model_->exportProfiles(zipFile, items, &err)) + { + QString msg = tr("%Ln profile(s) exported", "", static_cast(items.count())); + if (skipped > 0) + msg.append(tr(", %Ln profile(s) skipped", "", skipped)); + QMessageBox::information(this, tr("Exporting profiles"), msg); + + QFileInfo zip(zipFile); + storeLastDir(zip.absolutePath()); + } + else + { + QString msg = tr("An error has occurred while exporting profiles"); + if (err.length() > 0) + msg.append(QString("\n\n%1: %3").arg(tr("Error")).arg(err)); + QMessageBox::critical(this, tr("Exporting profiles"), msg); + } + } +} + +void ProfileDialog::importFromZip() +{ + QString zipFile = QFileDialog::getOpenFileName(this, tr("Select zip file for import"), openDialogInitialDir(), tr("Zip File (*.zip)")); + + QFileInfo fi(zipFile); + if (! fi.exists()) + return; + + int skipped = 0; + QStringList import; + int count = model_->importProfilesFromZip(zipFile, &skipped, &import); + + finishImport(fi, count, skipped, import); +} +#endif + +void ProfileDialog::importFromDirectory() +{ + QString importDir = QFileDialog::getExistingDirectory(this, tr("Select directory for import"), openDialogInitialDir()); + + QFileInfo fi(importDir); + if (! fi.isDir()) + return; + + int skipped = 0; + QStringList import; + int count = model_->importProfilesFromDir(importDir, &skipped, false, &import); + + finishImport(fi, count, skipped, import); +} + +void ProfileDialog::finishImport(QFileInfo fi, int count, int skipped, QStringList import) +{ + QString msg; + QMessageBox::Icon icon; + + if (count == 0 && skipped == 0) + { + icon = QMessageBox::Warning; + msg = tr("No profiles found for import in %1").arg(fi.fileName()); + } + else + { + icon = QMessageBox::Information; + msg = tr("%Ln profile(s) imported", "", count); + if (skipped > 0) + msg.append(tr(", %Ln profile(s) skipped", "", skipped)); + } + QMessageBox msgBox(icon, tr("Importing profiles"), msg, QMessageBox::Ok, this); + msgBox.exec(); + + storeLastDir(fi.absolutePath()); + + if (count > 0) + { + import.sort(); + resetTreeView(); + model_->markAsImported(import); + int rowFirstImported = model_->findByName(import.at(0)); + QModelIndex idx = sort_model_->mapFromSource(model_->index(rowFirstImported, ProfileModel::COL_NAME)); + pd_ui_->profileTreeView->selectRow(idx.isValid() ? idx.row() : 0); + } + + updateWidgets(); +} + +void ProfileDialog::resetTreeView() +{ + if (model_) + { + pd_ui_->profileTreeView->setModel(Q_NULLPTR); + sort_model_->setSourceModel(Q_NULLPTR); + model_->disconnect(); + if (pd_ui_->profileTreeView->selectionModel()) + pd_ui_->profileTreeView->selectionModel()->disconnect(); + delete sort_model_; + delete model_; + } + + model_ = new ProfileModel(pd_ui_->profileTreeView); + sort_model_ = new ProfileSortModel(pd_ui_->profileTreeView); + sort_model_->setSourceModel(model_); + pd_ui_->profileTreeView->setModel(sort_model_); + + connect(model_, &ProfileModel::itemChanged, this, &ProfileDialog::dataChanged, Qt::QueuedConnection); + QItemSelectionModel *selModel = pd_ui_->profileTreeView->selectionModel(); + connect(selModel, &QItemSelectionModel::currentChanged, + this, &ProfileDialog::currentItemChanged, Qt::QueuedConnection); + connect(selModel, &QItemSelectionModel::selectionChanged, + this, &ProfileDialog::selectionChanged); + + selectionChanged(); + + if (sort_model_->columnCount() <= 1) + pd_ui_->profileTreeView->header()->hide(); + else + { + pd_ui_->profileTreeView->header()->setStretchLastSection(false); + pd_ui_->profileTreeView->header()->setSectionResizeMode(ProfileModel::COL_NAME, QHeaderView::Stretch); + } +} diff --git a/ui/qt/profile_dialog.h b/ui/qt/profile_dialog.h new file mode 100644 index 00000000..d42594f0 --- /dev/null +++ b/ui/qt/profile_dialog.h @@ -0,0 +1,94 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PROFILE_DIALOG_H +#define PROFILE_DIALOG_H + +#include "config.h" + +#include +#include +#include + +#include +#include + +namespace Ui { +class ProfileDialog; +} + +class ProfileDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + enum ProfileAction { + ShowProfiles, NewProfile, ImportZipProfile, ImportDirProfile, + ExportSingleProfile, ExportAllProfiles, EditCurrentProfile, DeleteCurrentProfile + }; + + explicit ProfileDialog(QWidget *parent = Q_NULLPTR); + ~ProfileDialog(); + int execAction(ProfileAction profile_action); + + /** + * @brief Select the profile with the given name. + * + * If the profile name is empty, the currently selected profile will be choosen instead. + * If the choosen profile is invalid, the first row will be choosen. + * + * @param profile the name of the profile to be selected + */ + void selectProfile(QString profile = QString()); + +protected: + virtual void keyPressEvent(QKeyEvent *event); + +private: + Ui::ProfileDialog *pd_ui_; + QPushButton *ok_button_; + QPushButton *import_button_; +#ifdef HAVE_MINIZIP + QPushButton *export_button_; + QAction *export_selected_entry_; +#endif + ProfileModel *model_; + ProfileSortModel *sort_model_; + + void updateWidgets(); + void resetTreeView(); + + void finishImport(QFileInfo fi, int count, int skipped, QStringList import); + +private slots: + void currentItemChanged(const QModelIndex & c = QModelIndex(), const QModelIndex & p = QModelIndex()); +#ifdef HAVE_MINIZIP + void exportProfiles(bool exportAllPersonalProfiles = false); + void importFromZip(); +#endif + void importFromDirectory(); + + void on_newToolButton_clicked(); + void on_deleteToolButton_clicked(); + void on_copyToolButton_clicked(); + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + void on_buttonBox_helpRequested(); + void dataChanged(const QModelIndex &); + + void filterChanged(const QString &); + + void selectionChanged(); + QModelIndexList selectedProfiles(); + + // QWidget interface + +}; + +#endif // PROFILE_DIALOG_H diff --git a/ui/qt/profile_dialog.ui b/ui/qt/profile_dialog.ui new file mode 100644 index 00000000..fcc7591f --- /dev/null +++ b/ui/qt/profile_dialog.ui @@ -0,0 +1,195 @@ + + + ProfileDialog + + + + 0 + 0 + 570 + 400 + + + + + + + + + Search for profile … + + + + + + + + + + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + false + + + false + + + false + + + false + + + true + + + + + + + + + Create a new profile using default settings. + + + + + + + :/stock/plus-8.png:/stock/plus-8.png + + + + + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + + + + :/stock/minus-8.png:/stock/minus-8.png + + + + + + + Copy this profile. + + + + + + + :/stock/copy-8.png:/stock/copy-8.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+ + ProfileTreeView + QTreeView +
widgets/profile_tree_view.h
+
+ + ElidedLabel + QLabel +
widgets/elided_label.h
+
+
+ + + + buttonBox + accepted() + ProfileDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ProfileDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/progress_frame.cpp b/ui/qt/progress_frame.cpp new file mode 100644 index 00000000..2e40ac80 --- /dev/null +++ b/ui/qt/progress_frame.cpp @@ -0,0 +1,323 @@ +/* progress_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "progress_frame.h" +#include + +#include "ui/progress_dlg.h" + +#include +#include +#include +#include + +#include +#include "main_application.h" + +// To do: +// - Add an NSProgressIndicator to the dock icon on macOS. +// - Start adding the progress bar to dialogs. +// - Don't complain so loudly when the user stops a capture. + +progdlg_t * +create_progress_dlg(gpointer top_level_window, const gchar *task_title, const gchar *item_title, + gboolean terminate_is_stop, gboolean *stop_flag) { + ProgressFrame *pf; + QWidget *main_window; + + if (!top_level_window) { + return nullptr; + } + + main_window = qobject_cast((QObject *)top_level_window); + + if (!main_window) { + return nullptr; + } + + pf = main_window->findChild(); + + if (!pf) { + return nullptr; + } + + QString title = task_title; + if (item_title && strlen(item_title) > 0) { + title.append(" ").append(item_title); + } + return pf->showProgress(title, true, terminate_is_stop, stop_flag, 0); +} + +progdlg_t * +delayed_create_progress_dlg(gpointer top_level_window, const gchar *task_title, const gchar *item_title, + gboolean terminate_is_stop, gboolean *stop_flag, + gfloat progress) +{ + progdlg_t *progress_dialog = create_progress_dlg(top_level_window, task_title, item_title, terminate_is_stop, stop_flag); + update_progress_dlg(progress_dialog, progress, item_title); + return progress_dialog; +} + +/* + * Update the progress information of the progress bar box. + */ +void +update_progress_dlg(progdlg_t *dlg, gfloat percentage, const gchar *) +{ + if (!dlg) return; + + dlg->progress_frame->setValue((int)(percentage * 100)); + + /* + * Flush out the update and process any input events. + */ + MainApplication::processEvents(); +} + +/* + * Destroy the progress bar. + */ +void +destroy_progress_dlg(progdlg_t *dlg) +{ + dlg->progress_frame->hide(); +} + +ProgressFrame::ProgressFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::ProgressFrame) + , terminate_is_stop_(false) + , stop_flag_(nullptr) + , show_timer_(-1) + , effect_(nullptr) + , animation_(nullptr) +#ifdef QWINTASKBARPROGRESS_H + , update_taskbar_(false) + , taskbar_progress_(NULL) +#endif +{ + ui->setupUi(this); + + progress_dialog_.progress_frame = this; + progress_dialog_.top_level_window = window(); + +#ifdef Q_OS_MAC + ui->label->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + ui->label->setStyleSheet(QString( + "QLabel {" + " background: transparent;" + "}")); + + ui->progressBar->setStyleSheet(QString( + "QProgressBar {" + " max-width: 20em;" + " min-height: 0.5em;" + " max-height: 1em;" + " border-bottom: 0px;" + " border-top: 0px;" + " background: transparent;" + "}")); + + ui->stopButton->setStockIcon("x-filter-clear"); + ui->stopButton->setIconSize(QSize(14, 14)); + ui->stopButton->setStyleSheet( + "QToolButton {" + " border: none;" + " background: transparent;" // Disables platform style on Windows. + " padding: 0px;" + " margin: 0px;" + " min-height: 0.8em;" + " max-height: 1em;" + " min-width: 0.8em;" + " max-width: 1em;" + "}" + ); + + effect_ = new QGraphicsOpacityEffect(this); + animation_ = new QPropertyAnimation(effect_, "opacity", this); + connect(this, SIGNAL(showRequested(bool,bool,gboolean*)), + this, SLOT(show(bool,bool,gboolean*))); + hide(); +} + +ProgressFrame::~ProgressFrame() +{ + delete ui; +} + +struct progdlg *ProgressFrame::showProgress(const QString &title, bool animate, bool terminate_is_stop, gboolean *stop_flag, int value) +{ + setMaximumValue(100); + ui->progressBar->setValue(value); + QString elided_title = title; + int max_w = fontMetrics().height() * 20; // em-widths, arbitrary +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + int title_w = fontMetrics().horizontalAdvance(title); +#else + int title_w = fontMetrics().width(title); +#endif + if (title_w > max_w) { + elided_title = fontMetrics().elidedText(title, Qt::ElideRight, max_w); + } + // If we're in the main status bar, should we push this as a status message instead? + ui->label->setText(elided_title); + emit showRequested(animate, terminate_is_stop, stop_flag); + return &progress_dialog_; +} + +progdlg *ProgressFrame::showBusy(bool animate, bool terminate_is_stop, gboolean *stop_flag) +{ + setMaximumValue(0); + emit showRequested(animate, terminate_is_stop, stop_flag); + return &progress_dialog_; +} + +void ProgressFrame::addToButtonBox(QDialogButtonBox *button_box, QObject *main_window) +{ + // We have a ProgressFrame in the main status bar which is controlled + // from the capture file and other parts of the application via + // create_progress_dlg and delayed_create_progress_dlg. + // Create a new ProgressFrame and pair it with the main instance. + ProgressFrame *main_progress_frame = main_window->findChild(); + if (!button_box || !main_progress_frame) return; + + QBoxLayout *layout = qobject_cast(button_box->layout()); + if (!layout) return; + + ProgressFrame *progress_frame = new ProgressFrame(button_box); + + // Insert ourselves after the first spacer we find, otherwise the + // far right of the button box. + int idx = layout->count(); + for (int i = 0; i < layout->count(); i++) { + if (layout->itemAt(i)->spacerItem()) { + idx = i + 1; + break; + } + } + layout->insertWidget(idx, progress_frame); + + int one_em = progress_frame->fontMetrics().height(); + progress_frame->setMaximumWidth(one_em * 8); + connect(main_progress_frame, SIGNAL(showRequested(bool,bool,gboolean*)), + progress_frame, SLOT(show(bool,bool,gboolean*))); + connect(main_progress_frame, SIGNAL(maximumValueChanged(int)), + progress_frame, SLOT(setMaximumValue(int))); + connect(main_progress_frame, SIGNAL(valueChanged(int)), + progress_frame, SLOT(setValue(int))); + connect(main_progress_frame, SIGNAL(setHidden()), + progress_frame, SLOT(hide())); + + connect(progress_frame, SIGNAL(stopLoading()), + main_progress_frame, SIGNAL(stopLoading())); +} + +void ProgressFrame::captureFileClosing() +{ + // Hide any paired ProgressFrames and disconnect from them. + emit setHidden(); + disconnect(SIGNAL(showRequested(bool,bool,gboolean*))); + disconnect(SIGNAL(maximumValueChanged(int))); + disconnect(SIGNAL(valueChanged(int))); + + connect(this, SIGNAL(showRequested(bool,bool,gboolean*)), + this, SLOT(show(bool,bool,gboolean*))); +} + +void ProgressFrame::setValue(int value) +{ + ui->progressBar->setValue(value); + emit valueChanged(value); +} + +void ProgressFrame::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == show_timer_) { + killTimer(show_timer_); + show_timer_ = -1; + + this->setGraphicsEffect(effect_); + + animation_->setDuration(200); + animation_->setStartValue(0.1); + animation_->setEndValue(1.0); + animation_->setEasingCurve(QEasingCurve::InOutQuad); + animation_->start(); + + QFrame::show(); + } else { + QFrame::timerEvent(event); + } +} + +void ProgressFrame::hide() +{ + show_timer_ = -1; + emit setHidden(); + QFrame::hide(); +#ifdef QWINTASKBARPROGRESS_H + if (taskbar_progress_) { + disconnect(this, SIGNAL(valueChanged(int)), taskbar_progress_, SLOT(setValue(int))); + taskbar_progress_->reset(); + taskbar_progress_->hide(); + } +#endif +} + +void ProgressFrame::on_stopButton_clicked() +{ + emit stopLoading(); +} + +const int show_delay_ = 150; // ms + +void ProgressFrame::show(bool animate, bool terminate_is_stop, gboolean *stop_flag) +{ + terminate_is_stop_ = terminate_is_stop; + stop_flag_ = stop_flag; + + if (stop_flag) { + ui->stopButton->show(); + } else { + ui->stopButton->hide(); + } + + if (animate) { + show_timer_ = startTimer(show_delay_); + } else { + QFrame::show(); + } + +#ifdef QWINTASKBARPROGRESS_H + // windowHandle() is picky about returning a non-NULL value so we check it + // each time. + if (update_taskbar_ && !taskbar_progress_ && window()->windowHandle()) { + QWinTaskbarButton *taskbar_button = new QWinTaskbarButton(this); + if (taskbar_button) { + taskbar_button->setWindow(window()->windowHandle()); + taskbar_progress_ = taskbar_button->progress(); + } + } + if (taskbar_progress_) { + taskbar_progress_->show(); + taskbar_progress_->reset(); + connect(this, SIGNAL(valueChanged(int)), taskbar_progress_, SLOT(setValue(int))); + } +#endif +} + +void ProgressFrame::setMaximumValue(int value) +{ + ui->progressBar->setMaximum(value); + emit maximumValueChanged(value); +} diff --git a/ui/qt/progress_frame.h b/ui/qt/progress_frame.h new file mode 100644 index 00000000..241afab8 --- /dev/null +++ b/ui/qt/progress_frame.h @@ -0,0 +1,91 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PROGRESS_FRAME_H +#define PROGRESS_FRAME_H + +#include + +#include + +namespace Ui { +class ProgressFrame; +} + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && defined(Q_OS_WIN) +#include +#include +#endif + +class ProgressFrame; +class QDialogButtonBox; +class QElapsedTimer; +class QGraphicsOpacityEffect; +class QPropertyAnimation; + +// Define the structure describing a progress dialog. +struct progdlg { + ProgressFrame *progress_frame; // This progress frame + QWidget *top_level_window; // Progress frame's main window +}; + +class ProgressFrame : public QFrame +{ + Q_OBJECT + +public: + explicit ProgressFrame(QWidget *parent = 0); + ~ProgressFrame(); + +#ifdef QWINTASKBARPROGRESS_H + void enableTaskbarUpdates(bool enable = true) { update_taskbar_ = enable; } +#endif + static void addToButtonBox(QDialogButtonBox *button_box, QObject *main_window); + void captureFileClosing(); + +public slots: + struct progdlg *showProgress(const QString &title, bool animate, bool terminate_is_stop, gboolean *stop_flag, int value = 0); + struct progdlg *showBusy(bool animate, bool terminate_is_stop, gboolean *stop_flag); + void setValue(int value); + void hide(); + +signals: + void showRequested(bool animate, bool terminate_is_stop, gboolean *stop_flag); + void valueChanged(int value); + void maximumValueChanged(int value); + void setHidden(); + void stopLoading(); + +protected: + void timerEvent(QTimerEvent *event); + +private: + Ui::ProgressFrame *ui; + + struct progdlg progress_dialog_; + QString message_; + QString status_; + bool terminate_is_stop_; + gboolean *stop_flag_; + int show_timer_; + QGraphicsOpacityEffect *effect_; + QPropertyAnimation *animation_; +#ifdef QWINTASKBARPROGRESS_H + bool update_taskbar_; + QWinTaskbarProgress *taskbar_progress_; +#endif + +private slots: + void on_stopButton_clicked(); + + void show(bool animate, bool terminate_is_stop, gboolean *stop_flag); + void setMaximumValue(int value); +}; + +#endif // PROGRESS_FRAME_H diff --git a/ui/qt/progress_frame.ui b/ui/qt/progress_frame.ui new file mode 100644 index 00000000..609960b5 --- /dev/null +++ b/ui/qt/progress_frame.ui @@ -0,0 +1,76 @@ + + + ProgressFrame + + + + 0 + 0 + 210 + 38 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + QLayout::SetMinimumSize + + + 0 + + + 0 + + + + + Loading + + + + + + + 24 + + + false + + + + + + + + + + + 12 + 12 + + + + + + + + + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+
+ + +
diff --git a/ui/qt/proto_tree.cpp b/ui/qt/proto_tree.cpp new file mode 100644 index 00000000..77af8d22 --- /dev/null +++ b/ui/qt/proto_tree.cpp @@ -0,0 +1,909 @@ +/* proto_tree.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// To do: +// - Fix "apply as filter" behavior. + +ProtoTree::ProtoTree(QWidget *parent, epan_dissect_t *edt_fixed) : + QTreeView(parent), + proto_tree_model_(new ProtoTreeModel(this)), + column_resize_timer_(0), + cap_file_(NULL), + edt_(edt_fixed) +{ + setAccessibleName(tr("Packet details")); + // Leave the uniformRowHeights property as-is (false) since items might + // have multiple lines (e.g. packet comments). If this slows things down + // too much we should add a custom delegate which handles SizeHintRole + // similar to PacketListModel::data. + setHeaderHidden(true); + +#if !defined(Q_OS_WIN) + setStyleSheet(QString( + "QTreeView:item:hover {" + " background-color: %1;" + " color: palette(text);" + "}").arg(ColorUtils::hoverBackground().name(QColor::HexArgb))); +#endif + + // Shrink down to a small but nonzero size in the main splitter. + int one_em = fontMetrics().height(); + setMinimumSize(one_em, one_em); + + setModel(proto_tree_model_); + + connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(syncExpanded(QModelIndex))); + connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(syncCollapsed(QModelIndex))); + connect(this, SIGNAL(clicked(QModelIndex)), + this, SLOT(itemClicked(QModelIndex))); + connect(this, SIGNAL(doubleClicked(QModelIndex)), + this, SLOT(itemDoubleClicked(QModelIndex))); + + connect(&proto_prefs_menu_, SIGNAL(showProtocolPreferences(QString)), + this, SIGNAL(showProtocolPreferences(QString))); + connect(&proto_prefs_menu_, SIGNAL(editProtocolPreference(preference*,pref_module*)), + this, SIGNAL(editProtocolPreference(preference*,pref_module*))); + + // resizeColumnToContents checks 1000 items by default. The user might + // have scrolled to an area with a different width at this point. + connect(verticalScrollBar(), SIGNAL(sliderReleased()), + this, SLOT(updateContentWidth())); + + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(connectToMainWindow())); + + viewport()->installEventFilter(this); +} + +void ProtoTree::clear() { + proto_tree_model_->setRootNode(NULL); + updateContentWidth(); +} + +void ProtoTree::connectToMainWindow() +{ + if (mainApp->mainWindow()) + { + connect(mainApp->mainWindow(), SIGNAL(fieldSelected(FieldInformation *)), + this, SLOT(selectedFieldChanged(FieldInformation *))); + connect(mainApp->mainWindow(), SIGNAL(framesSelected(QList)), + this, SLOT(selectedFrameChanged(QList))); + } +} + +void ProtoTree::ctxCopyVisibleItems() +{ + bool selected_tree = false; + + QAction * send = qobject_cast(sender()); + if (send && send->property("selected_tree").isValid()) + selected_tree = true; + + QString clip; + if (selected_tree && selectionModel()->hasSelection()) + clip = toString(selectionModel()->selectedIndexes().first()); + else + clip = toString(); + + if (clip.length() > 0) + mainApp->clipboard()->setText(clip); +} + +void ProtoTree::ctxCopyAsFilter() +{ + QModelIndex idx = selectionModel()->selectedIndexes().first(); + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx)); + if (finfo.isValid()) + { + epan_dissect_t *edt = cap_file_ ? cap_file_->edt : edt_; + char *field_filter = proto_construct_match_selected_string(finfo.fieldInfo(), edt); + QString filter(field_filter); + wmem_free(Q_NULLPTR, field_filter); + + if (filter.length() > 0) + mainApp->clipboard()->setText(filter); + } +} + +void ProtoTree::ctxCopySelectedInfo() +{ + int val = -1; + QString clip; + QAction * send = qobject_cast(sender()); + if (send && send->property("field_type").isValid()) + val = send->property("field_type").toInt(); + + QModelIndex idx = selectionModel()->selectedIndexes().first(); + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx)); + if (! finfo.isValid()) + return; + + switch (val) + { + case ProtoTree::Name: + clip.append(finfo.headerInfo().abbreviation); + break; + + case ProtoTree::Description: + clip = idx.data(Qt::DisplayRole).toString(); + break; + + case ProtoTree::Value: + { + epan_dissect_t *edt = cap_file_ ? cap_file_->edt : edt_; + gchar* field_str = get_node_field_value(finfo.fieldInfo(), edt); + clip.append(field_str); + g_free(field_str); + } + break; + default: + break; + } + + if (clip.length() > 0) + mainApp->clipboard()->setText(clip); +} + +void ProtoTree::ctxOpenUrlWiki() +{ + QString url; + bool is_field_reference = false; + QAction * send = qobject_cast(sender()); + if (send && send->property("field_reference").isValid()) + is_field_reference = send->property("field_reference").toBool(); + QModelIndex idx = selectionModel()->selectedIndexes().first(); + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx)); + + int field_id = finfo.headerInfo().id; + bool protocol_field_selected = false; + if (!proto_registrar_is_protocol(field_id) && (field_id != hf_text_only)) { + protocol_field_selected = true; + field_id = proto_registrar_get_parent(field_id); + } + const QString proto_abbrev = proto_registrar_get_abbrev(field_id); + + if (! is_field_reference) + { + int ret = QMessageBox::question(this, mainApp->windowTitleString(tr("Wiki Page for %1").arg(proto_abbrev)), + tr("

The Wireshark Wiki is maintained by the community.

" + "

The page you are about to load might be wonderful, " + "incomplete, wrong, or nonexistent.

" + "

Proceed to the wiki?

"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (ret != QMessageBox::Yes) return; + + url = QString(WS_WIKI_URL("%1")).arg(proto_abbrev); + } + else + { + if (field_id != hf_text_only) { + url = QString(WS_DOCS_URL "dfref/%1/%2.html") + .arg(proto_abbrev[0]) + .arg(proto_abbrev); + + if (protocol_field_selected) + { + const QString proto_field_abbrev = proto_registrar_get_abbrev(finfo.headerInfo().id); + url.append(QString("#%1").arg(proto_field_abbrev)); + } + } else { + QMessageBox::information(this, tr("Not a field or protocol"), + tr("No field reference available for text labels."), + QMessageBox::Ok); + } + } + + QDesktopServices::openUrl(url); +} + +void ProtoTree::contextMenuEvent(QContextMenuEvent *event) +{ + QModelIndex index = indexAt(event->pos()); + if (! index.isValid()) + return; + + // We're in a PacketDialog + bool buildForDialog = false; + if (! window()->findChild("actionViewExpandSubtrees")) + buildForDialog = true; + + QMenu * ctx_menu = new QMenu(this); + ctx_menu->setAttribute(Qt::WA_DeleteOnClose); + ctx_menu->setProperty("toolTipsVisible", QVariant::fromValue(true)); + + QMenu *main_menu_item, *submenu; + QAction *action; + + bool have_subtree = false; + FieldInformation *finfo = new FieldInformation(proto_tree_model_->protoNodeFromIndex(index), ctx_menu); + field_info * fi = finfo->fieldInfo(); + bool is_selected = false; + epan_dissect_t *edt = cap_file_ ? cap_file_->edt : edt_; + + if (cap_file_ && cap_file_->finfo_selected == fi) + is_selected = true; + else if (! window()->findChild("actionViewExpandSubtrees")) + is_selected = true; + + if (is_selected) + { + if (fi && fi->tree_type != -1) { + have_subtree = true; + } + } + + action = ctx_menu->addAction(tr("Expand Subtrees"), this, SLOT(expandSubtrees())); + action->setEnabled(have_subtree); + action = ctx_menu->addAction(tr("Collapse Subtrees"), this, SLOT(collapseSubtrees())); + action->setEnabled(have_subtree); + ctx_menu->addAction(tr("Expand All"), this, SLOT(expandAll())); + ctx_menu->addAction(tr("Collapse All"), this, SLOT(collapseAll())); + ctx_menu->addSeparator(); + + if (! buildForDialog) + { + if (finfo->headerInfo().type == FT_IPv4 || finfo->headerInfo().type == FT_IPv6) { + action = window()->findChild("actionViewEditResolvedName"); + ctx_menu->addAction(action); + ctx_menu->addSeparator(); + } + action = window()->findChild("actionAnalyzeApplyAsColumn"); + ctx_menu->addAction(action); + ctx_menu->addSeparator(); + } + + char * selectedfilter = proto_construct_match_selected_string(finfo->fieldInfo(), edt); + bool can_match_selected = proto_can_match_selected(finfo->fieldInfo(), edt); + ctx_menu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionApply, selectedfilter, can_match_selected, ctx_menu)); + ctx_menu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionPrepare, selectedfilter, can_match_selected, ctx_menu)); + if (selectedfilter) + wmem_free(Q_NULLPTR, selectedfilter); + + if (! buildForDialog) + { + QMenu *main_conv_menu = window()->findChild("menuConversationFilter"); + conv_menu_.setTitle(main_conv_menu->title()); + conv_menu_.clear(); + foreach (QAction *action, main_conv_menu->actions()) { + conv_menu_.addAction(action); + } + + ctx_menu->addMenu(&conv_menu_); + + colorize_menu_.setTitle(tr("Colorize with Filter")); + ctx_menu->addMenu(&colorize_menu_); + + /* XXX: Should we just get a Follow action (if it exists) for the currently + * selected field info, similar to preferences and filters? + */ + main_menu_item = window()->findChild("menuFollow"); + if (main_menu_item) { + submenu = new QMenu(main_menu_item->title(), ctx_menu); + ctx_menu->addMenu(submenu); + foreach (FollowStreamAction *follow_action, main_menu_item->findChildren()) { + if (follow_action->isEnabled()) { + submenu->addAction(follow_action); + } + } + } + + ctx_menu->addSeparator(); + } + + submenu = ctx_menu->addMenu(tr("Copy")); + submenu->addAction(tr("All Visible Items"), this, SLOT(ctxCopyVisibleItems())); + action = submenu->addAction(tr("All Visible Selected Tree Items"), this, SLOT(ctxCopyVisibleItems())); + action->setProperty("selected_tree", QVariant::fromValue(true)); + action = submenu->addAction(tr("Description"), this, SLOT(ctxCopySelectedInfo())); + action->setProperty("field_type", ProtoTree::Description); + action = submenu->addAction(tr("Field Name"), this, SLOT(ctxCopySelectedInfo())); + action->setProperty("field_type", ProtoTree::Name); + action = submenu->addAction(tr("Value"), this, SLOT(ctxCopySelectedInfo())); + action->setProperty("field_type", ProtoTree::Value); + submenu->addSeparator(); + submenu->addAction(tr("As Filter"), this, SLOT(ctxCopyAsFilter())); + submenu->addSeparator(); + QActionGroup * copyEntries = DataPrinter::copyActions(this, finfo); + submenu->addActions(copyEntries->actions()); + ctx_menu->addSeparator(); + + if (! buildForDialog) + { + action = window()->findChild("actionAnalyzeShowPacketBytes"); + ctx_menu->addAction(action); + action = window()->findChild("actionFileExportPacketBytes"); + ctx_menu->addAction(action); + + ctx_menu->addSeparator(); + } + + int field_id = finfo->headerInfo().id; + bool protocol_field_selected = false; + if (!proto_registrar_is_protocol(field_id) && (field_id != hf_text_only)) { + protocol_field_selected = true; + field_id = proto_registrar_get_parent(field_id); + } + action = ctx_menu->addAction(tr("Wiki Protocol Page"), this, SLOT(ctxOpenUrlWiki())); + action->setProperty("toolTip", QString(WS_WIKI_URL("Protocols/%1")).arg(proto_registrar_get_abbrev(field_id))); + + action = ctx_menu->addAction(tr("Filter Field Reference"), this, SLOT(ctxOpenUrlWiki())); + action->setProperty("field_reference", QVariant::fromValue(true)); + if (field_id != hf_text_only) { + action->setEnabled(true); + const QString proto_abbrev = proto_registrar_get_abbrev(field_id); + QString url = QString(WS_DOCS_URL "dfref/%1/%2.html") + .arg(proto_abbrev[0]) + .arg(proto_abbrev); + + if (protocol_field_selected) + { + const QString proto_field_abbrev = proto_registrar_get_abbrev(finfo->headerInfo().id); + url.append(QString("#%1").arg(proto_field_abbrev)); + } + action->setProperty("toolTip", url); + } + else { + action->setEnabled(false); + action->setProperty("toolTip", tr("No field reference available for text labels.")); + } + ctx_menu->addMenu(&proto_prefs_menu_); + ctx_menu->addSeparator(); + + if (! buildForDialog) + { + QAction *decode_as_ = window()->findChild("actionAnalyzeDecodeAs"); + ctx_menu->addAction(decode_as_); + decode_as_->setProperty("create_new", QVariant::fromValue(true)); + + ctx_menu->addAction(window()->findChild("actionGoGoToLinkedPacket")); + ctx_menu->addAction(window()->findChild("actionContextShowLinkedPacketInNewWindow")); + } + + // The "text only" header field will not give preferences for the selected protocol. + // Use parent in this case. + ProtoNode *node = proto_tree_model_->protoNodeFromIndex(index); + while (node && node->isValid() && node->protoNode()->finfo && node->protoNode()->finfo->hfinfo && node->protoNode()->finfo->hfinfo->id == hf_text_only) { + node = node->parentNode(); + } + + FieldInformation pref_finfo(node); + proto_prefs_menu_.setModule(pref_finfo.moduleName()); + + ctx_menu->popup(event->globalPos()); +} + +void ProtoTree::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == column_resize_timer_) { + killTimer(column_resize_timer_); + column_resize_timer_ = 0; + resizeColumnToContents(0); + } else { + QTreeView::timerEvent(event); + } +} + +// resizeColumnToContents checks 1000 items by default. The user might +// have scrolled to an area with a different width at this point. +void ProtoTree::keyReleaseEvent(QKeyEvent *event) +{ + if (event->isAutoRepeat()) return; + + switch(event->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_Home: + case Qt::Key_End: + updateContentWidth(); + break; + default: + break; + } +} + +void ProtoTree::updateContentWidth() +{ + if (column_resize_timer_ == 0) { + column_resize_timer_ = startTimer(0); + } +} + +void ProtoTree::setMonospaceFont(const QFont &mono_font) +{ + setFont(mono_font); + update(); +} + +void ProtoTree::foreachTreeNode(proto_node *node, gpointer proto_tree_ptr) +{ + ProtoTree *tree_view = static_cast(proto_tree_ptr); + ProtoTreeModel *model = qobject_cast(tree_view->model()); + if (!tree_view || !model) { + return; + } + + // Related frames - there might be hidden FT_FRAMENUM nodes, so do this + // for each proto_node and not just the ProtoNodes in the model + if (node->finfo->hfinfo->type == FT_FRAMENUM) { + ft_framenum_type_t framenum_type = (ft_framenum_type_t)GPOINTER_TO_INT(node->finfo->hfinfo->strings); + tree_view->emitRelatedFrame(fvalue_get_uinteger(node->finfo->value), framenum_type); + } + + proto_tree_children_foreach(node, foreachTreeNode, proto_tree_ptr); +} + +void ProtoTree::foreachExpand(const QModelIndex &index = QModelIndex()) { + + // Restore expanded state. (Note QModelIndex() refers to the root node) + int children = proto_tree_model_->rowCount(index); + QModelIndex childIndex; + for (int child = 0; child < children; child++) { + childIndex = proto_tree_model_->index(child, 0, index); + if (childIndex.isValid()) { + ProtoNode *node = proto_tree_model_->protoNodeFromIndex(childIndex); + if (node && node->isValid() && tree_expanded(node->protoNode()->finfo->tree_type)) { + expand(childIndex); + } + foreachExpand(childIndex); + } + } +} + +// setRootNode sets the new contents for the protocol tree and subsequently +// restores the previously expanded state. +void ProtoTree::setRootNode(proto_node *root_node) { + // We track item expansion using proto.c:tree_is_expanded. + // Replace any existing (possibly invalidated) proto tree by the new tree. + // The expanded state will be reset as well and will be re-expanded below. + proto_tree_model_->setRootNode(root_node); + + disconnect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(syncExpanded(QModelIndex))); + proto_tree_children_foreach(root_node, foreachTreeNode, this); + foreachExpand(); + connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(syncExpanded(QModelIndex))); + + updateContentWidth(); +} + +void ProtoTree::emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_type) +{ + emit relatedFrame(related_frame, framenum_type); +} + +void ProtoTree::autoScrollTo(const QModelIndex &index) +{ + selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); + if (!index.isValid()) { + return; + } + + // ensure item is visible (expanding its parents as needed). + scrollTo(index); +} + +// XXX We select the first match, which might not be the desired item. +void ProtoTree::goToHfid(int hfid) +{ + QModelIndex index = proto_tree_model_->findFirstHfid(hfid); + autoScrollTo(index); +} + +void ProtoTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QTreeView::selectionChanged(selected, deselected); + if (selected.isEmpty()) { + emit fieldSelected(0); + return; + } + + QModelIndex index = selected.indexes().first(); + saveSelectedField(index); + + // Find and highlight the protocol bytes. select above won't call + // selectionChanged if the current and selected indexes are the same + // so we do this here. + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index), this); + if (finfo.isValid()) { + QModelIndex parent = index; + while (parent.isValid() && parent.parent().isValid()) { + parent = parent.parent(); + } + if (parent.isValid()) { + FieldInformation parent_finfo(proto_tree_model_->protoNodeFromIndex(parent)); + finfo.setParentField(parent_finfo.fieldInfo()); + } + emit fieldSelected(&finfo); + } +} + +void ProtoTree::syncExpanded(const QModelIndex &index) { + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index)); + if (!finfo.isValid()) return; + + /* + * Nodes with "finfo->tree_type" of -1 have no ett_ value, and + * are thus presumably leaf nodes and cannot be expanded. + */ + if (finfo.treeType() != -1) { + tree_expanded_set(finfo.treeType(), TRUE); + } +} + +void ProtoTree::syncCollapsed(const QModelIndex &index) { + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index)); + if (!finfo.isValid()) return; + + /* + * Nodes with "finfo->tree_type" of -1 have no ett_ value, and + * are thus presumably leaf nodes and cannot be collapsed. + */ + if (finfo.treeType() != -1) { + tree_expanded_set(finfo.treeType(), FALSE); + } +} + +void ProtoTree::expandSubtrees() +{ + if (!selectionModel()->hasSelection()) return; + + QStack index_stack; + index_stack.push(selectionModel()->selectedIndexes().first()); + + while (!index_stack.isEmpty()) { + QModelIndex index = index_stack.pop(); + expand(index); + int row_count = proto_tree_model_->rowCount(index); + for (int row = row_count - 1; row >= 0; row--) { + QModelIndex child = proto_tree_model_->index(row, 0, index); + if (proto_tree_model_->hasChildren(child)) { + index_stack.push(child); + } + } + } + + updateContentWidth(); +} + +void ProtoTree::collapseSubtrees() +{ + if (!selectionModel()->hasSelection()) return; + + QStack index_stack; + index_stack.push(selectionModel()->selectedIndexes().first()); + + while (!index_stack.isEmpty()) { + QModelIndex index = index_stack.pop(); + collapse(index); + int row_count = proto_tree_model_->rowCount(index); + for (int row = row_count - 1; row >= 0; row--) { + QModelIndex child = proto_tree_model_->index(row, 0, index); + if (proto_tree_model_->hasChildren(child)) { + index_stack.push(child); + } + } + } + + updateContentWidth(); +} + +void ProtoTree::expandAll() +{ + for (int i = 0; i < num_tree_types; i++) { + tree_expanded_set(i, TRUE); + } + QTreeView::expandAll(); + updateContentWidth(); +} + +void ProtoTree::collapseAll() +{ + for (int i = 0; i < num_tree_types; i++) { + tree_expanded_set(i, FALSE); + } + QTreeView::collapseAll(); + updateContentWidth(); +} + +void ProtoTree::itemClicked(const QModelIndex &index) +{ + // selectionChanged() is not issued when some action would select + // the same item as currently selected - but we want to make sure + // ByteViewText is highlighting that field. The BVT highlighted bytes + // might be different, due to hover highlighting or Find Packet "bytes". + // + // Unfortunately, clicked() is singled after selectionChanged(), so + // we emit fieldSelected() twice after clicking on a new frame, once + // in selectionChanged(), and once here. + // + // We can't get rid of the fieldSelected() handling because there are + // non-mouse event ways to select a field, such as keyboard navigation. + // + // All this would be easier if Qt had a signal similar to + // selectionChanged() that was triggered even if the new selection + // was the same as the old one. + if (selectionModel()->selectedIndexes().isEmpty()) { + emit fieldSelected(0); + } else if (index == selectionModel()->selectedIndexes().first()) { + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index), this); + + if (finfo.isValid()) { + // Find and highlight the protocol bytes. + QModelIndex parent = index; + while (parent.isValid() && parent.parent().isValid()) { + parent = parent.parent(); + } + if (parent.isValid()) { + FieldInformation parent_finfo(proto_tree_model_->protoNodeFromIndex(parent)); + finfo.setParentField(parent_finfo.fieldInfo()); + } + emit fieldSelected(&finfo); + } + } +} + +void ProtoTree::itemDoubleClicked(const QModelIndex &index) +{ + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index)); + if (!finfo.isValid()) return; + + if (finfo.headerInfo().type == FT_FRAMENUM) { + if (QApplication::queryKeyboardModifiers() & Qt::ShiftModifier) { + emit openPacketInNewWindow(true); + } else { + mainApp->gotoFrame(fvalue_get_uinteger(finfo.fieldInfo()->value)); + } + } else { + QString url = finfo.url(); + if (!url.isEmpty()) { + QApplication::clipboard()->setText(url); + QString push_msg = tr("Copied ") + url; + mainApp->pushStatus(MainApplication::TemporaryStatus, push_msg); + } + } +} + +void ProtoTree::selectedFrameChanged(QList 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. + proto_tree_model_->setRootNode(NULL); + } +} + +// Select a field and bring it into view. Intended to be called by external +// components (such as the byte view). +void ProtoTree::selectedFieldChanged(FieldInformation *finfo) +{ + if (finfo && finfo->parent() == this) { + // We only want inbound signals. + return; + } + + QModelIndex index = proto_tree_model_->findFieldInformation(finfo); + setUpdatesEnabled(false); + // The new finfo might match the current index. Clear our selection + // so that we force a fresh item selection, so that fieldSelected + // will in turn be emitted. + selectionModel()->clearSelection(); + autoScrollTo(index); + setUpdatesEnabled(true); +} + +// Remember the currently focussed field based on: +// - current hf_id (obviously) +// - parent items (to avoid selecting a text item in a different tree) +// - the row of each item +void ProtoTree::saveSelectedField(QModelIndex &index) +{ + selected_hfid_path_.clear(); + QModelIndex save_index = index; + while (save_index.isValid()) { + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(save_index)); + if (!finfo.isValid()) break; + selected_hfid_path_.prepend(QPair(save_index.row(), finfo.headerInfo().id)); + save_index = save_index.parent(); + } +} + +// Try to focus a tree item which was previously also visible +void ProtoTree::restoreSelectedField() +{ + if (selected_hfid_path_.isEmpty()) return; + + QModelIndex cur_index = QModelIndex(); + QPair path_entry; + foreach (path_entry, selected_hfid_path_) { + int row = path_entry.first; + int hf_id = path_entry.second; + cur_index = proto_tree_model_->index(row, 0, cur_index); + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(cur_index)); + if (!finfo.isValid() || finfo.headerInfo().id != hf_id) { + // Did not find the selected hfid path in the selected packet + cur_index = QModelIndex(); + emit fieldSelected(0); + break; + } + } + + autoScrollTo(cur_index); +} + +QString ProtoTree::traverseTree(const QModelIndex & travTree, int identLevel) const +{ + QString result = ""; + + if (travTree.isValid()) + { + result.append(QString(" ").repeated(identLevel)); + result.append(travTree.data().toString()); + result.append("\n"); + + /* if the element is expanded, we traverse one level down */ + if (isExpanded(travTree)) + { + int children = proto_tree_model_->rowCount(travTree); + identLevel++; + for (int child = 0; child < children; child++) + result += traverseTree(proto_tree_model_->index(child, 0, travTree), identLevel); + } + } + + return result; +} + +QString ProtoTree::toString(const QModelIndex &start_idx) const +{ + QString tree_string = ""; + if (start_idx.isValid()) + tree_string = traverseTree(start_idx, 0); + else + { + int children = proto_tree_model_->rowCount(); + for (int child = 0; child < children; child++) + tree_string += traverseTree(proto_tree_model_->index(child, 0, QModelIndex()), 0); + } + + return tree_string; +} + +void ProtoTree::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. + Q_ASSERT(edt_ == NULL); + cap_file_ = cf; +} + +bool ProtoTree::eventFilter(QObject * obj, QEvent * event) +{ + if (event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseMove) + return QTreeView::eventFilter(obj, event); + + /* Mouse was over scrollbar, ignoring */ + if (qobject_cast(obj)) + return QTreeView::eventFilter(obj, event); + + if (event->type() == QEvent::MouseButtonPress) + { + QMouseEvent * ev = (QMouseEvent *)event; + + if (ev->buttons() & Qt::LeftButton) + drag_start_position_ = ev->pos(); + } + else if (event->type() == QEvent::MouseMove) + { + QMouseEvent * ev = (QMouseEvent *)event; + + if ((ev->buttons() & Qt::LeftButton) && (ev->pos() - drag_start_position_).manhattanLength() + > QApplication::startDragDistance()) + { + QModelIndex idx = indexAt(drag_start_position_); + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx)); + if (finfo.isValid()) + { + /* Hack to prevent QItemSelection taking the item which has been dragged over at start + * of drag-drop operation. selectionModel()->blockSignals could have done the trick, but + * it does not take in a QTreeWidget (maybe View) */ + emit fieldSelected(&finfo); + selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect); + + epan_dissect_t *edt = cap_file_ ? cap_file_->edt : edt_; + char *field_filter = proto_construct_match_selected_string(finfo.fieldInfo(), edt); + QString filter(field_filter); + wmem_free(NULL, field_filter); + + if (filter.length() > 0) + { + QJsonObject filterData; + filterData["filter"] = filter; + filterData["name"] = finfo.headerInfo().abbreviation; + filterData["description"] = finfo.headerInfo().name; + QMimeData * mimeData = new QMimeData(); + + mimeData->setData(WiresharkMimeData::DisplayFilterMimeType, QJsonDocument(filterData).toJson()); + mimeData->setText(toString(idx)); + + QDrag * drag = new QDrag(this); + drag->setMimeData(mimeData); + + QString lblTxt = QString("%1\n%2").arg(finfo.headerInfo().name, filter); + + DragLabel * content = new DragLabel(lblTxt, this); + + qreal dpr = window()->windowHandle()->devicePixelRatio(); + QPixmap pixmap(content->size() * dpr); + pixmap.setDevicePixelRatio(dpr); + content->render(&pixmap); + drag->setPixmap(pixmap); + + drag->exec(Qt::CopyAction); + + return true; + } + } + } + } + + return QTreeView::eventFilter(obj, event); +} + +QModelIndex ProtoTree::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) +{ + if (cursorAction == MoveLeft && selectionModel()->hasSelection()) { + QModelIndex cur_idx = selectionModel()->selectedIndexes().first(); + QModelIndex parent = cur_idx.parent(); + if (!isExpanded(cur_idx) && parent.isValid() && parent != rootIndex()) { + return parent; + } + } + return QTreeView::moveCursor(cursorAction, modifiers); +} diff --git a/ui/qt/proto_tree.h b/ui/qt/proto_tree.h new file mode 100644 index 00000000..511063e9 --- /dev/null +++ b/ui/qt/proto_tree.h @@ -0,0 +1,116 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PROTO_TREE_H +#define PROTO_TREE_H + +#include + +#include + +#include "cfile.h" + +#include "protocol_preferences_menu.h" + +#include +#include +#include + +class ProtoTreeModel; +class ProtoNode; + +class ProtoTree : public QTreeView +{ + Q_OBJECT +public: + explicit ProtoTree(QWidget *parent = 0, epan_dissect_t *edt_fixed = 0); + QMenu *colorizeMenu() { return &colorize_menu_; } + void setRootNode(proto_node *root_node); + void emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_type = FT_FRAMENUM_NONE); + void autoScrollTo(const QModelIndex &index); + void goToHfid(int hfid); + void clear(); + void restoreSelectedField(); + QString toString(const QModelIndex &start_idx = QModelIndex()) const; + +protected: + + enum { + Name = 0, + Description, + Value + }; + + virtual void contextMenuEvent(QContextMenuEvent *event); + virtual void timerEvent(QTimerEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual bool eventFilter(QObject * obj, QEvent * ev); + virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); + + QString traverseTree(const QModelIndex & rootNode, int identLevel = 0) const; + +private: + ProtoTreeModel *proto_tree_model_; + QMenu conv_menu_; + QMenu colorize_menu_; + ProtocolPreferencesMenu proto_prefs_menu_; + QList copy_actions_; + int column_resize_timer_; + QList > selected_hfid_path_; // row, hfinfo + + QPoint drag_start_position_; + + capture_file *cap_file_; + epan_dissect_t *edt_; + + void saveSelectedField(QModelIndex &index); + static void foreachTreeNode(proto_node *node, gpointer proto_tree_ptr); + void foreachExpand(const QModelIndex &index); + +signals: + void fieldSelected(FieldInformation *); + void openPacketInNewWindow(bool); + void goToPacket(int); + void relatedFrame(int, ft_framenum_type_t); + void showProtocolPreferences(const QString module_name); + void editProtocolPreference(struct preference *pref, struct pref_module *module); + +public slots: + + /* Set the capture file */ + void setCaptureFile(capture_file *cf); + void setMonospaceFont(const QFont &mono_font); + void syncExpanded(const QModelIndex & index); + void syncCollapsed(const QModelIndex & index); + void expandSubtrees(); + void collapseSubtrees(); + void expandAll(); + void collapseAll(); + void itemClicked(const QModelIndex & index); + void itemDoubleClicked(const QModelIndex & index); + void selectedFieldChanged(FieldInformation *); + void selectedFrameChanged(QList); + +protected slots: + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); +#if 0 + void ctxShowPacketBytes(); + void ctxExportPacketBytes(); +#endif + void ctxCopyVisibleItems(); + void ctxCopyAsFilter(); + void ctxCopySelectedInfo(); + void ctxOpenUrlWiki(); + +private slots: + void updateContentWidth(); + void connectToMainWindow(); +}; + +#endif // PROTO_TREE_H diff --git a/ui/qt/protocol_hierarchy_dialog.cpp b/ui/qt/protocol_hierarchy_dialog.cpp new file mode 100644 index 00000000..a97f37b7 --- /dev/null +++ b/ui/qt/protocol_hierarchy_dialog.cpp @@ -0,0 +1,464 @@ +/* protocol_hierarchy_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "protocol_hierarchy_dialog.h" +#include + +#include "cfile.h" + +#include "ui/proto_hier_stats.h" + +#include + +#include + +#include +#include "main_application.h" + +#include +#include + +#include +#include +#include +#include + +/* + * @file Protocol Hierarchy Statistics dialog + * + * Displays tree of protocols with various statistics + * Allows filtering on tree items + */ + +// To do: +// - Make "Copy as YAML" output a tree? +// - Add time series data to ph_stats_node_t and draw sparklines. + +const int protocol_col_ = 0; +const int pct_packets_col_ = 1; +const int packets_col_ = 2; +const int pct_bytes_col_ = 3; +const int bytes_col_ = 4; +const int bandwidth_col_ = 5; +const int end_packets_col_ = 6; +const int end_bytes_col_ = 7; +const int end_bandwidth_col_ = 8; +const int pdus_col_ = 9; + +struct addTreeNodeData { + QSet *protos; + QTreeWidgetItem *widget; +}; + +class ProtocolHierarchyTreeWidgetItem : public QTreeWidgetItem +{ +public: + ProtocolHierarchyTreeWidgetItem(QTreeWidgetItem *parent, ph_stats_node_t& ph_stats_node) : + QTreeWidgetItem(parent), + total_packets_(ph_stats_node.num_pkts_total), + total_pdus_(ph_stats_node.num_pdus_total), + last_packets_(ph_stats_node.num_pkts_last), + total_bytes_(ph_stats_node.num_bytes_total), + last_bytes_(ph_stats_node.num_bytes_last), + percent_packets_(0), + percent_bytes_(0), + bits_s_(0.0), + end_bits_s_(0.0) + { + filter_name_ = ph_stats_node.hfinfo->abbrev; + + if (!parent) return; + ph_stats_t *ph_stats = VariantPointer::asPtr(parent->treeWidget()->invisibleRootItem()->data(0, Qt::UserRole)); + + if (!ph_stats || ph_stats->tot_packets < 1) return; + percent_packets_ = total_packets_ * 100.0 / ph_stats->tot_packets; + percent_bytes_ = total_bytes_ * 100.0 / ph_stats->tot_bytes; + + double seconds = ph_stats->last_time - ph_stats->first_time; + + if (seconds > 0.0) { + bits_s_ = total_bytes_ * 8.0 / seconds; + end_bits_s_ = last_bytes_ * 8.0 / seconds; + } + + setText(protocol_col_, ph_stats_node.hfinfo->name); + setToolTip(protocol_col_, QString("%1").arg(ph_stats_node.hfinfo->abbrev)); + setData(pct_packets_col_, Qt::UserRole, percent_packets_); + setText(packets_col_, QString::number(total_packets_)); + setData(pct_bytes_col_, Qt::UserRole, percent_bytes_); + setText(bytes_col_, QString::number(total_bytes_)); + setText(bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(bits_s_) : UTF8_EM_DASH); + setText(end_packets_col_, QString::number(last_packets_)); + setText(end_bytes_col_, QString::number(last_bytes_)); + setText(end_bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(end_bits_s_) : UTF8_EM_DASH); + setText(pdus_col_, QString::number(total_pdus_)); + } + + // Return a QString, int, double, or invalid QVariant representing the raw column data. + QVariant colData(int col) const { + switch(col) { + case protocol_col_: + return text(col); + case (pct_packets_col_): + return percent_packets_; + case (packets_col_): + return total_packets_; + case (pct_bytes_col_): + return percent_bytes_; + case (bytes_col_): + return total_bytes_; + case (bandwidth_col_): + return bits_s_; + case (end_packets_col_): + return last_packets_; + case (end_bytes_col_): + return last_bytes_; + case (end_bandwidth_col_): + return end_bits_s_; + case (pdus_col_): + return total_pdus_; + default: + break; + } + return QVariant(); + } + + bool operator< (const QTreeWidgetItem &other) const + { + const ProtocolHierarchyTreeWidgetItem &other_phtwi = dynamic_cast(other); + + switch (treeWidget()->sortColumn()) { + case pct_packets_col_: + return percent_packets_ < other_phtwi.percent_packets_; + case packets_col_: + return total_packets_ < other_phtwi.total_packets_; + case pct_bytes_col_: + return percent_packets_ < other_phtwi.percent_packets_; + case bytes_col_: + return total_bytes_ < other_phtwi.total_bytes_; + case bandwidth_col_: + return bits_s_ < other_phtwi.bits_s_; + case end_packets_col_: + return last_packets_ < other_phtwi.last_packets_; + case end_bytes_col_: + return last_bytes_ < other_phtwi.last_bytes_; + case end_bandwidth_col_: + return end_bits_s_ < other_phtwi.end_bits_s_; + case pdus_col_: + return total_pdus_ < other_phtwi.total_pdus_; + default: + break; + } + + // Fall back to string comparison + return QTreeWidgetItem::operator <(other); + } + + const QString filterName(void) { return filter_name_; } + +private: + QString filter_name_; + unsigned total_packets_; + unsigned total_pdus_; + unsigned last_packets_; + unsigned total_bytes_; + unsigned last_bytes_; + + double percent_packets_; + double percent_bytes_; + double bits_s_; + double end_bits_s_; +}; + +ProtocolHierarchyDialog::ProtocolHierarchyDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::ProtocolHierarchyDialog) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 4 / 5); + setWindowSubtitle(tr("Protocol Hierarchy Statistics")); + + ui->hierStatsTreeWidget->setItemDelegateForColumn(pct_packets_col_, &percent_bar_delegate_); + ui->hierStatsTreeWidget->setItemDelegateForColumn(pct_bytes_col_, &percent_bar_delegate_); + ph_stats_t *ph_stats = ph_stats_new(cap_file_.capFile()); + if (ph_stats) { + ui->hierStatsTreeWidget->invisibleRootItem()->setData(0, Qt::UserRole, VariantPointer::asQVariant(ph_stats)); + addTreeNodeData atnd { &used_protos_, ui->hierStatsTreeWidget->invisibleRootItem() }; + g_node_children_foreach(ph_stats->stats_tree, G_TRAVERSE_ALL, addTreeNode, (void *)&atnd); + ph_stats_free(ph_stats); + } + + ui->hierStatsTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->hierStatsTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showProtoHierMenu(QPoint))); + + ui->hierStatsTreeWidget->setSortingEnabled(true); + ui->hierStatsTreeWidget->expandAll(); + + for (int i = 0; i < ui->hierStatsTreeWidget->columnCount(); i++) { + ui->hierStatsTreeWidget->resizeColumnToContents(i); + } + + QMenu *submenu; + + FilterAction::Action cur_action = FilterAction::ActionApply; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + cur_action = FilterAction::ActionPrepare; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + FilterAction *fa = new FilterAction(&ctx_menu_, FilterAction::ActionFind); + ctx_menu_.addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + + fa = new FilterAction(&ctx_menu_, FilterAction::ActionColorize); + ctx_menu_.addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionCopyAsCsv); + ctx_menu_.addAction(ui->actionCopyAsYaml); + + QPushButton *copy_button = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ApplyRole); + + QMenu *copy_menu = new QMenu(copy_button); + QAction *ca; + ca = copy_menu->addAction(tr("as CSV")); + ca->setToolTip(ui->actionCopyAsCsv->toolTip()); + connect(ca, &QAction::triggered, this, &ProtocolHierarchyDialog::on_actionCopyAsCsv_triggered); + ca = copy_menu->addAction(tr("as YAML")); + ca->setToolTip(ui->actionCopyAsYaml->toolTip()); + connect(ca, &QAction::triggered, this, &ProtocolHierarchyDialog::on_actionCopyAsYaml_triggered); + copy_button->setMenu(copy_menu); + connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsYaml_triggered())); + ca = copy_menu->addAction(tr("protocol short names")); + ca->setToolTip(ui->actionCopyProtoList->toolTip()); + connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyProtoList_triggered())); + copy_button->setMenu(copy_menu); + + QPushButton *protos_button = ui->buttonBox->addButton(tr("Protocols"), QDialogButtonBox::ApplyRole); + QMenu *protos_menu = new QMenu(protos_button); + proto_disable_ = protos_menu->addAction(tr("Disable unused")); + proto_disable_->setToolTip(ui->actionDisableProtos->toolTip()); + connect(proto_disable_, SIGNAL(triggered()), this, SLOT(on_actionDisableProtos_triggered())); + proto_revert_ = protos_menu->addAction(tr("Revert changes")); + proto_revert_->setToolTip(ui->actionRevertProtos->toolTip()); + connect(proto_revert_, SIGNAL(triggered()), this, SLOT(on_actionRevertProtos_triggered())); + protos_button->setMenu(protos_menu); + + QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close); + if (close_bt) { + close_bt->setDefault(true); + } + + display_filter_ = cap_file_.capFile()->dfilter; + updateWidgets(); +} + +ProtocolHierarchyDialog::~ProtocolHierarchyDialog() +{ + delete ui; +} + +void ProtocolHierarchyDialog::showProtoHierMenu(QPoint pos) +{ + bool enable = ui->hierStatsTreeWidget->currentItem() != NULL && !file_closed_ ? true : false; + + foreach (QMenu *submenu, ctx_menu_.findChildren()) { + submenu->setEnabled(enable); + } + foreach (QAction *action, ctx_menu_.actions()) { + if (action != ui->actionCopyAsCsv && action != ui->actionCopyAsYaml) { + action->setEnabled(enable); + } + } + + ctx_menu_.popup(ui->hierStatsTreeWidget->viewport()->mapToGlobal(pos)); +} + +void ProtocolHierarchyDialog::filterActionTriggered() +{ + ProtocolHierarchyTreeWidgetItem *phti = static_cast(ui->hierStatsTreeWidget->currentItem()); + FilterAction *fa = qobject_cast(QObject::sender()); + + if (!fa || !phti) { + return; + } + QString filter_name(phti->filterName()); + + emit filterAction(filter_name, fa->action(), fa->actionType()); +} + +void ProtocolHierarchyDialog::addTreeNode(GNode *node, gpointer data) +{ + ph_stats_node_t *stats = (ph_stats_node_t *)node->data; + if (!stats) return; + + addTreeNodeData *atndp = (addTreeNodeData *)data; + QTreeWidgetItem *parent_ti = atndp->widget; + if (!parent_ti) return; + + atndp->protos->insert(QString(stats->hfinfo->abbrev)); + + ProtocolHierarchyTreeWidgetItem *phti = new ProtocolHierarchyTreeWidgetItem(parent_ti, *stats); + addTreeNodeData atnd { atndp->protos, phti }; + + g_node_children_foreach(node, G_TRAVERSE_ALL, addTreeNode, (void *)&atnd); + +} + +void ProtocolHierarchyDialog::updateWidgets() +{ + QString hint = ""; + if (display_filter_.isEmpty()) { + hint += tr("No display filter."); + } else { + hint += tr("Display filter: %1").arg(display_filter_); + } + hint += ""; + ui->hintLabel->setText(hint); + + proto_revert_->setEnabled(enabled_protos_unsaved_changes()); + + WiresharkDialog::updateWidgets(); +} + +QList ProtocolHierarchyDialog::protoHierRowData(QTreeWidgetItem *item) const +{ + QList row_data; + + for (int col = 0; col < ui->hierStatsTreeWidget->columnCount(); col++) { + if (!item) { + row_data << ui->hierStatsTreeWidget->headerItem()->text(col); + } else { + ProtocolHierarchyTreeWidgetItem *phti = static_cast(item); + if (phti) { + row_data << phti->colData(col); + } + } + } + return row_data; +} + +void ProtocolHierarchyDialog::on_actionCopyAsCsv_triggered() +{ + QString csv; + QTextStream stream(&csv, QIODevice::Text); + QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget); + bool first = true; + + while (*iter) { + QStringList separated_value; + QTreeWidgetItem *item = first ? NULL : (*iter); + + foreach (QVariant v, protoHierRowData(item)) { + if (!v.isValid()) { + separated_value << "\"\""; + } else if (v.userType() == QMetaType::QString) { + separated_value << QString("\"%1\"").arg(v.toString()); + } else { + separated_value << v.toString(); + } + } + stream << separated_value.join(",") << '\n'; + + if (!first) ++iter; + first = false; + } + mainApp->clipboard()->setText(stream.readAll()); +} + +void ProtocolHierarchyDialog::on_actionCopyAsYaml_triggered() +{ + QString yaml; + QTextStream stream(&yaml, QIODevice::Text); + QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget); + bool first = true; + + stream << "---" << '\n'; + while (*iter) { + QTreeWidgetItem *item = first ? NULL : (*iter); + + stream << "-" << '\n'; + foreach (QVariant v, protoHierRowData(item)) { + stream << " - " << v.toString() << '\n'; + } + if (!first) ++iter; + first = false; + } + mainApp->clipboard()->setText(stream.readAll()); +} + +void ProtocolHierarchyDialog::on_actionCopyProtoList_triggered() +{ + QString plist; + QTextStream stream(&plist, QIODevice::Text); + bool first = true; + QSetIterator iter(used_protos_); + while (iter.hasNext()) { + if (!first) stream << ','; + stream << iter.next(); + first = false; + } + mainApp->clipboard()->setText(stream.readAll()); +} + +void ProtocolHierarchyDialog::on_actionDisableProtos_triggered() +{ + proto_disable_all(); + + QSetIterator iter(used_protos_); + while (iter.hasNext()) { + proto_enable_proto_by_name(iter.next().toStdString().c_str()); + } + /* Note that we aren't saving the changes here; they only apply + * to the current dissection. + * (Though if the user goes to the Enabled Protocols dialog and + * makes changes, these changes as well as the user's will be saved.) + */ + proto_revert_->setEnabled(enabled_protos_unsaved_changes()); + + QString hint = "" + + tr("Unused protocols have been disabled.") + + ""; + ui->hintLabel->setText(hint); + + // If we've done everything right, nothing should change. + //wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged); +} + +void ProtocolHierarchyDialog::on_actionRevertProtos_triggered() +{ + proto_reenable_all(); + read_enabled_and_disabled_lists(); + + proto_revert_->setEnabled(enabled_protos_unsaved_changes()); + QString hint = "" + + tr("Protocol changes have been reverted.") + + ""; + ui->hintLabel->setText(hint); + + // If we've done everything right, nothing should change. + //wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged); +} + +void ProtocolHierarchyDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_STATS_PROTO_HIERARCHY_DIALOG); +} diff --git a/ui/qt/protocol_hierarchy_dialog.h b/ui/qt/protocol_hierarchy_dialog.h new file mode 100644 index 00000000..08f3dfd2 --- /dev/null +++ b/ui/qt/protocol_hierarchy_dialog.h @@ -0,0 +1,63 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PROTOCOL_HIERARCHY_DIALOG_H +#define PROTOCOL_HIERARCHY_DIALOG_H + +#include +#include + +#include "filter_action.h" +#include +#include "wireshark_dialog.h" + +class QPushButton; +class QTreeWidgetItem; + +namespace Ui { +class ProtocolHierarchyDialog; +} + +class ProtocolHierarchyDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit ProtocolHierarchyDialog(QWidget &parent, CaptureFile &cf); + ~ProtocolHierarchyDialog(); + +signals: + void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + +private slots: + void showProtoHierMenu(QPoint pos); + void filterActionTriggered(); + void on_actionCopyAsCsv_triggered(); + void on_actionCopyAsYaml_triggered(); + void on_actionCopyProtoList_triggered(); + void on_actionDisableProtos_triggered(); + void on_actionRevertProtos_triggered(); + void on_buttonBox_helpRequested(); + +private: + Ui::ProtocolHierarchyDialog *ui; + QAction *proto_disable_; + QAction *proto_revert_; + QMenu ctx_menu_; + PercentBarDelegate percent_bar_delegate_; + QString display_filter_; + QSet used_protos_; + + // Callback for g_node_children_foreach + static void addTreeNode(GNode *node, gpointer data); + void updateWidgets(); + QList protoHierRowData(QTreeWidgetItem *item) const; +}; + +#endif // PROTOCOL_HIERARCHY_DIALOG_H diff --git a/ui/qt/protocol_hierarchy_dialog.ui b/ui/qt/protocol_hierarchy_dialog.ui new file mode 100644 index 00000000..085cdc7e --- /dev/null +++ b/ui/qt/protocol_hierarchy_dialog.ui @@ -0,0 +1,177 @@ + + + ProtocolHierarchyDialog + + + + 0 + 0 + 620 + 480 + + + + Dialog + + + + + + true + + + 50 + + + false + + + + Protocol + + + + + Percent Packets + + + + + Packets + + + + + Percent Bytes + + + + + Bytes + + + + + Bits/s + + + + + End Packets + + + + + End Bytes + + + + + End Bits/s + + + + + PDUs + + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Copy as CSV + + + Copy stream list as CSV. + + + + + Copy as YAML + + + Copy stream list as YAML. + + + + + Copy short names + + + Copy short protocol names in use. + + + + + Disable unused protocols + + + Disable all protocols but those listed. + + + + + Re-enable unused protocols + + + Re-enable protocols that were disabled in this dialog. + + + + + + + buttonBox + accepted() + ProtocolHierarchyDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ProtocolHierarchyDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/protocol_preferences_menu.cpp b/ui/qt/protocol_preferences_menu.cpp new file mode 100644 index 00000000..75c3515c --- /dev/null +++ b/ui/qt/protocol_preferences_menu.cpp @@ -0,0 +1,436 @@ +/* protocol_preferences_menu.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "protocol_preferences_menu.h" + +#include +#include +#include "uat_dialog.h" +#include "main_application.h" +#include "main_window.h" + +#include +#include + +// To do: +// - Elide really long items? +// - Handle color prefs. + +class BoolPreferenceAction : public QAction +{ +public: + BoolPreferenceAction(pref_t *pref, QObject *parent=0) : + QAction(parent), + pref_(pref) + { + setText(prefs_get_title(pref_)); + setCheckable(true); + setChecked(prefs_get_bool_value(pref_, pref_current)); + } + + unsigned int setBoolValue() { + return prefs_set_bool_value(pref_, isChecked(), pref_current); + } + + pref_t *getPref() { return pref_; } + +private: + pref_t *pref_; +}; + +class EnumPreferenceAction : public QAction +{ +public: + EnumPreferenceAction(pref_t *pref, const char *title, int enumval, QActionGroup *ag, QObject *parent=0) : + QAction(parent), + pref_(pref), + enumval_(enumval) + { + setText(title); + setActionGroup(ag); + setCheckable(true); + } + + unsigned int setEnumValue() { + return prefs_set_enum_value(pref_, enumval_, pref_current); + } + + pref_t *getPref() { return pref_; } + +private: + pref_t *pref_; + int enumval_; +}; + +class EnumCustomTCPOverridePreferenceAction : public QAction +{ +public: + EnumCustomTCPOverridePreferenceAction(pref_t *pref, const char *title, int enumval, QActionGroup *ag, QObject *parent=0) : + QAction(parent), + pref_(pref), + enumval_(enumval) + { + setText(title); + setActionGroup(ag); + setCheckable(true); + } + + unsigned int setEnumValue() { + return prefs_set_enum_value(pref_, enumval_, pref_current); + } + + int getEnumValue() { return enumval_; } + + pref_t *getPref() { return pref_; } + +private: + pref_t *pref_; + int enumval_; +}; + +class UatPreferenceAction : public QAction +{ +public: + UatPreferenceAction(pref_t *pref, QObject *parent=0) : + QAction(parent), + pref_(pref) + { + setText(QString("%1" UTF8_HORIZONTAL_ELLIPSIS).arg(prefs_get_title(pref_))); + } + + void showUatDialog() { + UatDialog *uat_dlg = new UatDialog(qobject_cast(parent()), prefs_get_uat_value(pref_)); + connect(uat_dlg, SIGNAL(destroyed(QObject*)), mainApp, SLOT(flushAppSignals())); + uat_dlg->setWindowModality(Qt::ApplicationModal); + uat_dlg->setAttribute(Qt::WA_DeleteOnClose); + uat_dlg->show(); + } + + pref_t *getPref() { return pref_; } + +private: + pref_t *pref_; +}; + +// Preference requires an external editor (PreferenceEditorFrame) +class EditorPreferenceAction : public QAction +{ +public: + EditorPreferenceAction(pref_t *pref, QObject *parent=0) : + QAction(parent), + pref_(pref) + { + QString title = prefs_get_title(pref_); + + title.append(QString(": %1" UTF8_HORIZONTAL_ELLIPSIS).arg(gchar_free_to_qstring(prefs_pref_to_str(pref_, pref_current)))); + + setText(title); + } + pref_t *pref() { return pref_; } + +private: + pref_t *pref_; +}; + +extern "C" { +// Preference callback + +static guint +add_prefs_menu_item(pref_t *pref, gpointer menu_ptr) +{ + ProtocolPreferencesMenu *pp_menu = static_cast(menu_ptr); + if (!pp_menu) return 1; + + pp_menu->addMenuItem(pref); + + return 0; +} +} + + +ProtocolPreferencesMenu::ProtocolPreferencesMenu() +{ + setTitle(tr("Protocol Preferences")); + setModule(NULL); +} + +ProtocolPreferencesMenu::ProtocolPreferencesMenu(const QString &title, const QString &module_name, QWidget *parent) : + QMenu(title, parent) +{ + setModule(module_name); +} + +void ProtocolPreferencesMenu::setModule(const QString module_name) +{ + QAction *action; + int proto_id = -1; + + if (!module_name.isEmpty()) { + proto_id = proto_get_id_by_filter_name(module_name.toUtf8().constData()); + } + + clear(); + module_name_.clear(); + module_ = NULL; + + protocol_ = find_protocol_by_id(proto_id); + const QString long_name = proto_get_protocol_long_name(protocol_); + const QString short_name = proto_get_protocol_short_name(protocol_); + if (module_name.isEmpty() || proto_id < 0 || !protocol_) { + action = addAction(tr("No protocol preferences available")); + action->setDisabled(true); + return; + } + + QAction *disable_action = new QAction(tr("Disable %1").arg(short_name), this); + connect(disable_action, SIGNAL(triggered(bool)), this, SLOT(disableProtocolTriggered())); + disable_action->setDisabled(!proto_can_toggle_protocol(proto_id)); + + module_ = prefs_find_module(module_name.toUtf8().constData()); + if (!module_ || !prefs_is_registered_protocol(module_name.toUtf8().constData())) { + action = addAction(tr("%1 has no preferences").arg(long_name)); + action->setDisabled(true); + addSeparator(); + addAction(disable_action); + return; + } + + module_name_ = module_name; + + action = addAction(tr("Open %1 preferences…").arg(long_name)); + if (module_->use_gui) { + action->setData(QString(module_name)); + connect(action, SIGNAL(triggered(bool)), this, SLOT(modulePreferencesTriggered())); + } else { + action->setDisabled(true); + } + addSeparator(); + + prefs_pref_foreach(module_, add_prefs_menu_item, this); + + if (!actions().last()->isSeparator()) { + addSeparator(); + } + addAction(disable_action); +} + +void ProtocolPreferencesMenu::addMenuItem(preference *pref) +{ + switch (prefs_get_type(pref)) { + case PREF_BOOL: + { + BoolPreferenceAction *bpa = new BoolPreferenceAction(pref, this); + addAction(bpa); + connect(bpa, SIGNAL(triggered(bool)), this, SLOT(boolPreferenceTriggered())); + break; + } + case PREF_ENUM: + { + QMenu *enum_menu = addMenu(prefs_get_title(pref)); + const enum_val_t *enum_valp = prefs_get_enumvals(pref); + if (enum_valp && enum_valp->name) { + QActionGroup *ag = new QActionGroup(this); + while (enum_valp->name) { + EnumPreferenceAction *epa = new EnumPreferenceAction(pref, enum_valp->description, enum_valp->value, ag, this); + if (prefs_get_enum_value(pref, pref_current) == enum_valp->value) { + epa->setChecked(true); + } + enum_menu->addAction(epa); + connect(epa, SIGNAL(triggered(bool)), this, SLOT(enumPreferenceTriggered())); + enum_valp++; + } + } + break; + } + case PREF_UINT: + case PREF_STRING: + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + case PREF_RANGE: + case PREF_DECODE_AS_UINT: + case PREF_DECODE_AS_RANGE: + case PREF_PASSWORD: + { + EditorPreferenceAction *epa = new EditorPreferenceAction(pref, this); + addAction(epa); + connect(epa, SIGNAL(triggered(bool)), this, SLOT(editorPreferenceTriggered())); + break; + } + case PREF_UAT: + { + UatPreferenceAction *upa = new UatPreferenceAction(pref, this); + addAction(upa); + connect(upa, SIGNAL(triggered(bool)), this, SLOT(uatPreferenceTriggered())); + break; + } + case PREF_CUSTOM: + case PREF_STATIC_TEXT: + case PREF_OBSOLETE: + break; + case PREF_PROTO_TCP_SNDAMB_ENUM: + { + int override_id = -1; + + /* ensure we have access to MainWindow, and indirectly to the selection */ + if (mainApp) { + MainWindow * mainWin = qobject_cast(mainApp->mainWindow()); + + if (mainWin != nullptr && !mainWin->selectedRows().isEmpty()) { + frame_data * fdata = mainWin->frameDataForRow(mainWin->selectedRows().at(0)); + if(fdata) { + override_id = fdata->tcp_snd_manual_analysis; + } + } + } + + if (override_id != -1) { + QMenu *enum_menu = addMenu(prefs_get_title(pref)); + const enum_val_t *enum_valp = prefs_get_enumvals(pref); + if (enum_valp && enum_valp->name) { + QActionGroup *ag = new QActionGroup(this); + while (enum_valp->name) { + EnumCustomTCPOverridePreferenceAction *epa = new EnumCustomTCPOverridePreferenceAction(pref, enum_valp->description, enum_valp->value, ag, this); + if (override_id>=0) { + if(override_id==enum_valp->value) + epa->setChecked(true); + } + else { + if(enum_valp->value == 0) + epa->setChecked(true); + } + + enum_menu->addAction(epa); + connect(epa, SIGNAL(triggered(bool)), this, SLOT(enumCustomTCPOverridePreferenceTriggered())); + enum_valp++; + } + } + } + break; + } + default: + // A type we currently don't handle. Just open the prefs dialog. + QString title = QString("%1" UTF8_HORIZONTAL_ELLIPSIS).arg(prefs_get_title(pref)); + QAction *mpa = addAction(title); + connect(mpa, SIGNAL(triggered(bool)), this, SLOT(modulePreferencesTriggered())); + break; + } +} + +void ProtocolPreferencesMenu::disableProtocolTriggered() +{ + EnabledProtocolsModel::disableProtocol(protocol_); +} + +void ProtocolPreferencesMenu::modulePreferencesTriggered() +{ + if (!module_name_.isEmpty()) { + emit showProtocolPreferences(module_name_); + } +} + +void ProtocolPreferencesMenu::editorPreferenceTriggered() +{ + EditorPreferenceAction *epa = static_cast(QObject::sender()); + if (!epa) return; + + if (epa->pref() && module_) { + emit editProtocolPreference(epa->pref(), module_); + } +} + +void ProtocolPreferencesMenu::boolPreferenceTriggered() +{ + BoolPreferenceAction *bpa = static_cast(QObject::sender()); + if (!bpa) return; + + module_->prefs_changed_flags |= bpa->setBoolValue(); + unsigned int changed_flags = module_->prefs_changed_flags; + + prefs_apply(module_); + prefs_main_write(); + commandline_options_drop(module_->name, prefs_get_name(bpa->getPref())); + + if (changed_flags & PREF_EFFECT_FIELDS) { + mainApp->emitAppSignal(MainApplication::FieldsChanged); + } + /* Protocol preference changes almost always affect dissection, + so don't bother checking flags */ + mainApp->emitAppSignal(MainApplication::PacketDissectionChanged); +} + +void ProtocolPreferencesMenu::enumPreferenceTriggered() +{ + EnumPreferenceAction *epa = static_cast(QObject::sender()); + if (!epa) return; + + unsigned int changed_flags = epa->setEnumValue(); + if (changed_flags) { // Changed + module_->prefs_changed_flags |= changed_flags; + prefs_apply(module_); + prefs_main_write(); + commandline_options_drop(module_->name, prefs_get_name(epa->getPref())); + + if (changed_flags & PREF_EFFECT_FIELDS) { + mainApp->emitAppSignal(MainApplication::FieldsChanged); + } + /* Protocol preference changes almost always affect dissection, + so don't bother checking flags */ + mainApp->emitAppSignal(MainApplication::PacketDissectionChanged); + } +} + +void ProtocolPreferencesMenu::enumCustomTCPOverridePreferenceTriggered() +{ + EnumCustomTCPOverridePreferenceAction *epa = static_cast(QObject::sender()); + if (!epa) return; + + /* ensure we have access to MainWindow, and indirectly to the selection */ + if (mainApp) { + MainWindow * mainWin = qobject_cast(mainApp->mainWindow()); + if (mainWin != nullptr && !mainWin->selectedRows().isEmpty()) { + frame_data * fdata = mainWin->frameDataForRow(mainWin->selectedRows().at(0)); + if(!fdata) + return; + + if (fdata->tcp_snd_manual_analysis != epa->getEnumValue()) { // Changed + fdata->tcp_snd_manual_analysis = epa->getEnumValue(); + + unsigned int changed_flags = prefs_get_effect_flags(epa->getPref()); + if (changed_flags & PREF_EFFECT_FIELDS) { + mainApp->emitAppSignal(MainApplication::FieldsChanged); + } + /* Protocol preference changes almost always affect dissection, + so don't bother checking flags */ + mainApp->emitAppSignal(MainApplication::PacketDissectionChanged); + } + } + } +} + +void ProtocolPreferencesMenu::uatPreferenceTriggered() +{ + UatPreferenceAction *upa = static_cast(QObject::sender()); + if (!upa) return; + + upa->showUatDialog(); +} diff --git a/ui/qt/protocol_preferences_menu.h b/ui/qt/protocol_preferences_menu.h new file mode 100644 index 00000000..d7569d67 --- /dev/null +++ b/ui/qt/protocol_preferences_menu.h @@ -0,0 +1,49 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __PROTOCOL_PREFERENCES_MENU_H__ +#define __PROTOCOL_PREFERENCES_MENU_H__ + +#include + +struct _protocol; +struct pref_module; +struct preference; + +class ProtocolPreferencesMenu : public QMenu +{ + Q_OBJECT + +public: + ProtocolPreferencesMenu(); + ProtocolPreferencesMenu(const QString &title, const QString &module_name, QWidget *parent = nullptr); + + void setModule(const QString module_name); + void addMenuItem(struct preference *pref); + +signals: + void showProtocolPreferences(const QString module_name); + void editProtocolPreference(struct preference *pref, struct pref_module *module); + +private: + QString module_name_; + struct pref_module *module_; + struct _protocol *protocol_; + +private slots: + void disableProtocolTriggered(); + void modulePreferencesTriggered(); + void editorPreferenceTriggered(); + void boolPreferenceTriggered(); + void enumPreferenceTriggered(); + void uatPreferenceTriggered(); + void enumCustomTCPOverridePreferenceTriggered(); +}; + +#endif // __PROTOCOL_PREFERENCES_MENU_H__ diff --git a/ui/qt/qt6-migration-links.txt b/ui/qt/qt6-migration-links.txt new file mode 100644 index 00000000..bc830441 --- /dev/null +++ b/ui/qt/qt6-migration-links.txt @@ -0,0 +1,12 @@ +https://doc.qt.io/qt-5/qtmac-obsolete.html +https://doc.qt.io/qt-6/extras-changes-qt6.html +https://doc.qt.io/qt-6/modulechanges.html +https://doc.qt.io/qt-6/cmake-qt5-and-qt6-compatibility.html +https://www.qt.io/blog/porting-from-qt-5-to-qt-6-using-qt5compat-library +https://dangelog.wordpress.com/2012/04/07/qregularexpression/ +https://doc.qt.io/qt-6/qtcore-changes-qt6.html#regular-expression-classes + + +https://doc.qt.io/qt-6/qtmodules.html +https://doc.qt.io/qt-6/whatsnew60.html + diff --git a/ui/qt/recent_file_status.cpp b/ui/qt/recent_file_status.cpp new file mode 100644 index 00000000..c26cf488 --- /dev/null +++ b/ui/qt/recent_file_status.cpp @@ -0,0 +1,36 @@ +/* recent_file_status.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "recent_file_status.h" + +RecentFileStatus::RecentFileStatus(const QString filename, QObject *parent) : + QObject(parent), + // Force a deep copy. + filename_(QString::fromStdU16String(filename.toStdU16String())) +{ + // We're a QObject, which means that we emit a destroyed signal, + // which might happen at the wrong time when automatic deletion is + // enabled. This will trigger an assert in debug builds (bug 14279). + setAutoDelete(false); + // Qt::QueuedConnection creates a copy of our argument list. This + // squelches what appears to be a ThreadSanitizer false positive. + connect(this, SIGNAL(statusFound(QString, qint64, bool)), + parent, SLOT(itemStatusFinished(QString, qint64, bool)), Qt::QueuedConnection); +} + +void RecentFileStatus::run() { + fileinfo_.setFile(filename_); + + if (fileinfo_.isFile() && fileinfo_.isReadable()) { + emit statusFound(filename_, fileinfo_.size(), true); + } else { + emit statusFound(filename_, 0, false); + } + deleteLater(); +} diff --git a/ui/qt/recent_file_status.h b/ui/qt/recent_file_status.h new file mode 100644 index 00000000..b05598bb --- /dev/null +++ b/ui/qt/recent_file_status.h @@ -0,0 +1,33 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RECENT_FILE_STATUS_H +#define RECENT_FILE_STATUS_H + +#include +#include + +class RecentFileStatus : public QObject, public QRunnable +{ + Q_OBJECT +public: + RecentFileStatus(const QString filename, QObject *parent); + +protected: + void run(); + +private: + const QString filename_; + QFileInfo fileinfo_; + +signals: + void statusFound(const QString filename = QString(), qint64 size = 0, bool accessible = false); +}; + +#endif // RECENT_FILE_STATUS_H diff --git a/ui/qt/remote_capture_dialog.cpp b/ui/qt/remote_capture_dialog.cpp new file mode 100644 index 00000000..dff16527 --- /dev/null +++ b/ui/qt/remote_capture_dialog.cpp @@ -0,0 +1,169 @@ +/* remote_capture_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +// XXX This shouldn't exist. These controls should be in ManageInterfacesDialog instead. + +#include "config.h" +#ifdef HAVE_PCAP_REMOTE +#include +#include +#include "ui/capture_globals.h" +#include "remote_capture_dialog.h" +#include +#include "capture_opts.h" +#include "capture/capture-pcap-util.h" +#include "ui/capture_ui_utils.h" +#include "epan/prefs.h" +#include "epan/to_str.h" +#include "ui/ws_ui_util.h" +#include "ui/recent.h" + +#include + +RemoteCaptureDialog::RemoteCaptureDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::RemoteCaptureDialog) +{ + ui->setupUi(this); + + fillComboBox(); + connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(apply_remote())); + connect(this, SIGNAL(remoteAdded(GList *, remote_options*)), parent, SIGNAL(remoteAdded(GList *, remote_options*))); + connect(ui->hostCombo, &QComboBox::currentTextChanged, this, &RemoteCaptureDialog::hostChanged); +} + +RemoteCaptureDialog::~RemoteCaptureDialog() +{ + delete ui; +} + +void RemoteCaptureDialog::hostChanged(const QString host) +{ + if (!host.compare(tr("Clear list"))) { + recent_free_remote_host_list(); + ui->hostCombo->clear(); + } else { + struct remote_host *rh = recent_get_remote_host(host.toUtf8().constData()); + if (rh) { + ui->portText->setText(QString(rh->remote_port)); + if (rh->auth_type == CAPTURE_AUTH_NULL) { + ui->nullAuth->setChecked(true); + } else { + ui->pwAuth->setChecked(true); + } + } + } + +} + +static void fillBox(gpointer key, gpointer, gpointer user_data) +{ + QComboBox *cb = (QComboBox *)user_data; + cb->addItem(QString((gchar*)key)); +} + +void RemoteCaptureDialog::fillComboBox() +{ + int remote_host_list_size; + + ui->hostCombo->addItem(QString("")); + remote_host_list_size = recent_get_remote_host_list_size(); + if (remote_host_list_size > 0) { + recent_remote_host_list_foreach(fillBox, ui->hostCombo); + ui->hostCombo->insertSeparator(remote_host_list_size+1); + ui->hostCombo->addItem(QString(tr("Clear list"))); + } +} + +void RemoteCaptureDialog::apply_remote() +{ + int err; + gchar *err_str; + remote_options global_remote_opts; + + QString host = ui->hostCombo->currentText(); + global_remote_opts.src_type = CAPTURE_IFREMOTE; + global_remote_opts.remote_host_opts.remote_host = qstring_strdup(host); + QString port = ui->portText->text(); + global_remote_opts.remote_host_opts.remote_port = qstring_strdup(port); + if (ui->pwAuth->isChecked()) { + global_remote_opts.remote_host_opts.auth_type = CAPTURE_AUTH_PWD; + } else { + global_remote_opts.remote_host_opts.auth_type = CAPTURE_AUTH_NULL; + } + QString user = ui->userText->text(); + global_remote_opts.remote_host_opts.auth_username = qstring_strdup(user); + QString pw = ui->pwText->text(); + global_remote_opts.remote_host_opts.auth_password = qstring_strdup(pw); + global_remote_opts.remote_host_opts.datatx_udp = FALSE; + global_remote_opts.remote_host_opts.nocap_rpcap = TRUE; + global_remote_opts.remote_host_opts.nocap_local = FALSE; +#ifdef HAVE_PCAP_SETSAMPLING + global_remote_opts.sampling_method = CAPTURE_SAMP_NONE; + global_remote_opts.sampling_param = 0; +#endif + + GList *rlist = get_remote_interface_list(global_remote_opts.remote_host_opts.remote_host, + global_remote_opts.remote_host_opts.remote_port, + global_remote_opts.remote_host_opts.auth_type, + global_remote_opts.remote_host_opts.auth_username, + global_remote_opts.remote_host_opts.auth_password, + &err, &err_str); + if (rlist == NULL) { + if (err == 0) + QMessageBox::warning(this, tr("Error"), tr("No remote interfaces found.")); + else if (err == CANT_GET_INTERFACE_LIST) + QMessageBox::critical(this, tr("Error"), err_str); + else if (err == DONT_HAVE_PCAP) + QMessageBox::critical(this, tr("Error"), tr("PCAP not found")); + else + QMessageBox::critical(this, tr("Error"), "Unknown error"); + return; + } + if (ui->hostCombo->count() == 0) { + ui->hostCombo->addItem(""); + ui->hostCombo->addItem(host); + ui->hostCombo->insertSeparator(2); + ui->hostCombo->addItem(QString(tr("Clear list"))); + } else { + ui->hostCombo->insertItem(0, host); + } + struct remote_host *rh = recent_get_remote_host(host.toUtf8().constData()); + if (!rh) { + rh = (struct remote_host *)g_malloc (sizeof (*rh)); + rh->r_host = qstring_strdup(host); + rh->remote_port = qstring_strdup(port); + rh->auth_type = global_remote_opts.remote_host_opts.auth_type; + rh->auth_password = g_strdup(""); + rh->auth_username = g_strdup(""); + recent_add_remote_host(global_remote_opts.remote_host_opts.remote_host, rh); + } + emit remoteAdded(rlist, &global_remote_opts); +} + +void RemoteCaptureDialog::on_pwAuth_toggled(bool checked) +{ + if (checked) { + ui->userLabel->setEnabled(true); + ui->userText->setEnabled(true); + ui->pwLabel->setEnabled(true); + ui->pwText->setEnabled(true); + } +} + +void RemoteCaptureDialog::on_nullAuth_toggled(bool checked) +{ + if (checked) { + ui->userLabel->setEnabled(false); + ui->userText->setEnabled(false); + ui->pwLabel->setEnabled(false); + ui->pwText->setEnabled(false); + } +} +#endif /* HAVE_PCAP_REMOTE */ diff --git a/ui/qt/remote_capture_dialog.h b/ui/qt/remote_capture_dialog.h new file mode 100644 index 00000000..86ef0740 --- /dev/null +++ b/ui/qt/remote_capture_dialog.h @@ -0,0 +1,48 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef REMOTE_CAPTURE_DIALOG_H +#define REMOTE_CAPTURE_DIALOG_H + +#include + +#ifdef HAVE_PCAP_REMOTE +#include +#include +#include "capture_opts.h" + + +namespace Ui { +class RemoteCaptureDialog; +} + +class RemoteCaptureDialog : public QDialog +{ + Q_OBJECT + +public: + explicit RemoteCaptureDialog(QWidget *parent = 0); + ~RemoteCaptureDialog(); + +signals: + void remoteAdded(GList *rlist, remote_options *roptions); + +private slots: + void on_pwAuth_toggled(bool checked); + void on_nullAuth_toggled(bool checked); + void apply_remote(); + void hostChanged(const QString host); + +private: + Ui::RemoteCaptureDialog *ui; + + void fillComboBox(); +}; +#endif +#endif // REMOTE_CAPTURE_DIALOG_H diff --git a/ui/qt/remote_capture_dialog.ui b/ui/qt/remote_capture_dialog.ui new file mode 100644 index 00000000..585f4d8f --- /dev/null +++ b/ui/qt/remote_capture_dialog.ui @@ -0,0 +1,179 @@ + + + RemoteCaptureDialog + + + + 0 + 0 + 233 + 256 + + + + Remote Interface + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Host: + + + + + + + true + + + + + + + Port: + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + + + Authentication + + + + + + + + + Null authentication + + + true + + + + + + + Password authentication + + + + + + + + + false + + + Username: + + + + + + + false + + + + + + + false + + + Password: + + + + + + + false + + + QLineEdit::Password + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + buttonBox + accepted() + RemoteCaptureDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RemoteCaptureDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/remote_settings_dialog.cpp b/ui/qt/remote_settings_dialog.cpp new file mode 100644 index 00000000..e9dfa531 --- /dev/null +++ b/ui/qt/remote_settings_dialog.cpp @@ -0,0 +1,77 @@ +/* remote_settings_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +// XXX This shouldn't exist. These controls should be in ManageInterfacesDialog instead. + +#include "config.h" +#ifdef HAVE_PCAP_REMOTE +#include "remote_settings_dialog.h" +#include + +RemoteSettingsDialog::RemoteSettingsDialog(QWidget *parent, interface_t *iface) : + QDialog(parent), + ui(new Ui::RemoteSettingsDialog) +{ + ui->setupUi(this); + mydevice.name = g_strdup(iface->name); + ui->rpcapBox->setCheckState(iface->remote_opts.remote_host_opts.nocap_rpcap?Qt::Checked:Qt::Unchecked); + ui->udpBox->setCheckState(iface->remote_opts.remote_host_opts.datatx_udp?Qt::Checked:Qt::Unchecked); +#ifdef HAVE_PCAP_SETSAMPLING + switch (iface->remote_opts.sampling_method) + { + case CAPTURE_SAMP_NONE: + ui->sampleNone->setChecked(true); + break; + case CAPTURE_SAMP_BY_COUNT: + ui->samplePkt->setChecked(true); + ui->spinPkt->setValue(iface->remote_opts.sampling_param); + break; + case CAPTURE_SAMP_BY_TIMER: + ui->sampleTime->setChecked(true); + ui->spinTime->setValue(iface->remote_opts.sampling_param); + break; + } +#else + ui->sampleLabel->setVisible(false); + ui->sampleNone->setVisible(false); + ui->samplePkt->setVisible(false); + ui->sampleTime->setVisible(false); + ui->spinPkt->setVisible(false); + ui->spinTime->setVisible(false); + ui->pktLabel->setVisible(false); + ui->timeLabel->setVisible(false); + resize(width(), height() - ui->sampleLabel->height() - 3 * ui->sampleNone->height()); +#endif + connect(this, SIGNAL(remoteSettingsChanged(interface_t *)), parent, SIGNAL(remoteSettingsChanged(interface_t *))); +} + +RemoteSettingsDialog::~RemoteSettingsDialog() +{ + delete ui; +} + +void RemoteSettingsDialog::on_buttonBox_accepted() +{ + mydevice.remote_opts.remote_host_opts.nocap_rpcap = (ui->rpcapBox->checkState()==Qt::Checked)?true:false; + mydevice.remote_opts.remote_host_opts.datatx_udp = (ui->udpBox->checkState()==Qt::Checked)?true:false; +#ifdef HAVE_PCAP_SETSAMPLING + if (ui->sampleNone->isChecked()) { + mydevice.remote_opts.sampling_method = CAPTURE_SAMP_NONE; + mydevice.remote_opts.sampling_param = 0; + } else if (ui->samplePkt->isChecked()) { + mydevice.remote_opts.sampling_method = CAPTURE_SAMP_BY_COUNT; + mydevice.remote_opts.sampling_param = ui->spinPkt->value(); + } else { + mydevice.remote_opts.sampling_method = CAPTURE_SAMP_BY_TIMER; + mydevice.remote_opts.sampling_param = ui->spinTime->value(); + } +#endif + emit remoteSettingsChanged(&mydevice); +} +#endif diff --git a/ui/qt/remote_settings_dialog.h b/ui/qt/remote_settings_dialog.h new file mode 100644 index 00000000..8e751e09 --- /dev/null +++ b/ui/qt/remote_settings_dialog.h @@ -0,0 +1,42 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef REMOTE_SETTINGS_DIALOG_H +#define REMOTE_SETTINGS_DIALOG_H + +#include + +#ifdef HAVE_PCAP_REMOTE +#include +#include "capture_opts.h" + +namespace Ui { +class RemoteSettingsDialog; +} + +class RemoteSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit RemoteSettingsDialog(QWidget *parent = 0, interface_t *iface = NULL); + ~RemoteSettingsDialog(); + +signals: + void remoteSettingsChanged(interface_t *iface); + +private slots: + void on_buttonBox_accepted(); + +private: + Ui::RemoteSettingsDialog *ui; + interface_t mydevice; +}; +#endif +#endif // REMOTE_SETTINGS_DIALOG_H diff --git a/ui/qt/remote_settings_dialog.ui b/ui/qt/remote_settings_dialog.ui new file mode 100644 index 00000000..ae059f13 --- /dev/null +++ b/ui/qt/remote_settings_dialog.ui @@ -0,0 +1,164 @@ + + + RemoteSettingsDialog + + + + 0 + 0 + 231 + 229 + + + + + 0 + 0 + + + + Remote Capture Settings + + + + + + QLayout::SetMinimumSize + + + + + Capture Options + + + + + + + Do not capture own RPCAP traffic + + + + + + + Use UDP for data transfer + + + + + + + Sampling Options + + + + + + + true + + + None + + + + + + + + + 1 of + + + + + + + 1000000 + + + + + + + packets + + + + + + + + + + + 1 every + + + + + + + 1000000 + + + + + + + milliseconds + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + buttonBox + accepted() + RemoteSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RemoteSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/resolved_addresses_dialog.cpp b/ui/qt/resolved_addresses_dialog.cpp new file mode 100644 index 00000000..dfe51c84 --- /dev/null +++ b/ui/qt/resolved_addresses_dialog.cpp @@ -0,0 +1,204 @@ +/* resolved_addresses_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "resolved_addresses_dialog.h" +#include + +#include "config.h" + +#include + +#include "file.h" + +#include "epan/addr_resolv.h" +#include + +#include +#include +#include +#include + +#include "capture_file.h" +#include "main_application.h" + +#include +#include + +const QString no_entries_ = QObject::tr("No entries."); +const QString entry_count_ = QObject::tr("%1 entries."); + +ResolvedAddressesDialog::ResolvedAddressesDialog(QWidget *parent, QString captureFile, wtap* wth) : + GeometryStateDialog(parent), + ui(new Ui::ResolvedAddressesDialog), + file_name_(tr("[no file]")) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + + QStringList title_parts = QStringList() << tr("Resolved Addresses"); + + if (!captureFile.isEmpty()) { + file_name_ = captureFile; + title_parts << file_name_; + } + setWindowTitle(mainApp->windowTitleString(title_parts)); + + ui->plainTextEdit->setFont(mainApp->monospaceFont()); + ui->plainTextEdit->setReadOnly(true); + ui->plainTextEdit->setWordWrapMode(QTextOption::NoWrap); + + if (wth) { + // might return null + wtap_block_t nrb_hdr; + + /* + * XXX - support multiple NRBs. + */ + nrb_hdr = wtap_file_get_nrb(wth); + if (nrb_hdr != NULL) { + char *str; + + /* + * XXX - support multiple comments. + */ + if (wtap_block_get_nth_string_option_value(nrb_hdr, OPT_COMMENT, 0, &str) == WTAP_OPTTYPE_SUCCESS) { + comment_ = str; + } + } + } + + fillBlocks(); + + ethSortModel = new AStringListListSortFilterProxyModel(this); + ethTypeModel = new AStringListListSortFilterProxyModel(this); + EthernetAddressModel * ethModel = new EthernetAddressModel(this); + ethSortModel->setSourceModel(ethModel); + ethSortModel->setColumnsToFilter(QList() << 1 << 2); + ethSortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + ethTypeModel->setSourceModel(ethSortModel); + ethTypeModel->setColumnToFilter(0); + ethTypeModel->setColumnToHide(0); + ui->tblAddresses->setModel(ethTypeModel); + ui->tblAddresses->resizeColumnsToContents(); + ui->tblAddresses->horizontalHeader()->setStretchLastSection(true); + ui->tblAddresses->sortByColumn(1, Qt::AscendingOrder); + ui->cmbDataType->addItems(ethModel->filterValues()); + + portSortModel = new AStringListListSortFilterProxyModel(this); + portTypeModel = new AStringListListSortFilterProxyModel(this); + PortsModel * portModel = new PortsModel(this); + portSortModel->setSourceModel(portModel); + portSortModel->setColumnAsNumeric(1); + portSortModel->setColumnsToFilter(QList() << 0 << 1); + portSortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + portTypeModel->setSourceModel(portSortModel); + portTypeModel->setColumnToFilter(2); + portTypeModel->setColumnAsNumeric(1); + ui->tblPorts->setModel(portTypeModel); + ui->tblPorts->resizeColumnsToContents(); + ui->tblPorts->horizontalHeader()->setStretchLastSection(true); + ui->tblPorts->sortByColumn(1, Qt::AscendingOrder); + ui->cmbPortFilterType->addItems(portModel->filterValues()); +} + +ResolvedAddressesDialog::~ResolvedAddressesDialog() +{ + delete ui; +} + +void ResolvedAddressesDialog::on_cmbDataType_currentIndexChanged(int index) +{ + if (! ethSortModel) + return; + + QString filter = ui->cmbDataType->itemText(index); + if (index == 0) + { + filter.clear(); + ethTypeModel->setFilterType(AStringListListSortFilterProxyModel::FilterNone, 0); + } + else + ethTypeModel->setFilterType(AStringListListSortFilterProxyModel::FilterByEquivalent, 0); + ethTypeModel->setFilter(filter); +} + +void ResolvedAddressesDialog::on_txtSearchFilter_textChanged(QString) +{ + QString filter = ui->txtSearchFilter->text(); + if (!ethSortModel || (!filter.isEmpty() && filter.length() < 3)) + return; + + ethSortModel->setFilter(filter); +} + +void ResolvedAddressesDialog::on_cmbPortFilterType_currentIndexChanged(int index) +{ + if (! portSortModel) + return; + + QString filter = ui->cmbPortFilterType->itemText(index); + if (index == 0) + { + filter.clear(); + portTypeModel->setFilterType(AStringListListSortFilterProxyModel::FilterNone, 2); + } + else + portTypeModel->setFilterType(AStringListListSortFilterProxyModel::FilterByEquivalent, 2); + portTypeModel->setFilter(filter); +} + +void ResolvedAddressesDialog::on_txtPortFilter_textChanged(QString val) +{ + if (! portSortModel) + return; + + portSortModel->setFilter(val); +} + +void ResolvedAddressesDialog::changeEvent(QEvent *event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + fillBlocks(); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + +void ResolvedAddressesDialog::fillBlocks() +{ + setUpdatesEnabled(false); + ui->plainTextEdit->clear(); + + QString lines; + ui->plainTextEdit->appendPlainText(tr("# Resolved addresses found in %1").arg(file_name_)); + + if (ui->actionComment->isChecked()) { + lines = "\n"; + lines.append(tr("# Comments\n#\n# ")); + if (!comment_.isEmpty()) { + lines.append("\n\n"); + lines.append(comment_); + lines.append("\n"); + } else { + lines.append(no_entries_); + } + ui->plainTextEdit->appendPlainText(lines); + } + + ui->plainTextEdit->moveCursor(QTextCursor::Start); + setUpdatesEnabled(true); +} diff --git a/ui/qt/resolved_addresses_dialog.h b/ui/qt/resolved_addresses_dialog.h new file mode 100644 index 00000000..dc6a9b64 --- /dev/null +++ b/ui/qt/resolved_addresses_dialog.h @@ -0,0 +1,53 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RESOLVED_ADDRESSES_DIALOG_H +#define RESOLVED_ADDRESSES_DIALOG_H + +#include "geometry_state_dialog.h" + +#include + +class CaptureFile; +class AStringListListSortFilterProxyModel; + +namespace Ui { +class ResolvedAddressesDialog; +} + +class ResolvedAddressesDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit ResolvedAddressesDialog(QWidget *parent, QString captureFile, wtap* wth); + ~ResolvedAddressesDialog(); + +protected slots: + void on_cmbDataType_currentIndexChanged(int index); + void on_txtSearchFilter_textChanged(QString text); + void on_cmbPortFilterType_currentIndexChanged(int index); + void on_txtPortFilter_textChanged(QString text); + + void changeEvent(QEvent* event); + +private: + Ui::ResolvedAddressesDialog *ui; + QString file_name_; + QString comment_; + + AStringListListSortFilterProxyModel * ethSortModel; + AStringListListSortFilterProxyModel * ethTypeModel; + AStringListListSortFilterProxyModel * portSortModel; + AStringListListSortFilterProxyModel * portTypeModel; + + void fillBlocks(); +}; + +#endif // RESOLVED_ADDRESSES_DIALOG_H diff --git a/ui/qt/resolved_addresses_dialog.ui b/ui/qt/resolved_addresses_dialog.ui new file mode 100644 index 00000000..c77c5cc1 --- /dev/null +++ b/ui/qt/resolved_addresses_dialog.ui @@ -0,0 +1,271 @@ + + + ResolvedAddressesDialog + + + + 0 + 0 + 620 + 450 + + + + Dialog + + + + + + 0 + + + + Hosts + + + + + + + + Search for entry (min 3 characters) + + + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + true + + + false + + + + + + + + Ports + + + + + + + + Search for port or name + + + + + + + + + + + + true + + + false + + + + + + + + Capture File Comments + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + true + + + true + + + Comment + + + Show the comment. + + + + + true + + + IPv4 Hash Table + + + Show the IPv4 hash table entries. + + + + + true + + + IPv6 Hash Table + + + Show the IPv6 hash table entries. + + + + + Show All + + + Show all address types. + + + + + Hide All + + + Hide all address types. + + + + + true + + + true + + + IPv4 and IPv6 Addresses (hosts) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + + + + + true + + + true + + + Port names (services) + + + Show resolved port names in "services" format. + + + + + true + + + true + + + Ethernet Addresses + + + Show resolved Ethernet addresses in "ethers" format. + + + + + true + + + true + + + Ethernet Well-Known Addresses + + + Show well-known Ethernet addresses in "ethers" format. + + + + + true + + + true + + + Ethernet Manufacturers + + + Show Ethernet manufacturers in "ethers" format. + + + + + + + buttonBox + accepted() + ResolvedAddressesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ResolvedAddressesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/response_time_delay_dialog.cpp b/ui/qt/response_time_delay_dialog.cpp new file mode 100644 index 00000000..f656525e --- /dev/null +++ b/ui/qt/response_time_delay_dialog.cpp @@ -0,0 +1,270 @@ +/* response_time_delay_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "response_time_delay_dialog.h" + +#include "file.h" + +#include "epan/proto.h" +#include "epan/rtd_table.h" + +#include + +#include +#include "main_application.h" + +static QHash cfg_str_to_rtd_; + +extern "C" { +static void +rtd_init(const char *args, void*) { + QStringList args_l = QString(args).split(','); + if (args_l.length() > 1) { + QString rtd = QString("%1,%2").arg(args_l[0]).arg(args_l[1]); + QString filter; + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(","); + } + mainApp->emitTapParameterSignal(rtd, filter, NULL); + } +} +} + +bool register_response_time_delay_tables(const void *, void *value, void*) +{ + register_rtd_t *rtd = (register_rtd_t*)value; + const char* short_name = proto_get_protocol_short_name(find_protocol_by_id(get_rtd_proto_id(rtd))); + char *cfg_abbr = rtd_table_get_tap_string(rtd); + + cfg_str_to_rtd_[cfg_abbr] = rtd; + TapParameterDialog::registerDialog( + short_name, + cfg_abbr, + REGISTER_STAT_GROUP_RESPONSE_TIME, + rtd_init, + ResponseTimeDelayDialog::createRtdDialog); + g_free(cfg_abbr); + return FALSE; +} + +enum { + col_type_, + col_messages_, + col_min_srt_, + col_max_srt_, + col_avg_srt_, + col_min_frame_, + col_max_frame_, + col_open_requests, + col_discarded_responses_, + col_repeated_requests_, + col_repeated_responses_ +}; + +enum { + rtd_table_type_ = 1000, + rtd_time_stat_type_ +}; + +class RtdTimeStatTreeWidgetItem : public QTreeWidgetItem +{ +public: + RtdTimeStatTreeWidgetItem(QTreeWidget *parent, const QString type, const rtd_timestat *timestat) : + QTreeWidgetItem (parent, rtd_time_stat_type_), + type_(type), + timestat_(timestat) + { + setText(col_type_, type_); + setHidden(true); + } + void draw() { + setText(col_messages_, QString::number(timestat_->rtd->num)); + setText(col_min_srt_, QString::number(nstime_to_sec(×tat_->rtd->min), 'f', 6)); + setText(col_max_srt_, QString::number(nstime_to_sec(×tat_->rtd->max), 'f', 6)); + setText(col_avg_srt_, QString::number(get_average(×tat_->rtd->tot, timestat_->rtd->num) / 1000.0, 'f', 6)); + setText(col_min_frame_, QString::number(timestat_->rtd->min_num)); + setText(col_max_frame_, QString::number(timestat_->rtd->max_num)); + setText(col_open_requests, QString::number(timestat_->open_req_num)); + setText(col_discarded_responses_, QString::number(timestat_->disc_rsp_num)); + setText(col_repeated_requests_, QString::number(timestat_->req_dup_num)); + setText(col_repeated_responses_, QString::number(timestat_->rsp_dup_num)); + + setHidden(timestat_->rtd->num < 1); + } + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != rtd_time_stat_type_) return QTreeWidgetItem::operator< (other); + const RtdTimeStatTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case col_messages_: + return timestat_->rtd->num < other_row->timestat_->rtd->num; + case col_min_srt_: + return nstime_cmp(×tat_->rtd->min, &other_row->timestat_->rtd->min) < 0; + case col_max_srt_: + return nstime_cmp(×tat_->rtd->max, &other_row->timestat_->rtd->max) < 0; + case col_avg_srt_: + { + double our_avg = get_average(×tat_->rtd->tot, timestat_->rtd->num); + double other_avg = get_average(&other_row->timestat_->rtd->tot, other_row->timestat_->rtd->num); + return our_avg < other_avg; + } + case col_min_frame_: + return timestat_->rtd->min_num < other_row->timestat_->rtd->min_num; + case col_max_frame_: + return timestat_->rtd->max_num < other_row->timestat_->rtd->max_num; + case col_open_requests: + return timestat_->open_req_num < other_row->timestat_->open_req_num; + case col_discarded_responses_: + return timestat_->disc_rsp_num < other_row->timestat_->disc_rsp_num; + case col_repeated_requests_: + return timestat_->req_dup_num < other_row->timestat_->req_dup_num; + case col_repeated_responses_: + return timestat_->rsp_dup_num < other_row->timestat_->rsp_dup_num; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList rowData() { + return QList() << type_ << timestat_->rtd->num + << nstime_to_sec(×tat_->rtd->min) << nstime_to_sec(×tat_->rtd->max) + << get_average(×tat_->rtd->tot, timestat_->rtd->num) / 1000.0 + << timestat_->rtd->min_num << timestat_->rtd->max_num + << timestat_->open_req_num << timestat_->disc_rsp_num + << timestat_->req_dup_num << timestat_->rsp_dup_num; + } + +private: + const QString type_; + const rtd_timestat *timestat_; +}; + +ResponseTimeDelayDialog::ResponseTimeDelayDialog(QWidget &parent, CaptureFile &cf, register_rtd *rtd, const QString filter, int help_topic) : + TapParameterDialog(parent, cf, help_topic), + rtd_(rtd) +{ + QString subtitle = tr("%1 Response Time Delay Statistics") + .arg(proto_get_protocol_short_name(find_protocol_by_id(get_rtd_proto_id(rtd)))); + setWindowSubtitle(subtitle); + loadGeometry(0, 0, "ResponseTimeDelayDialog"); + + QStringList header_names = QStringList() + << tr("Type") << tr("Messages") + << tr("Min SRT") << tr("Max SRT") << tr("Avg SRT") + << tr("Min in Frame") << tr("Max in Frame") + << tr("Open Requests") << tr("Discarded Responses") + << tr("Repeated Requests") << tr("Repeated Responses"); + + statsTreeWidget()->setHeaderLabels(header_names); + + for (int col = 0; col < statsTreeWidget()->columnCount(); col++) { + if (col == col_type_) continue; + statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); + } + + if (!filter.isEmpty()) { + setDisplayFilter(filter); + } +} + +TapParameterDialog *ResponseTimeDelayDialog::createRtdDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf) +{ + if (!cfg_str_to_rtd_.contains(cfg_str)) { + // XXX MessageBox? + return NULL; + } + + register_rtd_t *rtd = cfg_str_to_rtd_[cfg_str]; + + return new ResponseTimeDelayDialog(parent, cf, rtd, filter); +} + +void ResponseTimeDelayDialog::addRtdTable(const _rtd_stat_table *rtd_table) +{ + for (unsigned i = 0; i < rtd_table->num_rtds; i++) { + const QString type = val_to_qstring(i, get_rtd_value_string(rtd_), "Other (%d)"); + new RtdTimeStatTreeWidgetItem(statsTreeWidget(), type, &rtd_table->time_stats[i]); + } +} + +void ResponseTimeDelayDialog::tapReset(void *rtdd_ptr) +{ + rtd_data_t *rtdd = (rtd_data_t*) rtdd_ptr; + ResponseTimeDelayDialog *rtd_dlg = static_cast(rtdd->user_data); + if (!rtd_dlg) return; + + reset_rtd_table(&rtdd->stat_table); + rtd_dlg->statsTreeWidget()->clear(); + rtd_dlg->addRtdTable(&rtdd->stat_table); +} + +void ResponseTimeDelayDialog::tapDraw(void *rtdd_ptr) +{ + rtd_data_t *rtdd = (rtd_data_t*) rtdd_ptr; + ResponseTimeDelayDialog *rtd_dlg = static_cast(rtdd->user_data); + if (!rtd_dlg || !rtd_dlg->statsTreeWidget()) return; + + QTreeWidgetItemIterator it(rtd_dlg->statsTreeWidget()); + while (*it) { + if ((*it)->type() == rtd_time_stat_type_) { + RtdTimeStatTreeWidgetItem *rtd_ts_ti = static_cast((*it)); + rtd_ts_ti->draw(); + } + ++it; + } + + for (int i = 0; i < rtd_dlg->statsTreeWidget()->columnCount() - 1; i++) { + rtd_dlg->statsTreeWidget()->resizeColumnToContents(i); + } +} + +void ResponseTimeDelayDialog::fillTree() +{ + rtd_data_t rtd_data; + memset (&rtd_data, 0, sizeof(rtd_data)); + rtd_table_dissector_init(rtd_, &rtd_data.stat_table, NULL, NULL); + rtd_data.user_data = this; + + QByteArray display_filter = displayFilter().toUtf8(); + if (!registerTapListener(get_rtd_tap_listener_name(rtd_), + &rtd_data, + display_filter.constData(), + 0, + tapReset, + get_rtd_packet_func(rtd_), + tapDraw)) { + free_rtd_table(&rtd_data.stat_table); + reject(); // XXX Stay open instead? + return; + } + + statsTreeWidget()->setSortingEnabled(false); + + cap_file_.retapPackets(); + + tapDraw(&rtd_data); + + statsTreeWidget()->sortItems(col_type_, Qt::AscendingOrder); + statsTreeWidget()->setSortingEnabled(true); + + removeTapListeners(); + free_rtd_table(&rtd_data.stat_table); +} + +QList ResponseTimeDelayDialog::treeItemData(QTreeWidgetItem *ti) const +{ + QList tid; + if (ti->type() == rtd_time_stat_type_) { + RtdTimeStatTreeWidgetItem *rtd_ts_ti = static_cast(ti); + tid << rtd_ts_ti->rowData(); + } + return tid; +} diff --git a/ui/qt/response_time_delay_dialog.h b/ui/qt/response_time_delay_dialog.h new file mode 100644 index 00000000..95b0d866 --- /dev/null +++ b/ui/qt/response_time_delay_dialog.h @@ -0,0 +1,54 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __RESPONSE_TIME_DELAY_DIALOG_H__ +#define __RESPONSE_TIME_DELAY_DIALOG_H__ + +#include "tap_parameter_dialog.h" + +struct _rtd_stat_table; + +class ResponseTimeDelayDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + ResponseTimeDelayDialog(QWidget &parent, CaptureFile &cf, struct register_rtd *rtd, const QString filter, int help_topic = 0); + static TapParameterDialog *createRtdDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf); + +protected: + /** Add a response time delay table. + * + * @param rtd_table The table to add. + */ + // gtk:service_response_table.h:init_srt_table + void addRtdTable(const struct _rtd_stat_table *rtd_table); + +private: + struct register_rtd *rtd_; + + // Callbacks for register_tap_listener + static void tapReset(void *rtdd_ptr); + static void tapDraw(void *rtdd_ptr); + + virtual QList treeItemData(QTreeWidgetItem *ti) const; + +private slots: + virtual void fillTree(); +}; + +/** Register function to register dissectors that support RTD for Qt. + * + * @param key is unused + * @param value register_rtd_t* representing dissetor RTD table + * @param userdata is unused + */ +bool register_response_time_delay_tables(const void *key, void *value, void *userdata); + +#endif // __RESPONSE_TIME_DELAY_DIALOG_H__ diff --git a/ui/qt/rpc_service_response_time_dialog.cpp b/ui/qt/rpc_service_response_time_dialog.cpp new file mode 100644 index 00000000..7c7f70b3 --- /dev/null +++ b/ui/qt/rpc_service_response_time_dialog.cpp @@ -0,0 +1,422 @@ +/* rpc_service_response_time_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +// warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4267) +#endif + +#include "rpc_service_response_time_dialog.h" + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// To do: +// - Don't assume that the user knows what programs+versions are in the +// capture. I.e. combine this dialog with the ONC-RPC Programs dialog, +// with two lists: programs on top, procedures on the bottom. +// - Allow the display of multiple programs and versions. +// - Expose the DCE-RPC UUIDs and ONC-RPC program numbers e.g. in an extra +// column. +// - Make the version in the command-line args optional? + +extern "C" { +static void +dce_rpc_add_program(gpointer key_ptr, gpointer value_ptr, gpointer rsrtd_ptr) +{ + RpcServiceResponseTimeDialog *rsrt_dlg = dynamic_cast((RpcServiceResponseTimeDialog *)rsrtd_ptr); + if (!rsrt_dlg) return; + + guid_key *key = (guid_key *)key_ptr; + dcerpc_uuid_value *value = (dcerpc_uuid_value *)value_ptr; + + rsrt_dlg->addDceRpcProgram(key, value); +} + +static void +dce_rpc_find_versions(gpointer key_ptr, gpointer, gpointer rsrtd_ptr) +{ + RpcServiceResponseTimeDialog *rsrt_dlg = dynamic_cast((RpcServiceResponseTimeDialog *)rsrtd_ptr); + if (!rsrt_dlg) return; + + guid_key *key = (guid_key *)key_ptr; + rsrt_dlg->addDceRpcProgramVersion(key); +} + +static void +onc_rpc_add_program(gpointer prog_ptr, gpointer value_ptr, gpointer rsrtd_ptr) +{ + RpcServiceResponseTimeDialog *rsrt_dlg = dynamic_cast((RpcServiceResponseTimeDialog *)rsrtd_ptr); + if (!rsrt_dlg) return; + + guint32 program = GPOINTER_TO_UINT(prog_ptr); + rpc_prog_info_value *value = (rpc_prog_info_value *) value_ptr; + + rsrt_dlg->addOncRpcProgram(program, value); +} + +static void +onc_rpc_find_versions(const gchar *, ftenum_t , gpointer rpik_ptr, gpointer, gpointer rsrtd_ptr) +{ + RpcServiceResponseTimeDialog *rsrt_dlg = dynamic_cast((RpcServiceResponseTimeDialog *)rsrtd_ptr); + if (!rsrt_dlg) return; + + rpc_proc_info_key *rpik = (rpc_proc_info_key *)rpik_ptr; + + rsrt_dlg->addOncRpcProgramVersion(rpik->prog, rpik->vers); +} + +static void +onc_rpc_count_procedures(const gchar *, ftenum_t , gpointer rpik_ptr, gpointer, gpointer rsrtd_ptr) +{ + RpcServiceResponseTimeDialog *rsrt_dlg = dynamic_cast((RpcServiceResponseTimeDialog *)rsrtd_ptr); + if (!rsrt_dlg) return; + + rpc_proc_info_key *rpik = (rpc_proc_info_key *)rpik_ptr; + + rsrt_dlg->updateOncRpcProcedureCount(rpik->prog, rpik->vers, rpik->proc); +} + +} // extern "C" + +RpcServiceResponseTimeDialog::RpcServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, struct register_srt *srt, RpcFamily dlg_type, const QString filter) : + ServiceResponseTimeDialog(parent, cf, srt, filter), + dlg_type_(dlg_type) +{ + setRetapOnShow(false); + setHint(tr("Select a program and version and enter a filter if desired, then press Apply.")); + + QHBoxLayout *filter_layout = filterLayout(); + program_combo_ = new QComboBox(this); + version_combo_ = new QComboBox(this); + + filter_layout->insertStretch(0, 1); + filter_layout->insertWidget(0, version_combo_); + filter_layout->insertWidget(0, new QLabel(tr("Version:"))); + filter_layout->insertWidget(0, program_combo_); + filter_layout->insertWidget(0, new QLabel(tr("Program:"))); + + if (dlg_type == DceRpc) { + setWindowSubtitle(tr("DCE-RPC Service Response Times")); + g_hash_table_foreach(dcerpc_uuids, dce_rpc_add_program, this); + // This is a loooooong list. The GTK+ UI addresses this by making + // the program combo a tree instead of a list. We might want to add a + // full-height list to the left of the stats tree instead. + QStringList programs = dce_name_to_uuid_key_.keys(); + std::sort(programs.begin(), programs.end(), qStringCaseLessThan); + connect(program_combo_, SIGNAL(currentTextChanged(const QString)), + this, SLOT(dceRpcProgramChanged(const QString))); + program_combo_->addItems(programs); + } else { + setWindowSubtitle(tr("ONC-RPC Service Response Times")); + g_hash_table_foreach(rpc_progs, onc_rpc_add_program, this); + QStringList programs = onc_name_to_program_.keys(); + std::sort(programs.begin(), programs.end(), qStringCaseLessThan); + connect(program_combo_, SIGNAL(currentTextChanged(const QString)), + this, SLOT(oncRpcProgramChanged(const QString))); + program_combo_->addItems(programs); + } +} + +TapParameterDialog *RpcServiceResponseTimeDialog::createDceRpcSrtDialog(QWidget &parent, const QString, const QString opt_arg, CaptureFile &cf) +{ + QString filter; + bool have_args = false; + QString program_name; + e_guid_t uuid; + int version = 0; + + // dcerpc,srt,,.[,] + QStringList args_l = QString(opt_arg).split(','); + if (args_l.length() > 1) { + // XXX Switch to QUuid. + unsigned d1, d2, d3, d4_0, d4_1, d4_2, d4_3, d4_4, d4_5, d4_6, d4_7; + if (sscanf(args_l[0].toUtf8().constData(), + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + &d1, &d2, &d3, + &d4_0, &d4_1, &d4_2, &d4_3, &d4_4, &d4_5, &d4_6, &d4_7) == 11) { + uuid.data1 = d1; + uuid.data2 = d2; + uuid.data3 = d3; + uuid.data4[0] = d4_0; + uuid.data4[1] = d4_1; + uuid.data4[2] = d4_2; + uuid.data4[3] = d4_3; + uuid.data4[4] = d4_4; + uuid.data4[5] = d4_5; + uuid.data4[6] = d4_6; + uuid.data4[7] = d4_7; + } else { + program_name = args_l[0]; + } + version = args_l[1].split('.')[0].toInt(); + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(","); + } + have_args = true; + } + RpcServiceResponseTimeDialog *dce_rpc_dlg = new RpcServiceResponseTimeDialog(parent, cf, get_srt_table_by_name("dcerpc"), DceRpc, filter); + + if (have_args) { + if (program_name.isEmpty()) { + dce_rpc_dlg->setDceRpcUuidAndVersion(&uuid, version); + } else { + dce_rpc_dlg->setRpcNameAndVersion(program_name, version); + } + } + // Else the GTK+ UI throws an error. + + return dce_rpc_dlg; +} + +TapParameterDialog *RpcServiceResponseTimeDialog::createOncRpcSrtDialog(QWidget &parent, const QString, const QString opt_arg, CaptureFile &cf) +{ + QString filter; + bool have_args = false; + QString program_name; + int program_num = 0; + int version = 0; + + // rpc,srt,,[, + QStringList args_l = QString(opt_arg).split(','); + if (args_l.length() > 1) { + bool ok = false; + program_num = args_l[0].toInt(&ok); + if (!ok) { + program_name = args_l[0]; + } + version = args_l[1].toInt(); + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(","); + } + have_args = true; + } + + RpcServiceResponseTimeDialog *onc_rpc_dlg = new RpcServiceResponseTimeDialog(parent, cf, get_srt_table_by_name("rpc"), OncRpc, filter); + + if (have_args) { + if (program_name.isEmpty()) { + onc_rpc_dlg->setOncRpcProgramAndVersion(program_num, version); + } else { + onc_rpc_dlg->setRpcNameAndVersion(program_name, version); + } + } + // Else the GTK+ UI throws an error. + + return onc_rpc_dlg; +} + +void RpcServiceResponseTimeDialog::addDceRpcProgram(_guid_key *key, _dcerpc_uuid_value *value) +{ + dce_name_to_uuid_key_.insert(value->name, key); +} + +void RpcServiceResponseTimeDialog::addDceRpcProgramVersion(_guid_key *key) +{ + if (guid_cmp(&(dce_name_to_uuid_key_[program_combo_->currentText()]->guid), &(key->guid))) return; + + versions_ << key->ver; + std::sort(versions_.begin(), versions_.end()); +} + +void RpcServiceResponseTimeDialog::addOncRpcProgram(guint32 program, _rpc_prog_info_value *value) +{ + onc_name_to_program_.insert(value->progname, program); +} + +void RpcServiceResponseTimeDialog::addOncRpcProgramVersion(guint32 program, guint32 version) +{ + if (onc_name_to_program_[program_combo_->currentText()] != program) return; + + if (versions_.isEmpty()) { + versions_ << version; + return; + } + while (version < versions_.first()) { + versions_.prepend(versions_.first() - 1); + } + while (version > versions_.last()) { + versions_.append(versions_.last() + 1); + } +} + +void RpcServiceResponseTimeDialog::updateOncRpcProcedureCount(guint32 program, guint32 version, int procedure) +{ + if (onc_name_to_program_[program_combo_->currentText()] != program) return; + if (version_combo_->itemData(version_combo_->currentIndex()).toUInt() != version) return; + + if (procedure > onc_rpc_num_procedures_) onc_rpc_num_procedures_ = procedure; +} + +void RpcServiceResponseTimeDialog::setDceRpcUuidAndVersion(_e_guid_t *uuid, int version) +{ + bool found = false; + for (int pi = 0; pi < program_combo_->count(); pi++) { + if (guid_cmp(uuid, &(dce_name_to_uuid_key_[program_combo_->itemText(pi)]->guid)) == 0) { + program_combo_->setCurrentIndex(pi); + + for (int vi = 0; vi < version_combo_->count(); vi++) { + if (version == (int) version_combo_->itemData(vi).toUInt()) { + version_combo_->setCurrentIndex(vi); + found = true; + break; + } + } + break; + } + } + if (found) fillTree(); +} + +void RpcServiceResponseTimeDialog::setOncRpcProgramAndVersion(int program, int version) +{ + bool found = false; + for (int pi = 0; pi < program_combo_->count(); pi++) { + if (program == (int) onc_name_to_program_[program_combo_->itemText(pi)]) { + program_combo_->setCurrentIndex(pi); + + for (int vi = 0; vi < version_combo_->count(); vi++) { + if (version == (int) version_combo_->itemData(vi).toUInt()) { + version_combo_->setCurrentIndex(vi); + found = true; + break; + } + } + break; + } + } + if (found) fillTree(); +} + +void RpcServiceResponseTimeDialog::setRpcNameAndVersion(const QString &program_name, int version) +{ + bool found = false; + for (int pi = 0; pi < program_combo_->count(); pi++) { + if (program_name.compare(program_combo_->itemText(pi), Qt::CaseInsensitive) == 0) { + program_combo_->setCurrentIndex(pi); + + for (int vi = 0; vi < version_combo_->count(); vi++) { + if (version == (int) version_combo_->itemData(vi).toUInt()) { + version_combo_->setCurrentIndex(vi); + found = true; + break; + } + } + break; + } + } + if (found) fillTree(); +} + +void RpcServiceResponseTimeDialog::dceRpcProgramChanged(const QString &program_name) +{ + clearVersionCombo(); + + if (!dce_name_to_uuid_key_.contains(program_name)) return; + + g_hash_table_foreach(dcerpc_uuids, dce_rpc_find_versions, this); + + fillVersionCombo(); +} + +void RpcServiceResponseTimeDialog::oncRpcProgramChanged(const QString &program_name) +{ + clearVersionCombo(); + + if (!onc_name_to_program_.contains(program_name)) return; + + dissector_table_foreach ("rpc.call", onc_rpc_find_versions, this); + dissector_table_foreach ("rpc.reply", onc_rpc_find_versions, this); + + fillVersionCombo(); +} + +void RpcServiceResponseTimeDialog::clearVersionCombo() +{ + version_combo_->clear(); + versions_.clear(); +} + +void RpcServiceResponseTimeDialog::fillVersionCombo() +{ + foreach (unsigned version, versions_) { + version_combo_->addItem(QString::number(version), version); + } + if (versions_.count() > 0) { + // Select the highest-numbered version. + version_combo_->setCurrentIndex(static_cast(versions_.count()) - 1); + } +} + +void RpcServiceResponseTimeDialog::provideParameterData() +{ + void *tap_data = NULL; + const QString program_name = program_combo_->currentText(); + guint32 max_procs = 0; + + switch (dlg_type_) { + case DceRpc: + { + if (!dce_name_to_uuid_key_.contains(program_name)) return; + + guid_key *dkey = dce_name_to_uuid_key_[program_name]; + guint16 version = (guint16) version_combo_->itemData(version_combo_->currentIndex()).toUInt(); + dcerpc_sub_dissector *procs = dcerpc_get_proto_sub_dissector(&(dkey->guid), version); + if (!procs) return; + + dcerpcstat_tap_data_t *dtap_data = g_new0(dcerpcstat_tap_data_t, 1); + dtap_data->uuid = dkey->guid; + dtap_data->ver = version; + dtap_data->prog = dcerpc_get_proto_name(&dtap_data->uuid, dtap_data->ver); + + for (int i = 0; procs[i].name; i++) { + if (procs[i].num > max_procs) max_procs = procs[i].num; + } + dtap_data->num_procedures = max_procs + 1; + + tap_data = dtap_data; + break; + } + case OncRpc: + { + if (!onc_name_to_program_.contains(program_name)) return; + + rpcstat_tap_data_t *otap_data = g_new0(rpcstat_tap_data_t, 1); + otap_data->program = onc_name_to_program_[program_name]; + otap_data->prog = rpc_prog_name(otap_data->program); + otap_data->version = (guint32) version_combo_->itemData(version_combo_->currentIndex()).toUInt(); + + onc_rpc_num_procedures_ = -1; + dissector_table_foreach ("rpc.call", onc_rpc_count_procedures, this); + dissector_table_foreach ("rpc.reply", onc_rpc_count_procedures, this); + otap_data->num_procedures = onc_rpc_num_procedures_ + 1; + + tap_data = otap_data; + break; + } + } + + set_srt_table_param_data(srt_, tap_data); +} diff --git a/ui/qt/rpc_service_response_time_dialog.h b/ui/qt/rpc_service_response_time_dialog.h new file mode 100644 index 00000000..d8f2ca53 --- /dev/null +++ b/ui/qt/rpc_service_response_time_dialog.h @@ -0,0 +1,71 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __RPC_SERVICE_RESPONSE_TIME_DIALOG_H__ +#define __RPC_SERVICE_RESPONSE_TIME_DIALOG_H__ + +#include "service_response_time_dialog.h" + +class QComboBox; + +struct _guid_key; +struct _dcerpc_uuid_value; +struct _e_guid_t; +struct _rpc_prog_info_value; + +class RpcServiceResponseTimeDialog : public ServiceResponseTimeDialog +{ + Q_OBJECT + +public: + enum RpcFamily { + DceRpc, + OncRpc + }; + + RpcServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, struct register_srt *srt, RpcFamily dlg_type, const QString filter); + static TapParameterDialog *createDceRpcSrtDialog(QWidget &parent, const QString, const QString opt_arg, CaptureFile &cf); + static TapParameterDialog *createOncRpcSrtDialog(QWidget &parent, const QString, const QString opt_arg, CaptureFile &cf); + + void addDceRpcProgram(_guid_key *key, struct _dcerpc_uuid_value *value); + void addDceRpcProgramVersion(_guid_key *key); + void addOncRpcProgram(guint32 program, struct _rpc_prog_info_value *value); + void addOncRpcProgramVersion(guint32 program, guint32 version); + void updateOncRpcProcedureCount(guint32 program, guint32 version, int procedure); + + void setDceRpcUuidAndVersion(struct _e_guid_t *uuid, int version); + void setOncRpcProgramAndVersion(int program, int version); + void setRpcNameAndVersion(const QString &program_name, int version); + +protected: + virtual void provideParameterData(); + +public slots: + void dceRpcProgramChanged(const QString &program_name); + void oncRpcProgramChanged(const QString &program_name); + +private: + RpcFamily dlg_type_; + QComboBox *program_combo_; + QComboBox *version_combo_; + QList versions_; + + // DCE-RPC + QMap dce_name_to_uuid_key_; + + // ONC-RPC + QMap onc_name_to_program_; + int onc_rpc_num_procedures_; + + void clearVersionCombo(); + void fillVersionCombo(); + +}; + +#endif // __RPC_SERVICE_RESPONSE_TIME_DIALOG_H__ diff --git a/ui/qt/rsa_keys_frame.cpp b/ui/qt/rsa_keys_frame.cpp new file mode 100644 index 00000000..c784ab9c --- /dev/null +++ b/ui/qt/rsa_keys_frame.cpp @@ -0,0 +1,273 @@ +/* rsa_keys_frame.cpp + * + * Copyright 2019 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "rsa_keys_frame.h" +#include + +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBGNUTLS +RsaKeysFrame::RsaKeysFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::RsaKeysFrame), + rsa_keys_model_(0), + pkcs11_libs_model_(0) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC + ui->addFileButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->addItemButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->deleteItemButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->addLibraryButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->deleteLibraryButton->setAttribute(Qt::WA_MacSmallSize, true); +#endif + +#ifdef HAVE_GNUTLS_PKCS11 + pkcs11_libs_model_ = new UatModel(this, "PKCS #11 Provider Libraries"); + ui->libsView->setModel(pkcs11_libs_model_); + connect(ui->libsView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &RsaKeysFrame::libCurrentChanged); +#else + ui->addLibraryButton->setEnabled(false); +#endif + + rsa_keys_model_ = new UatModel(this, "RSA Private Keys"); + ui->keysView->setModel(rsa_keys_model_); + connect(ui->keysView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &RsaKeysFrame::keyCurrentChanged); +} +#else /* ! HAVE_LIBGNUTLS */ +RsaKeysFrame::RsaKeysFrame(QWidget *parent) : QFrame(parent) { } +#endif /* ! HAVE_LIBGNUTLS */ + +#ifdef HAVE_LIBGNUTLS +RsaKeysFrame::~RsaKeysFrame() +{ + delete ui; +} + +gboolean RsaKeysFrame::verifyKey(const char *uri, const char *password, gboolean *need_password, QString &error) +{ + char *error_c = NULL; + gboolean key_ok = secrets_verify_key(qPrintable(uri), qPrintable(password), need_password, &error_c); + error = error_c ? error_c : ""; + g_free(error_c); + return key_ok; +} + +void RsaKeysFrame::addKey(const QString &uri, const QString &password) +{ + // Create a new UAT entry with the given URI and PIN/password. + int row = rsa_keys_model_->rowCount(); + rsa_keys_model_->insertRows(row, 1); + rsa_keys_model_->setData(rsa_keys_model_->index(row, 0), uri); + rsa_keys_model_->setData(rsa_keys_model_->index(row, 1), password); + ui->keysView->setCurrentIndex(rsa_keys_model_->index(row, 0)); +} + +void RsaKeysFrame::keyCurrentChanged(const QModelIndex ¤t, const QModelIndex & /* previous */) +{ + ui->deleteItemButton->setEnabled(current.isValid()); +} + +void RsaKeysFrame::on_addItemButton_clicked() +{ + GSList *keys_list = secrets_get_available_keys(); + QStringList keys; + if (keys_list) { + for (GSList *uri = keys_list; uri; uri = uri->next) { + keys << (char *)uri->data; + } + g_slist_free_full(keys_list, g_free); + } + + // Remove duplicates (keys that have already been added) + for (int row = rsa_keys_model_->rowCount() - 1; row >= 0; --row) { + QString item = rsa_keys_model_->data(rsa_keys_model_->index(row, 0)).toString(); + keys.removeAll(item); + } + + if (keys.isEmpty()) { + QMessageBox::information(this, tr("Add PKCS #11 token or key"), + tr("No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider."), + QMessageBox::Ok); + return; + } + + bool ok; + QString item = QInputDialog::getItem(this, + tr("Select a new PKCS #11 token or key"), + tr("PKCS #11 token or key"), keys, 0, false, &ok); + if (!ok || item.isEmpty()) { + return; + } + + // Validate the token, is a PIN needed? + gboolean key_ok = false, needs_pin = true; + QString error; + if (!item.startsWith("pkcs11:")) { + // For keys other than pkcs11, try to verify the key without password. + // (The PIN must always be prompted for PKCS #11 tokens, otherwise it is + // possible that an already unlocked token will not trigger a prompt). + key_ok = verifyKey(qPrintable(item), NULL, &needs_pin, error); + } + QString pin; + while (!key_ok && needs_pin) { + // A PIN is possibly needed, prompt for one. + QString msg; + if (!error.isEmpty()) { + msg = error + "\n"; + error.clear(); + } + msg += tr("Enter PIN or password for %1 (it will be stored unencrypted)"); + pin = QInputDialog::getText(this, tr("Enter PIN or password for key"), + msg.arg(item), QLineEdit::Password, "", &ok); + if (!ok) { + return; + } + key_ok = verifyKey(qPrintable(item), qPrintable(pin), NULL, error); + } + if (!key_ok) { + QMessageBox::warning(this, + tr("Add PKCS #11 token or key"), + tr("Key could not be added: %1").arg(item), + QMessageBox::Ok); + return; + } + + addKey(item, pin); +} + +void RsaKeysFrame::on_addFileButton_clicked() +{ + QString filter = + tr("RSA private key (*.pem *.p12 *.pfx *.key);;All Files (" ALL_FILES_WILDCARD ")"); + QString file = WiresharkFileDialog::getOpenFileName(this, + tr("Select RSA private key file"), "", filter); + if (file.isEmpty()) { + return; + } + + // Try to load the key as unencrypted key file. If any errors occur, assume + // an encrypted key file and prompt for a password. + QString password, error; + gboolean key_ok = secrets_verify_key(qPrintable(file), NULL, NULL, NULL); + while (!key_ok) { + QString msg; + if (!error.isEmpty()) { + msg = error + "\n"; + error.clear(); + } + msg += QString("Enter the password to open %1").arg(file); + + bool ok; + password = QInputDialog::getText(this, tr("Select RSA private key file"), msg, + QLineEdit::Password, "", &ok); + if (!ok) { + return; + } + key_ok = verifyKey(qPrintable(file), qPrintable(password), NULL, error); + } + + addKey(file, password); +} + +void RsaKeysFrame::on_deleteItemButton_clicked() +{ + const QModelIndex ¤t = ui->keysView->currentIndex(); + if (rsa_keys_model_ && current.isValid()) { + rsa_keys_model_->removeRows(current.row(), 1); + } +} + +void RsaKeysFrame::acceptChanges() +{ + // Save keys list mutations. The PKCS #11 provider list was already saved. + QString error; + if (rsa_keys_model_->applyChanges(error) && !error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } +} + +void RsaKeysFrame::rejectChanges() +{ + // Revert keys list mutations. The PKCS #11 provider list was already saved. + QString error; + if (rsa_keys_model_->revertChanges(error) && !error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } +} + +void RsaKeysFrame::libCurrentChanged(const QModelIndex ¤t, const QModelIndex & /* previous */) +{ + ui->deleteLibraryButton->setEnabled(current.isValid()); +} + +void RsaKeysFrame::on_addLibraryButton_clicked() +{ + if (!pkcs11_libs_model_) return; + +#ifdef Q_OS_WIN + QString filter(tr("Libraries (*.dll)")); +#else + QString filter(tr("Libraries (*.so)")); +#endif + QString file = WiresharkFileDialog::getOpenFileName(this, tr("Select PKCS #11 Provider Library"), "", filter); + if (file.isEmpty()) { + return; + } + + int row = pkcs11_libs_model_->rowCount(); + pkcs11_libs_model_->insertRows(row, 1); + pkcs11_libs_model_->setData(pkcs11_libs_model_->index(row, 0), file); + ui->libsView->setCurrentIndex(pkcs11_libs_model_->index(row, 0)); + + // As the libraries affect the availability of PKCS #11 tokens, we will + // immediately apply changes without waiting for the OK button to be + // activated. + QString error; + if (pkcs11_libs_model_->applyChanges(error) && error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } +} + +void RsaKeysFrame::on_deleteLibraryButton_clicked() +{ + if (!pkcs11_libs_model_) return; + + const QModelIndex ¤t = ui->libsView->currentIndex(); + if (!current.isValid()) { + return; + } + + QString file = pkcs11_libs_model_->data(current, 0).toString(); + pkcs11_libs_model_->removeRows(current.row(), 1); + // Due to technical limitations of GnuTLS, libraries cannot be unloaded or + // disabled once loaded. Inform the user of this caveat. + QMessageBox::information(this, tr("Changes will apply after a restart"), + tr("PKCS #11 provider %1 will be removed after the next restart.").arg(file), + QMessageBox::Ok); + // Make sure the UAT is actually saved to file. + QString error; + if (pkcs11_libs_model_->applyChanges(error) && error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } +} +#endif /* HAVE_LIBGNUTLS */ diff --git a/ui/qt/rsa_keys_frame.h b/ui/qt/rsa_keys_frame.h new file mode 100644 index 00000000..fae40e2f --- /dev/null +++ b/ui/qt/rsa_keys_frame.h @@ -0,0 +1,57 @@ +/** @file + * + * Copyright 2019 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RSA_KEYS_FRAME_H +#define RSA_KEYS_FRAME_H + +#include + +#include + +#include + +namespace Ui { +class RsaKeysFrame; +} + +class RsaKeysFrame : public QFrame +{ + Q_OBJECT + +public: + explicit RsaKeysFrame(QWidget *parent = NULL); +#ifdef HAVE_LIBGNUTLS + ~RsaKeysFrame(); + + void acceptChanges(); + void rejectChanges(); + +private: + Ui::RsaKeysFrame *ui; + + UatModel *rsa_keys_model_; + UatModel *pkcs11_libs_model_; + + gboolean verifyKey(const char *uri, const char *password, gboolean *need_password, QString &error); + void addKey(const QString &uri, const QString &password); + +private slots: + void keyCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); + void on_addFileButton_clicked(); + void on_addItemButton_clicked(); + void on_deleteItemButton_clicked(); + void libCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); + void on_addLibraryButton_clicked(); + void on_deleteLibraryButton_clicked(); +#endif /* HAVE_LIBGNUTLS */ +}; + +#endif /* RSA_KEYS_FRAME_H */ diff --git a/ui/qt/rsa_keys_frame.ui b/ui/qt/rsa_keys_frame.ui new file mode 100644 index 00000000..2f74bec4 --- /dev/null +++ b/ui/qt/rsa_keys_frame.ui @@ -0,0 +1,117 @@ + + + RsaKeysFrame + + + + 0 + 0 + 400 + 300 + + + + + + + RSA Keys + + + + + + RSA private keys are loaded from a file or PKCS #11 token. + + + + + + + + + + + + Add new keyfile… + + + + + + + Add new token… + + + + + + + false + + + Remove key + + + + + + + Qt::Horizontal + + + + + + + + + PKCS #11 provider libraries. + + + + + + + + 16777215 + 54 + + + + + + + + + + Add new provider… + + + + + + + false + + + Remove provider + + + + + + + Qt::Horizontal + + + + + + + + + + + + + diff --git a/ui/qt/rtp_analysis_dialog.cpp b/ui/qt/rtp_analysis_dialog.cpp new file mode 100644 index 00000000..d4eafd6c --- /dev/null +++ b/ui/qt/rtp_analysis_dialog.cpp @@ -0,0 +1,1191 @@ +/* rtp_analysis_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "rtp_analysis_dialog.h" +#include + +#include "file.h" +#include "frame_tvbuff.h" + +#include "epan/epan_dissect.h" +#include +#include "epan/rtp_pt.h" + +#include "epan/dfilter/dfilter.h" + +#include "epan/dissectors/packet-rtp.h" + +#include + +#include "ui/help_url.h" +#include "ui/simple_dialog.h" +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "rtp_player_dialog.h" +#include +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +/* + * @file RTP stream analysis dialog + * + * Displays forward and reverse RTP streams and graphs each stream + */ + +// To do: +// - Progress bar for tapping and saving. +// - Add a refresh button and/or action. +// - Fixup output file names. +// - Add a graph title and legend when saving? + +enum { + packet_col_, + sequence_col_, + delta_col_, + jitter_col_, + skew_col_, + bandwidth_col_, + marker_col_, + status_col_ +}; + +static const QRgb color_cn_ = 0xbfbfff; +static const QRgb color_rtp_warn_ = 0xffdbbf; +static const QRgb color_pt_event_ = 0xefffff; + +enum { rtp_analysis_type_ = 1000 }; +class RtpAnalysisTreeWidgetItem : public QTreeWidgetItem +{ +public: + RtpAnalysisTreeWidgetItem(QTreeWidget *tree, tap_rtp_stat_t *statinfo, packet_info *pinfo, const struct _rtp_info *rtpinfo) : + QTreeWidgetItem(tree, rtp_analysis_type_) + { + frame_num_ = pinfo->num; + sequence_num_ = rtpinfo->info_seq_num; + pkt_len_ = pinfo->fd->pkt_len; + flags_ = statinfo->flags; + if (flags_ & STAT_FLAG_FIRST) { + delta_ = 0.0; + jitter_ = 0.0; + skew_ = 0.0; + } else { + delta_ = statinfo->delta; + jitter_ = statinfo->jitter; + skew_ = statinfo->skew; + } + bandwidth_ = statinfo->bandwidth; + marker_ = rtpinfo->info_marker_set ? true : false; + ok_ = false; + + QColor bg_color = QColor(); + QString status; + + if (statinfo->pt == PT_CN) { + status = "Comfort noise (PT=13, RFC 3389)"; + bg_color = color_cn_; + } else if (statinfo->pt == PT_CN_OLD) { + status = "Comfort noise (PT=19, reserved)"; + bg_color = color_cn_; + } else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) { + status = "Wrong sequence number"; + bg_color = ColorUtils::expert_color_error; + } else if (statinfo->flags & STAT_FLAG_DUP_PKT) { + status = "Suspected duplicate (MAC address) only delta time calculated"; + bg_color = color_rtp_warn_; + } else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) { + status = QString("Payload changed to PT=%1").arg(statinfo->pt); + if (statinfo->flags & STAT_FLAG_PT_T_EVENT) { + status.append(" telephone/event"); + } + bg_color = color_rtp_warn_; + } else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) { + status = "Incorrect timestamp"; + /* color = COLOR_WARNING; */ + bg_color = color_rtp_warn_; + } else if ((statinfo->flags & STAT_FLAG_PT_CHANGE) + && !(statinfo->flags & STAT_FLAG_FIRST) + && !(statinfo->flags & STAT_FLAG_PT_CN) + && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN) + && !(statinfo->flags & STAT_FLAG_MARKER)) { + status = "Marker missing?"; + bg_color = color_rtp_warn_; + } else if (statinfo->flags & STAT_FLAG_PT_T_EVENT) { + status = QString("PT=%1 telephone/event").arg(statinfo->pt); + /* XXX add color? */ + bg_color = color_pt_event_; + } else { + if (statinfo->flags & STAT_FLAG_MARKER) { + bg_color = color_rtp_warn_; + } + } + + if (status.isEmpty()) { + ok_ = true; + status = UTF8_CHECK_MARK; + } + + setText(packet_col_, QString::number(frame_num_)); + setText(sequence_col_, QString::number(sequence_num_)); + setText(delta_col_, QString::number(delta_, 'f', prefs.gui_decimal_places3)); + setText(jitter_col_, QString::number(jitter_, 'f', prefs.gui_decimal_places3)); + setText(skew_col_, QString::number(skew_, 'f', prefs.gui_decimal_places3)); + setText(bandwidth_col_, QString::number(bandwidth_, 'f', prefs.gui_decimal_places1)); + if (marker_) { + setText(marker_col_, UTF8_BULLET); + } + setText(status_col_, status); + + setTextAlignment(packet_col_, Qt::AlignRight); + setTextAlignment(sequence_col_, Qt::AlignRight); + setTextAlignment(delta_col_, Qt::AlignRight); + setTextAlignment(jitter_col_, Qt::AlignRight); + setTextAlignment(skew_col_, Qt::AlignRight); + setTextAlignment(bandwidth_col_, Qt::AlignRight); + setTextAlignment(marker_col_, Qt::AlignCenter); + + if (bg_color.isValid()) { + for (int col = 0; col < columnCount(); col++) { + setBackground(col, bg_color); + setForeground(col, ColorUtils::expert_color_foreground); + } + } + } + + uint32_t frameNum() { return frame_num_; } + bool frameStatus() { return ok_; } + + QList rowData() { + QString marker_str; + QString status_str = ok_ ? "OK" : text(status_col_); + + if (marker_) marker_str = "SET"; + + return QList() + << frame_num_ << sequence_num_ << delta_ << jitter_ << skew_ << bandwidth_ + << marker_str << status_str; + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != rtp_analysis_type_) return QTreeWidgetItem::operator< (other); + const RtpAnalysisTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case (packet_col_): + return frame_num_ < other_row->frame_num_; + break; + case (sequence_col_): + return sequence_num_ < other_row->sequence_num_; + break; + case (delta_col_): + return delta_ < other_row->delta_; + break; + case (jitter_col_): + return jitter_ < other_row->jitter_; + break; + case (skew_col_): + return skew_ < other_row->skew_; + break; + case (bandwidth_col_): + return bandwidth_ < other_row->bandwidth_; + break; + default: + break; + } + + // Fall back to string comparison + return QTreeWidgetItem::operator <(other); + } +private: + uint32_t frame_num_; + uint32_t sequence_num_; + uint32_t pkt_len_; + uint32_t flags_; + double delta_; + double jitter_; + double skew_; + double bandwidth_; + bool marker_; + bool ok_; +}; + +enum { + fwd_jitter_graph_, + fwd_diff_graph_, + fwd_delta_graph_, + rev_jitter_graph_, + rev_diff_graph_, + rev_delta_graph_, + num_graphs_ +}; + +RtpAnalysisDialog *RtpAnalysisDialog::pinstance_{nullptr}; +std::mutex RtpAnalysisDialog::init_mutex_; +std::mutex RtpAnalysisDialog::run_mutex_; + +RtpAnalysisDialog *RtpAnalysisDialog::openRtpAnalysisDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list) +{ + std::lock_guard lock(init_mutex_); + if (pinstance_ == nullptr) + { + pinstance_ = new RtpAnalysisDialog(parent, cf); + connect(pinstance_, SIGNAL(goToPacket(int)), + packet_list, SLOT(goToPacket(int))); + } + return pinstance_; +} + +RtpAnalysisDialog::RtpAnalysisDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::RtpAnalysisDialog), + tab_seq(0) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 4 / 5); + setWindowSubtitle(tr("RTP Stream Analysis")); + // Used when tab contains IPs + //ui->tabWidget->setStyleSheet("QTabBar::tab { height: 7ex; }"); + ui->tabWidget->tabBar()->setTabsClosable(true); + + ui->progressFrame->hide(); + + stream_ctx_menu_.addAction(ui->actionGoToPacket); + stream_ctx_menu_.addAction(ui->actionNextProblem); + set_action_shortcuts_visible_in_context_menu(stream_ctx_menu_.actions()); + + connect(ui->streamGraph, SIGNAL(mousePress(QMouseEvent*)), + this, SLOT(graphClicked(QMouseEvent*))); + + graph_ctx_menu_.addAction(ui->actionSaveGraph); + + ui->streamGraph->xAxis->setLabel("Arrival Time"); + ui->streamGraph->yAxis->setLabel("Value (ms)"); + + QPushButton *prepare_button = ui->buttonBox->addButton(ui->actionPrepareButton->text(), QDialogButtonBox::ActionRole); + prepare_button->setToolTip(ui->actionPrepareButton->toolTip()); + prepare_button->setMenu(ui->menuPrepareFilter); + + player_button_ = RtpPlayerDialog::addPlayerButton(ui->buttonBox, this); + + QPushButton *export_btn = ui->buttonBox->addButton(ui->actionExportButton->text(), QDialogButtonBox::ActionRole); + export_btn->setToolTip(ui->actionExportButton->toolTip()); + + QMenu *save_menu = new QMenu(export_btn); + save_menu->addAction(ui->actionSaveOneCsv); + save_menu->addAction(ui->actionSaveAllCsv); + save_menu->addSeparator(); + save_menu->addAction(ui->actionSaveGraph); + export_btn->setMenu(save_menu); + + connect(ui->tabWidget, SIGNAL(currentChanged(int)), + this, SLOT(updateWidgets())); + connect(ui->tabWidget->tabBar(), SIGNAL(tabCloseRequested(int)), + this, SLOT(closeTab(int))); + connect(this, SIGNAL(updateFilter(QString, bool)), + &parent, SLOT(filterPackets(QString, bool))); + connect(this, SIGNAL(rtpPlayerDialogReplaceRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogReplaceRtpStreams(QVector))); + connect(this, SIGNAL(rtpPlayerDialogAddRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogAddRtpStreams(QVector))); + connect(this, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector))); + + updateWidgets(); + + updateStatistics(); +} + +RtpAnalysisDialog::~RtpAnalysisDialog() +{ + std::lock_guard lock(init_mutex_); + if (pinstance_ != nullptr) { + delete ui; + for(int i=0; itime_vals; + delete tab_info->jitter_vals; + delete tab_info->diff_vals; + delete tab_info->delta_vals; + delete tab_info->tab_name; + // tab_info->tree_widget was deleted by ui + // tab_info->statistics_label was deleted by ui + rtpstream_info_free_data(&tab_info->stream); +} + +int RtpAnalysisDialog::addTabUI(tab_info_t *new_tab) +{ + int new_tab_no; + rtpstream_info_calc_t s_calc; + rtpstream_info_calculate(&new_tab->stream, &s_calc); + new_tab->tab_name = new QString(QString("%1:%2 " UTF8_RIGHTWARDS_ARROW "\n%3:%4\n(%5)") + .arg(s_calc.src_addr_str) + .arg(s_calc.src_port) + .arg(s_calc.dst_addr_str) + .arg(s_calc.dst_port) + .arg(int_to_qstring(s_calc.ssrc, 8, 16))); + rtpstream_info_calc_free(&s_calc); + + QWidget *tab = new QWidget(); + tab->setProperty("tab_data", QVariant::fromValue((void *)new_tab)); + QHBoxLayout *horizontalLayout = new QHBoxLayout(tab); + QVBoxLayout *verticalLayout = new QVBoxLayout(); + new_tab->statistics_label = new QLabel(); + //new_tab->statistics_label->setStyleSheet("QLabel { color : blue; }"); + new_tab->statistics_label->setTextFormat(Qt::RichText); + new_tab->statistics_label->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse); + + verticalLayout->addWidget(new_tab->statistics_label); + + QSpacerItem *verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayout->addItem(verticalSpacer); + + horizontalLayout->addLayout(verticalLayout); + + new_tab->tree_widget = new QTreeWidget(); + new_tab->tree_widget->setRootIsDecorated(false); + new_tab->tree_widget->setUniformRowHeights(true); + new_tab->tree_widget->setItemsExpandable(false); + new_tab->tree_widget->setSortingEnabled(true); + new_tab->tree_widget->setExpandsOnDoubleClick(false); + + new_tab->tree_widget->installEventFilter(this); + new_tab->tree_widget->setContextMenuPolicy(Qt::CustomContextMenu); + new_tab->tree_widget->header()->setSortIndicator(0, Qt::AscendingOrder); + connect(new_tab->tree_widget, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showStreamMenu(QPoint))); + connect(new_tab->tree_widget, SIGNAL(itemSelectionChanged()), + this, SLOT(updateWidgets())); + + QTreeWidgetItem *ti = new_tab->tree_widget->headerItem(); + ti->setText(packet_col_, tr("Packet")); + ti->setText(sequence_col_, tr("Sequence")); + ti->setText(delta_col_, tr("Delta (ms)")); + ti->setText(jitter_col_, tr("Jitter (ms)")); + ti->setText(skew_col_, tr("Skew")); + ti->setText(bandwidth_col_, tr("Bandwidth")); + ti->setText(marker_col_, tr("Marker")); + ti->setText(status_col_, tr("Status")); + + QColor color = ColorUtils::graphColor(tab_seq++); + ui->tabWidget->setUpdatesEnabled(false); + horizontalLayout->addWidget(new_tab->tree_widget); + new_tab_no = ui->tabWidget->count() - 1; + // Used when tab contains IPs + //ui->tabWidget->insertTab(new_tab_no, tab, *new_tab->tab_name); + ui->tabWidget->insertTab(new_tab_no, tab, QString(tr("Stream %1")).arg(tab_seq - 1)); + ui->tabWidget->tabBar()->setTabTextColor(new_tab_no, color); + ui->tabWidget->tabBar()->setTabToolTip(new_tab_no, *new_tab->tab_name); + ui->tabWidget->setUpdatesEnabled(true); + + QPen pen = QPen(color); + QCPScatterStyle jitter_shape; + QCPScatterStyle diff_shape; + QCPScatterStyle delta_shape; + jitter_shape.setShape(QCPScatterStyle::ssCircle); + //jitter_shape.setSize(5); + diff_shape.setShape(QCPScatterStyle::ssCross); + //diff_shape.setSize(5); + delta_shape.setShape(QCPScatterStyle::ssTriangle); + //delta_shape.setSize(5); + + new_tab->jitter_graph = ui->streamGraph->addGraph(); + new_tab->diff_graph = ui->streamGraph->addGraph(); + new_tab->delta_graph = ui->streamGraph->addGraph(); + new_tab->jitter_graph->setPen(pen); + new_tab->diff_graph->setPen(pen); + new_tab->delta_graph->setPen(pen); + new_tab->jitter_graph->setScatterStyle(jitter_shape); + new_tab->diff_graph->setScatterStyle(diff_shape); + new_tab->delta_graph->setScatterStyle(delta_shape); + + new_tab->graphHorizontalLayout = new QHBoxLayout(); + + new_tab->stream_checkbox = new QCheckBox(tr("Stream %1").arg(tab_seq - 1), ui->graphTab); + new_tab->stream_checkbox->setChecked(true); + new_tab->stream_checkbox->setIcon(StockIcon::colorIcon(color.rgb(), QPalette::Text)); + new_tab->graphHorizontalLayout->addWidget(new_tab->stream_checkbox); + new_tab->graphHorizontalLayout->addItem(new QSpacerItem(10, 5, QSizePolicy::Expanding, QSizePolicy::Minimum)); + connect(new_tab->stream_checkbox, SIGNAL(stateChanged(int)), + this, SLOT(rowCheckboxChanged(int))); + + new_tab->jitter_checkbox = new QCheckBox(tr("Stream %1 Jitter").arg(tab_seq - 1), ui->graphTab); + new_tab->jitter_checkbox->setChecked(true); + new_tab->jitter_checkbox->setIcon(StockIcon::colorIconCircle(color.rgb(), QPalette::Text)); + new_tab->graphHorizontalLayout->addWidget(new_tab->jitter_checkbox); + new_tab->graphHorizontalLayout->addItem(new QSpacerItem(10, 5, QSizePolicy::Expanding, QSizePolicy::Minimum)); + connect(new_tab->jitter_checkbox, SIGNAL(stateChanged(int)), + this, SLOT(singleCheckboxChanged(int))); + + new_tab->diff_checkbox = new QCheckBox(tr("Stream %1 Difference").arg(tab_seq - 1), ui->graphTab); + new_tab->diff_checkbox->setChecked(true); + new_tab->diff_checkbox->setIcon(StockIcon::colorIconCross(color.rgb(), QPalette::Text)); + new_tab->graphHorizontalLayout->addWidget(new_tab->diff_checkbox); + new_tab->graphHorizontalLayout->addItem(new QSpacerItem(10, 5, QSizePolicy::Expanding, QSizePolicy::Minimum)); + connect(new_tab->diff_checkbox, SIGNAL(stateChanged(int)), + this, SLOT(singleCheckboxChanged(int))); + + new_tab->delta_checkbox = new QCheckBox(tr("Stream %1 Delta").arg(tab_seq - 1), ui->graphTab); + new_tab->delta_checkbox->setChecked(true); + new_tab->delta_checkbox->setIcon(StockIcon::colorIconTriangle(color.rgb(), QPalette::Text)); + new_tab->graphHorizontalLayout->addWidget(new_tab->delta_checkbox); + new_tab->graphHorizontalLayout->addItem(new QSpacerItem(10, 5, QSizePolicy::Expanding, QSizePolicy::Minimum)); + connect(new_tab->delta_checkbox, SIGNAL(stateChanged(int)), + this, SLOT(singleCheckboxChanged(int))); + + new_tab->graphHorizontalLayout->setStretch(6, 1); + + ui->layout->addLayout(new_tab->graphHorizontalLayout); + + return new_tab_no; +} + +// Handles all row checkBoxes +void RtpAnalysisDialog::rowCheckboxChanged(int checked) +{ + QObject *obj = sender(); + + // Find correct tab data + for(int i=0; istream_checkbox) { + // Set new state for all checkboxes on row + Qt::CheckState new_state; + + if (checked) { + new_state = Qt::Checked; + } else { + new_state = Qt::Unchecked; + } + tab->jitter_checkbox->setCheckState(new_state); + tab->diff_checkbox->setCheckState(new_state); + tab->delta_checkbox->setCheckState(new_state); + break; + } + } +} + +// Handles all single CheckBoxes +void RtpAnalysisDialog::singleCheckboxChanged(int checked) +{ + QObject *obj = sender(); + + // Find correct tab data + for(int i=0; ijitter_checkbox) { + tab->jitter_graph->setVisible(checked); + updateGraph(); + break; + } else if (obj == tab->diff_checkbox) { + tab->diff_graph->setVisible(checked); + updateGraph(); + break; + } else if (obj == tab->delta_checkbox) { + tab->delta_graph->setVisible(checked); + updateGraph(); + break; + } + } +} + +void RtpAnalysisDialog::updateWidgets() +{ + bool enable_tab = false; + bool enable_nav = false; + QString hint = err_str_; + + if ((!file_closed_) && + (tabs_.count() > 0)) { + enable_tab = true; + } + + if ((!file_closed_) && + (tabs_.count() > 0) && + (ui->tabWidget->currentIndex() < (ui->tabWidget->count()-1))) { + enable_nav = true; + } + + ui->actionGoToPacket->setEnabled(enable_nav); + ui->actionNextProblem->setEnabled(enable_nav); + + if (enable_nav) { + hint.append(tr(" %1 streams, ").arg(tabs_.count() - 1)); + hint.append(tr(" G: Go to packet, N: Next problem packet")); + } + + ui->actionExportButton->setEnabled(enable_tab); + ui->actionSaveOneCsv->setEnabled(enable_nav); + ui->actionSaveAllCsv->setEnabled(enable_tab); + ui->actionSaveGraph->setEnabled(enable_tab); + + ui->actionPrepareFilterOne->setEnabled(enable_nav); + ui->actionPrepareFilterAll->setEnabled(enable_tab); + +#if defined(QT_MULTIMEDIA_LIB) + player_button_->setEnabled(enable_tab); +#endif + + ui->tabWidget->setEnabled(enable_tab); + hint.prepend(""); + hint.append(""); + ui->hintLabel->setText(hint); + + WiresharkDialog::updateWidgets(); +} + +void RtpAnalysisDialog::on_actionGoToPacket_triggered() +{ + tab_info_t *tab_data = getTabInfoForCurrentTab(); + if (!tab_data) return; + + QTreeWidget *cur_tree = tab_data->tree_widget; + if (!cur_tree || cur_tree->selectedItems().length() < 1) return; + + QTreeWidgetItem *ti = cur_tree->selectedItems()[0]; + if (ti->type() != rtp_analysis_type_) return; + + RtpAnalysisTreeWidgetItem *ra_ti = dynamic_cast((RtpAnalysisTreeWidgetItem *)ti); + emit goToPacket(ra_ti->frameNum()); +} + +void RtpAnalysisDialog::on_actionNextProblem_triggered() +{ + tab_info_t *tab_data = getTabInfoForCurrentTab(); + if (!tab_data) return; + + QTreeWidget *cur_tree = tab_data->tree_widget; + if (!cur_tree || cur_tree->topLevelItemCount() < 2) return; + + // Choose convenience over correctness. + if (cur_tree->selectedItems().length() < 1) { + cur_tree->setCurrentItem(cur_tree->topLevelItem(0)); + } + + QTreeWidgetItem *sel_ti = cur_tree->selectedItems()[0]; + if (sel_ti->type() != rtp_analysis_type_) return; + QTreeWidgetItem *test_ti = cur_tree->itemBelow(sel_ti); + if (!test_ti) test_ti = cur_tree->topLevelItem(0); + while (test_ti != sel_ti) { + RtpAnalysisTreeWidgetItem *ra_ti = dynamic_cast((RtpAnalysisTreeWidgetItem *)test_ti); + if (!ra_ti->frameStatus()) { + cur_tree->setCurrentItem(ra_ti); + break; + } + + test_ti = cur_tree->itemBelow(test_ti); + if (!test_ti) test_ti = cur_tree->topLevelItem(0); + } +} + +void RtpAnalysisDialog::on_actionSaveOneCsv_triggered() +{ + saveCsv(dir_one_); +} + +void RtpAnalysisDialog::on_actionSaveAllCsv_triggered() +{ + saveCsv(dir_all_); +} + +void RtpAnalysisDialog::on_actionSaveGraph_triggered() +{ + ui->tabWidget->setCurrentWidget(ui->graphTab); + + QString file_name, extension; + QDir path(mainApp->openDialogInitialDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + 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)"); + QString filter = QString("%1;;%2;;%3;;%4") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter); + + QString save_file = path.canonicalPath(); + if (!file_closed_) { + save_file += QString("/%1").arg(cap_file_.fileBaseName()); + } + file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Graph As…")), + save_file, filter, &extension); + + if (!file_name.isEmpty()) { + bool save_ok = false; + // https://www.qcustomplot.com/index.php/support/forum/63 +// ui->streamGraph->legend->setVisible(true); + if (extension.compare(pdf_filter) == 0) { + save_ok = ui->streamGraph->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = ui->streamGraph->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = ui->streamGraph->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = ui->streamGraph->saveJpg(file_name); + } +// ui->streamGraph->legend->setVisible(false); + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } +} + +void RtpAnalysisDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_TELEPHONY_RTP_ANALYSIS_DIALOG); +} + +void RtpAnalysisDialog::tapReset(void *tapinfo_ptr) +{ + RtpAnalysisDialog *rtp_analysis_dialog = dynamic_cast((RtpAnalysisDialog*)tapinfo_ptr); + if (!rtp_analysis_dialog) return; + + rtp_analysis_dialog->resetStatistics(); +} + +tap_packet_status RtpAnalysisDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr, tap_flags_t) +{ + RtpAnalysisDialog *rtp_analysis_dialog = dynamic_cast((RtpAnalysisDialog*)tapinfo_ptr); + if (!rtp_analysis_dialog) return TAP_PACKET_DONT_REDRAW; + + const struct _rtp_info *rtpinfo = (const struct _rtp_info *)rtpinfo_ptr; + if (!rtpinfo) return TAP_PACKET_DONT_REDRAW; + + /* we ignore packets that are not displayed */ + if (pinfo->fd->passed_dfilter == 0) + return TAP_PACKET_DONT_REDRAW; + /* also ignore RTP Version != 2 */ + else if (rtpinfo->info_version != 2) + return TAP_PACKET_DONT_REDRAW; + /* is it the forward direction? */ + else { + // Search tab in hash key, if there are multiple tabs with same hash + QList tabs = rtp_analysis_dialog->tab_hash_.values(pinfo_rtp_info_to_hash(pinfo, rtpinfo)); + for (int i = 0; i < tabs.size(); i++) { + tab_info_t *tab = tabs.at(i); + if (rtpstream_id_equal_pinfo_rtp_info(&tab->stream.id, pinfo, rtpinfo)) { + rtp_analysis_dialog->addPacket(tab, pinfo, rtpinfo); + break; + } + } + } + + return TAP_PACKET_DONT_REDRAW; +} + +void RtpAnalysisDialog::tapDraw(void *tapinfo_ptr) +{ + RtpAnalysisDialog *rtp_analysis_dialog = dynamic_cast((RtpAnalysisDialog*)tapinfo_ptr); + if (!rtp_analysis_dialog) return; + + rtp_analysis_dialog->updateStatistics(); +} + +void RtpAnalysisDialog::resetStatistics() +{ + for(int i=0; istream.rtp_stats, 0, sizeof(tab->stream.rtp_stats)); + + tab->stream.rtp_stats.first_packet = true; + tab->stream.rtp_stats.reg_pt = PT_UNDEFINED; + tab->time_vals->clear(); + tab->jitter_vals->clear(); + tab->diff_vals->clear(); + tab->delta_vals->clear(); + tab->tree_widget->clear(); + } + + for (int i = 0; i < ui->streamGraph->graphCount(); i++) { + ui->streamGraph->graph(i)->data()->clear(); + } +} + +void RtpAnalysisDialog::addPacket(tab_info_t *tab, packet_info *pinfo, const _rtp_info *rtpinfo) +{ + rtpstream_info_analyse_process(&tab->stream, pinfo, rtpinfo); + new RtpAnalysisTreeWidgetItem(tab->tree_widget, &tab->stream.rtp_stats, pinfo, rtpinfo); + tab->time_vals->append(tab->stream.rtp_stats.time / 1000); + tab->jitter_vals->append(tab->stream.rtp_stats.jitter); + tab->diff_vals->append(tab->stream.rtp_stats.diff); + tab->delta_vals->append(tab->stream.rtp_stats.delta); +} + +void RtpAnalysisDialog::updateStatistics() +{ + for(int i=0; istream; + rtpstream_info_calc_t s_calc; + rtpstream_info_calculate(stream, &s_calc); + + QString stats_tables = "\n"; + stats_tables += "

Stream

\n"; + stats_tables += QString("

%1:%2 " UTF8_RIGHTWARDS_ARROW) + .arg(s_calc.src_addr_str) + .arg(s_calc.src_port); + stats_tables += QString("
%1:%2

\n") + .arg(s_calc.dst_addr_str) + .arg(s_calc.dst_port); + stats_tables += "

\n"; + stats_tables += QString("") + .arg(int_to_qstring(s_calc.ssrc, 8, 16)); + stats_tables += QString("") + .arg(s_calc.max_delta, 0, 'f', prefs.gui_decimal_places3) + .arg(s_calc.last_packet_num); + stats_tables += QString("") + .arg(s_calc.max_jitter, 0, 'f', prefs.gui_decimal_places3); + stats_tables += QString("") + .arg(s_calc.mean_jitter, 0, 'f', prefs.gui_decimal_places3); + stats_tables += QString("") + .arg(s_calc.max_skew, 0, 'f', prefs.gui_decimal_places3); + stats_tables += QString("") + .arg(s_calc.total_nr); + stats_tables += QString("") + .arg(s_calc.packet_expected); + stats_tables += QString("") + .arg(s_calc.lost_num).arg(s_calc.lost_perc, 0, 'f', prefs.gui_decimal_places1); + stats_tables += QString("") + .arg(s_calc.sequence_err); + stats_tables += QString("") + .arg(s_calc.start_time_ms, 0, 'f', 6) + .arg(s_calc.first_packet_num); + stats_tables += QString("") + .arg(s_calc.duration_ms, 0, 'f', prefs.gui_decimal_places1); + stats_tables += QString("") + .arg(s_calc.clock_drift_ms, 0, 'f', 0); + stats_tables += QString("") // XXX Terminology? + .arg(s_calc.freq_drift_hz, 0, 'f', 0).arg(s_calc.freq_drift_perc, 0, 'f', 2); + rtpstream_info_calc_free(&s_calc); + stats_tables += "
SSRC%1
Max Delta%1 ms @ %2
Max Jitter%1 ms
Mean Jitter%1 ms
Max Skew%1 ms
RTP Packets%1
Expected%1
Lost%1 (%2 %)
Seq Errs%1
Start at%1 s @ %2
Duration%1 s
Clock Drift%1 ms
Freq Drift%1 Hz (%2 %)

\n"; + + tabs_[i]->statistics_label->setText(stats_tables); + + for (int col = 0; col < tabs_[i]->tree_widget->columnCount() - 1; col++) { + tabs_[i]->tree_widget->resizeColumnToContents(col); + } + + tabs_[i]->jitter_graph->setData(*tabs_[i]->time_vals, *tabs_[i]->jitter_vals); + tabs_[i]->diff_graph->setData(*tabs_[i]->time_vals, *tabs_[i]->diff_vals); + tabs_[i]->delta_graph->setData(*tabs_[i]->time_vals, *tabs_[i]->delta_vals); + } + + updateGraph(); + + updateWidgets(); +} + +void RtpAnalysisDialog::updateGraph() +{ + for (int i = 0; i < ui->streamGraph->graphCount(); i++) { + if (ui->streamGraph->graph(i)->visible()) { + ui->streamGraph->graph(i)->rescaleAxes(i > 0); + } + } + ui->streamGraph->replot(); +} + +QVectorRtpAnalysisDialog::getSelectedRtpIds() +{ + QVector stream_ids; + for(int i=0; i < tabs_.count(); i++) { + stream_ids << &(tabs_[i]->stream.id); + } + + return stream_ids; +} + +void RtpAnalysisDialog::rtpPlayerReplace() +{ + if (tabs_.count() < 1) return; + + emit rtpPlayerDialogReplaceRtpStreams(getSelectedRtpIds()); +} + +void RtpAnalysisDialog::rtpPlayerAdd() +{ + if (tabs_.count() < 1) return; + + emit rtpPlayerDialogAddRtpStreams(getSelectedRtpIds()); +} + +void RtpAnalysisDialog::rtpPlayerRemove() +{ + if (tabs_.count() < 1) return; + + emit rtpPlayerDialogRemoveRtpStreams(getSelectedRtpIds()); +} + +void RtpAnalysisDialog::saveCsvHeader(QFile *save_file, QTreeWidget *tree) +{ + QList row_data; + QStringList values; + + for (int col = 0; col < tree->columnCount(); col++) { + row_data << tree->headerItem()->text(col); + } + foreach (QVariant v, row_data) { + if (!v.isValid()) { + values << "\"\""; + } else if (v.userType() == QMetaType::QString) { + values << QString("\"%1\"").arg(v.toString()); + } else { + values << v.toString(); + } + } + save_file->write(values.join(",").toUtf8()); + save_file->write("\n"); +} + +void RtpAnalysisDialog::saveCsvData(QFile *save_file, QTreeWidget *tree) +{ + for (int row = 0; row < tree->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = tree->topLevelItem(row); + if (ti->type() != rtp_analysis_type_) continue; + RtpAnalysisTreeWidgetItem *ra_ti = dynamic_cast((RtpAnalysisTreeWidgetItem *)ti); + QStringList values; + foreach (QVariant v, ra_ti->rowData()) { + if (!v.isValid()) { + values << "\"\""; + } else if (v.userType() == QMetaType::QString) { + values << QString("\"%1\"").arg(v.toString()); + } else { + values << v.toString(); + } + } + save_file->write(values.join(",").toUtf8()); + save_file->write("\n"); + } +} + +// XXX The GTK+ UI saves the length and timestamp. +void RtpAnalysisDialog::saveCsv(RtpAnalysisDialog::StreamDirection direction) +{ + QString caption; + + switch (direction) { + case dir_one_: + caption = tr("Save one stream CSV"); + break; + case dir_all_: + default: + caption = tr("Save all stream's CSV"); + break; + } + + QString file_path = WiresharkFileDialog::getSaveFileName( + this, caption, mainApp->openDialogInitialDir().absoluteFilePath("RTP Packet Data.csv"), + tr("Comma-separated values (*.csv)")); + + if (file_path.isEmpty()) return; + + QFile save_file(file_path); + save_file.open(QFile::WriteOnly); + + switch (direction) { + case dir_one_: + { + tab_info_t *tab_data = getTabInfoForCurrentTab(); + if (tab_data) { + + saveCsvHeader(&save_file, tab_data->tree_widget); + + QString n = QString(*tab_data->tab_name); + n.replace("\n"," "); + save_file.write("\""); + save_file.write(n.toUtf8()); + save_file.write("\"\n"); + saveCsvData(&save_file, tab_data->tree_widget); + } + } + break; + case dir_all_: + default: + if (tabs_.count() > 0) { + saveCsvHeader(&save_file, tabs_[0]->tree_widget); + } + + for(int i=0; itab_name); + n.replace("\n"," "); + save_file.write("\""); + save_file.write(n.toUtf8()); + save_file.write("\"\n"); + saveCsvData(&save_file, tabs_[i]->tree_widget); + save_file.write("\n"); + } + break; + } +} + +bool RtpAnalysisDialog::eventFilter(QObject *, QEvent *event) +{ + if (event->type() != QEvent::KeyPress) return false; + + QKeyEvent *kevt = static_cast(event); + + switch(kevt->key()) { + case Qt::Key_G: + on_actionGoToPacket_triggered(); + return true; + case Qt::Key_N: + on_actionNextProblem_triggered(); + return true; + default: + break; + } + return false; +} + +void RtpAnalysisDialog::graphClicked(QMouseEvent *event) +{ + updateWidgets(); + if (event->button() == Qt::RightButton) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + graph_ctx_menu_.popup(event->globalPosition().toPoint()); +#else + graph_ctx_menu_.popup(event->globalPos()); +#endif + } +} + +void RtpAnalysisDialog::clearLayout(QLayout *layout) +{ + if (layout) { + QLayoutItem *item; + + //the key point here is that the layout items are stored inside the layout in a stack + while((item = layout->takeAt(0)) != 0) { + if (item->widget()) { + layout->removeWidget(item->widget()); + delete item->widget(); + } + + delete item; + } + } +} + +void RtpAnalysisDialog::closeTab(int index) +{ + // Do not close last tab with graph + if (index != tabs_.count()) { + QWidget *remove_tab = qobject_cast(ui->tabWidget->widget(index)); + tab_info_t *tab = tabs_[index]; + tab_hash_.remove(rtpstream_to_hash(&tab->stream), tab); + tabs_.remove(index); + ui->tabWidget->removeTab(index); + ui->streamGraph->removeGraph(tab->jitter_graph); + ui->streamGraph->removeGraph(tab->diff_graph); + ui->streamGraph->removeGraph(tab->delta_graph); + clearLayout(tab->graphHorizontalLayout); + delete remove_tab; + deleteTabInfo(tab); + g_free(tab); + + updateGraph(); + } +} + +void RtpAnalysisDialog::showStreamMenu(QPoint pos) +{ + tab_info_t *tab_data = getTabInfoForCurrentTab(); + if (!tab_data) return; + + QTreeWidget *cur_tree = tab_data->tree_widget; + if (!cur_tree) return; + + updateWidgets(); + stream_ctx_menu_.popup(cur_tree->viewport()->mapToGlobal(pos)); +} + +void RtpAnalysisDialog::replaceRtpStreams(QVector stream_ids) +{ + std::unique_lock lock(run_mutex_, std::try_to_lock); + if (lock.owns_lock()) { + // Delete existing tabs (from last to first) + if (tabs_.count() > 0) { + for(int i = static_cast(tabs_.count()); i>0; i--) { + closeTab(i-1); + } + } + addRtpStreamsPrivate(stream_ids); + } else { + ws_warning("replaceRtpStreams was called while other thread locked it. Current call is ignored, try it later."); + } +} + +void RtpAnalysisDialog::addRtpStreams(QVector stream_ids) +{ + std::unique_lock lock(run_mutex_, std::try_to_lock); + if (lock.owns_lock()) { + addRtpStreamsPrivate(stream_ids); + } else { + ws_warning("addRtpStreams was called while other thread locked it. Current call is ignored, try it later."); + } +} + +void RtpAnalysisDialog::addRtpStreamsPrivate(QVector stream_ids) +{ + int first_tab_no = -1; + + setUpdatesEnabled(false); + foreach(rtpstream_id_t *id, stream_ids) { + bool found = false; + + QList tabs = tab_hash_.values(rtpstream_id_to_hash(id)); + for (int i = 0; i < tabs.size(); i++) { + tab_info_t *tab = tabs.at(i); + if (rtpstream_id_equal(&tab->stream.id, id, RTPSTREAM_ID_EQUAL_SSRC)) { + found = true; + break; + } + } + + if (!found) { + int cur_tab_no; + + tab_info_t *new_tab = g_new0(tab_info_t, 1); + rtpstream_id_copy(id, &(new_tab->stream.id)); + new_tab->time_vals = new QVector(); + new_tab->jitter_vals = new QVector(); + new_tab->diff_vals = new QVector(); + new_tab->delta_vals = new QVector(); + tabs_ << new_tab; + cur_tab_no = addTabUI(new_tab); + tab_hash_.insert(rtpstream_id_to_hash(id), new_tab); + if (first_tab_no == -1) { + first_tab_no = cur_tab_no; + } + } + } + if (first_tab_no != -1) { + ui->tabWidget->setCurrentIndex(first_tab_no); + } + setUpdatesEnabled(true); + registerTapListener("rtp", this, NULL, 0, tapReset, tapPacket, tapDraw); + cap_file_.retapPackets(); + updateStatistics(); + removeTapListeners(); + + updateGraph(); +} + +void RtpAnalysisDialog::removeRtpStreams(QVector stream_ids) +{ + std::unique_lock lock(run_mutex_, std::try_to_lock); + if (lock.owns_lock()) { + setUpdatesEnabled(false); + foreach(rtpstream_id_t *id, stream_ids) { + QList tabs = tab_hash_.values(rtpstream_id_to_hash(id)); + for (int i = 0; i < tabs.size(); i++) { + tab_info_t *tab = tabs.at(i); + if (rtpstream_id_equal(&tab->stream.id, id, RTPSTREAM_ID_EQUAL_SSRC)) { + closeTab(static_cast(tabs_.indexOf(tab))); + } + } + } + setUpdatesEnabled(true); + + updateGraph(); + } else { + ws_warning("removeRtpStreams was called while other thread locked it. Current call is ignored, try it later."); + } +} + +tab_info_t *RtpAnalysisDialog::getTabInfoForCurrentTab() +{ + tab_info_t *tab_data; + + if (file_closed_) return NULL; + QWidget *cur_tab = qobject_cast(ui->tabWidget->currentWidget()); + if (!cur_tab) return NULL; + tab_data = static_cast(cur_tab->property("tab_data").value()); + + return tab_data; +} + +QToolButton *RtpAnalysisDialog::addAnalyzeButton(QDialogButtonBox *button_box, QDialog *dialog) +{ + if (!button_box) return NULL; + + QAction *ca; + QToolButton *analysis_button = new QToolButton(); + button_box->addButton(analysis_button, QDialogButtonBox::ActionRole); + analysis_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + analysis_button->setPopupMode(QToolButton::MenuButtonPopup); + + ca = new QAction(tr("&Analyze"), analysis_button); + ca->setToolTip(tr("Open the analysis window for the selected stream(s)")); + connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpAnalysisReplace())); + analysis_button->setDefaultAction(ca); + // Overrides text striping of shortcut undercode in QAction + analysis_button->setText(ca->text()); + + QMenu *button_menu = new QMenu(analysis_button); + button_menu->setToolTipsVisible(true); + ca = button_menu->addAction(tr("&Set List")); + ca->setToolTip(tr("Replace existing list in RTP Analysis Dialog with new one")); + connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpAnalysisReplace())); + ca = button_menu->addAction(tr("&Add to List")); + ca->setToolTip(tr("Add new set to existing list in RTP Analysis Dialog")); + connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpAnalysisAdd())); + ca = button_menu->addAction(tr("&Remove from List")); + ca->setToolTip(tr("Remove selected streams from list in RTP Analysis Dialog")); + connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpAnalysisRemove())); + analysis_button->setMenu(button_menu); + + return analysis_button; +} + +void RtpAnalysisDialog::on_actionPrepareFilterOne_triggered() +{ + if ((ui->tabWidget->currentIndex() < (ui->tabWidget->count()-1))) { + QVector ids; + ids << &(tabs_[ui->tabWidget->currentIndex()]->stream.id); + QString filter = make_filter_based_on_rtpstream_id(ids); + if (filter.length() > 0) { + emit updateFilter(filter); + } + } +} + +void RtpAnalysisDialog::on_actionPrepareFilterAll_triggered() +{ + QVectorids = getSelectedRtpIds(); + QString filter = make_filter_based_on_rtpstream_id(ids); + if (filter.length() > 0) { + emit updateFilter(filter); + } +} + diff --git a/ui/qt/rtp_analysis_dialog.h b/ui/qt/rtp_analysis_dialog.h new file mode 100644 index 00000000..a7fcfced --- /dev/null +++ b/ui/qt/rtp_analysis_dialog.h @@ -0,0 +1,174 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTP_ANALYSIS_DIALOG_H +#define RTP_ANALYSIS_DIALOG_H + +#include + +#include +#include + +#include "epan/address.h" + +#include "ui/rtp_stream.h" +#include "ui/tap-rtp-common.h" +#include "ui/tap-rtp-analysis.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "wireshark_dialog.h" + +namespace Ui { +class RtpAnalysisDialog; +} + +class QCPGraph; +class QTemporaryFile; +class QDialogButtonBox; + +typedef struct { + rtpstream_info_t stream; + QVector *time_vals; + QVector *jitter_vals; + QVector *diff_vals; + QVector *delta_vals; + QTreeWidget *tree_widget; + QLabel *statistics_label; + QString *tab_name; + QCPGraph *jitter_graph; + QCPGraph *diff_graph; + QCPGraph *delta_graph; + QHBoxLayout *graphHorizontalLayout; + QCheckBox *stream_checkbox; + QCheckBox *jitter_checkbox; + QCheckBox *diff_checkbox; + QCheckBox *delta_checkbox; +} tab_info_t; + +// Singleton by https://refactoring.guru/design-patterns/singleton/cpp/example#example-1 +class RtpAnalysisDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + /** + * Returns singleton + */ + static RtpAnalysisDialog *openRtpAnalysisDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list); + + /** + * Should not be clonnable and assignable + */ + RtpAnalysisDialog(RtpAnalysisDialog &other) = delete; + void operator=(const RtpAnalysisDialog &) = delete; + + /** + * @brief Common routine to add a "Analyze" button to a QDialogButtonBox. + * @param button_box Caller's QDialogButtonBox. + * @return The new "Analyze" button. + */ + static QToolButton *addAnalyzeButton(QDialogButtonBox *button_box, QDialog *dialog); + + /** Replace/Add/Remove an RTP streams to analyse. + * Requires array of rtpstream_id_t. + * + * @param stream_ids structs with rtpstream_id + */ + void replaceRtpStreams(QVector stream_ids); + void addRtpStreams(QVector stream_ids); + void removeRtpStreams(QVector stream_ids); + +signals: + void goToPacket(int packet_num); + void rtpPlayerDialogReplaceRtpStreams(QVector stream_ids); + void rtpPlayerDialogAddRtpStreams(QVector stream_ids); + void rtpPlayerDialogRemoveRtpStreams(QVector stream_ids); + void updateFilter(QString filter, bool force = false); + +public slots: + void rtpPlayerReplace(); + void rtpPlayerAdd(); + void rtpPlayerRemove(); + +protected slots: + virtual void updateWidgets(); + +protected: + explicit RtpAnalysisDialog(QWidget &parent, CaptureFile &cf); + ~RtpAnalysisDialog(); + +private slots: + void on_actionGoToPacket_triggered(); + void on_actionNextProblem_triggered(); + void on_actionSaveOneCsv_triggered(); + void on_actionSaveAllCsv_triggered(); + void on_actionSaveGraph_triggered(); + void on_buttonBox_helpRequested(); + void showStreamMenu(QPoint pos); + void graphClicked(QMouseEvent *event); + void closeTab(int index); + void rowCheckboxChanged(int checked); + void singleCheckboxChanged(int checked); + void on_actionPrepareFilterOne_triggered(); + void on_actionPrepareFilterAll_triggered(); + +private: + static RtpAnalysisDialog *pinstance_; + static std::mutex init_mutex_; + static std::mutex run_mutex_; + + Ui::RtpAnalysisDialog *ui; + enum StreamDirection { dir_all_, dir_one_ }; + int tab_seq; + + QVector tabs_; + QMultiHash tab_hash_; + + QToolButton *player_button_; + + // Graph data for QCustomPlot + QListgraphs_; + + QString err_str_; + + QMenu stream_ctx_menu_; + QMenu graph_ctx_menu_; + + // Tap callbacks + static void tapReset(void *tapinfo_ptr); + static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr, tap_flags_t flags); + static void tapDraw(void *tapinfo_ptr); + + void resetStatistics(); + void addPacket(tab_info_t *tab, packet_info *pinfo, const struct _rtp_info *rtpinfo); + void updateStatistics(); + void updateGraph(); + + void saveCsvHeader(QFile *save_file, QTreeWidget *tree); + void saveCsvData(QFile *save_file, QTreeWidget *tree); + void saveCsv(StreamDirection direction); + + bool eventFilter(QObject*, QEvent* event); + + QVectorgetSelectedRtpIds(); + int addTabUI(tab_info_t *new_tab); + tab_info_t *getTabInfoForCurrentTab(); + void deleteTabInfo(tab_info_t *tab_info); + void clearLayout(QLayout *layout); + void addRtpStreamsPrivate(QVector stream_ids); +}; + +#endif // RTP_ANALYSIS_DIALOG_H diff --git a/ui/qt/rtp_analysis_dialog.ui b/ui/qt/rtp_analysis_dialog.ui new file mode 100644 index 00000000..3e7e54d6 --- /dev/null +++ b/ui/qt/rtp_analysis_dialog.ui @@ -0,0 +1,246 @@ + + + RtpAnalysisDialog + + + + 0 + 0 + 650 + 475 + + + + Dialog + + + + + + + + 0 + + + + Graph + + + + + + + + + + 0 + 200 + + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 606 + 298 + + + + + + + + + + + + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + &Export + + + Open export menu + + + + + CSV + + + Save tables as CSV. + + + + + Current Tab Stream CSV + + + Save the table on the current tab as CSV. + + + + + All Tab Streams CSV + + + Save the table from all tabs as CSV. + + + + + Save Graph + + + Save the graph image. + + + + + Go to Packet + + + Select the corresponding packet in the packet list. + + + G + + + + + Next Problem Packet + + + Go to the next problem packet + + + N + + + + + Prepare &Filter + + + Prepare a filter matching the selected stream(s). + + + + + Prepare &Filter + + + true + + + + + + + &Current Tab + + + Prepare a filter matching current tab. + + + + + &All Tabs + + + Prepare a filter matching all tabs. + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+ + ProgressFrame + QFrame +
progress_frame.h
+ 1 +
+
+ + + + buttonBox + accepted() + RtpAnalysisDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RtpAnalysisDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/rtp_audio_stream.cpp b/ui/qt/rtp_audio_stream.cpp new file mode 100644 index 00000000..5ceb809f --- /dev/null +++ b/ui/qt/rtp_audio_stream.cpp @@ -0,0 +1,953 @@ +/* rtp_audio_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "rtp_audio_stream.h" + +#ifdef QT_MULTIMEDIA_LIB + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include +#include +#endif +#include +#include +#include +#include + +// To do: +// - Only allow one rtpstream_info_t per RtpAudioStream? + +static const spx_int16_t visual_sample_rate_ = 1000; + +RtpAudioStream::RtpAudioStream(QObject *parent, rtpstream_id_t *id, bool stereo_required) : + QObject(parent) + , first_packet_(true) + , decoders_hash_(rtp_decoder_hash_table_new()) + , global_start_rel_time_(0.0) + , start_abs_offset_(0.0) + , start_rel_time_(0.0) + , stop_rel_time_(0.0) + , stereo_required_(stereo_required) + , first_sample_rate_(0) + , audio_out_rate_(0) + , audio_requested_out_rate_(0) + , max_sample_val_(1) + , max_sample_val_used_(1) + , color_(0) + , jitter_buffer_size_(50) + , timing_mode_(RtpAudioStream::JitterBuffer) + , start_play_time_(0) + , audio_output_(NULL) +{ + rtpstream_id_copy(id, &id_); + memset(&rtpstream_, 0, sizeof(rtpstream_)); + rtpstream_id_copy(&id_, &rtpstream_.id); + + // Rates will be set later, we just init visual resampler + visual_resampler_ = speex_resampler_init(1, visual_sample_rate_, + visual_sample_rate_, SPEEX_RESAMPLER_QUALITY_MIN, NULL); + + try { + // RtpAudioFile is ready for writing Frames + audio_file_ = new RtpAudioFile(prefs.gui_rtp_player_use_disk1, prefs.gui_rtp_player_use_disk2); + } catch (...) { + speex_resampler_destroy(visual_resampler_); + rtpstream_info_free_data(&rtpstream_); + rtpstream_id_free(&id_); + throw -1; + } + + // RTP_STREAM_DEBUG("Writing to %s", tempname.toUtf8().constData()); +} + +RtpAudioStream::~RtpAudioStream() +{ + for (int i = 0; i < rtp_packets_.size(); i++) { + rtp_packet_t *rtp_packet = rtp_packets_[i]; + g_free(rtp_packet->info); + g_free(rtp_packet->payload_data); + g_free(rtp_packet); + } + g_hash_table_destroy(decoders_hash_); + speex_resampler_destroy(visual_resampler_); + rtpstream_info_free_data(&rtpstream_); + rtpstream_id_free(&id_); + if (audio_file_) delete audio_file_; + // temp_file_ is released by audio_output_ + if (audio_output_) delete audio_output_; +} + +bool RtpAudioStream::isMatch(const rtpstream_id_t *id) const +{ + if (id + && rtpstream_id_equal(&id_, id, RTPSTREAM_ID_EQUAL_SSRC)) + return true; + return false; +} + +bool RtpAudioStream::isMatch(const _packet_info *pinfo, const _rtp_info *rtp_info) const +{ + if (pinfo && rtp_info + && rtpstream_id_equal_pinfo_rtp_info(&id_, pinfo, rtp_info)) + return true; + return false; +} + +void RtpAudioStream::addRtpPacket(const struct _packet_info *pinfo, const struct _rtp_info *rtp_info) +{ + if (!rtp_info) return; + + if (first_packet_) { + rtpstream_info_analyse_init(&rtpstream_, pinfo, rtp_info); + first_packet_ = false; + } + rtpstream_info_analyse_process(&rtpstream_, pinfo, rtp_info); + + rtp_packet_t *rtp_packet = g_new0(rtp_packet_t, 1); + rtp_packet->info = (struct _rtp_info *) g_memdup2(rtp_info, sizeof(struct _rtp_info)); + if (rtp_info->info_all_data_present && (rtp_info->info_payload_len != 0)) { + rtp_packet->payload_data = (guint8 *) g_memdup2(&(rtp_info->info_data[rtp_info->info_payload_offset]), + rtp_info->info_payload_len); + } + + if (rtp_packets_.size() < 1) { // First packet + start_abs_offset_ = nstime_to_sec(&pinfo->abs_ts) - start_rel_time_; + start_rel_time_ = stop_rel_time_ = nstime_to_sec(&pinfo->rel_ts); + } + rtp_packet->frame_num = pinfo->num; + rtp_packet->arrive_offset = nstime_to_sec(&pinfo->rel_ts) - start_rel_time_; + + rtp_packets_ << rtp_packet; +} + +void RtpAudioStream::clearPackets() +{ + for (int i = 0; i < rtp_packets_.size(); i++) { + rtp_packet_t *rtp_packet = rtp_packets_[i]; + g_free(rtp_packet->info); + g_free(rtp_packet->payload_data); + g_free(rtp_packet); + } + rtp_packets_.clear(); + rtpstream_info_free_data(&rtpstream_); + memset(&rtpstream_, 0, sizeof(rtpstream_)); + rtpstream_id_copy(&id_, &rtpstream_.id); + first_packet_ = true; +} + +void RtpAudioStream::reset(double global_start_time) +{ + global_start_rel_time_ = global_start_time; + stop_rel_time_ = start_rel_time_; + audio_out_rate_ = 0; + max_sample_val_ = 1; + packet_timestamps_.clear(); + visual_samples_.clear(); + out_of_seq_timestamps_.clear(); + jitter_drop_timestamps_.clear(); +} + +AudioRouting RtpAudioStream::getAudioRouting() +{ + return audio_routing_; +} + +void RtpAudioStream::setAudioRouting(AudioRouting audio_routing) +{ + audio_routing_ = audio_routing; +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +void RtpAudioStream::decode(QAudioDevice out_device) +#else +void RtpAudioStream::decode(QAudioDeviceInfo out_device) +#endif +{ + if (rtp_packets_.size() < 1) return; + + audio_file_->setFrameWriteStage(); + decodeAudio(out_device); + + // Skip silence at begin of the stream + audio_file_->setFrameReadStage(prepend_samples_); + + speex_resampler_reset_mem(visual_resampler_); + decodeVisual(); + audio_file_->setDataReadStage(); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +quint32 RtpAudioStream::calculateAudioOutRate(QAudioDevice out_device, unsigned int sample_rate, unsigned int requested_out_rate) +#else +quint32 RtpAudioStream::calculateAudioOutRate(QAudioDeviceInfo out_device, unsigned int sample_rate, unsigned int requested_out_rate) +#endif +{ + quint32 out_rate; + + // Use the first non-zero rate we find. Ajust it to match + // our audio hardware. + QAudioFormat format; + format.setSampleRate(sample_rate); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + // Must match rtp_media.h. + format.setSampleFormat(QAudioFormat::Int16); +#else + format.setSampleSize(SAMPLE_BYTES * 8); // bits + format.setSampleType(QAudioFormat::SignedInt); +#endif + if (stereo_required_) { + format.setChannelCount(2); + } else { + format.setChannelCount(1); + } +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + format.setCodec("audio/pcm"); +#endif + + if (!out_device.isNull() && + !out_device.isFormatSupported(format) && + (requested_out_rate == 0) + ) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + out_rate = out_device.preferredFormat().sampleRate(); +#else + out_rate = out_device.nearestFormat(format).sampleRate(); +#endif + } else { + if ((requested_out_rate != 0) && + (requested_out_rate != sample_rate) + ) { + out_rate = requested_out_rate; + } else { + out_rate = sample_rate; + } + } + + RTP_STREAM_DEBUG("Audio sample rate is %u", out_rate); + + return out_rate; +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +void RtpAudioStream::decodeAudio(QAudioDevice out_device) +#else +void RtpAudioStream::decodeAudio(QAudioDeviceInfo out_device) +#endif +{ + // XXX This is more messy than it should be. + + gint32 resample_buff_bytes = 0x1000; + SAMPLE *resample_buff = (SAMPLE *) g_malloc(resample_buff_bytes); + char *write_buff = NULL; + qint64 write_bytes = 0; + unsigned int channels = 0; + unsigned int sample_rate = 0; + guint32 last_sequence = 0; + guint32 last_sequence_w = 0; // Last sequence number we wrote data + + double rtp_time_prev = 0.0; + double arrive_time_prev = 0.0; + double pack_period = 0.0; + double start_time = 0.0; + double start_rtp_time = 0.0; + guint64 start_timestamp = 0; + + size_t decoded_bytes_prev = 0; + unsigned int audio_resampler_input_rate = 0; + struct SpeexResamplerState_ *audio_resampler = NULL; + + for (int cur_packet = 0; cur_packet < rtp_packets_.size(); cur_packet++) { + SAMPLE *decode_buff = NULL; + // TODO: Update a progress bar here. + rtp_packet_t *rtp_packet = rtp_packets_[cur_packet]; + + stop_rel_time_ = start_rel_time_ + rtp_packet->arrive_offset; + + QString payload_name; + if (rtp_packet->info->info_payload_type_str) { + payload_name = rtp_packet->info->info_payload_type_str; + } else { + payload_name = try_val_to_str_ext(rtp_packet->info->info_payload_type, &rtp_payload_type_short_vals_ext); + } + if (!payload_name.isEmpty()) { + payload_names_ << payload_name; + } + + if (cur_packet < 1) { // First packet + start_timestamp = rtp_packet->info->info_extended_timestamp; + start_rtp_time = 0; + rtp_time_prev = 0; + last_sequence = rtp_packet->info->info_extended_seq_num - 1; + } + + size_t decoded_bytes = decode_rtp_packet(rtp_packet, &decode_buff, decoders_hash_, &channels, &sample_rate); + // XXX: We don't actually *do* anything with channels, and just treat + // everything as if it were mono + + unsigned rtp_clock_rate = sample_rate; + if (rtp_packet->info->info_payload_type == PT_G722) { + // G.722 sample rate is 16kHz, but RTP clock rate is 8kHz + // for historic reasons. + rtp_clock_rate = 8000; + } + + // Length 2 for PT_PCM mean silence packet probably, ignore + if (decoded_bytes == 0 || sample_rate == 0 || + ((rtp_packet->info->info_payload_type == PT_PCMU || + rtp_packet->info->info_payload_type == PT_PCMA + ) && (decoded_bytes == 2) + ) + ) { + // We didn't decode anything. Clean up and prep for + // the next packet. + last_sequence = rtp_packet->info->info_extended_seq_num; + g_free(decode_buff); + continue; + } + + if (audio_out_rate_ == 0) { + first_sample_rate_ = sample_rate; + + // We calculate audio_out_rate just for first sample_rate. + // All later are just resampled to it. + // Side effect: it creates and initiates resampler if needed + audio_out_rate_ = calculateAudioOutRate(out_device, sample_rate, audio_requested_out_rate_); + + // Calculate count of prepend samples for the stream + // The earliest stream starts at 0. + // Note: Order of operations and separation to two formulas is + // important. + // When joined, calculated incorrectly - probably caused by + // conversions between int/double + prepend_samples_ = (start_rel_time_ - global_start_rel_time_) * sample_rate; + prepend_samples_ = prepend_samples_ * audio_out_rate_ / sample_rate; + + // Prepend silence to match our sibling streams. + if ((prepend_samples_ > 0) && (audio_out_rate_ != 0)) { + audio_file_->frameWriteSilence(rtp_packet->frame_num, prepend_samples_); + } + } + + if (rtp_packet->info->info_extended_seq_num != last_sequence+1) { + out_of_seq_timestamps_.append(stop_rel_time_); + } + last_sequence = rtp_packet->info->info_extended_seq_num; + + double rtp_time = (double)(rtp_packet->info->info_extended_timestamp-start_timestamp)/rtp_clock_rate - start_rtp_time; + double arrive_time; + if (timing_mode_ == RtpTimestamp) { + arrive_time = rtp_time; + } else { + arrive_time = rtp_packet->arrive_offset - start_time; + } + + double diff = qAbs(arrive_time - rtp_time); + if (diff*1000 > jitter_buffer_size_ && timing_mode_ != Uninterrupted) { + // rtp_player.c:628 + + jitter_drop_timestamps_.append(stop_rel_time_); + RTP_STREAM_DEBUG("Packet drop by jitter buffer exceeded %f > %d", diff*1000, jitter_buffer_size_); + + /* if there was a silence period (more than two packetization + * period) resync the source */ + if ((rtp_time - rtp_time_prev) > pack_period*2) { + qint64 silence_samples; + RTP_STREAM_DEBUG("Resync..."); + + silence_samples = (qint64)((arrive_time - arrive_time_prev)*sample_rate - decoded_bytes_prev / SAMPLE_BYTES); + silence_samples = silence_samples * audio_out_rate_ / sample_rate; + silence_timestamps_.append(stop_rel_time_); + // Timestamp shift can make silence calculation negative + if ((silence_samples > 0) && (audio_out_rate_ != 0)) { + audio_file_->frameWriteSilence(rtp_packet->frame_num, silence_samples); + } + + decoded_bytes_prev = 0; + start_timestamp = rtp_packet->info->info_extended_timestamp; + start_rtp_time = 0; + start_time = rtp_packet->arrive_offset; + rtp_time_prev = 0; + } + + } else { + // rtp_player.c:664 + /* Add silence if it is necessary */ + qint64 silence_samples; + + if (timing_mode_ == Uninterrupted) { + silence_samples = 0; + } else { + silence_samples = (int)((rtp_time - rtp_time_prev)*sample_rate - decoded_bytes_prev / SAMPLE_BYTES); + silence_samples = silence_samples * audio_out_rate_ / sample_rate; + } + + if (silence_samples != 0) { + wrong_timestamp_timestamps_.append(stop_rel_time_); + } + + if (silence_samples > 0) { + silence_timestamps_.append(stop_rel_time_); + if ((silence_samples > 0) && (audio_out_rate_ != 0)) { + audio_file_->frameWriteSilence(rtp_packet->frame_num, silence_samples); + } + } + + // XXX rtp_player.c:696 adds audio here. + rtp_time_prev = rtp_time; + pack_period = (double) decoded_bytes / SAMPLE_BYTES / sample_rate; + decoded_bytes_prev = decoded_bytes; + arrive_time_prev = arrive_time; + } + + // Prepare samples to write + write_buff = (char *) decode_buff; + write_bytes = decoded_bytes; + + if (audio_out_rate_ != sample_rate) { + // Resample the audio to match output rate. + // Buffer is in SAMPLEs + spx_uint32_t in_len = (spx_uint32_t) (write_bytes / SAMPLE_BYTES); + // Output is audio_out_rate_/sample_rate bigger than input + spx_uint32_t out_len = (spx_uint32_t) ((guint64)in_len * audio_out_rate_ / sample_rate); + resample_buff = resizeBufferIfNeeded(resample_buff, &resample_buff_bytes, out_len * SAMPLE_BYTES); + + if (audio_resampler && + sample_rate != audio_resampler_input_rate + ) { + // Clear old resampler because input rate changed + speex_resampler_destroy(audio_resampler); + audio_resampler_input_rate = 0; + audio_resampler = NULL; + } + if (!audio_resampler) { + audio_resampler_input_rate = sample_rate; + audio_resampler = speex_resampler_init(1, sample_rate, audio_out_rate_, 10, NULL); + RTP_STREAM_DEBUG("Started resampling from %u to (out) %u Hz.", sample_rate, audio_out_rate_); + } + speex_resampler_process_int(audio_resampler, 0, decode_buff, &in_len, resample_buff, &out_len); + + write_buff = (char *) resample_buff; + write_bytes = out_len * SAMPLE_BYTES; + } + + // We should write only newer data to avoid duplicates in replay + if (last_sequence_w < last_sequence) { + // Write the decoded, possibly-resampled audio to our temp file. + audio_file_->frameWriteSamples(rtp_packet->frame_num, write_buff, write_bytes); + last_sequence_w = last_sequence; + } + + g_free(decode_buff); + } + g_free(resample_buff); + + if (audio_resampler) speex_resampler_destroy(audio_resampler); +} + +// We preallocate buffer, 320 samples is enough for most scenarios +#define VISUAL_BUFF_LEN (320) +#define VISUAL_BUFF_BYTES (SAMPLE_BYTES * VISUAL_BUFF_LEN) +void RtpAudioStream::decodeVisual() +{ + spx_uint32_t read_len = 0; + gint32 read_buff_bytes = VISUAL_BUFF_BYTES; + SAMPLE *read_buff = (SAMPLE *) g_malloc(read_buff_bytes); + gint32 resample_buff_bytes = VISUAL_BUFF_BYTES; + SAMPLE *resample_buff = (SAMPLE *) g_malloc(resample_buff_bytes); + unsigned int sample_no = 0; + spx_uint32_t out_len; + guint32 frame_num; + rtp_frame_type type; + + speex_resampler_set_rate(visual_resampler_, audio_out_rate_, visual_sample_rate_); + + // Loop over every frame record + // readFrameSamples() maintains size of buffer for us + while (audio_file_->readFrameSamples(&read_buff_bytes, &read_buff, &read_len, &frame_num, &type)) { + out_len = (spx_uint32_t)(((guint64)read_len * visual_sample_rate_ ) / audio_out_rate_); + + if (type == RTP_FRAME_AUDIO) { + // We resample only audio samples + resample_buff = resizeBufferIfNeeded(resample_buff, &resample_buff_bytes, out_len * SAMPLE_BYTES); + + // Resample + speex_resampler_process_int(visual_resampler_, 0, read_buff, &read_len, resample_buff, &out_len); + + // Create timestamp and visual sample + for (unsigned i = 0; i < out_len; i++) { + double time = start_rel_time_ + (double) sample_no / visual_sample_rate_; + packet_timestamps_[time] = frame_num; + if (qAbs(resample_buff[i]) > max_sample_val_) max_sample_val_ = qAbs(resample_buff[i]); + visual_samples_.append(resample_buff[i]); + sample_no++; + } + } else { + // Insert end of line mark + double time = start_rel_time_ + (double) sample_no / visual_sample_rate_; + packet_timestamps_[time] = frame_num; + visual_samples_.append(SAMPLE_NaN); + sample_no += out_len; + } + } + + max_sample_val_used_ = max_sample_val_; + g_free(resample_buff); + g_free(read_buff); +} + +const QStringList RtpAudioStream::payloadNames() const +{ + QStringList payload_names = payload_names_.values(); + payload_names.sort(); + return payload_names; +} + +const QVector RtpAudioStream::visualTimestamps(bool relative) +{ + QVector ts_keys = packet_timestamps_.keys().toVector(); + if (relative) return ts_keys; + + QVector adj_timestamps; + for (int i = 0; i < ts_keys.size(); i++) { + adj_timestamps.append(ts_keys[i] + start_abs_offset_ - start_rel_time_); + } + return adj_timestamps; +} + +// Scale the height of the waveform to global scale (max_sample_val_used_) +// and adjust its Y offset so that they overlap slightly (stack_offset_). +static const double stack_offset_ = G_MAXINT16 / 3; +const QVector RtpAudioStream::visualSamples(int y_offset) +{ + QVector adj_samples; + double scaled_offset = y_offset * stack_offset_; + for (int i = 0; i < visual_samples_.size(); i++) { + if (SAMPLE_NaN != visual_samples_[i]) { + adj_samples.append(((double)visual_samples_[i] * G_MAXINT16 / max_sample_val_used_) + scaled_offset); + } else { + // Convert to break in graph line + adj_samples.append(qQNaN()); + } + } + return adj_samples; +} + +const QVector RtpAudioStream::outOfSequenceTimestamps(bool relative) +{ + if (relative) return out_of_seq_timestamps_; + + QVector adj_timestamps; + for (int i = 0; i < out_of_seq_timestamps_.size(); i++) { + adj_timestamps.append(out_of_seq_timestamps_[i] + start_abs_offset_ - start_rel_time_); + } + return adj_timestamps; +} + +const QVector RtpAudioStream::outOfSequenceSamples(int y_offset) +{ + QVector adj_samples; + double scaled_offset = y_offset * stack_offset_; // XXX Should be different for seq, jitter, wrong & silence + for (int i = 0; i < out_of_seq_timestamps_.size(); i++) { + adj_samples.append(scaled_offset); + } + return adj_samples; +} + +const QVector RtpAudioStream::jitterDroppedTimestamps(bool relative) +{ + if (relative) return jitter_drop_timestamps_; + + QVector adj_timestamps; + for (int i = 0; i < jitter_drop_timestamps_.size(); i++) { + adj_timestamps.append(jitter_drop_timestamps_[i] + start_abs_offset_ - start_rel_time_); + } + return adj_timestamps; +} + +const QVector RtpAudioStream::jitterDroppedSamples(int y_offset) +{ + QVector adj_samples; + double scaled_offset = y_offset * stack_offset_; // XXX Should be different for seq, jitter, wrong & silence + for (int i = 0; i < jitter_drop_timestamps_.size(); i++) { + adj_samples.append(scaled_offset); + } + return adj_samples; +} + +const QVector RtpAudioStream::wrongTimestampTimestamps(bool relative) +{ + if (relative) return wrong_timestamp_timestamps_; + + QVector adj_timestamps; + for (int i = 0; i < wrong_timestamp_timestamps_.size(); i++) { + adj_timestamps.append(wrong_timestamp_timestamps_[i] + start_abs_offset_ - start_rel_time_); + } + return adj_timestamps; +} + +const QVector RtpAudioStream::wrongTimestampSamples(int y_offset) +{ + QVector adj_samples; + double scaled_offset = y_offset * stack_offset_; // XXX Should be different for seq, jitter, wrong & silence + for (int i = 0; i < wrong_timestamp_timestamps_.size(); i++) { + adj_samples.append(scaled_offset); + } + return adj_samples; +} + +const QVector RtpAudioStream::insertedSilenceTimestamps(bool relative) +{ + if (relative) return silence_timestamps_; + + QVector adj_timestamps; + for (int i = 0; i < silence_timestamps_.size(); i++) { + adj_timestamps.append(silence_timestamps_[i] + start_abs_offset_ - start_rel_time_); + } + return adj_timestamps; +} + +const QVector RtpAudioStream::insertedSilenceSamples(int y_offset) +{ + QVector adj_samples; + double scaled_offset = y_offset * stack_offset_; // XXX Should be different for seq, jitter, wrong & silence + for (int i = 0; i < silence_timestamps_.size(); i++) { + adj_samples.append(scaled_offset); + } + return adj_samples; +} + +quint32 RtpAudioStream::nearestPacket(double timestamp, bool is_relative) +{ + if (packet_timestamps_.size() < 1) return 0; + + if (!is_relative) timestamp -= start_abs_offset_; + QMap::iterator it = packet_timestamps_.lowerBound(timestamp); + if (it == packet_timestamps_.end()) return 0; + return it.value(); +} + +QAudio::State RtpAudioStream::outputState() const +{ + if (!audio_output_) return QAudio::IdleState; + return audio_output_->state(); +} + +const QString RtpAudioStream::formatDescription(const QAudioFormat &format) +{ + QString fmt_descr = QString("%1 Hz, ").arg(format.sampleRate()); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + switch (format.sampleFormat()) { + case QAudioFormat::UInt8: + fmt_descr += "UInt8"; + break; + case QAudioFormat::Int16: + fmt_descr += "Int16"; + break; + case QAudioFormat::Int32: + fmt_descr += "Int32"; + break; + case QAudioFormat::Float: + fmt_descr += "Float"; + break; + default: + fmt_descr += "Unknown"; + break; + } +#else + switch (format.sampleType()) { + case QAudioFormat::SignedInt: + fmt_descr += "Int"; + fmt_descr += QString::number(format.sampleSize()); + fmt_descr += format.byteOrder() == QAudioFormat::BigEndian ? "BE" : "LE"; + break; + case QAudioFormat::UnSignedInt: + fmt_descr += "UInt"; + fmt_descr += QString::number(format.sampleSize()); + fmt_descr += format.byteOrder() == QAudioFormat::BigEndian ? "BE" : "LE"; + break; + case QAudioFormat::Float: + fmt_descr += "Float"; + break; + default: + fmt_descr += "Unknown"; + break; + } +#endif + + return fmt_descr; +} + +QString RtpAudioStream::getIDAsQString() +{ + gchar *src_addr_str = address_to_display(NULL, &id_.src_addr); + gchar *dst_addr_str = address_to_display(NULL, &id_.dst_addr); + QString str = QString("%1:%2 - %3:%4 %5") + .arg(src_addr_str) + .arg(id_.src_port) + .arg(dst_addr_str) + .arg(id_.dst_port) + .arg(QString("0x%1").arg(id_.ssrc, 0, 16)); + wmem_free(NULL, src_addr_str); + wmem_free(NULL, dst_addr_str); + + return str; +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +bool RtpAudioStream::prepareForPlay(QAudioDevice out_device) +#else +bool RtpAudioStream::prepareForPlay(QAudioDeviceInfo out_device) +#endif +{ + qint64 start_pos; + qint64 size; + + if (audio_routing_.isMuted()) + return false; + + if (audio_output_) + return false; + + if (audio_out_rate_ == 0) { + /* It is observed, but is not an error + QString error = tr("RTP stream (%1) is empty or codec is unsupported.") + .arg(getIDAsQString()); + + emit playbackError(error); + */ + return false; + } + + QAudioFormat format; + format.setSampleRate(audio_out_rate_); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + // Must match rtp_media.h. + format.setSampleFormat(QAudioFormat::Int16); +#else + format.setSampleSize(SAMPLE_BYTES * 8); // bits + format.setSampleType(QAudioFormat::SignedInt); +#endif + if (stereo_required_) { + format.setChannelCount(2); + } else { + format.setChannelCount(1); + } +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + format.setCodec("audio/pcm"); +#endif + + // RTP_STREAM_DEBUG("playing %s %d samples @ %u Hz", + // sample_file_->fileName().toUtf8().constData(), + // (int) sample_file_->size(), audio_out_rate_); + + if (!out_device.isFormatSupported(format)) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString playback_error = tr("%1 does not support PCM at %2. Preferred format is %3") + .arg(out_device.description(), formatDescription(format), formatDescription(out_device.preferredFormat())); +#else + QString playback_error = tr("%1 does not support PCM at %2. Preferred format is %3") + .arg(out_device.deviceName()) + .arg(formatDescription(format)) + .arg(formatDescription(out_device.nearestFormat(format))); +#endif + emit playbackError(playback_error); + } + + start_pos = (qint64)(start_play_time_ * SAMPLE_BYTES * audio_out_rate_); + // Round to SAMPLE_BYTES boundary + start_pos = (start_pos / SAMPLE_BYTES) * SAMPLE_BYTES; + size = audio_file_->sampleFileSize(); + if (stereo_required_) { + // There is 2x more samples for stereo + start_pos *= 2; + size *= 2; + } + if (start_pos < size) { + audio_file_->setDataReadStage(); + temp_file_ = new AudioRoutingFilter(audio_file_, stereo_required_, audio_routing_); + temp_file_->seek(start_pos); + if (audio_output_) delete audio_output_; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + audio_output_ = new QAudioSink(out_device, format, this); + connect(audio_output_, &QAudioSink::stateChanged, this, &RtpAudioStream::outputStateChanged); +#else + audio_output_ = new QAudioOutput(out_device, format, this); + connect(audio_output_, &QAudioOutput::stateChanged, this, &RtpAudioStream::outputStateChanged); +#endif + return true; + } else { + // Report stopped audio if start position is later than stream ends + outputStateChanged(QAudio::StoppedState); + return false; + } + + return false; +} + +void RtpAudioStream::startPlaying() +{ + // On Win32/Qt 6.x start() returns, but state() is QAudio::StoppedState even + // everything is OK + audio_output_->start(temp_file_); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + // Bug is related to Qt 4.x and probably for 5.x, but not for 6.x + // QTBUG-6548 StoppedState is not always emitted on error, force a cleanup + // in case playback fails immediately. + if (audio_output_ && audio_output_->state() == QAudio::StoppedState) { + outputStateChanged(QAudio::StoppedState); + } +#endif +} + +void RtpAudioStream::pausePlaying() +{ + if (audio_routing_.isMuted()) + return; + + if (audio_output_) { + if (QAudio::ActiveState == audio_output_->state()) { + audio_output_->suspend(); + } else if (QAudio::SuspendedState == audio_output_->state()) { + audio_output_->resume(); + } + } +} + +void RtpAudioStream::stopPlaying() +{ + if (audio_routing_.isMuted()) + return; + + if (audio_output_) { + if (audio_output_->state() == QAudio::StoppedState) { + // Looks like "delayed" QTBUG-6548 + // It may happen that stream is stopped, but no signal emited + // Probably triggered by some issue in sound system which is not + // handled by Qt correctly + outputStateChanged(QAudio::StoppedState); + } else { + audio_output_->stop(); + } + } +} + +void RtpAudioStream::seekPlaying(qint64 samples _U_) +{ + if (audio_routing_.isMuted()) + return; + + if (audio_output_) { + audio_output_->suspend(); + audio_file_->seekSample(samples); + audio_output_->resume(); + } +} + +void RtpAudioStream::outputStateChanged(QAudio::State new_state) +{ + if (!audio_output_) return; + + // On some platforms including macOS and Windows, the stateChanged signal + // is emitted while a QMutexLocker is active. As a result we shouldn't + // delete audio_output_ here. + switch (new_state) { + case QAudio::StoppedState: + { + // RTP_STREAM_DEBUG("stopped %f", audio_output_->processedUSecs() / 100000.0); + // Detach from parent (RtpAudioStream) to prevent deleteLater + // from being run during destruction of this class. + QAudio::Error error = audio_output_->error(); + + audio_output_->setParent(0); + audio_output_->disconnect(); + audio_output_->deleteLater(); + audio_output_ = NULL; + emit finishedPlaying(this, error); + break; + } + case QAudio::IdleState: + // Workaround for Qt behaving on some platforms with some soundcards: + // When ->stop() is called from outputStateChanged(), + // internalQMutexLock is locked and application hangs. + // We can stop the stream later. + QTimer::singleShot(0, this, SLOT(delayedStopStream())); + + break; + default: + break; + } +} + +void RtpAudioStream::delayedStopStream() +{ + audio_output_->stop(); +} + +SAMPLE *RtpAudioStream::resizeBufferIfNeeded(SAMPLE *buff, gint32 *buff_bytes, qint64 requested_size) +{ + if (requested_size > *buff_bytes) { + while ((requested_size > *buff_bytes)) + *buff_bytes *= 2; + buff = (SAMPLE *) g_realloc(buff, *buff_bytes); + } + + return buff; +} + +void RtpAudioStream::seekSample(qint64 samples) +{ + audio_file_->seekSample(samples); +} + +qint64 RtpAudioStream::readSample(SAMPLE *sample) +{ + return audio_file_->readSample(sample); +} + +bool RtpAudioStream::savePayload(QIODevice *file) +{ + for (int cur_packet = 0; cur_packet < rtp_packets_.size(); cur_packet++) { + // TODO: Update a progress bar here. + rtp_packet_t *rtp_packet = rtp_packets_[cur_packet]; + + if ((rtp_packet->info->info_payload_type != PT_CN) && + (rtp_packet->info->info_payload_type != PT_CN_OLD)) { + // All other payloads + int64_t nchars; + + if (rtp_packet->payload_data && (rtp_packet->info->info_payload_len > 0)) { + nchars = file->write((char *)rtp_packet->payload_data, rtp_packet->info->info_payload_len); + if (nchars != rtp_packet->info->info_payload_len) { + return false; + } + } + } + } + + return true; +} + + +#endif // QT_MULTIMEDIA_LIB diff --git a/ui/qt/rtp_audio_stream.h b/ui/qt/rtp_audio_stream.h new file mode 100644 index 00000000..1ddeaacd --- /dev/null +++ b/ui/qt/rtp_audio_stream.h @@ -0,0 +1,235 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTPAUDIOSTREAM_H +#define RTPAUDIOSTREAM_H + +#include "config.h" + +#ifdef QT_MULTIMEDIA_LIB + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class QAudioFormat; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +class QAudioSink; +#else +class QAudioOutput; +#endif +class QIODevice; + + +class RtpAudioStream : public QObject +{ + Q_OBJECT +public: + enum TimingMode { JitterBuffer, RtpTimestamp, Uninterrupted }; + + explicit RtpAudioStream(QObject *parent, rtpstream_id_t *id, bool stereo_required); + ~RtpAudioStream(); + bool isMatch(const rtpstream_id_t *id) const; + bool isMatch(const struct _packet_info *pinfo, const struct _rtp_info *rtp_info) const; + void addRtpPacket(const struct _packet_info *pinfo, const struct _rtp_info *rtp_info); + void clearPackets(); + void reset(double global_start_time); + AudioRouting getAudioRouting(); + void setAudioRouting(AudioRouting audio_routing); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + void decode(QAudioDevice out_device); +#else + void decode(QAudioDeviceInfo out_device); +#endif + + double startRelTime() const { return start_rel_time_; } + double stopRelTime() const { return stop_rel_time_; } + unsigned sampleRate() const { return first_sample_rate_; } + unsigned playRate() const { return audio_out_rate_; } + void setRequestedPlayRate(unsigned new_rate) { audio_requested_out_rate_ = new_rate; } + const QStringList payloadNames() const; + + /** + * @brief Return a list of visual timestamps. + * @return A set of timestamps suitable for passing to QCPGraph::setData. + */ + const QVector visualTimestamps(bool relative = true); + /** + * @brief Return a list of visual samples. There will be fewer visual samples + * per second (1000) than the actual audio. + * @param y_offset Y axis offset to be used for stacking graphs. + * @return A set of values suitable for passing to QCPGraph::setData. + */ + const QVector visualSamples(int y_offset = 0); + + /** + * @brief Return a list of out-of-sequence timestamps. + * @return A set of timestamps suitable for passing to QCPGraph::setData. + */ + const QVector outOfSequenceTimestamps(bool relative = true); + int outOfSequence() { return static_cast(out_of_seq_timestamps_.size()); } + /** + * @brief Return a list of out-of-sequence samples. Y value is constant. + * @param y_offset Y axis offset to be used for stacking graphs. + * @return A set of values suitable for passing to QCPGraph::setData. + */ + const QVector outOfSequenceSamples(int y_offset = 0); + + /** + * @brief Return a list of jitter dropped timestamps. + * @return A set of timestamps suitable for passing to QCPGraph::setData. + */ + const QVector jitterDroppedTimestamps(bool relative = true); + int jitterDropped() { return static_cast(jitter_drop_timestamps_.size()); } + /** + * @brief Return a list of jitter dropped samples. Y value is constant. + * @param y_offset Y axis offset to be used for stacking graphs. + * @return A set of values suitable for passing to QCPGraph::setData. + */ + const QVector jitterDroppedSamples(int y_offset = 0); + + /** + * @brief Return a list of wrong timestamps. + * @return A set of timestamps suitable for passing to QCPGraph::setData. + */ + const QVector wrongTimestampTimestamps(bool relative = true); + int wrongTimestamps() { return static_cast(wrong_timestamp_timestamps_.size()); } + /** + * @brief Return a list of wrong timestamp samples. Y value is constant. + * @param y_offset Y axis offset to be used for stacking graphs. + * @return A set of values suitable for passing to QCPGraph::setData. + */ + const QVector wrongTimestampSamples(int y_offset = 0); + + /** + * @brief Return a list of inserted silence timestamps. + * @return A set of timestamps suitable for passing to QCPGraph::setData. + */ + const QVector insertedSilenceTimestamps(bool relative = true); + int insertedSilences() { return static_cast(silence_timestamps_.size()); } + /** + * @brief Return a list of wrong timestamp samples. Y value is constant. + * @param y_offset Y axis offset to be used for stacking graphs. + * @return A set of values suitable for passing to QCPGraph::setData. + */ + const QVector insertedSilenceSamples(int y_offset = 0); + + quint32 nearestPacket(double timestamp, bool is_relative = true); + + QRgb color() { return color_; } + void setColor(QRgb color) { color_ = color; } + + QAudio::State outputState() const; + + void setJitterBufferSize(int jitter_buffer_size) { jitter_buffer_size_ = jitter_buffer_size; } + void setTimingMode(TimingMode timing_mode) { timing_mode_ = timing_mode; } + void setStartPlayTime(double start_play_time) { start_play_time_ = start_play_time; } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + bool prepareForPlay(QAudioDevice out_device); +#else + bool prepareForPlay(QAudioDeviceInfo out_device); +#endif + void startPlaying(); + void pausePlaying(); + void stopPlaying(); + void seekPlaying(qint64 samples); + void setStereoRequired(bool stereo_required) { stereo_required_ = stereo_required; } + qint16 getMaxSampleValue() { return max_sample_val_; } + void setMaxSampleValue(gint16 max_sample_val) { max_sample_val_used_ = max_sample_val; } + void seekSample(qint64 samples); + qint64 readSample(SAMPLE *sample); + qint64 getLeadSilenceSamples() { return prepend_samples_; } + qint64 getTotalSamples() { return (audio_file_->getTotalSamples()); } + qint64 getEndOfSilenceSample() { return (audio_file_->getEndOfSilenceSample()); } + double getEndOfSilenceTime() { return (double)getEndOfSilenceSample() / (double)playRate(); } + qint64 convertTimeToSamples(double time) { return (qint64)(time * playRate()); } + bool savePayload(QIODevice *file); + guint getHash() { return rtpstream_id_to_hash(&(id_)); } + rtpstream_id_t *getID() { return &(id_); } + QString getIDAsQString(); + rtpstream_info_t *getStreamInfo() { return &rtpstream_; } + +signals: + void processedSecs(double secs); + void playbackError(const QString error_msg); + void finishedPlaying(RtpAudioStream *stream, QAudio::Error error); + +private: + // Used to identify unique streams. + // The GTK+ UI also uses the call number + current channel. + rtpstream_id_t id_; + rtpstream_info_t rtpstream_; + bool first_packet_; + + QVectorrtp_packets_; + RtpAudioFile *audio_file_; // Stores waveform samples in sparse file + QIODevice *temp_file_; + struct _GHashTable *decoders_hash_; + double global_start_rel_time_; + double start_abs_offset_; + double start_rel_time_; + double stop_rel_time_; + qint64 prepend_samples_; // Count of silence samples at begin of the stream to align with other streams + AudioRouting audio_routing_; + bool stereo_required_; + quint32 first_sample_rate_; + quint32 audio_out_rate_; + quint32 audio_requested_out_rate_; + QSet payload_names_; + struct SpeexResamplerState_ *visual_resampler_; + QMap packet_timestamps_; + QVector visual_samples_; + QVector out_of_seq_timestamps_; + QVector jitter_drop_timestamps_; + QVector wrong_timestamp_timestamps_; + QVector silence_timestamps_; + qint16 max_sample_val_; + qint16 max_sample_val_used_; + QRgb color_; + + int jitter_buffer_size_; + TimingMode timing_mode_; + double start_play_time_; + + const QString formatDescription(const QAudioFormat & format); + QString currentOutputDevice(); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QAudioSink *audio_output_; + void decodeAudio(QAudioDevice out_device); + quint32 calculateAudioOutRate(QAudioDevice out_device, unsigned int sample_rate, unsigned int requested_out_rate); +#else + QAudioOutput *audio_output_; + void decodeAudio(QAudioDeviceInfo out_device); + quint32 calculateAudioOutRate(QAudioDeviceInfo out_device, unsigned int sample_rate, unsigned int requested_out_rate); +#endif + void decodeVisual(); + SAMPLE *resizeBufferIfNeeded(SAMPLE *buff, gint32 *buff_bytes, qint64 requested_size); + +private slots: + void outputStateChanged(QAudio::State new_state); + void delayedStopStream(); +}; + +#endif // QT_MULTIMEDIA_LIB + +#endif // RTPAUDIOSTREAM_H diff --git a/ui/qt/rtp_player_dialog.cpp b/ui/qt/rtp_player_dialog.cpp new file mode 100644 index 00000000..7d0ad62d --- /dev/null +++ b/ui/qt/rtp_player_dialog.cpp @@ -0,0 +1,2798 @@ +/* rtp_player_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include +#include +#include "rtp_player_dialog.h" +#include +#include "epan/epan_dissect.h" + +#include "file.h" +#include "frame_tvbuff.h" + +#include "rtp_analysis_dialog.h" + +#ifdef QT_MULTIMEDIA_LIB + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include "rtp_audio_stream.h" +#include +#include +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include + +#include +#include +#include + +#endif // QT_MULTIMEDIA_LIB + +#include +#include + +#include +#include "main_application.h" + +// To do: +// - Threaded decoding? + +// Current and former RTP player bugs. Many have attachments that can be usef for testing. +// Bug 3368 - The timestamp line in a RTP or RTCP packet display's "Not Representable" +// Bug 3952 - VoIP Call RTP Player: audio played is corrupted when RFC2833 packets are present +// Bug 4960 - RTP Player: Audio and visual feedback get rapidly out of sync +// Bug 5527 - Adding arbitrary value to x-axis RTP player +// Bug 7935 - Wrong Timestamps in RTP Player-Decode +// Bug 8007 - UI gets confused on playing decoded audio in rtp_player +// Bug 9007 - Switching SSRC values in RTP stream +// Bug 10613 - RTP audio player crashes +// Bug 11125 - RTP Player does not show progress in selected stream in Window 7 +// Bug 11409 - Wireshark crashes when using RTP player +// Bug 12166 - RTP audio player crashes + +// In some places we match by conv/call number, in others we match by first frame. + +enum { + channel_col_, + src_addr_col_, + src_port_col_, + dst_addr_col_, + dst_port_col_, + ssrc_col_, + first_pkt_col_, + num_pkts_col_, + time_span_col_, + sample_rate_col_, + play_rate_col_, + payload_col_, + + stream_data_col_ = src_addr_col_, // RtpAudioStream + graph_audio_data_col_ = src_port_col_, // QCPGraph (wave) + graph_sequence_data_col_ = dst_addr_col_, // QCPGraph (sequence) + graph_jitter_data_col_ = dst_port_col_, // QCPGraph (jitter) + graph_timestamp_data_col_ = ssrc_col_, // QCPGraph (timestamp) + // first_pkt_col_ is skipped, it is used for real data + graph_silence_data_col_ = num_pkts_col_, // QCPGraph (silence) +}; + +class RtpPlayerTreeWidgetItem : public QTreeWidgetItem +{ +public: + RtpPlayerTreeWidgetItem(QTreeWidget *tree) : + QTreeWidgetItem(tree) + { + } + + bool operator< (const QTreeWidgetItem &other) const + { + // Handle numeric sorting + switch (treeWidget()->sortColumn()) { + case src_port_col_: + case dst_port_col_: + case num_pkts_col_: + case sample_rate_col_: + return text(treeWidget()->sortColumn()).toInt() < other.text(treeWidget()->sortColumn()).toInt(); + case play_rate_col_: + return text(treeWidget()->sortColumn()).toInt() < other.text(treeWidget()->sortColumn()).toInt(); + case first_pkt_col_: + int v1; + int v2; + + v1 = data(first_pkt_col_, Qt::UserRole).toInt(); + v2 = other.data(first_pkt_col_, Qt::UserRole).toInt(); + + return v1 < v2; + default: + // Fall back to string comparison + return QTreeWidgetItem::operator <(other); + break; + } + } +}; + +RtpPlayerDialog *RtpPlayerDialog::pinstance_{nullptr}; +std::mutex RtpPlayerDialog::init_mutex_; +std::mutex RtpPlayerDialog::run_mutex_; + +RtpPlayerDialog *RtpPlayerDialog::openRtpPlayerDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list, bool capture_running) +{ + std::lock_guard lock(init_mutex_); + if (pinstance_ == nullptr) + { + pinstance_ = new RtpPlayerDialog(parent, cf, capture_running); + connect(pinstance_, SIGNAL(goToPacket(int)), + packet_list, SLOT(goToPacket(int))); + } + return pinstance_; +} + +RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf, bool capture_running _U_) : + WiresharkDialog(parent, cf) +#ifdef QT_MULTIMEDIA_LIB + , ui(new Ui::RtpPlayerDialog) + , first_stream_rel_start_time_(0.0) + , first_stream_abs_start_time_(0.0) + , first_stream_rel_stop_time_(0.0) + , streams_length_(0.0) + , start_marker_time_(0.0) + , number_ticker_(new QCPAxisTicker) + , datetime_ticker_(new QCPAxisTickerDateTime) + , stereo_available_(false) + , marker_stream_(0) + , marker_stream_requested_out_rate_(0) + , last_ti_(0) + , listener_removed_(true) + , block_redraw_(false) + , lock_ui_(0) + , read_capture_enabled_(capture_running) + , silence_skipped_time_(0.0) +#endif // QT_MULTIMEDIA_LIB +{ + ui->setupUi(this); + loadGeometry(parent.width(), parent.height()); + setWindowTitle(mainApp->windowTitleString(tr("RTP Player"))); + ui->streamTreeWidget->installEventFilter(this); + ui->audioPlot->installEventFilter(this); + installEventFilter(this); + +#ifdef QT_MULTIMEDIA_LIB + ui->splitter->setStretchFactor(0, 3); + ui->splitter->setStretchFactor(1, 1); + + ui->streamTreeWidget->sortByColumn(first_pkt_col_, Qt::AscendingOrder); + + graph_ctx_menu_ = new QMenu(this); + + graph_ctx_menu_->addAction(ui->actionZoomIn); + graph_ctx_menu_->addAction(ui->actionZoomOut); + graph_ctx_menu_->addAction(ui->actionReset); + graph_ctx_menu_->addSeparator(); + graph_ctx_menu_->addAction(ui->actionMoveRight10); + graph_ctx_menu_->addAction(ui->actionMoveLeft10); + graph_ctx_menu_->addAction(ui->actionMoveRight1); + graph_ctx_menu_->addAction(ui->actionMoveLeft1); + graph_ctx_menu_->addSeparator(); + graph_ctx_menu_->addAction(ui->actionGoToPacket); + graph_ctx_menu_->addAction(ui->actionGoToSetupPacketPlot); + set_action_shortcuts_visible_in_context_menu(graph_ctx_menu_->actions()); + + ui->streamTreeWidget->setMouseTracking(true); + connect(ui->streamTreeWidget, &QTreeWidget::itemEntered, this, &RtpPlayerDialog::itemEntered); + + connect(ui->audioPlot, &QCustomPlot::mouseMove, this, &RtpPlayerDialog::mouseMovePlot); + connect(ui->audioPlot, &QCustomPlot::mousePress, this, &RtpPlayerDialog::graphClicked); + connect(ui->audioPlot, &QCustomPlot::mouseDoubleClick, this, &RtpPlayerDialog::graphDoubleClicked); + connect(ui->audioPlot, &QCustomPlot::plottableClick, this, &RtpPlayerDialog::plotClicked); + + cur_play_pos_ = new QCPItemStraightLine(ui->audioPlot); + cur_play_pos_->setVisible(false); + + start_marker_pos_ = new QCPItemStraightLine(ui->audioPlot); + start_marker_pos_->setPen(QPen(Qt::green,4)); + setStartPlayMarker(0); + drawStartPlayMarker(); + start_marker_pos_->setVisible(true); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + notify_timer_.setInterval(100); // ~15 fps + connect(¬ify_timer_, &QTimer::timeout, this, &RtpPlayerDialog::outputNotify); +#endif + + datetime_ticker_->setDateTimeFormat("yyyy-MM-dd\nhh:mm:ss.zzz"); + + ui->audioPlot->xAxis->setNumberFormat("gb"); + ui->audioPlot->xAxis->setNumberPrecision(3); + ui->audioPlot->xAxis->setTicker(datetime_ticker_); + ui->audioPlot->yAxis->setVisible(false); + + ui->playButton->setIcon(StockIcon("media-playback-start")); + ui->playButton->setEnabled(false); + ui->pauseButton->setIcon(StockIcon("media-playback-pause")); + ui->pauseButton->setCheckable(true); + ui->pauseButton->setVisible(false); + ui->stopButton->setIcon(StockIcon("media-playback-stop")); + ui->stopButton->setEnabled(false); + ui->skipSilenceButton->setIcon(StockIcon("media-seek-forward")); + ui->skipSilenceButton->setCheckable(true); + ui->skipSilenceButton->setEnabled(false); + + read_btn_ = ui->buttonBox->addButton(ui->actionReadCapture->text(), QDialogButtonBox::ActionRole); + read_btn_->setToolTip(ui->actionReadCapture->toolTip()); + read_btn_->setEnabled(false); + connect(read_btn_, &QPushButton::pressed, this, &RtpPlayerDialog::on_actionReadCapture_triggered); + + inaudible_btn_ = new QToolButton(); + ui->buttonBox->addButton(inaudible_btn_, QDialogButtonBox::ActionRole); + inaudible_btn_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + inaudible_btn_->setPopupMode(QToolButton::MenuButtonPopup); + + connect(ui->actionInaudibleButton, &QAction::triggered, this, &RtpPlayerDialog::on_actionSelectInaudible_triggered); + inaudible_btn_->setDefaultAction(ui->actionInaudibleButton); + // Overrides text striping of shortcut undercode in QAction + inaudible_btn_->setText(ui->actionInaudibleButton->text()); + inaudible_btn_->setEnabled(false); + inaudible_btn_->setMenu(ui->menuInaudible); + + analyze_btn_ = RtpAnalysisDialog::addAnalyzeButton(ui->buttonBox, this); + + prepare_btn_ = ui->buttonBox->addButton(ui->actionPrepareFilter->text(), QDialogButtonBox::ActionRole); + prepare_btn_->setToolTip(ui->actionPrepareFilter->toolTip()); + connect(prepare_btn_, &QPushButton::pressed, this, &RtpPlayerDialog::on_actionPrepareFilter_triggered); + + export_btn_ = ui->buttonBox->addButton(ui->actionExportButton->text(), QDialogButtonBox::ActionRole); + export_btn_->setToolTip(ui->actionExportButton->toolTip()); + export_btn_->setEnabled(false); + export_btn_->setMenu(ui->menuExport); + + // Ordered, unique device names starting with the system default + QMap out_device_map; // true == default device +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + out_device_map.insert(QMediaDevices::defaultAudioOutput().description(), true); + foreach (QAudioDevice out_device, QMediaDevices::audioOutputs()) { + if (!out_device_map.contains(out_device.description())) { + out_device_map.insert(out_device.description(), false); + } + } +#else + out_device_map.insert(QAudioDeviceInfo::defaultOutputDevice().deviceName(), true); + foreach (QAudioDeviceInfo out_device, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) { + if (!out_device_map.contains(out_device.deviceName())) { + out_device_map.insert(out_device.deviceName(), false); + } + } +#endif + + ui->outputDeviceComboBox->blockSignals(true); + foreach (QString out_name, out_device_map.keys()) { + ui->outputDeviceComboBox->addItem(out_name); + if (out_device_map.value(out_name)) { + ui->outputDeviceComboBox->setCurrentIndex(ui->outputDeviceComboBox->count() - 1); + } + } + if (ui->outputDeviceComboBox->count() < 1) { + ui->outputDeviceComboBox->setEnabled(false); + ui->playButton->setEnabled(false); + ui->pauseButton->setEnabled(false); + ui->stopButton->setEnabled(false); + ui->skipSilenceButton->setEnabled(false); + ui->minSilenceSpinBox->setEnabled(false); + ui->outputDeviceComboBox->addItem(tr("No devices available")); + ui->outputAudioRate->setEnabled(false); + } else { + stereo_available_ = isStereoAvailable(); + fillAudioRateMenu(); + } + ui->outputDeviceComboBox->blockSignals(false); + + ui->audioPlot->setMouseTracking(true); + ui->audioPlot->setEnabled(true); + ui->audioPlot->setInteractions( + QCP::iRangeDrag | + QCP::iRangeZoom + ); + + graph_ctx_menu_->addSeparator(); + list_ctx_menu_ = new QMenu(this); + list_ctx_menu_->addAction(ui->actionPlay); + graph_ctx_menu_->addAction(ui->actionPlay); + list_ctx_menu_->addAction(ui->actionStop); + graph_ctx_menu_->addAction(ui->actionStop); + list_ctx_menu_->addMenu(ui->menuSelect); + graph_ctx_menu_->addMenu(ui->menuSelect); + list_ctx_menu_->addMenu(ui->menuAudioRouting); + graph_ctx_menu_->addMenu(ui->menuAudioRouting); + list_ctx_menu_->addAction(ui->actionRemoveStream); + graph_ctx_menu_->addAction(ui->actionRemoveStream); + list_ctx_menu_->addAction(ui->actionGoToSetupPacketTree); + set_action_shortcuts_visible_in_context_menu(list_ctx_menu_->actions()); + + connect(&cap_file_, &CaptureFile::captureEvent, this, &RtpPlayerDialog::captureEvent); + connect(this, SIGNAL(updateFilter(QString, bool)), + &parent, SLOT(filterPackets(QString, bool))); + connect(this, SIGNAL(rtpAnalysisDialogReplaceRtpStreams(QVector)), + &parent, SLOT(rtpAnalysisDialogReplaceRtpStreams(QVector))); + connect(this, SIGNAL(rtpAnalysisDialogAddRtpStreams(QVector)), + &parent, SLOT(rtpAnalysisDialogAddRtpStreams(QVector))); + connect(this, SIGNAL(rtpAnalysisDialogRemoveRtpStreams(QVector)), + &parent, SLOT(rtpAnalysisDialogRemoveRtpStreams(QVector))); +#endif // QT_MULTIMEDIA_LIB +} + +// _U_ is used when no QT_MULTIMEDIA_LIB is available +QToolButton *RtpPlayerDialog::addPlayerButton(QDialogButtonBox *button_box, QDialog *dialog _U_) +{ + if (!button_box) return NULL; + + QAction *ca; + QToolButton *player_button = new QToolButton(); + button_box->addButton(player_button, QDialogButtonBox::ActionRole); + player_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + player_button->setPopupMode(QToolButton::MenuButtonPopup); + + ca = new QAction(tr("&Play Streams"), player_button); + ca->setToolTip(tr("Open RTP player dialog")); + ca->setIcon(StockIcon("media-playback-start")); + connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpPlayerReplace())); + player_button->setDefaultAction(ca); + // Overrides text striping of shortcut undercode in QAction + player_button->setText(ca->text()); + +#if defined(QT_MULTIMEDIA_LIB) + QMenu *button_menu = new QMenu(player_button); + button_menu->setToolTipsVisible(true); + ca = button_menu->addAction(tr("&Set playlist")); + ca->setToolTip(tr("Replace existing playlist in RTP Player with new one")); + connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpPlayerReplace())); + ca = button_menu->addAction(tr("&Add to playlist")); + ca->setToolTip(tr("Add new set to existing playlist in RTP Player")); + connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpPlayerAdd())); + ca = button_menu->addAction(tr("&Remove from playlist")); + ca->setToolTip(tr("Remove selected streams from playlist in RTP Player")); + connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpPlayerRemove())); + player_button->setMenu(button_menu); +#else + player_button->setEnabled(false); + player_button->setText(tr("No Audio")); +#endif + + return player_button; +} + +#ifdef QT_MULTIMEDIA_LIB +RtpPlayerDialog::~RtpPlayerDialog() +{ + std::lock_guard lock(init_mutex_); + if (pinstance_ != nullptr) { + cleanupMarkerStream(); + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (audio_stream) + delete audio_stream; + } + delete ui; + pinstance_ = nullptr; + } +} + +void RtpPlayerDialog::accept() +{ + if (!listener_removed_) { + remove_tap_listener(this); + listener_removed_ = true; + } + + int row_count = ui->streamTreeWidget->topLevelItemCount(); + // Stop all streams before the dialogs are closed. + for (int row = 0; row < row_count; row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + audio_stream->stopPlaying(); + } + WiresharkDialog::accept(); +} + +void RtpPlayerDialog::reject() +{ + RtpPlayerDialog::accept(); +} + +void RtpPlayerDialog::retapPackets() +{ + if (!listener_removed_) { + // Retap is running, nothing better we can do + return; + } + lockUI(); + ui->hintLabel->setText("" + tr("Decoding streams...") + ""); + mainApp->processEvents(); + + // Clear packets from existing streams before retap + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + + row_stream->clearPackets(); + } + + // destroyCheck is protection againts destroying dialog during recap. + // It stores dialog pointer in data() and if dialog destroyed, it + // returns null + QPointer destroyCheck=this; + GString *error_string; + + listener_removed_ = false; + error_string = register_tap_listener("rtp", this, NULL, 0, NULL, tapPacket, NULL, NULL); + if (error_string) { + report_failure("RTP Player - tap registration failed: %s", error_string->str); + g_string_free(error_string, TRUE); + unlockUI(); + return; + } + cap_file_.retapPackets(); + + // Check if dialog exists still + if (destroyCheck.data()) { + if (!listener_removed_) { + remove_tap_listener(this); + listener_removed_ = true; + } + fillTappedColumns(); + rescanPackets(true); + } + unlockUI(); +} + +void RtpPlayerDialog::rescanPackets(bool rescale_axes) +{ + lockUI(); + // Show information for a user - it can last long time... + playback_error_.clear(); + ui->hintLabel->setText("" + tr("Decoding streams...") + ""); + mainApp->processEvents(); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QAudioDevice cur_out_device = getCurrentDeviceInfo(); +#else + QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo(); +#endif + int row_count = ui->streamTreeWidget->topLevelItemCount(); + + // Reset stream values + for (int row = 0; row < row_count; row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + audio_stream->setStereoRequired(stereo_available_); + audio_stream->reset(first_stream_rel_start_time_); + + audio_stream->setJitterBufferSize((int) ui->jitterSpinBox->value()); + + RtpAudioStream::TimingMode timing_mode = RtpAudioStream::JitterBuffer; + switch (ui->timingComboBox->currentIndex()) { + case RtpAudioStream::RtpTimestamp: + timing_mode = RtpAudioStream::RtpTimestamp; + break; + case RtpAudioStream::Uninterrupted: + timing_mode = RtpAudioStream::Uninterrupted; + break; + default: + break; + } + audio_stream->setTimingMode(timing_mode); + + //if (!cur_out_device.isNull()) { + audio_stream->decode(cur_out_device); + //} + } + + for (int col = 0; col < ui->streamTreeWidget->columnCount() - 1; col++) { + ui->streamTreeWidget->resizeColumnToContents(col); + } + + createPlot(rescale_axes); + + updateWidgets(); + unlockUI(); +} + +void RtpPlayerDialog::createPlot(bool rescale_axes) +{ + bool legend_out_of_sequence = false; + bool legend_jitter_dropped = false; + bool legend_wrong_timestamps = false; + bool legend_inserted_silences = false; + bool relative_timestamps = !ui->todCheckBox->isChecked(); + int row_count = ui->streamTreeWidget->topLevelItemCount(); + gint16 total_max_sample_value = 1; + + ui->audioPlot->clearGraphs(); + + if (relative_timestamps) { + ui->audioPlot->xAxis->setTicker(number_ticker_); + } else { + ui->audioPlot->xAxis->setTicker(datetime_ticker_); + } + + // Calculate common Y scale for graphs + for (int row = 0; row < row_count; row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + gint16 max_sample_value = audio_stream->getMaxSampleValue(); + + if (max_sample_value > total_max_sample_value) { + total_max_sample_value = max_sample_value; + } + } + + // Clear existing graphs + for (int row = 0; row < row_count; row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + int y_offset = row_count - row - 1; + AudioRouting audio_routing = audio_stream->getAudioRouting(); + + ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant()); + ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant()); + ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant()); + ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant()); + ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant()); + + // Set common scale + audio_stream->setMaxSampleValue(total_max_sample_value); + + // Waveform + RtpAudioGraph *audio_graph = new RtpAudioGraph(ui->audioPlot, audio_stream->color()); + audio_graph->setMuted(audio_routing.isMuted()); + audio_graph->setData(audio_stream->visualTimestamps(relative_timestamps), audio_stream->visualSamples(y_offset)); + ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant::fromValue(audio_graph)); + //RTP_STREAM_DEBUG("Plotting %s, %d samples", ti->text(src_addr_col_).toUtf8().constData(), audio_graph->wave->data()->size()); + + QString span_str; + if (ui->todCheckBox->isChecked()) { + QDateTime date_time1 = QDateTime::fromMSecsSinceEpoch((audio_stream->startRelTime() + first_stream_abs_start_time_ - audio_stream->startRelTime()) * 1000.0); + QDateTime date_time2 = QDateTime::fromMSecsSinceEpoch((audio_stream->stopRelTime() + first_stream_abs_start_time_ - audio_stream->startRelTime()) * 1000.0); + QString time_str1 = date_time1.toString("yyyy-MM-dd hh:mm:ss.zzz"); + QString time_str2 = date_time2.toString("yyyy-MM-dd hh:mm:ss.zzz"); + span_str = QString("%1 - %2 (%3)") + .arg(time_str1) + .arg(time_str2) + .arg(QString::number(audio_stream->stopRelTime() - audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1)); + } else { + span_str = QString("%1 - %2 (%3)") + .arg(QString::number(audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1)) + .arg(QString::number(audio_stream->stopRelTime(), 'f', prefs.gui_decimal_places1)) + .arg(QString::number(audio_stream->stopRelTime() - audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1)); + } + ti->setText(time_span_col_, span_str); + ti->setText(sample_rate_col_, QString::number(audio_stream->sampleRate())); + ti->setText(play_rate_col_, QString::number(audio_stream->playRate())); + ti->setText(payload_col_, audio_stream->payloadNames().join(", ")); + + if (audio_stream->outOfSequence() > 0) { + // Sequence numbers + QCPGraph *seq_graph = ui->audioPlot->addGraph(); + seq_graph->setLineStyle(QCPGraph::lsNone); + seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssSquare, tango_aluminium_6, Qt::white, mainApp->font().pointSize())); // Arbitrary + seq_graph->setSelectable(QCP::stNone); + seq_graph->setData(audio_stream->outOfSequenceTimestamps(relative_timestamps), audio_stream->outOfSequenceSamples(y_offset)); + ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant::fromValue(seq_graph)); + if (legend_out_of_sequence) { + seq_graph->removeFromLegend(); + } else { + seq_graph->setName(tr("Out of Sequence")); + legend_out_of_sequence = true; + } + } + + if (audio_stream->jitterDropped() > 0) { + // Jitter drops + QCPGraph *seq_graph = ui->audioPlot->addGraph(); + seq_graph->setLineStyle(QCPGraph::lsNone); + seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, tango_scarlet_red_5, Qt::white, mainApp->font().pointSize())); // Arbitrary + seq_graph->setSelectable(QCP::stNone); + seq_graph->setData(audio_stream->jitterDroppedTimestamps(relative_timestamps), audio_stream->jitterDroppedSamples(y_offset)); + ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant::fromValue(seq_graph)); + if (legend_jitter_dropped) { + seq_graph->removeFromLegend(); + } else { + seq_graph->setName(tr("Jitter Drops")); + legend_jitter_dropped = true; + } + } + + if (audio_stream->wrongTimestamps() > 0) { + // Wrong timestamps + QCPGraph *seq_graph = ui->audioPlot->addGraph(); + seq_graph->setLineStyle(QCPGraph::lsNone); + seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDiamond, tango_sky_blue_5, Qt::white, mainApp->font().pointSize())); // Arbitrary + seq_graph->setSelectable(QCP::stNone); + seq_graph->setData(audio_stream->wrongTimestampTimestamps(relative_timestamps), audio_stream->wrongTimestampSamples(y_offset)); + ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant::fromValue(seq_graph)); + if (legend_wrong_timestamps) { + seq_graph->removeFromLegend(); + } else { + seq_graph->setName(tr("Wrong Timestamps")); + legend_wrong_timestamps = true; + } + } + + if (audio_stream->insertedSilences() > 0) { + // Inserted silence + QCPGraph *seq_graph = ui->audioPlot->addGraph(); + seq_graph->setLineStyle(QCPGraph::lsNone); + seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssTriangle, tango_butter_5, Qt::white, mainApp->font().pointSize())); // Arbitrary + seq_graph->setSelectable(QCP::stNone); + seq_graph->setData(audio_stream->insertedSilenceTimestamps(relative_timestamps), audio_stream->insertedSilenceSamples(y_offset)); + ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant::fromValue(seq_graph)); + if (legend_inserted_silences) { + seq_graph->removeFromLegend(); + } else { + seq_graph->setName(tr("Inserted Silence")); + legend_inserted_silences = true; + } + } + } + ui->audioPlot->legend->setVisible(legend_out_of_sequence || legend_jitter_dropped || legend_wrong_timestamps || legend_inserted_silences); + + ui->audioPlot->replot(); + if (rescale_axes) resetXAxis(); +} + +void RtpPlayerDialog::fillTappedColumns() +{ + // true just for first stream + bool is_first = true; + + // Get all rows, immutable list. Later changes in rows migth reorder them + QList items = ui->streamTreeWidget->findItems( + QString("*"), Qt::MatchWrap | Qt::MatchWildcard | Qt::MatchRecursive); + + // Update rows by calculated values, it might reorder them in view... + foreach(QTreeWidgetItem *ti, items) { + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (audio_stream) { + rtpstream_info_t *rtpstream = audio_stream->getStreamInfo(); + + // 0xFFFFFFFF mean no setup frame + // first_packet_num == setup_frame_number happens, when + // rtp_udp is active or Decode as was used + if ((rtpstream->setup_frame_number == 0xFFFFFFFF) || + (rtpstream->rtp_stats.first_packet_num == rtpstream->setup_frame_number) + ) { + int packet = rtpstream->rtp_stats.first_packet_num; + ti->setText(first_pkt_col_, QString("RTP %1").arg(packet)); + ti->setData(first_pkt_col_, Qt::UserRole, QVariant(packet)); + } else { + int packet = rtpstream->setup_frame_number; + ti->setText(first_pkt_col_, QString("SETUP %1").arg(rtpstream->setup_frame_number)); + ti->setData(first_pkt_col_, Qt::UserRole, QVariant(packet)); + } + ti->setText(num_pkts_col_, QString::number(rtpstream->packet_count)); + updateStartStopTime(rtpstream, is_first); + is_first = false; + } + } + setMarkers(); +} + +void RtpPlayerDialog::addSingleRtpStream(rtpstream_id_t *id) +{ + bool found = false; + + AudioRouting audio_routing = AudioRouting(AUDIO_UNMUTED, channel_mono); + + if (!id) return; + + // Find the RTP streams associated with this conversation. + // gtk/rtp_player.c:mark_rtp_stream_to_play does this differently. + + QList streams = stream_hash_.values(rtpstream_id_to_hash(id)); + for (int i = 0; i < streams.size(); i++) { + RtpAudioStream *row_stream = streams.at(i); + if (row_stream->isMatch(id)) { + found = true; + break; + } + } + + + if (found) { + return; + } + + try { + int tli_count = ui->streamTreeWidget->topLevelItemCount(); + + RtpAudioStream *audio_stream = new RtpAudioStream(this, id, stereo_available_); + audio_stream->setColor(ColorUtils::graphColor(tli_count)); + + QTreeWidgetItem *ti = new RtpPlayerTreeWidgetItem(ui->streamTreeWidget); + stream_hash_.insert(rtpstream_id_to_hash(id), audio_stream); + ti->setText(src_addr_col_, address_to_qstring(&(id->src_addr))); + ti->setText(src_port_col_, QString::number(id->src_port)); + ti->setText(dst_addr_col_, address_to_qstring(&(id->dst_addr))); + ti->setText(dst_port_col_, QString::number(id->dst_port)); + ti->setText(ssrc_col_, int_to_qstring(id->ssrc, 8, 16)); + + // Calculated items are updated after every retapPackets() + + ti->setData(stream_data_col_, Qt::UserRole, QVariant::fromValue(audio_stream)); + if (stereo_available_) { + if (tli_count%2) { + audio_routing.setChannel(channel_stereo_right); + } else { + audio_routing.setChannel(channel_stereo_left); + } + } else { + audio_routing.setChannel(channel_mono); + } + ti->setToolTip(channel_col_, QString(tr("Double click on cell to change audio routing"))); + formatAudioRouting(ti, audio_routing); + audio_stream->setAudioRouting(audio_routing); + + for (int col = 0; col < ui->streamTreeWidget->columnCount(); col++) { + QBrush fgBrush = ti->foreground(col); + fgBrush.setColor(audio_stream->color()); + fgBrush.setStyle(Qt::SolidPattern); + ti->setForeground(col, fgBrush); + } + + connect(audio_stream, &RtpAudioStream::finishedPlaying, this, &RtpPlayerDialog::playFinished); + connect(audio_stream, &RtpAudioStream::playbackError, this, &RtpPlayerDialog::setPlaybackError); + } catch (...) { + qWarning() << "Stream ignored, try to add fewer streams to playlist"; + } + + RTP_STREAM_DEBUG("adding stream %d to layout", + ui->streamTreeWidget->topLevelItemCount()); +} + +void RtpPlayerDialog::lockUI() +{ + if (0 == lock_ui_++) { + if (playing_streams_.count() > 0) { + on_stopButton_clicked(); + } + setEnabled(false); + } +} + +void RtpPlayerDialog::unlockUI() +{ + if (--lock_ui_ == 0) { + setEnabled(true); + } +} + +void RtpPlayerDialog::replaceRtpStreams(QVector stream_ids) +{ + std::unique_lock lock(run_mutex_, std::try_to_lock); + if (lock.owns_lock()) { + lockUI(); + + // Delete all existing rows + if (last_ti_) { + highlightItem(last_ti_, false); + last_ti_ = NULL; + } + + for (int row = ui->streamTreeWidget->topLevelItemCount() - 1; row >= 0; row--) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + removeRow(ti); + } + + // Add all new streams + for (int i=0; i < stream_ids.size(); i++) { + addSingleRtpStream(stream_ids[i]); + } + setMarkers(); + + unlockUI(); +#ifdef QT_MULTIMEDIA_LIB + QTimer::singleShot(0, this, SLOT(retapPackets())); +#endif + } else { + ws_warning("replaceRtpStreams was called while other thread locked it. Current call is ignored, try it later."); + } +} + +void RtpPlayerDialog::addRtpStreams(QVector stream_ids) +{ + std::unique_lock lock(run_mutex_, std::try_to_lock); + if (lock.owns_lock()) { + lockUI(); + + int tli_count = ui->streamTreeWidget->topLevelItemCount(); + + // Add new streams + for (int i=0; i < stream_ids.size(); i++) { + addSingleRtpStream(stream_ids[i]); + } + + if (tli_count == 0) { + setMarkers(); + } + + unlockUI(); +#ifdef QT_MULTIMEDIA_LIB + QTimer::singleShot(0, this, SLOT(retapPackets())); +#endif + } else { + ws_warning("addRtpStreams was called while other thread locked it. Current call is ignored, try it later."); + } +} + +void RtpPlayerDialog::removeRtpStreams(QVector stream_ids) +{ + std::unique_lock lock(run_mutex_, std::try_to_lock); + if (lock.owns_lock()) { + lockUI(); + int tli_count = ui->streamTreeWidget->topLevelItemCount(); + + for (int i=0; i < stream_ids.size(); i++) { + for (int row = 0; row < tli_count; row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (row_stream->isMatch(stream_ids[i])) { + removeRow(ti); + tli_count--; + break; + } + } + } + updateGraphs(); + + updateWidgets(); + unlockUI(); + } else { + ws_warning("removeRtpStreams was called while other thread locked it. Current call is ignored, try it later."); + } +} + +void RtpPlayerDialog::setMarkers() +{ + setStartPlayMarker(0); + drawStartPlayMarker(); +} + +void RtpPlayerDialog::showEvent(QShowEvent *) +{ + QList split_sizes = ui->splitter->sizes(); + int tot_size = split_sizes[0] + split_sizes[1]; + int plot_size = tot_size * 3 / 4; + split_sizes.clear(); + split_sizes << plot_size << tot_size - plot_size; + ui->splitter->setSizes(split_sizes); +} + +bool RtpPlayerDialog::eventFilter(QObject *, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent &keyEvent = static_cast(*event); + int pan_secs = keyEvent.modifiers() & Qt::ShiftModifier ? 1 : 10; + + switch(keyEvent.key()) { + case Qt::Key_Minus: + case Qt::Key_Underscore: // Shifted minus on U.S. keyboards + case Qt::Key_O: // GTK+ + case Qt::Key_R: + on_actionZoomOut_triggered(); + return true; + case Qt::Key_Plus: + case Qt::Key_Equal: // Unshifted plus on U.S. keyboards + case Qt::Key_I: // GTK+ + if (keyEvent.modifiers() == Qt::ControlModifier) { + // Ctrl+I + on_actionSelectInvert_triggered(); + return true; + } else { + // I + on_actionZoomIn_triggered(); + return true; + } + break; + case Qt::Key_Right: + case Qt::Key_L: + panXAxis(pan_secs); + return true; + case Qt::Key_Left: + case Qt::Key_H: + panXAxis(-1 * pan_secs); + return true; + case Qt::Key_0: + case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards + on_actionReset_triggered(); + return true; + case Qt::Key_G: + if (keyEvent.modifiers() == Qt::ShiftModifier) { + // Goto SETUP frame, use correct call based on caller + QPoint pos1 = ui->audioPlot->mapFromGlobal(QCursor::pos()); + QPoint pos2 = ui->streamTreeWidget->mapFromGlobal(QCursor::pos()); + if (ui->audioPlot->rect().contains(pos1)) { + // audio plot, by mouse coords + on_actionGoToSetupPacketPlot_triggered(); + } else if (ui->streamTreeWidget->rect().contains(pos2)) { + // packet tree, by cursor + on_actionGoToSetupPacketTree_triggered(); + } + return true; + } else { + on_actionGoToPacket_triggered(); + return true; + } + case Qt::Key_A: + if (keyEvent.modifiers() == Qt::ControlModifier) { + // Ctrl+A + on_actionSelectAll_triggered(); + return true; + } else if (keyEvent.modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) { + // Ctrl+Shift+A + on_actionSelectNone_triggered(); + return true; + } + break; + case Qt::Key_M: + if (keyEvent.modifiers() == Qt::ShiftModifier) { + on_actionAudioRoutingUnmute_triggered(); + return true; + } else if (keyEvent.modifiers() == Qt::ControlModifier) { + on_actionAudioRoutingMuteInvert_triggered(); + return true; + } else { + on_actionAudioRoutingMute_triggered(); + return true; + } + case Qt::Key_Delete: + on_actionRemoveStream_triggered(); + return true; + case Qt::Key_X: + if (keyEvent.modifiers() == Qt::ControlModifier) { + // Ctrl+X + on_actionRemoveStream_triggered(); + return true; + } + break; + case Qt::Key_Down: + case Qt::Key_Up: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_Home: + case Qt::Key_End: + // Route keys to QTreeWidget + ui->streamTreeWidget->setFocus(); + break; + case Qt::Key_P: + if (keyEvent.modifiers() == Qt::NoModifier) { + on_actionPlay_triggered(); + return true; + } + break; + case Qt::Key_S: + on_actionStop_triggered(); + return true; + case Qt::Key_N: + if (keyEvent.modifiers() == Qt::ShiftModifier) { + // Shift+N + on_actionDeselectInaudible_triggered(); + return true; + } else { + on_actionSelectInaudible_triggered(); + return true; + } + break; + } + } + + return false; +} + +void RtpPlayerDialog::contextMenuEvent(QContextMenuEvent *event) +{ + list_ctx_menu_->popup(event->globalPos()); +} + +void RtpPlayerDialog::updateWidgets() +{ + bool enable_play = true; + bool enable_pause = false; + bool enable_stop = false; + bool enable_timing = true; + int count = ui->streamTreeWidget->topLevelItemCount(); + qsizetype selected = ui->streamTreeWidget->selectedItems().count(); + + if (count < 1) { + enable_play = false; + ui->skipSilenceButton->setEnabled(false); + ui->minSilenceSpinBox->setEnabled(false); + } else { + ui->skipSilenceButton->setEnabled(true); + ui->minSilenceSpinBox->setEnabled(true); + } + + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (audio_stream->outputState() != QAudio::IdleState) { + enable_play = false; + enable_pause = true; + enable_stop = true; + enable_timing = false; + } + } + + ui->actionAudioRoutingP->setVisible(!stereo_available_); + ui->actionAudioRoutingL->setVisible(stereo_available_); + ui->actionAudioRoutingLR->setVisible(stereo_available_); + ui->actionAudioRoutingR->setVisible(stereo_available_); + + ui->playButton->setEnabled(enable_play); + if (enable_play) { + ui->playButton->setVisible(true); + ui->pauseButton->setVisible(false); + } else if (enable_pause) { + ui->playButton->setVisible(false); + ui->pauseButton->setVisible(true); + } + ui->outputDeviceComboBox->setEnabled(enable_play); + ui->outputAudioRate->setEnabled(enable_play); + ui->pauseButton->setEnabled(enable_pause); + ui->stopButton->setEnabled(enable_stop); + ui->actionStop->setEnabled(enable_stop); + cur_play_pos_->setVisible(enable_stop); + + ui->jitterSpinBox->setEnabled(enable_timing); + ui->timingComboBox->setEnabled(enable_timing); + ui->todCheckBox->setEnabled(enable_timing); + + read_btn_->setEnabled(read_capture_enabled_); + inaudible_btn_->setEnabled(count > 0); + analyze_btn_->setEnabled(selected > 0); + prepare_btn_->setEnabled(selected > 0); + + updateHintLabel(); + ui->audioPlot->replot(); +} + +void RtpPlayerDialog::handleItemHighlight(QTreeWidgetItem *ti, bool scroll) +{ + if (ti) { + if (ti != last_ti_) { + if (last_ti_) { + highlightItem(last_ti_, false); + } + highlightItem(ti, true); + + if (scroll) + ui->streamTreeWidget->scrollToItem(ti, QAbstractItemView::EnsureVisible); + ui->audioPlot->replot(); + last_ti_ = ti; + } + } else { + if (last_ti_) { + highlightItem(last_ti_, false); + ui->audioPlot->replot(); + last_ti_ = NULL; + } + } +} + +void RtpPlayerDialog::highlightItem(QTreeWidgetItem *ti, bool highlight) +{ + QFont font; + RtpAudioGraph *audio_graph; + + font.setBold(highlight); + for(int i=0; istreamTreeWidget->columnCount(); i++) { + ti->setFont(i, font); + } + + audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value(); + if (audio_graph) { + audio_graph->setHighlight(highlight); + } +} + +void RtpPlayerDialog::itemEntered(QTreeWidgetItem *item, int column _U_) +{ + handleItemHighlight(item, false); +} + +void RtpPlayerDialog::mouseMovePlot(QMouseEvent *event) +{ + updateHintLabel(); + + QTreeWidgetItem *ti = findItemByCoords(event->pos()); + handleItemHighlight(ti, true); +} + +void RtpPlayerDialog::graphClicked(QMouseEvent *event) +{ + updateWidgets(); + if (event->button() == Qt::RightButton) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + graph_ctx_menu_->popup(event->globalPosition().toPoint()); +#else + graph_ctx_menu_->popup(event->globalPos()); +#endif + } +} + +void RtpPlayerDialog::graphDoubleClicked(QMouseEvent *event) +{ + updateWidgets(); + if (event->button() == Qt::LeftButton) { + // Move start play line + double ts = ui->audioPlot->xAxis->pixelToCoord(event->pos().x()); + + setStartPlayMarker(ts); + drawStartPlayMarker(); + + ui->audioPlot->replot(); + } +} + +void RtpPlayerDialog::plotClicked(QCPAbstractPlottable *plottable _U_, int dataIndex _U_, QMouseEvent *event) +{ + // Delivered plottable very often points to different element than a mouse + // so we find right one by mouse coordinates + QTreeWidgetItem *ti = findItemByCoords(event->pos()); + if (ti) { + if (event->modifiers() == Qt::NoModifier) { + ti->setSelected(true); + } else if (event->modifiers() == Qt::ControlModifier) { + ti->setSelected(!ti->isSelected()); + } + } +} + +QTreeWidgetItem *RtpPlayerDialog::findItemByCoords(QPoint point) +{ + QCPAbstractPlottable *plottable=ui->audioPlot->plottableAt(point); + if (plottable) { + return findItem(plottable); + } + + return NULL; +} + +QTreeWidgetItem *RtpPlayerDialog::findItem(QCPAbstractPlottable *plottable) +{ + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value(); + if (audio_graph && audio_graph->isMyPlottable(plottable)) { + return ti; + } + } + + return NULL; +} + +void RtpPlayerDialog::updateHintLabel() +{ + int packet_num = getHoveredPacket(); + QString hint = ""; + double start_pos = getStartPlayMarker(); + int row_count = ui->streamTreeWidget->topLevelItemCount(); + qsizetype selected = ui->streamTreeWidget->selectedItems().count(); + int not_muted = 0; + + hint += tr("%1 streams").arg(row_count); + + if (row_count > 0) { + if (selected > 0) { + hint += tr(", %1 selected").arg(selected); + } + + for (int row = 0; row < row_count; row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (audio_stream && (!audio_stream->getAudioRouting().isMuted())) { + not_muted++; + } + } + + hint += tr(", %1 not muted").arg(not_muted); + } + + if (packet_num == 0) { + hint += tr(", start: %1. Double click on graph to set start of playback.") + .arg(getFormatedTime(start_pos)); + } else if (packet_num > 0) { + hint += tr(", start: %1, cursor: %2. Press \"G\" to go to packet %3. Double click on graph to set start of playback.") + .arg(getFormatedTime(start_pos)) + .arg(getFormatedHoveredTime()) + .arg(packet_num); + } + + if (!playback_error_.isEmpty()) { + hint += " "; + hint += playback_error_; + hint += " "; + } + + hint += ""; + ui->hintLabel->setText(hint); +} + +void RtpPlayerDialog::resetXAxis() +{ + QCustomPlot *ap = ui->audioPlot; + + double pixel_pad = 10.0; // per side + + ap->rescaleAxes(true); + + double axis_pixels = ap->xAxis->axisRect()->width(); + ap->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->xAxis->range().center()); + + axis_pixels = ap->yAxis->axisRect()->height(); + ap->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->yAxis->range().center()); + + ap->replot(); +} + +void RtpPlayerDialog::updateGraphs() +{ + QCustomPlot *ap = ui->audioPlot; + + // Create new plots, just existing ones + createPlot(false); + + // Rescale Y axis + double pixel_pad = 10.0; // per side + double axis_pixels = ap->yAxis->axisRect()->height(); + ap->yAxis->rescale(true); + ap->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->yAxis->range().center()); + + ap->replot(); +} + +void RtpPlayerDialog::playFinished(RtpAudioStream *stream, QAudio::Error error) +{ + if ((error != QAudio::NoError) && (error != QAudio::UnderrunError)) { + setPlaybackError(tr("Playback of stream %1 failed!") + .arg(stream->getIDAsQString()) + ); + } + playing_streams_.removeOne(stream); + if (playing_streams_.isEmpty()) { + marker_stream_->stop(); + updateWidgets(); + } +} + +void RtpPlayerDialog::setPlayPosition(double secs) +{ + double cur_secs = cur_play_pos_->point1->key(); + + if (ui->todCheckBox->isChecked()) { + secs += first_stream_abs_start_time_; + } else { + secs += first_stream_rel_start_time_; + } + if (secs > cur_secs) { + cur_play_pos_->point1->setCoords(secs, 0.0); + cur_play_pos_->point2->setCoords(secs, 1.0); + ui->audioPlot->replot(); + } +} + +void RtpPlayerDialog::setPlaybackError(const QString playback_error) +{ + playback_error_ = playback_error; + updateHintLabel(); +} + +tap_packet_status RtpPlayerDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr, tap_flags_t) +{ + RtpPlayerDialog *rtp_player_dialog = dynamic_cast((RtpPlayerDialog*)tapinfo_ptr); + if (!rtp_player_dialog) return TAP_PACKET_DONT_REDRAW; + + const struct _rtp_info *rtpinfo = (const struct _rtp_info *)rtpinfo_ptr; + if (!rtpinfo) return TAP_PACKET_DONT_REDRAW; + + /* ignore RTP Version != 2 */ + if (rtpinfo->info_version != 2) + return TAP_PACKET_DONT_REDRAW; + + rtp_player_dialog->addPacket(pinfo, rtpinfo); + + return TAP_PACKET_DONT_REDRAW; +} + +void RtpPlayerDialog::addPacket(packet_info *pinfo, const _rtp_info *rtpinfo) +{ + // Search stream in hash key, if there are multiple streams with same hash + QList streams = stream_hash_.values(pinfo_rtp_info_to_hash(pinfo, rtpinfo)); + for (int i = 0; i < streams.size(); i++) { + RtpAudioStream *row_stream = streams.at(i); + if (row_stream->isMatch(pinfo, rtpinfo)) { + row_stream->addRtpPacket(pinfo, rtpinfo); + break; + } + } + +// qDebug() << "=ap no match!" << address_to_qstring(&pinfo->src) << address_to_qstring(&pinfo->dst); +} + +void RtpPlayerDialog::zoomXAxis(bool in) +{ + QCustomPlot *ap = ui->audioPlot; + double h_factor = ap->axisRect()->rangeZoomFactor(Qt::Horizontal); + + if (!in) { + h_factor = pow(h_factor, -1); + } + + ap->xAxis->scaleRange(h_factor, ap->xAxis->range().center()); + ap->replot(); +} + +// XXX I tried using seconds but pixels make more sense at varying zoom +// levels. +void RtpPlayerDialog::panXAxis(int x_pixels) +{ + QCustomPlot *ap = ui->audioPlot; + double h_pan; + + h_pan = ap->xAxis->range().size() * x_pixels / ap->xAxis->axisRect()->width(); + if (x_pixels) { + ap->xAxis->moveRange(h_pan); + ap->replot(); + } +} + +void RtpPlayerDialog::on_playButton_clicked() +{ + double start_time; + QList streams_to_start; + + ui->hintLabel->setText("" + tr("Preparing to play...") + ""); + mainApp->processEvents(); + ui->pauseButton->setChecked(false); + + // Protect start time against move of marker during the play + start_marker_time_play_ = start_marker_time_; + silence_skipped_time_ = 0.0; + cur_play_pos_->point1->setCoords(start_marker_time_play_, 0.0); + cur_play_pos_->point2->setCoords(start_marker_time_play_, 1.0); + cur_play_pos_->setVisible(true); + playback_error_.clear(); + + if (ui->todCheckBox->isChecked()) { + start_time = start_marker_time_play_; + } else { + start_time = start_marker_time_play_ - first_stream_rel_start_time_; + } + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QAudioDevice cur_out_device = getCurrentDeviceInfo(); +#else + QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo(); +#endif + playing_streams_.clear(); + int row_count = ui->streamTreeWidget->topLevelItemCount(); + for (int row = 0; row < row_count; row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + // All streams starts at first_stream_rel_start_time_ + audio_stream->setStartPlayTime(start_time); + if (audio_stream->prepareForPlay(cur_out_device)) { + playing_streams_ << audio_stream; + } + } + + // Prepare silent stream for progress marker + if (!marker_stream_) { + marker_stream_ = getSilenceAudioOutput(); + } else { + marker_stream_->stop(); + } + + // Start progress marker and then audio streams +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + notify_timer_start_diff_ = -1; +#endif + marker_stream_->start(new AudioSilenceGenerator(marker_stream_)); + // It may happen that stream play is finished before all others are started + // therefore we do not use playing_streams_ there, but separate temporarly + // list. It avoids access element/remove element race condition. + streams_to_start = playing_streams_; + for( int i = 0; istartPlaying(); + } + + updateWidgets(); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +QAudioDevice RtpPlayerDialog::getCurrentDeviceInfo() +{ + QAudioDevice cur_out_device = QMediaDevices::defaultAudioOutput(); + QString cur_out_name = currentOutputDeviceName(); + foreach (QAudioDevice out_device, QMediaDevices::audioOutputs()) { + if (cur_out_name == out_device.description()) { + cur_out_device = out_device; + } + } + + return cur_out_device; +} + +void RtpPlayerDialog::sinkStateChanged() +{ + if (marker_stream_->state() == QAudio::ActiveState) { + notify_timer_.start(); + } else { + notify_timer_.stop(); + } +} +#else +QAudioDeviceInfo RtpPlayerDialog::getCurrentDeviceInfo() +{ + QAudioDeviceInfo cur_out_device = QAudioDeviceInfo::defaultOutputDevice(); + QString cur_out_name = currentOutputDeviceName(); + foreach (QAudioDeviceInfo out_device, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) { + if (cur_out_name == out_device.deviceName()) { + cur_out_device = out_device; + } + } + + return cur_out_device; +} +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +QAudioSink *RtpPlayerDialog::getSilenceAudioOutput() +{ + QAudioDevice cur_out_device = getCurrentDeviceInfo(); + + QAudioFormat format; + if (marker_stream_requested_out_rate_ > 0) { + format.setSampleRate(marker_stream_requested_out_rate_); + } else { + format.setSampleRate(8000); + } + // Must match rtp_media.h. + format.setSampleFormat(QAudioFormat::Int16); + format.setChannelCount(1); + if (!cur_out_device.isFormatSupported(format)) { + format = cur_out_device.preferredFormat(); + } + + QAudioSink *sink = new QAudioSink(cur_out_device, format, this); + connect(sink, &QAudioSink::stateChanged, this, &RtpPlayerDialog::sinkStateChanged); + return sink; +} +#else +QAudioOutput *RtpPlayerDialog::getSilenceAudioOutput() +{ + QAudioOutput *o; + QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo(); + + QAudioFormat format; + if (marker_stream_requested_out_rate_ > 0) { + format.setSampleRate(marker_stream_requested_out_rate_); + } else { + format.setSampleRate(8000); + } + format.setSampleSize(SAMPLE_BYTES * 8); // bits + format.setSampleType(QAudioFormat::SignedInt); + format.setChannelCount(1); + format.setCodec("audio/pcm"); + if (!cur_out_device.isFormatSupported(format)) { + format = cur_out_device.nearestFormat(format); + } + + o = new QAudioOutput(cur_out_device, format, this); + o->setNotifyInterval(100); // ~15 fps + connect(o, &QAudioOutput::notify, this, &RtpPlayerDialog::outputNotify); + + return o; +} +#endif + +void RtpPlayerDialog::outputNotify() +{ + double new_current_pos = 0.0; + double current_pos = 0.0; + qint64 usecs = marker_stream_->processedUSecs(); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + // First notify can show end of buffer, not play point so we have + // remember the shift + if ( -1 == notify_timer_start_diff_ || 0 == notify_timer_start_diff_) { + notify_timer_start_diff_ = usecs; + } + usecs -= notify_timer_start_diff_; +#endif + double secs = usecs / 1000000.0; + + if (ui->skipSilenceButton->isChecked()) { + // We should check whether we can skip some silence + // We must calculate in time domain as every stream can use different + // play rate + double min_silence = playing_streams_[0]->getEndOfSilenceTime(); + for( int i = 1; igetEndOfSilenceTime(); + if (cur_silence < min_silence) { + min_silence = cur_silence; + } + } + + if (min_silence > 0.0) { + double silence_duration; + + // Calculate silence duration we can skip + new_current_pos = first_stream_rel_start_time_ + min_silence; + if (ui->todCheckBox->isChecked()) { + current_pos = secs + start_marker_time_play_ + first_stream_rel_start_time_; + } else { + current_pos = secs + start_marker_time_play_; + } + silence_duration = new_current_pos - current_pos; + + if (silence_duration >= ui->minSilenceSpinBox->value()) { + // Skip silence gap and update cursor difference + for( int i = 0; iconvertTimeToSamples(min_silence); + playing_streams_[i]->seekPlaying(skip_samples); + } + silence_skipped_time_ = silence_duration; + } + } + } + + // Calculate new cursor position + if (ui->todCheckBox->isChecked()) { + secs += start_marker_time_play_; + secs += silence_skipped_time_; + } else { + secs += start_marker_time_play_; + secs -= first_stream_rel_start_time_; + secs += silence_skipped_time_; + } + setPlayPosition(secs); +} + +void RtpPlayerDialog::on_pauseButton_clicked() +{ + for( int i = 0; ipausePlaying(); + } + if (ui->pauseButton->isChecked()) { + marker_stream_->suspend(); + } else { + marker_stream_->resume(); + } + updateWidgets(); +} + +void RtpPlayerDialog::on_stopButton_clicked() +{ + // We need copy of list because items will be removed during stopPlaying() + QList ps=QList(playing_streams_); + for( int i = 0; istopPlaying(); + } + marker_stream_->stop(); + cur_play_pos_->setVisible(false); + updateWidgets(); +} + +void RtpPlayerDialog::on_actionReset_triggered() +{ + resetXAxis(); +} + +void RtpPlayerDialog::on_actionZoomIn_triggered() +{ + zoomXAxis(true); +} + +void RtpPlayerDialog::on_actionZoomOut_triggered() +{ + zoomXAxis(false); +} + +void RtpPlayerDialog::on_actionMoveLeft10_triggered() +{ + panXAxis(-10); +} + +void RtpPlayerDialog::on_actionMoveRight10_triggered() +{ + panXAxis(10); +} + +void RtpPlayerDialog::on_actionMoveLeft1_triggered() +{ + panXAxis(-1); +} + +void RtpPlayerDialog::on_actionMoveRight1_triggered() +{ + panXAxis(1); +} + +void RtpPlayerDialog::on_actionGoToPacket_triggered() +{ + int packet_num = getHoveredPacket(); + if (packet_num > 0) emit goToPacket(packet_num); +} + +void RtpPlayerDialog::handleGoToSetupPacket(QTreeWidgetItem *ti) +{ + if (ti) { + bool ok; + + int packet_num = ti->data(first_pkt_col_, Qt::UserRole).toInt(&ok); + if (ok) { + emit goToPacket(packet_num); + } + } +} + +void RtpPlayerDialog::on_actionGoToSetupPacketPlot_triggered() +{ + QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos()); + handleGoToSetupPacket(findItemByCoords(pos)); +} + +void RtpPlayerDialog::on_actionGoToSetupPacketTree_triggered() +{ + handleGoToSetupPacket(last_ti_); +} + +// Make waveform graphs selectable and update the treewidget selection accordingly. +void RtpPlayerDialog::on_streamTreeWidget_itemSelectionChanged() +{ + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value(); + if (audio_graph) { + audio_graph->setSelected(ti->isSelected()); + } + } + + qsizetype selected = ui->streamTreeWidget->selectedItems().count(); + if (selected == 0) { + analyze_btn_->setEnabled(false); + prepare_btn_->setEnabled(false); + export_btn_->setEnabled(false); + } else if (selected == 1) { + analyze_btn_->setEnabled(true); + prepare_btn_->setEnabled(true); + export_btn_->setEnabled(true); + ui->actionSavePayload->setEnabled(true); + } else { + analyze_btn_->setEnabled(true); + prepare_btn_->setEnabled(true); + export_btn_->setEnabled(true); + ui->actionSavePayload->setEnabled(false); + } + + if (!block_redraw_) { + ui->audioPlot->replot(); + updateHintLabel(); + } +} + +// Change channel audio routing if double clicked channel column +void RtpPlayerDialog::on_streamTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, const int column) +{ + if (column == channel_col_) { + RtpAudioStream *audio_stream = item->data(stream_data_col_, Qt::UserRole).value(); + if (!audio_stream) + return; + + AudioRouting audio_routing = audio_stream->getAudioRouting(); + audio_routing = audio_routing.getNextChannel(stereo_available_); + changeAudioRoutingOnItem(item, audio_routing); + } + updateHintLabel(); +} + +void RtpPlayerDialog::removeRow(QTreeWidgetItem *ti) +{ + if (last_ti_ && (last_ti_ == ti)) { + highlightItem(last_ti_, false); + last_ti_ = NULL; + } + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (audio_stream) { + stream_hash_.remove(audio_stream->getHash(), audio_stream); + ti->setData(stream_data_col_, Qt::UserRole, QVariant()); + delete audio_stream; + } + + RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value(); + if (audio_graph) { + ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant()); + audio_graph->remove(ui->audioPlot); + } + + QCPGraph *graph; + graph = ti->data(graph_sequence_data_col_, Qt::UserRole).value(); + if (graph) { + ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant()); + ui->audioPlot->removeGraph(graph); + } + + graph = ti->data(graph_jitter_data_col_, Qt::UserRole).value(); + if (graph) { + ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant()); + ui->audioPlot->removeGraph(graph); + } + + graph = ti->data(graph_timestamp_data_col_, Qt::UserRole).value(); + if (graph) { + ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant()); + ui->audioPlot->removeGraph(graph); + } + + graph = ti->data(graph_silence_data_col_, Qt::UserRole).value(); + if (graph) { + ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant()); + ui->audioPlot->removeGraph(graph); + } + + delete ti; +} + +void RtpPlayerDialog::on_actionRemoveStream_triggered() +{ + lockUI(); + QList items = ui->streamTreeWidget->selectedItems(); + + block_redraw_ = true; + for(int i = static_cast(items.count()) - 1; i>=0; i-- ) { + removeRow(items[i]); + } + block_redraw_ = false; + // TODO: Recalculate legend + // - Graphs used for legend could be removed above and we must add new + // - If no legend is required, it should be removed + + // Redraw existing waveforms and rescale Y axis + updateGraphs(); + + updateWidgets(); + unlockUI(); +} + +// If called with channel_any, just muted flag should be changed +void RtpPlayerDialog::changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting new_audio_routing) +{ + if (!ti) + return; + + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (!audio_stream) + return; + + AudioRouting audio_routing = audio_stream->getAudioRouting(); + audio_routing.mergeAudioRouting(new_audio_routing); + formatAudioRouting(ti, audio_routing); + + audio_stream->setAudioRouting(audio_routing); + + RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value(); + if (audio_graph) { + + audio_graph->setSelected(ti->isSelected()); + audio_graph->setMuted(audio_routing.isMuted()); + if (!block_redraw_) { + ui->audioPlot->replot(); + } + } +} + +// Find current item and apply change on it +void RtpPlayerDialog::changeAudioRouting(AudioRouting new_audio_routing) +{ + lockUI(); + QList items = ui->streamTreeWidget->selectedItems(); + + block_redraw_ = true; + for(int i = 0; iaudioPlot->replot(); + updateHintLabel(); + unlockUI(); +} + +// Invert mute/unmute on item +void RtpPlayerDialog::invertAudioMutingOnItem(QTreeWidgetItem *ti) +{ + if (!ti) + return; + + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (!audio_stream) + return; + + AudioRouting audio_routing = audio_stream->getAudioRouting(); + // Invert muting + if (audio_routing.isMuted()) { + changeAudioRoutingOnItem(ti, AudioRouting(AUDIO_UNMUTED, channel_any)); + } else { + changeAudioRoutingOnItem(ti, AudioRouting(AUDIO_MUTED, channel_any)); + } +} + +void RtpPlayerDialog::on_actionAudioRoutingP_triggered() +{ + changeAudioRouting(AudioRouting(AUDIO_UNMUTED, channel_mono)); +} + +void RtpPlayerDialog::on_actionAudioRoutingL_triggered() +{ + changeAudioRouting(AudioRouting(AUDIO_UNMUTED, channel_stereo_left)); +} + +void RtpPlayerDialog::on_actionAudioRoutingLR_triggered() +{ + changeAudioRouting(AudioRouting(AUDIO_UNMUTED, channel_stereo_both)); +} + +void RtpPlayerDialog::on_actionAudioRoutingR_triggered() +{ + changeAudioRouting(AudioRouting(AUDIO_UNMUTED, channel_stereo_right)); +} + +void RtpPlayerDialog::on_actionAudioRoutingMute_triggered() +{ + changeAudioRouting(AudioRouting(AUDIO_MUTED, channel_any)); +} + +void RtpPlayerDialog::on_actionAudioRoutingUnmute_triggered() +{ + changeAudioRouting(AudioRouting(AUDIO_UNMUTED, channel_any)); +} + +void RtpPlayerDialog::on_actionAudioRoutingMuteInvert_triggered() +{ + lockUI(); + QList items = ui->streamTreeWidget->selectedItems(); + + block_redraw_ = true; + for(int i = 0; iaudioPlot->replot(); + updateHintLabel(); + unlockUI(); +} + +const QString RtpPlayerDialog::getFormatedTime(double f_time) +{ + QString time_str; + + if (ui->todCheckBox->isChecked()) { + QDateTime date_time = QDateTime::fromMSecsSinceEpoch(f_time * 1000.0); + time_str = date_time.toString("yyyy-MM-dd hh:mm:ss.zzz"); + } else { + time_str = QString::number(f_time, 'f', 6); + time_str += " s"; + } + + return time_str; +} + +const QString RtpPlayerDialog::getFormatedHoveredTime() +{ + QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos()); + QTreeWidgetItem *ti = findItemByCoords(pos); + if (!ti) return tr("Unknown"); + + double ts = ui->audioPlot->xAxis->pixelToCoord(pos.x()); + + return getFormatedTime(ts); +} + +int RtpPlayerDialog::getHoveredPacket() +{ + QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos()); + QTreeWidgetItem *ti = findItemByCoords(pos); + if (!ti) return 0; + + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + + double ts = ui->audioPlot->xAxis->pixelToCoord(pos.x()); + + return audio_stream->nearestPacket(ts, !ui->todCheckBox->isChecked()); +} + +// Used by RtpAudioStreams to initialize QAudioOutput. We could alternatively +// pass the corresponding QAudioDeviceInfo directly. +QString RtpPlayerDialog::currentOutputDeviceName() +{ + return ui->outputDeviceComboBox->currentText(); +} + +void RtpPlayerDialog::fillAudioRateMenu() +{ + ui->outputAudioRate->blockSignals(true); + ui->outputAudioRate->clear(); + ui->outputAudioRate->addItem(tr("Automatic")); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + // XXX QAudioDevice doesn't provide supportedSampleRates(). Fake it with + // what's available. + QAudioDevice cur_out_device = getCurrentDeviceInfo(); + QSetsample_rates; + if (!cur_out_device.isNull()) { + sample_rates.insert(cur_out_device.preferredFormat().sampleRate()); + // Add 8000 if supported + if ((cur_out_device.minimumSampleRate() <= 8000) && + (8000 <= cur_out_device.maximumSampleRate()) + ) { + sample_rates.insert(8000); + } + // Add 16000 if supported + if ((cur_out_device.minimumSampleRate() <= 16000) && + (16000 <= cur_out_device.maximumSampleRate()) + ) { + sample_rates.insert(16000); + } + // Add 44100 if supported + if ((cur_out_device.minimumSampleRate() <= 44100) && + (44100 <= cur_out_device.maximumSampleRate()) + ) { + sample_rates.insert(44100); + } + } + + // Sort values + QList sorter = sample_rates.values(); + std::sort(sorter.begin(), sorter.end()); + + // Insert rates to the list + for (auto rate : sorter) { + ui->outputAudioRate->addItem(QString::number(rate)); + } +#else + QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo(); + + if (!cur_out_device.isNull()) { + foreach (int rate, cur_out_device.supportedSampleRates()) { + ui->outputAudioRate->addItem(QString::number(rate)); + } + } +#endif + ui->outputAudioRate->blockSignals(false); +} + +void RtpPlayerDialog::cleanupMarkerStream() +{ + if (marker_stream_) { + marker_stream_->stop(); + delete marker_stream_; + marker_stream_ = NULL; + } +} + +void RtpPlayerDialog::on_outputDeviceComboBox_currentTextChanged(const QString &) +{ + lockUI(); + stereo_available_ = isStereoAvailable(); + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (!audio_stream) + continue; + + changeAudioRoutingOnItem(ti, audio_stream->getAudioRouting().convert(stereo_available_)); + } + + marker_stream_requested_out_rate_ = 0; + cleanupMarkerStream(); + fillAudioRateMenu(); + rescanPackets(); + unlockUI(); +} + +void RtpPlayerDialog::on_outputAudioRate_currentTextChanged(const QString & rate_string) +{ + lockUI(); + // Any unconvertable string is converted to 0 => used as Automatic rate + unsigned selected_rate = rate_string.toInt(); + + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (!audio_stream) + continue; + + audio_stream->setRequestedPlayRate(selected_rate); + } + marker_stream_requested_out_rate_ = selected_rate; + cleanupMarkerStream(); + rescanPackets(); + unlockUI(); +} + +void RtpPlayerDialog::on_jitterSpinBox_valueChanged(double) +{ + rescanPackets(); +} + +void RtpPlayerDialog::on_timingComboBox_currentIndexChanged(int) +{ + rescanPackets(); +} + +void RtpPlayerDialog::on_todCheckBox_toggled(bool) +{ + QCPAxis *x_axis = ui->audioPlot->xAxis; + double move; + + // Create plot with new tod settings + createPlot(); + + // Move view to same place as was shown before the change + if (ui->todCheckBox->isChecked()) { + // rel -> abs + // based on abs time of first sample + setStartPlayMarker(first_stream_abs_start_time_ + start_marker_time_ - first_stream_rel_start_time_); + move = first_stream_abs_start_time_ - first_stream_rel_start_time_; + } else { + // abs -> rel + // based on 0s + setStartPlayMarker(first_stream_rel_start_time_ + start_marker_time_); + move = - first_stream_abs_start_time_ + first_stream_rel_start_time_; + } + x_axis->moveRange(move); + drawStartPlayMarker(); + ui->audioPlot->replot(); +} + +void RtpPlayerDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_TELEPHONY_RTP_PLAYER_DIALOG); +} + +double RtpPlayerDialog::getStartPlayMarker() +{ + double start_pos; + + if (ui->todCheckBox->isChecked()) { + start_pos = start_marker_time_ + first_stream_abs_start_time_; + } else { + start_pos = start_marker_time_; + } + + return start_pos; +} + +void RtpPlayerDialog::drawStartPlayMarker() +{ + double pos = getStartPlayMarker(); + + start_marker_pos_->point1->setCoords(pos, 0.0); + start_marker_pos_->point2->setCoords(pos, 1.0); + + updateHintLabel(); +} + +void RtpPlayerDialog::setStartPlayMarker(double new_time) +{ + if (ui->todCheckBox->isChecked()) { + new_time = qBound(first_stream_abs_start_time_, new_time, first_stream_abs_start_time_ + streams_length_); + // start_play_time is relative, we must calculate it + start_marker_time_ = new_time - first_stream_abs_start_time_; + } else { + new_time = qBound(first_stream_rel_start_time_, new_time, first_stream_rel_start_time_ + streams_length_); + start_marker_time_ = new_time; + } +} + +void RtpPlayerDialog::updateStartStopTime(rtpstream_info_t *rtpstream, bool is_first) +{ + // Calculate start time of first last packet of last stream + double stream_rel_start_time = nstime_to_sec(&rtpstream->start_rel_time); + double stream_abs_start_time = nstime_to_sec(&rtpstream->start_abs_time); + double stream_rel_stop_time = nstime_to_sec(&rtpstream->stop_rel_time); + + if (is_first) { + // Take start/stop time for first stream + first_stream_rel_start_time_ = stream_rel_start_time; + first_stream_abs_start_time_ = stream_abs_start_time; + first_stream_rel_stop_time_ = stream_rel_stop_time; + } else { + // Calculate min/max for start/stop time for other streams + first_stream_rel_start_time_ = qMin(first_stream_rel_start_time_, stream_rel_start_time); + first_stream_abs_start_time_ = qMin(first_stream_abs_start_time_, stream_abs_start_time); + first_stream_rel_stop_time_ = qMax(first_stream_rel_stop_time_, stream_rel_stop_time); + } + streams_length_ = first_stream_rel_stop_time_ - first_stream_rel_start_time_; +} + +void RtpPlayerDialog::formatAudioRouting(QTreeWidgetItem *ti, AudioRouting audio_routing) +{ + ti->setText(channel_col_, tr(audio_routing.formatAudioRoutingToString())); +} + +bool RtpPlayerDialog::isStereoAvailable() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QAudioDevice cur_out_device = getCurrentDeviceInfo(); + if (cur_out_device.maximumChannelCount() > 1) { + return true; + } +#else + QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo(); + foreach(int count, cur_out_device.supportedChannelCounts()) { + if (count > 1) { + return true; + } + } +#endif + + return false; +} + +void RtpPlayerDialog::invertSelection() +{ + block_redraw_ = true; + ui->streamTreeWidget->blockSignals(true); + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + ti->setSelected(!ti->isSelected()); + } + ui->streamTreeWidget->blockSignals(false); + block_redraw_ = false; + ui->audioPlot->replot(); + updateHintLabel(); +} + +void RtpPlayerDialog::on_actionSelectAll_triggered() +{ + ui->streamTreeWidget->selectAll(); + updateHintLabel(); +} + +void RtpPlayerDialog::on_actionSelectInvert_triggered() +{ + invertSelection(); + updateHintLabel(); +} + +void RtpPlayerDialog::on_actionSelectNone_triggered() +{ + ui->streamTreeWidget->clearSelection(); + updateHintLabel(); +} + +void RtpPlayerDialog::on_actionPlay_triggered() +{ + if (ui->playButton->isEnabled()) { + ui->playButton->animateClick(); + } else if (ui->pauseButton->isEnabled()) { + ui->pauseButton->animateClick(); + } +} + +void RtpPlayerDialog::on_actionStop_triggered() +{ + if (ui->stopButton->isEnabled()) { + ui->stopButton->animateClick(); + } +} + +qint64 RtpPlayerDialog::saveAudioHeaderAU(QFile *save_file, quint32 channels, unsigned audio_rate) +{ + uint8_t pd[4]; + int64_t nchars; + + /* https://pubs.opengroup.org/external/auformat.html */ + /* First we write the .au header. All values in the header are + * 4-byte big-endian values, so we use pntoh32() to copy them + * to a 4-byte buffer, in big-endian order, and then write out + * the buffer. */ + + /* the magic word 0x2e736e64 == .snd */ + phton32(pd, 0x2e736e64); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* header offset == 24 bytes */ + phton32(pd, 24); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* total length; it is permitted to set this to 0xffffffff */ + phton32(pd, 0xffffffff); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* encoding format == 16-bit linear PCM */ + phton32(pd, 3); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* sample rate [Hz] */ + phton32(pd, audio_rate); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* channels */ + phton32(pd, channels); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + return save_file->pos(); +} + +qint64 RtpPlayerDialog::saveAudioHeaderWAV(QFile *save_file, quint32 channels, unsigned audio_rate, qint64 samples) +{ + uint8_t pd[4]; + int64_t nchars; + gint32 subchunk2Size; + gint32 data32; + gint16 data16; + + subchunk2Size = sizeof(SAMPLE) * channels * (gint32)samples; + + /* http://soundfile.sapp.org/doc/WaveFormat/ */ + + /* RIFF header, ChunkID 0x52494646 == RIFF */ + phton32(pd, 0x52494646); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* RIFF header, ChunkSize */ + data32 = 36 + subchunk2Size; + nchars = save_file->write((const char *)&data32, 4); + if (nchars != 4) { + return -1; + } + + /* RIFF header, Format 0x57415645 == WAVE */ + phton32(pd, 0x57415645); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* WAVE fmt header, Subchunk1ID 0x666d7420 == 'fmt ' */ + phton32(pd, 0x666d7420); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* WAVE fmt header, Subchunk1Size */ + data32 = 16; + nchars = save_file->write((const char *)&data32, 4); + if (nchars != 4) { + return -1; + } + + /* WAVE fmt header, AudioFormat 1 == PCM */ + data16 = 1; + nchars = save_file->write((const char *)&data16, 2); + if (nchars != 2) { + return -1; + } + + /* WAVE fmt header, NumChannels */ + data16 = channels; + nchars = save_file->write((const char *)&data16, 2); + if (nchars != 2) { + return -1; + } + + /* WAVE fmt header, SampleRate */ + data32 = audio_rate; + nchars = save_file->write((const char *)&data32, 4); + if (nchars != 4) { + return -1; + } + + /* WAVE fmt header, ByteRate */ + data32 = audio_rate * channels * sizeof(SAMPLE); + nchars = save_file->write((const char *)&data32, 4); + if (nchars != 4) { + return -1; + } + + /* WAVE fmt header, BlockAlign */ + data16 = channels * (gint16)sizeof(SAMPLE); + nchars = save_file->write((const char *)&data16, 2); + if (nchars != 2) { + return -1; + } + + /* WAVE fmt header, BitsPerSample */ + data16 = (gint16)sizeof(SAMPLE) * 8; + nchars = save_file->write((const char *)&data16, 2); + if (nchars != 2) { + return -1; + } + + /* WAVE data header, Subchunk2ID 0x64617461 == 'data' */ + phton32(pd, 0x64617461); + nchars = save_file->write((const char *)pd, 4); + if (nchars != 4) { + return -1; + } + + /* WAVE data header, Subchunk2Size */ + data32 = subchunk2Size; + nchars = save_file->write((const char *)&data32, 4); + if (nchars != 4) { + return -1; + } + + /* Now we are ready for saving data */ + + return save_file->pos(); +} + +bool RtpPlayerDialog::writeAudioSilenceSamples(QFile *out_file, qint64 samples, int stream_count) +{ + uint8_t pd[2]; + + phton16(pd, 0x0000); + for(int s=0; s < stream_count; s++) { + for(qint64 i=0; i < samples; i++) { + if (sizeof(SAMPLE) != out_file->write((char *)&pd, sizeof(SAMPLE))) { + return false; + } + } + } + + return true; +} + +bool RtpPlayerDialog::writeAudioStreamsSamples(QFile *out_file, QVector streams, bool swap_bytes) +{ + SAMPLE sample; + uint8_t pd[2]; + + // Did we read something in last cycle? + bool read = true; + + while (read) { + read = false; + // Loop over all streams, read one sample from each, write to output + foreach(RtpAudioStream *audio_stream, streams) { + if (sizeof(sample) == audio_stream->readSample(&sample)) { + if (swap_bytes) { + // same as phton16(), but more clear in compare + // to else branch + pd[0] = (guint8)(sample >> 8); + pd[1] = (guint8)(sample >> 0); + } else { + // just copy + pd[1] = (guint8)(sample >> 8); + pd[0] = (guint8)(sample >> 0); + } + read = true; + } else { + // for 0x0000 doesn't matter on order + phton16(pd, 0x0000); + } + if (sizeof(sample) != out_file->write((char *)&pd, sizeof(sample))) { + return false; + } + } + } + + return true; +} + +save_audio_t RtpPlayerDialog::selectFileAudioFormatAndName(QString *file_path) +{ + QString ext_filter = ""; + QString ext_filter_wav = tr("WAV (*.wav)"); + QString ext_filter_au = tr("Sun Audio (*.au)"); + ext_filter.append(ext_filter_wav); + ext_filter.append(";;"); + ext_filter.append(ext_filter_au); + + QString sel_filter; + *file_path = WiresharkFileDialog::getSaveFileName( + this, tr("Save audio"), mainApp->openDialogInitialDir().absoluteFilePath(""), + ext_filter, &sel_filter); + + if (file_path->isEmpty()) return save_audio_none; + + save_audio_t save_format = save_audio_none; + if (0 == QString::compare(sel_filter, ext_filter_au)) { + save_format = save_audio_au; + } else if (0 == QString::compare(sel_filter, ext_filter_wav)) { + save_format = save_audio_wav; + } + + return save_format; +} + +save_payload_t RtpPlayerDialog::selectFilePayloadFormatAndName(QString *file_path) +{ + QString ext_filter = ""; + QString ext_filter_raw = tr("Raw (*.raw)"); + ext_filter.append(ext_filter_raw); + + QString sel_filter; + *file_path = WiresharkFileDialog::getSaveFileName( + this, tr("Save payload"), mainApp->openDialogInitialDir().absoluteFilePath(""), + ext_filter, &sel_filter); + + if (file_path->isEmpty()) return save_payload_none; + + save_payload_t save_format = save_payload_none; + if (0 == QString::compare(sel_filter, ext_filter_raw)) { + save_format = save_payload_data; + } + + return save_format; +} + +QVectorRtpPlayerDialog::getSelectedRtpStreamIDs() +{ + QList items = ui->streamTreeWidget->selectedItems(); + QVector ids; + + if (items.count() > 0) { + foreach(QTreeWidgetItem *ti, items) { + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (audio_stream) { + ids << audio_stream->getID(); + } + } + } + + return ids; +} + +QVectorRtpPlayerDialog::getSelectedAudibleNonmutedAudioStreams() +{ + QList items = ui->streamTreeWidget->selectedItems(); + QVector streams; + + if (items.count() > 0) { + foreach(QTreeWidgetItem *ti, items) { + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + // Ignore muted streams and streams with no audio + if (audio_stream && + !audio_stream->getAudioRouting().isMuted() && + (audio_stream->sampleRate()>0) + ) { + streams << audio_stream; + } + } + } + + return streams; +} + +void RtpPlayerDialog::saveAudio(save_mode_t save_mode) +{ + qint64 minSilenceSamples; + qint64 startSample; + qint64 lead_silence_samples; + qint64 maxSample; + QString path; + QVectorstreams; + + streams = getSelectedAudibleNonmutedAudioStreams(); + if (streams.count() < 1) { + QMessageBox::warning(this, tr("Warning"), tr("No stream selected or none of selected streams provide audio")); + return; + } + + unsigned save_audio_rate = streams[0]->playRate(); + // Check whether all streams use same audio rate + foreach(RtpAudioStream *audio_stream, streams) { + if (save_audio_rate != audio_stream->playRate()) { + QMessageBox::warning(this, tr("Error"), tr("All selected streams must use same play rate. Manual set of Output Audio Rate might help.")); + return; + } + } + + save_audio_t format = selectFileAudioFormatAndName(&path); + if (format == save_audio_none) return; + + // Use start silence and length of first stream + minSilenceSamples = streams[0]->getLeadSilenceSamples(); + maxSample = streams[0]->getTotalSamples(); + // Find shortest start silence and longest stream + foreach(RtpAudioStream *audio_stream, streams) { + if (minSilenceSamples > audio_stream->getLeadSilenceSamples()) { + minSilenceSamples = audio_stream->getLeadSilenceSamples(); + } + if (maxSample < audio_stream->getTotalSamples()) { + maxSample = audio_stream->getTotalSamples(); + } + } + + switch (save_mode) { + case save_mode_from_cursor: + if (ui->todCheckBox->isChecked()) { + startSample = start_marker_time_ * save_audio_rate; + } else { + startSample = (start_marker_time_ - first_stream_rel_start_time_) * save_audio_rate; + } + lead_silence_samples = 0; + break; + case save_mode_sync_stream: + // Skip start of first stream, no lead silence + startSample = minSilenceSamples; + lead_silence_samples = 0; + break; + case save_mode_sync_file: + default: + // Full first stream, lead silence + startSample = 0; + lead_silence_samples = first_stream_rel_start_time_ * save_audio_rate; + break; + } + + QVectortemp = QVector(streams); + + // Remove streams shorter than startSample and + // seek to correct start for longer ones + foreach(RtpAudioStream *audio_stream, temp) { + if (startSample > audio_stream->getTotalSamples()) { + streams.removeAll(audio_stream); + } else { + audio_stream->seekSample(startSample); + } + } + + if (streams.count() < 1) { + QMessageBox::warning(this, tr("Warning"), tr("No streams are suitable for save")); + return; + } + + QFile file(path); + file.open(QIODevice::WriteOnly); + + if (!file.isOpen() || (file.error() != QFile::NoError)) { + QMessageBox::warning(this, tr("Warning"), tr("Save failed!")); + } else { + switch (format) { + case save_audio_au: + if (-1 == saveAudioHeaderAU(&file, static_cast(streams.count()), save_audio_rate)) { + QMessageBox::warning(this, tr("Error"), tr("Can't write header of AU file")); + return; + } + if (lead_silence_samples > 0) { + if (!writeAudioSilenceSamples(&file, lead_silence_samples, static_cast(streams.count()))) { + QMessageBox::warning(this, tr("Warning"), tr("Save failed!")); + } + } + if (!writeAudioStreamsSamples(&file, streams, true)) { + QMessageBox::warning(this, tr("Warning"), tr("Save failed!")); + } + break; + case save_audio_wav: + if (-1 == saveAudioHeaderWAV(&file, static_cast(streams.count()), save_audio_rate, (maxSample - startSample) + lead_silence_samples)) { + QMessageBox::warning(this, tr("Error"), tr("Can't write header of WAV file")); + return; + } + if (lead_silence_samples > 0) { + if (!writeAudioSilenceSamples(&file, lead_silence_samples, static_cast(streams.count()))) { + QMessageBox::warning(this, tr("Warning"), tr("Save failed!")); + } + } + if (!writeAudioStreamsSamples(&file, streams, false)) { + QMessageBox::warning(this, tr("Warning"), tr("Save failed!")); + } + break; + case save_audio_none: + break; + } + } + + file.close(); +} + +void RtpPlayerDialog::savePayload() +{ + QString path; + QList items; + RtpAudioStream *audio_stream = NULL; + + items = ui->streamTreeWidget->selectedItems(); + foreach(QTreeWidgetItem *ti, items) { + audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + if (audio_stream) + break; + } + if (items.count() != 1 || !audio_stream) { + QMessageBox::warning(this, tr("Warning"), tr("Payload save works with just one audio stream.")); + return; + } + + save_payload_t format = selectFilePayloadFormatAndName(&path); + if (format == save_payload_none) return; + + QFile file(path); + file.open(QIODevice::WriteOnly); + + if (!file.isOpen() || (file.error() != QFile::NoError)) { + QMessageBox::warning(this, tr("Warning"), tr("Save failed!")); + } else if (!audio_stream->savePayload(&file)) { + QMessageBox::warning(this, tr("Warning"), tr("Save failed!")); + } + + file.close(); +} + +void RtpPlayerDialog::on_actionSaveAudioFromCursor_triggered() +{ + saveAudio(save_mode_from_cursor); +} + +void RtpPlayerDialog::on_actionSaveAudioSyncStream_triggered() +{ + saveAudio(save_mode_sync_stream); +} + +void RtpPlayerDialog::on_actionSaveAudioSyncFile_triggered() +{ + saveAudio(save_mode_sync_file); +} + +void RtpPlayerDialog::on_actionSavePayload_triggered() +{ + savePayload(); +} + +void RtpPlayerDialog::selectInaudible(bool select) +{ + block_redraw_ = true; + ui->streamTreeWidget->blockSignals(true); + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + // Streams with no audio + if (audio_stream && (audio_stream->sampleRate()==0)) { + ti->setSelected(select); + } + } + ui->streamTreeWidget->blockSignals(false); + block_redraw_ = false; + ui->audioPlot->replot(); + updateHintLabel(); +} + +void RtpPlayerDialog::on_actionSelectInaudible_triggered() +{ + selectInaudible(true); +} + +void RtpPlayerDialog::on_actionDeselectInaudible_triggered() +{ + selectInaudible(false); +} + +void RtpPlayerDialog::on_actionPrepareFilter_triggered() +{ + QVector ids = getSelectedRtpStreamIDs(); + QString filter = make_filter_based_on_rtpstream_id(ids); + if (filter.length() > 0) { + emit updateFilter(filter); + } +} + +void RtpPlayerDialog::rtpAnalysisReplace() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpAnalysisDialogReplaceRtpStreams(getSelectedRtpStreamIDs()); +} + +void RtpPlayerDialog::rtpAnalysisAdd() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpAnalysisDialogAddRtpStreams(getSelectedRtpStreamIDs()); +} + +void RtpPlayerDialog::rtpAnalysisRemove() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpAnalysisDialogRemoveRtpStreams(getSelectedRtpStreamIDs()); +} + +void RtpPlayerDialog::on_actionReadCapture_triggered() +{ +#ifdef QT_MULTIMEDIA_LIB + QTimer::singleShot(0, this, SLOT(retapPackets())); +#endif +} + +// _U_ is used for case w have no LIBPCAP +void RtpPlayerDialog::captureEvent(CaptureEvent e _U_) +{ +#ifdef HAVE_LIBPCAP + bool new_read_capture_enabled = false; + bool found = false; + + if ((e.captureContext() & CaptureEvent::Capture) && + (e.eventType() == CaptureEvent::Prepared) + ) { + new_read_capture_enabled = true; + found = true; + } else if ((e.captureContext() & CaptureEvent::Capture) && + (e.eventType() == CaptureEvent::Finished) + ) { + new_read_capture_enabled = false; + found = true; + } + + if (found) { + bool retap = false; + if (read_capture_enabled_ && !new_read_capture_enabled) { + // Capturing ended, automatically refresh data + retap = true; + } + read_capture_enabled_ = new_read_capture_enabled; + updateWidgets(); + if (retap) { + QTimer::singleShot(0, this, SLOT(retapPackets())); + } + } +#endif +} + +#endif // QT_MULTIMEDIA_LIB diff --git a/ui/qt/rtp_player_dialog.h b/ui/qt/rtp_player_dialog.h new file mode 100644 index 00000000..5fba67db --- /dev/null +++ b/ui/qt/rtp_player_dialog.h @@ -0,0 +1,314 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTP_PLAYER_DIALOG_H +#define RTP_PLAYER_DIALOG_H + +#include "config.h" + +#include +#include + +#include "ui/rtp_stream.h" + +#include "wireshark_dialog.h" +#include "rtp_audio_stream.h" + +#include +#include +#include +#include +#include +#include + +#ifdef QT_MULTIMEDIA_LIB +# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +# include +# else +# include +# endif +#endif + +namespace Ui { +class RtpPlayerDialog; +} + +class QCPItemStraightLine; +class QDialogButtonBox; +class QMenu; +class RtpAudioStream; +class QCPAxisTicker; +class QCPAxisTickerDateTime; + +typedef enum { + save_audio_none, + save_audio_au, + save_audio_wav +} save_audio_t; + +typedef enum { + save_payload_none, + save_payload_data +} save_payload_t; + +typedef enum { + save_mode_from_cursor, + save_mode_sync_stream, + save_mode_sync_file +} save_mode_t; + +// Singleton by https://refactoring.guru/design-patterns/singleton/cpp/example#example-1 +class RtpPlayerDialog : public WiresharkDialog +{ + Q_OBJECT +#ifdef QT_MULTIMEDIA_LIB + Q_PROPERTY(QString currentOutputDeviceName READ currentOutputDeviceName) +#endif + +public: + /** + * Returns singleton + */ + static RtpPlayerDialog *openRtpPlayerDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list, bool capture_running); + + /** + * Should not be clonnable and assignable + */ + RtpPlayerDialog(RtpPlayerDialog &other) = delete; + void operator=(const RtpPlayerDialog &) = delete; + + /** + * @brief Common routine to add a "Play call" button to a QDialogButtonBox. + * @param button_box Caller's QDialogButtonBox. + * @return The new "Play call" button. + */ + static QToolButton *addPlayerButton(QDialogButtonBox *button_box, QDialog *dialog); + +#ifdef QT_MULTIMEDIA_LIB + void accept(); + void reject(); + + void setMarkers(); + + /** Replace/Add/Remove an RTP streams to play. + * Requires array of rtpstream_info_t. + * Each item must have filled items: src_addr, src_port, dest_addr, + * dest_port, ssrc, packet_count, setup_frame_number, and start_rel_time. + * + * @param stream_ids struct with rtpstream info + */ + void replaceRtpStreams(QVector stream_ids); + void addRtpStreams(QVector stream_ids); + void removeRtpStreams(QVector stream_ids); + +signals: + // Tells the packet list to redraw. An alternative might be to add a + // cf_packet_marked callback to file.[ch] but that's synchronous and + // might incur too much overhead. + void packetsMarked(); + void updateFilter(QString filter, bool force = false); + void goToPacket(int packet_num); + void rtpAnalysisDialogReplaceRtpStreams(QVector stream_infos); + void rtpAnalysisDialogAddRtpStreams(QVector stream_infos); + void rtpAnalysisDialogRemoveRtpStreams(QVector stream_infos); + +public slots: + void rtpAnalysisReplace(); + void rtpAnalysisAdd(); + void rtpAnalysisRemove(); + +#endif +protected: + explicit RtpPlayerDialog(QWidget &parent, CaptureFile &cf, bool capture_running); +#ifdef QT_MULTIMEDIA_LIB + ~RtpPlayerDialog(); + + virtual void showEvent(QShowEvent *); + void contextMenuEvent(QContextMenuEvent *event); + bool eventFilter(QObject *obj, QEvent *event); + +private slots: + /** Retap the capture file, reading RTP packets that match the + * streams added using ::addRtpStream. + */ + void retapPackets(); + void captureEvent(CaptureEvent e); + /** Clear, decode, and redraw each stream. + */ + void rescanPackets(bool rescale_axes = false); + void createPlot(bool rescale_axes = false); + void updateWidgets(); + void itemEntered(QTreeWidgetItem *item, int column); + void mouseMovePlot(QMouseEvent *event); + void graphClicked(QMouseEvent *event); + void graphDoubleClicked(QMouseEvent *event); + void plotClicked(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event); + void updateHintLabel(); + void resetXAxis(); + void updateGraphs(); + void playFinished(RtpAudioStream *stream, QAudio::Error error); + + void setPlayPosition(double secs); + void setPlaybackError(const QString playback_error); + void changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting new_audio_routing); + void changeAudioRouting(AudioRouting new_audio_routing); + void invertAudioMutingOnItem(QTreeWidgetItem *ti); + void on_playButton_clicked(); + void on_pauseButton_clicked(); + void on_stopButton_clicked(); + void on_actionReset_triggered(); + void on_actionZoomIn_triggered(); + void on_actionZoomOut_triggered(); + void on_actionMoveLeft10_triggered(); + void on_actionMoveRight10_triggered(); + void on_actionMoveLeft1_triggered(); + void on_actionMoveRight1_triggered(); + void on_actionGoToPacket_triggered(); + void on_actionGoToSetupPacketPlot_triggered(); + void on_actionGoToSetupPacketTree_triggered(); + void on_actionRemoveStream_triggered(); + void on_actionAudioRoutingP_triggered(); + void on_actionAudioRoutingL_triggered(); + void on_actionAudioRoutingLR_triggered(); + void on_actionAudioRoutingR_triggered(); + void on_actionAudioRoutingMute_triggered(); + void on_actionAudioRoutingUnmute_triggered(); + void on_actionAudioRoutingMuteInvert_triggered(); + void on_streamTreeWidget_itemSelectionChanged(); + void on_streamTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, const int column); + void on_outputDeviceComboBox_currentTextChanged(const QString &); + void on_outputAudioRate_currentTextChanged(const QString &); + void on_jitterSpinBox_valueChanged(double); + void on_timingComboBox_currentIndexChanged(int); + void on_todCheckBox_toggled(bool checked); + void on_buttonBox_helpRequested(); + void on_actionSelectAll_triggered(); + void on_actionSelectInvert_triggered(); + void on_actionSelectNone_triggered(); + void outputNotify(); + void on_actionPlay_triggered(); + void on_actionStop_triggered(); + void on_actionSaveAudioFromCursor_triggered(); + void on_actionSaveAudioSyncStream_triggered(); + void on_actionSaveAudioSyncFile_triggered(); + void on_actionSavePayload_triggered(); + void on_actionSelectInaudible_triggered(); + void on_actionDeselectInaudible_triggered(); + void on_actionPrepareFilter_triggered(); + void on_actionReadCapture_triggered(); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + void sinkStateChanged(); +#endif + +#endif +private: + static RtpPlayerDialog *pinstance_; + static std::mutex init_mutex_; + static std::mutex run_mutex_; + +#ifdef QT_MULTIMEDIA_LIB + Ui::RtpPlayerDialog *ui; + QMenu *graph_ctx_menu_; + QMenu *list_ctx_menu_; + double first_stream_rel_start_time_; // Relative start time of first stream + double first_stream_abs_start_time_; // Absolute start time of first stream + double first_stream_rel_stop_time_; // Relative end time of first stream (ued for streams_length_ calculation + double streams_length_; // Difference between start of first stream and end of last stream + double start_marker_time_; // Always relative time to start of the capture + double start_marker_time_play_; // Copy when play started + QCPItemStraightLine *cur_play_pos_; + QCPItemStraightLine *start_marker_pos_; + QString playback_error_; + QSharedPointer number_ticker_; + QSharedPointer datetime_ticker_; + bool stereo_available_; + QList playing_streams_; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QAudioSink *marker_stream_; + QTimer notify_timer_; + qint64 notify_timer_start_diff_; // Used to shift play cursor to correct place +#else + QAudioOutput *marker_stream_; +#endif + quint32 marker_stream_requested_out_rate_; + QTreeWidgetItem *last_ti_; + bool listener_removed_; + QPushButton *read_btn_; + QToolButton *inaudible_btn_; + QToolButton *analyze_btn_; + QPushButton *prepare_btn_; + QPushButton *export_btn_; + QMultiHash stream_hash_; + bool block_redraw_; + int lock_ui_; + bool read_capture_enabled_; + double silence_skipped_time_; + +// const QString streamKey(const rtpstream_info_t *rtpstream); +// const QString streamKey(const packet_info *pinfo, const struct _rtp_info *rtpinfo); + + // Tap callbacks +// static void tapReset(void *tapinfo_ptr); + static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr, tap_flags_t flags); + static void tapDraw(void *tapinfo_ptr); + + void addPacket(packet_info *pinfo, const struct _rtp_info *rtpinfo); + void zoomXAxis(bool in); + void panXAxis(int x_pixels); + const QString getFormatedTime(double f_time); + const QString getFormatedHoveredTime(); + int getHoveredPacket(); + QString currentOutputDeviceName(); + double getStartPlayMarker(); + void drawStartPlayMarker(); + void setStartPlayMarker(double new_time); + void updateStartStopTime(rtpstream_info_t *rtpstream, bool is_first); + void formatAudioRouting(QTreeWidgetItem *ti, AudioRouting audio_routing); + bool isStereoAvailable(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QAudioSink *getSilenceAudioOutput(); + QAudioDevice getCurrentDeviceInfo(); +#else + QAudioOutput *getSilenceAudioOutput(); + QAudioDeviceInfo getCurrentDeviceInfo(); +#endif + QTreeWidgetItem *findItemByCoords(QPoint point); + QTreeWidgetItem *findItem(QCPAbstractPlottable *plottable); + void handleItemHighlight(QTreeWidgetItem *ti, bool scroll); + void highlightItem(QTreeWidgetItem *ti, bool highlight); + void invertSelection(); + void handleGoToSetupPacket(QTreeWidgetItem *ti); + void addSingleRtpStream(rtpstream_id_t *id); + void removeRow(QTreeWidgetItem *ti); + void fillAudioRateMenu(); + void cleanupMarkerStream(); + + qint64 saveAudioHeaderAU(QFile *save_file, quint32 channels, unsigned audio_rate); + qint64 saveAudioHeaderWAV(QFile *save_file, quint32 channels, unsigned audio_rate, qint64 samples); + bool writeAudioSilenceSamples(QFile *out_file, qint64 samples, int stream_count); + bool writeAudioStreamsSamples(QFile *out_file, QVector streams, bool swap_bytes); + save_audio_t selectFileAudioFormatAndName(QString *file_path); + save_payload_t selectFilePayloadFormatAndName(QString *file_path); + QVectorgetSelectedAudibleNonmutedAudioStreams(); + void saveAudio(save_mode_t save_mode); + void savePayload(); + void lockUI(); + void unlockUI(); + void selectInaudible(bool select); + QVectorgetSelectedRtpStreamIDs(); + void fillTappedColumns(); + +#else // QT_MULTIMEDIA_LIB +private: + Ui::RtpPlayerDialog *ui; +#endif // QT_MULTIMEDIA_LIB +}; + +#endif // RTP_PLAYER_DIALOG_H diff --git a/ui/qt/rtp_player_dialog.ui b/ui/qt/rtp_player_dialog.ui new file mode 100644 index 00000000..b1a00dba --- /dev/null +++ b/ui/qt/rtp_player_dialog.ui @@ -0,0 +1,837 @@ + + + RtpPlayerDialog + + + + 0 + 0 + 750 + 600 + + + + RTP Player + + + + + + Qt::Vertical + + + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + false + + + true + + + false + + + + Play + + + Double click to change audio routing + + + + + Source Address + + + + + Source Port + + + + + Destination Address + + + + + Destination Port + + + + + SSRC + + + + + Setup Frame + + + + + Packets + + + + + Time Span (s) + + + + + SR (Hz) + + + Sample rate of codec + + + + + PR (Hz) + + + Play rate of decoded audio (depends e. g. on selected sound card) + + + + + Payloads + + + + + + + + + <small><i>No audio</i></small> + + + true + + + + + + + + + + + + Start playback of all unmuted streams + + + + + + + + + + Pause/unpause playback + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + Stop playback + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + Enable/disable skipping of silence during playback + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Min silence: + + + + + + + Minimum silence duration to skip in seconds + + + 0 + + + 1.000000000000000 + + + 1.000000000000000 + + + 2.000000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Output Device: + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Output Audio Rate: + + + + + + + + + + + + + + Jitter Buffer: + + + + + + + The simulated jitter buffer in milliseconds. + + + 0 + + + 500.000000000000000 + + + 5.000000000000000 + + + 50.000000000000000 + + + + + + + Qt::Horizontal + + + + 20 + 10 + + + + + + + + Playback Timing: + + + + + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + + + + Jitter Buffer + + + + + RTP Timestamp + + + + + Uninterrupted Mode + + + + + + + + Qt::Horizontal + + + + 20 + 10 + + + + + + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + + + Time of Day + + + + + + + Qt::Horizontal + + + + 48 + 24 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + &Export + + + Export audio of all unmuted selected channels or export payload of one channel. + + + + + &Export + + + true + + + + + + + + + + From &cursor + + + Save audio data started at the cursor + + + + + &Stream Synchronized Audio + + + Save audio data synchronized to start of the earliest stream. + + + + + &File Synchronized Audio + + + Save audio data synchronized to start of the capture file. + + + + + &Payload + + + Save RTP payload of selected stream. + + + + + Reset Graph + + + Reset the graph to its initial state. + + + 0 + + + + + Zoom In + + + Zoom In + + + + + + + + + Zoom Out + + + Zoom Out + + + - + + + + + Move Left 10 Pixels + + + Move Left 10 Pixels + + + Left + + + + + Move Right 10 Pixels + + + Move Right 10 Pixels + + + Right + + + + + Move Left 1 Pixels + + + Move Left 1 Pixels + + + Shift+Left + + + + + Move Right 1 Pixels + + + Move Right 1 Pixels + + + Shift+Right + + + + + Go To Packet Under Cursor + + + Go to packet currently under the cursor + + + G + + + + + Go To Setup Packet + + + Go to setup packet of stream currently under the cursor + + + Shift+G + + + + + Go To Setup Packet + + + Go to setup packet of stream currently under the cursor + + + Shift+G + + + + + Audio Routing + + + true + + + + + + + + + + + + Mute + + + Mute selected streams + + + M + + + + + Unmute + + + Unmute selected streams + + + Shift+M + + + + + Invert Muting + + + Invert muting of selected streams + + + Ctrl+M + + + + + Play + + + Play the stream + + + + + To Left + + + Route audio to left channel of selected streams + + + + + Left + Right + + + Route audio to left and right channel of selected streams + + + + + To Right + + + Route audio to right channel of selected streams + + + + + Remove Streams + + + Remove selected streams from the list + + + Delete + + + + + Select + + + true + + + + + + + + All + + + Select all + + + Ctrl+A + + + + + None + + + Clear selection + + + Ctrl+Shift+A + + + + + Invert + + + Invert selection + + + Ctrl+I + + + + + Play/Pause + + + Start playing or pause playing + + + P + + + + + Stop + + + Stop playing + + + S + + + + + I&naudible streams + + + Select/Deselect inaudible streams + + + + + Inaudible streams + + + true + + + + + + + &Select + + + Select inaudible streams + + + N + + + + + &Deselect + + + Deselect inaudible streams + + + Shift+N + + + + + Prepare &Filter + + + Prepare a filter matching the selected stream(s). + + + + + R&efresh streams + + + Read captured packets from capture in progress to player + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+
+ + + + buttonBox + accepted() + RtpPlayerDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RtpPlayerDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/rtp_stream_dialog.cpp b/ui/qt/rtp_stream_dialog.cpp new file mode 100644 index 00000000..6b28a837 --- /dev/null +++ b/ui/qt/rtp_stream_dialog.cpp @@ -0,0 +1,1064 @@ +/* rtp_stream_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "rtp_stream_dialog.h" +#include + +#include "file.h" + +#include "epan/addr_resolv.h" +#include + +#include + +#include +#include "rtp_analysis_dialog.h" +#include "progress_frame.h" +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * @file RTP stream dialog + * + * Displays a list of RTP streams with the following information: + * - UDP 4-tuple + * - SSRC + * - Payload type + * - Stats: Packets, lost, max delta, max jitter, mean jitter + * - Problems + * + * Finds reverse streams + * "Save As" rtpdump + * Mark packets + * Go to the setup frame + * Prepare filter + * Copy As CSV and YAML + * Analyze + */ + +// To do: +// - Add more statistics to the hint text (e.g. lost packets). +// - Add more statistics to the main list (e.g. stream duration) + +const int src_addr_col_ = 0; +const int src_port_col_ = 1; +const int dst_addr_col_ = 2; +const int dst_port_col_ = 3; +const int ssrc_col_ = 4; +const int start_time_col_ = 5; +const int duration_col_ = 6; +const int payload_col_ = 7; +const int packets_col_ = 8; +const int lost_col_ = 9; +const int min_delta_col_ = 10; +const int mean_delta_col_ = 11; +const int max_delta_col_ = 12; +const int min_jitter_col_ = 13; +const int mean_jitter_col_ = 14; +const int max_jitter_col_ = 15; +const int status_col_ = 16; +const int ssrc_fmt_col_ = 17; +const int lost_perc_col_ = 18; + +enum { rtp_stream_type_ = 1000 }; + +bool operator==(rtpstream_id_t const& a, rtpstream_id_t const& b); + +class RtpStreamTreeWidgetItem : public QTreeWidgetItem +{ +public: + RtpStreamTreeWidgetItem(QTreeWidget *tree, rtpstream_info_t *stream_info) : + QTreeWidgetItem(tree, rtp_stream_type_), + stream_info_(stream_info), + tod_(0) + { + drawData(); + } + + rtpstream_info_t *streamInfo() const { return stream_info_; } + + void drawData() { + rtpstream_info_calc_t calc; + + if (!stream_info_) { + return; + } + rtpstream_info_calculate(stream_info_, &calc); + + setText(src_addr_col_, calc.src_addr_str); + setText(src_port_col_, QString::number(calc.src_port)); + setText(dst_addr_col_, calc.dst_addr_str); + setText(dst_port_col_, QString::number(calc.dst_port)); + setText(ssrc_col_, QString("0x%1").arg(calc.ssrc, 0, 16)); + if (tod_) { + QDateTime abs_dt = QDateTime::fromMSecsSinceEpoch(nstime_to_msec(&stream_info_->start_fd->abs_ts)); + setText(start_time_col_, QString("%1") + .arg(abs_dt.toString("yyyy-MM-dd hh:mm:ss.zzz"))); + } else { + setText(start_time_col_, QString::number(calc.start_time_ms, 'f', 6)); + } + setText(duration_col_, QString::number(calc.duration_ms, 'f', prefs.gui_decimal_places1)); + setText(payload_col_, calc.all_payload_type_names); + setText(packets_col_, QString::number(calc.packet_count)); + setText(lost_col_, QObject::tr("%1 (%L2%)").arg(calc.lost_num).arg(QString::number(calc.lost_perc, 'f', 1))); + setText(min_delta_col_, QString::number(calc.min_delta, 'f', prefs.gui_decimal_places3)); // This is RTP. Do we need nanoseconds? + setText(mean_delta_col_, QString::number(calc.mean_delta, 'f', prefs.gui_decimal_places3)); // This is RTP. Do we need nanoseconds? + setText(max_delta_col_, QString::number(calc.max_delta, 'f', prefs.gui_decimal_places3)); // This is RTP. Do we need nanoseconds? + setText(min_jitter_col_, QString::number(calc.min_jitter, 'f', prefs.gui_decimal_places3)); + setText(mean_jitter_col_, QString::number(calc.mean_jitter, 'f', prefs.gui_decimal_places3)); + setText(max_jitter_col_, QString::number(calc.max_jitter, 'f', prefs.gui_decimal_places3)); + + if (calc.problem) { + setText(status_col_, UTF8_BULLET); + setTextAlignment(status_col_, Qt::AlignCenter); + QColor bgColor(ColorUtils::warningBackground()); + QColor textColor(QApplication::palette().text().color()); + for (int i = 0; i < columnCount(); i++) { + QBrush bgBrush = background(i); + bgBrush.setColor(bgColor); + bgBrush.setStyle(Qt::SolidPattern); + setBackground(i, bgBrush); + QBrush fgBrush = foreground(i); + fgBrush.setColor(textColor); + fgBrush.setStyle(Qt::SolidPattern); + setForeground(i, fgBrush); + } + } + + rtpstream_info_calc_free(&calc); + } + // Return a QString, int, double, or invalid QVariant representing the raw column data. + QVariant colData(int col) const { + rtpstream_info_calc_t calc; + if (!stream_info_) { + return QVariant(); + } + + QVariant ret; + rtpstream_info_calculate(stream_info_, &calc); + + switch(col) { + case src_addr_col_: + ret = QVariant(text(col)); + break; + case src_port_col_: + ret = calc.src_port; + break; + case dst_addr_col_: + ret = text(col); + break; + case dst_port_col_: + ret = calc.dst_port; + break; + case ssrc_col_: + ret = calc.ssrc; + break; + case start_time_col_: + ret = calc.start_time_ms; + break; + case duration_col_: + ret = calc.duration_ms; + break; + case payload_col_: + ret = text(col); + break; + case packets_col_: + ret = calc.packet_count; + break; + case lost_col_: + ret = calc.lost_num; + break; + case min_delta_col_: + ret = calc.min_delta; + break; + case mean_delta_col_: + ret = calc.mean_delta; + break; + case max_delta_col_: + ret = calc.max_delta; + break; + case min_jitter_col_: + ret = calc.min_jitter; + break; + case mean_jitter_col_: + ret = calc.mean_jitter; + break; + case max_jitter_col_: + ret = calc.max_jitter; + break; + case status_col_: + ret = calc.problem ? "Problem" : ""; + break; + case ssrc_fmt_col_: + ret = QString("0x%1").arg(calc.ssrc, 0, 16); + break; + case lost_perc_col_: + ret = QString::number(calc.lost_perc, 'f', prefs.gui_decimal_places1); + break; + default: + ret = QVariant(); + break; + } + rtpstream_info_calc_free(&calc); + return ret; + } + + bool operator< (const QTreeWidgetItem &other) const + { + rtpstream_info_calc_t calc1; + rtpstream_info_calc_t calc2; + bool ret; + + if (other.type() != rtp_stream_type_) return QTreeWidgetItem::operator <(other); + const RtpStreamTreeWidgetItem &other_rstwi = dynamic_cast(other); + + switch (treeWidget()->sortColumn()) { + case src_addr_col_: + return cmp_address(&(stream_info_->id.src_addr), &(other_rstwi.stream_info_->id.src_addr)) < 0; + case src_port_col_: + return stream_info_->id.src_port < other_rstwi.stream_info_->id.src_port; + case dst_addr_col_: + return cmp_address(&(stream_info_->id.dst_addr), &(other_rstwi.stream_info_->id.dst_addr)) < 0; + case dst_port_col_: + return stream_info_->id.dst_port < other_rstwi.stream_info_->id.dst_port; + case ssrc_col_: + return stream_info_->id.ssrc < other_rstwi.stream_info_->id.ssrc; + case start_time_col_: + rtpstream_info_calculate(stream_info_, &calc1); + rtpstream_info_calculate(other_rstwi.stream_info_, &calc2); + ret = calc1.start_time_ms < calc2.start_time_ms; + rtpstream_info_calc_free(&calc1); + rtpstream_info_calc_free(&calc2); + return ret; + case duration_col_: + rtpstream_info_calculate(stream_info_, &calc1); + rtpstream_info_calculate(other_rstwi.stream_info_, &calc2); + ret = calc1.duration_ms < calc2.duration_ms; + rtpstream_info_calc_free(&calc1); + rtpstream_info_calc_free(&calc2); + return ret; + case payload_col_: + return g_strcmp0(stream_info_->all_payload_type_names, other_rstwi.stream_info_->all_payload_type_names); + case packets_col_: + return stream_info_->packet_count < other_rstwi.stream_info_->packet_count; + case lost_col_: + rtpstream_info_calculate(stream_info_, &calc1); + rtpstream_info_calculate(other_rstwi.stream_info_, &calc2); + /* XXX: Should this sort on the total number or the percentage? + * lost_num is displayed first and lost_perc in parenthesis, + * so let's use the total number. + */ + ret = calc1.lost_num < calc2.lost_num; + rtpstream_info_calc_free(&calc1); + rtpstream_info_calc_free(&calc2); + return ret; + break; + case min_delta_col_: + return stream_info_->rtp_stats.min_delta < other_rstwi.stream_info_->rtp_stats.min_delta; + case mean_delta_col_: + return stream_info_->rtp_stats.mean_delta < other_rstwi.stream_info_->rtp_stats.mean_delta; + case max_delta_col_: + return stream_info_->rtp_stats.max_delta < other_rstwi.stream_info_->rtp_stats.max_delta; + case min_jitter_col_: + return stream_info_->rtp_stats.min_jitter < other_rstwi.stream_info_->rtp_stats.min_jitter; + case mean_jitter_col_: + return stream_info_->rtp_stats.mean_jitter < other_rstwi.stream_info_->rtp_stats.mean_jitter; + case max_jitter_col_: + return stream_info_->rtp_stats.max_jitter < other_rstwi.stream_info_->rtp_stats.max_jitter; + default: + break; + } + + // Fall back to string comparison + return QTreeWidgetItem::operator <(other); + } + + void setTOD(gboolean tod) + { + tod_ = tod; + } + +private: + rtpstream_info_t *stream_info_; + gboolean tod_; +}; + + +RtpStreamDialog *RtpStreamDialog::pinstance_{nullptr}; +std::mutex RtpStreamDialog::mutex_; + +RtpStreamDialog *RtpStreamDialog::openRtpStreamDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list) +{ + std::lock_guard lock(mutex_); + if (pinstance_ == nullptr) + { + pinstance_ = new RtpStreamDialog(parent, cf); + connect(pinstance_, SIGNAL(packetsMarked()), + packet_list, SLOT(redrawVisiblePackets())); + connect(pinstance_, SIGNAL(goToPacket(int)), + packet_list, SLOT(goToPacket(int))); + } + return pinstance_; +} + +RtpStreamDialog::RtpStreamDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::RtpStreamDialog), + need_redraw_(false) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 2 / 3); + setWindowSubtitle(tr("RTP Streams")); + ui->streamTreeWidget->installEventFilter(this); + + ctx_menu_.addMenu(ui->menuSelect); + ctx_menu_.addMenu(ui->menuFindReverse); + ctx_menu_.addAction(ui->actionGoToSetup); + ctx_menu_.addAction(ui->actionMarkPackets); + ctx_menu_.addAction(ui->actionPrepareFilter); + ctx_menu_.addAction(ui->actionExportAsRtpDump); + ctx_menu_.addAction(ui->actionCopyAsCsv); + ctx_menu_.addAction(ui->actionCopyAsYaml); + ctx_menu_.addAction(ui->actionAnalyze); + set_action_shortcuts_visible_in_context_menu(ctx_menu_.actions()); + + ui->streamTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + ui->streamTreeWidget->header()->setSortIndicator(0, Qt::AscendingOrder); + connect(ui->streamTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showStreamMenu(QPoint))); + + find_reverse_button_ = new QToolButton(); + ui->buttonBox->addButton(find_reverse_button_, QDialogButtonBox::ActionRole); + find_reverse_button_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + find_reverse_button_->setPopupMode(QToolButton::MenuButtonPopup); + + connect(ui->actionFindReverse, &QAction::triggered, this, &RtpStreamDialog::on_actionFindReverseNormal_triggered); + find_reverse_button_->setDefaultAction(ui->actionFindReverse); + // Overrides text striping of shortcut undercode in QAction + find_reverse_button_->setText(ui->actionFindReverseNormal->text()); + find_reverse_button_->setMenu(ui->menuFindReverse); + + analyze_button_ = RtpAnalysisDialog::addAnalyzeButton(ui->buttonBox, this); + prepare_button_ = ui->buttonBox->addButton(ui->actionPrepareFilter->text(), QDialogButtonBox::ActionRole); + prepare_button_->setToolTip(ui->actionPrepareFilter->toolTip()); + connect(prepare_button_, &QPushButton::pressed, this, &RtpStreamDialog::on_actionPrepareFilter_triggered); + player_button_ = RtpPlayerDialog::addPlayerButton(ui->buttonBox, this); + copy_button_ = ui->buttonBox->addButton(ui->actionCopyButton->text(), QDialogButtonBox::ActionRole); + copy_button_->setToolTip(ui->actionCopyButton->toolTip()); + export_button_ = ui->buttonBox->addButton(ui->actionExportAsRtpDump->text(), QDialogButtonBox::ActionRole); + export_button_->setToolTip(ui->actionExportAsRtpDump->toolTip()); + connect(export_button_, &QPushButton::pressed, this, &RtpStreamDialog::on_actionExportAsRtpDump_triggered); + + QMenu *copy_menu = new QMenu(copy_button_); + QAction *ca; + ca = copy_menu->addAction(tr("as CSV")); + ca->setToolTip(ui->actionCopyAsCsv->toolTip()); + connect(ca, &QAction::triggered, this, &RtpStreamDialog::on_actionCopyAsCsv_triggered); + ca = copy_menu->addAction(tr("as YAML")); + ca->setToolTip(ui->actionCopyAsYaml->toolTip()); + connect(ca, &QAction::triggered, this, &RtpStreamDialog::on_actionCopyAsYaml_triggered); + copy_button_->setMenu(copy_menu); + connect(&cap_file_, SIGNAL(captureEvent(CaptureEvent)), + this, SLOT(captureEvent(CaptureEvent))); + + /* Register the tap listener */ + memset(&tapinfo_, 0, sizeof(rtpstream_tapinfo_t)); + tapinfo_.tap_reset = tapReset; + tapinfo_.tap_draw = tapDraw; + tapinfo_.tap_mark_packet = tapMarkPacket; + tapinfo_.tap_data = this; + tapinfo_.mode = TAP_ANALYSE; + + register_tap_listener_rtpstream(&tapinfo_, NULL, show_tap_registration_error); + if (cap_file_.isValid() && cap_file_.capFile()->dfilter) { + // Activate display filter checking + tapinfo_.apply_display_filter = true; + ui->displayFilterCheckBox->setChecked(true); + } + + connect(ui->displayFilterCheckBox, &QCheckBox::toggled, + this, &RtpStreamDialog::displayFilterCheckBoxToggled); + connect(this, SIGNAL(updateFilter(QString, bool)), + &parent, SLOT(filterPackets(QString, bool))); + connect(&parent, SIGNAL(displayFilterSuccess(bool)), + this, SLOT(displayFilterSuccess(bool))); + connect(this, SIGNAL(rtpPlayerDialogReplaceRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogReplaceRtpStreams(QVector))); + connect(this, SIGNAL(rtpPlayerDialogAddRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogAddRtpStreams(QVector))); + connect(this, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector))); + connect(this, SIGNAL(rtpAnalysisDialogReplaceRtpStreams(QVector)), + &parent, SLOT(rtpAnalysisDialogReplaceRtpStreams(QVector))); + connect(this, SIGNAL(rtpAnalysisDialogAddRtpStreams(QVector)), + &parent, SLOT(rtpAnalysisDialogAddRtpStreams(QVector))); + connect(this, SIGNAL(rtpAnalysisDialogRemoveRtpStreams(QVector)), + &parent, SLOT(rtpAnalysisDialogRemoveRtpStreams(QVector))); + + ProgressFrame::addToButtonBox(ui->buttonBox, &parent); + + updateWidgets(); + + if (cap_file_.isValid()) { + cap_file_.delayedRetapPackets(); + } +} + +RtpStreamDialog::~RtpStreamDialog() +{ + std::lock_guard lock(mutex_); + freeLastSelected(); + delete ui; + rtpstream_reset(&tapinfo_); + remove_tap_listener_rtpstream(&tapinfo_); + pinstance_ = nullptr; +} + +void RtpStreamDialog::setRtpStreamSelection(rtpstream_id_t *id, bool state) +{ + QTreeWidgetItemIterator iter(ui->streamTreeWidget); + while (*iter) { + RtpStreamTreeWidgetItem *rsti = static_cast(*iter); + rtpstream_info_t *stream_info = rsti->streamInfo(); + if (stream_info) { + if (rtpstream_id_equal(id,&stream_info->id,RTPSTREAM_ID_EQUAL_SSRC)) { + (*iter)->setSelected(state); + } + } + ++iter; + } +} + +void RtpStreamDialog::selectRtpStream(QVector stream_ids) +{ + std::lock_guard lock(mutex_); + foreach(rtpstream_id_t *id, stream_ids) { + setRtpStreamSelection(id, true); + } +} + +void RtpStreamDialog::deselectRtpStream(QVector stream_ids) +{ + std::lock_guard lock(mutex_); + foreach(rtpstream_id_t *id, stream_ids) { + setRtpStreamSelection(id, false); + } +} + +bool RtpStreamDialog::eventFilter(QObject *, QEvent *event) +{ + if (ui->streamTreeWidget->hasFocus() && event->type() == QEvent::KeyPress) { + QKeyEvent &keyEvent = static_cast(*event); + switch(keyEvent.key()) { + case Qt::Key_G: + on_actionGoToSetup_triggered(); + return true; + case Qt::Key_M: + on_actionMarkPackets_triggered(); + return true; + case Qt::Key_P: + on_actionPrepareFilter_triggered(); + return true; + case Qt::Key_R: + if (keyEvent.modifiers() == Qt::ShiftModifier) { + on_actionFindReversePair_triggered(); + } else if (keyEvent.modifiers() == Qt::ControlModifier) { + on_actionFindReverseSingle_triggered(); + } else { + on_actionFindReverseNormal_triggered(); + } + return true; + case Qt::Key_I: + if (keyEvent.modifiers() == Qt::ControlModifier) { + // Ctrl+I + on_actionSelectInvert_triggered(); + return true; + } + break; + case Qt::Key_A: + if (keyEvent.modifiers() == Qt::ControlModifier) { + // Ctrl+A + on_actionSelectAll_triggered(); + return true; + } else if (keyEvent.modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) { + // Ctrl+Shift+A + on_actionSelectNone_triggered(); + return true; + } else if (keyEvent.modifiers() == Qt::NoModifier) { + on_actionAnalyze_triggered(); + } + break; + default: + break; + } + } + return false; +} + +void RtpStreamDialog::captureEvent(CaptureEvent e) +{ + if (e.captureContext() == CaptureEvent::Retap) + { + switch (e.eventType()) + { + case CaptureEvent::Started: + ui->displayFilterCheckBox->setEnabled(false); + break; + case CaptureEvent::Finished: + ui->displayFilterCheckBox->setEnabled(true); + break; + default: + break; + } + } + +} + +void RtpStreamDialog::tapReset(rtpstream_tapinfo_t *tapinfo) +{ + RtpStreamDialog *rtp_stream_dialog = dynamic_cast((RtpStreamDialog *)tapinfo->tap_data); + if (rtp_stream_dialog) { + rtp_stream_dialog->freeLastSelected(); + /* Copy currently selected rtpstream_ids */ + QTreeWidgetItemIterator iter(rtp_stream_dialog->ui->streamTreeWidget); + rtpstream_id_t selected_id; + while (*iter) { + RtpStreamTreeWidgetItem *rsti = static_cast(*iter); + rtpstream_info_t *stream_info = rsti->streamInfo(); + if ((*iter)->isSelected()) { + /* QList.append() does a member by member copy, so allocate new + * addresses. rtpstream_id_copy() overwrites all struct members. + */ + rtpstream_id_copy(&stream_info->id, &selected_id); + rtp_stream_dialog->last_selected_.append(selected_id); + } + ++iter; + } + /* invalidate items which refer to old strinfo_list items. */ + rtp_stream_dialog->ui->streamTreeWidget->clear(); + } +} + +void RtpStreamDialog::tapDraw(rtpstream_tapinfo_t *tapinfo) +{ + RtpStreamDialog *rtp_stream_dialog = dynamic_cast((RtpStreamDialog *)tapinfo->tap_data); + if (rtp_stream_dialog) { + rtp_stream_dialog->updateStreams(); + } +} + +void RtpStreamDialog::tapMarkPacket(rtpstream_tapinfo_t *tapinfo, frame_data *fd) +{ + if (!tapinfo) return; + + RtpStreamDialog *rtp_stream_dialog = dynamic_cast((RtpStreamDialog *)tapinfo->tap_data); + if (rtp_stream_dialog) { + cf_mark_frame(rtp_stream_dialog->cap_file_.capFile(), fd); + rtp_stream_dialog->need_redraw_ = true; + } +} + +/* Operator == for rtpstream_id_t */ +bool operator==(rtpstream_id_t const& a, rtpstream_id_t const& b) +{ + return rtpstream_id_equal(&a, &b, RTPSTREAM_ID_EQUAL_SSRC); +} + +void RtpStreamDialog::updateStreams() +{ + // string_list is reverse ordered, so we must add + // just first "to_insert_count" of streams + GList *cur_stream = g_list_first(tapinfo_.strinfo_list); + guint tap_len = g_list_length(tapinfo_.strinfo_list); + guint tree_len = static_cast(ui->streamTreeWidget->topLevelItemCount()); + guint to_insert_count = tap_len - tree_len; + + // Add any missing items + while (cur_stream && cur_stream->data && to_insert_count) { + rtpstream_info_t *stream_info = gxx_list_data(rtpstream_info_t*, cur_stream); + RtpStreamTreeWidgetItem *rsti = new RtpStreamTreeWidgetItem(ui->streamTreeWidget, stream_info); + cur_stream = gxx_list_next(cur_stream); + to_insert_count--; + + // Check if item was selected last time. If so, select it + if (-1 != last_selected_.indexOf(stream_info->id)) { + rsti->setSelected(true); + } + } + + // Recalculate values + QTreeWidgetItemIterator iter(ui->streamTreeWidget); + while (*iter) { + RtpStreamTreeWidgetItem *rsti = static_cast(*iter); + rsti->drawData(); + ++iter; + } + + // Resize columns + for (int i = 0; i < ui->streamTreeWidget->columnCount(); i++) { + ui->streamTreeWidget->resizeColumnToContents(i); + } + + ui->streamTreeWidget->setSortingEnabled(true); + + updateWidgets(); + + if (need_redraw_) { + emit packetsMarked(); + need_redraw_ = false; + } +} + +void RtpStreamDialog::updateWidgets() +{ + bool selected = ui->streamTreeWidget->selectedItems().count() > 0; + + QString hint = ""; + hint += tr("%1 streams").arg(ui->streamTreeWidget->topLevelItemCount()); + + if (selected) { + int tot_packets = 0; + foreach(QTreeWidgetItem *ti, ui->streamTreeWidget->selectedItems()) { + RtpStreamTreeWidgetItem *rsti = static_cast(ti); + if (rsti->streamInfo()) { + tot_packets += rsti->streamInfo()->packet_count; + } + } + hint += tr(", %1 selected, %2 total packets") + .arg(ui->streamTreeWidget->selectedItems().count()) + .arg(tot_packets); + } + + hint += ". Right-click for more options."; + hint += ""; + ui->hintLabel->setText(hint); + + bool enable = selected && !file_closed_; + bool has_data = ui->streamTreeWidget->topLevelItemCount() > 0; + + find_reverse_button_->setEnabled(has_data); + prepare_button_->setEnabled(enable); + export_button_->setEnabled(enable); + copy_button_->setEnabled(has_data); + analyze_button_->setEnabled(enable); + + ui->actionFindReverseNormal->setEnabled(enable); + ui->actionFindReversePair->setEnabled(has_data); + ui->actionFindReverseSingle->setEnabled(has_data); + ui->actionGoToSetup->setEnabled(enable); + ui->actionMarkPackets->setEnabled(enable); + ui->actionPrepareFilter->setEnabled(enable); + ui->actionExportAsRtpDump->setEnabled(enable); + ui->actionCopyAsCsv->setEnabled(has_data); + ui->actionCopyAsYaml->setEnabled(has_data); + ui->actionAnalyze->setEnabled(enable); + +#if defined(QT_MULTIMEDIA_LIB) + player_button_->setEnabled(enable); +#endif + + WiresharkDialog::updateWidgets(); +} + +QList RtpStreamDialog::streamRowData(int row) const +{ + QList row_data; + + if (row >= ui->streamTreeWidget->topLevelItemCount()) { + return row_data; + } + + for (int col = 0; col < ui->streamTreeWidget->columnCount(); col++) { + if (row < 0) { + row_data << ui->streamTreeWidget->headerItem()->text(col); + } else { + RtpStreamTreeWidgetItem *rsti = static_cast(ui->streamTreeWidget->topLevelItem(row)); + if (rsti) { + row_data << rsti->colData(col); + } + } + } + + // Add additional columns to export + if (row < 0) { + row_data << QString("SSRC formatted"); + row_data << QString("Lost percentage"); + } else { + RtpStreamTreeWidgetItem *rsti = static_cast(ui->streamTreeWidget->topLevelItem(row)); + if (rsti) { + row_data << rsti->colData(ssrc_fmt_col_); + row_data << rsti->colData(lost_perc_col_); + } + } + return row_data; +} + +void RtpStreamDialog::freeLastSelected() +{ + /* Free old IDs */ + for(int i=0; itodCheckBox->setEnabled(false); + ui->displayFilterCheckBox->setEnabled(false); + + WiresharkDialog::captureFileClosed(); +} + +void RtpStreamDialog::showStreamMenu(QPoint pos) +{ + ui->actionGoToSetup->setEnabled(!file_closed_); + ui->actionMarkPackets->setEnabled(!file_closed_); + ui->actionPrepareFilter->setEnabled(!file_closed_); + ui->actionExportAsRtpDump->setEnabled(!file_closed_); + ui->actionAnalyze->setEnabled(!file_closed_); + ctx_menu_.popup(ui->streamTreeWidget->viewport()->mapToGlobal(pos)); +} + +void RtpStreamDialog::on_actionCopyAsCsv_triggered() +{ + QString csv; + QTextStream stream(&csv, QIODevice::Text); + for (int row = -1; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QStringList rdsl; + foreach (QVariant v, streamRowData(row)) { + if (!v.isValid()) { + rdsl << "\"\""; + } else if (v.userType() == QMetaType::QString) { + rdsl << QString("\"%1\"").arg(v.toString()); + } else { + rdsl << v.toString(); + } + } + stream << rdsl.join(",") << '\n'; + } + mainApp->clipboard()->setText(stream.readAll()); +} + +void RtpStreamDialog::on_actionCopyAsYaml_triggered() +{ + QString yaml; + QTextStream stream(&yaml, QIODevice::Text); + stream << "---" << '\n'; + for (int row = -1; row < ui->streamTreeWidget->topLevelItemCount(); row ++) { + stream << "-" << '\n'; + foreach (QVariant v, streamRowData(row)) { + stream << " - " << v.toString() << '\n'; + } + } + mainApp->clipboard()->setText(stream.readAll()); +} + +void RtpStreamDialog::on_actionExportAsRtpDump_triggered() +{ + if (file_closed_ || ui->streamTreeWidget->selectedItems().count() < 1) return; + + // XXX If the user selected multiple frames is this the one we actually want? + QTreeWidgetItem *ti = ui->streamTreeWidget->selectedItems()[0]; + RtpStreamTreeWidgetItem *rsti = static_cast(ti); + rtpstream_info_t *stream_info = rsti->streamInfo(); + if (stream_info) { + QString file_name; + QDir path(mainApp->openDialogInitialDir()); + QString save_file = path.canonicalPath() + "/" + cap_file_.fileBaseName(); + QString extension; + file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save RTPDump As…")), + save_file, "RTPDump Format (*.rtp)", &extension); + + if (file_name.length() > 0) { + gchar *dest_file = qstring_strdup(file_name); + gboolean save_ok = rtpstream_save(&tapinfo_, cap_file_.capFile(), stream_info, dest_file); + g_free(dest_file); + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } + + } +} + +// Search for reverse stream of every selected stream +void RtpStreamDialog::on_actionFindReverseNormal_triggered() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + ui->streamTreeWidget->blockSignals(true); + + // Traverse all items and if stream is selected, search reverse from + // current position till last item (NxN/2) + for (int fwd_row = 0; fwd_row < ui->streamTreeWidget->topLevelItemCount(); fwd_row++) { + RtpStreamTreeWidgetItem *fwd_rsti = static_cast(ui->streamTreeWidget->topLevelItem(fwd_row)); + rtpstream_info_t *fwd_stream = fwd_rsti->streamInfo(); + if (fwd_stream && fwd_rsti->isSelected()) { + for (int rev_row = fwd_row + 1; rev_row < ui->streamTreeWidget->topLevelItemCount(); rev_row++) { + RtpStreamTreeWidgetItem *rev_rsti = static_cast(ui->streamTreeWidget->topLevelItem(rev_row)); + rtpstream_info_t *rev_stream = rev_rsti->streamInfo(); + if (rev_stream && rtpstream_info_is_reverse(fwd_stream, rev_stream)) { + rev_rsti->setSelected(true); + break; + } + } + } + } + ui->streamTreeWidget->blockSignals(false); + updateWidgets(); +} + +// Select all pairs of forward/reverse streams +void RtpStreamDialog::on_actionFindReversePair_triggered() +{ + ui->streamTreeWidget->blockSignals(true); + ui->streamTreeWidget->clearSelection(); + + // Traverse all items and search reverse from current position till last + // item (NxN/2) + for (int fwd_row = 0; fwd_row < ui->streamTreeWidget->topLevelItemCount(); fwd_row++) { + RtpStreamTreeWidgetItem *fwd_rsti = static_cast(ui->streamTreeWidget->topLevelItem(fwd_row)); + rtpstream_info_t *fwd_stream = fwd_rsti->streamInfo(); + if (fwd_stream) { + for (int rev_row = fwd_row + 1; rev_row < ui->streamTreeWidget->topLevelItemCount(); rev_row++) { + RtpStreamTreeWidgetItem *rev_rsti = static_cast(ui->streamTreeWidget->topLevelItem(rev_row)); + rtpstream_info_t *rev_stream = rev_rsti->streamInfo(); + if (rev_stream && rtpstream_info_is_reverse(fwd_stream, rev_stream)) { + fwd_rsti->setSelected(true); + rev_rsti->setSelected(true); + break; + } + } + } + } + ui->streamTreeWidget->blockSignals(false); + updateWidgets(); +} + +// Select all streams which don't have reverse stream +void RtpStreamDialog::on_actionFindReverseSingle_triggered() +{ + ui->streamTreeWidget->blockSignals(true); + ui->streamTreeWidget->selectAll(); + + // Traverse all items and search reverse from current position till last + // item (NxN/2) + for (int fwd_row = 0; fwd_row < ui->streamTreeWidget->topLevelItemCount(); fwd_row++) { + RtpStreamTreeWidgetItem *fwd_rsti = static_cast(ui->streamTreeWidget->topLevelItem(fwd_row)); + rtpstream_info_t *fwd_stream = fwd_rsti->streamInfo(); + if (fwd_stream) { + for (int rev_row = fwd_row + 1; rev_row < ui->streamTreeWidget->topLevelItemCount(); rev_row++) { + RtpStreamTreeWidgetItem *rev_rsti = static_cast(ui->streamTreeWidget->topLevelItem(rev_row)); + rtpstream_info_t *rev_stream = rev_rsti->streamInfo(); + if (rev_stream && rtpstream_info_is_reverse(fwd_stream, rev_stream)) { + fwd_rsti->setSelected(false); + rev_rsti->setSelected(false); + break; + } + } + } + } + ui->streamTreeWidget->blockSignals(false); + updateWidgets(); +} + +void RtpStreamDialog::on_actionGoToSetup_triggered() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + // XXX If the user selected multiple frames is this the one we actually want? + QTreeWidgetItem *ti = ui->streamTreeWidget->selectedItems()[0]; + RtpStreamTreeWidgetItem *rsti = static_cast(ti); + rtpstream_info_t *stream_info = rsti->streamInfo(); + if (stream_info) { + emit goToPacket(stream_info->setup_frame_number); + } +} + +void RtpStreamDialog::on_actionMarkPackets_triggered() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + rtpstream_info_t *stream_a, *stream_b = NULL; + + QTreeWidgetItem *ti = ui->streamTreeWidget->selectedItems()[0]; + RtpStreamTreeWidgetItem *rsti = static_cast(ti); + stream_a = rsti->streamInfo(); + if (ui->streamTreeWidget->selectedItems().count() > 1) { + ti = ui->streamTreeWidget->selectedItems()[1]; + rsti = static_cast(ti); + stream_b = rsti->streamInfo(); + } + + if (stream_a == NULL && stream_b == NULL) return; + + // XXX Mark the setup frame as well? + need_redraw_ = false; + rtpstream_mark(&tapinfo_, cap_file_.capFile(), stream_a, stream_b); + updateWidgets(); +} + +void RtpStreamDialog::on_actionPrepareFilter_triggered() +{ + QVector ids = getSelectedRtpIds(); + QString filter = make_filter_based_on_rtpstream_id(ids); + if (filter.length() > 0) { + remove_tap_listener_rtpstream(&tapinfo_); + emit updateFilter(filter); + } +} + +void RtpStreamDialog::on_streamTreeWidget_itemSelectionChanged() +{ + updateWidgets(); +} + +void RtpStreamDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_TELEPHONY_RTP_STREAMS_DIALOG); +} + +void RtpStreamDialog::displayFilterCheckBoxToggled(bool checked) +{ + if (!cap_file_.isValid()) { + return; + } + + tapinfo_.apply_display_filter = checked; + + cap_file_.retapPackets(); +} + +void RtpStreamDialog::on_todCheckBox_toggled(bool checked) +{ + QTreeWidgetItemIterator iter(ui->streamTreeWidget); + while (*iter) { + RtpStreamTreeWidgetItem *rsti = static_cast(*iter); + rsti->setTOD(checked); + rsti->drawData(); + ++iter; + } + ui->streamTreeWidget->resizeColumnToContents(start_time_col_); +} + +void RtpStreamDialog::on_actionSelectAll_triggered() +{ + ui->streamTreeWidget->selectAll(); +} + +void RtpStreamDialog::on_actionSelectInvert_triggered() +{ + invertSelection(); +} + +void RtpStreamDialog::on_actionSelectNone_triggered() +{ + ui->streamTreeWidget->clearSelection(); +} + +QVectorRtpStreamDialog::getSelectedRtpIds() +{ + // Gather up our selected streams... + QVector stream_ids; + foreach(QTreeWidgetItem *ti, ui->streamTreeWidget->selectedItems()) { + RtpStreamTreeWidgetItem *rsti = static_cast(ti); + rtpstream_info_t *selected_stream = rsti->streamInfo(); + if (selected_stream) { + stream_ids << &(selected_stream->id); + } + } + + return stream_ids; +} + +void RtpStreamDialog::rtpPlayerReplace() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpPlayerDialogReplaceRtpStreams(getSelectedRtpIds()); +} + +void RtpStreamDialog::rtpPlayerAdd() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpPlayerDialogAddRtpStreams(getSelectedRtpIds()); +} + +void RtpStreamDialog::rtpPlayerRemove() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpPlayerDialogRemoveRtpStreams(getSelectedRtpIds()); +} + +void RtpStreamDialog::rtpAnalysisReplace() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpAnalysisDialogReplaceRtpStreams(getSelectedRtpIds()); +} + +void RtpStreamDialog::rtpAnalysisAdd() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpAnalysisDialogAddRtpStreams(getSelectedRtpIds()); +} + +void RtpStreamDialog::rtpAnalysisRemove() +{ + if (ui->streamTreeWidget->selectedItems().count() < 1) return; + + emit rtpAnalysisDialogRemoveRtpStreams(getSelectedRtpIds()); +} + +void RtpStreamDialog::displayFilterSuccess(bool success) +{ + if (success && ui->displayFilterCheckBox->isChecked()) { + cap_file_.retapPackets(); + } +} + +void RtpStreamDialog::invertSelection() +{ + ui->streamTreeWidget->blockSignals(true); + for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); + ti->setSelected(!ti->isSelected()); + } + ui->streamTreeWidget->blockSignals(false); + updateWidgets(); +} + +void RtpStreamDialog::on_actionAnalyze_triggered() +{ + RtpStreamDialog::rtpAnalysisAdd(); +} + diff --git a/ui/qt/rtp_stream_dialog.h b/ui/qt/rtp_stream_dialog.h new file mode 100644 index 00000000..4420094a --- /dev/null +++ b/ui/qt/rtp_stream_dialog.h @@ -0,0 +1,133 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTP_STREAM_DIALOG_H +#define RTP_STREAM_DIALOG_H + +#include "wireshark_dialog.h" + +#include + +#include "ui/rtp_stream.h" +#include "rtp_player_dialog.h" + +#include +#include + +namespace Ui { +class RtpStreamDialog; +} + +// Singleton by https://refactoring.guru/design-patterns/singleton/cpp/example#example-1 +class RtpStreamDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + /** + * Returns singleton + */ + static RtpStreamDialog *openRtpStreamDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list); + + /** + * Should not be clonnable and assignable + */ + RtpStreamDialog(RtpStreamDialog &other) = delete; + void operator=(const RtpStreamDialog &) = delete; + + // Caller must provide ids which are immutable to recap + void selectRtpStream(QVector stream_ids); + // Caller must provide ids which are immutable to recap + void deselectRtpStream(QVector stream_ids); + +signals: + // Tells the packet list to redraw. An alternative might be to add a + // cf_packet_marked callback to file.[ch] but that's synchronous and + // might incur too much overhead. + void packetsMarked(); + void updateFilter(QString filter, bool force = false); + void goToPacket(int packet_num); + void rtpPlayerDialogReplaceRtpStreams(QVector stream_ids); + void rtpPlayerDialogAddRtpStreams(QVector stream_ids); + void rtpPlayerDialogRemoveRtpStreams(QVector stream_ids); + void rtpAnalysisDialogReplaceRtpStreams(QVector stream_infos); + void rtpAnalysisDialogAddRtpStreams(QVector stream_infos); + void rtpAnalysisDialogRemoveRtpStreams(QVector stream_infos); + +public slots: + void displayFilterSuccess(bool success); + void rtpPlayerReplace(); + void rtpPlayerAdd(); + void rtpPlayerRemove(); + void rtpAnalysisReplace(); + void rtpAnalysisAdd(); + void rtpAnalysisRemove(); + +protected: + explicit RtpStreamDialog(QWidget &parent, CaptureFile &cf); + ~RtpStreamDialog(); + + bool eventFilter(QObject *obj, QEvent *event); + void captureFileClosing(); + void captureFileClosed(); + +private: + static RtpStreamDialog *pinstance_; + static std::mutex mutex_; + + Ui::RtpStreamDialog *ui; + rtpstream_tapinfo_t tapinfo_; + QToolButton *find_reverse_button_; + QPushButton *prepare_button_; + QPushButton *export_button_; + QPushButton *copy_button_; + QToolButton *analyze_button_; + QToolButton *player_button_; + QMenu ctx_menu_; + bool need_redraw_; + QList last_selected_; + + static void tapReset(rtpstream_tapinfo_t *tapinfo); + static void tapDraw(rtpstream_tapinfo_t *tapinfo); + static void tapMarkPacket(rtpstream_tapinfo_t *tapinfo, frame_data *fd); + + void updateStreams(); + void updateWidgets(); + void showPlayer(); + + void setRtpStreamSelection(rtpstream_id_t *id, bool state); + + QList streamRowData(int row) const; + void freeLastSelected(); + void invertSelection(); + QVectorgetSelectedRtpIds(); + +private slots: + void showStreamMenu(QPoint pos); + void on_actionCopyAsCsv_triggered(); + void on_actionCopyAsYaml_triggered(); + void on_actionFindReverseNormal_triggered(); + void on_actionFindReversePair_triggered(); + void on_actionFindReverseSingle_triggered(); + void on_actionGoToSetup_triggered(); + void on_actionMarkPackets_triggered(); + void on_actionPrepareFilter_triggered(); + void on_streamTreeWidget_itemSelectionChanged(); + void on_buttonBox_helpRequested(); + void on_actionExportAsRtpDump_triggered(); + void captureEvent(CaptureEvent e); + void displayFilterCheckBoxToggled(bool checked); + void on_todCheckBox_toggled(bool checked); + void on_actionSelectAll_triggered(); + void on_actionSelectInvert_triggered(); + void on_actionSelectNone_triggered(); + void on_actionAnalyze_triggered(); +}; + +#endif // RTP_STREAM_DIALOG_H diff --git a/ui/qt/rtp_stream_dialog.ui b/ui/qt/rtp_stream_dialog.ui new file mode 100644 index 00000000..4794c639 --- /dev/null +++ b/ui/qt/rtp_stream_dialog.ui @@ -0,0 +1,396 @@ + + + RtpStreamDialog + + + + 0 + 0 + 600 + 460 + + + + Dialog + + + + + + QAbstractItemView::MultiSelection + + + Qt::ElideMiddle + + + false + + + true + + + false + + + true + + + false + + + 50 + + + + Source Address + + + + + Source Port + + + + + Destination Address + + + + + Destination Port + + + + + SSRC + + + + + Start Time + + + + + Duration + + + + + Payload + + + + + Packets + + + + + Lost + + + + + Min Delta (ms) + + + + + Mean Delta (ms) + + + + + Max Delta (ms) + + + + + Min Jitter + + + + + Mean Jitter + + + + + Max Jitter + + + + + Status + + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + Limit to display filter + + + + + + + Time of Day + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Find &Reverse + + + All forward/reverse stream actions + + + + + Find &Reverse + + + Find the reverse stream matching the selected forward stream. + + + R + + + + + Find All &Pairs + + + Select all streams which are paired in forward/reverse relation + + + Shift+R + + + + + Find Only &Singles + + + Find all streams which don't have paired reverse stream + + + Ctrl+R + + + + + Find &Reverse + + + true + + + + + + + + Mark Packets + + + Mark the packets of the selected stream(s). + + + M + + + + + Select + + + true + + + + + + + + All + + + Select all + + + Ctrl+A + + + + + None + + + Clear selection + + + Ctrl+Shift+A + + + + + Invert + + + Invert selection + + + Ctrl+I + + + + + Go To Setup + + + Go to the setup packet for this stream. + + + G + + + + + Prepare &Filter + + + Prepare a filter matching the selected stream(s). + + + P + + + + + &Export + + + Export the stream payload as rtpdump + + + E + + + + + &Analyze + + + Open the analysis window for the selected stream(s) and add it to it + + + A + + + + + Cop&y + + + Open copy menu + + + + + Copy as CSV + + + Copy stream list as CSV. + + + + + Copy as YAML + + + Copy stream list as YAML. + + + + + + + buttonBox + accepted() + RtpStreamDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RtpStreamDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/scsi_service_response_time_dialog.cpp b/ui/qt/scsi_service_response_time_dialog.cpp new file mode 100644 index 00000000..807235e2 --- /dev/null +++ b/ui/qt/scsi_service_response_time_dialog.cpp @@ -0,0 +1,86 @@ +/* scsi_service_response_time_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "scsi_service_response_time_dialog.h" + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +ScsiServiceResponseTimeDialog::ScsiServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, struct register_srt *srt, const QString filter) : + ServiceResponseTimeDialog(parent, cf, srt, filter) +{ + setRetapOnShow(false); + setHint(tr("Select a command and enter a filter if desired, then press Apply.")); + + QHBoxLayout *filter_layout = filterLayout(); + command_combo_ = new QComboBox(this); + + filter_layout->insertStretch(0, 1); + filter_layout->insertWidget(0, command_combo_); + filter_layout->insertWidget(0, new QLabel(tr("Command:"))); + + setWindowSubtitle(tr("SCSI Service Response Times")); + + QStringList commands; + commands << "SBC (disk)" << "SSC (tape)" << "MMC (cd/dvd)" << "SMC (tape robot)" << "OSD (object based)"; + command_combo_->addItems(commands); +} + +TapParameterDialog *ScsiServiceResponseTimeDialog::createScsiSrtDialog(QWidget &parent, const QString, const QString opt_arg, CaptureFile &cf) +{ + QString filter; + bool have_args = false; + QString command; + + // rpc,srt,scsi,command[, + QStringList args_l = QString(opt_arg).split(','); + if (args_l.length() > 0) { + command = args_l[0]; + if (args_l.length() > 1) { + filter = QStringList(args_l.mid(1)).join(","); + } + have_args = true; + } + + ScsiServiceResponseTimeDialog *scsi_dlg = new ScsiServiceResponseTimeDialog(parent, cf, get_srt_table_by_name("scsi"), filter); + + if (have_args) { + if (!command.isEmpty()) { + scsi_dlg->setScsiCommand(command.toInt()); + } + } + + return scsi_dlg; +} + +void ScsiServiceResponseTimeDialog::setScsiCommand(int command) +{ + command_combo_->setCurrentIndex(command); + fillTree(); +} + +void ScsiServiceResponseTimeDialog::provideParameterData() +{ + char* err; + QString command; + + command = QString(",%1").arg(command_combo_->currentIndex()); + + scsistat_param(srt_, command.toStdString().c_str(), &err); +} diff --git a/ui/qt/scsi_service_response_time_dialog.h b/ui/qt/scsi_service_response_time_dialog.h new file mode 100644 index 00000000..a9fd564e --- /dev/null +++ b/ui/qt/scsi_service_response_time_dialog.h @@ -0,0 +1,34 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __SCSI_SERVICE_RESPONSE_TIME_DIALOG_H__ +#define __SCSI_SERVICE_RESPONSE_TIME_DIALOG_H__ + +#include "service_response_time_dialog.h" + +class QComboBox; + +class ScsiServiceResponseTimeDialog : public ServiceResponseTimeDialog +{ + Q_OBJECT + +public: + ScsiServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, struct register_srt *srt, const QString filter); + static TapParameterDialog *createScsiSrtDialog(QWidget &parent, const QString, const QString opt_arg, CaptureFile &cf); + + void setScsiCommand(int command); + +protected: + virtual void provideParameterData(); + +private: + QComboBox *command_combo_; +}; + +#endif // __SCSI_SERVICE_RESPONSE_TIME_DIALOG_H__ diff --git a/ui/qt/sctp_all_assocs_dialog.cpp b/ui/qt/sctp_all_assocs_dialog.cpp new file mode 100644 index 00000000..c8208d66 --- /dev/null +++ b/ui/qt/sctp_all_assocs_dialog.cpp @@ -0,0 +1,118 @@ +/* sctp_all_assocs_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "sctp_all_assocs_dialog.h" +#include +#include "sctp_assoc_analyse_dialog.h" + +#include +//#include "main_application.h" +#include "file.h" +#include "ui/qt/main_window.h" + +#include +#include +#include + +//#include + +SCTPAllAssocsDialog::SCTPAllAssocsDialog(QWidget *parent, capture_file *cf) : + QDialog(parent), + ui(new Ui::SCTPAllAssocsDialog), + cap_file_(cf) +{ + ui->setupUi(this); + Qt::WindowFlags flags = Qt::Window | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint + | Qt::WindowMaximizeButtonHint + | Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); + fillTable(); +} + +SCTPAllAssocsDialog::~SCTPAllAssocsDialog() +{ + delete ui; +} + +void SCTPAllAssocsDialog::fillTable() +{ + const sctp_allassocs_info_t *sctp_assocs; + GList *list; + const sctp_assoc_info_t* assinfo; + int numAssocs; + + ui->assocList->setColumnHidden(0, true); + ui->assocList->setColumnWidth(1, 85); + ui->assocList->setColumnWidth(2, 85); + ui->assocList->setColumnWidth(3, 150); + ui->assocList->setColumnWidth(4, 150); + + sctp_assocs = sctp_stat_get_info(); + if (sctp_assocs->is_registered == FALSE) { + register_tap_listener_sctp_stat(); + /* (redissect all packets) */ + cf_retap_packets(cap_file_); + } + numAssocs = 0; + ui->assocList->setRowCount(static_cast(g_list_length(sctp_assocs->assoc_info_list))); + + list = g_list_first(sctp_assocs->assoc_info_list); + + while (list) { + assinfo = gxx_list_data(const sctp_assoc_info_t*, list); + ui->assocList->setItem(numAssocs, 0, new QTableWidgetItem(QString("%1").arg(assinfo->assoc_id))); + ui->assocList->setItem(numAssocs, 1, new QTableWidgetItem(QString("%1").arg(assinfo->port1))); + ui->assocList->setItem(numAssocs, 2, new QTableWidgetItem(QString("%1").arg(assinfo->port2))); + ui->assocList->setItem(numAssocs, 3, new QTableWidgetItem(QString("%1").arg(assinfo->n_packets))); + ui->assocList->setItem(numAssocs, 4, new QTableWidgetItem(QString("%1").arg(assinfo->n_data_chunks))); + ui->assocList->setItem(numAssocs, 5, new QTableWidgetItem(QString("%1").arg(assinfo->n_data_bytes))); + list = gxx_list_next(list); + numAssocs++; + } + ui->analyseButton->setEnabled(false); + ui->setFilterButton->setEnabled(false); + connect(ui->assocList, SIGNAL(itemSelectionChanged()), this, SLOT(getSelectedItem())); + } + +void SCTPAllAssocsDialog::getSelectedItem() +{ + ui->analyseButton->setEnabled(true); + ui->setFilterButton->setEnabled(true); + ui->analyseButton->setFocus(Qt::OtherFocusReason); + selected_assoc_id = ui->assocList->item(ui->assocList->selectedItems().at(0)->row(), 0)->data(0).toInt(); +} + +void SCTPAllAssocsDialog::on_analyseButton_clicked() +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + SCTPAssocAnalyseDialog *sctp_analyse = new SCTPAssocAnalyseDialog(this, selected_assoc, cap_file_); + connect(sctp_analyse, SIGNAL(filterPackets(QString,bool)), + parent(), SLOT(filterPackets(QString,bool))); + + if (sctp_analyse->isMinimized() == true) + { + sctp_analyse->showNormal(); + } + else + { + sctp_analyse->show(); + } + + sctp_analyse->raise(); + sctp_analyse->activateWindow(); +} + +void SCTPAllAssocsDialog::on_setFilterButton_clicked() +{ + QString newFilter = QString("sctp.assoc_index==%1").arg(selected_assoc_id); + emit filterPackets(newFilter, false); +} diff --git a/ui/qt/sctp_all_assocs_dialog.h b/ui/qt/sctp_all_assocs_dialog.h new file mode 100644 index 00000000..c29c8e9b --- /dev/null +++ b/ui/qt/sctp_all_assocs_dialog.h @@ -0,0 +1,58 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SCTP_ALL_ASSOCS_DIALOG_H +#define SCTP_ALL_ASSOCS_DIALOG_H + +#include + +#include + +#include + +#include + +#include "ui/tap-sctp-analysis.h" + +#include +#include + +namespace Ui { +class SCTPAllAssocsDialog; +} + +class SCTPAllAssocsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SCTPAllAssocsDialog(QWidget *parent = 0, capture_file *cf = NULL); + ~SCTPAllAssocsDialog(); + + void fillTable(); + +public slots: + void setCaptureFile(capture_file *cf) { cap_file_ = cf; } + +private slots: + void on_analyseButton_clicked(); + void on_setFilterButton_clicked(); + void getSelectedItem(); + +private: + Ui::SCTPAllAssocsDialog *ui; + capture_file *cap_file_; + guint16 selected_assoc_id; + + +signals: + void filterPackets(QString new_filter, bool force); +}; + +#endif // SCTP_ALL_ASSOCS_DIALOG_H diff --git a/ui/qt/sctp_all_assocs_dialog.ui b/ui/qt/sctp_all_assocs_dialog.ui new file mode 100644 index 00000000..561836b1 --- /dev/null +++ b/ui/qt/sctp_all_assocs_dialog.ui @@ -0,0 +1,166 @@ + + + SCTPAllAssocsDialog + + + + 0 + 0 + 827 + 546 + + + + Wireshark - SCTP Associations + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + 2 + + + 6 + + + 50 + + + 120 + + + true + + + + + + ID + + + + + Port 1 + + + + + Port 2 + + + + + Number of Packets + + + + + Number of DATA Chunks + + + + + Number of Bytes + + + + + + + + + + + + + false + + + Qt::ClickFocus + + + Filter Selected Association + + + + + + + false + + + Qt::ClickFocus + + + Analyze + + + + + + + Qt::Horizontal + + + + 10 + 20 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + buttonBox + clicked(QAbstractButton*) + SCTPAllAssocsDialog + close() + + + 644 + 274 + + + 659 + 322 + + + + + diff --git a/ui/qt/sctp_assoc_analyse_dialog.cpp b/ui/qt/sctp_assoc_analyse_dialog.cpp new file mode 100644 index 00000000..c6b4b11a --- /dev/null +++ b/ui/qt/sctp_assoc_analyse_dialog.cpp @@ -0,0 +1,322 @@ +/* sctp_assoc_analyse_dialog.cpp + * + * Copyright 2021 Thomas Dreibholz + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "epan/to_str.h" + +#include "sctp_assoc_analyse_dialog.h" +#include + +#include +#include "sctp_graph_dialog.h" +#include "sctp_graph_arwnd_dialog.h" +#include "sctp_graph_byte_dialog.h" +#include "sctp_chunk_statistics_dialog.h" + +SCTPAssocAnalyseDialog::SCTPAssocAnalyseDialog(QWidget *parent, const sctp_assoc_info_t *assoc, + capture_file *cf) : + QDialog(parent), + ui(new Ui::SCTPAssocAnalyseDialog), + cap_file_(cf) +{ + Q_ASSERT(assoc); + selected_assoc_id = assoc->assoc_id; + + ui->setupUi(this); + ui->SCTPAssocAnalyseTab->setCurrentWidget(ui->Statistics); + Qt::WindowFlags flags = Qt::Window | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint + | Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); + + this->setWindowTitle(QString(tr("SCTP Analyse Association: %1 Port1 %2 Port2 %3")) + .arg(gchar_free_to_qstring(cf_get_display_name(cap_file_))).arg(assoc->port1).arg(assoc->port2)); + fillTabs(assoc); +} + +SCTPAssocAnalyseDialog::~SCTPAssocAnalyseDialog() +{ + delete ui; +} + +const sctp_assoc_info_t* SCTPAssocAnalyseDialog::findAssocForPacket(capture_file* cf) +{ + frame_data *fdata; + GList *list, *framelist; + const sctp_assoc_info_t *assoc; + bool frame_found = false; + + fdata = cf->current_frame; + if (sctp_stat_get_info()->is_registered == FALSE) { + register_tap_listener_sctp_stat(); + /* (redissect all packets) */ + cf_retap_packets(cf); + } + list = g_list_first(sctp_stat_get_info()->assoc_info_list); + + while (list) { + assoc = gxx_list_data(const sctp_assoc_info_t*, list); + + framelist = g_list_first(assoc->frame_numbers); + guint32 fn; + while (framelist) { + fn = GPOINTER_TO_UINT(framelist->data); + if (fn == fdata->num) { + frame_found = TRUE; + break; + } + framelist = gxx_list_next(framelist); + } + if (frame_found) { + return assoc; + } else { + list = gxx_list_next(list); + } + } + + if (!frame_found) { + QMessageBox msgBox; + msgBox.setText(tr("No Association found for this packet.")); + msgBox.exec(); + } + return Q_NULLPTR; +} + +const _sctp_assoc_info* SCTPAssocAnalyseDialog::findAssoc(QWidget *parent, guint16 assoc_id) +{ + const sctp_assoc_info_t* result = get_sctp_assoc_info(assoc_id); + if (result) return result; + + QMessageBox::warning(parent, tr("Warning"), tr("Could not find SCTP Association with id: %1") + .arg(assoc_id)); + return NULL; +} + +void SCTPAssocAnalyseDialog::fillTabs(const sctp_assoc_info_t* selected_assoc) +{ + Q_ASSERT(selected_assoc); + + /* Statistics Tab */ + + ui->checksumLabel->setText(selected_assoc->checksum_type); + ui->data12Label->setText(QString("%1").arg(selected_assoc->n_data_chunks_ep1)); + ui->bytes12Label->setText(QString("%1").arg(selected_assoc->n_data_bytes_ep1)); + ui->data21Label->setText(QString("%1").arg(selected_assoc->n_data_chunks_ep2)); + ui->bytes21Label->setText(QString("%1").arg(selected_assoc->n_data_bytes_ep2)); + + /* Tab Endpoint 1 */ + + if (selected_assoc->init) + ui->labelEP1->setText(QString(tr("Complete list of IP addresses from INIT Chunk:"))); + else if ((selected_assoc->initack) && (selected_assoc->initack_dir == 1)) + ui->labelEP1->setText(QString(tr("Complete list of IP addresses from INIT_ACK Chunk:"))); + else + ui->labelEP1->setText(QString(tr("List of Used IP Addresses"))); + + if (selected_assoc->addr1 != Q_NULLPTR) { + GList *list; + + list = g_list_first(selected_assoc->addr1); + while (list) { + address *store; + + store = gxx_list_data(address *, list); + if (store->type != AT_NONE) { + if ((store->type == AT_IPv4) || (store->type == AT_IPv6)) { + ui->listWidgetEP1->addItem(address_to_qstring(store)); + } + } + list = gxx_list_next(list); + } + } else { + return; + } + + ui->label_221->setText(QString("%1").arg(selected_assoc->port1)); + ui->label_222->setText(QString("0x%1").arg(selected_assoc->verification_tag1, 0, 16)); + + if ((selected_assoc->init) || + ((selected_assoc->initack) && (selected_assoc->initack_dir == 1))) { + ui->label_213->setText(QString(tr("Requested Number of Inbound Streams:"))); + ui->label_223->setText(QString("%1").arg(selected_assoc->instream1)); + ui->label_214->setText(QString(tr("Minimum Number of Inbound Streams:"))); + ui->label_224->setText(QString("%1").arg(((selected_assoc->instream1 > selected_assoc->outstream2) ? + selected_assoc->outstream2 : selected_assoc->instream1))); + ui->label_215->setText(QString(tr("Provided Number of Outbound Streams:"))); + ui->label_225->setText(QString("%1").arg(selected_assoc->outstream1)); + ui->label_216->setText(QString(tr("Minimum Number of Outbound Streams:"))); + ui->label_226->setText(QString("%1").arg(((selected_assoc->outstream1 > selected_assoc->instream2) ? + selected_assoc->instream2 : selected_assoc->outstream1))); + } else { + ui->label_213->setText(QString(tr("Used Number of Inbound Streams:"))); + ui->label_223->setText(QString("%1").arg(selected_assoc->instream1)); + ui->label_214->setText(QString(tr("Used Number of Outbound Streams:"))); + ui->label_224->setText(QString("%1").arg(selected_assoc->outstream1)); + ui->label_215->setText(QString("")); + ui->label_225->setText(QString("")); + ui->label_216->setText(QString("")); + ui->label_226->setText(QString("")); + } + + /* Tab Endpoint 2 */ + + if ((selected_assoc->initack) && (selected_assoc->initack_dir == 2)) + ui->labelEP2->setText(QString(tr("Complete list of IP addresses from INIT_ACK Chunk:"))); + else + ui->labelEP2->setText(QString(tr("List of Used IP Addresses"))); + + if (selected_assoc->addr2 != Q_NULLPTR) { + GList *list; + + list = g_list_first(selected_assoc->addr2); + while (list) { + address *store; + + store = gxx_list_data(address *, list); + if (store->type != AT_NONE) { + if ((store->type == AT_IPv4) || (store->type == AT_IPv6)) { + ui->listWidgetEP2->addItem(address_to_qstring(store)); + } + } + list = gxx_list_next(list); + } + } else { + return; + } + + ui->label_321->setText(QString("%1").arg(selected_assoc->port2)); + ui->label_322->setText(QString("0x%1").arg(selected_assoc->verification_tag2, 0, 16)); + + if (selected_assoc->initack) { + ui->label_313->setText(QString(tr("Requested Number of Inbound Streams:"))); + ui->label_323->setText(QString("%1").arg(selected_assoc->instream2)); + ui->label_314->setText(QString(tr("Minimum Number of Inbound Streams:"))); + ui->label_324->setText(QString("%1").arg(((selected_assoc->instream2 > selected_assoc->outstream1) ? + selected_assoc->outstream1 : selected_assoc->instream2))); + ui->label_315->setText(QString(tr("Provided Number of Outbound Streams:"))); + ui->label_325->setText(QString("%1").arg(selected_assoc->outstream2)); + ui->label_316->setText(QString(tr("Minimum Number of Outbound Streams:"))); + ui->label_326->setText(QString("%1").arg(((selected_assoc->outstream2 > selected_assoc->instream1) ? + selected_assoc->instream1 : selected_assoc->outstream2))); + } else { + ui->label_313->setText(QString(tr("Used Number of Inbound Streams:"))); + ui->label_323->setText(QString("%1").arg(selected_assoc->instream2)); + ui->label_314->setText(QString(tr("Used Number of Outbound Streams:"))); + ui->label_324->setText(QString("%1").arg(selected_assoc->outstream2)); + ui->label_315->setText(QString("")); + ui->label_325->setText(QString("")); + ui->label_316->setText(QString("")); + ui->label_326->setText(QString("")); + } +} + +void SCTPAssocAnalyseDialog::openGraphDialog(int direction) +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + SCTPGraphDialog *sctp_dialog = new SCTPGraphDialog(this, selected_assoc, cap_file_, direction); + + if (sctp_dialog->isMinimized() == true) { + sctp_dialog->showNormal(); + } else { + sctp_dialog->show(); + } + + sctp_dialog->raise(); + sctp_dialog->activateWindow(); +} + +void SCTPAssocAnalyseDialog::on_GraphTSN_2_clicked() +{ + openGraphDialog(2); +} + +void SCTPAssocAnalyseDialog::on_GraphTSN_1_clicked() +{ + openGraphDialog(1); +} + +void SCTPAssocAnalyseDialog::on_chunkStatisticsButton_clicked() +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + SCTPChunkStatisticsDialog *sctp_dialog = new SCTPChunkStatisticsDialog(this, selected_assoc, cap_file_); + + if (sctp_dialog->isMinimized() == true) { + sctp_dialog->showNormal(); + } else { + sctp_dialog->show(); + } + + sctp_dialog->raise(); + sctp_dialog->activateWindow(); +} + +void SCTPAssocAnalyseDialog::on_setFilterButton_clicked() +{ + QString newFilter = QString("sctp.assoc_index==%1").arg(selected_assoc_id); + emit filterPackets(newFilter, false); +} + +void SCTPAssocAnalyseDialog::openGraphByteDialog(int direction) +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + SCTPGraphByteDialog *sctp_dialog = new SCTPGraphByteDialog(this, selected_assoc, cap_file_, direction); + + if (sctp_dialog->isMinimized() == true) { + sctp_dialog->showNormal(); + } else { + sctp_dialog->show(); + } + + sctp_dialog->raise(); + sctp_dialog->activateWindow(); +} + +void SCTPAssocAnalyseDialog::on_GraphBytes_1_clicked() +{ + openGraphByteDialog(1); +} + +void SCTPAssocAnalyseDialog::on_GraphBytes_2_clicked() +{ + openGraphByteDialog(2); +} + +void SCTPAssocAnalyseDialog::openGraphArwndDialog(int direction) +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + SCTPGraphArwndDialog *sctp_dialog = new SCTPGraphArwndDialog(this, selected_assoc, cap_file_, direction); + + if (sctp_dialog->isMinimized() == true) { + sctp_dialog->showNormal(); + } else { + sctp_dialog->show(); + } + + sctp_dialog->raise(); + sctp_dialog->activateWindow(); +} + +void SCTPAssocAnalyseDialog::on_GraphArwnd_1_clicked() +{ + openGraphArwndDialog(1); +} + +void SCTPAssocAnalyseDialog::on_GraphArwnd_2_clicked() +{ + openGraphArwndDialog(2); +} diff --git a/ui/qt/sctp_assoc_analyse_dialog.h b/ui/qt/sctp_assoc_analyse_dialog.h new file mode 100644 index 00000000..ec361f4c --- /dev/null +++ b/ui/qt/sctp_assoc_analyse_dialog.h @@ -0,0 +1,77 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SCTP_ASSOC_ANALYSE_DIALOG_H +#define SCTP_ASSOC_ANALYSE_DIALOG_H + +#include + +#include + +#include + +#include + +#include "sctp_all_assocs_dialog.h" + +#include +#include +#include +#include +#include + + +namespace Ui { +class SCTPAssocAnalyseDialog; +} + +struct _sctp_assoc_info; + +class SCTPAssocAnalyseDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SCTPAssocAnalyseDialog(QWidget *parent = 0, const _sctp_assoc_info *assoc = NULL, + capture_file *cf = NULL); + ~SCTPAssocAnalyseDialog(); + + void fillTabs(const _sctp_assoc_info* selected_assoc); + static const _sctp_assoc_info* findAssocForPacket(capture_file* cf); + static const _sctp_assoc_info* findAssoc(QWidget *parent, guint16 assoc_id); + +public slots: + void setCaptureFile(capture_file *cf) { cap_file_ = cf; } + +private slots: + void on_GraphTSN_2_clicked(); + void on_GraphTSN_1_clicked(); + void on_chunkStatisticsButton_clicked(); + void on_setFilterButton_clicked(); + + void on_GraphBytes_1_clicked(); + void on_GraphBytes_2_clicked(); + + void on_GraphArwnd_1_clicked(); + void on_GraphArwnd_2_clicked(); + +private: + Ui::SCTPAssocAnalyseDialog *ui; + guint16 selected_assoc_id; + capture_file *cap_file_; + void openGraphDialog(int direction); + void openGraphByteDialog(int direction); + void openGraphArwndDialog(int direction); + + +signals: + void filterPackets(QString new_filter, bool force); +}; + +#endif // SCTP_ASSOC_ANALYSE_DIALOG_H diff --git a/ui/qt/sctp_assoc_analyse_dialog.ui b/ui/qt/sctp_assoc_analyse_dialog.ui new file mode 100644 index 00000000..01fa88db --- /dev/null +++ b/ui/qt/sctp_assoc_analyse_dialog.ui @@ -0,0 +1,610 @@ + + + SCTPAssocAnalyseDialog + + + + 0 + 0 + 826 + 672 + + + + + 0 + 0 + + + + Wireshark - Analyse Association + + + + + + + + 5 + 10 + 821 + 661 + + + + TabWidget + + + 2 + + + + Qt::NoFocus + + + Statistics + + + + + 20 + 40 + 781 + 231 + + + + + + + Number of Data Bytes from EP1 to EP2: + + + + + + + - + + + + + + + Number of Data Bytes from EP2 to EP1: + + + + + + + Checksum Type: + + + + + + + - + + + + + + + - + + + + + + + - + + + + + + + Number of Data Chunks from EP2 to EP1: + + + + + + + - + + + + + + + Number of Data Chunks from EP1 to EP2: + + + + + + + + + 10 + 540 + 791 + 55 + + + + + + + Qt::NoFocus + + + Filter Association + + + + + + + Qt::NoFocus + + + Chunk Statistics + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + 10 + 510 + 791 + 20 + + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + + + Endpoint 1 + + + + + 10 + 10 + 791 + 41 + + + + Complete List of IP addresses from INIT Chunk: + + + + + + 10 + 210 + 791 + 301 + + + + + + + - + + + + + + + - + + + + + + + - + + + + + + + Minimum Number of Outbound Streams: + + + + + + + Provided Number of Outbound Streams: + + + + + + + Minimum Number of Inbound Streams: + + + + + + + Sent Verification Tag: + + + + + + + - + + + + + + + Port: + + + + + + + - + + + + + + + - + + + + + + + Requested Number of Inbound Streams: + + + + + + + + + 10 + 60 + 791 + 141 + + + + + + + 10 + 540 + 791 + 55 + + + + + + + Qt::NoFocus + + + Graph Bytes + + + + + + + Qt::NoFocus + + + Graph TSN + + + + + + + Graph Arwnd + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + 10 + 520 + 791 + 20 + + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + + + Endpoint 2 + + + + + 10 + 210 + 791 + 301 + + + + + + + Requested Number of Inbound Streams: + + + + + + + - + + + + + + + - + + + + + + + Sent Verification Tag: + + + + + + + Minimum Number of Outbound Streams: + + + + + + + Port: + + + + + + + Minimum Number of Inbound Streams: + + + + + + + - + + + + + + + - + + + + + + + - + + + + + + + Provided Number of Outbound Streams: + + + + + + + - + + + + + + + + + 10 + 10 + 791 + 41 + + + + Complete List of IP addresses from INIT_ACK Chunk: + + + + + + 10 + 60 + 791 + 141 + + + + + + + 10 + 540 + 791 + 55 + + + + + + + Qt::NoFocus + + + Graph Bytes + + + + + + + Qt::NoFocus + + + Graph TSN + + + + + + + Graph Arwnd + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + 10 + 520 + 791 + 20 + + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + + + + + + buttonBox + clicked() + SCTPAssocAnalyseDialog + close() + + + 494 + 361 + + + 523 + 433 + + + + + buttonBox_2 + clicked() + SCTPAssocAnalyseDialog + close() + + + 471 + 365 + + + 475 + 384 + + + + + buttonBox_3 + clicked() + SCTPAssocAnalyseDialog + close() + + + 502 + 362 + + + 520 + 459 + + + + + diff --git a/ui/qt/sctp_chunk_statistics_dialog.cpp b/ui/qt/sctp_chunk_statistics_dialog.cpp new file mode 100644 index 00000000..f77c2edd --- /dev/null +++ b/ui/qt/sctp_chunk_statistics_dialog.cpp @@ -0,0 +1,330 @@ +/* sctp_chunk_statistics_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "sctp_chunk_statistics_dialog.h" +#include "sctp_assoc_analyse_dialog.h" +#include +#include "uat_dialog.h" + +#include +#include + +#include "ui/tap-sctp-analysis.h" +#include + +SCTPChunkStatisticsDialog::SCTPChunkStatisticsDialog(QWidget *parent, const sctp_assoc_info_t *assoc, + capture_file *cf) : + QDialog(parent), + ui(new Ui::SCTPChunkStatisticsDialog), + cap_file_(cf) +{ + Q_ASSERT(assoc); + selected_assoc_id = assoc->assoc_id; + + ui->setupUi(this); + Qt::WindowFlags flags = Qt::Window | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint + | Qt::WindowMaximizeButtonHint + | Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); + ui->tableWidget->verticalHeader()->setSectionsClickable(true); + ui->tableWidget->verticalHeader()->setSectionsMovable(true); + + + ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + + ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + + this->setWindowTitle(QString(tr("SCTP Chunk Statistics: %1 Port1 %2 Port2 %3")) + .arg(gchar_free_to_qstring(cf_get_display_name(cap_file_))) + .arg(assoc->port1).arg(assoc->port2)); +// connect(ui->tableWidget->verticalHeader(), &QHeaderView::sectionMoved, this, &SCTPChunkStatisticsDialog::on_sectionMoved); + + ctx_menu_.addAction(ui->actionHideChunkType); + ctx_menu_.addAction(ui->actionChunkTypePreferences); + ctx_menu_.addAction(ui->actionShowAllChunkTypes); + initializeChunkMap(); + fillTable(false, assoc); +} + +SCTPChunkStatisticsDialog::~SCTPChunkStatisticsDialog() +{ + delete ui; +} + +void SCTPChunkStatisticsDialog::initializeChunkMap() +{ + struct chunkTypes temp; + gchar buf[16]; + + for (int i = 0; i < 256; i++) { + temp.id = i; + temp.row = i; + snprintf(buf, sizeof buf, "%d", i); + (void) g_strlcpy(temp.name, val_to_str_const(i, chunk_type_values, "NA"), sizeof temp.name); + if (strcmp(temp.name, "NA") == 0) { + temp.hide = 1; + (void) g_strlcpy(temp.name, buf, sizeof temp.name); + } else { + temp.hide = 0; + } + chunks.insert(i, temp); + } +} + +void SCTPChunkStatisticsDialog::fillTable(bool all, const sctp_assoc_info_t *selected_assoc) +{ + if (!selected_assoc) { + selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + } + + FILE* fp = NULL; + + pref_t *pref = prefs_find_preference(prefs_find_module("sctp"),"statistics_chunk_types"); + if (!pref) { + ws_log(LOG_DOMAIN_QTUI, LOG_LEVEL_ERROR, "Can't find preference sctp/statistics_chunk_types"); + return; + } + uat_t *uat = prefs_get_uat_value(pref); + gchar* fname = uat_get_actual_filename(uat,TRUE); + bool init = false; + + if (!fname) { + init = true; + } else { + fp = ws_fopen(fname,"r"); + + if (!fp) { + if (errno == ENOENT) { + init = true; + } else { + ws_log(LOG_DOMAIN_QTUI, LOG_LEVEL_ERROR, "Can't open %s: %s", fname, g_strerror(errno)); + return; + } + } + } + g_free (fname); + + if (init || all) { + int i, j = 0; + + for (i = 0; i < chunks.size(); i++) { + if (!chunks.value(i).hide) { + ui->tableWidget->setRowCount(ui->tableWidget->rowCount()+1); + ui->tableWidget->setVerticalHeaderItem(j, new QTableWidgetItem(QString("%1").arg(chunks.value(i).name))); + ui->tableWidget->setItem(j,0, new QTableWidgetItem(QString("%1").arg(selected_assoc->chunk_count[chunks.value(i).id]))); + ui->tableWidget->setItem(j,1, new QTableWidgetItem(QString("%1").arg(selected_assoc->ep1_chunk_count[chunks.value(i).id]))); + ui->tableWidget->setItem(j,2, new QTableWidgetItem(QString("%1").arg(selected_assoc->ep2_chunk_count[chunks.value(i).id]))); + j++; + } + } + for (i = 0; i < chunks.size(); i++) { + if (chunks.value(i).hide) { + ui->tableWidget->setRowCount(ui->tableWidget->rowCount()+1); + ui->tableWidget->setVerticalHeaderItem(j, new QTableWidgetItem(QString("%1").arg(chunks.value(i).name))); + ui->tableWidget->setItem(j,0, new QTableWidgetItem(QString("%1").arg(selected_assoc->chunk_count[chunks.value(i).id]))); + ui->tableWidget->setItem(j,1, new QTableWidgetItem(QString("%1").arg(selected_assoc->ep1_chunk_count[chunks.value(i).id]))); + ui->tableWidget->setItem(j,2, new QTableWidgetItem(QString("%1").arg(selected_assoc->ep2_chunk_count[chunks.value(i).id]))); + ui->tableWidget->hideRow(j); + j++; + } + } + } else { + char line[100]; + char *token, id[5]; + int i = 0, j = 0; + struct chunkTypes temp; + + while (fgets(line, (int)sizeof line, fp)) { + if (line[0] == '#') + continue; + token = strtok(line, ","); + if (!token) + continue; + /* Get rid of the quotation marks */ + QString ch = QString(token).mid(1, (int)strlen(token)-2); + (void) g_strlcpy(id, qPrintable(ch), sizeof id); + if (!ws_strtoi32(id, NULL, &temp.id)) + continue; + temp.hide = 0; + temp.name[0] = '\0'; + while (token != NULL) { + token = strtok(NULL, ","); + if (token) { + if ((strstr(token, "Hide"))) { + temp.hide = 1; + } else if ((strstr(token, "Show"))) { + temp.hide = 0; + } else { + QString ch2 = QString(token).mid(1, (int)strlen(token)-2); + (void) g_strlcpy(temp.name, qPrintable(ch2), sizeof temp.name); + } + } + } + if (!temp.hide) { + ui->tableWidget->setRowCount(ui->tableWidget->rowCount()+1); + ui->tableWidget->setVerticalHeaderItem(j, new QTableWidgetItem(QString("%1").arg(temp.name))); + ui->tableWidget->setItem(j,0, new QTableWidgetItem(QString("%1").arg(selected_assoc->chunk_count[temp.id]))); + ui->tableWidget->setItem(j,1, new QTableWidgetItem(QString("%1").arg(selected_assoc->ep1_chunk_count[temp.id]))); + ui->tableWidget->setItem(j,2, new QTableWidgetItem(QString("%1").arg(selected_assoc->ep2_chunk_count[temp.id]))); + j++; + } + chunks.insert(i, temp); + i++; + } + j = ui->tableWidget->rowCount(); + for (i = 0; i < chunks.size(); i++) { + if (chunks.value(i).hide) { + ui->tableWidget->setRowCount(ui->tableWidget->rowCount()+1); + ui->tableWidget->setVerticalHeaderItem(j, new QTableWidgetItem(QString("%1").arg(chunks.value(i).name))); + ui->tableWidget->setItem(j,0, new QTableWidgetItem(QString("%1").arg(selected_assoc->chunk_count[chunks.value(i).id]))); + ui->tableWidget->setItem(j,1, new QTableWidgetItem(QString("%1").arg(selected_assoc->ep1_chunk_count[chunks.value(i).id]))); + ui->tableWidget->setItem(j,2, new QTableWidgetItem(QString("%1").arg(selected_assoc->ep2_chunk_count[chunks.value(i).id]))); + ui->tableWidget->hideRow(j); + j++; + } + } + } + if (fp) + fclose(fp); +} + +void SCTPChunkStatisticsDialog::contextMenuEvent(QContextMenuEvent * event) +{ + selected_point = event->pos(); + QTableWidgetItem *item = ui->tableWidget->itemAt(selected_point.x(), selected_point.y()-60); + if (item) { + ctx_menu_.popup(event->globalPos()); + } +} + + + +void SCTPChunkStatisticsDialog::on_pushButton_clicked() +{ + FILE* fp; + + pref_t *pref = prefs_find_preference(prefs_find_module("sctp"),"statistics_chunk_types"); + if (!pref) { + ws_log(LOG_DOMAIN_QTUI, LOG_LEVEL_ERROR, "Can't find preference sctp/statistics_chunk_types"); + return; + } + + uat_t *uat = prefs_get_uat_value(pref); + + gchar* fname = uat_get_actual_filename(uat,TRUE); + + if (!fname) { + return; + } + fp = ws_fopen(fname,"w"); + + if (!fp && errno == ENOENT) { + gchar *pf_dir_path = NULL; + if (create_persconffile_dir(&pf_dir_path) != 0) { + g_free (pf_dir_path); + return; + } + fp = ws_fopen(fname,"w"); + } + + if (!fp) { + return; + } + + g_free (fname); + + fprintf(fp,"# This file is automatically generated, DO NOT MODIFY.\n"); + char str[40]; + struct chunkTypes tempChunk; + + for (int i = 0; i < chunks.size(); i++) { + tempChunk = chunks.value(i); + snprintf(str, sizeof str, "\"%d\",\"%s\",\"%s\"\n", tempChunk.id, tempChunk.name, tempChunk.hide==0?"Show":"Hide"); + fputs(str, fp); + void *rec = g_malloc0(uat->record_size); + uat_add_record(uat, rec, TRUE); + if (uat->free_cb) { + uat->free_cb(rec); + } + g_free(rec); + } + + fclose(fp); +} + +/*void SCTPChunkStatisticsDialog::on_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) +{ +}*/ + +void SCTPChunkStatisticsDialog::on_actionHideChunkType_triggered() +{ + int row; + + QTableWidgetItem *itemPoint = ui->tableWidget->itemAt(selected_point.x(), selected_point.y()-60); + if (itemPoint) { + row = itemPoint->row(); + ui->tableWidget->hideRow(row); + QTableWidgetItem *item = ui->tableWidget->verticalHeaderItem(row); + QMap::iterator iter; + for (iter = chunks.begin(); iter != chunks.end(); ++iter) { + if (strcmp(iter.value().name, item->text().toUtf8().constData()) == 0) { + iter.value().hide = true; + break; + } + } + } + +} + +void SCTPChunkStatisticsDialog::on_actionChunkTypePreferences_triggered() +{ + gchar* err = NULL; + + pref_t *pref = prefs_find_preference(prefs_find_module("sctp"),"statistics_chunk_types"); + if (!pref) { + ws_log(LOG_DOMAIN_QTUI, LOG_LEVEL_ERROR, "Can't find preference sctp/statistics_chunk_types"); + return; + } + + uat_t *uat = prefs_get_uat_value(pref); + uat_clear(uat); + + if (!uat_load(uat, NULL, &err)) { + /* XXX - report this through the GUI */ + ws_log(LOG_DOMAIN_QTUI, LOG_LEVEL_WARNING, "Error loading table '%s': %s", uat->name, err); + g_free(err); + } + + UatDialog *uatdialog = new UatDialog(this, uat); + uatdialog->exec(); + // Emitting PacketDissectionChanged directly from a QDialog can cause + // problems on macOS. + mainApp->flushAppSignals(); + + ui->tableWidget->clear(); + ui->tableWidget->setRowCount(0); + ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(QString(tr("Association")))); + ui->tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem(QString(tr("Endpoint 1")))); + ui->tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(QString(tr("Endpoint 2")))); + fillTable(); +} + +void SCTPChunkStatisticsDialog::on_actionShowAllChunkTypes_triggered() +{ + ui->tableWidget->clear(); + ui->tableWidget->setRowCount(0); + ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(QString(tr("Association")))); + ui->tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem(QString(tr("Endpoint 1")))); + ui->tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(QString(tr("Endpoint 2")))); + initializeChunkMap(); + fillTable(true); +} diff --git a/ui/qt/sctp_chunk_statistics_dialog.h b/ui/qt/sctp_chunk_statistics_dialog.h new file mode 100644 index 00000000..aa45f752 --- /dev/null +++ b/ui/qt/sctp_chunk_statistics_dialog.h @@ -0,0 +1,83 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SCTP_CHUNK_STATISTICS_DIALOG_H +#define SCTP_CHUNK_STATISTICS_DIALOG_H + +#include +#include + +#include +#include +#include +#include "epan/packet.h" +#include "epan/value_string.h" +#include +#include +#include +#include +#include "wireshark_application.h" + +#include +#include +#include +#include + +namespace Ui { +class SCTPChunkStatisticsDialog; +} + +struct _sctp_assoc_info; + +class SCTPChunkStatisticsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SCTPChunkStatisticsDialog(QWidget *parent = 0, const _sctp_assoc_info *assoc = NULL, capture_file *cf = NULL); + ~SCTPChunkStatisticsDialog(); + +public slots: + void setCaptureFile(capture_file *cf) { cap_file_ = cf; } + +private slots: + // void on_sectionClicked(int row); + // void on_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); + void on_pushButton_clicked(); + void on_actionHideChunkType_triggered(); + void on_actionChunkTypePreferences_triggered(); + void contextMenuEvent(QContextMenuEvent * event); + + void on_actionShowAllChunkTypes_triggered(); + +signals: + // void sectionClicked(int); + // void sectionMoved(int, int, int); + +private: + Ui::SCTPChunkStatisticsDialog *ui; + guint16 selected_assoc_id; + capture_file *cap_file_; + QMenu ctx_menu_; + QPoint selected_point; + + struct chunkTypes { + int row; + int id; + int hide; + char name[30]; + }; + + QMap chunks, tempChunks; + + void initializeChunkMap(); + void fillTable(bool all = false, const _sctp_assoc_info *selected_assoc = NULL); +}; + +#endif // SCTP_CHUNK_STATISTICS_DIALOG_H diff --git a/ui/qt/sctp_chunk_statistics_dialog.ui b/ui/qt/sctp_chunk_statistics_dialog.ui new file mode 100644 index 00000000..e13bfce8 --- /dev/null +++ b/ui/qt/sctp_chunk_statistics_dialog.ui @@ -0,0 +1,157 @@ + + + SCTPChunkStatisticsDialog + + + + 0 + 0 + 830 + 673 + + + + + 0 + 0 + + + + Dialog + + + + + 510 + 610 + 311 + 51 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Close + + + + + + 0 + 10 + 831 + 591 + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + 0 + + + false + + + true + + + false + + + + Association + + + + + Endpoint 1 + + + + + Endpoint 2 + + + + + + + 0 + 620 + 176 + 28 + + + + Save Chunk Type Order + + + + + Hide Chunk Type + + + Remove the chunk type from the table + + + + + Chunk Type Preferences + + + Go to the chunk type preferences dialog to show or hide other chunk types + + + + + Show All Registered Chunk Types + + + Show all chunk types with defined names + + + + + + + buttonBox + clicked(QAbstractButton*) + SCTPChunkStatisticsDialog + close() + + + 456 + 483 + + + 445 + 563 + + + + + buttonBox + clicked(QAbstractButton*) + SCTPChunkStatisticsDialog + close() + + + 357 + 486 + + + 355 + 542 + + + + + diff --git a/ui/qt/sctp_graph_arwnd_dialog.cpp b/ui/qt/sctp_graph_arwnd_dialog.cpp new file mode 100644 index 00000000..2734f28a --- /dev/null +++ b/ui/qt/sctp_graph_arwnd_dialog.cpp @@ -0,0 +1,178 @@ +/* sctp_graph_arwnd_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "sctp_graph_arwnd_dialog.h" +#include +#include "sctp_assoc_analyse_dialog.h" + +#include +#include +#include +#include "epan/packet.h" + +#include "ui/tap-sctp-analysis.h" + +#include +#include +#include "sctp_graph_dialog.h" + +SCTPGraphArwndDialog::SCTPGraphArwndDialog(QWidget *parent, const sctp_assoc_info_t *assoc, + _capture_file *cf, int dir) : + QDialog(parent), + ui(new Ui::SCTPGraphArwndDialog), + cap_file_(cf), + frame_num(0), + direction(dir), + startArwnd(0) +{ + Q_ASSERT(assoc); + selected_assoc_id = assoc->assoc_id; + + ui->setupUi(this); + Qt::WindowFlags flags = Qt::Window | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint + | Qt::WindowMaximizeButtonHint + | Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); + this->setWindowTitle(QString(tr("SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3")) + .arg(gchar_free_to_qstring(cf_get_display_name(cap_file_))).arg(assoc->port1).arg(assoc->port2)); + if ((direction == 1 && assoc->n_array_tsn1 == 0) || (direction == 2 && assoc->n_array_tsn2 == 0)) { + QMessageBox msgBox; + msgBox.setText(tr("No Data Chunks sent")); + msgBox.exec(); + return; + } else { + drawGraph(assoc); + } +} + +SCTPGraphArwndDialog::~SCTPGraphArwndDialog() +{ + delete ui; +} + +void SCTPGraphArwndDialog::drawArwndGraph(const sctp_assoc_info_t *selected_assoc) +{ + GList *listSACK = Q_NULLPTR, *tlist; + struct sack_chunk_header *sack_header; + struct nr_sack_chunk_header *nr_sack_header; + tsn_t *tsn; + guint8 type; + guint32 arwnd=0; + + if (direction == 1) { + listSACK = g_list_last(selected_assoc->sack1); + startArwnd = selected_assoc->arwnd2; + } else { + listSACK = g_list_last(selected_assoc->sack2); + startArwnd = selected_assoc->arwnd1; + } + bool detect_max_arwnd = (startArwnd == 0) ? true : false; + + while (listSACK) { + tsn = gxx_list_data(tsn_t*, listSACK); + tlist = g_list_first(tsn->tsns); + while (tlist) { + type = gxx_list_data(struct chunk_header *, tlist)->type; + if (type == SCTP_SACK_CHUNK_ID) { + sack_header = gxx_list_data(struct sack_chunk_header *, tlist); + arwnd = g_ntohl(sack_header->a_rwnd); + } else if (type == SCTP_NR_SACK_CHUNK_ID) { + nr_sack_header = gxx_list_data(struct nr_sack_chunk_header *, tlist); + arwnd = g_ntohl(nr_sack_header->a_rwnd); + } + if (detect_max_arwnd && startArwnd < arwnd) { + startArwnd = arwnd; + } + ya.append(arwnd); + xa.append(tsn->secs + tsn->usecs/1000000.0); + fa.append(tsn->frame_number); + tlist = gxx_list_next(tlist); + } + listSACK = gxx_list_previous(listSACK); + } + + QCPScatterStyle myScatter; + myScatter.setShape(QCPScatterStyle::ssCircle); + myScatter.setSize(3); + + // create graph and assign data to it: + + // Add Arwnd graph + if (xa.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(ui->sctpPlot->xAxis, ui->sctpPlot->yAxis); + gr->setName(QString(tr("Arwnd"))); + myScatter.setPen(QPen(Qt::red)); + myScatter.setBrush(Qt::red); + ui->sctpPlot->graph(0)->setScatterStyle(myScatter); + ui->sctpPlot->graph(0)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(0)->setData(xa, ya); + } + + ui->sctpPlot->xAxis->setLabel(tr("time [secs]")); + ui->sctpPlot->yAxis->setLabel(tr("Advertised Receiver Window [Bytes]")); + + // set axes ranges, so we see all data: + QCPRange myXArwndRange(0, (selected_assoc->max_secs+1)); + // QCPRange myXArwndRange(0, 1); + QCPRange myYArwndRange(0, startArwnd); + ui->sctpPlot->xAxis->setRange(myXArwndRange); + ui->sctpPlot->yAxis->setRange(myYArwndRange); +} + + +void SCTPGraphArwndDialog::drawGraph(const sctp_assoc_info_t *selected_assoc) +{ + ui->sctpPlot->clearGraphs(); + drawArwndGraph(selected_assoc); + ui->sctpPlot->setInteractions(QCP::iRangeZoom | QCP::iRangeDrag | QCP::iSelectPlottables); + ui->sctpPlot->axisRect(0)->setRangeZoomAxes(ui->sctpPlot->xAxis, ui->sctpPlot->yAxis); + ui->sctpPlot->axisRect(0)->setRangeZoom(Qt::Horizontal); + connect(ui->sctpPlot, &QCustomPlot::plottableClick, this, &SCTPGraphArwndDialog::graphClicked); + ui->sctpPlot->replot(); +} + + +void SCTPGraphArwndDialog::on_pushButton_4_clicked() +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + ui->sctpPlot->xAxis->setRange(selected_assoc->min_secs+selected_assoc->min_usecs/1000000.0, selected_assoc->max_secs+selected_assoc->max_usecs/1000000.0); + ui->sctpPlot->yAxis->setRange(0, startArwnd); + ui->sctpPlot->replot(); +} + +void SCTPGraphArwndDialog::graphClicked(QCPAbstractPlottable* plottable, int, QMouseEvent* event) +{ + if (plottable->name().contains("Arwnd", Qt::CaseInsensitive)) { + double times = ui->sctpPlot->xAxis->pixelToCoord(event->pos().x()); + int i=0; + for (i = 0; i < xa.size(); i++) { + if (times <= xa.value(i)) { + frame_num = fa.at(i); + break; + } + } + if (cap_file_ && frame_num > 0) { + cf_goto_frame(cap_file_, frame_num); + } + + ui->hintLabel->setText(QString(tr("Graph %1: a_rwnd=%2 Time=%3 secs ")) + .arg(plottable->name()) + .arg(ya.value(i)) + .arg(xa.value(i))); + } +} + + +void SCTPGraphArwndDialog::on_saveButton_clicked() +{ + SCTPGraphDialog::save_graph(this, ui->sctpPlot); +} diff --git a/ui/qt/sctp_graph_arwnd_dialog.h b/ui/qt/sctp_graph_arwnd_dialog.h new file mode 100644 index 00000000..c74d43d3 --- /dev/null +++ b/ui/qt/sctp_graph_arwnd_dialog.h @@ -0,0 +1,62 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SCTP_GRAPH_ARWND_DIALOG_H +#define SCTP_GRAPH_ARWND_DIALOG_H + +#include +#include + +#include "cfile.h" + +#include + +namespace Ui { +class SCTPGraphArwndDialog; +} + +class QCPAbstractPlottable; + +struct _sctp_assoc_info; + +class SCTPGraphArwndDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SCTPGraphArwndDialog(QWidget *parent = 0, const _sctp_assoc_info *assoc = NULL, + capture_file *cf = NULL, int dir = 0); + ~SCTPGraphArwndDialog(); + +public slots: + void setCaptureFile(capture_file *cf) { cap_file_ = cf; } + +private slots: + void on_pushButton_4_clicked(); + + void graphClicked(QCPAbstractPlottable* plottable, int, QMouseEvent* event); + + void on_saveButton_clicked(); + +private: + Ui::SCTPGraphArwndDialog *ui; + guint16 selected_assoc_id; + capture_file *cap_file_; + int frame_num; + int direction; + guint32 startArwnd; + QVector xa, ya; + QVector fa; + // QVector typeStrings; + + void drawGraph(const _sctp_assoc_info *selected_assoc); + void drawArwndGraph(const _sctp_assoc_info *selected_assoc); +}; + +#endif // SCTP_GRAPH_DIALOG_H diff --git a/ui/qt/sctp_graph_arwnd_dialog.ui b/ui/qt/sctp_graph_arwnd_dialog.ui new file mode 100644 index 00000000..8ec86f8e --- /dev/null +++ b/ui/qt/sctp_graph_arwnd_dialog.ui @@ -0,0 +1,125 @@ + + + SCTPGraphArwndDialog + + + + 0 + 0 + 826 + 546 + + + + SCTP Graph + + + + + + + + + 0 + 1 + + + + + + + + + + + true + + + + + + + + + Qt::NoFocus + + + Reset to full size + + + + + + + Save Graph + + + + + + + Qt::Horizontal + + + + 428 + 20 + + + + + + + + Qt::NoFocus + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + goToPacket + + + Go to Packet + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+
+ + + + buttonBox + clicked(QAbstractButton*) + SCTPGraphArwndDialog + close() + + + 706 + 530 + + + 703 + 574 + + + + +
diff --git a/ui/qt/sctp_graph_byte_dialog.cpp b/ui/qt/sctp_graph_byte_dialog.cpp new file mode 100644 index 00000000..bd91c088 --- /dev/null +++ b/ui/qt/sctp_graph_byte_dialog.cpp @@ -0,0 +1,181 @@ +/* sctp_graph_byte_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "sctp_graph_byte_dialog.h" +#include + +#include +#include +#include +#include "epan/packet.h" + +#include "ui/tap-sctp-analysis.h" + +#include +#include +#include "sctp_graph_dialog.h" +#include "sctp_assoc_analyse_dialog.h" + +SCTPGraphByteDialog::SCTPGraphByteDialog(QWidget *parent, const sctp_assoc_info_t *assoc, + capture_file *cf, int dir) : + QDialog(parent), + ui(new Ui::SCTPGraphByteDialog), + cap_file_(cf), + frame_num(0), + direction(dir) +{ + Q_ASSERT(assoc); + selected_assoc_id = assoc->assoc_id; + + ui->setupUi(this); + Qt::WindowFlags flags = Qt::Window | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint + | Qt::WindowMaximizeButtonHint + | Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); + this->setWindowTitle(QString(tr("SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3")) + .arg(gchar_free_to_qstring(cf_get_display_name(cap_file_))).arg(assoc->port1).arg(assoc->port2)); + if ((direction == 1 && assoc->n_array_tsn1 == 0) || (direction == 2 && assoc->n_array_tsn2 == 0)) { + QMessageBox msgBox; + msgBox.setText(tr("No Data Chunks sent")); + msgBox.exec(); + return; + } else { + drawGraph(); + } +} + +SCTPGraphByteDialog::~SCTPGraphByteDialog() +{ + delete ui; +} + + +void SCTPGraphByteDialog::drawBytesGraph(const sctp_assoc_info_t *selected_assoc) +{ + GList *listTSN = Q_NULLPTR, *tlist = Q_NULLPTR; + tsn_t *tsn = Q_NULLPTR; + guint8 type; + guint32 maxBytes; + guint64 sumBytes = 0; + + if (direction == 1) { + maxBytes = selected_assoc->n_data_bytes_ep1; + listTSN = g_list_last(selected_assoc->tsn1); + } else { + maxBytes = selected_assoc->n_data_bytes_ep2; + listTSN = g_list_last(selected_assoc->tsn2); + } + + + while (listTSN) { + tsn = gxx_list_data(tsn_t*, listTSN); + tlist = g_list_first(tsn->tsns); + guint16 length; + while (tlist) + { + type = gxx_list_data(struct chunk_header *, tlist)->type; + if (type == SCTP_DATA_CHUNK_ID || type == SCTP_I_DATA_CHUNK_ID) { + length = g_ntohs(gxx_list_data(struct data_chunk_header *, tlist)->length); + if (type == SCTP_DATA_CHUNK_ID) + length -= DATA_CHUNK_HEADER_LENGTH; + else + length -= I_DATA_CHUNK_HEADER_LENGTH; + sumBytes += length; + yb.append(sumBytes); + xb.append(tsn->secs + tsn->usecs/1000000.0); + fb.append(tsn->frame_number); + } + tlist = gxx_list_next(tlist); + } + listTSN = gxx_list_previous(listTSN); + } + + + QCPScatterStyle myScatter; + myScatter.setShape(QCPScatterStyle::ssCircle); + myScatter.setSize(3); + + // create graph and assign data to it: + + // Add Bytes graph + if (xb.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(ui->sctpPlot->xAxis, ui->sctpPlot->yAxis); + gr->setName(QString(tr("Bytes"))); + myScatter.setPen(QPen(Qt::red)); + myScatter.setBrush(Qt::red); + ui->sctpPlot->graph(0)->setScatterStyle(myScatter); + ui->sctpPlot->graph(0)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(0)->setData(xb, yb); + } + ui->sctpPlot->xAxis->setLabel(tr("time [secs]")); + ui->sctpPlot->yAxis->setLabel(tr("Received Bytes")); + + // set axes ranges, so we see all data: + QCPRange myXByteRange(0, (selected_assoc->max_secs+1)); + QCPRange myYByteRange(0, maxBytes); + ui->sctpPlot->xAxis->setRange(myXByteRange); + ui->sctpPlot->yAxis->setRange(myYByteRange); +} + + +void SCTPGraphByteDialog::drawGraph() +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + ui->sctpPlot->clearGraphs(); + drawBytesGraph(selected_assoc); + ui->sctpPlot->setInteractions(QCP::iRangeZoom | QCP::iRangeDrag | QCP::iSelectPlottables); + connect(ui->sctpPlot, &QCustomPlot::plottableClick, this, &SCTPGraphByteDialog::graphClicked); + ui->sctpPlot->replot(); +} + + +void SCTPGraphByteDialog::on_pushButton_4_clicked() +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + ui->sctpPlot->xAxis->setRange(selected_assoc->min_secs+selected_assoc->min_usecs/1000000.0, selected_assoc->max_secs+selected_assoc->max_usecs/1000000.0); + if (direction == 1) { + ui->sctpPlot->yAxis->setRange(0, selected_assoc->n_data_bytes_ep1); + } else { + ui->sctpPlot->yAxis->setRange(0, selected_assoc->n_data_bytes_ep2); + } + ui->sctpPlot->replot(); +} + +void SCTPGraphByteDialog::graphClicked(QCPAbstractPlottable* plottable, int, QMouseEvent* event) +{ + if (plottable->name().contains(tr("Bytes"), Qt::CaseInsensitive)) { + double bytes = ui->sctpPlot->yAxis->pixelToCoord(event->pos().y()); + int i; + for (i = 0; i < yb.size(); i++) { + if (bytes <= yb.value(i)) { + frame_num = fb.at(i); + break; + } + } + if (cap_file_ && frame_num > 0) { + cf_goto_frame(cap_file_, frame_num); + } + + ui->hintLabel->setText(QString(tr("Graph %1: Received bytes=%2 Time=%3 secs ")) + .arg(plottable->name()) + .arg(yb.value(i)) + .arg(xb.value(i))); + } +} + + +void SCTPGraphByteDialog::on_saveButton_clicked() +{ + SCTPGraphDialog::save_graph(this, ui->sctpPlot); +} diff --git a/ui/qt/sctp_graph_byte_dialog.h b/ui/qt/sctp_graph_byte_dialog.h new file mode 100644 index 00000000..e6785da9 --- /dev/null +++ b/ui/qt/sctp_graph_byte_dialog.h @@ -0,0 +1,60 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SCTP_GRAPH_BYTE_DIALOG_H +#define SCTP_GRAPH_BYTE_DIALOG_H + +#include +#include + +#include "cfile.h" + +#include + +namespace Ui { +class SCTPGraphByteDialog; +} + +class QCPAbstractPlottable; + +struct _sctp_assoc_info; + +class SCTPGraphByteDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SCTPGraphByteDialog(QWidget *parent = 0, const _sctp_assoc_info *assoc = NULL, + capture_file *cf = NULL, int dir = 0); + ~SCTPGraphByteDialog(); + +public slots: + void setCaptureFile(capture_file *cf) { cap_file_ = cf; } + +private slots: + void on_pushButton_4_clicked(); + + void graphClicked(QCPAbstractPlottable* plottable, int, QMouseEvent* event); + + void on_saveButton_clicked(); + +private: + Ui::SCTPGraphByteDialog *ui; + guint16 selected_assoc_id; + capture_file *cap_file_; + int frame_num; + int direction; + QVector xb, yb; + QVector fb; + + void drawGraph(); + void drawBytesGraph(const _sctp_assoc_info *selected_assoc); +}; + +#endif // SCTP_GRAPH_DIALOG_H diff --git a/ui/qt/sctp_graph_byte_dialog.ui b/ui/qt/sctp_graph_byte_dialog.ui new file mode 100644 index 00000000..b14baa86 --- /dev/null +++ b/ui/qt/sctp_graph_byte_dialog.ui @@ -0,0 +1,125 @@ + + + SCTPGraphByteDialog + + + + 0 + 0 + 987 + 546 + + + + SCTP Graph + + + + + + + + + 0 + 1 + + + + + + + + + + + true + + + + + + + + + Qt::NoFocus + + + Reset to full size + + + + + + + Save Graph + + + + + + + Qt::Horizontal + + + + 428 + 20 + + + + + + + + Qt::NoFocus + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + goToPacket + + + Go to Packet + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+
+ + + + buttonBox + clicked(QAbstractButton*) + SCTPGraphByteDialog + close() + + + 706 + 530 + + + 703 + 574 + + + + +
diff --git a/ui/qt/sctp_graph_dialog.cpp b/ui/qt/sctp_graph_dialog.cpp new file mode 100644 index 00000000..a26f0d11 --- /dev/null +++ b/ui/qt/sctp_graph_dialog.cpp @@ -0,0 +1,524 @@ +/* sctp_graph_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "sctp_graph_dialog.h" +#include +#include "sctp_assoc_analyse_dialog.h" + +#include +#include +#include +#include "epan/packet.h" + +#include "ui/tap-sctp-analysis.h" + +#include + +#include +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include "main_application.h" + +SCTPGraphDialog::SCTPGraphDialog(QWidget *parent, const sctp_assoc_info_t *assoc, + capture_file *cf, int dir) : + QDialog(parent), + ui(new Ui::SCTPGraphDialog), + cap_file_(cf), + frame_num(0), + direction(dir), + relative(false), + type(1) +{ + Q_ASSERT(assoc); + selected_assoc_id = assoc->assoc_id; + + ui->setupUi(this); + Qt::WindowFlags flags = Qt::Window | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint + | Qt::WindowMaximizeButtonHint + | Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); + this->setWindowTitle(QString(tr("SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3")) + .arg(gchar_free_to_qstring(cf_get_display_name(cap_file_))).arg(assoc->port1).arg(assoc->port2)); + if ((direction == 1 && assoc->n_array_tsn1 == 0) || (direction == 2 && assoc->n_array_tsn2 == 0)) { + QMessageBox msgBox; + msgBox.setText(tr("No Data Chunks sent")); + msgBox.exec(); + return; + } else { + drawGraph(assoc); + } +} + +SCTPGraphDialog::~SCTPGraphDialog() +{ + delete ui; +} + +void SCTPGraphDialog::drawNRSACKGraph(const sctp_assoc_info_t* selected_assoc) +{ + tsn_t *sack = Q_NULLPTR; + GList *list = Q_NULLPTR, *tlist = Q_NULLPTR; + guint16 gap_start=0, gap_end=0, i, numberOf_gaps, numberOf_nr_gaps; + guint8 type; + guint32 tsnumber, j = 0, min_tsn, rel = 0; + struct nr_sack_chunk_header *nr_sack_header = Q_NULLPTR; + struct gaps *nr_gap = Q_NULLPTR; + /* This holds the sum of gap acks and nr gap acks */ + guint16 total_gaps = 0; + + if (direction == 1) { + list = g_list_last(selected_assoc->sack1); + min_tsn = selected_assoc->min_tsn1; + } else { + list = g_list_last(selected_assoc->sack2); + min_tsn = selected_assoc->min_tsn2; + } + if (relative) { + rel = min_tsn; + } + while (list) { + sack = gxx_list_data(tsn_t*, list); + tlist = g_list_first(sack->tsns); + while (tlist) { + type = gxx_list_data(struct chunk_header *, tlist)->type; + if (type == SCTP_NR_SACK_CHUNK_ID) { + nr_sack_header = gxx_list_data(struct nr_sack_chunk_header *, tlist); + numberOf_nr_gaps=g_ntohs(nr_sack_header->nr_of_nr_gaps); + numberOf_gaps=g_ntohs(nr_sack_header->nr_of_gaps); + tsnumber = g_ntohl(nr_sack_header->cum_tsn_ack); + total_gaps = numberOf_gaps + numberOf_nr_gaps; + /* If the number of nr_gaps is greater than 0 */ + if (total_gaps > 0) { + nr_gap = &nr_sack_header->gaps[0]; + for (i = 0; i < total_gaps; i++) { + gap_start = g_ntohs(nr_gap->start); + gap_end = g_ntohs(nr_gap->end); + for (j = gap_start; j <= gap_end; j++) { + if (i >= numberOf_gaps) { + yn.append(j + tsnumber - rel); + xn.append(sack->secs + sack->usecs/1000000.0); + fn.append(sack->frame_number); + } else { + yg.append(j + tsnumber - rel); + xg.append(sack->secs + sack->usecs/1000000.0); + fg.append(sack->frame_number); + } + } + if (i < total_gaps-1) + nr_gap++; + } + + if (tsnumber>=min_tsn) { + ys.append(j + tsnumber - rel); + xs.append(sack->secs + sack->usecs/1000000.0); + fs.append(sack->frame_number); + } + } + } + tlist = gxx_list_next(tlist); + } + list = gxx_list_previous(list); + } +} + +void SCTPGraphDialog::drawSACKGraph(const sctp_assoc_info_t* selected_assoc) +{ + GList *listSACK = Q_NULLPTR, *tlist = Q_NULLPTR; + guint16 gap_start=0, gap_end=0, nr, dup_nr; + struct sack_chunk_header *sack_header = Q_NULLPTR; + struct gaps *gap = Q_NULLPTR; + tsn_t *tsn = Q_NULLPTR; + guint8 type; + guint32 tsnumber=0, rel = 0; + guint32 minTSN; + guint32 *dup_list = Q_NULLPTR; + int i, j; + + if (direction == 1) { + minTSN = selected_assoc->min_tsn1; + listSACK = g_list_last(selected_assoc->sack1); + } else { + minTSN = selected_assoc->min_tsn2; + listSACK = g_list_last(selected_assoc->sack2); + } + if (relative) { + rel = minTSN; + } + while (listSACK) { + tsn = gxx_list_data(tsn_t*, listSACK); + tlist = g_list_first(tsn->tsns); + while (tlist) { + type = gxx_list_data(struct chunk_header *, tlist)->type; + if (type == SCTP_SACK_CHUNK_ID) { + sack_header = gxx_list_data(struct sack_chunk_header *, tlist); + nr=g_ntohs(sack_header->nr_of_gaps); + tsnumber = g_ntohl(sack_header->cum_tsn_ack); + dup_nr=g_ntohs(sack_header->nr_of_dups); + if (nr>0) { // Gap Reports green + gap = &sack_header->gaps[0]; + for (i=0;istart); + gap_end = g_ntohs(gap->end); + for (j=gap_start; j<=gap_end; j++) { + yg.append(j + tsnumber - rel); + xg.append(tsn->secs + tsn->usecs/1000000.0); + fg.append(tsn->frame_number); + } + if (i < nr-1) + gap++; + } + } + if (tsnumber>=minTSN) { // CumTSNAck red + ys.append(tsnumber - rel); + xs.append(tsn->secs + tsn->usecs/1000000.0); + fs.append(tsn->frame_number); + } + if (dup_nr > 0) { // Duplicates cyan + dup_list = &sack_header->a_rwnd + 2 + nr; + for (i = 0; i < dup_nr; i++) { + tsnumber = g_ntohl(dup_list[i]); + if (tsnumber >= minTSN) { + yd.append(tsnumber - rel); + xd.append(tsn->secs + tsn->usecs/1000000.0); + fd.append(tsn->frame_number); + } + } + } + } + tlist = gxx_list_next(tlist); + } + listSACK = gxx_list_previous(listSACK); + } + + QCPScatterStyle myScatter; + myScatter.setShape(QCPScatterStyle::ssCircle); + myScatter.setSize(3); + + int graphcount = ui->sctpPlot->graphCount(); + // create graph and assign data to it: + + // Add SACK graph + if (xs.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("SACK")); + myScatter.setPen(QPen(Qt::red)); + myScatter.setBrush(Qt::red); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xs, ys); + typeStrings.insert(graphcount, QString(tr("CumTSNAck"))); + graphcount++; + } + + // Add Gap Acks + if (xg.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("GAP")); + myScatter.setPen(QPen(Qt::green)); + myScatter.setBrush(Qt::green); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xg, yg); + typeStrings.insert(graphcount, QString(tr("Gap Ack"))); + graphcount++; + } + + // Add NR Gap Acks + if (xn.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("NR_GAP")); + myScatter.setPen(QPen(Qt::blue)); + myScatter.setBrush(Qt::blue); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xn, yn); + typeStrings.insert(graphcount, QString(tr("NR Gap Ack"))); + graphcount++; + } + + // Add Duplicates + if (xd.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("DUP")); + myScatter.setPen(QPen(Qt::cyan)); + myScatter.setBrush(Qt::cyan); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xd, yd); + typeStrings.insert(graphcount, QString(tr("Duplicate Ack"))); + } +} + +void SCTPGraphDialog::drawTSNGraph(const sctp_assoc_info_t* selected_assoc) +{ + GList *listTSN = Q_NULLPTR,*tlist = Q_NULLPTR; + tsn_t *tsn = Q_NULLPTR; + guint8 type; + guint32 tsnumber=0, rel = 0, minTSN; + + if (direction == 1) { + listTSN = g_list_last(selected_assoc->tsn1); + minTSN = selected_assoc->min_tsn1; + } else { + listTSN = g_list_last(selected_assoc->tsn2); + minTSN = selected_assoc->min_tsn2; + } + + if (relative) { + rel = minTSN; + } + + while (listTSN) { + tsn = gxx_list_data(tsn_t*, listTSN); + tlist = g_list_first(tsn->tsns); + while (tlist) + { + type = gxx_list_data(struct chunk_header *, tlist)->type; + if (type == SCTP_DATA_CHUNK_ID || type == SCTP_I_DATA_CHUNK_ID || type == SCTP_FORWARD_TSN_CHUNK_ID) { + tsnumber = g_ntohl(gxx_list_data(struct data_chunk_header *, tlist)->tsn); + yt.append(tsnumber - rel); + xt.append(tsn->secs + tsn->usecs/1000000.0); + ft.append(tsn->frame_number); + } + tlist = gxx_list_next(tlist); + } + listTSN = gxx_list_previous(listTSN); + } + + QCPScatterStyle myScatter; + myScatter.setShape(QCPScatterStyle::ssCircle); + myScatter.setSize(3); + + int graphcount = ui->sctpPlot->graphCount(); + // create graph and assign data to it: + + // Add TSN graph + if (xt.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("TSN")); + myScatter.setPen(QPen(Qt::black)); + myScatter.setBrush(Qt::black); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xt, yt); + typeStrings.insert(graphcount, QString(tr("TSN"))); + } +} + +void SCTPGraphDialog::drawGraph(const sctp_assoc_info_t* selected_assoc) +{ + if (!selected_assoc) { + selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + } + + guint32 maxTSN, minTSN; + + if (direction == 1) { + maxTSN = selected_assoc->max_tsn1; + minTSN = selected_assoc->min_tsn1; + } else { + maxTSN = selected_assoc->max_tsn2; + minTSN = selected_assoc->min_tsn2; + } + ui->sctpPlot->clearGraphs(); + xt.clear(); + yt.clear(); + xs.clear(); + ys.clear(); + xg.clear(); + yg.clear(); + xd.clear(); + yd.clear(); + xn.clear(); + yn.clear(); + ft.clear(); + fs.clear(); + fg.clear(); + fd.clear(); + fn.clear(); + typeStrings.clear(); + switch (type) { + case 1: + drawSACKGraph(selected_assoc); + drawNRSACKGraph(selected_assoc); + break; + case 2: + drawTSNGraph(selected_assoc); + break; + case 3: + drawTSNGraph(selected_assoc); + drawSACKGraph(selected_assoc); + drawNRSACKGraph(selected_assoc); + break; + default: + drawTSNGraph(selected_assoc); + drawSACKGraph(selected_assoc); + drawNRSACKGraph(selected_assoc); + break; + } + + // give the axes some labels: + ui->sctpPlot->xAxis->setLabel(tr("time [secs]")); + ui->sctpPlot->yAxis->setLabel(tr("TSNs")); + ui->sctpPlot->setInteractions(QCP::iRangeZoom | QCP::iRangeDrag | QCP::iSelectPlottables); + connect(ui->sctpPlot, &QCustomPlot::plottableClick, this, &SCTPGraphDialog::graphClicked); + // set axes ranges, so we see all data: + QCPRange myXRange(selected_assoc->min_secs, (selected_assoc->max_secs+1)); + if (relative) { + QCPRange myYRange(0, maxTSN - minTSN + 1); + ui->sctpPlot->yAxis->setRange(myYRange); + } else { + QCPRange myYRange(minTSN, maxTSN + 1); + ui->sctpPlot->yAxis->setRange(myYRange); + } + ui->sctpPlot->xAxis->setRange(myXRange); + ui->sctpPlot->replot(); +} + +void SCTPGraphDialog::on_pushButton_clicked() +{ + type = 1; + drawGraph(); +} + +void SCTPGraphDialog::on_pushButton_2_clicked() +{ + type = 2; + drawGraph(); +} + +void SCTPGraphDialog::on_pushButton_3_clicked() +{ + type = 3; + drawGraph(); +} + +void SCTPGraphDialog::on_pushButton_4_clicked() +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + ui->sctpPlot->xAxis->setRange(selected_assoc->min_secs, selected_assoc->max_secs+1); + if (relative) { + if (direction == 1) { + ui->sctpPlot->yAxis->setRange(0, selected_assoc->max_tsn1 - selected_assoc->min_tsn1); + } else { + ui->sctpPlot->yAxis->setRange(0, selected_assoc->max_tsn2 - selected_assoc->min_tsn2); + } + } else { + if (direction == 1) { + ui->sctpPlot->yAxis->setRange(selected_assoc->min_tsn1, selected_assoc->max_tsn1); + } else { + ui->sctpPlot->yAxis->setRange(selected_assoc->min_tsn2, selected_assoc->max_tsn2); + } + } + ui->sctpPlot->replot(); +} + +void SCTPGraphDialog::graphClicked(QCPAbstractPlottable* plottable, int, QMouseEvent* event) +{ + frame_num = 0; + int i=0; + double times = ui->sctpPlot->xAxis->pixelToCoord(event->pos().x()); + if (plottable->name().contains("TSN", Qt::CaseInsensitive)) { + for (i = 0; i < xt.size(); i++) { + if (times <= xt.value(i)) { + frame_num = ft.at(i); + break; + } + } + } else if (plottable->name().contains("SACK", Qt::CaseInsensitive)) { + for (i = 0; i < xs.size(); i++) { + if (times <= xs.value(i)) { + frame_num = fs.at(i); + break; + } + } + } else if (plottable->name().contains("DUP", Qt::CaseInsensitive)) { + for (i = 0; i < xd.size(); i++) { + if (times <= xd.value(i)) { + frame_num = fd.at(i); + break; + } + } + } else if (plottable->name().contains("NR_GAP", Qt::CaseInsensitive)) { + for (i = 0; i < xn.size(); i++) { + if (times <= xn.value(i)) { + frame_num = fn.at(i); + break; + } + } + } else if (plottable->name().contains("GAP", Qt::CaseInsensitive)) { + for (i = 0; i < xs.size(); i++) { + if (times <= xs.value(i)) { + frame_num = fs.at(i); + break; + } + } + } + if (cap_file_ && frame_num > 0) { + cf_goto_frame(cap_file_, frame_num); + } + ui->hintLabel->setText(QString(tr("%1: %2 Time: %3 secs ")) + .arg(plottable->name()) + .arg(floor(ui->sctpPlot->yAxis->pixelToCoord(event->pos().y()) + 0.5)) + .arg(ui->sctpPlot->xAxis->pixelToCoord(event->pos().x()))); +} + +void SCTPGraphDialog::save_graph(QDialog *dlg, QCustomPlot *plot) +{ + QString file_name, extension; + QDir path(mainApp->openDialogInitialDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + 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)"); + QString filter = QString("%1;;%2;;%3;;%4") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter); + + file_name = WiresharkFileDialog::getSaveFileName(dlg, mainApp->windowTitleString(tr("Save Graph As…")), + path.canonicalPath(), filter, &extension); + + if (file_name.length() > 0) { + bool save_ok = false; + if (extension.compare(pdf_filter) == 0) { + save_ok = plot->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = plot->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = plot->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = plot->saveJpg(file_name); + } + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } +} + + +void SCTPGraphDialog::on_saveButton_clicked() +{ + save_graph(this, ui->sctpPlot); +} + +void SCTPGraphDialog::on_relativeTsn_stateChanged(int arg1) +{ + relative = arg1; + drawGraph(); +} diff --git a/ui/qt/sctp_graph_dialog.h b/ui/qt/sctp_graph_dialog.h new file mode 100644 index 00000000..28aeccb0 --- /dev/null +++ b/ui/qt/sctp_graph_dialog.h @@ -0,0 +1,121 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SCTP_GRAPH_DIALOG_H +#define SCTP_GRAPH_DIALOG_H + +#include +#include + +#include "cfile.h" + +#include + +namespace Ui { +class SCTPGraphDialog; +} + +class QCPAbstractPlottable; +class QCustomPlot; + +struct _sctp_assoc_info; + +struct chunk_header { + guint8 type; + guint8 flags; + guint16 length; +}; + +struct data_chunk_header { + guint8 type; + guint8 flags; + guint16 length; + guint32 tsn; + guint16 sid; + guint16 ssn; + guint32 ppi; +}; + +struct gaps { + guint16 start; + guint16 end; +}; + +struct sack_chunk_header { + guint8 type; + guint8 flags; + guint16 length; + guint32 cum_tsn_ack; + guint32 a_rwnd; + guint16 nr_of_gaps; + guint16 nr_of_dups; + struct gaps gaps[1]; +}; + +struct nr_sack_chunk_header { + guint8 type; + guint8 flags; + guint16 length; + guint32 cum_tsn_ack; + guint32 a_rwnd; + guint16 nr_of_gaps; + guint16 nr_of_nr_gaps; + guint16 nr_of_dups; + guint16 reserved; + struct gaps gaps[1]; +}; + + +class SCTPGraphDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SCTPGraphDialog(QWidget *parent = 0, const _sctp_assoc_info *assoc = NULL, + capture_file *cf = NULL, int dir = 0); + ~SCTPGraphDialog(); + static void save_graph(QDialog *dlg, QCustomPlot *plot); + +public slots: + void setCaptureFile(capture_file *cf) { cap_file_ = cf; } + +private slots: + void on_pushButton_clicked(); + + void on_pushButton_2_clicked(); + + void on_pushButton_3_clicked(); + + void on_pushButton_4_clicked(); + + void graphClicked(QCPAbstractPlottable* plottable, int, QMouseEvent* event); + + void on_saveButton_clicked(); + + void on_relativeTsn_stateChanged(int arg1); + +private: + Ui::SCTPGraphDialog *ui; + guint16 selected_assoc_id; + capture_file *cap_file_; + int frame_num; + int direction; + QVector xt, yt, xs, ys, xg, yg, xd, yd, xn, yn; + QVector ft, fs, fg, fd, fn; + QVector typeStrings; + bool relative; + int type; + + void drawGraph(const _sctp_assoc_info* selected_assoc = NULL); + void drawTSNGraph(const _sctp_assoc_info* selected_assoc); + void drawSACKGraph(const _sctp_assoc_info* selected_assoc); + void drawNRSACKGraph(const _sctp_assoc_info* selected_assoc); +}; + +#endif // SCTP_GRAPH_DIALOG_H diff --git a/ui/qt/sctp_graph_dialog.ui b/ui/qt/sctp_graph_dialog.ui new file mode 100644 index 00000000..e5a41e75 --- /dev/null +++ b/ui/qt/sctp_graph_dialog.ui @@ -0,0 +1,166 @@ + + + SCTPGraphDialog + + + + 0 + 0 + 800 + 546 + + + + SCTP Graph + + + + + + + + + 0 + 1 + + + + + + + + + + + 300 + 0 + + + + + + + + Relative TSNs + + + + + + + + + + + Qt::NoFocus + + + Only SACKs + + + + + + + Qt::NoFocus + + + Only TSNs + + + + + + + Qt::NoFocus + + + Show both + + + + + + + Qt::NoFocus + + + Reset to full size + + + + + + + Save Graph + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::NoFocus + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + goToPacket + + + Go to Packet + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+
+ + + + buttonBox + clicked(QAbstractButton*) + SCTPGraphDialog + close() + + + 706 + 530 + + + 703 + 574 + + + + +
diff --git a/ui/qt/search_frame.cpp b/ui/qt/search_frame.cpp new file mode 100644 index 00000000..95a5b8e8 --- /dev/null +++ b/ui/qt/search_frame.cpp @@ -0,0 +1,532 @@ +/* search_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "search_frame.h" +#include + +#include "file.h" +#include "ui/recent.h" + +#include +#include + +#include +#include + +#include "main_application.h" +#include +#include + +enum { + in_packet_list_, + in_proto_tree_, + in_bytes_ +}; + +enum { + df_search_, + hex_search_, + string_search_, + regex_search_ +}; + +enum { + narrow_and_wide_chars_, + narrow_chars_, + wide_chars_ +}; + +SearchFrame::SearchFrame(QWidget *parent) : + AccordionFrame(parent), + sf_ui_(new Ui::SearchFrame), + cap_file_(nullptr), + regex_(nullptr) +{ + sf_ui_->setupUi(this); + +#ifdef Q_OS_MAC + foreach (QWidget *w, findChildren()) { + w->setAttribute(Qt::WA_MacSmallSize, true); + } +#endif + + applyRecentSearchSettings(); + + updateWidgets(); +} + +SearchFrame::~SearchFrame() +{ + if (regex_) { + ws_regex_free(regex_); + } + delete sf_ui_; +} + +void SearchFrame::animatedShow() +{ + AccordionFrame::animatedShow(); + + sf_ui_->searchLineEdit->setFocus(); +} + +void SearchFrame::findNext() +{ + if (!cap_file_) return; + + cap_file_->dir = SD_FORWARD; + if (isHidden()) { + animatedShow(); + return; + } + on_findButton_clicked(); +} + +void SearchFrame::findPrevious() +{ + if (!cap_file_) return; + + cap_file_->dir = SD_BACKWARD; + if (isHidden()) { + animatedShow(); + return; + } + on_findButton_clicked(); +} + +void SearchFrame::setFocus() +{ + sf_ui_->searchLineEdit->setFocus(); + sf_ui_->searchLineEdit->selectAll(); + cap_file_->dir = SD_FORWARD; +} + +void SearchFrame::setCaptureFile(capture_file *cf) +{ + cap_file_ = cf; + if (!cf && isVisible()) { + animatedHide(); + } + updateWidgets(); +} + +void SearchFrame::findFrameWithFilter(QString &filter) +{ + animatedShow(); + sf_ui_->searchLineEdit->setText(filter); + sf_ui_->searchLineEdit->setCursorPosition(0); + sf_ui_->searchTypeComboBox->setCurrentIndex(df_search_); + updateWidgets(); + on_findButton_clicked(); +} + +void SearchFrame::keyPressEvent(QKeyEvent *event) +{ + if (event->modifiers() == Qt::NoModifier) { + if (event->key() == Qt::Key_Escape) { + on_cancelButton_clicked(); + } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + on_findButton_clicked(); + } + } + + AccordionFrame::keyPressEvent(event); +} + +bool SearchFrame::regexCompile() +{ + unsigned flags = 0; + if (!sf_ui_->caseCheckBox->isChecked()) { + flags |= WS_REGEX_CASELESS; + } + + if (regex_) { + ws_regex_free(regex_); + } + + if (sf_ui_->searchLineEdit->text().isEmpty()) { + regex_ = nullptr; + return false; + } + + char *errmsg = nullptr; + regex_ = ws_regex_compile_ex(sf_ui_->searchLineEdit->text().toUtf8().constData(), -1, + &errmsg, flags); + + if (errmsg != nullptr) { + regex_error_ = errmsg; + } + + return regex_ ? true : false; +} + +void SearchFrame::applyRecentSearchSettings() +{ + int search_in_idx = in_packet_list_; + int char_encoding_idx = narrow_and_wide_chars_; + int search_type_idx = df_search_; + + switch (recent.gui_search_in) { + case SEARCH_IN_PACKET_LIST: + search_in_idx = in_packet_list_; + break; + case SEARCH_IN_PACKET_DETAILS: + search_in_idx = in_proto_tree_; + break; + case SEARCH_IN_PACKET_BYTES: + search_in_idx = in_bytes_; + break; + default: + break; + } + + switch (recent.gui_search_char_set) { + case SEARCH_CHAR_SET_NARROW_AND_WIDE: + char_encoding_idx = narrow_and_wide_chars_; + break; + case SEARCH_CHAR_SET_NARROW: + char_encoding_idx = narrow_chars_; + break; + case SEARCH_CHAR_SET_WIDE: + char_encoding_idx = wide_chars_; + break; + default: + break; + } + + switch (recent.gui_search_type) { + case SEARCH_TYPE_DISPLAY_FILTER: + search_type_idx = df_search_; + break; + case SEARCH_TYPE_HEX_VALUE: + search_type_idx = hex_search_; + break; + case SEARCH_TYPE_STRING: + search_type_idx = string_search_; + break; + case SEARCH_TYPE_REGEX: + search_type_idx = regex_search_; + break; + default: + break; + } + + sf_ui_->searchInComboBox->setCurrentIndex(search_in_idx); + sf_ui_->charEncodingComboBox->setCurrentIndex(char_encoding_idx); + sf_ui_->caseCheckBox->setChecked(recent.gui_search_case_sensitive); + sf_ui_->searchTypeComboBox->setCurrentIndex(search_type_idx); +} + +void SearchFrame::updateWidgets() +{ + if (cap_file_) { + setEnabled(true); + } else { + setEnabled(false); + return; + } + + int search_type = sf_ui_->searchTypeComboBox->currentIndex(); + sf_ui_->searchInComboBox->setEnabled(search_type == string_search_ || search_type == regex_search_); + sf_ui_->caseCheckBox->setEnabled(search_type == string_search_ || search_type == regex_search_); + sf_ui_->charEncodingComboBox->setEnabled(search_type == string_search_); + + switch (search_type) { + case df_search_: + sf_ui_->searchLineEdit->checkDisplayFilter(sf_ui_->searchLineEdit->text()); + break; + case hex_search_: + if (sf_ui_->searchLineEdit->text().isEmpty()) { + sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + guint8 *bytes; + size_t nbytes; + bytes = convert_string_to_hex(sf_ui_->searchLineEdit->text().toUtf8().constData(), &nbytes); + if (bytes == nullptr) + sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Invalid); + else { + g_free(bytes); + sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Valid); + } + } + break; + case string_search_: + if (sf_ui_->searchLineEdit->text().isEmpty()) { + sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Valid); + } + break; + case regex_search_: + if (regexCompile()) { + sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Valid); + } else { + sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Invalid); + } + break; + default: + // currentIndex is probably -1. Nothing is selected or list is empty. + return; + } + + if (sf_ui_->searchLineEdit->text().isEmpty() || sf_ui_->searchLineEdit->syntaxState() == SyntaxLineEdit::Invalid) { + sf_ui_->findButton->setEnabled(false); + } else { + sf_ui_->findButton->setEnabled(true); + } +} + +void SearchFrame::on_searchInComboBox_currentIndexChanged(int idx) +{ + switch (idx) { + case in_packet_list_: + recent.gui_search_in = SEARCH_IN_PACKET_LIST; + break; + case in_proto_tree_: + recent.gui_search_in = SEARCH_IN_PACKET_DETAILS; + break; + case in_bytes_: + recent.gui_search_in = SEARCH_IN_PACKET_BYTES; + break; + default: + break; + } +} + +void SearchFrame::on_charEncodingComboBox_currentIndexChanged(int idx) +{ + switch (idx) { + case narrow_and_wide_chars_: + recent.gui_search_char_set = SEARCH_CHAR_SET_NARROW_AND_WIDE; + break; + case narrow_chars_: + recent.gui_search_char_set = SEARCH_CHAR_SET_NARROW; + break; + case wide_chars_: + recent.gui_search_char_set = SEARCH_CHAR_SET_WIDE; + break; + default: + break; + } +} + +void SearchFrame::on_caseCheckBox_toggled(bool checked) +{ + recent.gui_search_case_sensitive = checked; + regexCompile(); +} + +void SearchFrame::on_searchTypeComboBox_currentIndexChanged(int idx) +{ + switch (idx) { + case df_search_: + recent.gui_search_type = SEARCH_TYPE_DISPLAY_FILTER; + break; + case hex_search_: + recent.gui_search_type = SEARCH_TYPE_HEX_VALUE; + break; + case string_search_: + recent.gui_search_type = SEARCH_TYPE_STRING; + break; + case regex_search_: + recent.gui_search_type = SEARCH_TYPE_REGEX; + break; + default: + break; + } + + // Enable completion only for display filter search. + sf_ui_->searchLineEdit->allowCompletion(idx == df_search_); + + if (idx == df_search_) { + sf_ui_->searchLineEdit->checkFilter(); + } else { + sf_ui_->searchLineEdit->setToolTip(QString()); + mainApp->popStatus(MainApplication::FilterSyntax); + } + + updateWidgets(); +} + +void SearchFrame::on_searchLineEdit_textChanged(const QString &) +{ + updateWidgets(); +} + +void SearchFrame::on_findButton_clicked() +{ + guint8 *bytes = nullptr; + size_t nbytes = 0; + char *string = nullptr; + dfilter_t *dfp = nullptr; + gboolean found_packet = FALSE; + QString err_string; + + if (!cap_file_) { + return; + } + + cap_file_->hex = FALSE; + cap_file_->string = FALSE; + cap_file_->case_type = FALSE; + cap_file_->regex = nullptr; + cap_file_->packet_data = FALSE; + cap_file_->decode_data = FALSE; + cap_file_->summary_data = FALSE; + cap_file_->scs_type = SCS_NARROW_AND_WIDE; + + int search_type = sf_ui_->searchTypeComboBox->currentIndex(); + switch (search_type) { + case df_search_: + if (!dfilter_compile(sf_ui_->searchLineEdit->text().toUtf8().constData(), &dfp, nullptr)) { + err_string = tr("Invalid filter."); + goto search_done; + } + + if (dfp == nullptr) { + err_string = tr("That filter doesn't test anything."); + goto search_done; + } + break; + case hex_search_: + bytes = convert_string_to_hex(sf_ui_->searchLineEdit->text().toUtf8().constData(), &nbytes); + if (bytes == nullptr) { + err_string = tr("That's not a valid hex string."); + goto search_done; + } + cap_file_->hex = TRUE; + break; + case string_search_: + case regex_search_: + if (sf_ui_->searchLineEdit->text().isEmpty()) { + err_string = tr("You didn't specify any text for which to search."); + goto search_done; + } + cap_file_->string = TRUE; + cap_file_->case_type = sf_ui_->caseCheckBox->isChecked() ? FALSE : TRUE; + cap_file_->regex = (search_type == regex_search_ ? regex_ : nullptr); + switch (sf_ui_->charEncodingComboBox->currentIndex()) { + case narrow_and_wide_chars_: + cap_file_->scs_type = SCS_NARROW_AND_WIDE; + break; + case narrow_chars_: + cap_file_->scs_type = SCS_NARROW; + break; + case wide_chars_: + cap_file_->scs_type = SCS_WIDE; + break; + default: + err_string = tr("No valid character set selected. Please report this to the development team."); + goto search_done; + } + string = convert_string_case(sf_ui_->searchLineEdit->text().toUtf8().constData(), cap_file_->case_type); + break; + default: + err_string = tr("No valid search type selected. Please report this to the development team."); + goto search_done; + } + + switch (sf_ui_->searchInComboBox->currentIndex()) { + case in_packet_list_: + cap_file_->summary_data = TRUE; + break; + case in_proto_tree_: + cap_file_->decode_data = TRUE; + break; + case in_bytes_: + cap_file_->packet_data = TRUE; + break; + default: + err_string = tr("No valid search area selected. Please report this to the development team."); + goto search_done; + } + + g_free(cap_file_->sfilter); + cap_file_->sfilter = g_strdup(sf_ui_->searchLineEdit->text().toUtf8().constData()); + mainApp->popStatus(MainApplication::FileStatus); + mainApp->pushStatus(MainApplication::FileStatus, tr("Searching for %1…").arg(sf_ui_->searchLineEdit->text())); + + if (cap_file_->hex) { + /* Hex value in packet data */ + found_packet = cf_find_packet_data(cap_file_, bytes, nbytes, cap_file_->dir); + g_free(bytes); + if (!found_packet) { + /* We didn't find a packet */ + err_string = tr("No packet contained those bytes."); + goto search_done; + } + } else if (cap_file_->string) { + if (search_type == regex_search_ && !cap_file_->regex) { + err_string = regex_error_; + goto search_done; + } + if (cap_file_->summary_data) { + /* String in the Info column of the summary line */ + found_packet = cf_find_packet_summary_line(cap_file_, string, cap_file_->dir); + g_free(string); + if (!found_packet) { + err_string = tr("No packet contained that string in its Info column."); + goto search_done; + } + } else if (cap_file_->decode_data) { + /* String in the protocol tree headings */ + found_packet = cf_find_packet_protocol_tree(cap_file_, string, cap_file_->dir); + g_free(string); + if (!found_packet) { + err_string = tr("No packet contained that string in its dissected display."); + goto search_done; + } + } else if (cap_file_->packet_data && string) { + /* String in the ASCII-converted packet data */ + found_packet = cf_find_packet_data(cap_file_, (guint8 *) string, strlen(string), cap_file_->dir); + g_free(string); + if (!found_packet) { + err_string = tr("No packet contained that string in its converted data."); + goto search_done; + } + } + } else { + /* Search via display filter */ + found_packet = cf_find_packet_dfilter(cap_file_, dfp, cap_file_->dir); + dfilter_free(dfp); + if (!found_packet) { + err_string = tr("No packet matched that filter."); + g_free(bytes); + goto search_done; + } + } + + search_done: + mainApp->popStatus(MainApplication::FileStatus); + if (!err_string.isEmpty()) { + mainApp->pushStatus(MainApplication::FilterSyntax, err_string); + } +} + +void SearchFrame::on_cancelButton_clicked() +{ + mainApp->popStatus(MainApplication::FilterSyntax); + animatedHide(); +} + +void SearchFrame::changeEvent(QEvent* event) +{ + if (event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + sf_ui_->retranslateUi(this); + break; + default: + break; + } + } + AccordionFrame::changeEvent(event); +} diff --git a/ui/qt/search_frame.h b/ui/qt/search_frame.h new file mode 100644 index 00000000..7ea0946a --- /dev/null +++ b/ui/qt/search_frame.h @@ -0,0 +1,63 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEARCH_FRAME_H +#define SEARCH_FRAME_H + +#include + +#include "accordion_frame.h" + +#include "cfile.h" + +namespace Ui { +class SearchFrame; +} + +class SearchFrame : public AccordionFrame +{ + Q_OBJECT + +public: + explicit SearchFrame(QWidget *parent = 0); + ~SearchFrame(); + void animatedShow(); + void findNext(); + void findPrevious(); + void setFocus(); + +public slots: + void setCaptureFile(capture_file *cf); + void findFrameWithFilter(QString &filter); + +protected: + virtual void keyPressEvent(QKeyEvent *event); + void changeEvent(QEvent* event); + +private: + bool regexCompile(); + void applyRecentSearchSettings(); + void updateWidgets(); + + Ui::SearchFrame *sf_ui_; + capture_file *cap_file_; + ws_regex_t *regex_; + QString regex_error_; + +private slots: + void on_searchInComboBox_currentIndexChanged(int idx); + void on_charEncodingComboBox_currentIndexChanged(int idx); + void on_caseCheckBox_toggled(bool checked); + void on_searchTypeComboBox_currentIndexChanged(int idx); + void on_searchLineEdit_textChanged(const QString &); + void on_findButton_clicked(); + void on_cancelButton_clicked(); +}; + +#endif // SEARCH_FRAME_H diff --git a/ui/qt/search_frame.ui b/ui/qt/search_frame.ui new file mode 100644 index 00000000..c3bb9908 --- /dev/null +++ b/ui/qt/search_frame.ui @@ -0,0 +1,205 @@ + + + SearchFrame + + + + 0 + 0 + 1026 + 34 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + + + + Packet list + + + + + Packet details + + + + + Packet bytes + + + + + + + + Qt::Horizontal + + + + 20 + 10 + + + + + + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + + + + Narrow & Wide + + + + + Narrow (UTF-8 / ASCII) + + + + + Wide (UTF-16) + + + + + + + + Case sensitive + + + + + + + Qt::Horizontal + + + + 20 + 10 + + + + + + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + + + + Display filter + + + + + Hex value + + + + + String + + + + + Regular Expression + + + + + + + + + 1 + 0 + + + + + + + + + 16777215 + 27 + + + + Find + + + true + + + + + + + + 16777215 + 27 + + + + Cancel + + + + + + + + AccordionFrame + QFrame +
accordion_frame.h
+ 1 +
+ + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + +
diff --git a/ui/qt/sequence_diagram.cpp b/ui/qt/sequence_diagram.cpp new file mode 100644 index 00000000..4628915b --- /dev/null +++ b/ui/qt/sequence_diagram.cpp @@ -0,0 +1,401 @@ +/* sequence_diagram.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "sequence_diagram.h" + +#include "epan/addr_resolv.h" +#include "epan/sequence_analysis.h" + +#include +#include +#include "ui/recent.h" + +#include +#include +#include +#include +#include + +const int max_comment_em_width_ = 20; + +// UML-like network node sequence diagrams. +// https://developer.ibm.com/articles/the-sequence-diagram/ + +WSCPSeqData::WSCPSeqData() : + key(0), + value(NULL) +{ +} + +WSCPSeqData::WSCPSeqData(double key, struct _seq_analysis_item *value) : + key(key), + value(value) +{ +} + +SequenceDiagram::SequenceDiagram(QCPAxis *keyAxis, QCPAxis *valueAxis, QCPAxis *commentAxis) : + QCPAbstractPlottable(keyAxis, valueAxis), + key_axis_(keyAxis), + value_axis_(valueAxis), + comment_axis_(commentAxis), + data_(NULL), + sainfo_(NULL), + selected_packet_(0), + selected_key_(-1.0) +{ + data_ = new WSCPSeqDataMap(); + // xaxis (value): Address + // yaxis (key): Time + // yaxis2 (comment): Extra info ("Comment" in GTK+) + +// valueAxis->setAutoTickStep(false); + + /* The comments are not numbers, don't try to pretty print exponentials. */ + commentAxis->setNumberFormat("f"); + QList axes; + axes << value_axis_ << key_axis_ << comment_axis_; + QPen no_pen(Qt::NoPen); + foreach (QCPAxis *axis, axes) { + QSharedPointer ticker(new QCPAxisTickerText); + axis->setTicker(ticker); + axis->setSubTickPen(no_pen); + axis->setTickPen(no_pen); + axis->setBasePen(no_pen); + } + + value_axis_->grid()->setVisible(false); + + key_axis_->setRangeReversed(true); + key_axis_->grid()->setVisible(false); + + comment_axis_->setRangeReversed(true); + comment_axis_->grid()->setVisible(false); + + QFont comment_font = comment_axis_->tickLabelFont(); + comment_font.setPointSizeF(comment_font.pointSizeF() * 0.8); + smooth_font_size(comment_font); + comment_axis_->setTickLabelFont(comment_font); + comment_axis_->setSelectedTickLabelFont(QFont(comment_font.family(), comment_font.pointSizeF(), QFont::Bold)); + // frame_label + // port_src -----------------> port_dst + +// setTickVectorLabels + // valueAxis->setTickLabelRotation(30); +} + +SequenceDiagram::~SequenceDiagram() +{ + delete data_; +} + +int SequenceDiagram::adjacentPacket(bool next) +{ + int adjacent_packet = -1; + WSCPSeqDataMap::const_iterator it; + + if (data_->size() < 1) return adjacent_packet; + + if (selected_packet_ < 1) { + if (next) { + it = data_->constBegin(); + } else { + it = data_->constEnd(); + --it; + } + selected_key_ = it.value().key; + return it.value().value->frame_number; + } + + if (next) { + for (it = data_->constBegin(); it != data_->constEnd(); ++it) { + if (it.value().value->frame_number == selected_packet_) { + ++it; + if (it != data_->constEnd()) { + adjacent_packet = it.value().value->frame_number; + selected_key_ = it.value().key; + } + break; + } + } + } else { + it = data_->constEnd(); + --it; + while (it != data_->constBegin()) { + guint32 prev_frame = it.value().value->frame_number; + --it; + if (prev_frame == selected_packet_) { + adjacent_packet = it.value().value->frame_number; + selected_key_ = it.value().key; + break; + } + } + } + + return adjacent_packet; +} + +void SequenceDiagram::setData(_seq_analysis_info *sainfo) +{ + data_->clear(); + sainfo_ = sainfo; + if (!sainfo) return; + + double cur_key = 0.0; + QVector key_ticks, val_ticks; + QVector key_labels, val_labels, com_labels; + QFontMetrics com_fm(comment_axis_->tickLabelFont()); + int elide_w = com_fm.height() * max_comment_em_width_; + char* addr_str; + + for (GList *cur = g_queue_peek_nth_link(sainfo->items, 0); cur; cur = gxx_list_next(cur)) { + seq_analysis_item_t *sai = gxx_list_data(seq_analysis_item_t *, cur); + if (sai->display) { + WSCPSeqData new_data; + + new_data.key = cur_key; + new_data.value = sai; + data_->insert(new_data.key, new_data); + + key_ticks.append(cur_key); + key_labels.append(sai->time_str); + + com_labels.append(com_fm.elidedText(sai->comment, Qt::ElideRight, elide_w)); + + cur_key++; + } + } + + for (unsigned int i = 0; i < sainfo_->num_nodes; i++) { + val_ticks.append(i); + addr_str = address_to_display(Q_NULLPTR, &(sainfo_->nodes[i])); + val_labels.append(addr_str); + if (i % 2 == 0) { + val_labels.last().append("\n"); + } + + wmem_free(Q_NULLPTR, addr_str); + } + + QSharedPointer key_ticker = qSharedPointerCast(keyAxis()->ticker()); + key_ticker->setTicks(key_ticks, key_labels); + QSharedPointer value_ticker = qSharedPointerCast(valueAxis()->ticker()); + value_ticker->setTicks(val_ticks, val_labels); + QSharedPointer comment_ticker = qSharedPointerCast(comment_axis_->ticker()); + comment_ticker->setTicks(key_ticks, com_labels); +} + +void SequenceDiagram::setSelectedPacket(int selected_packet) +{ + selected_key_ = -1; + if (selected_packet > 0) { + selected_packet_ = selected_packet; + } else { + selected_packet_ = 0; + } + mParentPlot->replot(); +} + +_seq_analysis_item *SequenceDiagram::itemForPosY(int ypos) +{ + double key_pos = qRound(key_axis_->pixelToCoord(ypos)); + + if (key_pos >= 0 && key_pos < data_->size()) { + return data_->value(key_pos).value; + } + return NULL; +} + +double SequenceDiagram::selectTest(const QPointF &pos, bool, QVariant *) const +{ + double key_pos = qRound(key_axis_->pixelToCoord(pos.y())); + + if (key_pos >= 0 && key_pos < data_->size()) { + return 1.0; + } + + return -1.0; +} + +void SequenceDiagram::draw(QCPPainter *painter) +{ + QPen fg_pen; + qreal alpha = 0.50; + + // Lifelines (node lines). Will likely be overdrawn below. + painter->save(); + painter->setOpacity(alpha); + fg_pen = pen(); + fg_pen.setStyle(Qt::DashLine); + painter->setPen(fg_pen); + for (int ll_x = value_axis_->range().lower; ll_x < value_axis_->range().upper; ll_x++) { + // Only draw where we have arrows. + if (ll_x < 0 || ll_x >= value_axis_->tickVector().size()) continue; + QPoint ll_start(coordsToPixels(key_axis_->range().upper, ll_x).toPoint()); + QPoint ll_end(coordsToPixels(key_axis_->range().lower, ll_x).toPoint()); + painter->drawLine(ll_start, ll_end); + } + painter->restore(); + fg_pen = pen(); + + WSCPSeqDataMap::const_iterator it; + for (it = data_->constBegin(); it != data_->constEnd(); ++it) { + double cur_key = it.key(); + seq_analysis_item_t *sai = it.value().value; + QColor bg_color; + + if (sai->frame_number == selected_packet_) { + QPalette sel_pal; + fg_pen.setColor(sel_pal.color(QPalette::HighlightedText)); + bg_color = sel_pal.color(QPalette::Highlight); + selected_key_ = cur_key; + } else if ((sai->has_color_filter) && (recent.packet_list_colorize)) { + fg_pen.setColor(QColor().fromRgb(sai->fg_color)); + bg_color = QColor().fromRgb(sai->bg_color); + } else { + fg_pen.setColor(Qt::black); + bg_color = ColorUtils::sequenceColor(sai->conv_num); + } + + // Highlighted background +// painter->save(); + QRect bg_rect( + QPoint(coordsToPixels(cur_key - 0.5, value_axis_->range().lower).toPoint()), + QPoint(coordsToPixels(cur_key + 0.5, value_axis_->range().upper).toPoint())); + if (bg_color.isValid()) { + painter->fillRect(bg_rect, bg_color); + } +// painter->restore(); + + // Highlighted lifelines + painter->save(); + QPen hl_pen = fg_pen; + hl_pen.setStyle(Qt::DashLine); + painter->setPen(hl_pen); + painter->setOpacity(alpha); + for (int ll_x = value_axis_->range().lower; ll_x < value_axis_->range().upper; ll_x++) { + // Only draw where we have arrows. + if (ll_x < 0 || ll_x >= value_axis_->tickVector().size()) continue; + QPoint ll_start(coordsToPixels(cur_key - 0.5, ll_x).toPoint()); + QPoint ll_end(coordsToPixels(cur_key + 0.5, ll_x).toPoint()); + hl_pen.setDashOffset(bg_rect.top() - ll_start.x()); + painter->drawLine(ll_start, ll_end); + } + painter->restore(); + + if (cur_key < key_axis_->range().lower || cur_key > key_axis_->range().upper) { + continue; + } + if (sai->dst_node > sai->src_node && (sai->dst_node < value_axis_->range().lower || sai->src_node > value_axis_->range().upper)) { + continue; + } + if (sai->src_node > sai->dst_node && (sai->src_node < value_axis_->range().lower || sai->dst_node > value_axis_->range().upper)) { + continue; + } + + // Message + if (pen().style() != Qt::NoPen && pen().color().alpha() != 0) { + painter->save(); + + QFontMetrics cfm(comment_axis_->tickLabelFont()); + double en_w = cfm.height() / 2.0; + int dir_mul = (sai->src_node < sai->dst_node) ? 1 : -1; + double ah_size = (cfm.height() / 5) * dir_mul; + QPoint arrow_start(coordsToPixels(cur_key, sai->src_node).toPoint()); + arrow_start.setY(arrow_start.y() + (en_w / 2)); + QPoint arrow_end(coordsToPixels(cur_key, sai->dst_node).toPoint()); + arrow_end.setY(arrow_start.y()); + QLine arrow_line(arrow_start, arrow_end); + QPolygon arrow_head; + arrow_head + << QPoint(arrow_end.x() - (ah_size*3), arrow_end.y() - ah_size) + << arrow_end + << QPoint(arrow_end.x() - (ah_size*3), arrow_end.y() + ah_size); + + painter->setBrush(fg_pen.color()); + painter->setPen(fg_pen); + painter->drawLine(arrow_line); + painter->drawPolygon(arrow_head); + + double comment_start = (sai->src_node < sai->dst_node) + ? arrow_start.x() : arrow_end.x(); + double arrow_width = (arrow_end.x() - arrow_start.x()) * dir_mul; + QString arrow_label = cfm.elidedText(sai->frame_label, Qt::ElideRight, arrow_width); + int arrow_label_width = 0; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + arrow_label_width = cfm.horizontalAdvance(arrow_label); +#else + arrow_label_width = cfm.width(arrow_label); +#endif + QPoint text_pt(comment_start + ((arrow_width - arrow_label_width) / 2), + arrow_start.y() - (en_w / 2)); + + painter->setFont(comment_axis_->tickLabelFont()); + painter->drawText(text_pt, arrow_label); + + if (sai->port_src && sai->port_dst) { + int left_x = dir_mul > 0 ? arrow_start.x() : arrow_end.x(); + int right_x = dir_mul > 0 ? arrow_end.x() : arrow_start.x(); + QString port_left = QString::number(dir_mul > 0 ? sai->port_src : sai->port_dst); + QString port_right = QString::number(dir_mul > 0 ? sai->port_dst : sai->port_src); + int port_left_width = 0; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + port_left_width = cfm.horizontalAdvance(port_left); +#else + port_left_width = cfm.width(port_left); +#endif + text_pt = QPoint(left_x - en_w - port_left_width, arrow_start.y() + (en_w / 2)); + painter->drawText(text_pt, port_left); + + text_pt.setX(right_x + en_w); + painter->drawText(text_pt, port_right); + } + painter->restore(); + } + } +} + +void SequenceDiagram::drawLegendIcon(QCPPainter *, const QRectF &) const +{ +} + +QCPRange SequenceDiagram::getKeyRange(bool &validRange, QCP::SignDomain) const +{ + QCPRange range; + bool valid = false; + + WSCPSeqDataMap::const_iterator it = data_->constBegin(); + while (it != data_->constEnd()) { + double cur_key = it.key(); + if (!valid) { + range.lower = range.upper = cur_key; + valid = true; + } else if (cur_key < range.lower) { + range.lower = cur_key; + } else if (cur_key > range.upper) { + range.upper = cur_key; + } + ++it; + } + validRange = valid; + return range; +} + +QCPRange SequenceDiagram::getValueRange(bool &validRange, QCP::SignDomain, const QCPRange &) const +{ + QCPRange range; + bool valid = false; + + if (sainfo_) { + range.lower = 0; + range.upper = data_->size(); + valid = true; + } + validRange = valid; + return range; +} diff --git a/ui/qt/sequence_diagram.h b/ui/qt/sequence_diagram.h new file mode 100644 index 00000000..aa9a377a --- /dev/null +++ b/ui/qt/sequence_diagram.h @@ -0,0 +1,80 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEQUENCE_DIAGRAM_H +#define SEQUENCE_DIAGRAM_H + +#include + +#include + +#include + +#include +#include +#include + +struct _seq_analysis_info; +struct _seq_analysis_item; + +// Some of this is probably unnecessary +class WSCPSeqData +{ +public: + WSCPSeqData(); + WSCPSeqData(double key, _seq_analysis_item *value); + double key; + struct _seq_analysis_item *value; +}; + +typedef QMultiMap WSCPSeqDataMap; + +class SequenceDiagram : public QCPAbstractPlottable +{ + Q_OBJECT +public: + explicit SequenceDiagram(QCPAxis *keyAxis, QCPAxis *valueAxis, QCPAxis *commentAxis); + virtual ~SequenceDiagram(); + + // getters: + // Next / previous packet. + int adjacentPacket(bool next); + + double selectedKey() { return selected_key_; } + + // setters: + void setData(struct _seq_analysis_info *sainfo); + + // non-property methods: + struct _seq_analysis_item *itemForPosY(int ypos); + + // reimplemented virtual methods: + virtual void clearData() { data_->clear(); } + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + +public slots: + void setSelectedPacket(int selected_packet); + +protected: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE; + virtual QCPRange getKeyRange(bool &validRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; + virtual QCPRange getValueRange(bool &validRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange = QCPRange()) const Q_DECL_OVERRIDE; + +private: + QCPAxis *key_axis_; + QCPAxis *value_axis_; + QCPAxis *comment_axis_; + WSCPSeqDataMap *data_; + struct _seq_analysis_info *sainfo_; + guint32 selected_packet_; + double selected_key_; +}; + +#endif // SEQUENCE_DIAGRAM_H diff --git a/ui/qt/sequence_dialog.cpp b/ui/qt/sequence_dialog.cpp new file mode 100644 index 00000000..2f5b5ede --- /dev/null +++ b/ui/qt/sequence_dialog.cpp @@ -0,0 +1,864 @@ +/* sequence_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "sequence_dialog.h" +#include + +#include "epan/addr_resolv.h" + +#include "file.h" + +#include "wsutil/nstime.h" +#include "wsutil/utf8_entities.h" +#include "wsutil/file_util.h" +#include + +#include +#include "progress_frame.h" +#include +#include "sequence_diagram.h" +#include "main_application.h" +#include +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include +#include "rtp_stream_dialog.h" + +#include +#include +#include + +// To do: +// - Resize or show + hide the Time and Comment axes, possibly via one of +// the following: +// - Split the time, diagram, and comment sections into three separate +// widgets inside a QSplitter. This would resemble the GTK+ UI, but we'd +// have to coordinate between the three and we'd lose time and comment +// values in PDF and PNG exports. +// - Add separate controls for the width and/or visibility of the Time and +// Comment columns. +// - Fake a splitter widget by catching mouse events in the plot area. +// Drawing a QCPItemLine or QCPItemPixmap over each Y axis might make +// this easier. +// - For general flows, let the user show columns other than COL_INFO. +// - Add UTF8 to text dump +// - Save to XMI? https://www.spinellis.gr/umlgraph/ +// - Time: abs vs delta +// - Hide nodes +// - Clickable time + comments? +// - Incorporate packet comments? +// - Change line_style to seq_type (i.e. draw ACKs dashed) +// - Create WSGraph subclasses with common behavior. +// - Help button and text + +static const double min_top_ = -1.0; +static const double min_left_ = -0.5; + +typedef struct { + int curr_index; + QComboBox *flow; + SequenceInfo *info; +} sequence_items_t; + +SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *info) : + WiresharkDialog(parent, cf), + ui(new Ui::SequenceDialog), + info_(info), + num_items_(0), + packet_num_(0), + sequence_w_(1), + voipFeaturesEnabled(false) +{ + QAction *action; + + ui->setupUi(this); + ui->hintLabel->setSmallText(); + + QCustomPlot *sp = ui->sequencePlot; + setWindowSubtitle(info_ ? tr("Call Flow") : tr("Flow")); + + if (!info_) { + info_ = new SequenceInfo(sequence_analysis_info_new()); + info_->sainfo()->name = "any"; + } else { + info_->ref(); + sequence_analysis_free_nodes(info_->sainfo()); + num_items_ = sequence_analysis_get_nodes(info_->sainfo()); + } + + seq_diagram_ = new SequenceDiagram(sp->yAxis, sp->xAxis2, sp->yAxis2); + + // When dragging is enabled it's easy to drag past the lower and upper + // bounds of each axis. Disable it for now. + //sp->axisRect()->setRangeDragAxes(sp->xAxis2, sp->yAxis); + //sp->setInteractions(QCP::iRangeDrag); + + sp->xAxis->setVisible(false); + sp->xAxis->setPadding(0); + sp->xAxis->setLabelPadding(0); + sp->xAxis->setTickLabelPadding(0); + + QPen base_pen(ColorUtils::alphaBlend(palette().text(), palette().base(), 0.25)); + base_pen.setWidthF(0.5); + sp->xAxis2->setBasePen(base_pen); + sp->yAxis->setBasePen(base_pen); + sp->yAxis2->setBasePen(base_pen); + + sp->xAxis2->setVisible(true); + sp->yAxis2->setVisible(true); + + key_text_ = new QCPItemText(sp); + key_text_->setText(tr("Time")); + + key_text_->setPositionAlignment(Qt::AlignRight | Qt::AlignVCenter); + key_text_->position->setType(QCPItemPosition::ptAbsolute); + key_text_->setClipToAxisRect(false); + + comment_text_ = new QCPItemText(sp); + comment_text_->setText(tr("Comment")); + + comment_text_->setPositionAlignment(Qt::AlignLeft | Qt::AlignVCenter); + comment_text_->position->setType(QCPItemPosition::ptAbsolute); + comment_text_->setClipToAxisRect(false); + + one_em_ = QFontMetrics(sp->yAxis->labelFont()).height(); + ui->horizontalScrollBar->setSingleStep(100 / one_em_); + ui->verticalScrollBar->setSingleStep(100 / one_em_); + + ui->gridLayout->setSpacing(0); + connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), sp->yAxis2, SLOT(setRange(QCPRange))); + + ctx_menu_.addAction(ui->actionZoomIn); + ctx_menu_.addAction(ui->actionZoomOut); + action = ctx_menu_.addAction(tr("Reset Diagram"), this, SLOT(resetView())); + action->setToolTip(tr("Reset the diagram to its initial state.")); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionMoveRight10); + ctx_menu_.addAction(ui->actionMoveLeft10); + ctx_menu_.addAction(ui->actionMoveUp10); + ctx_menu_.addAction(ui->actionMoveDown10); + ctx_menu_.addAction(ui->actionMoveRight1); + ctx_menu_.addAction(ui->actionMoveLeft1); + ctx_menu_.addAction(ui->actionMoveUp1); + ctx_menu_.addAction(ui->actionMoveDown1); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionGoToPacket); + ctx_menu_.addAction(ui->actionGoToNextPacket); + ctx_menu_.addAction(ui->actionGoToPreviousPacket); + ctx_menu_.addSeparator(); + action = ui->actionSelectRtpStreams; + ctx_menu_.addAction(action); + action->setVisible(false); + action->setEnabled(false); + action = ui->actionDeselectRtpStreams; + ctx_menu_.addAction(action); + action->setVisible(false); + action->setEnabled(false); + set_action_shortcuts_visible_in_context_menu(ctx_menu_.actions()); + + ui->addressComboBox->setCurrentIndex(0); + + sequence_items_t item_data; + + item_data.curr_index = 0; + item_data.flow = ui->flowComboBox; + item_data.info = info_; + + //Add all registered analysis to combo box + sequence_analysis_table_iterate_tables(addFlowSequenceItem, &item_data); + + if (strcmp(info_->sainfo()->name, "voip") == 0) { + ui->flowComboBox->blockSignals(true); + ui->controlFrame->hide(); + } + + reset_button_ = ui->buttonBox->addButton(ui->actionResetDiagram->text(), QDialogButtonBox::ActionRole); + reset_button_->setToolTip(ui->actionResetDiagram->toolTip()); + player_button_ = RtpPlayerDialog::addPlayerButton(ui->buttonBox, this); + export_button_ = ui->buttonBox->addButton(ui->actionExportDiagram->text(), QDialogButtonBox::ActionRole); + export_button_->setToolTip(ui->actionExportDiagram->toolTip()); + + QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close); + if (close_bt) { + close_bt->setDefault(true); + } + + ProgressFrame::addToButtonBox(ui->buttonBox, &parent); + + loadGeometry(parent.width(), parent.height() * 4 / 5); + + connect(ui->horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(hScrollBarChanged(int))); + connect(ui->verticalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(vScrollBarChanged(int))); + connect(sp->xAxis2, SIGNAL(rangeChanged(QCPRange)), this, SLOT(xAxisChanged(QCPRange))); + connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(yAxisChanged(QCPRange))); + connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(diagramClicked(QMouseEvent*))); + connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + connect(sp, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheeled(QWheelEvent*))); + + // Button must be enabled by VoIP dialogs + player_button_->setVisible(false); + player_button_->setEnabled(false); +} + +SequenceDialog::~SequenceDialog() +{ + info_->unref(); + delete ui; +} + +void SequenceDialog::enableVoIPFeatures() +{ + voipFeaturesEnabled = true; + player_button_->setVisible(true); + ui->actionSelectRtpStreams->setVisible(true); + ui->actionDeselectRtpStreams->setVisible(true); +} + +void SequenceDialog::updateWidgets() +{ + WiresharkDialog::updateWidgets(); +} + +void SequenceDialog::showEvent(QShowEvent *) +{ + QTimer::singleShot(0, this, SLOT(fillDiagram())); +} + +void SequenceDialog::resizeEvent(QResizeEvent *) +{ + if (!info_) return; + + resetAxes(true); +} + +void SequenceDialog::keyPressEvent(QKeyEvent *event) +{ + int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10; + + // XXX - Copy some shortcuts from tcp_stream_dialog.cpp + switch(event->key()) { + case Qt::Key_Minus: + case Qt::Key_Underscore: // Shifted minus on U.S. keyboards + on_actionZoomOut_triggered(); + break; + case Qt::Key_Plus: + case Qt::Key_Equal: // Unshifted plus on U.S. keyboards + on_actionZoomIn_triggered(); + break; + + case Qt::Key_Right: + case Qt::Key_L: + panAxes(pan_pixels, 0); + break; + case Qt::Key_Left: + case Qt::Key_H: + panAxes(-1 * pan_pixels, 0); + break; + case Qt::Key_Up: + case Qt::Key_K: + panAxes(0, pan_pixels); + break; + case Qt::Key_Down: + case Qt::Key_J: + panAxes(0, -1 * pan_pixels); + break; + + case Qt::Key_PageDown: + case Qt::Key_Space: + ui->verticalScrollBar->setValue(ui->verticalScrollBar->value() + ui->verticalScrollBar->pageStep()); + break; + case Qt::Key_PageUp: + ui->verticalScrollBar->setValue(ui->verticalScrollBar->value() - ui->verticalScrollBar->pageStep()); + break; + + case Qt::Key_0: + case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards + case Qt::Key_R: + case Qt::Key_Home: + resetAxes(); + break; + + case Qt::Key_G: + on_actionGoToPacket_triggered(); + break; + case Qt::Key_N: + on_actionGoToNextPacket_triggered(); + break; + case Qt::Key_P: + on_actionGoToPreviousPacket_triggered(); + break; + case Qt::Key_S: + if (voipFeaturesEnabled) { + on_actionSelectRtpStreams_triggered(); + } + break; + case Qt::Key_D: + if (voipFeaturesEnabled) { + on_actionDeselectRtpStreams_triggered(); + } + break; + } + + QDialog::keyPressEvent(event); +} + +void SequenceDialog::hScrollBarChanged(int value) +{ + if (qAbs(ui->sequencePlot->xAxis2->range().center()-value/100.0) > 0.01) { + ui->sequencePlot->xAxis2->setRange(value/100.0, ui->sequencePlot->xAxis2->range().size(), Qt::AlignCenter); + ui->sequencePlot->replot(); + } +} + +void SequenceDialog::vScrollBarChanged(int value) +{ + if (qAbs(ui->sequencePlot->yAxis->range().center()-value/100.0) > 0.01) { + ui->sequencePlot->yAxis->setRange(value/100.0, ui->sequencePlot->yAxis->range().size(), Qt::AlignCenter); + ui->sequencePlot->replot(); + } +} + +void SequenceDialog::xAxisChanged(QCPRange range) +{ + ui->horizontalScrollBar->setValue(qRound(qreal(range.center()*100.0))); + ui->horizontalScrollBar->setPageStep(qRound(qreal(range.size()*100.0))); +} + +void SequenceDialog::yAxisChanged(QCPRange range) +{ + ui->verticalScrollBar->setValue(qRound(qreal(range.center()*100.0))); + ui->verticalScrollBar->setPageStep(qRound(qreal(range.size()*100.0))); +} + +void SequenceDialog::diagramClicked(QMouseEvent *event) +{ + current_rtp_sai_selected_ = NULL; + if (event) { + seq_analysis_item_t *sai = seq_diagram_->itemForPosY(event->pos().y()); + if (voipFeaturesEnabled) { + ui->actionSelectRtpStreams->setEnabled(false); + ui->actionDeselectRtpStreams->setEnabled(false); + player_button_->setEnabled(false); + if (sai) { + if (GA_INFO_TYPE_RTP == sai->info_type) { + ui->actionSelectRtpStreams->setEnabled(true && !file_closed_); + ui->actionDeselectRtpStreams->setEnabled(true && !file_closed_); + player_button_->setEnabled(true && !file_closed_); + current_rtp_sai_selected_ = sai; + } + } + } + + switch (event->button()) { + case Qt::LeftButton: + on_actionGoToPacket_triggered(); + break; + case Qt::RightButton: +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + ctx_menu_.popup(event->globalPosition().toPoint()); +#else + ctx_menu_.popup(event->globalPos()); +#endif + break; + default: + break; + } + } + +} + +void SequenceDialog::mouseMoved(QMouseEvent *event) +{ + current_rtp_sai_hovered_ = NULL; + packet_num_ = 0; + QString hint; + if (event) { + seq_analysis_item_t *sai = seq_diagram_->itemForPosY(event->pos().y()); + if (sai) { + if (GA_INFO_TYPE_RTP == sai->info_type) { + ui->actionSelectRtpStreams->setEnabled(true); + ui->actionDeselectRtpStreams->setEnabled(true); + current_rtp_sai_hovered_ = sai; + } + packet_num_ = sai->frame_number; + hint = QString("Packet %1: %2").arg(packet_num_).arg(sai->comment); + } + } + + if (hint.isEmpty()) { + if (!info_->sainfo()) { + hint += tr("No data"); + } else { + hint += tr("%Ln node(s)", "", info_->sainfo()->num_nodes) + QString(", ") + + tr("%Ln item(s)", "", num_items_); + } + } + + ui->hintLabel->setText(hint); +} + +void SequenceDialog::mouseWheeled(QWheelEvent *event) +{ + int scroll_x = event->angleDelta().x() * -1 / 8; + scroll_x *= ui->horizontalScrollBar->singleStep(); + if (scroll_x) { + ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->value() + scroll_x); + } + + int scroll_y = event->angleDelta().ry() * -1 / 8; + scroll_y *= ui->verticalScrollBar->singleStep(); + if (scroll_y) { + ui->verticalScrollBar->setValue(ui->verticalScrollBar->value() + scroll_y); + } + + event->accept(); +} + +void SequenceDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + if (button == reset_button_) { + resetView(); + } else if (button == export_button_) { + exportDiagram(); + } +} + +void SequenceDialog::exportDiagram() +{ + QString file_name, extension; + QDir path(mainApp->openDialogInitialDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + 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)"); + QString ascii_filter = tr("ASCII (*.txt)"); + + QString filter = QString("%1;;%2;;%3;;%4") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter); + if (!file_closed_) { + filter.append(QString(";;%5").arg(ascii_filter)); + } + + file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Graph As…")), + path.canonicalPath(), filter, &extension); + + if (file_name.length() > 0) { + bool save_ok = false; + if (extension.compare(pdf_filter) == 0) { + save_ok = ui->sequencePlot->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = ui->sequencePlot->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = ui->sequencePlot->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = ui->sequencePlot->saveJpg(file_name); + } else if (extension.compare(ascii_filter) == 0 && !file_closed_ && info_->sainfo()) { + FILE *outfile = ws_fopen(file_name.toUtf8().constData(), "w"); + if (outfile != NULL) { + sequence_analysis_dump_to_file(outfile, info_->sainfo(), 0); + save_ok = true; + fclose(outfile); + } else { + save_ok = false; + } + } + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } else { + open_failure_alert_box(file_name.toUtf8().constData(), errno, TRUE); + } + } +} + +void SequenceDialog::fillDiagram() +{ + if (!info_->sainfo() || file_closed_) return; + + QCustomPlot *sp = ui->sequencePlot; + + if (strcmp(info_->sainfo()->name, "voip") == 0) { + seq_diagram_->setData(info_->sainfo()); + } else { + seq_diagram_->clearData(); + sequence_analysis_list_free(info_->sainfo()); + + register_analysis_t* analysis = sequence_analysis_find_by_name(info_->sainfo()->name); + if (analysis != NULL) + { + GString *error_string; + const char *filter = NULL; + if (ui->displayFilterCheckBox->checkState() == Qt::Checked) + filter = cap_file_.capFile()->dfilter; + + error_string = register_tap_listener(sequence_analysis_get_tap_listener_name(analysis), info_->sainfo(), filter, sequence_analysis_get_tap_flags(analysis), + NULL, sequence_analysis_get_packet_func(analysis), NULL, NULL); + if (error_string) { + report_failure("Sequence dialog - tap registration failed: %s", error_string->str); + g_string_free(error_string, TRUE); + } + + cf_retap_packets(cap_file_.capFile()); + remove_tap_listener(info_->sainfo()); + + num_items_ = sequence_analysis_get_nodes(info_->sainfo()); + seq_diagram_->setData(info_->sainfo()); + } + } + + sequence_w_ = one_em_ * 15; // Arbitrary + + mouseMoved(NULL); + resetAxes(); + + // XXX QCustomPlot doesn't seem to draw any sort of focus indicator. + sp->setFocus(); +} + +void SequenceDialog::panAxes(int x_pixels, int y_pixels) +{ + // We could simplify this quite a bit if we set the scroll bar values instead. + if (!info_->sainfo()) return; + + QCustomPlot *sp = ui->sequencePlot; + double h_pan = 0.0; + double v_pan = 0.0; + + h_pan = sp->xAxis2->range().size() * x_pixels / sp->xAxis2->axisRect()->width(); + if (h_pan < 0) { + h_pan = qMax(h_pan, min_left_ - sp->xAxis2->range().lower); + } else { + h_pan = qMin(h_pan, info_->sainfo()->num_nodes - sp->xAxis2->range().upper); + } + + v_pan = sp->yAxis->range().size() * y_pixels / sp->yAxis->axisRect()->height(); + if (v_pan < 0) { + v_pan = qMax(v_pan, min_top_ - sp->yAxis->range().lower); + } else { + v_pan = qMin(v_pan, num_items_ - sp->yAxis->range().upper); + } + + if (h_pan && !(sp->xAxis2->range().contains(min_left_) && sp->xAxis2->range().contains(info_->sainfo()->num_nodes))) { + sp->xAxis2->moveRange(h_pan); + sp->replot(); + } + if (v_pan && !(sp->yAxis->range().contains(min_top_) && sp->yAxis->range().contains(num_items_))) { + sp->yAxis->moveRange(v_pan); + sp->replot(); + } +} + +void SequenceDialog::resetAxes(bool keep_lower) +{ + if (!info_->sainfo()) return; + + QCustomPlot *sp = ui->sequencePlot; + + // Allow space for labels on the top and port numbers on the left. + double top_pos = min_top_, left_pos = min_left_; + if (keep_lower) { + top_pos = sp->yAxis->range().lower; + left_pos = sp->xAxis2->range().lower; + } + + double range_span = sp->viewport().width() / sequence_w_ * sp->axisRect()->rangeZoomFactor(Qt::Horizontal); + sp->xAxis2->setRange(left_pos, range_span + left_pos); + + range_span = sp->axisRect()->height() / (one_em_ * 1.5); + sp->yAxis->setRange(top_pos, range_span + top_pos); + + double rmin = sp->xAxis2->range().size() / 2; + ui->horizontalScrollBar->setRange((rmin - 0.5) * 100, (info_->sainfo()->num_nodes - 0.5 - rmin) * 100); + xAxisChanged(sp->xAxis2->range()); + ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->minimum()); // Shouldn't be needed. + + rmin = (sp->yAxis->range().size() / 2); + ui->verticalScrollBar->setRange((rmin - 1.0) * 100, (num_items_ - 0.5 - rmin) * 100); + yAxisChanged(sp->yAxis->range()); + + // It would be exceedingly handy if we could do one or both of the + // following: + // - Position an axis label above its axis inline with the tick labels. + // - Anchor a QCPItemText to one of the corners of a QCPAxis. + // Neither of those appear to be possible, so we first call replot in + // order to lay out our X axes, place our labels, the call replot again. + sp->replot(QCustomPlot::rpQueuedReplot); + + QRect axis_rect = sp->axisRect()->rect(); + + key_text_->position->setCoords(axis_rect.left() + - sp->yAxis->padding() + - sp->yAxis->tickLabelPadding() + - sp->yAxis->offset(), + axis_rect.top() / 2); + comment_text_->position->setCoords(axis_rect.right() + + sp->yAxis2->padding() + + sp->yAxis2->tickLabelPadding() + + sp->yAxis2->offset(), + axis_rect.top() / 2); + + sp->replot(QCustomPlot::rpRefreshHint); +} + +void SequenceDialog::resetView() +{ + resetAxes(); +} + +void SequenceDialog::on_actionGoToPacket_triggered() +{ + if (!file_closed_ && packet_num_ > 0) { + cf_goto_frame(cap_file_.capFile(), packet_num_); + seq_diagram_->setSelectedPacket(packet_num_); + } +} + +void SequenceDialog::goToAdjacentPacket(bool next) +{ + if (file_closed_) return; + + int old_key = seq_diagram_->selectedKey(); + int adjacent_packet = seq_diagram_->adjacentPacket(next); + int new_key = seq_diagram_->selectedKey(); + + if (adjacent_packet > 0) { + if (new_key >= 0) { + QCustomPlot *sp = ui->sequencePlot; + double range_offset = 0.0; + // Scroll if we're at our scroll margin and we haven't reached + // the end of our range. + double scroll_margin = 3.0; // Lines + + if (old_key >= 0) { + range_offset = new_key - old_key; + } + + if (new_key < sp->yAxis->range().lower) { + // Out of range, top + range_offset = qRound(new_key - sp->yAxis->range().lower - scroll_margin - 0.5); + } else if (new_key > sp->yAxis->range().upper) { + // Out of range, bottom + range_offset = qRound(new_key - sp->yAxis->range().upper + scroll_margin + 0.5); + } else if (next) { + // In range, next + if (new_key + scroll_margin < sp->yAxis->range().upper) { + range_offset = 0.0; + } + } else { + // In range, previous + if (new_key - scroll_margin > sp->yAxis->range().lower) { + range_offset = 0.0; + } + } + + // Clamp to our upper & lower bounds. + if (range_offset > 0) { + range_offset = qMin(range_offset, num_items_ - sp->yAxis->range().upper); + } else if (range_offset < 0) { + range_offset = qMax(range_offset, min_top_ - sp->yAxis->range().lower); + } + sp->yAxis->moveRange(range_offset); + } + cf_goto_frame(cap_file_.capFile(), adjacent_packet); + seq_diagram_->setSelectedPacket(adjacent_packet); + } +} + +void SequenceDialog::on_displayFilterCheckBox_toggled(bool) +{ + fillDiagram(); +} + +void SequenceDialog::on_flowComboBox_activated(int index) +{ + if (!info_->sainfo() || (strcmp(info_->sainfo()->name, "voip") == 0) || index < 0) + return; + + register_analysis_t* analysis = VariantPointer::asPtr(ui->flowComboBox->itemData(index)); + info_->sainfo()->name = sequence_analysis_get_name(analysis); + + fillDiagram(); +} + +void SequenceDialog::on_addressComboBox_activated(int index) +{ + if (!info_->sainfo()) return; + + if (index == 0) { + info_->sainfo()->any_addr = TRUE; + } else { + info_->sainfo()->any_addr = FALSE; + } + fillDiagram(); +} + +void SequenceDialog::on_actionMoveRight10_triggered() +{ + panAxes(10, 0); +} + +void SequenceDialog::on_actionMoveLeft10_triggered() +{ + panAxes(-10, 0); +} + +void SequenceDialog::on_actionMoveUp10_triggered() +{ + panAxes(0, 10); +} + +void SequenceDialog::on_actionMoveDown10_triggered() +{ + panAxes(0, -10); +} + +void SequenceDialog::on_actionMoveRight1_triggered() +{ + panAxes(1, 0); +} + +void SequenceDialog::on_actionMoveLeft1_triggered() +{ + panAxes(-1, 0); +} + +void SequenceDialog::on_actionMoveUp1_triggered() +{ + panAxes(0, 1); +} + +void SequenceDialog::on_actionMoveDown1_triggered() +{ + panAxes(0, -1); +} + +void SequenceDialog::on_actionZoomIn_triggered() +{ + zoomXAxis(true); +} + +void SequenceDialog::on_actionZoomOut_triggered() +{ + zoomXAxis(false); +} + +void SequenceDialog::processRtpStream(bool select) +{ + seq_analysis_item_t *current_rtp_sai = NULL; + + // If RTP sai is below mouse, use it. If not, try selected RTP sai + if (current_rtp_sai_hovered_ && GA_INFO_TYPE_RTP == current_rtp_sai_hovered_->info_type) { + current_rtp_sai = current_rtp_sai_hovered_; + } else if (current_rtp_sai_selected_ && GA_INFO_TYPE_RTP == current_rtp_sai_selected_->info_type) { + current_rtp_sai = current_rtp_sai_selected_; + } + + if (current_rtp_sai) { + QVector stream_ids; + + // We don't need copy it as it is not cleared during retap + stream_ids << &((rtpstream_info_t *)current_rtp_sai->info_ptr)->id; + if (select) { + emit rtpStreamsDialogSelectRtpStreams(stream_ids); + } else { + emit rtpStreamsDialogDeselectRtpStreams(stream_ids); + } + raise(); + } +} + +void SequenceDialog::on_actionSelectRtpStreams_triggered() +{ + processRtpStream(true); +} + +void SequenceDialog::on_actionDeselectRtpStreams_triggered() +{ + processRtpStream(false); +} + +void SequenceDialog::zoomXAxis(bool in) +{ + QCustomPlot *sp = ui->sequencePlot; + double h_factor = sp->axisRect()->rangeZoomFactor(Qt::Horizontal); + + if (!in) { + h_factor = pow(h_factor, -1); + } + + sp->xAxis2->scaleRange(h_factor, sp->xAxis->range().lower); + sp->replot(); +} + +bool SequenceDialog::addFlowSequenceItem(const void* key, void *value, void *userdata) +{ + const char* name = (const char*)key; + register_analysis_t* analysis = (register_analysis_t*)value; + sequence_items_t* item_data = (sequence_items_t*)userdata; + + /* XXX - Although "voip" isn't a registered name yet, it appears to have special + handling that will be done outside of registered data */ + if (strcmp(name, "voip") == 0) + return FALSE; + + item_data->flow->addItem(sequence_analysis_get_ui_name(analysis), VariantPointer::asQVariant(analysis)); + + if (item_data->flow->itemData(item_data->curr_index).toString().compare(item_data->info->sainfo()->name) == 0) + item_data->flow->setCurrentIndex(item_data->curr_index); + + item_data->curr_index++; + + return FALSE; +} + +QVectorSequenceDialog::getSelectedRtpIds() +{ + QVector stream_ids; + + if (current_rtp_sai_selected_ && GA_INFO_TYPE_RTP == current_rtp_sai_selected_->info_type) { + stream_ids << &((rtpstream_info_t *)current_rtp_sai_selected_->info_ptr)->id; + } + + return stream_ids; +} + +void SequenceDialog::rtpPlayerReplace() +{ + emit rtpPlayerDialogReplaceRtpStreams(getSelectedRtpIds()); +} + +void SequenceDialog::rtpPlayerAdd() +{ + emit rtpPlayerDialogAddRtpStreams(getSelectedRtpIds()); +} + +void SequenceDialog::rtpPlayerRemove() +{ + emit rtpPlayerDialogRemoveRtpStreams(getSelectedRtpIds()); +} + +void SequenceDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_STAT_FLOW_GRAPH); +} + +SequenceInfo::SequenceInfo(seq_analysis_info_t *sainfo) : + sainfo_(sainfo), + count_(1) +{ +} + +SequenceInfo::~SequenceInfo() +{ + sequence_analysis_info_free(sainfo_); +} diff --git a/ui/qt/sequence_dialog.h b/ui/qt/sequence_dialog.h new file mode 100644 index 00000000..8c321907 --- /dev/null +++ b/ui/qt/sequence_dialog.h @@ -0,0 +1,137 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEQUENCE_DIALOG_H +#define SEQUENCE_DIALOG_H + +#include + +#include + +#include "cfile.h" + +#include "epan/packet.h" +#include "epan/sequence_analysis.h" + +#include +#include "wireshark_dialog.h" +#include "rtp_stream_dialog.h" + +#include + +namespace Ui { +class SequenceDialog; +} + +class SequenceDiagram; + +class SequenceInfo +{ +public: + SequenceInfo(seq_analysis_info_t *sainfo = NULL); + seq_analysis_info_t * sainfo() { return sainfo_;} + void ref() { count_++; } + void unref() { if (--count_ == 0) delete this; } +private: + ~SequenceInfo(); + seq_analysis_info_t *sainfo_; + unsigned int count_; +}; + +class SequenceDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *info = NULL); + ~SequenceDialog(); + void enableVoIPFeatures(); + +protected: + void showEvent(QShowEvent *event); + void resizeEvent(QResizeEvent *event); + void keyPressEvent(QKeyEvent *event); + +signals: + void rtpStreamsDialogSelectRtpStreams(QVector stream_infos); + void rtpStreamsDialogDeselectRtpStreams(QVector stream_infos); + void rtpPlayerDialogReplaceRtpStreams(QVector stream_ids); + void rtpPlayerDialogAddRtpStreams(QVector stream_ids); + void rtpPlayerDialogRemoveRtpStreams(QVector stream_ids); + +private slots: + void updateWidgets(); + void hScrollBarChanged(int value); + void vScrollBarChanged(int value); + void xAxisChanged(QCPRange range); + void yAxisChanged(QCPRange range); + void diagramClicked(QMouseEvent *event); + void mouseMoved(QMouseEvent *event); + void mouseWheeled(QWheelEvent *event); + + void fillDiagram(); + void resetView(); + void exportDiagram(); + + void on_buttonBox_clicked(QAbstractButton *button); + void on_actionGoToPacket_triggered(); + void on_actionGoToNextPacket_triggered() { goToAdjacentPacket(true); } + void on_actionGoToPreviousPacket_triggered() { goToAdjacentPacket(false); } + void on_displayFilterCheckBox_toggled(bool checked); + void on_flowComboBox_activated(int index); + void on_addressComboBox_activated(int index); + void on_actionMoveRight10_triggered(); + void on_actionMoveLeft10_triggered(); + void on_actionMoveUp10_triggered(); + void on_actionMoveDown10_triggered(); + void on_actionMoveRight1_triggered(); + void on_actionMoveLeft1_triggered(); + void on_actionMoveUp1_triggered(); + void on_actionMoveDown1_triggered(); + void on_actionZoomIn_triggered(); + void on_actionZoomOut_triggered(); + void on_actionSelectRtpStreams_triggered(); + void on_actionDeselectRtpStreams_triggered(); + void on_buttonBox_helpRequested(); + + void rtpPlayerReplace(); + void rtpPlayerAdd(); + void rtpPlayerRemove(); + +private: + Ui::SequenceDialog *ui; + SequenceDiagram *seq_diagram_; + SequenceInfo *info_; + int num_items_; + guint32 packet_num_; + double one_em_; + int sequence_w_; + QPushButton *reset_button_; + QToolButton *player_button_; + QPushButton *export_button_; + QMenu ctx_menu_; + QCPItemText *key_text_; + QCPItemText *comment_text_; + seq_analysis_item_t *current_rtp_sai_selected_; // Used for passing current sai to rtp processing + seq_analysis_item_t *current_rtp_sai_hovered_; // Used for passing current sai to rtp processing + QPointer rtp_stream_dialog_; // Singleton pattern used + bool voipFeaturesEnabled; + + void zoomXAxis(bool in); + void panAxes(int x_pixels, int y_pixels); + void resetAxes(bool keep_lower = false); + void goToAdjacentPacket(bool next); + + static bool addFlowSequenceItem(const void *key, void *value, void *userdata); + + void processRtpStream(bool select); + QVectorgetSelectedRtpIds(); +}; + +#endif // SEQUENCE_DIALOG_H diff --git a/ui/qt/sequence_dialog.ui b/ui/qt/sequence_dialog.ui new file mode 100644 index 00000000..a62e76e0 --- /dev/null +++ b/ui/qt/sequence_dialog.ui @@ -0,0 +1,434 @@ + + + SequenceDialog + + + + 0 + 0 + 679 + 568 + + + + + + + + + + 0 + 1 + + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + + + + + + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + + + Limit to display filter + + + + + + + Qt::Horizontal + + + + 13 + 20 + + + + + + + + Flow type: + + + + + + + + + + Qt::Horizontal + + + + 13 + 20 + + + + + + + + Addresses: + + + + + + + + Any + + + + + Network + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Reset &Diagram + + + Reset the diagram to its initial state. + + + 0 + + + + + &Reset Diagram + + + Reset the diagram to its initial state + + + + + &Export + + + Export diagram + + + + + Zoom In + + + Zoom In + + + + + + + + + Zoom Out + + + Zoom Out + + + - + + + + + Move Up 10 Pixels + + + Move Up 10 Pixels + + + Up + + + + + Move Left 10 Pixels + + + Move Left 10 Pixels + + + Left + + + + + Move Right 10 Pixels + + + Move Right 10 Pixels + + + Right + + + + + Move Down 10 Pixels + + + Move Down 10 Pixels + + + Down + + + + + Move Up 1 Pixel + + + Move Up 1 Pixel + + + Shift+Up + + + + + Move Left 1 Pixel + + + Move Left 1 Pixel + + + Shift+Left + + + + + Move Right 1 Pixel + + + Move Right 1 Pixel + + + Shift+Right + + + + + Move Down 1 Pixel + + + Move Down 1 Pixel + + + Shift+Down + + + + + Go To Packet Under Cursor + + + Go to packet currently under the cursor + + + G + + + + + All Flows + + + Show flows for all packets + + + 1 + + + + + TCP Flows + + + Show only TCP flow information + + + 1 + + + + + Go To Next Packet + + + Go to the next packet + + + N + + + + + Go To Previous Packet + + + Go to the previous packet + + + P + + + + + Select RTP Stream + + + Select RTP stream in RTP Streams dialog + + + S + + + + + Deselect RTP Stream + + + Deselect RTP stream in RTP Streams dialog + + + D + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+ + ElidedLabel + QLabel +
widgets/elided_label.h
+
+
+ + + + buttonBox + rejected() + SequenceDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/service_response_time_dialog.cpp b/ui/qt/service_response_time_dialog.cpp new file mode 100644 index 00000000..95bd4801 --- /dev/null +++ b/ui/qt/service_response_time_dialog.cpp @@ -0,0 +1,363 @@ +/* service_response_time_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "service_response_time_dialog.h" + +#include "file.h" + +#include +#include +#include + +#include "rpc_service_response_time_dialog.h" +#include "scsi_service_response_time_dialog.h" +#include "main_application.h" + +#include +#include + +static QHash cfg_str_to_srt_; + +extern "C" { +static void +srt_init(const char *args, void*) { + QStringList args_l = QString(args).split(','); + if (args_l.length() > 1) { + QString srt = QString("%1,%2").arg(args_l[0]).arg(args_l[1]); + QString filter; + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(","); + } + mainApp->emitTapParameterSignal(srt, filter, NULL); + } +} +} + +bool register_service_response_tables(const void *, void *value, void*) +{ + register_srt_t *srt = (register_srt_t*)value; + const char* short_name = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt))); + char *cfg_abbr = srt_table_get_tap_string(srt); + tpdCreator tpd_creator = ServiceResponseTimeDialog::createSrtDialog; + + /* XXX - These dissectors haven't been converted over to due to an "interactive input dialog" for their + tap data. Let those specific dialogs register for themselves */ + if (strcmp(short_name, "DCERPC") == 0) { + short_name = "DCE-RPC"; + tpd_creator = RpcServiceResponseTimeDialog::createDceRpcSrtDialog; + } else if (strcmp(short_name, "RPC") == 0) { + short_name = "ONC-RPC"; + tpd_creator = RpcServiceResponseTimeDialog::createOncRpcSrtDialog; + } else if (strcmp(short_name, "SCSI") == 0) { + tpd_creator = ScsiServiceResponseTimeDialog::createScsiSrtDialog; + } + + cfg_str_to_srt_[cfg_abbr] = srt; + TapParameterDialog::registerDialog( + short_name, + cfg_abbr, + REGISTER_STAT_GROUP_RESPONSE_TIME, + srt_init, + tpd_creator); + g_free(cfg_abbr); + return FALSE; +} + +enum { + srt_table_type_ = 1000, + srt_row_type_ +}; + +class SrtRowTreeWidgetItem : public QTreeWidgetItem +{ +public: + SrtRowTreeWidgetItem(QTreeWidgetItem *parent, const srt_procedure_t *procedure) : + QTreeWidgetItem (parent, srt_row_type_), + procedure_(procedure) + { + setText(SRT_COLUMN_PROCEDURE, procedure_->procedure); + setHidden(true); + } + + void draw() { + setText(SRT_COLUMN_INDEX, QString::number(procedure_->proc_index)); + setText(SRT_COLUMN_CALLS, QString::number(procedure_->stats.num)); + setText(SRT_COLUMN_MIN, QString::number(nstime_to_sec(&procedure_->stats.min), 'f', 6)); + setText(SRT_COLUMN_MAX, QString::number(nstime_to_sec(&procedure_->stats.max), 'f', 6)); + setText(SRT_COLUMN_AVG, QString::number(get_average(&procedure_->stats.tot, procedure_->stats.num) / 1000.0, 'f', 6)); + setText(SRT_COLUMN_SUM, QString::number(nstime_to_sec(&procedure_->stats.tot), 'f', 6)); + + for (int col = 0; col < columnCount(); col++) { + if (col == SRT_COLUMN_PROCEDURE) continue; + setTextAlignment(col, Qt::AlignRight); + } + + setHidden(procedure_->stats.num < 1); + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != srt_row_type_) return QTreeWidgetItem::operator< (other); + const SrtRowTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case SRT_COLUMN_INDEX: + return procedure_->proc_index < other_row->procedure_->proc_index; + case SRT_COLUMN_CALLS: + return procedure_->stats.num < other_row->procedure_->stats.num; + case SRT_COLUMN_MIN: + return nstime_cmp(&procedure_->stats.min, &other_row->procedure_->stats.min) < 0; + case SRT_COLUMN_MAX: + return nstime_cmp(&procedure_->stats.max, &other_row->procedure_->stats.max) < 0; + case SRT_COLUMN_AVG: + { + double our_avg = get_average(&procedure_->stats.tot, procedure_->stats.num); + double other_avg = get_average(&other_row->procedure_->stats.tot, other_row->procedure_->stats.num); + return our_avg < other_avg; + } + case SRT_COLUMN_SUM: + return nstime_cmp(&procedure_->stats.tot, &other_row->procedure_->stats.tot) < 0; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList rowData() { + return QList() << QString(procedure_->procedure) << procedure_->proc_index << procedure_->stats.num + << nstime_to_sec(&procedure_->stats.min) << nstime_to_sec(&procedure_->stats.max) + << get_average(&procedure_->stats.tot, procedure_->stats.num) / 1000.0 + << nstime_to_sec(&procedure_->stats.tot); + } +private: + const srt_procedure_t *procedure_; +}; + +class SrtTableTreeWidgetItem : public QTreeWidgetItem +{ +public: + SrtTableTreeWidgetItem(QTreeWidget *parent, const srt_stat_table *srt_table) : + QTreeWidgetItem (parent, srt_table_type_), + srt_table_(srt_table) + { + setText(0, srt_table_->name); + setFirstColumnSpanned(true); + setExpanded(true); + + for (int i = 0; i < srt_table_->num_procs; i++) { + new SrtRowTreeWidgetItem(this, &srt_table_->procedures[i]); + } + } + const QString columnTitle() { return srt_table_->proc_column_name; } + + QList rowData() { + return QList() << srt_table_->name; + } + const QString filterField() { return srt_table_->filter_string; } + +private: + const srt_stat_table *srt_table_; +}; + + +ServiceResponseTimeDialog::ServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, register_srt *srt, const QString filter, int help_topic) : + TapParameterDialog(parent, cf, help_topic), + srt_(srt) +{ + QString subtitle = QString("%1 Service Response Time Statistics") + .arg(proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)))); + setWindowSubtitle(subtitle); + loadGeometry(0, 0, "ServiceResponseTimeDialog"); + + srt_data_.srt_array = NULL; + srt_data_.user_data = NULL; + + // Add number of columns for this stats_tree + QStringList header_labels; + for (int col = 0; col < NUM_SRT_COLUMNS; col++) { + header_labels.push_back(service_response_time_get_column_name(col)); + } + statsTreeWidget()->setColumnCount(static_cast(header_labels.count())); + statsTreeWidget()->setHeaderLabels(header_labels); + + for (int col = 0; col < statsTreeWidget()->columnCount(); col++) { + if (col == SRT_COLUMN_PROCEDURE) continue; + statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); + } + + addFilterActions(); + + if (!filter.isEmpty()) { + setDisplayFilter(filter); + } + + connect(statsTreeWidget(), &QTreeWidget::itemChanged, + this, &ServiceResponseTimeDialog::statsTreeWidgetItemChanged); +} + +ServiceResponseTimeDialog::~ServiceResponseTimeDialog() +{ + if (srt_data_.srt_array) { + free_srt_table(srt_, srt_data_.srt_array); + g_array_free(srt_data_.srt_array, TRUE); + } +} + +TapParameterDialog *ServiceResponseTimeDialog::createSrtDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf) +{ + if (!cfg_str_to_srt_.contains(cfg_str)) { + // XXX MessageBox? + return NULL; + } + + register_srt_t *srt = cfg_str_to_srt_[cfg_str]; + + return new ServiceResponseTimeDialog(parent, cf, srt, filter); +} + +void ServiceResponseTimeDialog::addSrtTable(const struct _srt_stat_table *srt_table) +{ + new SrtTableTreeWidgetItem(statsTreeWidget(), srt_table); +} + +void ServiceResponseTimeDialog::tapReset(void *srtd_ptr) +{ + srt_data_t *srtd = (srt_data_t*) srtd_ptr; + ServiceResponseTimeDialog *srt_dlg = static_cast(srtd->user_data); + if (!srt_dlg) return; + + reset_srt_table(srtd->srt_array); + + srt_dlg->statsTreeWidget()->clear(); +} + +void ServiceResponseTimeDialog::tapDraw(void *srtd_ptr) +{ + srt_data_t *srtd = (srt_data_t*) srtd_ptr; + ServiceResponseTimeDialog *srt_dlg = static_cast(srtd->user_data); + if (!srt_dlg || !srt_dlg->statsTreeWidget()) return; + + QTreeWidgetItemIterator it(srt_dlg->statsTreeWidget()); + while (*it) { + if ((*it)->type() == srt_row_type_) { + SrtRowTreeWidgetItem *srtr_ti = static_cast((*it)); + srtr_ti->draw(); + } + ++it; + } + + for (int i = 0; i < srt_dlg->statsTreeWidget()->columnCount() - 1; i++) { + srt_dlg->statsTreeWidget()->resizeColumnToContents(i); + } +} + +void ServiceResponseTimeDialog::endRetapPackets() +{ + for (guint i = 0; i < srt_data_.srt_array->len; i++) { + srt_stat_table *srt_table = g_array_index(srt_data_.srt_array, srt_stat_table*, i); + addSrtTable(srt_table); + } + WiresharkDialog::endRetapPackets(); +} + +void ServiceResponseTimeDialog::fillTree() +{ + if (srt_data_.srt_array) { + free_srt_table(srt_, srt_data_.srt_array); + g_array_free(srt_data_.srt_array, TRUE); + } + srt_data_.srt_array = g_array_new(FALSE, TRUE, sizeof(srt_stat_table*)); + srt_data_.user_data = this; + + provideParameterData(); + + srt_table_dissector_init(srt_, srt_data_.srt_array); + + QString display_filter = displayFilter(); + if (!registerTapListener(get_srt_tap_listener_name(srt_), + &srt_data_, + display_filter.toUtf8().constData(), + 0, + tapReset, + get_srt_packet_func(srt_), + tapDraw)) { + reject(); // XXX Stay open instead? + return; + } + + statsTreeWidget()->setSortingEnabled(false); + + cap_file_.retapPackets(); + + // We only have one table. Move its tree items up one level. + if (statsTreeWidget()->invisibleRootItem()->childCount() == 1) { + statsTreeWidget()->setRootIndex(statsTreeWidget()->model()->index(0, 0)); + } + + tapDraw(&srt_data_); + + statsTreeWidget()->sortItems(SRT_COLUMN_PROCEDURE, Qt::AscendingOrder); + statsTreeWidget()->setSortingEnabled(true); + + removeTapListeners(); +} + +QList ServiceResponseTimeDialog::treeItemData(QTreeWidgetItem *ti) const +{ + QList tid; + if (ti->type() == srt_table_type_) { + SrtTableTreeWidgetItem *srtt_ti = static_cast(ti); + if (srtt_ti) { + tid << srtt_ti->rowData(); + } + } else if (ti->type() == srt_row_type_) { + SrtRowTreeWidgetItem *srtr_ti = static_cast(ti); + if (srtr_ti) { + tid << srtr_ti->rowData(); + } + } + return tid; +} + +const QString ServiceResponseTimeDialog::filterExpression() +{ + QString filter_expr; + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + if (ti->type() == srt_row_type_) { + SrtTableTreeWidgetItem *srtt_ti = static_cast(ti->parent()); + ws_assert(srtt_ti); + QString field = srtt_ti->filterField(); + QString value = ti->text(SRT_COLUMN_INDEX); + if (!field.isEmpty() && !value.isEmpty()) { + filter_expr = QString("%1==%2").arg(field).arg(value); + } + } + } + return filter_expr; +} + +void ServiceResponseTimeDialog::statsTreeWidgetItemChanged() +{ + QString procedure_title = service_response_time_get_column_name(SRT_COLUMN_PROCEDURE); + + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + SrtTableTreeWidgetItem *srtt_ti = NULL; + if (ti->type() == srt_row_type_) { + srtt_ti = static_cast(ti->parent()); + } else { + srtt_ti = static_cast(ti); + } + if (srtt_ti) { + procedure_title = srtt_ti->columnTitle(); + } + } + statsTreeWidget()->headerItem()->setText(SRT_COLUMN_PROCEDURE, procedure_title); +} diff --git a/ui/qt/service_response_time_dialog.h b/ui/qt/service_response_time_dialog.h new file mode 100644 index 00000000..7830e11f --- /dev/null +++ b/ui/qt/service_response_time_dialog.h @@ -0,0 +1,74 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __SERVICE_RESPONSE_TIME_DIALOG_H__ +#define __SERVICE_RESPONSE_TIME_DIALOG_H__ + +#include "tap_parameter_dialog.h" +#include + +struct register_srt; +struct _srt_stat_table; + +class QTreeWidgetItem; + +class ServiceResponseTimeDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + ServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, struct register_srt *srt, const QString filter, int help_topic = 0); + ~ServiceResponseTimeDialog(); + static TapParameterDialog *createSrtDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf); + +public slots: + void endRetapPackets(); + +protected: + struct register_srt *srt_; + + /** Add a service response time table. + * + * In the GTK+ UI "tables" are separate, tabbed widgets. In the Qt UI they are + * separate groups of QTreeWidgetItems. + * + * @param srt_table The table to add. + */ + // gtk:service_response_table.h:init_srt_table + void addSrtTable(const struct _srt_stat_table *srt_table); + + + virtual void provideParameterData() {} + +protected slots: + void fillTree(); + +private: + // Callbacks for register_tap_listener + static void tapReset(void *srtd_ptr); + static void tapDraw(void *srtd_ptr); + + virtual QList treeItemData(QTreeWidgetItem *ti) const; + virtual const QString filterExpression(); + + srt_data_t srt_data_; + +private slots: + void statsTreeWidgetItemChanged(); +}; + +/** Register function to register dissectors that support SRT. + * + * @param key is unused + * @param value register_srt_t* representing dissetor SRT table + * @param userdata is unused + */ +bool register_service_response_tables(const void *key, void *value, void *userdata); + +#endif // __SERVICE_RESPONSE_TIME_DIALOG_H__ diff --git a/ui/qt/show_packet_bytes_dialog.cpp b/ui/qt/show_packet_bytes_dialog.cpp new file mode 100644 index 00000000..d85a1ea5 --- /dev/null +++ b/ui/qt/show_packet_bytes_dialog.cpp @@ -0,0 +1,932 @@ +/* show_packet_bytes_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "show_packet_bytes_dialog.h" +#include + +#include "main_window.h" +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include "ui/recent.h" + +#include "epan/strutil.h" + +#include "wsutil/utf8_entities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// To do: +// - Add show as custom protocol in a Packet Details view +// - Use ByteViewText to ShowAsHexDump and supplementary view for custom protocol +// - Handle large data blocks + +Q_DECLARE_METATYPE(bytes_show_type) +Q_DECLARE_METATYPE(bytes_decode_type) + +ShowPacketBytesDialog::ShowPacketBytesDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::ShowPacketBytesDialog), + finfo_(cf.capFile()->finfo_selected), + use_regex_find_(false) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 2 / 3, parent.height() * 3 / 4); + + QString field_name = QString("%1 (%2)").arg(finfo_->hfinfo->name, finfo_->hfinfo->abbrev); + setWindowSubtitle (field_name); + + hint_label_ = tr("Frame %1, %2, %Ln byte(s).", "", finfo_->length) + .arg(cf.capFile()->current_frame->num) + .arg(field_name); + + ui->tePacketBytes->installEventFilter(this); + + connect(ui->tePacketBytes, SIGNAL(showSelected(int,int)), this, SLOT(showSelected(int,int))); + connect(ui->leFind, SIGNAL(useRegexFind(bool)), this, SLOT(useRegexFind(bool))); + + ui->cbDecodeAs->blockSignals(true); + ui->cbDecodeAs->addItem(tr("None"), DecodeAsNone); + ui->cbDecodeAs->addItem(tr("Base64"), DecodeAsBASE64); + ui->cbDecodeAs->addItem(tr("Compressed"), DecodeAsCompressed); + ui->cbDecodeAs->addItem(tr("Hex Digits"), DecodeAsHexDigits); + ui->cbDecodeAs->addItem(tr("Percent-Encoding"), DecodeAsPercentEncoding); + ui->cbDecodeAs->addItem(tr("Quoted-Printable"), DecodeAsQuotedPrintable); + ui->cbDecodeAs->addItem(tr("ROT13"), DecodeAsROT13); + ui->cbDecodeAs->setCurrentIndex(ui->cbDecodeAs->findData(recent.gui_show_bytes_decode)); + ui->cbDecodeAs->blockSignals(false); + + ui->cbShowAs->blockSignals(true); + ui->cbShowAs->addItem(tr("ASCII"), SHOW_ASCII); + ui->cbShowAs->addItem(tr("ASCII & Control"), SHOW_ASCII_CONTROL); + ui->cbShowAs->addItem(tr("C Array"), SHOW_CARRAY); + ui->cbShowAs->addItem(tr("EBCDIC"), SHOW_EBCDIC); + ui->cbShowAs->addItem(tr("Hex Dump"), SHOW_HEXDUMP); + ui->cbShowAs->addItem(tr("HTML"), SHOW_HTML); + ui->cbShowAs->addItem(tr("Image"), SHOW_IMAGE); + ui->cbShowAs->addItem(tr("JSON"), SHOW_JSON); + ui->cbShowAs->addItem(tr("Raw"), SHOW_RAW); + ui->cbShowAs->addItem(tr("Rust Array"), SHOW_RUSTARRAY); + // UTF-8 is guaranteed to exist as a QTextCodec + ui->cbShowAs->addItem(tr("UTF-8"), SHOW_CODEC); + ui->cbShowAs->addItem(tr("YAML"), SHOW_YAML); + ui->cbShowAs->setCurrentIndex(ui->cbShowAs->findData(recent.gui_show_bytes_show)); + ui->cbShowAs->blockSignals(false); + + ui->sbStart->setMinimum(0); + ui->sbEnd->setMaximum(finfo_->length - 1); + + print_button_ = ui->buttonBox->addButton(tr("Print"), QDialogButtonBox::ActionRole); + connect(print_button_, SIGNAL(clicked()), this, SLOT(printBytes())); + + copy_button_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); + connect(copy_button_, SIGNAL(clicked()), this, SLOT(copyBytes())); + + save_as_button_ = ui->buttonBox->addButton(tr("Save as…"), QDialogButtonBox::ActionRole); + connect(save_as_button_, SIGNAL(clicked()), this, SLOT(saveAs())); + + connect(ui->buttonBox, SIGNAL(helpRequested()), this, SLOT(helpButton())); + + setStartAndEnd(0, (finfo_->length - 1)); + updateFieldBytes(true); +} + +ShowPacketBytesDialog::~ShowPacketBytesDialog() +{ + delete ui; +} + +void ShowPacketBytesDialog::addCodecs(const QMap &codecMap) +{ + ui->cbShowAs->blockSignals(true); + // Make the combobox respect max visible items? + //ui->cbShowAs->setStyleSheet("QComboBox { combobox-popup: 0;}"); + ui->cbShowAs->insertSeparator(ui->cbShowAs->count()); + for (const auto &codec : codecMap) { + // This is already placed in the menu and handled separately + if (codec->name() != "US-ASCII" && codec->name() != "UTF-8") + ui->cbShowAs->addItem(tr(codec->name()), SHOW_CODEC); + } + ui->cbShowAs->blockSignals(false); +} + +void ShowPacketBytesDialog::showSelected(int start, int end) +{ + if (end == -1) { + // end set to -1 means show all packet bytes + setStartAndEnd(0, (finfo_->length - 1)); + } else { + if (recent.gui_show_bytes_show == SHOW_RAW) { + start /= 2; + end = (end + 1) / 2; + } + setStartAndEnd(start_ + start, start_ + end - 1); + } + updateFieldBytes(); +} + +void ShowPacketBytesDialog::setStartAndEnd(int start, int end) +{ + start_ = start; + end_ = end; + + ui->sbStart->blockSignals(true); + ui->sbStart->setMaximum(end_); + ui->sbStart->setValue(start_); + ui->sbStart->blockSignals(false); + + ui->sbEnd->blockSignals(true); + ui->sbEnd->setMinimum(start_); + ui->sbEnd->setValue(end_); + ui->sbEnd->blockSignals(false); + + updateHintLabel(); +} + +bool ShowPacketBytesDialog::enableShowSelected() +{ + // "Show Selected" only works when showing all bytes: + // - DecodeAs must not alter the number of bytes in the buffer + // - ShowAs must show all bytes in the buffer + + return (((recent.gui_show_bytes_decode == DecodeAsNone) || + (recent.gui_show_bytes_decode == DecodeAsROT13)) && + ((recent.gui_show_bytes_show == SHOW_ASCII) || + (recent.gui_show_bytes_show == SHOW_ASCII_CONTROL) || + (recent.gui_show_bytes_show == SHOW_EBCDIC) || + (recent.gui_show_bytes_show == SHOW_RAW))); +} + +void ShowPacketBytesDialog::updateWidgets() +{ + WiresharkDialog::updateWidgets(); +} + +void ShowPacketBytesDialog::updateHintLabel() +{ + QString hint = hint_label_; + + if (start_ > 0 || end_ < (finfo_->length - 1)) { + hint.append(" " + + tr("Displaying %Ln byte(s).", "", end_ - start_ + 1) + + ""); + } + + ui->hintLabel->setText("" + hint + ""); +} + +void ShowPacketBytesDialog::on_sbStart_valueChanged(int value) +{ + start_ = value; + ui->sbEnd->setMinimum(value); + + updateHintLabel(); + updateFieldBytes(); +} + +void ShowPacketBytesDialog::on_sbEnd_valueChanged(int value) +{ + end_ = value; + ui->sbStart->setMaximum(value); + + updateHintLabel(); + updateFieldBytes(); +} + +void ShowPacketBytesDialog::on_cbDecodeAs_currentIndexChanged(int idx) +{ + if (idx < 0) return; + recent.gui_show_bytes_decode = ui->cbDecodeAs->currentData().value(); + + ui->tePacketBytes->setShowSelectedEnabled(enableShowSelected()); + + updateFieldBytes(); +} + +void ShowPacketBytesDialog::on_cbShowAs_currentIndexChanged(int idx) +{ + if (idx < 0) return; + recent.gui_show_bytes_show = ui->cbShowAs->currentData().value(); + + ui->tePacketBytes->setShowSelectedEnabled(enableShowSelected()); + ui->lFind->setEnabled(true); + ui->leFind->setEnabled(true); + ui->bFind->setEnabled(true); + print_button_->setEnabled(true); + copy_button_->setEnabled(true); + save_as_button_->setEnabled(true); + + updatePacketBytes(); +} + +void ShowPacketBytesDialog::useRegexFind(bool use_regex) +{ + use_regex_find_ = use_regex; + if (use_regex_find_) + ui->lFind->setText(tr("Regex Find:")); + else + ui->lFind->setText(tr("Find:")); +} + +void ShowPacketBytesDialog::findText(bool go_back) +{ + if (ui->leFind->text().isEmpty()) return; + + bool found; + if (use_regex_find_) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + QRegularExpression regex(ui->leFind->text(), QRegularExpression::UseUnicodePropertiesOption); +#else + QRegExp regex(ui->leFind->text()); +#endif + found = ui->tePacketBytes->find(regex); + } else { + found = ui->tePacketBytes->find(ui->leFind->text()); + } + + if (found) { + ui->tePacketBytes->setFocus(); + } else if (go_back) { + ui->tePacketBytes->moveCursor(QTextCursor::Start); + findText(false); + } +} + +void ShowPacketBytesDialog::printBytes() +{ +#ifndef QT_NO_PRINTER + QPrinter printer(QPrinter::HighResolution); + QPrintDialog dialog(&printer, this); + if (dialog.exec() == QDialog::Accepted) + ui->tePacketBytes->print(&printer); +#endif +} + +void ShowPacketBytesDialog::copyBytes() +{ + switch (recent.gui_show_bytes_show) { + + case SHOW_ASCII: + { + QByteArray ba(field_bytes_); + sanitizeBuffer(ba, true); + mainApp->clipboard()->setText(ba); + break; + } + + case SHOW_ASCII_CONTROL: + case SHOW_CARRAY: + case SHOW_RUSTARRAY: + case SHOW_EBCDIC: + case SHOW_HEXDUMP: + case SHOW_JSON: + case SHOW_RAW: + case SHOW_YAML: + mainApp->clipboard()->setText(ui->tePacketBytes->toPlainText()); + break; + + case SHOW_HTML: + mainApp->clipboard()->setText(ui->tePacketBytes->toHtml()); + break; + + case SHOW_IMAGE: + mainApp->clipboard()->setImage(image_); + break; + + case SHOW_CODEC: + mainApp->clipboard()->setText(ui->tePacketBytes->toPlainText().toUtf8()); + break; + } +} + +void ShowPacketBytesDialog::saveAs() +{ + QString file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Selected Packet Bytes As…"))); + + if (file_name.isEmpty()) + return; + + QFile::OpenMode open_mode = QFile::WriteOnly; + switch (recent.gui_show_bytes_show) { + case SHOW_ASCII: + case SHOW_ASCII_CONTROL: + case SHOW_CARRAY: + case SHOW_RUSTARRAY: + case SHOW_EBCDIC: + // We always save as UTF-8, so set text mode as we would for UTF-8 + case SHOW_CODEC: + case SHOW_HEXDUMP: + case SHOW_JSON: + case SHOW_YAML: + case SHOW_HTML: + open_mode |= QFile::Text; + default: + break; + } + + QFile file(file_name); + file.open(open_mode); + + switch (recent.gui_show_bytes_show) { + + case SHOW_ASCII: + { + QByteArray ba(field_bytes_); + sanitizeBuffer(ba, true); + file.write(ba); + break; + } + + case SHOW_ASCII_CONTROL: + case SHOW_CARRAY: + case SHOW_RUSTARRAY: + case SHOW_EBCDIC: + case SHOW_HEXDUMP: + case SHOW_JSON: + case SHOW_YAML: + { + QTextStream out(&file); + out << ui->tePacketBytes->toPlainText(); + break; + } + + case SHOW_HTML: + { + QTextStream out(&file); + out << ui->tePacketBytes->toHtml(); + break; + } + + case SHOW_CODEC: + { + QTextStream out(&file); + out << ui->tePacketBytes->toPlainText().toUtf8(); + break; + } + + case SHOW_IMAGE: + case SHOW_RAW: + file.write(field_bytes_); + break; + } + + file.close(); +} + +void ShowPacketBytesDialog::helpButton() +{ + mainApp->helpTopicAction(HELP_SHOW_PACKET_BYTES_DIALOG); +} + +void ShowPacketBytesDialog::on_bFind_clicked() +{ + findText(); +} + +void ShowPacketBytesDialog::on_leFind_returnPressed() +{ + findText(); +} + +// Not sure why we have to do this manually. +void ShowPacketBytesDialog::on_buttonBox_rejected() +{ + WiresharkDialog::reject(); +} + +// The following keyboard shortcuts should work (although +// they may not work consistently depending on focus): +// / (slash), Ctrl-F - Focus and highlight the search box +// Ctrl-G, Ctrl-N, F3 - Find next +// Should we make it so that typing any text starts searching? +bool ShowPacketBytesDialog::eventFilter(QObject *, QEvent *event) +{ + if (ui->tePacketBytes->hasFocus() && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->matches(QKeySequence::SelectAll) || keyEvent->matches(QKeySequence::Copy) + || keyEvent->text().isEmpty()) { + return false; + } + ui->leFind->setFocus(); + if (keyEvent->matches(QKeySequence::Find)) { + return true; + } else if (keyEvent->matches(QKeySequence::FindNext)) { + findText(); + return true; + } + } + + return false; +} + +void ShowPacketBytesDialog::keyPressEvent(QKeyEvent *event) +{ + if (ui->leFind->hasFocus()) { + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + findText(); + return; + } + } else { + if (event->key() == Qt::Key_Slash || event->matches(QKeySequence::Find)) { + ui->leFind->setFocus(); + ui->leFind->selectAll(); + } + return; + } + + if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_N && event->modifiers() & Qt::ControlModifier)) { + findText(); + return; + } + + QDialog::keyPressEvent(event); +} + +void ShowPacketBytesDialog::sanitizeBuffer(QByteArray &ba, bool keep_CR) +{ + for (int i = 0; i < ba.length(); i++) { + if (ba[i] == '\n' || (keep_CR && ba[i] == '\r')) + // Keep LF and optionally CR + continue; + + if (ba[i] == '\0' || g_ascii_isspace(ba[i])) { + ba[i] = ' '; + } else if (!g_ascii_isprint(ba[i])) { + ba.replace(i, 1, UTF8_MIDDLE_DOT); + i += sizeof(UTF8_MIDDLE_DOT) - 2; + } + } +} + +void ShowPacketBytesDialog::symbolizeBuffer(QByteArray &ba) +{ + // Replace all octets that don't correspond to an ASCII + // character with MIDDLE DOT. An octet corresponds to an + // ASCII character iff the 0x80 bit isn't set in its + // value; if char is signed (which it is *not* guaranteed + // to be; it is, for example, unsigned on non-Apple ARM + // platforms), sign-extension won't affect that bit, so + // simply testing the 0x80 bit suffices on all platforms. + for (int i = 0; i < ba.length(); i++) { + if (ba[i] & 0x80) { + ba.replace(i, 1, UTF8_MIDDLE_DOT); + i += sizeof(UTF8_MIDDLE_DOT) - 2; + } + } + + // Replace all control characters (NUL through US, i.e. [0, ' '), + // and DEL, i.e. 0x7f) with the code point for the symbol for that + // character, i.e. the character's abbreviation in small letters. + // + // The UTF-8 encodings for those code points are all three octets + // long, from 0xe2 0x90 0x80 through 0xe2 0x90 0xa1, so we initialize + // a QByteArray with the octets for the symbol for NUL and, for + // each of the octets from 0x00 through 0x1f, replace all + // occurrences of that value with that sequence, and then add 1 to + // the last octet of the sequence to get the symbol for the next + // value and continue. + // + QByteArray symbol(UTF8_SYMBOL_FOR_NULL); + for (char i = 0; i < ' '; i++) { + // Replace all occurrences of that value with that symbol. + ba.replace(i, symbol); + // Get the symbol for the next value. + symbol[2] = symbol[2] + 1; + } + // symbol now has the UTF-8 for the symbol for SP, as that follows + // the symbol for US; skip it - the next code point is for the + // symbol for DEL. + symbol[2] = symbol[2] + 1; + ba.replace((char)0x7f, symbol); // DEL +} + +QByteArray ShowPacketBytesDialog::decodeQuotedPrintable(const guint8 *bytes, int length) +{ + QByteArray ba; + + for (int i = 0; i < length; i++) { + if (bytes[i] == '=' && i + 1 < length) { + if (bytes[i+1] == '\n') { + i++; // Soft line break LF + } else if (bytes[i+1] == '\r' && i + 2 < length && bytes[i+2] == '\n') { + i += 2; // Soft line break CRLF + } else if (g_ascii_isxdigit(bytes[i+1]) && i + 2 < length && g_ascii_isxdigit(bytes[i+2])) { + ba.append(QByteArray::fromHex(QByteArray((const char *)&bytes[i+1], 2))); + i += 2; // Valid Quoted-Printable sequence + } else { + // Illegal Quoted-Printable, just add byte + ba.append(bytes[i]); + } + } else { + ba.append(bytes[i]); + } + } + + return ba; +} + +void ShowPacketBytesDialog::rot13(QByteArray &ba) +{ + for (int i = 0; i < ba.length(); i++) { + gchar upper = g_ascii_toupper(ba[i]); + if (upper >= 'A' && upper <= 'M') ba[i] = ba[i] + 13; + else if (upper >= 'N' && upper <= 'Z') ba[i] = ba[i] - 13; + } +} + +void ShowPacketBytesDialog::updateFieldBytes(bool initialization) +{ + int start = finfo_->start + start_; + int length = end_ - start_ + 1; + const guint8 *bytes; + gsize new_length = 0; + + if (!finfo_->ds_tvb) + return; + + switch (recent.gui_show_bytes_decode) { + + case DecodeAsNone: + bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1); + field_bytes_ = QByteArray((const char *)bytes, length); + break; + + case DecodeAsBASE64: + { + bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1); + field_bytes_ = QByteArray((const char *)bytes, length); + if (field_bytes_.size() > 1) { + g_base64_decode_inplace(field_bytes_.data(), &new_length); + } + field_bytes_.resize((int)new_length); + break; + } + + case DecodeAsCompressed: + { + tvbuff *uncompr_tvb = tvb_uncompress(finfo_->ds_tvb, start, length); + if (uncompr_tvb) { + bytes = tvb_get_ptr(uncompr_tvb, 0, -1); + field_bytes_ = QByteArray((const char *)bytes, tvb_reported_length(uncompr_tvb)); + tvb_free(uncompr_tvb); + } else { + field_bytes_.clear(); + } + break; + } + + case DecodeAsHexDigits: + bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1); + field_bytes_ = QByteArray::fromHex(QByteArray::fromRawData((const char *)bytes, length)); + break; + + case DecodeAsPercentEncoding: + { + bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1); +#if GLIB_CHECK_VERSION(2, 66, 0) + GBytes *ba = g_uri_unescape_bytes((const char*)bytes, length, NULL, NULL); + if (ba != NULL) { + gsize size; + const char* data = (const char *)g_bytes_unref_to_data(ba, &size); + field_bytes_ = QByteArray(data, (int)size); + } +#else + GByteArray *ba = g_byte_array_new(); + if (uri_to_bytes((const char*)bytes, ba, length)) { + field_bytes_ = QByteArray((const char *)ba->data, ba->len); + } + g_byte_array_free(ba, TRUE); +#endif + break; + } + + case DecodeAsQuotedPrintable: + bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1); + field_bytes_ = decodeQuotedPrintable(bytes, length); + break; + + case DecodeAsROT13: + bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1); + field_bytes_ = QByteArray((const char *)bytes, length); + rot13(field_bytes_); + break; + } + + // Try loading as image at startup + if (initialization && image_.loadFromData(field_bytes_)) { + recent.gui_show_bytes_show = SHOW_IMAGE; + ui->cbShowAs->blockSignals(true); + ui->cbShowAs->setCurrentIndex(ui->cbShowAs->findData(SHOW_IMAGE)); + ui->cbShowAs->blockSignals(false); + } + + updatePacketBytes(); +} + +void ShowPacketBytesDialog::updatePacketBytes(void) +{ + static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + ui->tePacketBytes->clear(); + ui->tePacketBytes->setCurrentFont(mainApp->monospaceFont()); + + switch (recent.gui_show_bytes_show) { + + case SHOW_ASCII: + { + QByteArray ba(field_bytes_); + sanitizeBuffer(ba, false); + ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth); + ui->tePacketBytes->setPlainText(ba); + break; + } + + case SHOW_ASCII_CONTROL: + { + QByteArray ba(field_bytes_); + symbolizeBuffer(ba); + ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth); + ui->tePacketBytes->setPlainText(ba); + break; + } + + case SHOW_CARRAY: + { + int pos = 0, len = static_cast(field_bytes_.length()); + QString text("char packet_bytes[] = {\n"); + + while (pos < len) { + gchar hexbuf[256]; + char *cur = hexbuf; + int i; + + *cur++ = ' '; + for (i = 0; i < 8 && pos + i < len; i++) { + // Prepend entries with " 0x" + *cur++ = ' '; + *cur++ = '0'; + *cur++ = 'x'; + *cur++ = hexchars[(field_bytes_[pos + i] & 0xf0) >> 4]; + *cur++ = hexchars[field_bytes_[pos + i] & 0x0f]; + + // Delimit array entries with a comma + if (pos + i + 1 < len) + *cur++ = ','; + } + + pos += i; + *cur++ = '\n'; + *cur = 0; + + text.append(hexbuf); + } + + text.append("};\n"); + ui->tePacketBytes->setLineWrapMode(QTextEdit::NoWrap); + ui->tePacketBytes->setPlainText(text); + break; + } + + case SHOW_RUSTARRAY: + { + int pos = 0, len = static_cast(field_bytes_.length()); + QString text("let packet_bytes: [u8; _] = [\n"); + + while (pos < len) { + gchar hexbuf[256]; + char *cur = hexbuf; + int i; + + *cur++ = ' '; + for (i = 0; i < 8 && pos + i < len; i++) { + // Prepend entries with " 0x" + *cur++ = ' '; + *cur++ = '0'; + *cur++ = 'x'; + *cur++ = hexchars[(field_bytes_[pos + i] & 0xf0) >> 4]; + *cur++ = hexchars[field_bytes_[pos + i] & 0x0f]; + + // Delimit array entries with a comma + if (pos + i + 1 < len) + *cur++ = ','; + } + + pos += i; + *cur++ = '\n'; + *cur = 0; + + text.append(hexbuf); + } + + text.append("];\n"); + ui->tePacketBytes->setLineWrapMode(QTextEdit::NoWrap); + ui->tePacketBytes->setPlainText(text); + break; + } + + case SHOW_CODEC: + { + // The QTextCodecs docs say that there's a flag to cause invalid + // characters to be replaced with null. It's unclear what happens + // in the default case; it might depend on the codec though it + // seems that in practice replacement characters are used. + QTextCodec *codec = QTextCodec::codecForName(ui->cbShowAs->currentText().toUtf8()); + QByteArray ba(field_bytes_); + QString decoded = codec->toUnicode(ba); + ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth); + ui->tePacketBytes->setPlainText(decoded); + break; + } + + case SHOW_EBCDIC: + { + QByteArray ba(field_bytes_); + EBCDIC_to_ASCII((guint8*)ba.data(), static_cast(ba.length())); + sanitizeBuffer(ba, false); + ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth); + ui->tePacketBytes->setPlainText(ba); + break; + } + + case SHOW_HEXDUMP: + { + int pos = 0, len = static_cast(field_bytes_.length()); + // Use 16-bit offset if there are <= 65536 bytes, 32-bit offset if there are more + unsigned int offset_chars = (len - 1 <= 0xFFFF) ? 4 : 8; + QString text; + text.reserve((len / 16) * 80); + + while (pos < len) { + char hexbuf[256]; + char *cur = hexbuf; + int i; + + // Dump offset + cur += snprintf(cur, 20, "%0*X ", offset_chars, pos); + + // Dump bytes as hex + for (i = 0; i < 16 && pos + i < len; i++) { + *cur++ = hexchars[(field_bytes_[pos + i] & 0xf0) >> 4]; + *cur++ = hexchars[field_bytes_[pos + i] & 0x0f]; + *cur++ = ' '; + if (i == 7) + *cur++ = ' '; + } + + while (cur < hexbuf + offset_chars + 53) + *cur++ = ' '; // Fill it up with space to ascii column + + // Dump bytes as text + for (i = 0; i < 16 && pos + i < len; i++) { + if (g_ascii_isprint(field_bytes_[pos + i])) { + *cur++ = field_bytes_[pos + i]; + } else { + memcpy(cur, UTF8_MIDDLE_DOT, sizeof(UTF8_MIDDLE_DOT) - 1); + cur += sizeof(UTF8_MIDDLE_DOT) - 1; + } + if (i == 7) + *cur++ = ' '; + } + + pos += i; + *cur++ = '\n'; + *cur = 0; + + text.append(hexbuf); + } + + ui->tePacketBytes->setLineWrapMode(QTextEdit::NoWrap); + ui->tePacketBytes->setPlainText(text); + break; + } + + case SHOW_HTML: + ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth); + ui->tePacketBytes->setHtml(field_bytes_); + break; + + case SHOW_IMAGE: + { + ui->lFind->setEnabled(false); + ui->leFind->setEnabled(false); + ui->bFind->setEnabled(false); + + ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth); + if (image_.loadFromData(field_bytes_)) { + ui->tePacketBytes->textCursor().insertImage(image_); + } + + print_button_->setEnabled(!image_.isNull()); + copy_button_->setEnabled(!image_.isNull()); + save_as_button_->setEnabled(!image_.isNull()); + break; + } + + case SHOW_JSON: + ui->tePacketBytes->setLineWrapMode(QTextEdit::NoWrap); + ui->tePacketBytes->setPlainText(QJsonDocument::fromJson(field_bytes_).toJson()); + break; + + case SHOW_YAML: + { + const int base64_raw_len = 57; // Encodes to 76 bytes, common in RFCs + int pos = 0, len = static_cast(field_bytes_.length()); + QString text("# Packet Bytes: !!binary |\n"); + + while (pos < len) { + QByteArray base64_data = field_bytes_.mid(pos, base64_raw_len); + pos += base64_data.length(); + /* XXX: GCC 12.1 has a bogus stringop-overread warning using the Qt + * conversions from QByteArray to QString at -O2 and higher due to + * computing a branch that will never be taken. + */ +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_OFF(stringop-overread) +#endif + text.append(" " + base64_data.toBase64() + "\n"); +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_ON(stringop-overread) +#endif + } + + ui->tePacketBytes->setLineWrapMode(QTextEdit::NoWrap); + ui->tePacketBytes->setPlainText(text); + break; + } + + case SHOW_RAW: + ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth); + ui->tePacketBytes->setPlainText(field_bytes_.toHex()); + break; + } +} + +void ShowPacketBytesDialog::captureFileClosing() +{ + finfo_ = NULL; // This will invalidate the source backend + + WiresharkDialog::captureFileClosing(); +} + +void ShowPacketBytesDialog::captureFileClosed() +{ + // We have lost the source backend and must disable all functions + // for manipulating decoding and displayed range. + + ui->tePacketBytes->setMenusEnabled(false); + ui->lDecodeAs->setEnabled(false); + ui->cbDecodeAs->setEnabled(false); + ui->lStart->setEnabled(false); + ui->sbStart->setEnabled(false); + ui->lEnd->setEnabled(false); + ui->sbEnd->setEnabled(false); + + WiresharkDialog::captureFileClosed(); +} + +void ShowPacketBytesTextEdit::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = createStandardContextMenu(); + QAction *action; + + menu->setAttribute(Qt::WA_DeleteOnClose); + menu->addSeparator(); + + action = menu->addAction(tr("Show Selected")); + action->setEnabled(menus_enabled_ && show_selected_enabled_ && textCursor().hasSelection()); + connect(action, SIGNAL(triggered()), this, SLOT(showSelected())); + + action = menu->addAction(tr("Show All")); + action->setEnabled(menus_enabled_); + connect(action, SIGNAL(triggered()), this, SLOT(showAll())); + + menu->popup(event->globalPos()); +} + +void ShowPacketBytesTextEdit::showSelected() +{ + QTextCursor cursor = textCursor(); + int start = cursor.selectionStart(); + int end = cursor.selectionEnd(); + + emit showSelected(start, end); +} + +void ShowPacketBytesTextEdit::showAll() +{ + emit showSelected(0, -1); +} diff --git a/ui/qt/show_packet_bytes_dialog.h b/ui/qt/show_packet_bytes_dialog.h new file mode 100644 index 00000000..80201c81 --- /dev/null +++ b/ui/qt/show_packet_bytes_dialog.h @@ -0,0 +1,118 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SHOW_PACKET_BYTES_DIALOG_H +#define SHOW_PACKET_BYTES_DIALOG_H + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "file.h" +#include "wireshark_dialog.h" + +#include +#include +#include +#include + +namespace Ui { +class ShowPacketBytesDialog; +class ShowPacketBytesTextEdit; +} + +class ShowPacketBytesDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit ShowPacketBytesDialog(QWidget &parent, CaptureFile &cf); + ~ShowPacketBytesDialog(); + + void addCodecs(const QMap &codecMap); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + void keyPressEvent(QKeyEvent *event); + void captureFileClosing(); + void captureFileClosed(); + +private slots: + void on_sbStart_valueChanged(int value); + void on_sbEnd_valueChanged(int value); + void on_cbDecodeAs_currentIndexChanged(int idx); + void on_cbShowAs_currentIndexChanged(int idx); + void on_leFind_returnPressed(); + void on_bFind_clicked(); + void on_buttonBox_rejected(); + + void showSelected(int start, int end); + void useRegexFind(bool use_regex); + void findText(bool go_back = true); + void helpButton(); + void printBytes(); + void copyBytes(); + void saveAs(); + +private: + void setStartAndEnd(int start, int end); + bool enableShowSelected(); + void updateWidgets(); // Needed for WiresharkDialog? + void updateHintLabel(); + void sanitizeBuffer(QByteArray &ba, bool handle_CR); + void symbolizeBuffer(QByteArray &ba); + QByteArray decodeQuotedPrintable(const guint8 *bytes, int length); + void rot13(QByteArray &ba); + void updateFieldBytes(bool initialization = false); + void updatePacketBytes(); + + Ui::ShowPacketBytesDialog *ui; + + const field_info *finfo_; + QByteArray field_bytes_; + QString hint_label_; + QPushButton *print_button_; + QPushButton *copy_button_; + QPushButton *save_as_button_; + bool use_regex_find_; + int start_; + int end_; + QImage image_; +}; + +class ShowPacketBytesTextEdit : public QTextEdit +{ + Q_OBJECT + +public: + explicit ShowPacketBytesTextEdit(QWidget *parent = 0) : + QTextEdit(parent), show_selected_enabled_(true), menus_enabled_(true) { } + ~ShowPacketBytesTextEdit() { } + + void setShowSelectedEnabled(bool enabled) { show_selected_enabled_ = enabled; } + void setMenusEnabled(bool enabled) { menus_enabled_ = enabled; } + +signals: + void showSelected(int, int); + +private slots: + void contextMenuEvent(QContextMenuEvent *event); + void showSelected(); + void showAll(); + +private: + bool show_selected_enabled_; + bool menus_enabled_; +}; + +#endif // SHOW_PACKET_BYTES_DIALOG_H diff --git a/ui/qt/show_packet_bytes_dialog.ui b/ui/qt/show_packet_bytes_dialog.ui new file mode 100644 index 00000000..f60e625d --- /dev/null +++ b/ui/qt/show_packet_bytes_dialog.ui @@ -0,0 +1,151 @@ + + + ShowPacketBytesDialog + + + + 0 + 0 + 710 + 620 + + + + + 0 + 0 + + + + Show Packet Bytes + + + true + + + + + + true + + + + + + + Hint. + + + true + + + Qt::TextSelectableByMouse + + + + + + + + + Decode as + + + + + + + + + + Show as + + + + + + + -1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Start + + + + + + + + + + End + + + + + + + + + + + + + + Find: + + + + + + + + + + Find &Next + + + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + + FindLineEdit + QLineEdit +
widgets/find_line_edit.h
+
+ + ShowPacketBytesTextEdit + QTextEdit +
show_packet_bytes_dialog.h
+
+
+ + +
diff --git a/ui/qt/simple_dialog.cpp b/ui/qt/simple_dialog.cpp new file mode 100644 index 00000000..b45c7a19 --- /dev/null +++ b/ui/qt/simple_dialog.cpp @@ -0,0 +1,450 @@ +/* simple_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "simple_dialog.h" + +#include "file.h" + +#include "epan/strutil.h" +#include "epan/prefs.h" + +#include "ui/commandline.h" + +#include +#include + +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include + +/* Simple dialog function - Displays a dialog box with the supplied message + * text. + * + * This is meant to be used as a backend for the functions defined in + * ui/simple_dialog.h. Qt code should use QMessageBox directly. + * + * Args: + * type : One of ESD_TYPE_*. + * btn_mask : The value passed in determines which buttons are displayed. + * msg_format : Sprintf-style format of the text displayed in the dialog. + * ... : Argument list for msg_format + */ + +QList message_queue_; +ESD_TYPE_E max_severity_ = ESD_TYPE_INFO; + +const char *primary_delimiter_ = "__CB754A38-94A2-4E59-922D-DD87EDC80E22__"; + +struct VisibleAsyncMessage +{ + QMessageBox *box; + int counter; + + VisibleAsyncMessage(QMessageBox *box) : box(box), counter(0) {} +}; + +static QList visible_messages; +static QMutex visible_messages_mutex; + +static void visible_message_finished(QMessageBox *box, int result _U_) +{ + visible_messages_mutex.lock(); + for (int i = 0; i < visible_messages.size(); i++) { + if (visible_messages[i].box == box) { + if (visible_messages[i].counter) { + ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_WARNING, "%d duplicates of \"%s\" were suppressed", + visible_messages[i].counter, box->text().toStdString().c_str()); + } + visible_messages.removeAt(i); + break; + } + } + visible_messages_mutex.unlock(); +} + +const char * +simple_dialog_primary_start(void) { + return primary_delimiter_; +} + +const char * +simple_dialog_primary_end(void) { + return primary_delimiter_; +} + +char * +simple_dialog_format_message(const char *msg) +{ + return g_strdup(msg); +} + +gpointer +simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...) +{ + va_list ap; + + va_start(ap, msg_format); + SimpleDialog sd(mainApp->mainWindow(), type, btn_mask, msg_format, ap); + va_end(ap); + + sd.exec(); + return NULL; +} + +gpointer +simple_dialog_async(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...) +{ + va_list ap; + + va_start(ap, msg_format); + SimpleDialog sd(mainApp->mainWindow(), type, btn_mask, msg_format, ap); + va_end(ap); + + sd.show(); + return NULL; +} + +/* + * Alert box, with optional "don't show this message again" variable + * and checkbox, and optional secondary text. + */ +void +simple_message_box(ESD_TYPE_E type, gboolean *notagain, + const char *secondary_msg, const char *msg_format, ...) +{ + if (notagain && *notagain) { + return; + } + + va_list ap; + + va_start(ap, msg_format); + SimpleDialog sd(mainApp->mainWindow(), type, ESD_BTN_OK, msg_format, ap); + va_end(ap); + + sd.setDetailedText(secondary_msg); + + QCheckBox *cb = NULL; + if (notagain) { + cb = new QCheckBox(); + cb->setChecked(true); + cb->setText(SimpleDialog::dontShowThisAgain()); + sd.setCheckBox(cb); + } + + sd.exec(); + + if (notagain && cb) { + *notagain = cb->isChecked(); + } +} + +/* + * Error alert box, taking a format and a va_list argument. + */ +void +vsimple_error_message_box(const char *msg_format, va_list ap) +{ +#ifdef HAVE_LIBPCAP + // We want to quit after reading the capture file, hence + // we don't actually open the error dialog. + if (global_commandline_info.quit_after_cap) + exit(0); +#endif + + SimpleDialog sd(mainApp->mainWindow(), ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap); + sd.show(); +} + +/* + * Warning alert box, taking a format and a va_list argument. + */ +void +vsimple_warning_message_box(const char *msg_format, va_list ap) +{ +#ifdef HAVE_LIBPCAP + // We want to quit after reading the capture file, hence + // we don't actually open the error dialog. + if (global_commandline_info.quit_after_cap) + exit(0); +#endif + + SimpleDialog sd(mainApp->mainWindow(), ESD_TYPE_WARN, ESD_BTN_OK, msg_format, ap); + sd.show(); +} + +/* + * Error alert box, taking a format and a list of arguments. + */ +void +simple_error_message_box(const char *msg_format, ...) +{ + va_list ap; + + va_start(ap, msg_format); + vsimple_error_message_box(msg_format, ap); + va_end(ap); +} + +SimpleDialog::SimpleDialog(QWidget *parent, ESD_TYPE_E type, int btn_mask, const char *msg_format, va_list ap) : + check_box_(0), + message_box_(0) +{ + gchar *vmessage; + QString message; + + vmessage = ws_strdup_vprintf(msg_format, ap); +#ifdef _WIN32 + // + // On Windows, filename strings inside Wireshark are UTF-8 strings, + // so error messages containing file names are UTF-8 strings. Convert + // from UTF-8, not from the local code page. + // + message = QString().fromUtf8(vmessage, -1); +#else + // + // On UN*X, who knows? Assume the locale's encoding. + // + message = QTextCodec::codecForLocale()->toUnicode(vmessage); +#endif + g_free(vmessage); + + MessagePair msg_pair = splitMessage(message); + // Remove leading and trailing whitespace along with excessive newline runs. + QString primary = msg_pair.first.trimmed(); + QString secondary = msg_pair.second.trimmed(); + secondary.replace(QRegularExpression("\n\n+"), "\n\n"); + + if (primary.isEmpty()) { + return; + } + + if (!parent || !mainApp->isInitialized() || mainApp->isReloadingLua()) { + message_queue_ << msg_pair; + if (type > max_severity_) { + max_severity_ = type; + } + return; + } + + message_box_ = new QMessageBox(parent); + message_box_->setTextFormat(Qt::PlainText); + message_box_->setTextInteractionFlags(Qt::TextSelectableByMouse); + + switch(type) { + case ESD_TYPE_ERROR: + message_box_->setIcon(QMessageBox::Critical); + break; + case ESD_TYPE_WARN: + message_box_->setIcon(QMessageBox::Warning); + break; + case ESD_TYPE_CONFIRMATION: + message_box_->setIcon(QMessageBox::Question); + break; + case ESD_TYPE_INFO: + default: + message_box_->setIcon(QMessageBox::Information); + break; + } + + if (btn_mask & ESD_BTN_OK) { + message_box_->addButton(QMessageBox::Ok); + } + if (btn_mask & ESD_BTN_CANCEL) { + message_box_->addButton(QMessageBox::Cancel); + } + if (btn_mask & ESD_BTN_YES) { + message_box_->addButton(QMessageBox::Yes); + } + if (btn_mask & ESD_BTN_NO) { + message_box_->addButton(QMessageBox::No); + } +// if (btn_mask & ESD_BTN_CLEAR) { +// addButton(QMessageBox::); +// } + if (btn_mask & ESD_BTN_SAVE) { + message_box_->addButton(QMessageBox::Save); + } + if (btn_mask & ESD_BTN_DONT_SAVE) { + message_box_->addButton(QMessageBox::Discard); + } +// if (btn_mask & ESD_BTN_QUIT_DONT_SAVE) { +// addButton(QMessageBox::); +// } + + + message_box_->setText(primary); + message_box_->setInformativeText(secondary); +} + +SimpleDialog::~SimpleDialog() +{ +} + +void SimpleDialog::displayQueuedMessages(QWidget *parent) +{ + if (message_queue_.isEmpty()) { + return; + } + + QMessageBox mb(parent ? parent : mainApp->mainWindow()); + + switch(max_severity_) { + case ESD_TYPE_ERROR: + mb.setIcon(QMessageBox::Critical); + break; + case ESD_TYPE_WARN: + mb.setIcon(QMessageBox::Warning); + break; + case ESD_TYPE_CONFIRMATION: + mb.setIcon(QMessageBox::Question); + break; + case ESD_TYPE_INFO: + default: + mb.setIcon(QMessageBox::Information); + break; + } + + mb.addButton(QMessageBox::Ok); + + if (message_queue_.length() > 1) { + QStringList msg_details; + QString first_primary = message_queue_[0].first; + first_primary.append(UTF8_HORIZONTAL_ELLIPSIS); + + mb.setText(QObject::tr("Multiple problems found")); + mb.setInformativeText(first_primary); + + foreach (MessagePair msg_pair, message_queue_) { + msg_details << msg_pair.first; + if (!msg_pair.second.isEmpty()) { + msg_details.append(msg_pair.second); + } + } + mb.setDetailedText(msg_details.join("\n\n")); + } else { + mb.setText(message_queue_[0].first); + mb.setInformativeText(message_queue_[0].second); + } + + message_queue_.clear(); + max_severity_ = ESD_TYPE_INFO; + + mb.exec(); +} + +QString SimpleDialog::dontShowThisAgain() +{ + return QObject::tr("Don't show this message again."); +} + +int SimpleDialog::exec() +{ + if (!message_box_) { + return 0; + } + + message_box_->setDetailedText(detailed_text_); + message_box_->setCheckBox(check_box_); + + int status = message_box_->exec(); + delete message_box_; + message_box_ = 0; + detailed_text_ = QString(); + + switch (status) { + case QMessageBox::Ok: + return ESD_BTN_OK; + case QMessageBox::Yes: + return ESD_BTN_YES; + case QMessageBox::No: + return ESD_BTN_NO; + case QMessageBox::Save: + return ESD_BTN_SAVE; + case QMessageBox::Discard: + return ESD_BTN_DONT_SAVE; + case QMessageBox::Cancel: // XXX Should OK be the default? + default: + return ESD_BTN_CANCEL; + } +} + +void SimpleDialog::show() +{ + if (!message_box_) { + return; + } + + message_box_->setDetailedText(detailed_text_); + message_box_->setCheckBox(check_box_); + + visible_messages_mutex.lock(); + bool found = false; + for (int i = 0; i < visible_messages.size(); i++) { + VisibleAsyncMessage &msg = visible_messages[i]; + if ((msg.box->icon() == message_box_->icon()) && + (msg.box->checkBox() == message_box_->checkBox()) && + (msg.box->text() == message_box_->text()) && + (msg.box->informativeText() == message_box_->informativeText()) && + (msg.box->detailedText() == message_box_->detailedText())) + { + /* Message box of same type with same text is already visible. */ + msg.counter++; + found = true; + break; + } + } + if (!found) { + visible_messages.append(VisibleAsyncMessage(message_box_)); + } + visible_messages_mutex.unlock(); + + if (found) + { + delete message_box_; + } + else + { + QObject::connect(message_box_, &QMessageBox::finished, + std::bind(visible_message_finished,message_box_,std::placeholders::_1)); + message_box_->setModal(Qt::WindowModal); + message_box_->setAttribute(Qt::WA_DeleteOnClose); + message_box_->show(); + } + + /* Message box was shown and will be deleted once user closes it */ + message_box_ = 0; +} + +const MessagePair SimpleDialog::splitMessage(QString &message) const +{ + if (message.startsWith(primary_delimiter_)) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList parts = message.split(primary_delimiter_, Qt::SkipEmptyParts); +#else + QStringList parts = message.split(primary_delimiter_, QString::SkipEmptyParts); +#endif + switch (parts.length()) { + case 0: + return MessagePair(QString(), QString()); + case 1: + return MessagePair(parts[0], QString()); + default: + QString first = parts.takeFirst(); + return MessagePair(first, parts.join(" ")); + } + } + return MessagePair(message, QString()); +} diff --git a/ui/qt/simple_dialog.h b/ui/qt/simple_dialog.h new file mode 100644 index 00000000..2afda98d --- /dev/null +++ b/ui/qt/simple_dialog.h @@ -0,0 +1,51 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SIMPLE_DIALOG_H +#define SIMPLE_DIALOG_H + +#include + +#include + +#include + +#include "ui/simple_dialog.h" + +#include +#include + +typedef QPair MessagePair; + +class QCheckBox; +class QMessageBox; +class QWidget; + +// This might be constructed before Qt is initialized and must be a plain, non-Qt object. +class SimpleDialog +{ +public: + explicit SimpleDialog(QWidget *parent, ESD_TYPE_E type, int btn_mask, const char *msg_format, va_list ap); + ~SimpleDialog(); + + static void displayQueuedMessages(QWidget *parent = 0); + static QString dontShowThisAgain(); + void setDetailedText(QString text) { detailed_text_ = text; } + void setCheckBox(QCheckBox *cb) { check_box_ = cb; } + int exec(); + void show(); + +private: + const MessagePair splitMessage(QString &message) const; + QString detailed_text_; + QCheckBox *check_box_; + QMessageBox *message_box_; +}; + +#endif // SIMPLE_DIALOG_H diff --git a/ui/qt/simple_statistics_dialog.cpp b/ui/qt/simple_statistics_dialog.cpp new file mode 100644 index 00000000..ae715603 --- /dev/null +++ b/ui/qt/simple_statistics_dialog.cpp @@ -0,0 +1,308 @@ +/* simple_statistics_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "simple_statistics_dialog.h" + +#include "file.h" + +#include "epan/stat_tap_ui.h" + +#include + +#include "main_application.h" + +// To do: +// - Hide rows with zero counts. + +static QHash cfg_str_to_stu_; + +extern "C" { +static void +simple_stat_init(const char *args, void*) { + QStringList args_l = QString(args).split(','); + if (args_l.length() > 1) { + QString simple_stat = QString("%1,%2").arg(args_l[0]).arg(args_l[1]); + QString filter; + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(","); + } + mainApp->emitTapParameterSignal(simple_stat, filter, NULL); + } +} +} + +bool register_simple_stat_tables(const void *key, void *value, void*) { + stat_tap_table_ui *stu = (stat_tap_table_ui*)value; + + cfg_str_to_stu_[stu->cli_string] = stu; + TapParameterDialog::registerDialog( + stu->title, + (const char*)key, + stu->group, + simple_stat_init, + SimpleStatisticsDialog::createSimpleStatisticsDialog); + return FALSE; +} + +enum { + simple_row_type_ = 1000 +}; + +class SimpleStatisticsTreeWidgetItem : public QTreeWidgetItem +{ +public: + SimpleStatisticsTreeWidgetItem(QTreeWidgetItem *parent, int num_fields, const stat_tap_table_item_type *fields) : + QTreeWidgetItem (parent, simple_row_type_), + num_fields_(num_fields), + fields_(fields) + { + } + void draw() { + for (int i = 0; i < num_fields_ && i < treeWidget()->columnCount(); i++) { + switch (fields_[i].type) { + case TABLE_ITEM_UINT: + setText(i, QString::number(fields_[i].value.uint_value)); + break; + case TABLE_ITEM_INT: + setText(i, QString::number(fields_[i].value.int_value)); + break; + case TABLE_ITEM_STRING: + setText(i, fields_[i].value.string_value); + break; + case TABLE_ITEM_FLOAT: + setText(i, QString::number(fields_[i].value.float_value, 'f', 6)); + break; + case TABLE_ITEM_ENUM: + setText(i, QString::number(fields_[i].value.enum_value)); + break; + default: + break; + } + } + } + bool operator< (const QTreeWidgetItem &other) const + { + int col = treeWidget()->sortColumn(); + if (other.type() != simple_row_type_ || col >= num_fields_) { + return QTreeWidgetItem::operator< (other); + } + const SimpleStatisticsTreeWidgetItem *other_row = static_cast(&other); + switch (fields_[col].type) { + case TABLE_ITEM_UINT: + return fields_[col].value.uint_value < other_row->fields_[col].value.uint_value; + case TABLE_ITEM_INT: + return fields_[col].value.int_value < other_row->fields_[col].value.int_value; + case TABLE_ITEM_STRING: + return g_strcmp0(fields_[col].value.string_value, other_row->fields_[col].value.string_value) < 0; + case TABLE_ITEM_FLOAT: + return fields_[col].value.float_value < other_row->fields_[col].value.float_value; + case TABLE_ITEM_ENUM: + return fields_[col].value.enum_value < other_row->fields_[col].value.enum_value; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList rowData() { + QList row_data; + + for (int i = 0; i < num_fields_ && i < columnCount(); i++) { + switch (fields_[i].type) { + case TABLE_ITEM_UINT: + row_data << fields_[i].value.uint_value; + break; + case TABLE_ITEM_INT: + row_data << fields_[i].value.int_value; + break; + case TABLE_ITEM_STRING: + row_data << fields_[i].value.string_value; + break; + case TABLE_ITEM_FLOAT: + row_data << fields_[i].value.float_value; + break; + case TABLE_ITEM_ENUM: + row_data << fields_[i].value.enum_value; + break; + default: + break; + } + } + + return row_data; + } + +private: + const int num_fields_; + const stat_tap_table_item_type *fields_; +}; + +SimpleStatisticsDialog::SimpleStatisticsDialog(QWidget &parent, CaptureFile &cf, struct _stat_tap_table_ui *stu, const QString filter, int help_topic) : + TapParameterDialog(parent, cf, help_topic), + stu_(stu) +{ + stu->refcount++; + setWindowSubtitle(stu_->title); + loadGeometry(0, 0, stu_->title); + + QStringList header_labels; + for (int col = 0; col < (int) stu_->nfields; col++) { + header_labels << stu_->fields[col].column_name; + } + statsTreeWidget()->setHeaderLabels(header_labels); + + for (int col = 0; col < (int) stu_->nfields; col++) { + if (stu_->fields[col].align == TAP_ALIGN_RIGHT) { + statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); + } + } + + setDisplayFilter(filter); +} + +TapParameterDialog *SimpleStatisticsDialog::createSimpleStatisticsDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf) +{ + if (!cfg_str_to_stu_.contains(cfg_str)) { + // XXX MessageBox? + return NULL; + } + + stat_tap_table_ui *stu = cfg_str_to_stu_[cfg_str]; + + return new SimpleStatisticsDialog(parent, cf, stu, filter); +} + +void SimpleStatisticsDialog::addMissingRows(struct _stat_data_t *stat_data) +{ + // Hierarchy: + // - tables (GTK+ UI only supports one currently) + // - elements (rows?) + // - fields (columns?) + // For multiple table support we might want to add them as subtrees, with + // the top-level tree item text set to the column labels for that table. + + // Add any missing tables and rows. + for (guint table_idx = 0; table_idx < stat_data->stat_tap_data->tables->len; table_idx++) { + stat_tap_table* st_table = g_array_index(stat_data->stat_tap_data->tables, stat_tap_table*, table_idx); + QTreeWidgetItem *ti = NULL; + + if ((int) table_idx >= statsTreeWidget()->topLevelItemCount()) { + ti = new QTreeWidgetItem(statsTreeWidget()); + ti->setText(0, st_table->title); + ti->setFirstColumnSpanned(true); + ti->setExpanded(true); + } else { + ti = statsTreeWidget()->topLevelItem(table_idx); + } + for (guint element = ti->childCount(); element < st_table->num_elements; element++) { + stat_tap_table_item_type* fields = stat_tap_get_field_data(st_table, element, 0); + if (stu_->nfields > 0) { + SimpleStatisticsTreeWidgetItem *ss_ti = new SimpleStatisticsTreeWidgetItem(ti, st_table->num_fields, fields); + for (int col = 0; col < (int) stu_->nfields; col++) { + if (stu_->fields[col].align == TAP_ALIGN_RIGHT) { + ss_ti->setTextAlignment(col, Qt::AlignRight); + } + } + } + } + } +} + +void SimpleStatisticsDialog::tapReset(void *sd_ptr) +{ + stat_data_t *sd = (stat_data_t*) sd_ptr; + SimpleStatisticsDialog *ss_dlg = static_cast(sd->user_data); + if (!ss_dlg) return; + + reset_stat_table(sd->stat_tap_data); + ss_dlg->statsTreeWidget()->clear(); +} + +void SimpleStatisticsDialog::tapDraw(void *sd_ptr) +{ + stat_data_t *sd = (stat_data_t*) sd_ptr; + SimpleStatisticsDialog *ss_dlg = static_cast(sd->user_data); + if (!ss_dlg) return; + + ss_dlg->addMissingRows(sd); + + QTreeWidgetItemIterator it(ss_dlg->statsTreeWidget()); + while (*it) { + if ((*it)->type() == simple_row_type_) { + SimpleStatisticsTreeWidgetItem *ss_ti = static_cast((*it)); + ss_ti->draw(); + } + ++it; + } + + for (int i = 0; i < ss_dlg->statsTreeWidget()->columnCount() - 1; i++) { + ss_dlg->statsTreeWidget()->resizeColumnToContents(i); + } +} + +void SimpleStatisticsDialog::fillTree() +{ + stat_data_t stat_data; + stat_data.stat_tap_data = stu_; + stat_data.user_data = this; + + stu_->stat_tap_init_cb(stu_); + + QString display_filter = displayFilter(); + if (!registerTapListener(stu_->tap_name, + &stat_data, + display_filter.toUtf8().constData(), + 0, + tapReset, + stu_->packet_func, + tapDraw)) { + free_stat_tables(stu_); + reject(); // XXX Stay open instead? + return; + } + + statsTreeWidget()->setSortingEnabled(false); + + cap_file_.retapPackets(); + + // We only have one table. Move its tree items up one level. + if (statsTreeWidget()->invisibleRootItem()->childCount() == 1) { + statsTreeWidget()->setRootIndex(statsTreeWidget()->model()->index(0, 0)); + } + + tapDraw(&stat_data); + + statsTreeWidget()->sortItems(0, Qt::AscendingOrder); + statsTreeWidget()->setSortingEnabled(true); + + removeTapListeners(); +} + +// This is how an item is represented for exporting. +QList SimpleStatisticsDialog::treeItemData(QTreeWidgetItem *it) const +{ + // Cast up to our type. + SimpleStatisticsTreeWidgetItem *rit = dynamic_cast(it); + if (rit) { + return rit->rowData(); + } + else { + return QList(); + } +} + + +SimpleStatisticsDialog::~SimpleStatisticsDialog() +{ + stu_->refcount--; + if (stu_->refcount == 0) { + if (stu_->tables) + free_stat_tables(stu_); + } +} diff --git a/ui/qt/simple_statistics_dialog.h b/ui/qt/simple_statistics_dialog.h new file mode 100644 index 00000000..3f898308 --- /dev/null +++ b/ui/qt/simple_statistics_dialog.h @@ -0,0 +1,57 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __SIMPLE_STATISTICS_DIALOG_H__ +#define __SIMPLE_STATISTICS_DIALOG_H__ + +#include "tap_parameter_dialog.h" + +struct _stat_data_t; + +class SimpleStatisticsDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + SimpleStatisticsDialog(QWidget &parent, CaptureFile &cf, struct _stat_tap_table_ui *stu, const QString filter, int help_topic = 0); + static TapParameterDialog *createSimpleStatisticsDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf); + +protected: + /** Add a simple statistics table. + * + * @param stat_data The table to add. + */ + // gtk:service_response_table.h:init_srt_table + void addMissingRows(struct _stat_data_t *stat_data); + +private: + struct _stat_tap_table_ui *stu_; + + // Callbacks for register_tap_listener + static void tapReset(void *sd_ptr); + static void tapDraw(void *sd_ptr); + + // How each item (SimpleStatisticsTreeWidgetItem) will be exported + virtual QList treeItemData(QTreeWidgetItem *) const; + + ~SimpleStatisticsDialog(); + +private slots: + virtual void fillTree(); + +}; + +/** Register function to register dissectors that support a "simple" statistics table. + * + * @param key is tap string + * @param value stat_tap_table_ui* representing dissetor stat table + */ +bool register_simple_stat_tables(const void *key, void *value, void*); + +#endif // __SIMPLE_STATISTICS_DIALOG_H__ diff --git a/ui/qt/stats_tree_dialog.cpp b/ui/qt/stats_tree_dialog.cpp new file mode 100644 index 00000000..1ec66b7e --- /dev/null +++ b/ui/qt/stats_tree_dialog.cpp @@ -0,0 +1,208 @@ +/* stats_tree_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "stats_tree_dialog.h" + +#include "file.h" + +#include "epan/stats_tree_priv.h" + +#include + +#include + +#include +#include +#include +#include + +const int item_col_ = 0; + +const int sn_type_ = 1000; +class StatsTreeWidgetItem : public QTreeWidgetItem +{ +public: + StatsTreeWidgetItem(int type = sn_type_) : QTreeWidgetItem (type) + { + for (int col = 1; col < columnCount(); col++) { + setTextAlignment(col, Qt::AlignRight); + } + } + bool operator< (const QTreeWidgetItem &other) const + { + stat_node *thisnode = VariantPointer::asPtr(data(item_col_, Qt::UserRole)); + stat_node *othernode = VariantPointer::asPtr(other.data(item_col_, Qt::UserRole)); + Qt::SortOrder order = treeWidget()->header()->sortIndicatorOrder(); + int result; + + result = stats_tree_sort_compare(thisnode, othernode, treeWidget()->sortColumn(), + order==Qt::DescendingOrder); + if (order==Qt::DescendingOrder) { + result = -result; + } + return result < 0; + } +}; + +StatsTreeDialog::StatsTreeDialog(QWidget &parent, CaptureFile &cf, const char *cfg_abbr) : + TapParameterDialog(parent, cf), + st_(NULL), + st_cfg_(NULL) +{ + loadGeometry(800, height(), cfg_abbr); + st_cfg_ = stats_tree_get_cfg_by_abbr(cfg_abbr); + memset(&cfg_pr_, 0, sizeof(struct _tree_cfg_pres)); + + addTreeCollapseAllActions(); + + if (!st_cfg_) { + QMessageBox::critical(this, tr("Configuration not found"), + tr("Unable to find configuration for %1.").arg(cfg_abbr)); + QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); + } +} + +StatsTreeDialog::~StatsTreeDialog() +{ + if (st_) { + stats_tree_free(st_); + } +} + +// Adds a node to the QTreeWidget +// Note: We're passing QTreeWidgetItem pointers as st_node_pres pointers +void StatsTreeDialog::setupNode(stat_node* node) +{ + if (!node || !node->st || !node->st->cfg || !node->st->cfg->pr + || !node->st->cfg->pr->st_dlg) return; + StatsTreeDialog *st_dlg = node->st->cfg->pr->st_dlg; + + QTreeWidgetItem *ti = new StatsTreeWidgetItem(), *parent = NULL; + + ti->setText(item_col_, node->name); + ti->setData(item_col_, Qt::UserRole, VariantPointer::asQVariant(node)); + node->pr = (st_node_pres *) ti; + if (node->parent && node->parent->pr) { + parent = (QTreeWidgetItem *) node->parent->pr; + parent->setExpanded(true); + } + if (parent) { + parent->addChild(ti); + } else { + st_dlg->statsTreeWidget()->addTopLevelItem(ti); + } + st_dlg->statsTreeWidget()->resizeColumnToContents(item_col_); +} + +void StatsTreeDialog::fillTree() +{ + if (!st_cfg_ || file_closed_) return; + + QString display_name = gchar_free_to_qstring(stats_tree_get_displayname(st_cfg_->name)); + + // The GTK+ UI appends "Stats Tree" to the window title. If we do the same + // here we should expand the name completely, e.g. to "Statistics Tree". + setWindowSubtitle(display_name); + + st_cfg_->pr = &cfg_pr_; + cfg_pr_.st_dlg = this; + + if (st_) { + stats_tree_free(st_); + } + QString display_filter = displayFilter(); + st_ = stats_tree_new(st_cfg_, NULL, display_filter.toUtf8().constData()); + + // Add number of columns for this stats_tree + QStringList header_labels; + for (int count = 0; countnum_columns; count++) { + header_labels.push_back(stats_tree_get_column_name(count)); + } + statsTreeWidget()->setColumnCount(static_cast(header_labels.count())); + statsTreeWidget()->setHeaderLabels(header_labels); + statsTreeWidget()->setSortingEnabled(false); + + if (!registerTapListener(st_cfg_->tapname, + st_, + st_->filter, + st_cfg_->flags, + resetTap, + stats_tree_packet, + drawTreeItems)) { + reject(); // XXX Stay open instead? + return; + } + + cap_file_.retapPackets(); + drawTreeItems(st_); + + statsTreeWidget()->setSortingEnabled(true); + removeTapListeners(); + + st_cfg_->pr = NULL; +} + +void StatsTreeDialog::resetTap(void *st_ptr) +{ + stats_tree *st = (stats_tree *) st_ptr; + if (!st || !st->cfg || !st->cfg->pr || !st->cfg->pr->st_dlg) return; + + st->cfg->pr->st_dlg->statsTreeWidget()->clear(); + st->cfg->init(st); +} + +void StatsTreeDialog::drawTreeItems(void *st_ptr) +{ + stats_tree *st = (stats_tree *) st_ptr; + if (!st || !st->cfg || !st->cfg->pr || !st->cfg->pr->st_dlg) return; + TapParameterDialog *st_dlg = st->cfg->pr->st_dlg; + QTreeWidgetItemIterator iter(st_dlg->statsTreeWidget()); + + while (*iter) { + stat_node *node = VariantPointer::asPtr((*iter)->data(item_col_, Qt::UserRole)); + if (node) { + gchar **valstrs = stats_tree_get_values_from_node(node); + for (int count = 0; countnum_columns; count++) { + (*iter)->setText(count,valstrs[count]); + g_free(valstrs[count]); + } + (*iter)->setExpanded((node->parent==(&st->root)) && + (!(node->st_flags&ST_FLG_DEF_NOEXPAND))); + g_free(valstrs); + } + ++iter; + } + + st_dlg->drawTreeItems(); +} + +QByteArray StatsTreeDialog::getTreeAsString(st_format_type format) +{ + GString *str_tree; + + // produce output in selected format using current sort information + str_tree = stats_tree_format_as_str(st_, format, statsTreeWidget()->sortColumn(), + statsTreeWidget()->header()->sortIndicatorOrder()==Qt::DescendingOrder); + + return gstring_free_to_qbytearray(str_tree); +} + +extern "C" { + +void register_tap_listener_qt_stats_tree_stat(void); + +void +register_tap_listener_qt_stats_tree_stat(void) +{ + stats_tree_presentation(NULL, + StatsTreeDialog::setupNode, + NULL, NULL); +} + +} diff --git a/ui/qt/stats_tree_dialog.h b/ui/qt/stats_tree_dialog.h new file mode 100644 index 00000000..de040811 --- /dev/null +++ b/ui/qt/stats_tree_dialog.h @@ -0,0 +1,47 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef STATS_TREE_DIALOG_H +#define STATS_TREE_DIALOG_H + +#include "tap_parameter_dialog.h" + +#include + +#include + +#include "epan/stats_tree_priv.h" + +struct _tree_cfg_pres { + class StatsTreeDialog* st_dlg; +}; + +class StatsTreeDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + explicit StatsTreeDialog(QWidget &parent, CaptureFile &cf, const char *cfg_abbr); + ~StatsTreeDialog(); + static void setupNode(stat_node* node); + +private: + struct _tree_cfg_pres cfg_pr_; + stats_tree *st_; + stats_tree_cfg *st_cfg_; + + static void resetTap(void *st_ptr); + static void drawTreeItems(void *st_ptr); + virtual QByteArray getTreeAsString(st_format_type format); + +private slots: + virtual void fillTree(); +}; + +#endif // STATS_TREE_DIALOG_H diff --git a/ui/qt/strip_headers_dialog.cpp b/ui/qt/strip_headers_dialog.cpp new file mode 100644 index 00000000..73775ddf --- /dev/null +++ b/ui/qt/strip_headers_dialog.cpp @@ -0,0 +1,47 @@ +/* strip_headers_dialog.cpp + * Dialog for stripping lower level protocols and outputting protocols + * with a native encapsulation to file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "strip_headers_dialog.h" +#include + +#include +#include + +#include "ui/export_pdu_ui_utils.h" +#include "ui/capture_globals.h" + +StripHeadersDialog::StripHeadersDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::StripHeadersDialog) +{ + GSList *tap_name_list; + + ui->setupUi(this); + + for (tap_name_list = get_export_pdu_tap_list(); tap_name_list; tap_name_list = g_slist_next(tap_name_list)) { + if (export_pdu_tap_get_encap((const char*)tap_name_list->data) != WTAP_ENCAP_WIRESHARK_UPPER_PDU) { + ui->comboBox->addItem((const char*)(tap_name_list->data)); + } + } +} +void StripHeadersDialog::on_buttonBox_accepted() +{ + const QByteArray& filter = ui->displayFilterLineEdit->text().toUtf8(); + const QByteArray& tap_name = ui->comboBox->currentText().toUtf8(); + + do_export_pdu(filter.constData(), global_capture_opts.temp_dir, tap_name.constData()); +} +StripHeadersDialog::~StripHeadersDialog() +{ + delete ui; +} diff --git a/ui/qt/strip_headers_dialog.h b/ui/qt/strip_headers_dialog.h new file mode 100644 index 00000000..bce54784 --- /dev/null +++ b/ui/qt/strip_headers_dialog.h @@ -0,0 +1,35 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef STRIP_HEADERS_DIALOG_H +#define STRIP_HEADERS_DIALOG_H + +#include +#include + +namespace Ui { +class StripHeadersDialog; +} + +class StripHeadersDialog : public QDialog +{ + Q_OBJECT + +public: + explicit StripHeadersDialog(QWidget *parent = 0); + ~StripHeadersDialog(); + +private: + Ui::StripHeadersDialog *ui; + +private slots: + void on_buttonBox_accepted(); +}; + +#endif // STRIP_HEADERS_DIALOG_H diff --git a/ui/qt/strip_headers_dialog.ui b/ui/qt/strip_headers_dialog.ui new file mode 100644 index 00000000..cc40162a --- /dev/null +++ b/ui/qt/strip_headers_dialog.ui @@ -0,0 +1,106 @@ + + + StripHeadersDialog + + + + 0 + 0 + 393 + 158 + + + + Dialog + + + + + 30 + 100 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 16 + 20 + 361 + 29 + + + + + + + Display filter: + + + + + + + + + + + + 10 + 60 + 120 + 30 + + + + + + + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + + buttonBox + accepted() + StripHeadersDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + StripHeadersDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/supported_protocols_dialog.cpp b/ui/qt/supported_protocols_dialog.cpp new file mode 100644 index 00000000..eea2d738 --- /dev/null +++ b/ui/qt/supported_protocols_dialog.cpp @@ -0,0 +1,98 @@ +/* supported_protocols_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "supported_protocols_dialog.h" +#include +#include + +#include + +#include "main_application.h" + +SupportedProtocolsDialog::SupportedProtocolsDialog(QWidget *parent) : + GeometryStateDialog(parent), + ui(new Ui::SupportedProtocolsDialog), + supported_protocols_model_(new SupportedProtocolsModel()), + proxyModel_(new SupportedProtocolsProxyModel(this)) +{ + ui->setupUi(this); + + proxyModel_->setSourceModel(supported_protocols_model_); + ui->supportedProtocolsTreeView->setModel(proxyModel_); + + //always sort by protocol/field name + proxyModel_->sort(SupportedProtocolsModel::colName); + + if (parent) + loadGeometry(parent->width() * 3 / 4, parent->height()); + setAttribute(Qt::WA_DeleteOnClose, true); + + setWindowTitle(mainApp->windowTitleString(tr("Supported Protocols"))); + + // Some of our names are unreasonably long. + int one_em = fontMetrics().height(); + ui->supportedProtocolsTreeView->setColumnWidth(SupportedProtocolsModel::colName, one_em * 15); + ui->supportedProtocolsTreeView->setColumnWidth(SupportedProtocolsModel::colFilter, one_em * 10); + ui->supportedProtocolsTreeView->setColumnWidth(SupportedProtocolsModel::colType, one_em * 12); + ui->supportedProtocolsTreeView->setColumnWidth(SupportedProtocolsModel::colDescription, one_em * 30); + + QTimer::singleShot(0, this, SLOT(fillTree())); + + /* Create a single-shot timer for debouncing calls to + * updateSearchLineEdit() */ + searchLineEditTimer = new QTimer(this); + searchLineEditTimer->setSingleShot(true); + connect(searchLineEditTimer, &QTimer::timeout, this, &SupportedProtocolsDialog::updateSearchLineEdit); +} + +SupportedProtocolsDialog::~SupportedProtocolsDialog() +{ + delete searchLineEditTimer; + delete ui; + delete supported_protocols_model_; + delete proxyModel_; +} + +void SupportedProtocolsDialog::updateStatistics() +{ + QLocale locale = QLocale::system(); + QString hint = tr("%1 protocols, %2 fields.") + .arg(locale.toString(supported_protocols_model_->rowCount())) + .arg(locale.toString(supported_protocols_model_->fieldCount())); + ui->hintLabel->setText(hint); +} + +void SupportedProtocolsDialog::fillTree() +{ + supported_protocols_model_->populate(); + updateStatistics(); +} + +void SupportedProtocolsDialog::updateSearchLineEdit() +{ + proxyModel_->setFilter(searchLineEditText); +} + +void SupportedProtocolsDialog::on_searchLineEdit_textChanged(const QString &search_re) +{ + /* As filtering the list of protocols takes a noticeable amount + * of time and so would introduce significant lag while typing a string + * into the Search box, we instead debounce the call to + * proxyModel_->setFilter(), so that it doesn't run until a set amount of + * time has elapsed with no updates to the Search field. + * + * If the user types something before the timer elapses, the timer restarts + * the countdown. + */ + searchLineEditText = search_re; + guint gui_debounce_timer = prefs_get_uint_value("gui", "debounce.timer"); + searchLineEditTimer->start(gui_debounce_timer); +} diff --git a/ui/qt/supported_protocols_dialog.h b/ui/qt/supported_protocols_dialog.h new file mode 100644 index 00000000..89afd3fa --- /dev/null +++ b/ui/qt/supported_protocols_dialog.h @@ -0,0 +1,51 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SUPPORTED_PROTOCOLS_DIALOG_H +#define SUPPORTED_PROTOCOLS_DIALOG_H + +#include "geometry_state_dialog.h" +#include + +namespace Ui { +class SupportedProtocolsDialog; +} + +class SupportedProtocolsDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit SupportedProtocolsDialog(QWidget *parent = 0); + ~SupportedProtocolsDialog(); + +private: + Ui::SupportedProtocolsDialog *ui; + + SupportedProtocolsModel* supported_protocols_model_; + SupportedProtocolsProxyModel* proxyModel_; + QTimer *searchLineEditTimer; + QString searchLineEditText; + + void updateStatistics(); + +private slots: + void fillTree(); + + /** + * Update search results from the searchLineEdit field + * + * This is performed separately from on_searchLineEdit_textChanged + * to support debouncing. + */ + void updateSearchLineEdit(); + void on_searchLineEdit_textChanged(const QString &search_re); +}; + +#endif // SUPPORTED_PROTOCOLS_DIALOG_H diff --git a/ui/qt/supported_protocols_dialog.ui b/ui/qt/supported_protocols_dialog.ui new file mode 100644 index 00000000..deb34ef7 --- /dev/null +++ b/ui/qt/supported_protocols_dialog.ui @@ -0,0 +1,108 @@ + + + SupportedProtocolsDialog + + + + 0 + 0 + 640 + 540 + + + + Dialog + + + + + + + + + + + + <html><head/><body><p>Search the list of field names.</p></body></html> + + + Search: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <small><i>Gathering protocol information…</i></small> + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + SupportedProtocolsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SupportedProtocolsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/tabnav_tree_widget.cpp b/ui/qt/tabnav_tree_widget.cpp new file mode 100644 index 00000000..34c31210 --- /dev/null +++ b/ui/qt/tabnav_tree_widget.cpp @@ -0,0 +1,44 @@ +/* tabnav_tree_widget.cpp + * Tree widget with saner tab navigation properties. + * + * Copyright 2017 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tabnav_tree_widget.h" + +// Copy of TabnavTreeView, modified to use QTreeWidget instead of QTreeView. + +TabnavTreeWidget::TabnavTreeWidget(QWidget *parent) : QTreeWidget(parent) +{ +} + +// Note: if a QTableWidget is used, then this is not needed anymore since Tab +// works as "expected" (move to next cell instead of row). +// Note 2: this does not help with fields with no widget (like filename). +QModelIndex TabnavTreeWidget::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) +{ + QModelIndex current = currentIndex(); + // If an item is currently selected, interpret Next/Previous. Otherwise, + // fallback to the default selection (e.g. first row for Next). + if (current.isValid()) { + if (cursorAction == MoveNext) { + if (current.column() < model()->columnCount()) { + return current.sibling(current.row(), current.column() + 1); + } + return current; + } else if (cursorAction == MovePrevious) { + if (current.column() > 0) { + return current.sibling(current.row(), current.column() - 1); + } + return current; + } + } + + return QTreeView::moveCursor(cursorAction, modifiers); +} diff --git a/ui/qt/tabnav_tree_widget.h b/ui/qt/tabnav_tree_widget.h new file mode 100644 index 00000000..e8c60d39 --- /dev/null +++ b/ui/qt/tabnav_tree_widget.h @@ -0,0 +1,30 @@ +/** @file + * + * Tree widget with saner tab navigation properties. + * + * Copyright 2017 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TABNAV_TREE_WIDGET_H +#define TABNAV_TREE_WIDGET_H + +#include +#include + +/** + * Like QTreeWidget, but instead of changing to the next row (same column) when + * pressing Tab while editing, change to the next column (same row). + */ +class TabnavTreeWidget : public QTreeWidget +{ +public: + TabnavTreeWidget(QWidget *parent = 0); + QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); +}; +#endif // TABNAV_TREE_WIDGET_H diff --git a/ui/qt/tap_parameter_dialog.cpp b/ui/qt/tap_parameter_dialog.cpp new file mode 100644 index 00000000..4652b672 --- /dev/null +++ b/ui/qt/tap_parameter_dialog.cpp @@ -0,0 +1,613 @@ +/* tap_parameter_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * @file Tap parameter dialog class + * + * Base class for statistics dialogs. Subclasses must implement: + * - fillTree. Called when the dialog is first displayed and when a display + * filter is applied. In most cases the subclass should clear the tree and + * retap packets here. + * - filterExpression. If the subclass supports filtering context menu items + * ("Apply As Filter", etc.) it should fill in ctx_menu_ and implement + * filterExpression. + * - getTreeAsString or treeItemData. Used for "Copy" and "Save As...". + * - + */ + +#include "tap_parameter_dialog.h" +#include + +#include + +#include "epan/stat_tap_ui.h" + +#ifdef Q_OS_WIN +#include +#include "ui/packet_range.h" +#include "ui/win32/file_dlg_win32.h" +#endif // Q_OS_WIN + +#include "ui/util.h" +#include + +#include "wsutil/file_util.h" + +#include "progress_frame.h" +#include +#include "main_application.h" + +#include +#include +#include +#include + +// The GTK+ counterpart uses tap_param_dlg, which we don't use. If we +// need tap parameters we should probably create a TapParameterDialog +// class based on WiresharkDialog and subclass it here. + +// To do: +// - Add tap parameters? SCSI SRT uses PARAM_ENUM. Everything appears to use +// PARAM_FILTER. Nothing uses _UINT, _STRING, or _UUID. +// - Update to match bug 9452 / r53657. +// - Create a TapParameterTreeWidgetItem class? +// - Better / more usable XML output. + +const int expand_all_threshold_ = 100; // Arbitrary + +static QHash cfg_str_to_creator_; +const QString TapParameterDialog::action_name_ = "TapParameterAction"; + +TapParameterDialog::TapParameterDialog(QWidget &parent, CaptureFile &cf, int help_topic) : + WiresharkDialog(parent, cf), + ui(new Ui::TapParameterDialog), + help_topic_(help_topic) +{ + ui->setupUi(this); + + // Only show a hint label if a subclass provides a hint. + ui->hintLabel->hide(); + + ctx_menu_.addAction(ui->actionCopyToClipboard); + ctx_menu_.addAction(ui->actionSaveAs); + + QPushButton *button; + button = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); + connect(button, &QPushButton::clicked, this, &TapParameterDialog::on_actionCopyToClipboard_triggered); + + button = ui->buttonBox->addButton(tr("Save as…"), QDialogButtonBox::ActionRole); + connect(button, &QPushButton::clicked, this, &TapParameterDialog::on_actionSaveAs_triggered); + + connect(ui->displayFilterLineEdit, SIGNAL(textChanged(QString)), + this, SLOT(updateWidgets())); + + ProgressFrame::addToButtonBox(ui->buttonBox, &parent); + + if (help_topic_ < 1) { + ui->buttonBox->button(QDialogButtonBox::Help)->hide(); + } + + if (!ui->displayFilterLineEdit->text().isEmpty()) { + QString filter = ui->displayFilterLineEdit->text(); + emit updateFilter(filter); + } + show_timer_ = new QTimer(this); + setRetapOnShow(true); +} + +TapParameterDialog::~TapParameterDialog() +{ + delete ui; + show_timer_->stop(); + delete show_timer_; +} + +void TapParameterDialog::registerDialog(const QString title, const char *cfg_abbr, register_stat_group_t group, stat_tap_init_cb tap_init_cb, tpdCreator creator) +{ + stat_tap_ui ui_info; + + ui_info.group = group; + ui_info.title = title.toUtf8().constData(); + ui_info.cli_string = cfg_abbr; + ui_info.tap_init_cb = tap_init_cb; + ui_info.nparams = 0; // We'll need this for SCSI SRT + ui_info.params = NULL; + register_stat_tap_ui(&ui_info, NULL); + + QString cfg_str = cfg_abbr; + cfg_str_to_creator_[cfg_str] = creator; + + QAction *tpd_action = new QAction(title, mainApp); + tpd_action->setObjectName(action_name_); + tpd_action->setData(cfg_str); + mainApp->addDynamicMenuGroupItem(group, tpd_action); +} + +TapParameterDialog *TapParameterDialog::showTapParameterStatistics(QWidget &parent, CaptureFile &cf, const QString cfg_str, const QString arg, void *) +{ + if (cfg_str_to_creator_.contains(cfg_str)) { + TapParameterDialog *tpd = cfg_str_to_creator_[cfg_str](parent, cfg_str, arg, cf); + return tpd; + } + return NULL; +} + +QTreeWidget *TapParameterDialog::statsTreeWidget() +{ + return ui->statsTreeWidget; +} + +QLineEdit *TapParameterDialog::displayFilterLineEdit() +{ + return ui->displayFilterLineEdit; +} + +QPushButton *TapParameterDialog::applyFilterButton() +{ + return ui->applyFilterButton; +} + +QVBoxLayout *TapParameterDialog::verticalLayout() +{ + return ui->verticalLayout; +} + +QHBoxLayout *TapParameterDialog::filterLayout() +{ + return ui->filterLayout; +} + +QString TapParameterDialog::displayFilter() +{ + return ui->displayFilterLineEdit->text(); +} + +// This assumes that we're called before signals are connected or show() +// is called. +void TapParameterDialog::setDisplayFilter(const QString &filter) +{ + ui->displayFilterLineEdit->setText(filter); +} + +void TapParameterDialog::setHint(const QString &hint) +{ + ui->hintLabel->setText(hint); + ui->hintLabel->show(); +} + +void TapParameterDialog::setRetapOnShow(bool retap) +{ + show_timer_->stop(); + if (retap) { + show_timer_->singleShot(0, this, SLOT(on_applyFilterButton_clicked())); + } +} + +void TapParameterDialog::filterActionTriggered() +{ + FilterAction *fa = qobject_cast(QObject::sender()); + QString filter_expr = filterExpression(); + + if (!fa || filter_expr.isEmpty()) { + return; + } + + emit filterAction(filter_expr, fa->action(), fa->actionType()); +} + +void TapParameterDialog::collapseAllActionTriggered() { + ui->statsTreeWidget->collapseAll(); +} +void TapParameterDialog::expandAllActionTriggered() { + ui->statsTreeWidget->expandAll(); +} + +QString TapParameterDialog::itemDataToPlain(QVariant var, int width) +{ + QString plain_str; + int align_mul = 1; + + switch (var.userType()) { + case QMetaType::QString: + align_mul = -1; + // Fall through + case QMetaType::Int: + case QMetaType::UInt: + plain_str = var.toString(); + break; + case QMetaType::Double: + plain_str = QString::number(var.toDouble(), 'f', 6); + break; + default: + break; + } + + if (plain_str.length() < width) { + plain_str = QString("%1").arg(plain_str, width * align_mul); + } + return plain_str; +} + +QList TapParameterDialog::treeItemData(QTreeWidgetItem *) const +{ + return QList(); +} + +const QString plain_sep_ = " "; +QByteArray TapParameterDialog::getTreeAsString(st_format_type format) +{ + QByteArray ba; + QTreeWidgetItemIterator it(ui->statsTreeWidget, QTreeWidgetItemIterator::NotHidden); + + QList col_widths; + QByteArray footer; + + // Title + header + switch (format) { + case ST_FORMAT_PLAIN: + { + // Iterating over trees. + QTreeWidgetItemIterator width_it(it); + QString plain_header; + while (*width_it) { + // Iterating over items within this tree. + for (int col=0; col < ui->statsTreeWidget->columnCount(); col++) { + if (col_widths.size() <= col) { + col_widths.append(static_cast(ui->statsTreeWidget->headerItem()->text(col).length())); + } + QVariant var = ui->statsTreeWidget->headerItem()->data(col, Qt::DisplayRole); + if (var.userType() == QMetaType::QString) { + col_widths[col] = qMax(col_widths[col], static_cast(itemDataToPlain(var).length())); + } + } + ++width_it; + } + QStringList ph_parts; + for (int col = 0; col < ui->statsTreeWidget->columnCount() && col < col_widths.length(); col++) { + ph_parts << ui->statsTreeWidget->headerItem()->text(col); + } + plain_header = ph_parts.join(plain_sep_); + + QByteArray top_separator; + top_separator.fill('=', plain_header.length()); + top_separator.append('\n'); + QString file_header = QString("%1 - %2:\n").arg(windowSubtitle(), cap_file_.fileDisplayName()); + footer.fill('-', plain_header.length()); + footer.append('\n'); + plain_header.append('\n'); + + ba.append(top_separator); + ba.append(file_header.toUtf8()); + ba.append(plain_header.toUtf8()); + ba.append(footer); + break; + } + case ST_FORMAT_CSV: + { + QString csv_header; + QStringList ch_parts; + for (int col = 0; col < ui->statsTreeWidget->columnCount(); col++) { + ch_parts << QString("\"%1\"").arg(ui->statsTreeWidget->headerItem()->text(col)); + } + csv_header = ch_parts.join(","); + csv_header.append('\n'); + ba.append(csv_header.toUtf8().constData()); + break; + } + case ST_FORMAT_XML: + { + // XXX What's a useful format? This mostly conforms to DocBook. + ba.append("\n"); + QString title = html_escape(windowSubtitle()); + QString xml_header = QString("\n%1\n").arg(title); + ba.append(xml_header.toUtf8()); + ba.append("\n\n"); + for (int col = 0; col < ui->statsTreeWidget->columnCount(); col++) { + title = html_escape(ui->statsTreeWidget->headerItem()->text(col)); + title = QString(" %1\n").arg(title); + ba.append(title.toUtf8()); + } + ba.append("\n\n"); + ba.append("\n"); + footer = "\n
\n"; + break; + } + case ST_FORMAT_YAML: + { + QString yaml_header; + ba.append("---\n"); + yaml_header = QString("Description: \"%1\"\nFile: \"%2\"\nItems:\n").arg(windowSubtitle()).arg(cap_file_.fileDisplayName()); + ba.append(yaml_header.toUtf8()); + break; + } + default: + break; + } + + // Data + while (*it) { + QList tid = treeItemData((*it)); + if (tid.length() < 1) { + ++it; + continue; + } + + if (tid.length() < ui->statsTreeWidget->columnCount()) { + // Assume we have a header + } + + // Assume var length == columnCount + QString line; + QStringList parts; + + switch (format) { + case ST_FORMAT_PLAIN: + { + int i = 0; + foreach (QVariant var, tid) { + parts << itemDataToPlain(var, col_widths[i]); + i++; + } + line = parts.join(plain_sep_); + line.append('\n'); + break; + } + case ST_FORMAT_CSV: + foreach (QVariant var, tid) { + if (var.userType() == QMetaType::QString) { + parts << QString("\"%1\"").arg(var.toString()); + } else { + parts << var.toString(); + } + } + line = parts.join(","); + line.append('\n'); + break; + case ST_FORMAT_XML: + { + line = "\n"; + foreach (QVariant var, tid) { + QString entry = html_escape(var.toString()); + line.append(QString(" %1\n").arg(entry)); + } + line.append("\n"); + break; + } + case ST_FORMAT_YAML: + { + int col = 0; + QString indent = "-"; + foreach (QVariant var, tid) { + QString entry; + if (var.userType() == QMetaType::QString) { + entry = QString("\"%1\"").arg(var.toString()); + } else { + entry = var.toString(); + } + line.append(QString(" %1 %2: %3\n").arg(indent).arg(ui->statsTreeWidget->headerItem()->text(col), entry)); + indent = " "; + col++; + } + break; + } + default: + break; + } + + ba.append(line.toUtf8()); + ++it; + } + + // Footer + ba.append(footer); // plain only? + return ba; +} + +void TapParameterDialog::drawTreeItems() +{ + if (ui->statsTreeWidget->model()->rowCount() < expand_all_threshold_) { + ui->statsTreeWidget->expandAll(); + } + + for (int col = 0; col < ui->statsTreeWidget->columnCount(); col++) { + ui->statsTreeWidget->resizeColumnToContents(col); + } +} + +void TapParameterDialog::contextMenuEvent(QContextMenuEvent *event) +{ + bool enable = filterExpression().length() > 0 ? true : false; + + foreach (QAction *fa, filter_actions_) { + fa->setEnabled(enable); + } + + ctx_menu_.popup(event->globalPos()); +} + +void TapParameterDialog::addFilterActions() +{ + QMenu *submenu; + QAction *insert_action = ctx_menu_.actions().first(); + + FilterAction::Action cur_action = FilterAction::ActionApply; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + filter_actions_ << fa; + } + ctx_menu_.insertMenu(insert_action, submenu); + + cur_action = FilterAction::ActionPrepare; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + filter_actions_ << fa; + } + ctx_menu_.insertMenu(insert_action, submenu); + + cur_action = FilterAction::ActionFind; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes(cur_action)) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + filter_actions_ << fa; + } + ctx_menu_.insertMenu(insert_action, submenu); + + cur_action = FilterAction::ActionColorize; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes(cur_action)) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + filter_actions_ << fa; + } + ctx_menu_.insertMenu(insert_action, submenu); + ctx_menu_.insertSeparator(insert_action); +} + +void TapParameterDialog::addTreeCollapseAllActions() +{ + ctx_menu_.addSeparator(); + + QAction *collapse = new QAction(tr("Collapse All"), this); + ctx_menu_.addAction(collapse); + connect(collapse, SIGNAL(triggered()), this, SLOT(collapseAllActionTriggered())); + + QAction *expand = new QAction(tr("Expand All"), this); + ctx_menu_.addAction(expand); + connect(expand, SIGNAL(triggered()), this, SLOT(expandAllActionTriggered())); +} + +void TapParameterDialog::updateWidgets() +{ + bool edit_enable = true; + bool apply_enable = true; + + if (file_closed_) { + edit_enable = false; + apply_enable = false; + } else if (!ui->displayFilterLineEdit->checkFilter()) { + // XXX Tell the user why the filter is invalid. + apply_enable = false; + } + ui->displayFilterLineEdit->setEnabled(edit_enable); + ui->applyFilterButton->setEnabled(apply_enable); + + WiresharkDialog::updateWidgets(); +} + +void TapParameterDialog::on_applyFilterButton_clicked() +{ + beginRetapPackets(); + if (!ui->displayFilterLineEdit->checkFilter()) + return; + + QString filter = ui->displayFilterLineEdit->text(); + emit updateFilter(filter); + // If we wanted to be fancy we could add an isRetapping function to + // either WiresharkDialog or CaptureFile and use it in updateWidgets + // to enable and disable the apply button as needed. + // For now we use more simple but less useful logic. + bool df_enabled = ui->displayFilterLineEdit->isEnabled(); + bool af_enabled = ui->applyFilterButton->isEnabled(); + ui->displayFilterLineEdit->setEnabled(false); + ui->applyFilterButton->setEnabled(false); + fillTree(); + ui->applyFilterButton->setEnabled(af_enabled); + ui->displayFilterLineEdit->setEnabled(df_enabled); + endRetapPackets(); +} + +void TapParameterDialog::on_actionCopyToClipboard_triggered() +{ + mainApp->clipboard()->setText(getTreeAsString(ST_FORMAT_PLAIN)); +} + +void TapParameterDialog::on_actionSaveAs_triggered() +{ + QString selectedFilter; + st_format_type format; + const char *file_ext; + FILE *f; + bool success = false; + int last_errno; + +#ifdef Q_OS_WIN + HANDLE da_ctx = set_thread_per_monitor_v2_awareness(); +#endif + QFileDialog SaveAsDialog(this, mainApp->windowTitleString(tr("Save Statistics As…")), + get_open_dialog_initial_dir()); + SaveAsDialog.setNameFilter(tr("Plain text file (*.txt);;" + "Comma separated values (*.csv);;" + "XML document (*.xml);;" + "YAML document (*.yaml)")); + SaveAsDialog.selectNameFilter(tr("Plain text file (*.txt)")); + SaveAsDialog.setAcceptMode(QFileDialog::AcceptSave); + int result = SaveAsDialog.exec(); +#ifdef Q_OS_WIN + revert_thread_per_monitor_v2_awareness(da_ctx); +#endif + if (!result) { + return; + } + selectedFilter= SaveAsDialog.selectedNameFilter(); + if (selectedFilter.contains("*.yaml", Qt::CaseInsensitive)) { + format = ST_FORMAT_YAML; + file_ext = ".yaml"; + } + else if (selectedFilter.contains("*.xml", Qt::CaseInsensitive)) { + format = ST_FORMAT_XML; + file_ext = ".xml"; + } + else if (selectedFilter.contains("*.csv", Qt::CaseInsensitive)) { + format = ST_FORMAT_CSV; + file_ext = ".csv"; + } + else { + format = ST_FORMAT_PLAIN; + file_ext = ".txt"; + } + + // Get selected filename and add extension of necessary + QString file_name = SaveAsDialog.selectedFiles()[0]; + if (!file_name.endsWith(file_ext, Qt::CaseInsensitive)) { + file_name.append(file_ext); + } + + QByteArray tree_as_ba = getTreeAsString(format); + + // actually save the file + f = ws_fopen (file_name.toUtf8().constData(), "w"); + last_errno = errno; + if (f) { + if (fputs(tree_as_ba.data(), f) != EOF) { + success = true; + } + last_errno = errno; + fclose(f); + } + if (!success) { + QMessageBox::warning(this, tr("Error saving file %1").arg(file_name), + g_strerror (last_errno)); + } +} + +void TapParameterDialog::on_buttonBox_helpRequested() +{ + if (help_topic_ > 0) { + mainApp->helpTopicAction((topic_action_e) help_topic_); + } +} diff --git a/ui/qt/tap_parameter_dialog.h b/ui/qt/tap_parameter_dialog.h new file mode 100644 index 00000000..1ed9db78 --- /dev/null +++ b/ui/qt/tap_parameter_dialog.h @@ -0,0 +1,111 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TAP_PARAMETER_DIALOG_H +#define TAP_PARAMETER_DIALOG_H + +/* + * @file Base class for statistics and analysis dialogs. + * Provides convenience classes for command-line tap parameters ("-z ...") + * and general tapping. + */ + +#include "config.h" + +#include + +#include +#include + +#include + +#include "filter_action.h" +#include "wireshark_dialog.h" + +class QHBoxLayout; +class QLineEdit; +class QTreeWidget; +class QTreeWidgetItem; +class QVBoxLayout; + +namespace Ui { +class TapParameterDialog; +} + +class TapParameterDialog; +typedef TapParameterDialog* (*tpdCreator)(QWidget &parent, const QString cfg_str, const QString arg, CaptureFile &cf); + +class TapParameterDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit TapParameterDialog(QWidget &parent, CaptureFile &cf, int help_topic = 0); + ~TapParameterDialog(); + + static const QString &actionName() { return action_name_; } + static void registerDialog(const QString title, const char *cfg_abbr, register_stat_group_t group, stat_tap_init_cb tap_init_cb, tpdCreator creator); + + static TapParameterDialog *showTapParameterStatistics(QWidget &parent, CaptureFile &cf, const QString cfg_str, const QString arg, void *); + // Needed by static member functions in subclasses. Should we just make + // "ui" available instead? + QTreeWidget *statsTreeWidget(); + QLineEdit *displayFilterLineEdit(); + QPushButton *applyFilterButton(); + QVBoxLayout *verticalLayout(); + QHBoxLayout *filterLayout(); + + void drawTreeItems(); + +signals: + void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + void updateFilter(QString filter); + +public slots: + +protected: + void contextMenuEvent(QContextMenuEvent *event); + void addFilterActions(); + void addTreeCollapseAllActions(); + QString displayFilter(); + void setDisplayFilter(const QString &filter); + void setHint(const QString &hint); + // Retap packets on first display. RPC stats need to disable this. + void setRetapOnShow(bool retap); + +protected slots: + void filterActionTriggered(); + void collapseAllActionTriggered(); + void expandAllActionTriggered(); + void updateWidgets(); + +private: + Ui::TapParameterDialog *ui; + QMenu ctx_menu_; + QList filter_actions_; + int help_topic_; + static const QString action_name_; + QTimer *show_timer_; + + virtual const QString filterExpression() { return QString(); } + QString itemDataToPlain(QVariant var, int width = 0); + virtual QList treeItemData(QTreeWidgetItem *) const; + virtual QByteArray getTreeAsString(st_format_type format); + +private slots: + // Called by the constructor. The subclass should tap packets here. + virtual void fillTree() = 0; + + void on_applyFilterButton_clicked(); + void on_actionCopyToClipboard_triggered(); + void on_actionSaveAs_triggered(); + void on_buttonBox_helpRequested(); +}; + +#endif // TAP_PARAMETER_DIALOG_H diff --git a/ui/qt/tap_parameter_dialog.ui b/ui/qt/tap_parameter_dialog.ui new file mode 100644 index 00000000..ec739e80 --- /dev/null +++ b/ui/qt/tap_parameter_dialog.ui @@ -0,0 +1,142 @@ + + + TapParameterDialog + + + + 0 + 0 + 587 + 459 + + + + Dialog + + + + + + true + + + true + + + + Item + + + + + + + + <small><i>A hint.</i></small> + + + true + + + + + + + + + Display filter: + + + + + + + + + + Regenerate statistics using this display filter + + + Apply + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Copy + + + Copy a text representation of the tree to the clipboard + + + Ctrl+C + + + + + Save as… + + + Save the displayed data in various formats + + + Ctrl+S + + + + + + DisplayFilterEdit + QLineEdit +
widgets/display_filter_edit.h
+
+
+ + + + buttonBox + accepted() + TapParameterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TapParameterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/tcp_stream_dialog.cpp b/ui/qt/tcp_stream_dialog.cpp new file mode 100644 index 00000000..49980d8d --- /dev/null +++ b/ui/qt/tcp_stream_dialog.cpp @@ -0,0 +1,2217 @@ +/* tcp_stream_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tcp_stream_dialog.h" +#include + +#include // for std::sort +#include // for std::pair +#include + +#include "epan/to_str.h" + +#include "wsutil/str_util.h" + +#include + +#include +#include +#include "progress_frame.h" +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include + +#include + +// To do: +// - Make the Help button work. +// - Show a message or disable the graph if we don't have any data. +// - Add a bytes in flight graph +// - Make the crosshairs tracer a vertical band? +// - Implement File->Copy +// - Add UDP graphs +// - Make the first throughput MA period a dotted/dashed line? +// - Add range scroll bars? +// - ACK & RWIN segment ticks in tcptrace graph +// - Add missing elements (retrans, URG, SACK, etc) to tcptrace. It probably makes +// sense to subclass QCPGraph for this. + +// The GTK+ version computes a 20 (or 21!) segment moving average. Comment +// out the line below to use that. By default we use a 1 second MA. +#define MA_1_SECOND + +#ifndef MA_1_SECOND +const int moving_avg_period_ = 20; +#endif + +const QRgb graph_color_1 = tango_sky_blue_5; +const QRgb graph_color_2 = tango_butter_6; +const QRgb graph_color_3 = tango_chameleon_5; +const QRgb graph_color_4 = tango_scarlet_red_4; +const QRgb graph_color_5 = tango_scarlet_red_6; + +// Size of selectable packet points in the base graph +const double pkt_point_size_ = 3.0; + +// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph +// in zoom mode. +const int min_zoom_pixels_ = 20; + +const QString average_throughput_label_ = QObject::tr("Average Throughput (bits/s)"); +const QString round_trip_time_ms_label_ = QObject::tr("Round Trip Time (ms)"); +const QString segment_length_label_ = QObject::tr("Segment Length (B)"); +const QString sequence_number_label_ = QObject::tr("Sequence Number (B)"); +const QString time_s_label_ = QObject::tr("Time (s)"); +const QString window_size_label_ = QObject::tr("Window Size (B)"); + +QCPErrorBarsNotSelectable::QCPErrorBarsNotSelectable(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPErrorBars(keyAxis, valueAxis) +{ +} + +QCPErrorBarsNotSelectable::~QCPErrorBarsNotSelectable() +{ +} + +double QCPErrorBarsNotSelectable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(pos); + Q_UNUSED(onlySelectable); + Q_UNUSED(details); + return -1.0; +} + +TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_type graph_type) : + GeometryStateDialog(parent), + ui(new Ui::TCPStreamDialog), + cap_file_(cf), + ts_offset_(0), + ts_origin_conn_(true), + seq_offset_(0), + seq_origin_zero_(true), + title_(nullptr), + base_graph_(nullptr), + tput_graph_(nullptr), + goodput_graph_(nullptr), + seg_graph_(nullptr), + seg_eb_(nullptr), + ack_graph_(nullptr), + sack_graph_(nullptr), + sack_eb_(nullptr), + sack2_graph_(nullptr), + sack2_eb_(nullptr), + rwin_graph_(nullptr), + dup_ack_graph_(nullptr), + zero_win_graph_(nullptr), + tracer_(nullptr), + packet_num_(0), + mouse_drags_(true), + rubber_band_(nullptr), + graph_updater_(this), + num_dsegs_(-1), + num_acks_(-1), + num_sack_ranges_(-1), + ma_window_size_(1.0) +{ + int graph_idx = -1; + + memset(&graph_, 0, sizeof(graph_)); + + ui->setupUi(this); + if (parent) loadGeometry(parent->width() * 2 / 3, parent->height() * 4 / 5); + setAttribute(Qt::WA_DeleteOnClose, true); + + ui->streamNumberSpinBox->setStyleSheet("QSpinBox { min-width: 2em; }"); + + guint32 th_stream = select_tcpip_session(cap_file_); + if (th_stream == G_MAXUINT32) { + done(QDialog::Rejected); + return; + } + + QComboBox *gtcb = ui->graphTypeComboBox; + gtcb->setUpdatesEnabled(false); + gtcb->addItem(ui->actionRoundTripTime->text(), GRAPH_RTT); + if (graph_type == GRAPH_RTT) graph_idx = gtcb->count() - 1; + gtcb->addItem(ui->actionThroughput->text(), GRAPH_THROUGHPUT); + if (graph_type == GRAPH_THROUGHPUT) graph_idx = gtcb->count() - 1; + gtcb->addItem(ui->actionStevens->text(), GRAPH_TSEQ_STEVENS); + if (graph_type == GRAPH_TSEQ_STEVENS) graph_idx = gtcb->count() - 1; + gtcb->addItem(ui->actionTcptrace->text(), GRAPH_TSEQ_TCPTRACE); + if (graph_type == GRAPH_TSEQ_TCPTRACE) graph_idx = gtcb->count() - 1; + gtcb->addItem(ui->actionWindowScaling->text(), GRAPH_WSCALE); + if (graph_type == GRAPH_WSCALE) graph_idx = gtcb->count() - 1; + gtcb->setUpdatesEnabled(true); + + ui->dragRadioButton->setChecked(mouse_drags_); + + ctx_menu_.addAction(ui->actionZoomIn); + ctx_menu_.addAction(ui->actionZoomInX); + ctx_menu_.addAction(ui->actionZoomInY); + ctx_menu_.addAction(ui->actionZoomOut); + ctx_menu_.addAction(ui->actionZoomOutX); + ctx_menu_.addAction(ui->actionZoomOutY); + ctx_menu_.addAction(ui->actionReset); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionMoveRight10); + ctx_menu_.addAction(ui->actionMoveLeft10); + ctx_menu_.addAction(ui->actionMoveUp10); + ctx_menu_.addAction(ui->actionMoveDown10); + ctx_menu_.addAction(ui->actionMoveRight1); + ctx_menu_.addAction(ui->actionMoveLeft1); + ctx_menu_.addAction(ui->actionMoveUp1); + ctx_menu_.addAction(ui->actionMoveDown1); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionNextStream); + ctx_menu_.addAction(ui->actionPreviousStream); + ctx_menu_.addAction(ui->actionSwitchDirection); + ctx_menu_.addAction(ui->actionGoToPacket); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionDragZoom); + ctx_menu_.addAction(ui->actionToggleSequenceNumbers); + ctx_menu_.addAction(ui->actionToggleTimeOrigin); + ctx_menu_.addAction(ui->actionCrosshairs); + ctx_menu_.addSeparator(); + ctx_menu_.addAction(ui->actionRoundTripTime); + ctx_menu_.addAction(ui->actionThroughput); + ctx_menu_.addAction(ui->actionStevens); + ctx_menu_.addAction(ui->actionTcptrace); + ctx_menu_.addAction(ui->actionWindowScaling); + set_action_shortcuts_visible_in_context_menu(ctx_menu_.actions()); + + graph_.type = graph_type; + graph_.stream = th_stream; + findStream(); + + showWidgetsForGraphType(); + + ui->streamNumberSpinBox->blockSignals(true); + ui->streamNumberSpinBox->setMaximum(get_tcp_stream_count() - 1); + ui->streamNumberSpinBox->setValue(graph_.stream); + ui->streamNumberSpinBox->blockSignals(false); + +#ifdef MA_1_SECOND + ui->maWindowSizeSpinBox->blockSignals(true); + ui->maWindowSizeSpinBox->setDecimals(6); + ui->maWindowSizeSpinBox->setMinimum(0.000001); + ui->maWindowSizeSpinBox->setValue(ma_window_size_); + ui->maWindowSizeSpinBox->blockSignals(false); +#endif + + // set which Throughput graphs are displayed by default + ui->showSegLengthCheckBox->blockSignals(true); + ui->showSegLengthCheckBox->setChecked(true); + ui->showSegLengthCheckBox->blockSignals(false); + + ui->showThroughputCheckBox->blockSignals(true); + ui->showThroughputCheckBox->setChecked(true); + ui->showThroughputCheckBox->blockSignals(false); + + // set which WScale graphs are displayed by default + ui->showRcvWinCheckBox->blockSignals(true); + ui->showRcvWinCheckBox->setChecked(true); + ui->showRcvWinCheckBox->blockSignals(false); + + ui->showBytesOutCheckBox->blockSignals(true); + ui->showBytesOutCheckBox->setChecked(true); + ui->showBytesOutCheckBox->blockSignals(false); + + QCustomPlot *sp = ui->streamPlot; + QCPTextElement *file_title = new QCPTextElement(sp, gchar_free_to_qstring(cf_get_display_name(cap_file_))); + file_title->setFont(sp->xAxis->labelFont()); + title_ = new QCPTextElement(sp); + sp->plotLayout()->insertRow(0); + sp->plotLayout()->addElement(0, 0, file_title); + sp->plotLayout()->insertRow(0); + sp->plotLayout()->addElement(0, 0, title_); + + qreal pen_width = 0.5; + // Base Graph - enables selecting segments (both data and SACKs) + base_graph_ = sp->addGraph(); + base_graph_->setPen(QPen(QBrush(graph_color_1), pen_width)); + + // Throughput Graph - rate of sent bytes + tput_graph_ = sp->addGraph(sp->xAxis, sp->yAxis2); + tput_graph_->setPen(QPen(QBrush(graph_color_2), pen_width)); + tput_graph_->setLineStyle(QCPGraph::lsStepLeft); + + // Goodput Graph - rate of ACKed bytes + goodput_graph_ = sp->addGraph(sp->xAxis, sp->yAxis2); + goodput_graph_->setPen(QPen(QBrush(graph_color_3), pen_width)); + goodput_graph_->setLineStyle(QCPGraph::lsStepLeft); + + // Seg Graph - displays forward data segments on tcptrace graph + seg_graph_ = sp->addGraph(); + seg_graph_->setLineStyle(QCPGraph::lsNone); + seg_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot, Qt::transparent, 0)); + seg_eb_ = new QCPErrorBarsNotSelectable(sp->xAxis, sp->yAxis); + seg_eb_->setErrorType(QCPErrorBars::etValueError); + seg_eb_->setPen(QPen(QBrush(graph_color_1), pen_width)); + seg_eb_->setSymbolGap(0.0); // draw error spine as single line + seg_eb_->setWhiskerWidth(pkt_point_size_); + seg_eb_->removeFromLegend(); + seg_eb_->setDataPlottable(seg_graph_); + + // Ack Graph - displays ack numbers from reverse packets + ack_graph_ = sp->addGraph(); + ack_graph_->setPen(QPen(QBrush(graph_color_2), pen_width)); + ack_graph_->setLineStyle(QCPGraph::lsStepLeft); + + // Sack Graph - displays highest number (most recent) SACK block + sack_graph_ = sp->addGraph(); + sack_graph_->setLineStyle(QCPGraph::lsNone); + sack_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot, Qt::transparent, 0)); + sack_eb_ = new QCPErrorBarsNotSelectable(sp->xAxis, sp->yAxis); + sack_eb_->setErrorType(QCPErrorBars::etValueError); + sack_eb_->setPen(QPen(QBrush(graph_color_4), pen_width)); + sack_eb_->setSymbolGap(0.0); // draw error spine as single line + sack_eb_->setWhiskerWidth(0.0); + sack_eb_->removeFromLegend(); + sack_eb_->setDataPlottable(sack_graph_); + + // Sack Graph 2 - displays subsequent SACK blocks + sack2_graph_ = sp->addGraph(); + sack2_graph_->setLineStyle(QCPGraph::lsNone); + sack2_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot, Qt::transparent, 0)); + sack2_eb_ = new QCPErrorBarsNotSelectable(sp->xAxis, sp->yAxis); + sack2_eb_->setErrorType(QCPErrorBars::etValueError); + sack2_eb_->setPen(QPen(QBrush(graph_color_5), pen_width)); + sack2_eb_->setSymbolGap(0.0); // draw error spine as single line + sack2_eb_->setWhiskerWidth(0.0); + sack2_eb_->removeFromLegend(); + sack2_eb_->setDataPlottable(sack2_graph_); + + // RWin graph - displays upper extent of RWIN advertised on reverse packets + rwin_graph_ = sp->addGraph(); + rwin_graph_->setPen(QPen(QBrush(graph_color_3), pen_width)); + rwin_graph_->setLineStyle(QCPGraph::lsStepLeft); + + // Duplicate ACK Graph - displays duplicate ack ticks + // QCustomPlot doesn't have QCPScatterStyle::ssTick so we have to make our own. + int tick_len = 3; + tick_len *= devicePixelRatio(); + QPixmap da_tick_pm = QPixmap(1, tick_len * 2); + da_tick_pm.fill(Qt::transparent); + QPainter painter(&da_tick_pm); + QPen da_tick_pen; + da_tick_pen.setColor(graph_color_2); + da_tick_pen.setWidthF(pen_width); + painter.setPen(da_tick_pen); + painter.drawLine(0, tick_len, 0, tick_len * 2); + dup_ack_graph_ = sp->addGraph(); + dup_ack_graph_->setLineStyle(QCPGraph::lsNone); + QCPScatterStyle da_ss = QCPScatterStyle(QCPScatterStyle::ssPixmap, graph_color_2, 0); + da_ss.setPixmap(da_tick_pm); + dup_ack_graph_->setScatterStyle(da_ss); + + // Zero Window Graph - displays zero window crosses (x) + zero_win_graph_ = sp->addGraph(); + zero_win_graph_->setLineStyle(QCPGraph::lsNone); + zero_win_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCross, graph_color_1, 5)); + + tracer_ = new QCPItemTracer(sp); + + // Triggers fillGraph() [ UNLESS the index is already graph_idx!! ] + if (graph_idx != ui->graphTypeComboBox->currentIndex()) + // changing the current index will call fillGraph + ui->graphTypeComboBox->setCurrentIndex(graph_idx); + else + // the current index is what we want - so fillGraph() manually + fillGraph(); + + sp->setMouseTracking(true); + + sp->yAxis->setLabelColor(QColor(graph_color_1)); + sp->yAxis->setTickLabelColor(QColor(graph_color_1)); + + tracer_->setVisible(false); + toggleTracerStyle(true); + + QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save); + save_bt->setText(tr("Save As…")); + + QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close); + if (close_bt) { + close_bt->setDefault(true); + } + + ProgressFrame::addToButtonBox(ui->buttonBox, parent); + + connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*))); + connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + connect(sp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*))); + connect(sp, SIGNAL(axisClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)), + this, SLOT(axisClicked(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*))); + connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(transformYRange(QCPRange))); + this->setResult(QDialog::Accepted); +} + +TCPStreamDialog::~TCPStreamDialog() +{ + graph_segment_list_free(&graph_); + + delete ui; +} + +void TCPStreamDialog::showEvent(QShowEvent *) +{ + resetAxes(); +} + +void TCPStreamDialog::keyPressEvent(QKeyEvent *event) +{ + int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10; + + QWidget* focusWidget = QApplication::focusWidget(); + + // Block propagation of "Enter" key when focus is not default (e.g. SpinBox) + // [ Note that if focus was on, e.g. Close button, event would never reach + // here ] + if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && + focusWidget !=NULL && focusWidget != ui->streamPlot) { + + // reset focus to default, and accept event + ui->streamPlot->setFocus(); + event->accept(); + return; + } + + // XXX - This differs from the main window but matches other applications (e.g. Mozilla and Safari) + switch(event->key()) { + case Qt::Key_Minus: + case Qt::Key_Underscore: // Shifted minus on U.S. keyboards + case Qt::Key_O: // GTK+ + zoomAxes(false); + break; + case Qt::Key_Plus: + case Qt::Key_Equal: // Unshifted plus on U.S. keyboards + case Qt::Key_I: // GTK+ + zoomAxes(true); + break; + case Qt::Key_X: // Zoom X axis only + if (event->modifiers() & Qt::ShiftModifier) { + zoomXAxis(false); // upper case X -> Zoom out + } else { + zoomXAxis(true); // lower case x -> Zoom in + } + break; + case Qt::Key_Y: // Zoom Y axis only + if (event->modifiers() & Qt::ShiftModifier) { + zoomYAxis(false); // upper case Y -> Zoom out + } else { + zoomYAxis(true); // lower case y -> Zoom in + } + break; + case Qt::Key_Right: + case Qt::Key_L: + panAxes(pan_pixels, 0); + break; + case Qt::Key_Left: + case Qt::Key_H: + panAxes(-1 * pan_pixels, 0); + break; + case Qt::Key_Up: + case Qt::Key_K: + panAxes(0, pan_pixels); + break; + case Qt::Key_Down: + case Qt::Key_J: + panAxes(0, -1 * pan_pixels); + break; + + case Qt::Key_Space: + toggleTracerStyle(); + break; + + case Qt::Key_0: + case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards + case Qt::Key_R: + case Qt::Key_Home: + resetAxes(); + break; + + case Qt::Key_PageUp: + on_actionNextStream_triggered(); + break; + case Qt::Key_PageDown: + on_actionPreviousStream_triggered(); + break; + + case Qt::Key_D: + on_actionSwitchDirection_triggered(); + break; + case Qt::Key_G: + on_actionGoToPacket_triggered(); + break; + case Qt::Key_S: + on_actionToggleSequenceNumbers_triggered(); + break; + case Qt::Key_T: + on_actionToggleTimeOrigin_triggered(); + break; + case Qt::Key_Z: + on_actionDragZoom_triggered(); + break; + + case Qt::Key_1: + on_actionRoundTripTime_triggered(); + break; + case Qt::Key_2: + on_actionThroughput_triggered(); + break; + case Qt::Key_3: + on_actionStevens_triggered(); + break; + case Qt::Key_4: + on_actionTcptrace_triggered(); + break; + case Qt::Key_5: + on_actionWindowScaling_triggered(); + break; + // Alas, there is no Blade Runner-style Qt::Key_Enhance + } + + QDialog::keyPressEvent(event); +} + +void TCPStreamDialog::mousePressEvent(QMouseEvent *event) +{ + // if no-one else wants the event, then this is a click on blank space. + // Use this opportunity to set focus back to default, and accept event. + ui->streamPlot->setFocus(); + event->accept(); +} + +void TCPStreamDialog::mouseReleaseEvent(QMouseEvent *event) +{ + mouseReleased(event); +} + +void TCPStreamDialog::findStream() +{ + QCustomPlot *sp = ui->streamPlot; + + disconnect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + // if streamNumberSpinBox has focus - + // first clear focus, then disable/enable, then restore focus + bool spin_box_focused = ui->streamNumberSpinBox->hasFocus(); + if (spin_box_focused) + ui->streamNumberSpinBox->clearFocus(); + ui->streamNumberSpinBox->setEnabled(false); + graph_segment_list_free(&graph_); + graph_segment_list_get(cap_file_, &graph_); + ui->streamNumberSpinBox->setEnabled(true); + if (spin_box_focused) + ui->streamNumberSpinBox->setFocus(); + + connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); +} + +void TCPStreamDialog::fillGraph(bool reset_axes, bool set_focus) +{ + QCustomPlot *sp = ui->streamPlot; + + if (sp->graphCount() < 1) return; + + base_graph_->setLineStyle(QCPGraph::lsNone); + tracer_->setGraph(NULL); + + // base_graph_ is always visible. + for (int i = 0; i < sp->graphCount(); i++) { + sp->graph(i)->data()->clear(); + sp->graph(i)->setVisible(i == 0 ? true : false); + } + // also clear and hide ErrorBars plottables + seg_eb_->setVisible(false); + seg_eb_->data()->clear(); + sack_eb_->setVisible(false); + sack_eb_->data()->clear(); + sack2_eb_->setVisible(false); + sack2_eb_->data()->clear(); + + base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + + sp->xAxis->setLabel(time_s_label_); + sp->xAxis->setNumberFormat("gb"); + // Use enough precision to mark microseconds + // when zooming in on a <100s capture + sp->xAxis->setNumberPrecision(8); + sp->yAxis->setNumberFormat("f"); + sp->yAxis->setNumberPrecision(0); + sp->yAxis2->setVisible(false); + sp->yAxis2->setLabel(QString()); + + if (!cap_file_) { + QString dlg_title = QString(tr("No Capture Data")); + setWindowTitle(dlg_title); + title_->setText(dlg_title); + sp->setEnabled(false); + sp->yAxis->setLabel(QString()); + sp->replot(); + return; + } + + ts_offset_ = 0; + seq_offset_ = 0; + bool first = true; + guint64 bytes_fwd = 0; + guint64 bytes_rev = 0; + int pkts_fwd = 0; + int pkts_rev = 0; + + time_stamp_map_.clear(); + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + // NOTE - adding both forward and reverse packets to time_stamp_map_ + // so that both data and acks are selectable + // (this is important especially in selecting particular SACK pkts) + bool insert = true; + if (!compareHeaders(seg)) { + bytes_rev += seg->th_seglen; + pkts_rev++; + // only insert reverse packets if SACK present + insert = (seg->num_sack_ranges != 0); + } else { + bytes_fwd += seg->th_seglen; + pkts_fwd++; + } + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + if (first) { + if (ts_origin_conn_) ts_offset_ = ts; + if (seq_origin_zero_) { + if (compareHeaders(seg)) + seq_offset_ = seg->th_seq; + else + seq_offset_ = seg->th_ack; + } + first = false; + } + if (insert) { + time_stamp_map_.insert(ts - ts_offset_, seg); + } + } + + switch (graph_.type) { + case GRAPH_TSEQ_STEVENS: + fillStevens(); + break; + case GRAPH_TSEQ_TCPTRACE: + fillTcptrace(); + break; + case GRAPH_THROUGHPUT: + fillThroughput(); + break; + case GRAPH_RTT: + fillRoundTripTime(); + break; + case GRAPH_WSCALE: + fillWindowScale(); + break; + default: + break; + } + sp->setEnabled(true); + + stream_desc_ = tr("%1 %2 pkts, %3 %4 %5 pkts, %6 ") + .arg(UTF8_RIGHTWARDS_ARROW) + .arg(gchar_free_to_qstring(format_size(pkts_fwd, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI))) + .arg(gchar_free_to_qstring(format_size(bytes_fwd, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))) + .arg(UTF8_LEFTWARDS_ARROW) + .arg(gchar_free_to_qstring(format_size(pkts_rev, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI))) + .arg(gchar_free_to_qstring(format_size(bytes_rev, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + mouseMoved(NULL); + if (reset_axes) + resetAxes(); + else + sp->replot(); + // Throughput and Window Scale graphs can hide base_graph_ + if (base_graph_->visible()) + tracer_->setGraph(base_graph_); + + // XXX QCustomPlot doesn't seem to draw any sort of focus indicator. + if (set_focus) + sp->setFocus(); +} + +void TCPStreamDialog::showWidgetsForGraphType() +{ + if (graph_.type == GRAPH_RTT) { + ui->bySeqNumberCheckBox->setVisible(true); + } else { + ui->bySeqNumberCheckBox->setVisible(false); + } + if (graph_.type == GRAPH_THROUGHPUT) { +#ifdef MA_1_SECOND + ui->maWindowSizeLabel->setVisible(true); + ui->maWindowSizeSpinBox->setVisible(true); +#else + ui->maWindowSizeLabel->setVisible(false); + ui->maWindowSizeSpinBox->setVisible(false); +#endif + ui->showSegLengthCheckBox->setVisible(true); + ui->showThroughputCheckBox->setVisible(true); + ui->showGoodputCheckBox->setVisible(true); + } else { + ui->maWindowSizeLabel->setVisible(false); + ui->maWindowSizeSpinBox->setVisible(false); + ui->showSegLengthCheckBox->setVisible(false); + ui->showThroughputCheckBox->setVisible(false); + ui->showGoodputCheckBox->setVisible(false); + } + + if (graph_.type == GRAPH_TSEQ_TCPTRACE) { + ui->selectSACKsCheckBox->setVisible(true); + } else { + ui->selectSACKsCheckBox->setVisible(false); + } + + if (graph_.type == GRAPH_WSCALE) { + ui->showRcvWinCheckBox->setVisible(true); + ui->showBytesOutCheckBox->setVisible(true); + } else { + ui->showRcvWinCheckBox->setVisible(false); + ui->showBytesOutCheckBox->setVisible(false); + } +} + +void TCPStreamDialog::zoomAxes(bool in) +{ + QCustomPlot *sp = ui->streamPlot; + double h_factor = sp->axisRect()->rangeZoomFactor(Qt::Horizontal); + double v_factor = sp->axisRect()->rangeZoomFactor(Qt::Vertical); + + if (!in) { + h_factor = pow(h_factor, -1); + v_factor = pow(v_factor, -1); + } + + sp->xAxis->scaleRange(h_factor, sp->xAxis->range().center()); + sp->yAxis->scaleRange(v_factor, sp->yAxis->range().center()); + sp->replot(); +} + +void TCPStreamDialog::zoomXAxis(bool in) +{ + QCustomPlot *sp = ui->streamPlot; + double h_factor = sp->axisRect()->rangeZoomFactor(Qt::Horizontal); + + if (!in) { + h_factor = pow(h_factor, -1); + } + + sp->xAxis->scaleRange(h_factor, sp->xAxis->range().center()); + sp->replot(); +} + +void TCPStreamDialog::zoomYAxis(bool in) +{ + QCustomPlot *sp = ui->streamPlot; + double v_factor = sp->axisRect()->rangeZoomFactor(Qt::Vertical); + + if (!in) { + v_factor = pow(v_factor, -1); + } + + sp->yAxis->scaleRange(v_factor, sp->yAxis->range().center()); + sp->replot(); +} + +void TCPStreamDialog::panAxes(int x_pixels, int y_pixels) +{ + QCustomPlot *sp = ui->streamPlot; + double h_pan = 0.0; + double v_pan = 0.0; + + h_pan = sp->xAxis->range().size() * x_pixels / sp->xAxis->axisRect()->width(); + v_pan = sp->yAxis->range().size() * y_pixels / sp->yAxis->axisRect()->height(); + // The GTK+ version won't pan unless we're zoomed. Should we do the same here? + if (h_pan) { + sp->xAxis->moveRange(h_pan); + sp->replot(); + } + if (v_pan) { + sp->yAxis->moveRange(v_pan); + sp->replot(); + } +} + +void TCPStreamDialog::resetAxes() +{ + QCustomPlot *sp = ui->streamPlot; + + y_axis_xfrm_.reset(); + double pixel_pad = 10.0; // per side + + sp->rescaleAxes(true); +// tput_graph_->rescaleValueAxis(false, true); +// base_graph_->rescaleAxes(false, true); +// for (int i = 0; i < sp->graphCount(); i++) { +// sp->graph(i)->rescaleValueAxis(false, true); +// } + + double axis_pixels = sp->xAxis->axisRect()->width(); + sp->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, sp->xAxis->range().center()); + + if (sp->yAxis2->visible()) { + double ratio = sp->yAxis2->range().size() / sp->yAxis->range().size(); + y_axis_xfrm_.translate(0.0, sp->yAxis2->range().lower - (sp->yAxis->range().lower * ratio)); + y_axis_xfrm_.scale(1.0, ratio); + } + + axis_pixels = sp->yAxis->axisRect()->height(); + sp->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, sp->yAxis->range().center()); + + sp->replot(); +} + +void TCPStreamDialog::fillStevens() +{ + QString dlg_title = QString(tr("Sequence Numbers (Stevens)")) + streamDescription(); + setWindowTitle(dlg_title); + title_->setText(dlg_title); + + QCustomPlot *sp = ui->streamPlot; + sp->yAxis->setLabel(sequence_number_label_); + + // True Stevens-style graphs don't have lines but I like them - gcc + base_graph_->setLineStyle(QCPGraph::lsStepLeft); + + QVector rel_time, seq; + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + if (!compareHeaders(seg)) { + continue; + } + + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + rel_time.append(ts - ts_offset_); + seq.append(seg->th_seq - seq_offset_); + } + base_graph_->setData(rel_time, seq); +} + +void TCPStreamDialog::fillTcptrace() +{ + QString dlg_title = QString(tr("Sequence Numbers (tcptrace)")) + streamDescription(); + setWindowTitle(dlg_title); + title_->setText(dlg_title); + + bool allow_sack_select = ui->selectSACKsCheckBox->isChecked(); + + QCustomPlot *sp = ui->streamPlot; + sp->yAxis->setLabel(sequence_number_label_); + + base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot)); + + seg_graph_->setVisible(true); + seg_eb_->setVisible(true); + ack_graph_->setVisible(true); + sack_graph_->setVisible(true); + sack_eb_->setVisible(true); + sack2_graph_->setVisible(true); + sack2_eb_->setVisible(true); + rwin_graph_->setVisible(true); + dup_ack_graph_->setVisible(true); + zero_win_graph_->setVisible(true); + + QVector pkt_time, pkt_seqnums; + QVector sb_time, sb_center, sb_span; + QVector ackrwin_time, ack, rwin; + QVector sack_time, sack_center, sack_span; + QVector sack2_time, sack2_center, sack2_span; + QVector dup_ack_time, dup_ack; + QVector zero_win_time, zero_win; + + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_; + if (compareHeaders(seg)) { + double half = seg->th_seglen / 2.0; + double center = seg->th_seq - seq_offset_ + half; + + // Add forward direction to base_graph_ (to select data packets) + // Forward direction: seq + data + pkt_time.append(ts); + pkt_seqnums.append(center); + + // QCP doesn't have a segment graph type. For now, fake + // it with error bars. + if (seg->th_seglen > 0) { + sb_time.append(ts); + sb_center.append(center); + sb_span.append(half); + } + + // Look for zero window sizes. + // Should match the TCP_A_ZERO_WINDOW test in packet-tcp.c. + if (seg->th_win == 0 && (seg->th_flags & (TH_RST|TH_FIN|TH_SYN)) == 0) { + zero_win_time.append(ts); + zero_win.append(center); + } + } else { + // Reverse direction: ACK + RWIN + if (! (seg->th_flags & TH_ACK)) { + // SYNs and RSTs do not necessarily have ACKs + continue; + } + double ackno = seg->th_ack - seq_offset_; + // add SACK segments to sack, sack2, and selectable packet graph + for (int i = 0; i < seg->num_sack_ranges; ++i) { + double half = seg->sack_right_edge[i] - seg->sack_left_edge[i]; + half = half/2.0; + double center = seg->sack_left_edge[i] - seq_offset_ + half; + if (i == 0) { + sack_time.append(ts); + sack_center.append(center); + sack_span.append(half); + if (allow_sack_select) { + pkt_time.append(ts); + pkt_seqnums.append(center); + } + } else { + sack2_time.append(ts); + sack2_center.append(center); + sack2_span.append(half); + } + } + // If ackno is the same as our last one mark it as a duplicate. + // (but don't mark window updates as duplicate acks) + if (ack.size() > 0 && ack.last() == ackno + && rwin.last() == ackno + seg->th_win) { + dup_ack_time.append(ts); + dup_ack.append(ackno); + } + // Also add reverse packets to the ack_graph_ + ackrwin_time.append(ts); + ack.append(ackno); + rwin.append(ackno + seg->th_win); + } + } + base_graph_->setData(pkt_time, pkt_seqnums, true); + ack_graph_->setData(ackrwin_time, ack, true); + seg_graph_->setData(sb_time, sb_center, true); + seg_eb_->setData(sb_span); + sack_graph_->setData(sack_time, sack_center, true); + sack_eb_->setData(sack_span); + sack2_graph_->setData(sack2_time, sack2_center, true); + sack2_eb_->setData(sack2_span); + rwin_graph_->setData(ackrwin_time, rwin, true); + dup_ack_graph_->setData(dup_ack_time, dup_ack, true); + zero_win_graph_->setData(zero_win_time, zero_win, true); +} + +// If the current implementation of incorporating SACKs in goodput calc +// is slow, comment out the following line to ignore SACKs in goodput calc. +#define USE_SACKS_IN_GOODPUT_CALC + +#ifdef USE_SACKS_IN_GOODPUT_CALC +// to incorporate SACKED segments into goodput calculation, +// need to keep track of all the SACK blocks we haven't yet +// fully ACKed. +// I expect this to be _relatively_ small, so using vector to store +// them. If this performs badly, it can be refactored with std::list +// or std::map. +typedef std::pair sack_t; +typedef std::vector sack_list_t; +static inline bool compare_sack(const sack_t& s1, const sack_t& s2) { + return tcp_seq_before(s1.first, s2.first); +} + +// Helper function to adjust an acked seglen for goodput: +// - removes previously sacked ranges from seglen (and from old_sacks), +// - adds newly sacked ranges to seglen (and to old_sacks) +static void +goodput_adjust_for_sacks(guint32 *seglen, guint32 last_ack, + sack_list_t& new_sacks, guint8 num_sack_ranges, + sack_list_t& old_sacks) { + + // Step 1 - For any old_sacks acked by last_ack, + // delete their acked length from seglen, + // and remove the sack block (or portion) + // from (sorted) old_sacks. + sack_list_t::iterator unacked = old_sacks.begin(); + while (unacked != old_sacks.end()) { + // break on first sack not fully acked + if (tcp_seq_before(last_ack, unacked->second)) { + if (tcp_seq_after(last_ack, unacked->first)) { + // partially acked - modify to remove acked part + *seglen -= (last_ack - unacked->first); + unacked->first = last_ack; + } + break; + } + // remove fully acked sacks from seglen and move on + // (we'll actually remove from the list when loop is done) + *seglen -= (unacked->second - unacked->first); + ++unacked; + } + // actually remove all fully acked sacks from old_sacks list + if (unacked != old_sacks.begin()) + old_sacks.erase(old_sacks.begin(), unacked); + + // Step 2 - for any new_sacks that precede last_ack, + // ignore them. (These would generally be SACKed dup-acks of + // a retransmitted seg). + // [ in the unlikely case that any new SACK straddles last_ack, + // the sack block will be modified to remove the acked portion ] + int next_new_idx = 0; + while (next_new_idx < num_sack_ranges) { + if (tcp_seq_before(last_ack, new_sacks[next_new_idx].second)) { + // if a new SACK block is unacked by its own packet, then it's + // likely fully unacked, but let's check for partial ack anyway, + // and truncate the SACK so that it's fully unacked: + if (tcp_seq_before(new_sacks[next_new_idx].first, last_ack)) + new_sacks[next_new_idx].first = last_ack; + break; + } + ++next_new_idx; + } + + // Step 3 - for any byte ranges in remaining new_sacks + // that don't already exist in old_sacks, add + // their length to seglen + // and add that range (by extension, if possible) to + // the list of old_sacks. + + sack_list_t::iterator next_old = old_sacks.begin(); + + while (next_new_idx < num_sack_ranges && + next_old != old_sacks.end()) { + sack_t* next_new = &new_sacks[next_new_idx]; + + // Assumptions / Invariants: + // - new and old lists are sorted + // - span of leftmost to rightmost endpt. is less than half uint32 range + // [ensures transitivity - e.g. before(a,b) and before(b,c) ==> before(a,c)] + // - all SACKs are non-empty (sack.left before sack.right) + // - adjacent SACKs in list always have a gap between them + // (sack.right before next_sack.left) + + // Given these assumptions, and noting that there are only three + // possible comparisons for a pair of points (before/equal/after), + // there are only a few possible relative configurations + // of next_old and next_new: + // next_new: + // [-------------) + // next_old: + // 1. [---) + // 2. [-------) + // 3. [----------------) + // 4. [---------------------) + // 5. [----------------------------) + // 6. [--------) + // 7. [-------------) + // 8. [--------------------) + // 9. [---) + // 10. [--------) + // 11. [---------------) + // 12. [------) + // 13. [--) + + // Case 1: end of next_old is before beginning of next_new + // next_new: + // [-------------) ... + // next_old: + // 1. [---) ... + if (tcp_seq_before(next_old->second, next_new->first)) { + // Actions: + // advance to the next sack in old_sacks + ++next_old; + // retry from the top + continue; + } + + // Case 13: end of next_new is before beginning of next_old + // next_new: + // [-------------) ... + // next_old: + // 13. [--) ... + if (tcp_seq_before(next_new->second, next_old->first)) { + // Actions: + // add then entire length of next_new into seglen + *seglen += (next_new->second - next_new->first); + // insert next_new before next_old in old_sacks + // (be sure to save and restore next_old iterator around insert!) + int next_old_idx = int(next_old - old_sacks.begin()); + old_sacks.insert(next_old, *next_new); + next_old = old_sacks.begin() + next_old_idx + 1; + // advance to the next remaining sack in new_sacks + ++next_new_idx; + // retry from the top + continue; + } + + // Remaining possible configurations: + // next_new: + // [-------------) + // next_old: + // 2. [-------) + // 3. [----------------) + // 4. [---------------------) + // 5. [----------------------------) + // 6. [--------) + // 7. [-------------) + // 8. [--------------------) + // 9. [---) + // 10. [--------) + // 11. [---------------) + // 12. [------) + + // Cases 2,3,6,9: end of next_old is before end of next_new + // next_new: + // [-------------) + // next_old: + // 2. [-------) + // 3. [----------------) + // 6. [--------) + // 9. [---) + // Actions: + // until end of next_old is equal or after end of next_new, + // repeatedly extend next_old, coalescing with next_next_old + // if necessary. (and add extended bytes to seglen) + while (tcp_seq_before(next_old->second, next_new->second)) { + // if end of next_new doesn't collide with start of next_next_old, + if (((next_old+1) == old_sacks.end()) || + tcp_seq_before(next_new->second, (next_old + 1)->first)) { + // extend end of next_old up to end of next_new, + // adding extended bytes to seglen + *seglen += (next_new->second - next_old->second); + next_old->second = next_new->second; + } + // otherwise, coalesce next_old with next_next_old + else { + // add bytes to close gap between sacks to seglen + *seglen += ((next_old + 1)->first - next_old->second); + // coalesce next_next_old into next_old + next_old->second = (next_old + 1)->second; + old_sacks.erase(next_old + 1); + } + } + // This operation turns: + // Cases 2 and 3 into Case 4 or 5 + // Case 6 into Case 7 + // Case 9 into Case 10 + // Leaving: + + // Remaining possible configurations: + // next_new: + // [-------------) + // next_old: + // 4. [---------------------) + // 5. [----------------------------) + // 7. [-------------) + // 8. [--------------------) + // 10. [--------) + // 11. [---------------) + // 12. [------) + + // Cases 10,11,12: start of next_new is before start of next_old + // next_new: + // [-------------) + // next_old: + // 10. [--------) + // 11. [---------------) + // 12. [------) + if (tcp_seq_before(next_new->first, next_old->first)) { + // Actions: + // add the unaccounted bytes in next_new to seglen + *seglen += (next_old->first - next_new->first); + // then pull the start of next_old back to the start of next_new + next_old->first = next_new->first; + } + // This operation turns: + // Case 10 into Case 7 + // Cases 11 and 12 into Case 8 + // Leaving: + + // Remaining possible configurations: + // next_new: + // [-------------) + // next_old: + // 4. [---------------------) + // 5. [----------------------------) + // 7. [-------------) + // 8. [--------------------) + + // In these cases, the bytes in next_new are fully accounted + // by the bytes in next_old, so we can move on to look at + // the next sack block in new_sacks + ++next_new_idx; + } + // Conditions for leaving loop: + // - we processed all remaining new_sacks - nothing left to do + // (next_new_idx == num_sack_ranges) + // OR + // - all remaining new_sacks start at least one byte after + // the rightmost edge of the last old_sack + // (meaning we can just add the remaining new_sacks to old_sacks list, + // and add them directly to the goodput seglen) + while (next_new_idx < num_sack_ranges) { + sack_t* next_new = &new_sacks[next_new_idx]; + *seglen += (next_new->second - next_new->first); + old_sacks.push_back(*next_new); + ++next_new_idx; + } +} +#endif // USE_SACKS_IN_GOODPUT_CALC + +void TCPStreamDialog::fillThroughput() +{ + QString dlg_title = QString(tr("Throughput")) + streamDescription(); +#ifdef MA_1_SECOND + dlg_title.append(tr(" (MA)")); +#else + dlg_title.append(QString(tr(" (%1 Segment MA)")).arg(moving_avg_period_)); +#endif + setWindowTitle(dlg_title); + title_->setText(dlg_title); + + QCustomPlot *sp = ui->streamPlot; + sp->yAxis->setLabel(segment_length_label_); + sp->yAxis2->setLabel(average_throughput_label_); + sp->yAxis2->setLabelColor(QColor(graph_color_2)); + sp->yAxis2->setTickLabelColor(QColor(graph_color_2)); + sp->yAxis2->setVisible(true); + + base_graph_->setVisible(ui->showSegLengthCheckBox->isChecked()); + tput_graph_->setVisible(ui->showThroughputCheckBox->isChecked()); + goodput_graph_->setVisible(ui->showGoodputCheckBox->isChecked()); + +#ifdef MA_1_SECOND + if (!graph_.segments) { +#else + if (!graph_.segments || !graph_.segments->next) { +#endif + dlg_title.append(tr(" [not enough data]")); + return; + } + + QVector seg_rel_times, ack_rel_times; + QVector seg_lens, ack_lens; + QVector tput_times, gput_times; + QVector tputs, gputs; + int oldest_seg = 0, oldest_ack = 0; + guint64 seg_sum = 0, ack_sum = 0; + guint32 seglen = 0; + +#ifdef USE_SACKS_IN_GOODPUT_CALC + // to incorporate SACKED segments into goodput calculation, + // need to keep track of all the SACK blocks we haven't yet + // fully ACKed. + sack_list_t old_sacks, new_sacks; + new_sacks.reserve(MAX_TCP_SACK_RANGES); + // statically allocate current_sacks vector + // [ std::array might be better, but that is C++11 ] + for (int i = 0; i < MAX_TCP_SACK_RANGES; ++i) { + new_sacks.push_back(sack_t(0,0)); + } + old_sacks.reserve(2*MAX_TCP_SACK_RANGES); +#endif // USE_SACKS_IN_GOODPUT_CALC + + // need first acked sequence number to jump-start + // computation of acked bytes per packet + guint32 last_ack = 0; + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + // first reverse packet with ACK flag tells us first acked sequence # + if (!compareHeaders(seg) && (seg->th_flags & TH_ACK)) { + last_ack = seg->th_ack; + break; + } + } + // Financial charts don't show MA data until a full period has elapsed. + // [ NOTE - this is because they assume that there's old data that they + // don't have access to - but in our case we know that there's NO + // data prior to the first packet in the stream - so it's fine to + // spit out the MA immediately... ] + // The Rosetta Code MA examples start spitting out values immediately. + // For now use not-really-correct initial values just to keep our vector + // lengths the same. +#ifdef MA_1_SECOND + // NOTE that for the time-based MA case, you certainly can start with the + // first segment! + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { +#else + for (struct segment *seg = graph_.segments->next; seg != NULL; seg = seg->next) { +#endif + bool is_forward_seg = compareHeaders(seg); + QVector& r_pkt_times = is_forward_seg ? seg_rel_times : ack_rel_times; + QVector& r_lens = is_forward_seg ? seg_lens : ack_lens; + QVector& r_Xput_times = is_forward_seg ? tput_times : gput_times; + QVector& r_Xputs = is_forward_seg ? tputs : gputs; + int& r_oldest = is_forward_seg ? oldest_seg : oldest_ack; + guint64& r_sum = is_forward_seg ? seg_sum : ack_sum; + + double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_; + + if (is_forward_seg) { + seglen = seg->th_seglen; + } else { + if ((seg->th_flags & TH_ACK) && + tcp_seq_eq_or_after(seg->th_ack, last_ack)) { + seglen = seg->th_ack - last_ack; + last_ack = seg->th_ack; +#ifdef USE_SACKS_IN_GOODPUT_CALC + // copy any sack_ranges into new_sacks, and sort. + for (int i = 0; i < seg->num_sack_ranges; ++i) { + new_sacks[i].first = seg->sack_left_edge[i]; + new_sacks[i].second = seg->sack_right_edge[i]; + } + std::sort(new_sacks.begin(), + new_sacks.begin() + seg->num_sack_ranges, + compare_sack); + + // adjust the seglen based on new and old sacks, + // and update the old_sacks list + goodput_adjust_for_sacks(&seglen, last_ack, + new_sacks, seg->num_sack_ranges, + old_sacks); +#endif // USE_SACKS_IN_GOODPUT_CALC + } else { + seglen = 0; + } + } + + r_pkt_times.append(ts); + r_lens.append(seglen); + +#ifdef MA_1_SECOND + while (r_oldest < r_pkt_times.size() && ts - r_pkt_times[r_oldest] > ma_window_size_) { + r_sum -= r_lens[r_oldest]; + // append points where a packet LEAVES the MA window + // (as well as, below, where they ENTER the MA window) + r_Xputs.append(r_sum * 8.0 / ma_window_size_); + r_Xput_times.append(r_pkt_times[r_oldest] + ma_window_size_); + r_oldest++; + } +#else + if (r_lens.size() > moving_avg_period_) { + r_sum -= r_lens[r_oldest]; + r_oldest++; + } +#endif + + // av_Xput computes Xput, i.e.: + // throughput for forward packets + // goodput for reverse packets + double av_Xput; + r_sum += seglen; +#ifdef MA_1_SECOND + // for time-based MA, delta_t is constant + av_Xput = r_sum * 8.0 / ma_window_size_; +#else + double dtime = 0.0; + if (r_oldest > 0) + dtime = ts - r_pkt_times[r_oldest-1]; + if (dtime > 0.0) { + av_Xput = r_sum * 8.0 / dtime; + } else { + av_Xput = 0.0; + } +#endif + + // Add a data point only if our time window has advanced. Otherwise + // update the most recent point. (We might want to show a warning + // for out-of-order packets.) + if (r_Xput_times.size() > 0 && ts <= r_Xput_times.last()) { + r_Xputs[r_Xputs.size() - 1] = av_Xput; + } else { + r_Xputs.append(av_Xput); + r_Xput_times.append(ts); + } + } + base_graph_->setData(seg_rel_times, seg_lens); + tput_graph_->setData(tput_times, tputs); + goodput_graph_->setData(gput_times, gputs); +} + +// rtt_selectively_ack_range: +// "Helper" function for fillRoundTripTime +// given an rtt_unack list, two pointers to a range of segments in the list, +// and the [left,right) edges of a SACK block, selectively ACKs the range +// from "begin" to "end" - possibly splitting one segment in the range +// into two (and relinking the new segment in order after the first) +// +// Assumptions: +// "begin must be non-NULL +// "begin" must precede "end" (or "end" must be NULL) +// [ there are minor optimizations that could be added if +// the range from "begin" to "end" are in sequence number order. +// (this function would preserve that as an invariant). ] +static struct rtt_unack * +rtt_selectively_ack_range(QVector& x_vals, bool bySeqNumber, + QVector& rtt, + struct rtt_unack **list, + struct rtt_unack *begin, struct rtt_unack *end, + unsigned int left, unsigned int right, double rt_val) { + struct rtt_unack *cur, *next; + // sanity check: + if (tcp_seq_eq_or_after(left, right)) + return begin; + // real work: + for (cur = begin; cur != end; cur = next) { + next = cur->next; + // check #1: does [left,right) intersect current unack at all? + // (if not, we can just move on to the next unack) + if (tcp_seq_eq_or_after(cur->seqno, right) || + tcp_seq_eq_or_after(left, cur->end_seqno)) { + // no intersection - just skip this. + continue; + } + // yes, we intersect! + int left_end_acked = tcp_seq_eq_or_after(cur->seqno, left); + int right_end_acked = tcp_seq_eq_or_after(right, cur->end_seqno); + // check #2 - did we fully ack the current unack? + // (if so, we can delete it and move on) + if (left_end_acked && right_end_acked) { + // ACK the whole segment + if (bySeqNumber) { + x_vals.append(cur->seqno); + } else { + x_vals.append(cur->time); + } + rtt.append((rt_val - cur->time) * 1000.0); + // in this case, we will delete current unack + // [ update "begin" if necessary - we will return it to the + // caller to let them know we deleted it ] + if (cur == begin) + begin = next; + rtt_delete_unack_from_list(list, cur); + continue; + } + // check #3 - did we ACK the left-hand side of the current unack? + // (if so, we can just modify it and move on) + if (left_end_acked) { // and right_end_not_acked + // ACK the left end + if (bySeqNumber) { + x_vals.append(cur->seqno); + } else { + x_vals.append(cur->time); + } + rtt.append((rt_val - cur->time) * 1000.0); + // in this case, "right" marks the start of remaining bytes + cur->seqno = right; + continue; + } + // check #4 - did we ACK the right-hand side of the current unack? + // (if so, we can just modify it and move on) + if (right_end_acked) { // and left_end_not_acked + // ACK the right end + if (bySeqNumber) { + x_vals.append(left); + } else { + x_vals.append(cur->time); + } + rtt.append((rt_val - cur->time) * 1000.0); + // in this case, "left" is just beyond the remaining bytes + cur->end_seqno = left; + continue; + } + // at this point, we know: + // - the SACK block does intersect this unack, but + // - it does not intersect the left or right endpoints + // Therefore, it must intersect the middle, so we must split the unack + // into left and right unacked segments: + // ACK the SACK block + if (bySeqNumber) { + x_vals.append(left); + } else { + x_vals.append(cur->time); + } + rtt.append((rt_val - cur->time) * 1000.0); + // then split cur into two unacked segments + // (linking the right-hand unack after the left) + cur->next = rtt_get_new_unack(cur->time, right, cur->end_seqno - right); + cur->next->next = next; + cur->end_seqno = left; + } + return begin; +} + +void TCPStreamDialog::fillRoundTripTime() +{ + QString dlg_title = QString(tr("Round Trip Time")) + streamDescription(); + setWindowTitle(dlg_title); + title_->setText(dlg_title); + + QCustomPlot *sp = ui->streamPlot; + bool bySeqNumber = ui->bySeqNumberCheckBox->isChecked(); + + if (bySeqNumber) { + sequence_num_map_.clear(); + sp->xAxis->setLabel(sequence_number_label_); + sp->xAxis->setNumberFormat("f"); + sp->xAxis->setNumberPrecision(0); + } + sp->yAxis->setLabel(round_trip_time_ms_label_); + sp->yAxis->setNumberFormat("gb"); + sp->yAxis->setNumberPrecision(3); + + base_graph_->setLineStyle(QCPGraph::lsLine); + + QVector x_vals, rtt; + guint32 seq_base = 0; + struct rtt_unack *unack_list = NULL, *u = NULL; + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + if (compareHeaders(seg)) { + seq_base = seg->th_seq; + break; + } + } + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + if (compareHeaders(seg)) { + guint32 seqno = seg->th_seq - seq_base; + if (seg->th_seglen && !rtt_is_retrans(unack_list, seqno)) { + double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0; + rt_val -= ts_offset_; + u = rtt_get_new_unack(rt_val, seqno, seg->th_seglen); + if (!u) { + // make sure to free list before returning! + rtt_destroy_unack_list(&unack_list); + return; + } + rtt_put_unack_on_list(&unack_list, u); + } + } else { + guint32 ack_no = seg->th_ack - seq_base; + double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0; + rt_val -= ts_offset_; + struct rtt_unack *v; + + for (u = unack_list; u; u = v) { + if (tcp_seq_after(ack_no, u->seqno)) { + // full or partial ack of seg by ack_no + if (bySeqNumber) { + x_vals.append(u->seqno); + sequence_num_map_.insert(u->seqno, seg); + } else { + x_vals.append(u->time); + } + rtt.append((rt_val - u->time) * 1000.0); + if (tcp_seq_eq_or_after(ack_no, u->end_seqno)) { + // fully acked segment - nothing more to see here + v = u->next; + rtt_delete_unack_from_list(&unack_list, u); + // no need to compare SACK blocks for fully ACKed seg + continue; + } else { + // partial ack of GSO seg + u->seqno = ack_no; + // (keep going - still need to compare SACK blocks...) + } + } + v = u->next; + // selectively acking u more than once + // can shatter it into multiple intervals. + // If we link those back into the list between u and v, + // then each subsequent SACK selectively ACKs that range. + for (int i = 0; i < seg->num_sack_ranges; ++i) { + guint32 left = seg->sack_left_edge[i] - seq_base; + guint32 right = seg->sack_right_edge[i] - seq_base; + u = rtt_selectively_ack_range(x_vals, bySeqNumber, rtt, + &unack_list, u, v, + left, right, rt_val); + // if range is empty after selective ack, we can + // skip the rest of the SACK blocks + if (u == v) break; + } + } + } + } + // it's possible there's still unacked segs - so be sure to free list! + rtt_destroy_unack_list(&unack_list); + base_graph_->setData(x_vals, rtt); +} + +void TCPStreamDialog::fillWindowScale() +{ + QString dlg_title = QString(tr("Window Scaling")) + streamDescription(); + setWindowTitle(dlg_title); + title_->setText(dlg_title); + + QCustomPlot *sp = ui->streamPlot; + // use base_graph_ to represent unacked window size + // (estimate of congestion window) + base_graph_->setLineStyle(QCPGraph::lsStepLeft); + // use rwin_graph_ here to show rwin window scale + // (derived from ACK packets) + base_graph_->setVisible(ui->showBytesOutCheckBox->isChecked()); + rwin_graph_->setVisible(ui->showRcvWinCheckBox->isChecked()); + + QVector rel_time, win_size; + QVector cwnd_time, cwnd_size; + guint32 last_ack = 0; + bool found_first_ack = false; + for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + + // The receive window that applies to this flow comes + // from packets in the opposite direction + if (compareHeaders(seg)) { + // compute bytes_in_flight for cwnd graph + guint32 end_seq = seg->th_seq + seg->th_seglen; + if (found_first_ack && + tcp_seq_eq_or_after(end_seq, last_ack)) { + cwnd_time.append(ts - ts_offset_); + cwnd_size.append((double)(end_seq - last_ack)); + } + } else { + // packet in opposite direction - has advertised rwin + guint16 flags = seg->th_flags; + + if ((flags & (TH_SYN|TH_RST)) == 0) { + rel_time.append(ts - ts_offset_); + win_size.append(seg->th_win); + } + if ((flags & (TH_ACK)) != 0) { + // use this to update last_ack + if (!found_first_ack || + tcp_seq_eq_or_after(seg->th_ack, last_ack)) { + last_ack = seg->th_ack; + found_first_ack = true; + } + } + } + } + base_graph_->setData(cwnd_time, cwnd_size); + rwin_graph_->setData(rel_time, win_size); + sp->yAxis->setLabel(window_size_label_); +} + +QString TCPStreamDialog::streamDescription() +{ + QString description(tr(" for %1:%2 %3 %4:%5") + .arg(address_to_qstring(&graph_.src_address)) + .arg(graph_.src_port) + .arg(UTF8_RIGHTWARDS_ARROW) + .arg(address_to_qstring(&graph_.dst_address)) + .arg(graph_.dst_port)); + return description; +} + +bool TCPStreamDialog::compareHeaders(segment *seg) +{ + return (compare_headers(&graph_.src_address, &graph_.dst_address, + graph_.src_port, graph_.dst_port, + &seg->ip_src, &seg->ip_dst, + seg->th_sport, seg->th_dport, + COMPARE_CURR_DIR)); +} + +void TCPStreamDialog::toggleTracerStyle(bool force_default) +{ + if (!tracer_->visible() && !force_default) return; + + QPen sp_pen = ui->streamPlot->graph(0)->pen(); + QCPItemTracer::TracerStyle tstyle = QCPItemTracer::tsCrosshair; + QPen tr_pen = QPen(tracer_->pen()); + QColor tr_color = sp_pen.color(); + + if (force_default || tracer_->style() != QCPItemTracer::tsCircle) { + tstyle = QCPItemTracer::tsCircle; + tr_color.setAlphaF(1.0); + tr_pen.setWidthF(1.5); + } else { + tr_color.setAlphaF(0.5); + tr_pen.setWidthF(1.0); + } + + tracer_->setStyle(tstyle); + tr_pen.setColor(tr_color); + tracer_->setPen(tr_pen); + ui->streamPlot->replot(); +} + +QRectF TCPStreamDialog::getZoomRanges(QRect zoom_rect) +{ + QRectF zoom_ranges = QRectF(); + + QCustomPlot *sp = ui->streamPlot; + QRect zr = zoom_rect.normalized(); + + if (zr.width() < min_zoom_pixels_ && zr.height() < min_zoom_pixels_) { + return zoom_ranges; + } + + QRect ar = sp->axisRect()->rect(); + if (ar.intersects(zr)) { + QRect zsr = ar.intersected(zr); + zoom_ranges.setX(sp->xAxis->range().lower + + sp->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width()); + zoom_ranges.setWidth(sp->xAxis->range().size() * zsr.width() / ar.width()); + + // QRects grow down + zoom_ranges.setY(sp->yAxis->range().lower + + sp->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height()); + zoom_ranges.setHeight(sp->yAxis->range().size() * zsr.height() / ar.height()); + } + return zoom_ranges; +} + +void TCPStreamDialog::graphClicked(QMouseEvent *event) +{ + QCustomPlot *sp = ui->streamPlot; + + // mouse press on graph should reset focus to graph + sp->setFocus(); + + if (event->button() == Qt::RightButton) { + // XXX We should find some way to get streamPlot to handle a + // contextMenuEvent instead. +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + ctx_menu_.popup(event->globalPosition().toPoint()); +#else + ctx_menu_.popup(event->globalPos()); +#endif + } else if (mouse_drags_) { + if (sp->axisRect()->rect().contains(event->pos())) { + sp->setCursor(QCursor(Qt::ClosedHandCursor)); + } + on_actionGoToPacket_triggered(); + } else { + if (!rubber_band_) { + rubber_band_ = new QRubberBand(QRubberBand::Rectangle, sp); + } + rb_origin_ = event->pos(); + rubber_band_->setGeometry(QRect(rb_origin_, QSize())); + rubber_band_->show(); + } +} + +void TCPStreamDialog::axisClicked(QCPAxis *axis, QCPAxis::SelectablePart, QMouseEvent *) +{ + QCustomPlot *sp = ui->streamPlot; + + if (axis == sp->xAxis) { + switch (graph_.type) { + case GRAPH_THROUGHPUT: + case GRAPH_TSEQ_STEVENS: + case GRAPH_TSEQ_TCPTRACE: + case GRAPH_WSCALE: + case GRAPH_RTT: + ts_origin_conn_ = ts_origin_conn_ ? false : true; + fillGraph(); + break; + default: + break; + } + } else if (axis == sp->yAxis) { + switch (graph_.type) { + case GRAPH_TSEQ_STEVENS: + case GRAPH_TSEQ_TCPTRACE: + seq_origin_zero_ = seq_origin_zero_ ? false : true; + fillGraph(); + break; + default: + break; + } + } +} + +// Setting mouseTracking on our streamPlot may not be as reliable +// as we need. If it's not we might want to poll the mouse position +// using a QTimer instead. +void TCPStreamDialog::mouseMoved(QMouseEvent *event) +{ + QCustomPlot *sp = ui->streamPlot; + Qt::CursorShape shape = Qt::ArrowCursor; + if (event) { + if (event->buttons().testFlag(Qt::LeftButton)) { + if (mouse_drags_) { + shape = Qt::ClosedHandCursor; + } else { + shape = Qt::CrossCursor; + } + } else { + if (sp->axisRect()->rect().contains(event->pos())) { + if (mouse_drags_) { + shape = Qt::OpenHandCursor; + } else { + shape = Qt::CrossCursor; + } + } + } + } + sp->setCursor(QCursor(shape)); + + QString hint = ""; + if (mouse_drags_) { + double tr_key = tracer_->position->key(); + struct segment *packet_seg = NULL; + packet_num_ = 0; + + // XXX If we have multiple packets with the same timestamp tr_key + // may not return the packet we want. It might be possible to fudge + // unique keys using nextafter(). + if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) { + switch (graph_.type) { + case GRAPH_TSEQ_STEVENS: + case GRAPH_TSEQ_TCPTRACE: + case GRAPH_THROUGHPUT: + case GRAPH_WSCALE: + packet_seg = time_stamp_map_.value(tr_key, NULL); + break; + case GRAPH_RTT: + if (ui->bySeqNumberCheckBox->isChecked()) + packet_seg = sequence_num_map_.value(tr_key, NULL); + else + packet_seg = time_stamp_map_.value(tr_key, NULL); + default: + break; + } + } + + if (!packet_seg) { + tracer_->setVisible(false); + hint += "Hover over the graph for details. " + stream_desc_ + ""; + ui->hintLabel->setText(hint); + ui->streamPlot->replot(); + return; + } + + tracer_->setVisible(true); + packet_num_ = packet_seg->num; + hint += tr("%1 %2 (%3s len %4 seq %5 ack %6 win %7)") + .arg(cap_file_ ? tr("Click to select packet") : tr("Packet")) + .arg(packet_num_) + .arg(QString::number(packet_seg->rel_secs + packet_seg->rel_usecs / 1000000.0, 'g', 4)) + .arg(packet_seg->th_seglen) + .arg(packet_seg->th_seq) + .arg(packet_seg->th_ack) + .arg(packet_seg->th_win); + tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x())); + sp->replot(); + } else { + if (rubber_band_ && rubber_band_->isVisible() && event) { + rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized()); + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + hint += tr("Release to zoom, x = %1 to %2, y = %3 to %4") + .arg(zoom_ranges.x()) + .arg(zoom_ranges.x() + zoom_ranges.width()) + .arg(zoom_ranges.y()) + .arg(zoom_ranges.y() + zoom_ranges.height()); + } else { + hint += tr("Unable to select range."); + } + } else { + hint += tr("Click to select a portion of the graph."); + } + } + hint += " " + stream_desc_ + ""; + ui->hintLabel->setText(hint); +} + +void TCPStreamDialog::mouseReleased(QMouseEvent *event) +{ + if (rubber_band_ && rubber_band_->isVisible()) { + rubber_band_->hide(); + if (!mouse_drags_) { + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + QCustomPlot *sp = ui->streamPlot; + sp->xAxis->setRangeLower(zoom_ranges.x()); + sp->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width()); + sp->yAxis->setRangeLower(zoom_ranges.y()); + sp->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height()); + sp->replot(); + } + } + } else if (ui->streamPlot->cursor().shape() == Qt::ClosedHandCursor) { + ui->streamPlot->setCursor(QCursor(Qt::OpenHandCursor)); + } +} + +void TCPStreamDialog::transformYRange(const QCPRange &y_range1) +{ + if (y_axis_xfrm_.isIdentity()) return; + + QCustomPlot *sp = ui->streamPlot; + QLineF yp1 = QLineF(1.0, y_range1.lower, 1.0, y_range1.upper); + QLineF yp2 = y_axis_xfrm_.map(yp1); + + sp->yAxis2->setRangeUpper(yp2.y2()); + 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() +{ + QString file_name, extension; + QDir path(mainApp->openDialogInitialDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + 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)"); + QString filter = QString("%1;;%2;;%3;;%4") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter); + + file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Graph As…")), + path.canonicalPath(), filter, &extension); + + if (file_name.length() > 0) { + bool save_ok = false; + if (extension.compare(pdf_filter) == 0) { + save_ok = ui->streamPlot->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = ui->streamPlot->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = ui->streamPlot->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = ui->streamPlot->saveJpg(file_name); + } + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } +} + +void TCPStreamDialog::on_graphTypeComboBox_currentIndexChanged(int index) +{ + if (index < 0) return; + graph_.type = static_cast(ui->graphTypeComboBox->itemData(index).toInt()); + showWidgetsForGraphType(); + + fillGraph(/*reset_axes=*/true, /*set_focus=*/false); +} + +void TCPStreamDialog::on_resetButton_clicked() +{ + resetAxes(); +} + +void TCPStreamDialog::setCaptureFile(capture_file *cf) +{ + if (!cf) { // We only want to know when the file closes. + cap_file_ = NULL; + } +} + +void TCPStreamDialog::updateGraph() +{ + graph_updater_.doUpdate(); +} + +void TCPStreamDialog::on_streamNumberSpinBox_valueChanged(int new_stream) +{ + if (new_stream >= 0 && new_stream < int(get_tcp_stream_count())) { + graph_updater_.triggerUpdate(1000, /*reset_axes =*/true); + } +} + +void TCPStreamDialog::on_streamNumberSpinBox_editingFinished() +{ + updateGraph(); +} + +void TCPStreamDialog::on_maWindowSizeSpinBox_valueChanged(double new_ma_size) +{ + if (new_ma_size > 0.0) { + ma_window_size_ = new_ma_size; + graph_updater_.triggerUpdate(1000, /*reset_axes =*/false); + } +} + +void TCPStreamDialog::on_maWindowSizeSpinBox_editingFinished() +{ + updateGraph(); +} + +void TCPStreamDialog::on_selectSACKsCheckBox_stateChanged(int /* state */) +{ + fillGraph(/*reset_axes=*/false, /*set_focus=*/false); +} + +void TCPStreamDialog::on_otherDirectionButton_clicked() +{ + on_actionSwitchDirection_triggered(); +} + +void TCPStreamDialog::on_dragRadioButton_toggled(bool checked) +{ + if (checked) { + mouse_drags_ = true; + if (rubber_band_ && rubber_band_->isVisible()) + rubber_band_->hide(); + ui->streamPlot->setInteractions( + QCP::iRangeDrag | + QCP::iRangeZoom + ); + } +} + +void TCPStreamDialog::on_zoomRadioButton_toggled(bool checked) +{ + if (checked) { + mouse_drags_ = false; + ui->streamPlot->setInteractions(QCP::Interactions()); + } +} + +void TCPStreamDialog::on_bySeqNumberCheckBox_stateChanged(int /* state */) +{ + fillGraph(/*reset_axes=*/true, /*set_focus=*/false); +} + +void TCPStreamDialog::on_showSegLengthCheckBox_stateChanged(int state) +{ + bool visible = (state != 0); + if (graph_.type == GRAPH_THROUGHPUT && base_graph_ != NULL) { + base_graph_->setVisible(visible); + tracer_->setGraph(visible ? base_graph_ : NULL); + ui->streamPlot->replot(); + } +} + +void TCPStreamDialog::on_showThroughputCheckBox_stateChanged(int state) +{ + bool visible = (state != 0); + if (graph_.type == GRAPH_THROUGHPUT && tput_graph_ != NULL) { + tput_graph_->setVisible(visible); + ui->streamPlot->replot(); + } +} + +void TCPStreamDialog::on_showGoodputCheckBox_stateChanged(int state) +{ + bool visible = (state != 0); + if (graph_.type == GRAPH_THROUGHPUT && goodput_graph_ != NULL) { + goodput_graph_->setVisible(visible); + ui->streamPlot->replot(); + } +} + +void TCPStreamDialog::on_showRcvWinCheckBox_stateChanged(int state) +{ + bool visible = (state != 0); + if (graph_.type == GRAPH_WSCALE && rwin_graph_ != NULL) { + rwin_graph_->setVisible(visible); + ui->streamPlot->replot(); + } +} + +void TCPStreamDialog::on_showBytesOutCheckBox_stateChanged(int state) +{ + bool visible = (state != 0); + if (graph_.type == GRAPH_WSCALE && base_graph_ != NULL) { + base_graph_->setVisible(visible); + tracer_->setGraph(visible ? base_graph_ : NULL); + ui->streamPlot->replot(); + } +} + +void TCPStreamDialog::on_actionZoomIn_triggered() +{ + zoomAxes(true); +} + +void TCPStreamDialog::on_actionZoomInX_triggered() +{ + zoomXAxis(true); +} + +void TCPStreamDialog::on_actionZoomInY_triggered() +{ + zoomYAxis(true); +} + +void TCPStreamDialog::on_actionZoomOut_triggered() +{ + zoomAxes(false); +} + +void TCPStreamDialog::on_actionZoomOutX_triggered() +{ + zoomXAxis(false); +} + +void TCPStreamDialog::on_actionZoomOutY_triggered() +{ + zoomYAxis(false); +} + +void TCPStreamDialog::on_actionReset_triggered() +{ + on_resetButton_clicked(); +} + +void TCPStreamDialog::on_actionMoveRight10_triggered() +{ + panAxes(10, 0); +} + +void TCPStreamDialog::on_actionMoveLeft10_triggered() +{ + panAxes(-10, 0); +} + +void TCPStreamDialog::on_actionMoveUp10_triggered() +{ + panAxes(0, 10); +} + +void TCPStreamDialog::on_actionMoveDown10_triggered() +{ + panAxes(0, -10); +} + +void TCPStreamDialog::on_actionMoveRight1_triggered() +{ + panAxes(1, 0); +} + +void TCPStreamDialog::on_actionMoveLeft1_triggered() +{ + panAxes(-1, 0); +} + +void TCPStreamDialog::on_actionMoveUp1_triggered() +{ + panAxes(0, 1); +} + +void TCPStreamDialog::on_actionMoveDown1_triggered() +{ + panAxes(0, -1); +} + +void TCPStreamDialog::on_actionNextStream_triggered() +{ + if (int(graph_.stream) < int(get_tcp_stream_count()) - 1) { + ui->streamNumberSpinBox->setValue(graph_.stream + 1); + updateGraph(); + } +} + +void TCPStreamDialog::on_actionPreviousStream_triggered() +{ + if (graph_.stream > 0) { + ui->streamNumberSpinBox->setValue(graph_.stream - 1); + updateGraph(); + } +} + +void TCPStreamDialog::on_actionSwitchDirection_triggered() +{ + address tmp_addr; + guint16 tmp_port; + + copy_address(&tmp_addr, &graph_.src_address); + tmp_port = graph_.src_port; + free_address(&graph_.src_address); + copy_address(&graph_.src_address, &graph_.dst_address); + graph_.src_port = graph_.dst_port; + free_address(&graph_.dst_address); + copy_address(&graph_.dst_address, &tmp_addr); + graph_.dst_port = tmp_port; + free_address(&tmp_addr); + + fillGraph(/*reset_axes=*/true, /*set_focus=*/false); +} + +void TCPStreamDialog::on_actionGoToPacket_triggered() +{ + if (tracer_->visible() && cap_file_ && packet_num_ > 0) { + emit goToPacket(packet_num_); + } +} + +void TCPStreamDialog::on_actionDragZoom_triggered() +{ + if (mouse_drags_) { + ui->zoomRadioButton->toggle(); + } else { + ui->dragRadioButton->toggle(); + } +} + +void TCPStreamDialog::on_actionToggleSequenceNumbers_triggered() +{ + seq_origin_zero_ = seq_origin_zero_ ? false : true; + fillGraph(); +} + +void TCPStreamDialog::on_actionToggleTimeOrigin_triggered() +{ + ts_origin_conn_ = ts_origin_conn_ ? false : true; + fillGraph(); +} + +void TCPStreamDialog::on_actionRoundTripTime_triggered() +{ + ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_RTT)); +} + +void TCPStreamDialog::on_actionThroughput_triggered() +{ + ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_THROUGHPUT)); +} + +void TCPStreamDialog::on_actionStevens_triggered() +{ + ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_TSEQ_STEVENS)); +} + +void TCPStreamDialog::on_actionTcptrace_triggered() +{ + ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_TSEQ_TCPTRACE)); +} + +void TCPStreamDialog::on_actionWindowScaling_triggered() +{ + ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_WSCALE)); +} + +void TCPStreamDialog::GraphUpdater::triggerUpdate(int timeout, bool reset_axes) +{ + if (!hasPendingUpdate()) { + graph_update_timer_ = new QTimer(dialog_); + graph_update_timer_->setSingleShot(true); + dialog_->connect(graph_update_timer_, SIGNAL(timeout()), dialog_, SLOT(updateGraph())); + } + reset_axes_ = (reset_axes_ || reset_axes); + graph_update_timer_->start(timeout); +} + +void TCPStreamDialog::GraphUpdater::clearPendingUpdate() +{ + if (hasPendingUpdate()) { + if (graph_update_timer_->isActive()) + graph_update_timer_->stop(); + delete graph_update_timer_; + graph_update_timer_ = NULL; + reset_axes_ = false; + } +} + +void TCPStreamDialog::GraphUpdater::doUpdate() +{ + if (hasPendingUpdate()) { + bool reset_axes = reset_axes_; + clearPendingUpdate(); + // if the stream has changed, update the data here + int new_stream = dialog_->ui->streamNumberSpinBox->value(); + if ((int(dialog_->graph_.stream) != new_stream) && + (new_stream >= 0 && new_stream < int(get_tcp_stream_count()))) { + dialog_->graph_.stream = new_stream; + dialog_->findStream(); + } + dialog_->fillGraph(reset_axes, /*set_focus =*/false); + } +} + +void TCPStreamDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_STATS_TCP_STREAM_GRAPHS_DIALOG); +} diff --git a/ui/qt/tcp_stream_dialog.h b/ui/qt/tcp_stream_dialog.h new file mode 100644 index 00000000..c5b97de0 --- /dev/null +++ b/ui/qt/tcp_stream_dialog.h @@ -0,0 +1,196 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TCP_STREAM_DIALOG_H +#define TCP_STREAM_DIALOG_H + +#include + +#include + +#include + +#include + +#include "ui/tap-tcp-stream.h" + +#include "geometry_state_dialog.h" + +#include +#include +#include +#include + +namespace Ui { +class TCPStreamDialog; +class QCPErrorBarsNotSelectable; +} + +class QCPErrorBarsNotSelectable : public QCPErrorBars +{ + Q_OBJECT + +public: + explicit QCPErrorBarsNotSelectable(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPErrorBarsNotSelectable(); + + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details = 0) const Q_DECL_OVERRIDE; +}; + +class TCPStreamDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit TCPStreamDialog(QWidget *parent = 0, capture_file *cf = NULL, tcp_graph_type graph_type = GRAPH_TSEQ_TCPTRACE); + ~TCPStreamDialog(); + +signals: + void goToPacket(int packet_num); + +public slots: + void setCaptureFile(capture_file *cf); + void updateGraph(); + +protected: + void showEvent(QShowEvent *event); + void keyPressEvent(QKeyEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +private: + Ui::TCPStreamDialog *ui; + capture_file *cap_file_; + QMultiMap time_stamp_map_; + double ts_offset_; + bool ts_origin_conn_; + QMap sequence_num_map_; + double seq_offset_; + bool seq_origin_zero_; + struct tcp_graph graph_; + QCPTextElement *title_; + QString stream_desc_; + QCPGraph *base_graph_; // Clickable packets + QCPGraph *tput_graph_; + QCPGraph *goodput_graph_; + QCPGraph *seg_graph_; + QCPErrorBars *seg_eb_; + QCPGraph *ack_graph_; + QCPGraph *sack_graph_; + QCPErrorBars *sack_eb_; + QCPGraph *sack2_graph_; + QCPErrorBars *sack2_eb_; + QCPGraph *rwin_graph_; + QCPGraph *dup_ack_graph_; + QCPGraph *zero_win_graph_; + QCPItemTracer *tracer_; + QRectF axis_bounds_; + guint32 packet_num_; + QTransform y_axis_xfrm_; + bool mouse_drags_; + QRubberBand *rubber_band_; + QPoint rb_origin_; + QMenu ctx_menu_; + + class GraphUpdater { + public: + GraphUpdater(TCPStreamDialog *dialog) : + dialog_(dialog), + graph_update_timer_(NULL), + reset_axes_(false) {} + void triggerUpdate(int timeout, bool reset_axes = false); + void clearPendingUpdate(); + void doUpdate(); + bool hasPendingUpdate() { return graph_update_timer_ != NULL; } + private: + TCPStreamDialog *dialog_; + QTimer *graph_update_timer_; + bool reset_axes_; + }; + friend class GraphUpdater; + GraphUpdater graph_updater_; + + int num_dsegs_; + int num_acks_; + int num_sack_ranges_; + + double ma_window_size_; + + void findStream(); + void fillGraph(bool reset_axes = true, bool set_focus = true); + void showWidgetsForGraphType(); + void zoomAxes(bool in); + void zoomXAxis(bool in); + void zoomYAxis(bool in); + void panAxes(int x_pixels, int y_pixels); + void resetAxes(); + void fillStevens(); + void fillTcptrace(); + void fillThroughput(); + void fillRoundTripTime(); + void fillWindowScale(); + QString streamDescription(); + bool compareHeaders(struct segment *seg); + void toggleTracerStyle(bool force_default = false); + QRectF getZoomRanges(QRect zoom_rect); + +private slots: + void graphClicked(QMouseEvent *event); + void axisClicked(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event); + void mouseMoved(QMouseEvent *event); + void mouseReleased(QMouseEvent *event); + void transformYRange(const QCPRange &y_range1); + void on_buttonBox_accepted(); + void on_graphTypeComboBox_currentIndexChanged(int index); + void on_resetButton_clicked(); + void on_streamNumberSpinBox_valueChanged(int new_stream); + void on_streamNumberSpinBox_editingFinished(); + void on_maWindowSizeSpinBox_valueChanged(double new_ma_size); + void on_maWindowSizeSpinBox_editingFinished(); + void on_selectSACKsCheckBox_stateChanged(int state); + void on_otherDirectionButton_clicked(); + void on_dragRadioButton_toggled(bool checked); + void on_zoomRadioButton_toggled(bool checked); + void on_bySeqNumberCheckBox_stateChanged(int state); + void on_showSegLengthCheckBox_stateChanged(int state); + void on_showThroughputCheckBox_stateChanged(int state); + void on_showGoodputCheckBox_stateChanged(int state); + void on_showRcvWinCheckBox_stateChanged(int state); + void on_showBytesOutCheckBox_stateChanged(int state); + void on_actionZoomIn_triggered(); + void on_actionZoomInX_triggered(); + void on_actionZoomInY_triggered(); + void on_actionZoomOut_triggered(); + void on_actionZoomOutX_triggered(); + void on_actionZoomOutY_triggered(); + void on_actionReset_triggered(); + void on_actionMoveRight10_triggered(); + void on_actionMoveLeft10_triggered(); + void on_actionMoveUp10_triggered(); + void on_actionMoveDown10_triggered(); + void on_actionMoveRight1_triggered(); + void on_actionMoveLeft1_triggered(); + void on_actionMoveUp1_triggered(); + void on_actionMoveDown1_triggered(); + void on_actionNextStream_triggered(); + void on_actionPreviousStream_triggered(); + void on_actionSwitchDirection_triggered(); + void on_actionGoToPacket_triggered(); + void on_actionDragZoom_triggered(); + void on_actionToggleSequenceNumbers_triggered(); + void on_actionToggleTimeOrigin_triggered(); + void on_actionRoundTripTime_triggered(); + void on_actionThroughput_triggered(); + void on_actionStevens_triggered(); + void on_actionTcptrace_triggered(); + void on_actionWindowScaling_triggered(); + void on_buttonBox_helpRequested(); +}; + +#endif // TCP_STREAM_DIALOG_H diff --git a/ui/qt/tcp_stream_dialog.ui b/ui/qt/tcp_stream_dialog.ui new file mode 100644 index 00000000..3e742f65 --- /dev/null +++ b/ui/qt/tcp_stream_dialog.ui @@ -0,0 +1,685 @@ + + + TCPStreamDialog + + + + 0 + 0 + 969 + 640 + + + + Dialog + + + + + + + 0 + 1 + + + + Qt::TabFocus + + + + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + + + true + + + + + + + + + Type + + + + + + + Qt::TabFocus + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + MA Window (s) + + + + + + + + + + Qt::TabFocus + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + + + Select SACKs + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Stream + + + + + + + + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + + + Switch Direction + + + + + + + + + + + Mouse + + + + + + + Qt::TabFocus + + + Drag using the mouse button. + + + drags + + + true + + + mouseButtonGroup + + + + + + + Qt::TabFocus + + + Select using the mouse button. + + + zooms + + + true + + + mouseButtonGroup + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::TabFocus + + + Display Round Trip Time vs Sequence Number + + + RTT By Sequence Number + + + + + + + Qt::TabFocus + + + Display graph of Segment Length vs Time + + + Segment Length + + + + + + + Qt::TabFocus + + + Display graph of Mean Transmitted Bytes vs Time + + + Throughput + + + + + + + Qt::TabFocus + + + Display graph of Mean ACKed Bytes vs Time + + + Goodput + + + + + + + Qt::TabFocus + + + Display graph of Receive Window Size vs Time + + + Rcv Win + + + + + + + Qt::TabFocus + + + Display graph of Outstanding Bytes vs Time + + + Bytes Out + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + + + Reset + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save + + + + + + + Reset Graph + + + Reset the graph to its initial state. + + + 0 + + + + + Zoom In + + + Zoom In + + + + + + + + + Zoom Out + + + Zoom Out + + + - + + + + + Move Up 10 Pixels + + + Move Up 10 Pixels + + + Up + + + + + Move Left 10 Pixels + + + Move Left 10 Pixels + + + Left + + + + + Move Right 10 Pixels + + + Move Right 10 Pixels + + + Right + + + + + Move Down 10 Pixels + + + Move Down 10 Pixels + + + Down + + + + + Move Up 1 Pixel + + + Move Up 1 Pixel + + + Shift+Up + + + + + Move Left 1 Pixel + + + Move Left 1 Pixel + + + Shift+Left + + + + + Move Right 1 Pixel + + + Move Right 1 Pixel + + + Shift+Right + + + + + Move Down 1 Pixel + + + Move Down 1 Pixel + + + Shift+Down + + + + + Next Stream + + + Go to the next stream in the capture + + + PgUp + + + + + Previous Stream + + + Go to the previous stream in the capture + + + PgDown + + + + + Switch Direction + + + Switch direction (swap TCP endpoints) + + + D + + + + + Go To Packet Under Cursor + + + Go to packet currently under the cursor + + + G + + + + + Drag / Zoom + + + Toggle mouse drag / zoom behavior + + + Z + + + + + Relative / Absolute Sequence Numbers + + + Toggle relative / absolute sequence numbers + + + S + + + + + Capture / Session Time Origin + + + Toggle capture / session time origin + + + T + + + + + Crosshairs + + + Toggle crosshairs + + + Space + + + + + Round Trip Time + + + Switch to the Round Trip Time graph + + + 1 + + + + + Throughput + + + Switch to the Throughput graph + + + 2 + + + + + Time / Sequence (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + + + 3 + + + + + Window Scaling + + + Switch to the Window Scaling graph + + + 5 + + + + + Time / Sequence (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + + + 4 + + + + + Zoom In X Axis + + + Zoom In X Axis + + + X + + + + + Zoom Out X Axis + + + Zoom Out X Axis + + + Shift+X + + + + + Zoom In Y Axis + + + Zoom In Y Axis + + + Y + + + + + Zoom Out Y Axis + + + Zoom Out Y Axis + + + Shift+Y + + + + + + QCustomPlot + QWidget +
widgets/qcustomplot.h
+ 1 +
+
+ + + + buttonBox + rejected() + TCPStreamDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + + +
diff --git a/ui/qt/time_shift_dialog.cpp b/ui/qt/time_shift_dialog.cpp new file mode 100644 index 00000000..f1b325f0 --- /dev/null +++ b/ui/qt/time_shift_dialog.cpp @@ -0,0 +1,266 @@ +/* time_shift_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "time_shift_dialog.h" +#include + +#include "main_application.h" + +#include +#include + +#include + +TimeShiftDialog::TimeShiftDialog(QWidget *parent, capture_file *cf) : + QDialog(parent), + ts_ui_(new Ui::TimeShiftDialog), + cap_file_(cf), + apply_button_(NULL) +{ + ts_ui_->setupUi(this); + setWindowTitle(mainApp->windowTitleString(tr("Time Shift"))); + apply_button_ = ts_ui_->buttonBox->button(QDialogButtonBox::Apply); + apply_button_->setDefault(true); + connect(apply_button_, &QPushButton::clicked, this, &TimeShiftDialog::applyTimeShift); + + QStyleOption style_opt; + int rb_label_offset = ts_ui_->shiftAllButton->style()->subElementRect(QStyle::SE_RadioButtonContents, &style_opt).left(); + int cb_label_offset = ts_ui_->shiftAllButton->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left(); + setStyleSheet(QString( + "QCheckBox#setTwoCheckBox {" + " margin-left: %1px;" + "}" + "QLabel#extrapolateLabel {" + " margin-left: %2px;" + "}" + ) + .arg(rb_label_offset) + .arg(rb_label_offset + cb_label_offset) + ); + + if (cap_file_) { + if (cap_file_->current_frame) { + ts_ui_->setOneFrameLineEdit->setText(QString::number(cap_file_->current_frame->num)); + } else { + ts_ui_->setOneFrameLineEdit->setText(QString::number(cap_file_->first_displayed)); + } + ts_ui_->setTwoFrameLineEdit->setText(QString::number(cap_file_->last_displayed)); + } + + ts_ui_->shiftAllButton->setChecked(true); + ts_ui_->setTwoCheckBox->setChecked(false); + enableWidgets(); +} + +TimeShiftDialog::~TimeShiftDialog() +{ + delete ts_ui_; +} + +void TimeShiftDialog::enableWidgets() +{ + bool enable_two = ts_ui_->setOneButton->isChecked(); + bool enable_apply = false; + + ts_ui_->setTwoCheckBox->setEnabled(enable_two); + ts_ui_->setTwoFrameLineEdit->setEnabled(enable_two); + ts_ui_->setTwoToLabel->setEnabled(enable_two); + ts_ui_->setTwoTimeLineEdit->setEnabled(enable_two); + ts_ui_->extrapolateLabel->setEnabled(enable_two && ts_ui_->setTwoCheckBox->isChecked()); + + if (ts_ui_->shiftAllButton->isChecked()) { + if (ts_ui_->shiftAllTimeLineEdit->syntaxState() == SyntaxLineEdit::Valid) + enable_apply = true; + } else if (ts_ui_->setOneButton->isChecked()) { + bool set_two_valid = false; + if (ts_ui_->setTwoCheckBox->isChecked()) { + if (ts_ui_->setTwoFrameLineEdit->syntaxState() == SyntaxLineEdit::Valid && + ts_ui_->setTwoTimeLineEdit->syntaxState() == SyntaxLineEdit::Valid) { + set_two_valid = true; + } + } else { + set_two_valid = true; + } + if (set_two_valid && + ts_ui_->setOneFrameLineEdit->syntaxState() == SyntaxLineEdit::Valid && + ts_ui_->setOneTimeLineEdit->syntaxState() == SyntaxLineEdit::Valid) { + enable_apply = true; + } + } else if (ts_ui_->unshiftAllButton->isChecked()) { + enable_apply = true; + } + + if (syntax_err_.isEmpty()) { + ts_ui_->errorLabel->clear(); + ts_ui_->errorLabel->setStyleSheet(" QLabel { margin-top: 0.5em; }"); + } else { + ts_ui_->errorLabel->setText(syntax_err_); + ts_ui_->errorLabel->setStyleSheet(QString( + "QLabel {" + " margin-top: 0.5em;" + " background-color: %2;" + "}" + ) + .arg(ColorUtils::warningBackground().name()) + ); + } + apply_button_->setEnabled(enable_apply); +} + +void TimeShiftDialog::checkFrameNumber(SyntaxLineEdit &frame_le) +{ + bool frame_valid; + guint frame_num = frame_le.text().toUInt(&frame_valid); + + syntax_err_.clear(); + if (frame_le.text().isEmpty()) { + frame_le.setSyntaxState(SyntaxLineEdit::Empty); + } else if (!frame_valid || !cap_file_ || frame_num < 1 || frame_num > cap_file_->count) { + frame_le.setSyntaxState(SyntaxLineEdit::Invalid); + if (cap_file_) { + syntax_err_ = QString(tr("Frame numbers must be between 1 and %1.").arg(cap_file_->count)); + } else { + syntax_err_ = tr("Invalid frame number."); + } + } else { + frame_le.setSyntaxState(SyntaxLineEdit::Valid); + } +} + +void TimeShiftDialog::checkDateTime(SyntaxLineEdit &time_le) +{ + int Y, M, D, h, m; + long double s; + const gchar *err_str; + + syntax_err_.clear(); + if (time_le.text().isEmpty()) { + time_le.setSyntaxState(SyntaxLineEdit::Empty); + } else if ((err_str = time_string_parse(time_le.text().toUtf8().constData(), + &Y, &M, &D, NULL, &h, &m, &s)) != NULL) { + syntax_err_ = err_str; + time_le.setSyntaxState(SyntaxLineEdit::Invalid); + } else { + time_le.setSyntaxState(SyntaxLineEdit::Valid); + } +} + +void TimeShiftDialog::on_shiftAllButton_toggled(bool) +{ + enableWidgets(); +} + +void TimeShiftDialog::on_setOneButton_toggled(bool) +{ + enableWidgets(); +} + +void TimeShiftDialog::on_unshiftAllButton_toggled(bool) +{ + enableWidgets(); +} + +void TimeShiftDialog::on_setTwoCheckBox_toggled(bool) +{ + enableWidgets(); +} + +void TimeShiftDialog::on_shiftAllTimeLineEdit_textChanged(const QString &sa_text) +{ + int h, m; + long double s; + gboolean neg; + const gchar *err_str; + + syntax_err_.clear(); + if (sa_text.isEmpty()) { + ts_ui_->shiftAllTimeLineEdit->setSyntaxState(SyntaxLineEdit::Empty); + } else if ((err_str = time_string_parse(sa_text.toUtf8().constData(), + NULL, NULL, NULL, &neg, &h, &m, &s)) != NULL) { + syntax_err_ = err_str; + ts_ui_->shiftAllTimeLineEdit->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + ts_ui_->shiftAllTimeLineEdit->setSyntaxState(SyntaxLineEdit::Valid); + } + ts_ui_->shiftAllButton->setChecked(true); + enableWidgets(); +} + +void TimeShiftDialog::on_setOneFrameLineEdit_textChanged(const QString &) +{ + checkFrameNumber(*ts_ui_->setOneFrameLineEdit); + ts_ui_->setOneButton->setChecked(true); + enableWidgets(); +} +void TimeShiftDialog::on_setOneTimeLineEdit_textChanged(const QString &) +{ + checkDateTime(*ts_ui_->setOneTimeLineEdit); + ts_ui_->setOneButton->setChecked(true); + enableWidgets(); +} + +void TimeShiftDialog::on_setTwoFrameLineEdit_textChanged(const QString &) +{ + checkFrameNumber(*ts_ui_->setTwoFrameLineEdit); + if (ts_ui_->setTwoCheckBox->isEnabled()) + ts_ui_->setTwoCheckBox->setChecked(true); + enableWidgets(); +} + +void TimeShiftDialog::on_setTwoTimeLineEdit_textChanged(const QString &) +{ + checkDateTime(*ts_ui_->setTwoTimeLineEdit); + if (ts_ui_->setTwoCheckBox->isEnabled()) + ts_ui_->setTwoCheckBox->setChecked(true); + enableWidgets(); +} + +void TimeShiftDialog::applyTimeShift() +{ + const gchar *err_str = NULL; + + if (!cap_file_ || cap_file_->state == FILE_CLOSED || cap_file_->state == FILE_READ_PENDING) return; + + syntax_err_.clear(); + if (cap_file_->state == FILE_READ_IN_PROGRESS) { + syntax_err_ = tr("Time shifting is not available while capturing packets."); + } else if (ts_ui_->shiftAllButton->isChecked()) { + err_str = time_shift_all(cap_file_, + ts_ui_->shiftAllTimeLineEdit->text().toUtf8().constData()); + } else if (ts_ui_->setOneButton->isChecked()) { + if (!ts_ui_->setTwoCheckBox->isChecked()) { + err_str = time_shift_settime(cap_file_, + ts_ui_->setOneFrameLineEdit->text().toUInt(), + ts_ui_->setOneTimeLineEdit->text().toUtf8().constData() + ); + } else { + err_str = time_shift_adjtime(cap_file_, + ts_ui_->setOneFrameLineEdit->text().toUInt(), + ts_ui_->setOneTimeLineEdit->text().toUtf8().constData(), + ts_ui_->setTwoFrameLineEdit->text().toUInt(), + ts_ui_->setTwoTimeLineEdit->text().toUtf8().constData() + ); + } + } else if (ts_ui_->unshiftAllButton->isChecked()) { + err_str = time_shift_undo(cap_file_); + } + + if (err_str) { + syntax_err_ = err_str; + } else { + emit timeShifted(); + } + + enableWidgets(); +} + +void TimeShiftDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_TIME_SHIFT_DIALOG); +} diff --git a/ui/qt/time_shift_dialog.h b/ui/qt/time_shift_dialog.h new file mode 100644 index 00000000..65164946 --- /dev/null +++ b/ui/qt/time_shift_dialog.h @@ -0,0 +1,66 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TIME_SHIFT_DIALOG_H +#define TIME_SHIFT_DIALOG_H + +#include + +#include + +#include "cfile.h" + +#include + +#include +#include + +namespace Ui { +class TimeShiftDialog; +} + +class TimeShiftDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TimeShiftDialog(QWidget *parent = 0, capture_file *cf = NULL); + ~TimeShiftDialog(); + +public slots: + void setCaptureFile(capture_file *cf) { cap_file_ = cf; } + +signals: + void timeShifted(); + +private: + Ui::TimeShiftDialog *ts_ui_; + capture_file *cap_file_; + QPushButton *apply_button_; + QString syntax_err_; + + void enableWidgets(); + void checkFrameNumber(SyntaxLineEdit &frame_le); + void checkDateTime(SyntaxLineEdit &time_le); + +private slots: + void on_shiftAllButton_toggled(bool checked); + void on_setOneButton_toggled(bool checked); + void on_unshiftAllButton_toggled(bool checked); + void on_setTwoCheckBox_toggled(bool checked); + void on_shiftAllTimeLineEdit_textChanged(const QString &sa_text); + void on_setOneTimeLineEdit_textChanged(const QString &so_text); + void on_setOneFrameLineEdit_textChanged(const QString &frame_text); + void on_setTwoFrameLineEdit_textChanged(const QString &frame_text); + void on_setTwoTimeLineEdit_textChanged(const QString &st_text); + void applyTimeShift(); + void on_buttonBox_helpRequested(); +}; + +#endif // TIME_SHIFT_DIALOG_H diff --git a/ui/qt/time_shift_dialog.ui b/ui/qt/time_shift_dialog.ui new file mode 100644 index 00000000..a92bed32 --- /dev/null +++ b/ui/qt/time_shift_dialog.ui @@ -0,0 +1,253 @@ + + + TimeShiftDialog + + + + 0 + 0 + 549 + 257 + + + + + 0 + 0 + + + + + + + + + Shift all packets by + + + true + + + + + + + + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Set the time for packet + + + + + + + + + + to + + + + + + + + 1 + 0 + + + + + + + + + + + + true + + + …then set packet + + + + + + + + + + to + + + + + + + + 1 + 0 + + + + + + + + Qt::Horizontal + + + + 28 + 20 + + + + + + + + + + + + and extrapolate the time for all other packets + + + + + + + Qt::Horizontal + + + + 60 + 20 + + + + + + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + + + + + + + Undo all shifts + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + buttonBox + unshiftAllButton + + + + SyntaxLineEdit + QLineEdit +
widgets/syntax_line_edit.h
+
+
+ + + + buttonBox + accepted() + TimeShiftDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TimeShiftDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/tlskeylog_launcher_dialog.cpp b/ui/qt/tlskeylog_launcher_dialog.cpp new file mode 100644 index 00000000..82ad8b26 --- /dev/null +++ b/ui/qt/tlskeylog_launcher_dialog.cpp @@ -0,0 +1,225 @@ +/* + * tlskeylog_launcher_dialog.c + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "tlskeylog_launcher_dialog.h" +#include + +#include "main_application.h" +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include "wsutil/report_message.h" +#include +#include +#include + +TLSKeylogDialog::TLSKeylogDialog(QWidget &parent) : + QDialog(&parent), + ui(new Ui::TLSKeylogDialog), + pref_tls_keylog_(nullptr), + pref_tlskeylog_command_(nullptr) +{ + ui->setupUi(this); + + QString title(tr("Launch application with SSLKEYLOGFILE")); + setWindowTitle(mainApp->windowTitleString(title)); + + QPushButton *launch_button = ui->buttonBox->addButton(tr("Launch"), QDialogButtonBox::ActionRole); + launch_button->setDefault(true); + connect(launch_button, &QPushButton::clicked, this, &TLSKeylogDialog::on_launchActivated); + + QPushButton *save_button = ui->buttonBox->addButton(tr("Save"), QDialogButtonBox::ApplyRole); + connect(save_button, &QPushButton::clicked, this, &TLSKeylogDialog::on_saveActivated); + + QPushButton *reset_button = ui->buttonBox->button(QDialogButtonBox::Reset); + connect(reset_button, &QPushButton::clicked, this, &TLSKeylogDialog::on_resetActivated); + + connect(ui->keylogPushButton, &QPushButton::clicked, this, &TLSKeylogDialog::on_browseKeylogPath); + connect(ui->programPushbutton, &QPushButton::clicked, this, &TLSKeylogDialog::on_browseProgramPath); + + tls_module_ = prefs_find_module("tls"); + if (tls_module_) { + pref_tls_keylog_ = prefs_find_preference(tls_module_, "keylog_file"); + if (pref_tls_keylog_) { + const char *path = prefs_get_string_value(pref_tls_keylog_, pref_current); + if (path && *path) { + ui->keylogLineEdit->setText(QString(path)); + } + } + } + + gui_module_ = prefs_find_module("gui"); + ws_assert(gui_module_); + pref_tlskeylog_command_ = prefs_find_preference(gui_module_, "tlskeylog_command"); + ws_assert(pref_tlskeylog_command_); + const char *path = prefs_get_string_value(pref_tlskeylog_command_, pref_current); + if (path && *path) { + ui->commandLineEdit->setText(QString(path)); + } +} + +TLSKeylogDialog::~TLSKeylogDialog() +{ + delete ui; +} + +void TLSKeylogDialog::on_saveActivated() +{ + int changed; + + if (pref_tls_keylog_) { + QString keylog = ui->keylogLineEdit->text(); + changed = prefs_set_string_value(pref_tls_keylog_, qUtf8Printable(keylog), pref_current); + tls_module_->prefs_changed_flags |= changed; + } + + QString command = ui->commandLineEdit->text(); + changed = prefs_set_string_value(pref_tlskeylog_command_, qUtf8Printable(command), pref_current); + gui_module_->prefs_changed_flags |= changed; + + prefs_main_write(); +} + +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) +// Splits the string \a command into a list of tokens, and returns the list. +// +// Tokens with spaces can be surrounded by double quotes; three +// consecutive double quotes represent the quote character itself. +// +// Copied from Qt 5.15.2 +static QStringList splitCommand(QStringView command) +{ + QStringList args; + QString tmp; + int quoteCount = 0; + bool inQuote = false; + + // handle quoting. tokens can be surrounded by double quotes + // "hello world". three consecutive double quotes represent + // the quote character itself. + for (int i = 0; i < command.size(); ++i) { + if (command.at(i) == QLatin1Char('"')) { + ++quoteCount; + if (quoteCount == 3) { + // third consecutive quote + quoteCount = 0; + tmp += command.at(i); + } + continue; + } + if (quoteCount) { + if (quoteCount == 1) + inQuote = !inQuote; + quoteCount = 0; + } + if (!inQuote && command.at(i).isSpace()) { + if (!tmp.isEmpty()) { + args += tmp; + tmp.clear(); + } + } else { + tmp += command.at(i); + } + } + if (!tmp.isEmpty()) + args += tmp; + + return args; +} +#endif + +void TLSKeylogDialog::on_launchActivated() +{ + QProcess externalProcess; + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + + QString keylog = ui->keylogLineEdit->text(); + if (keylog.isEmpty()) + return; + QString command = ui->commandLineEdit->text(); + if (command.isEmpty()) + return; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + QStringList commandArgs = QProcess::splitCommand(command); +#else + QStringList commandArgs = splitCommand(command); +#endif + if (commandArgs.isEmpty()) + return; + + // This should work with command lines such as: + // - firefox + // - firefox -profile /tmp/ff + // - /usr/bin/firefox -profile /tmp/ff + // - "C:\Program Files\Mozilla Firefox\firefox.exe" + // - "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --user-data-dir=/tmp/cr + externalProcess.setProgram(commandArgs.takeFirst()); + externalProcess.setArguments(commandArgs); + + env.insert("SSLKEYLOGFILE", keylog); + externalProcess.setProcessEnvironment(env); + bool ok = externalProcess.startDetached(); + if (ok) { + return; + } + + QString error = externalProcess.errorString(); + if (!error.isEmpty()) + report_failure("Error launching command: %s", qUtf8Printable(error)); + else + report_failure("Error launching command"); +} + +// Restore user preferences +void TLSKeylogDialog::on_resetActivated() +{ + QString keylog_path; + QString tlskeylog_command; + + if (pref_tls_keylog_) { + keylog_path = prefs_get_string_value(pref_tls_keylog_, pref_current); + ui->keylogLineEdit->setText(keylog_path); + } + tlskeylog_command = prefs_get_string_value(pref_tlskeylog_command_, pref_current); + ui->commandLineEdit->setText(tlskeylog_command); +} + +void TLSKeylogDialog::on_browseKeylogPath() +{ + QString caption = mainApp->windowTitleString(tr("TLS Keylog file")); + QString file_name = WiresharkFileDialog::getSaveFileName(this, caption, + mainApp->openDialogInitialDir().path()); + if (!file_name.isEmpty()) { + ui->keylogLineEdit->setText(file_name); + } +} + +void TLSKeylogDialog::on_browseProgramPath() +{ + QString caption = mainApp->windowTitleString(tr("Program to start with SSLKEYLOGFILE")); + QString file_name = WiresharkFileDialog::getOpenFileName(this, caption); + if (file_name.isEmpty()) { + return; + } +#ifdef Q_OS_MAC + if (file_name.endsWith(".app")) { + QString base_name = QFileInfo(file_name).baseName(); + QString bundle_exe_name = QString("%1/Contents/MacOS/%2").arg(file_name, base_name); + if (QFile::exists(bundle_exe_name)) { + file_name = bundle_exe_name; + } + } +#endif + // If the program contains spaces, quote it to ensure it is not broken up + // into multiple arguments. + if (file_name.contains(" ")) { + file_name = QString("\"%1\"").arg(file_name); + } + ui->commandLineEdit->setText(file_name); +} diff --git a/ui/qt/tlskeylog_launcher_dialog.h b/ui/qt/tlskeylog_launcher_dialog.h new file mode 100644 index 00000000..10040b92 --- /dev/null +++ b/ui/qt/tlskeylog_launcher_dialog.h @@ -0,0 +1,48 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TLSKEYLOG_DIALOG_H +#define TLSKEYLOG_DIALOG_H + +#include +#include +#include + +#include + +namespace Ui { +class TLSKeylogDialog; +} + +class TLSKeylogDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TLSKeylogDialog(QWidget &parent); + ~TLSKeylogDialog(); + +private slots: + void on_launchActivated(); + void on_saveActivated(); + void on_resetActivated(); + void on_browseKeylogPath(); + void on_browseProgramPath(); + +private: + Ui::TLSKeylogDialog *ui; + + module_t *tls_module_; + pref_t *pref_tls_keylog_; + + module_t *gui_module_; + pref_t *pref_tlskeylog_command_; +}; + +#endif // TLSKEYLOG_DIALOG_H diff --git a/ui/qt/tlskeylog_launcher_dialog.ui b/ui/qt/tlskeylog_launcher_dialog.ui new file mode 100644 index 00000000..02202557 --- /dev/null +++ b/ui/qt/tlskeylog_launcher_dialog.ui @@ -0,0 +1,217 @@ + + + TLSKeylogDialog + + + + 0 + 0 + 640 + 380 + + + + Dialog + + + + + + + + + + + + + Browse… + + + + + + + + + + + Command line + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + + + + + + Browse… + + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Reset + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Maximum + + + + 20 + 10 + + + + + + + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + true + + + + + + + + + Qt::Vertical + + + QSizePolicy::Maximum + + + + 20 + 10 + + + + + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + + + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + + + + + + Qt::Horizontal + + + + + + + + + buttonBox + accepted() + TLSKeylogDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TLSKeylogDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/traffic_table_dialog.cpp b/ui/qt/traffic_table_dialog.cpp new file mode 100644 index 00000000..2040eedf --- /dev/null +++ b/ui/qt/traffic_table_dialog.cpp @@ -0,0 +1,163 @@ +/* traffic_table_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "traffic_table_dialog.h" +#include + +#include +#include + +#include "ui/recent.h" + +#include "progress_frame.h" +#include "main_application.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TrafficTableDialog::TrafficTableDialog(QWidget &parent, CaptureFile &cf, const QString &table_name) : + WiresharkDialog(parent, cf), + ui(new Ui::TrafficTableDialog) +{ + ui->setupUi(this); + loadGeometry(parent.width(), parent.height() * 3 / 4); + + ui->absoluteTimeCheckBox->hide(); + setWindowSubtitle(QString("%1s").arg(table_name)); + ui->grpSettings->setTitle(QString("%1 Settings").arg(table_name)); + + copy_bt_ = buttonBox()->addButton(tr("Copy"), QDialogButtonBox::ActionRole); + copy_bt_->setMenu(ui->trafficTab->createCopyMenu(copy_bt_)); + + if (cf.displayFilter().length() > 0) { + ui->displayFilterCheckBox->setChecked(true); + ui->trafficTab->setFilter(cf.displayFilter()); + } + + ui->trafficTab->setFocus(); + ui->trafficTab->useNanosecondTimestamps(cf.timestampPrecision() == WTAP_TSPREC_NSEC); + connect(ui->displayFilterCheckBox, &QCheckBox::toggled, this, &TrafficTableDialog::displayFilterCheckBoxToggled); + connect(ui->trafficList, &TrafficTypesList::protocolsChanged, ui->trafficTab, &TrafficTab::setOpenTabs); + connect(ui->trafficTab, &TrafficTab::tabsChanged, ui->trafficList, &TrafficTypesList::selectProtocols); + + connect(mainApp, SIGNAL(addressResolutionChanged()), this, SLOT(currentTabChanged())); + connect(ui->trafficTab, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged())); + connect(&cap_file_, SIGNAL(captureEvent(CaptureEvent)), this, SLOT(captureEvent(CaptureEvent))); + + connect(ui->absoluteTimeCheckBox, &QCheckBox::toggled, ui->trafficTab, &TrafficTab::useAbsoluteTime); + connect(ui->trafficTab, &TrafficTab::retapRequired, &cap_file_, &CaptureFile::delayedRetapPackets); + + connect(ui->trafficListSearch, &QLineEdit::textChanged, ui->trafficList, &TrafficTypesList::filterList); + connect(ui->trafficList, &TrafficTypesList::clearFilterList, ui->trafficListSearch, &QLineEdit::clear); + + QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close); + if (close_bt) + close_bt->setDefault(true); + + addProgressFrame(&parent); +} + +TrafficTableDialog::~TrafficTableDialog() +{ + delete ui; +} + +void TrafficTableDialog::addProgressFrame(QObject *parent) +{ + ProgressFrame::addToButtonBox(ui->buttonBox, parent); +} + +QDialogButtonBox *TrafficTableDialog::buttonBox() const +{ + return ui->btnBoxSettings; +} + +QCheckBox *TrafficTableDialog::displayFilterCheckBox() const +{ + return ui->displayFilterCheckBox; +} + +QCheckBox *TrafficTableDialog::absoluteTimeCheckBox() const +{ + return ui->absoluteTimeCheckBox; +} + +TrafficTab *TrafficTableDialog::trafficTab() const +{ + return ui->trafficTab; +} + +TrafficTypesList *TrafficTableDialog::trafficList() const +{ + return ui->trafficList; +} + +void TrafficTableDialog::currentTabChanged() +{ + bool has_resolution = ui->trafficTab->hasNameResolution(); + copy_bt_->setMenu(ui->trafficTab->createCopyMenu(copy_bt_)); + + ui->nameResolutionCheckBox->setEnabled(has_resolution); + if (! has_resolution) { + ui->nameResolutionCheckBox->setChecked(false); + ui->trafficTab->setNameResolution(false); + } +} + +void TrafficTableDialog::on_nameResolutionCheckBox_toggled(bool checked) +{ + ui->trafficTab->setNameResolution(checked); +} + +void TrafficTableDialog::displayFilterCheckBoxToggled(bool checked) +{ + if (!cap_file_.isValid()) { + return; + } + + if (checked) + trafficTab()->setFilter(cap_file_.displayFilter()); + else + trafficTab()->setFilter(QString()); + + cap_file_.retapPackets(); +} + +void TrafficTableDialog::captureEvent(CaptureEvent e) +{ + if (e.captureContext() == CaptureEvent::Retap) + { + switch (e.eventType()) + { + case CaptureEvent::Started: + ui->displayFilterCheckBox->setEnabled(false); + break; + case CaptureEvent::Finished: + ui->displayFilterCheckBox->setEnabled(true); + break; + default: + break; + } + } + +} diff --git a/ui/qt/traffic_table_dialog.h b/ui/qt/traffic_table_dialog.h new file mode 100644 index 00000000..b742a52c --- /dev/null +++ b/ui/qt/traffic_table_dialog.h @@ -0,0 +1,84 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TRAFFIC_TABLE_DIALOG_H +#define TRAFFIC_TABLE_DIALOG_H + +#include + +#include "file.h" + +#include "epan/conversation_table.h" + +#include "epan/follow.h" + +#include "capture_file.h" +#include "filter_action.h" +#include "wireshark_dialog.h" + +#include +#include + +class QCheckBox; +class QDialogButtonBox; +class QPushButton; +class QTabWidget; +class QTreeWidget; +class TrafficTab; +class TrafficTypesList; + +namespace Ui { +class TrafficTableDialog; +} + +class TrafficTableDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + /** Create a new conversation window. + * + * @param parent Parent widget. + * @param cf Capture file. No statistics will be calculated if this is NULL. + * @param table_name If valid, add this protocol and bring it to the front. + */ + explicit TrafficTableDialog(QWidget &parent, CaptureFile &cf, const QString &table_name = tr("Unknown")); + ~TrafficTableDialog(); + +signals: + void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + void openFollowStreamDialog(int proto_id); + void openTcpStreamGraph(int graph_type); + +protected: + Ui::TrafficTableDialog *ui; + + QPushButton *copy_bt_; + + void addProgressFrame(QObject *parent); + + // UI getters + QDialogButtonBox *buttonBox() const; + QCheckBox *displayFilterCheckBox() const; + QCheckBox *absoluteTimeCheckBox() const; + TrafficTab *trafficTab() const; + TrafficTypesList *trafficList() const; + +protected slots: + virtual void currentTabChanged(); + +private slots: + void on_nameResolutionCheckBox_toggled(bool checked); + void displayFilterCheckBoxToggled(bool checked); + void captureEvent(CaptureEvent e); + + virtual void on_buttonBox_helpRequested() = 0; +}; + +#endif // TRAFFIC_TABLE_DIALOG_H diff --git a/ui/qt/traffic_table_dialog.ui b/ui/qt/traffic_table_dialog.ui new file mode 100644 index 00000000..901df7bf --- /dev/null +++ b/ui/qt/traffic_table_dialog.ui @@ -0,0 +1,177 @@ + + + TrafficTableDialog + + + + 0 + 0 + 680 + 475 + + + + + + + + + + 210 + 16777215 + + + + + + + GroupBox + + + + + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + + + Name resolution + + + + + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + + + Absolute start time + + + + + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + Limit to display filter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Qt::Vertical + + + QDialogButtonBox::NoButton + + + + + + + + + + + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + + + + + + Filter list for specific type + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + + TrafficTab + QTabWidget +
ui/qt/widgets/traffic_tab.h
+ 1 +
+ + TrafficTypesList + QTreeView +
ui/qt/widgets/traffic_types_list.h
+
+
+ + + + buttonBox + accepted() + TrafficTableDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TrafficTableDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/uat_dialog.cpp b/ui/qt/uat_dialog.cpp new file mode 100644 index 00000000..6f9d2a7c --- /dev/null +++ b/ui/qt/uat_dialog.cpp @@ -0,0 +1,413 @@ +/* uat_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "uat_dialog.h" +#include +#include "main_application.h" + +#include "epan/strutil.h" +#include "epan/uat-int.h" +#include "ui/help_url.h" +#include + +#include +#include + +#include +#include +#include + +#include + +// NOTE currently uat setter is always invoked in UatModel even if the uat checker fails. + +UatDialog::UatDialog(QWidget *parent, epan_uat *uat) : + GeometryStateDialog(parent), + ui(new Ui::UatDialog), + uat_model_(NULL), + uat_delegate_(NULL), + uat_(uat) +{ + ui->setupUi(this); + if (uat) loadGeometry(0, 0, uat->name); + + ok_button_ = ui->buttonBox->button(QDialogButtonBox::Ok); + help_button_ = ui->buttonBox->button(QDialogButtonBox::Help); + + ui->newToolButton->setStockIcon("list-add"); + ui->deleteToolButton->setStockIcon("list-remove"); + ui->copyToolButton->setStockIcon("list-copy"); + ui->moveUpToolButton->setStockIcon("list-move-up"); + ui->moveDownToolButton->setStockIcon("list-move-down"); + ui->clearToolButton->setStockIcon("list-clear"); + +#ifdef Q_OS_MAC + ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->moveUpToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->moveDownToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->clearToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + setUat(uat); + + // FIXME: this prevents the columns from being resized, even if the text + // within a combobox needs more space (e.g. in the USER DLT settings). For + // very long filenames in the TLS RSA keys dialog, it also results in a + // vertical scrollbar. Maybe remove this since the editor is not limited to + // the column width (and overlays other fields if more width is needed)? + ui->uatTreeView->header()->setSectionResizeMode(QHeaderView::Interactive); + + // start editing as soon as the field is selected or when typing starts + ui->uatTreeView->setEditTriggers(ui->uatTreeView->editTriggers() | + QAbstractItemView::CurrentChanged | QAbstractItemView::AnyKeyPressed); + + // Need to add uat_move or uat_insert to the UAT API. + ui->uatTreeView->setDragEnabled(false); +// qDebug() << "FIX Add drag reordering to UAT dialog"; + + // Do NOT start editing the first column for the first item + ui->uatTreeView->setCurrentIndex(QModelIndex()); +} + +UatDialog::~UatDialog() +{ + delete ui; + delete uat_delegate_; + delete uat_model_; +} + +void UatDialog::setUat(epan_uat *uat) +{ + QString title(tr("Unknown User Accessible Table")); + + uat_ = uat; + + ui->pathLabel->clear(); + ui->pathLabel->setEnabled(false); + help_button_->setEnabled(false); + + if (uat_) { + if (uat_->name) { + title = uat_->name; + } + + if (uat->from_profile) { + CopyFromProfileButton * copy_button = new CopyFromProfileButton(this, uat->filename); + ui->buttonBox->addButton(copy_button, QDialogButtonBox::ActionRole); + connect(copy_button, &CopyFromProfileButton::copyProfile, this, &UatDialog::copyFromProfile); + } + + QString abs_path = gchar_free_to_qstring(uat_get_actual_filename(uat_, FALSE)); + if (abs_path.length() > 0) { + ui->pathLabel->setText(abs_path); + ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString()); + ui->pathLabel->setToolTip(tr("Open ") + uat->filename); + } else { + ui->pathLabel->setText(uat_->filename); + } + ui->pathLabel->setEnabled(true); + + uat_model_ = new UatModel(NULL, uat); + uat_delegate_ = new UatDelegate; + ui->uatTreeView->setModel(uat_model_); + ui->uatTreeView->setItemDelegate(uat_delegate_); + resizeColumns(); + ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0); + + connect(uat_model_, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(modelDataChanged(QModelIndex))); + connect(uat_model_, SIGNAL(rowsRemoved(QModelIndex, int, int)), + this, SLOT(modelRowsRemoved())); + connect(uat_model_, SIGNAL(modelReset()), this, SLOT(modelRowsReset())); + + ok_button_->setEnabled(!uat_model_->hasErrors()); + + if (uat_->help && strlen(uat_->help) > 0) { + help_button_->setEnabled(true); + } + + connect(this, SIGNAL(rejected()), this, SLOT(rejectChanges())); + connect(this, SIGNAL(accepted()), this, SLOT(acceptChanges())); + } + + setWindowTitle(title); +} + +void UatDialog::copyFromProfile(QString filename) +{ + gchar *err = NULL; + if (uat_load(uat_, filename.toUtf8().constData(), &err)) { + uat_->changed = TRUE; + uat_model_->reloadUat(); + } else { + report_failure("Error while loading %s: %s", uat_->name, err); + g_free(err); + } +} + +// Invoked when a field in the model changes (e.g. by closing the editor) +void UatDialog::modelDataChanged(const QModelIndex &topLeft) +{ + checkForErrorHint(topLeft, QModelIndex()); + ok_button_->setEnabled(!uat_model_->hasErrors()); + resizeColumns(); +} + +// Invoked after a row has been removed from the model. +void UatDialog::modelRowsRemoved() +{ + const QModelIndex ¤t = ui->uatTreeView->currentIndex(); + + // Because currentItemChanged() is called before the row is removed from the model + // we also need to check for button enabling here. + if (current.isValid()) { + ui->moveUpToolButton->setEnabled(current.row() != 0); + ui->moveDownToolButton->setEnabled(current.row() != (uat_model_->rowCount() - 1)); + } else { + ui->moveUpToolButton->setEnabled(false); + ui->moveDownToolButton->setEnabled(false); + } + + checkForErrorHint(current, QModelIndex()); + ok_button_->setEnabled(!uat_model_->hasErrors()); +} + +void UatDialog::modelRowsReset() +{ + ui->deleteToolButton->setEnabled(false); + ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0); + ui->copyToolButton->setEnabled(false); + ui->moveUpToolButton->setEnabled(false); + ui->moveDownToolButton->setEnabled(false); +} + + +// Invoked when a different field is selected. Note: when selecting a different +// field after editing, this event is triggered after modelDataChanged. +void UatDialog::on_uatTreeView_currentItemChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.isValid()) { + ui->deleteToolButton->setEnabled(true); + ui->clearToolButton->setEnabled(true); + ui->copyToolButton->setEnabled(true); + ui->moveUpToolButton->setEnabled(current.row() != 0); + ui->moveDownToolButton->setEnabled(current.row() != (uat_model_->rowCount() - 1)); + } else { + ui->deleteToolButton->setEnabled(false); + ui->clearToolButton->setEnabled(false); + ui->copyToolButton->setEnabled(false); + ui->moveUpToolButton->setEnabled(false); + ui->moveDownToolButton->setEnabled(false); + } + + checkForErrorHint(current, previous); +} + +// If the current field has errors, show them. +// Otherwise if the row has not changed, but the previous field has errors, show them. +// Otherwise pick the first error in the current row. +// Otherwise show the error from the previous field (if any). +// Otherwise clear the error hint. +void UatDialog::checkForErrorHint(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.isValid()) { + if (trySetErrorHintFromField(current)) { + return; + } + + const int row = current.row(); + if (row == previous.row() && trySetErrorHintFromField(previous)) { + return; + } + + for (int i = 0; i < uat_model_->columnCount(); i++) { + if (trySetErrorHintFromField(uat_model_->index(row, i))) { + return; + } + } + } + + if (previous.isValid()) { + if (trySetErrorHintFromField(previous)) { + return; + } + } + + ui->hintLabel->clear(); +} + +bool UatDialog::trySetErrorHintFromField(const QModelIndex &index) +{ + const QVariant &data = uat_model_->data(index, Qt::UserRole + 1); + if (!data.isNull()) { + // use HTML instead of PlainText because that handles wordwrap properly + ui->hintLabel->setText("" + html_escape(data.toString()) + ""); + return true; + } + return false; +} + +void UatDialog::addRecord(bool copy_from_current) +{ + if (!uat_) return; + + QModelIndex current = ui->uatTreeView->currentIndex(); + if (copy_from_current && !current.isValid()) return; + + QModelIndex new_index; + if (copy_from_current) { + new_index = uat_model_->copyRow(current); + } else { + // should not fail, but you never know. + if (!uat_model_->insertRows(uat_model_->rowCount(), 1)) { + qDebug() << "Failed to add a new record"; + return; + } + + new_index = uat_model_->index(uat_model_->rowCount() - 1, 0); + } + + // due to an EditTrigger, this will also start editing. + ui->uatTreeView->setCurrentIndex(new_index); + // trigger updating error messages and the OK button state. + modelDataChanged(new_index); +} + +void UatDialog::on_newToolButton_clicked() +{ + addRecord(); +} + +void UatDialog::on_deleteToolButton_clicked() +{ + const QModelIndex ¤t = ui->uatTreeView->currentIndex(); + if (uat_model_ && current.isValid()) { + if (!uat_model_->removeRows(current.row(), 1)) { + qDebug() << "Failed to remove row"; + } + } +} + +void UatDialog::on_copyToolButton_clicked() +{ + addRecord(true); +} + +void UatDialog::on_moveUpToolButton_clicked() +{ + const QModelIndex ¤t = ui->uatTreeView->currentIndex(); + int current_row = current.row(); + if (uat_model_ && current.isValid() && current_row > 0) { + if (!uat_model_->moveRow(current_row, current_row - 1)) { + qDebug() << "Failed to move row up"; + return; + } + current_row--; + ui->moveUpToolButton->setEnabled(current_row > 0); + ui->moveDownToolButton->setEnabled(current_row < (uat_model_->rowCount() - 1)); + } +} + +void UatDialog::on_moveDownToolButton_clicked() +{ + const QModelIndex ¤t = ui->uatTreeView->currentIndex(); + int current_row = current.row(); + if (uat_model_ && current.isValid() && current_row < (uat_model_->rowCount() - 1)) { + if (!uat_model_->moveRow(current_row, current_row + 1)) { + qDebug() << "Failed to move row down"; + return; + } + current_row++; + ui->moveUpToolButton->setEnabled(current_row > 0); + ui->moveDownToolButton->setEnabled(current_row < (uat_model_->rowCount() - 1)); + } +} + +void UatDialog::on_clearToolButton_clicked() +{ + if (uat_model_) { + uat_model_->clearAll(); + } +} + +void UatDialog::applyChanges() +{ + if (!uat_) return; + + if (uat_->flags & UAT_AFFECTS_FIELDS) { + /* Recreate list with new fields */ + mainApp->queueAppSignal(MainApplication::FieldsChanged); + } + if (uat_->flags & UAT_AFFECTS_DISSECTION) { + /* Redissect packets if we have any */ + mainApp->queueAppSignal(MainApplication::PacketDissectionChanged); + } +} + + +void UatDialog::acceptChanges() +{ + if (!uat_model_) return; + + QString error; + if (uat_model_->applyChanges(error)) { + if (!error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } + applyChanges(); + } +} + +void UatDialog::rejectChanges() +{ + if (!uat_model_) return; + + QString error; + if (uat_model_->revertChanges(error)) { + if (!error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } + // Why do we have to trigger a redissection? If the original UAT is + // restored and dissectors only apply changes after the post_update_cb + // method is invoked, then it should not be necessary to trigger + // redissection. One potential exception is when something modifies the + // UAT file after Wireshark has started, but this behavior is not + // supported and causes potentially unnecessary redissection whenever + // the preferences dialog is closed. + // XXX audit all UAT providers and check whether it is safe to remove + // the next call (that is, when their update_cb has no side-effects). + applyChanges(); + } +} + +void UatDialog::on_buttonBox_helpRequested() +{ + if (!uat_) return; + + QString help_page = uat_->help, url; + + help_page.append(".html"); + url = gchar_free_to_qstring(user_guide_url(help_page.toUtf8().constData())); + if (!url.isNull()) { + QDesktopServices::openUrl(QUrl(url)); + } +} + +void UatDialog::resizeColumns() +{ + for (int i = 0; i < uat_model_->columnCount(); i++) { + ui->uatTreeView->resizeColumnToContents(i); + if (i == 0) { + ui->uatTreeView->setColumnWidth(i, ui->uatTreeView->columnWidth(i)+ui->uatTreeView->indentation()); + } + } +} diff --git a/ui/qt/uat_dialog.h b/ui/qt/uat_dialog.h new file mode 100644 index 00000000..92d7d21b --- /dev/null +++ b/ui/qt/uat_dialog.h @@ -0,0 +1,71 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UAT_DIALOG_H +#define UAT_DIALOG_H + +#include + +#include + +#include "geometry_state_dialog.h" +#include +#include + +class QComboBox; +class QPushButton; + +struct epan_uat; + +namespace Ui { +class UatDialog; +} + +class UatDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + explicit UatDialog(QWidget *parent = 0, struct epan_uat *uat = NULL); + ~UatDialog(); + + void setUat(struct epan_uat *uat = NULL); + +private slots: + void copyFromProfile(QString filename); + void modelDataChanged(const QModelIndex &topLeft); + void modelRowsRemoved(); + void modelRowsReset(); + void on_uatTreeView_currentItemChanged(const QModelIndex ¤t, const QModelIndex &previous); + void acceptChanges(); + void rejectChanges(); + void on_newToolButton_clicked(); + void on_deleteToolButton_clicked(); + void on_copyToolButton_clicked(); + void on_moveUpToolButton_clicked(); + void on_moveDownToolButton_clicked(); + void on_clearToolButton_clicked(); + void on_buttonBox_helpRequested(); + +private: + Ui::UatDialog *ui; + UatModel *uat_model_; + UatDelegate *uat_delegate_; + QPushButton *ok_button_; + QPushButton *help_button_; + struct epan_uat *uat_; + + void checkForErrorHint(const QModelIndex ¤t, const QModelIndex &previous); + bool trySetErrorHintFromField(const QModelIndex &index); + void applyChanges(); + void addRecord(bool copy_from_current = false); + void resizeColumns(); +}; + +#endif // UAT_DIALOG_H diff --git a/ui/qt/uat_dialog.ui b/ui/qt/uat_dialog.ui new file mode 100644 index 00000000..cdc8fbe6 --- /dev/null +++ b/ui/qt/uat_dialog.ui @@ -0,0 +1,192 @@ + + + UatDialog + + + + 0 + 0 + 566 + 403 + + + + + + + + + + QLabel { color: red; } + + + + + + Qt::RichText + + + true + + + + + + + + + Create a new entry. + + + + + + + + + + false + + + Remove this entry. + + + + + + + false + + + Copy this entry. + + + + + + + + + + false + + + Move entry up. + + + + + + + + + + false + + + Move entry down. + + + + + + + + + + false + + + Clear all entries. + + + + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+ + TabnavTreeView + QTreeView +
widgets/tabnav_tree_view.h
+
+ + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+
+ + + + buttonBox + accepted() + UatDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + UatDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/ui/qt/uat_frame.cpp b/ui/qt/uat_frame.cpp new file mode 100644 index 00000000..c48cf4c2 --- /dev/null +++ b/ui/qt/uat_frame.cpp @@ -0,0 +1,372 @@ +/* uat_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#include "uat_frame.h" +#include +#include +#include "main_application.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include + +UatFrame::UatFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::UatFrame), + uat_model_(NULL), + uat_delegate_(NULL), + uat_(NULL) +{ + ui->setupUi(this); + + ui->newToolButton->setStockIcon("list-add"); + ui->deleteToolButton->setStockIcon("list-remove"); + ui->copyToolButton->setStockIcon("list-copy"); + ui->moveUpToolButton->setStockIcon("list-move-up"); + ui->moveDownToolButton->setStockIcon("list-move-down"); + ui->clearToolButton->setStockIcon("list-clear"); + +#ifdef Q_OS_MAC + ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->moveUpToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->moveDownToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->clearToolButton->setAttribute(Qt::WA_MacSmallSize, true); + ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + // FIXME: this prevents the columns from being resized, even if the text + // within a combobox needs more space (e.g. in the USER DLT settings). For + // very long filenames in the TLS RSA keys dialog, it also results in a + // vertical scrollbar. Maybe remove this since the editor is not limited to + // the column width (and overlays other fields if more width is needed)? + ui->uatTreeView->header()->setSectionResizeMode(QHeaderView::Interactive); + + // start editing as soon as the field is selected or when typing starts + ui->uatTreeView->setEditTriggers(ui->uatTreeView->editTriggers() | + QAbstractItemView::CurrentChanged | QAbstractItemView::AnyKeyPressed); + + // XXX - Need to add uat_move or uat_insert to the UAT API for drag/drop +} + +UatFrame::~UatFrame() +{ + delete ui; + delete uat_delegate_; + delete uat_model_; +} + +void UatFrame::setUat(epan_uat *uat) +{ + QString title(tr("Unknown User Accessible Table")); + + uat_ = uat; + + ui->pathLabel->clear(); + ui->pathLabel->setEnabled(false); + + if (uat_) { + if (uat_->name) { + title = uat_->name; + } + + if (uat->from_profile) { + ui->copyFromProfileButton->setFilename(uat->filename); + connect(ui->copyFromProfileButton, &CopyFromProfileButton::copyProfile, this, &UatFrame::copyFromProfile); + } + + QString abs_path = gchar_free_to_qstring(uat_get_actual_filename(uat_, FALSE)); + if (abs_path.length() > 0) { + ui->pathLabel->setText(abs_path); + ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString()); + ui->pathLabel->setToolTip(tr("Open ") + uat->filename); + } else { + ui->pathLabel->setText(uat_->filename); + } + ui->pathLabel->setEnabled(true); + + uat_model_ = new UatModel(NULL, uat); + uat_delegate_ = new UatDelegate; + ui->uatTreeView->setModel(uat_model_); + ui->uatTreeView->setItemDelegate(uat_delegate_); + resizeColumns(); + ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0); + + connect(uat_model_, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(modelDataChanged(QModelIndex))); + connect(uat_model_, SIGNAL(rowsRemoved(QModelIndex, int, int)), + this, SLOT(modelRowsRemoved())); + connect(uat_model_, SIGNAL(modelReset()), this, SLOT(modelRowsReset())); + } + + setWindowTitle(title); +} + +void UatFrame::copyFromProfile(QString filename) +{ + gchar *err = NULL; + if (uat_load(uat_, filename.toUtf8().constData(), &err)) { + uat_->changed = TRUE; + uat_model_->reloadUat(); + } else { + report_failure("Error while loading %s: %s", uat_->name, err); + g_free(err); + } +} + +void UatFrame::showEvent(QShowEvent *) +{ +#ifndef Q_OS_MAC + ui->copyFromProfileButton->setFixedHeight(ui->copyToolButton->geometry().height()); +#endif +} + +void UatFrame::applyChanges() +{ + if (!uat_) return; + + if (uat_->flags & UAT_AFFECTS_FIELDS) { + /* Recreate list with new fields */ + mainApp->queueAppSignal(MainApplication::FieldsChanged); + } + if (uat_->flags & UAT_AFFECTS_DISSECTION) { + /* Redissect packets if we have any */ + mainApp->queueAppSignal(MainApplication::PacketDissectionChanged); + } +} + +void UatFrame::acceptChanges() +{ + if (!uat_model_) return; + + QString error; + if (uat_model_->applyChanges(error)) { + if (!error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } + applyChanges(); + } +} + +void UatFrame::rejectChanges() +{ + if (!uat_model_) return; + + QString error; + if (uat_model_->revertChanges(error)) { + if (!error.isEmpty()) { + report_failure("%s", qPrintable(error)); + } + } +} + +void UatFrame::addRecord(bool copy_from_current) +{ + if (!uat_) return; + + QModelIndex current = ui->uatTreeView->currentIndex(); + if (copy_from_current && !current.isValid()) return; + + QModelIndex new_index; + if (copy_from_current) { + new_index = uat_model_->copyRow(current); + } else { + // should not fail, but you never know. + if (!uat_model_->insertRows(uat_model_->rowCount(), 1)) { + qDebug() << "Failed to add a new record"; + return; + } + new_index = uat_model_->index(uat_model_->rowCount() - 1, 0); + } + + // due to an EditTrigger, this will also start editing. + ui->uatTreeView->setCurrentIndex(new_index); + // trigger updating error messages and the OK button state. + modelDataChanged(new_index); +} + +// Invoked when a different field is selected. Note: when selecting a different +// field after editing, this event is triggered after modelDataChanged. +void UatFrame::on_uatTreeView_currentItemChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.isValid()) { + ui->deleteToolButton->setEnabled(true); + ui->clearToolButton->setEnabled(true); + ui->copyToolButton->setEnabled(true); + ui->moveUpToolButton->setEnabled(current.row() != 0); + ui->moveDownToolButton->setEnabled(current.row() != (uat_model_->rowCount() - 1)); + } else { + ui->deleteToolButton->setEnabled(false); + ui->clearToolButton->setEnabled(false); + ui->copyToolButton->setEnabled(false); + ui->moveUpToolButton->setEnabled(false); + ui->moveDownToolButton->setEnabled(false); + } + + checkForErrorHint(current, previous); +} + +// Invoked when a field in the model changes (e.g. by closing the editor) +void UatFrame::modelDataChanged(const QModelIndex &topLeft) +{ + checkForErrorHint(topLeft, QModelIndex()); + resizeColumns(); +} + +// Invoked after a row has been removed from the model. +void UatFrame::modelRowsRemoved() +{ + const QModelIndex ¤t = ui->uatTreeView->currentIndex(); + + // Because currentItemChanged() is called before the row is removed from the model + // we also need to check for button enabling here. + if (current.isValid()) { + ui->moveUpToolButton->setEnabled(current.row() != 0); + ui->moveDownToolButton->setEnabled(current.row() != (uat_model_->rowCount() - 1)); + } else { + ui->moveUpToolButton->setEnabled(false); + ui->moveDownToolButton->setEnabled(false); + } + + checkForErrorHint(current, QModelIndex()); +} + +void UatFrame::modelRowsReset() +{ + ui->deleteToolButton->setEnabled(false); + ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0); + ui->copyToolButton->setEnabled(false); + ui->moveUpToolButton->setEnabled(false); + ui->moveDownToolButton->setEnabled(false); +} + +// If the current field has errors, show them. +// Otherwise if the row has not changed, but the previous field has errors, show them. +// Otherwise pick the first error in the current row. +// Otherwise show the error from the previous field (if any). +// Otherwise clear the error hint. +void UatFrame::checkForErrorHint(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.isValid()) { + if (trySetErrorHintFromField(current)) { + return; + } + + const int row = current.row(); + if (row == previous.row() && trySetErrorHintFromField(previous)) { + return; + } + + for (int i = 0; i < uat_model_->columnCount(); i++) { + if (trySetErrorHintFromField(uat_model_->index(row, i))) { + return; + } + } + } + + if (previous.isValid()) { + if (trySetErrorHintFromField(previous)) { + return; + } + } + + ui->hintLabel->clear(); +} + +bool UatFrame::trySetErrorHintFromField(const QModelIndex &index) +{ + const QVariant &data = uat_model_->data(index, Qt::UserRole + 1); + if (!data.isNull()) { + // use HTML instead of PlainText because that handles wordwrap properly + ui->hintLabel->setText("" + html_escape(data.toString()) + ""); + return true; + } + return false; +} + +void UatFrame::on_newToolButton_clicked() +{ + addRecord(); +} + +void UatFrame::on_deleteToolButton_clicked() +{ + const QModelIndex ¤t = ui->uatTreeView->currentIndex(); + if (uat_model_ && current.isValid()) { + if (!uat_model_->removeRows(current.row(), 1)) { + qDebug() << "Failed to remove row"; + } + } +} + +void UatFrame::on_copyToolButton_clicked() +{ + addRecord(true); +} + +void UatFrame::on_moveUpToolButton_clicked() +{ + const QModelIndex ¤t = ui->uatTreeView->currentIndex(); + int current_row = current.row(); + if (uat_model_ && current.isValid() && current_row > 0) { + if (!uat_model_->moveRow(current_row, current_row - 1)) { + qDebug() << "Failed to move row up"; + return; + } + current_row--; + ui->moveUpToolButton->setEnabled(current_row > 0); + ui->moveDownToolButton->setEnabled(current_row < (uat_model_->rowCount() - 1)); + } +} + +void UatFrame::on_moveDownToolButton_clicked() +{ + const QModelIndex ¤t = ui->uatTreeView->currentIndex(); + int current_row = current.row(); + if (uat_model_ && current.isValid() && current_row < (uat_model_->rowCount() - 1)) { + if (!uat_model_->moveRow(current_row, current_row + 1)) { + qDebug() << "Failed to move row down"; + return; + } + current_row++; + ui->moveUpToolButton->setEnabled(current_row > 0); + ui->moveDownToolButton->setEnabled(current_row < (uat_model_->rowCount() - 1)); + } +} + +void UatFrame::on_clearToolButton_clicked() +{ + if (uat_model_) { + uat_model_->clearAll(); + } +} + +void UatFrame::resizeColumns() +{ + for (int i = 0; i < uat_model_->columnCount(); i++) { + ui->uatTreeView->resizeColumnToContents(i); + if (i == 0) { + ui->uatTreeView->setColumnWidth(i, ui->uatTreeView->columnWidth(i)+ui->uatTreeView->indentation()); + } + } +} diff --git a/ui/qt/uat_frame.h b/ui/qt/uat_frame.h new file mode 100644 index 00000000..870bba9c --- /dev/null +++ b/ui/qt/uat_frame.h @@ -0,0 +1,66 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UAT_FRAME_H +#define UAT_FRAME_H + +#include + +#include +#include +#include + +namespace Ui { +class UatFrame; +} + +class UatFrame : public QFrame +{ + Q_OBJECT + +public: + explicit UatFrame(QWidget *parent = NULL); + ~UatFrame(); + + void setUat(struct epan_uat *uat); + + void acceptChanges(); + void rejectChanges(); + +protected: + void showEvent(QShowEvent *); + +private: + Ui::UatFrame *ui; + + UatModel *uat_model_; + UatDelegate *uat_delegate_; + struct epan_uat *uat_; + + void checkForErrorHint(const QModelIndex ¤t, const QModelIndex &previous); + bool trySetErrorHintFromField(const QModelIndex &index); + void addRecord(bool copy_from_current = false); + void applyChanges(); + void resizeColumns(); + +private slots: + void copyFromProfile(QString filename); + void modelDataChanged(const QModelIndex &topLeft); + void modelRowsRemoved(); + void modelRowsReset(); + void on_uatTreeView_currentItemChanged(const QModelIndex ¤t, const QModelIndex &previous); + void on_newToolButton_clicked(); + void on_deleteToolButton_clicked(); + void on_copyToolButton_clicked(); + void on_moveUpToolButton_clicked(); + void on_moveDownToolButton_clicked(); + void on_clearToolButton_clicked(); +}; + +#endif // UAT_FRAME_H diff --git a/ui/qt/uat_frame.ui b/ui/qt/uat_frame.ui new file mode 100644 index 00000000..452ae65d --- /dev/null +++ b/ui/qt/uat_frame.ui @@ -0,0 +1,161 @@ + + + UatFrame + + + + 0 + 0 + 513 + 397 + + + + Frame + + + 0 + + + + + + + + + QLabel { color: red; } + + + + + + Qt::RichText + + + true + + + + + + + + + Create a new entry. + + + + + + + + + + false + + + Remove this entry. + + + + + + + false + + + Copy this entry. + + + + + + + + + + Move entry up. + + + + + + + + + + Move entry down. + + + + + + + + + + Clear all entries. + + + + + + + + + + Copy entries from another profile. + + + Copy from + + + + + + + + 1 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + + ElidedLabel + QLabel +
widgets/elided_label.h
+
+ + TabnavTreeView + QTreeView +
widgets/tabnav_tree_view.h
+
+ + StockIconToolButton + QToolButton +
widgets/stock_icon_tool_button.h
+
+ + CopyFromProfileButton + QPushButton +
widgets/copy_from_profile_button.h
+
+
+ + +
diff --git a/ui/qt/utils/color_utils.cpp b/ui/qt/utils/color_utils.cpp new file mode 100644 index 00000000..e7d7c6c5 --- /dev/null +++ b/ui/qt/utils/color_utils.cpp @@ -0,0 +1,216 @@ +/* color_utils.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +#include +#endif + +// Colors we use in various parts of the UI. +// +// New colors should be chosen from tango_colors.h. The expert and hidden +// colors come from the GTK+ UI and are grandfathered in. +// +// At some point we should probably make these configurable along with the +// graph and sequence colors. + +const QColor ColorUtils::expert_color_comment = QColor (0xb7, 0xf7, 0x74); /* Green */ +const QColor ColorUtils::expert_color_chat = QColor (0x80, 0xb7, 0xf7); /* Light blue */ +const QColor ColorUtils::expert_color_note = QColor (0xa0, 0xff, 0xff); /* Bright turquoise */ +const QColor ColorUtils::expert_color_warn = QColor (0xf7, 0xf2, 0x53); /* Yellow */ +const QColor ColorUtils::expert_color_error = QColor (0xff, 0x5c, 0x5c); /* Pale red */ +const QColor ColorUtils::expert_color_foreground = QColor (0x00, 0x00, 0x00); /* Black */ +const QColor ColorUtils::hidden_proto_item = QColor (0x44, 0x44, 0x44); /* Gray */ + +ColorUtils::ColorUtils(QObject *parent) : + QObject(parent) +{ +} + +// +// A color_t has RGB values in [0,65535]. +// Qt RGB colors have RGB values in [0,255]. +// +// 65535/255 = 257 = 0x0101, so converting from [0,255] to +// [0,65535] involves just shifting the 8-bit value left 8 bits +// and ORing them together. +// +// Converting from [0,65535] to [0,255] without rounding involves +// just shifting the 16-bit value right 8 bits; I guess you could +// round them by adding 0x80 to the value before shifting. +// +QColor ColorUtils::fromColorT (const color_t *color) { + if (!color) return QColor(); + // Convert [0,65535] values to [0,255] values + return QColor(color->red >> 8, color->green >> 8, color->blue >> 8); +} + +QColor ColorUtils::fromColorT(color_t color) +{ + return fromColorT(&color); +} + +const color_t ColorUtils::toColorT(const QColor color) +{ + color_t colort; + + // Convert [0,255] values to [0,65535] values + colort.red = (color.red() << 8) | color.red(); + colort.green = (color.green() << 8) | color.green(); + colort.blue = (color.blue() << 8) | color.blue(); + + return colort; +} + +QRgb ColorUtils::alphaBlend(const QColor &color1, const QColor &color2, qreal alpha) +{ + alpha = qBound(qreal(0.0), alpha, qreal(1.0)); + + int r1 = color1.red() * alpha; + int g1 = color1.green() * alpha; + int b1 = color1.blue() * alpha; + int r2 = color2.red() * (1 - alpha); + int g2 = color2.green() * (1 - alpha); + int b2 = color2.blue() * (1 - alpha); + + QColor alpha_color(r1 + r2, g1 + g2, b1 + b2); + return alpha_color.rgb(); +} + +QRgb ColorUtils::alphaBlend(const QBrush &brush1, const QBrush &brush2, qreal alpha) +{ + return alphaBlend(brush1.color(), brush2.color(), alpha); +} + +QList ColorUtils::graph_colors_; +const QList ColorUtils::graphColors() +{ + if (graph_colors_.isEmpty()) { + // Available graph colors + // XXX - Add custom + graph_colors_ = QList() + << tango_aluminium_6 // Bar outline (use black instead)? + << tango_sky_blue_5 + << tango_butter_6 + << tango_chameleon_5 + << tango_scarlet_red_5 + << tango_plum_5 + << tango_orange_6 + << tango_aluminium_3 + << tango_sky_blue_3 + << tango_butter_3 + << tango_chameleon_3 + << tango_scarlet_red_3 + << tango_plum_3 + << tango_orange_3; + } + return graph_colors_; +} + +QRgb ColorUtils::graphColor(int item) +{ + if (graph_colors_.isEmpty()) graphColors(); // Init list. + return graph_colors_[item % graph_colors_.size()]; +} + +QList ColorUtils::sequence_colors_; +QRgb ColorUtils::sequenceColor(int item) +{ + if (sequence_colors_.isEmpty()) { + // Available sequence colors. Copied from gtk/graph_analysis.c. + // XXX - Add custom? + sequence_colors_ = QList() + << qRgb(144, 238, 144) + << qRgb(255, 160, 123) + << qRgb(255, 182, 193) + << qRgb(250, 250, 210) + << qRgb(255, 255, 52) + << qRgb(103, 205, 170) + << qRgb(224, 255, 255) + << qRgb(176, 196, 222) + << qRgb(135, 206, 254) + << qRgb(211, 211, 211); + } + return sequence_colors_[item % sequence_colors_.size()]; +} + +bool ColorUtils::themeIsDark() +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + return qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark; +#else + return qApp->palette().windowText().color().lightness() > qApp->palette().window().color().lightness(); +#endif +} + +// Qt < 5.12.6 on macOS always uses Qt::blue for the link color, which is +// unreadable when using a dark theme. Changing the application palette +// via ...Application::setPalette is problematic, since QGuiApplication +// sets a flag (ApplicationPaletteExplicitlySet) which keeps us from +// catching theme changes. +// +// themeLinkBrush and themeLinkStyle provide convenience routines for +// fetching the link brush and style. +// +// We could also override MainApplication::palette, but keeping the +// routines together here seemed to make more sense. +QBrush ColorUtils::themeLinkBrush() +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 12, 6) + // https://bugreports.qt.io/browse/QTBUG-71740 + if (themeIsDark()) { + return QBrush(tango_sky_blue_2); + } +#endif + return qApp->palette().link(); +} + +QString ColorUtils::themeLinkStyle() +{ + QString link_style; + + if (themeIsDark()) { + link_style = QString("") + .arg(themeLinkBrush().color().name()); + } + return link_style; +} + +const QColor ColorUtils::contrastingTextColor(const QColor color) +{ + bool background_is_light = color.lightness() > 127; + if ( (background_is_light && !ColorUtils::themeIsDark()) || (!background_is_light && ColorUtils::themeIsDark()) ) { + return QApplication::palette().text().color(); + } + return QApplication::palette().base().color(); +} + +const QColor ColorUtils::hoverBackground() +{ + QPalette hover_palette = QApplication::palette(); +#if defined(Q_OS_MAC) + hover_palette.setCurrentColorGroup(QPalette::Active); + return hover_palette.highlight().color(); +#else + return ColorUtils::alphaBlend(hover_palette.window(), hover_palette.highlight(), 0.5); +#endif +} + +const QColor ColorUtils::warningBackground() +{ + if (themeIsDark()) { + return QColor(tango_butter_6); + } + return QColor(tango_butter_2); +} diff --git a/ui/qt/utils/color_utils.h b/ui/qt/utils/color_utils.h new file mode 100644 index 00000000..a205cccc --- /dev/null +++ b/ui/qt/utils/color_utils.h @@ -0,0 +1,92 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COLOR_UTILS_H +#define COLOR_UTILS_H + +#include + +#include + +#include + +#include +#include +#include + +class ColorUtils : public QObject +{ +public: + explicit ColorUtils(QObject *parent = 0); + + static QColor fromColorT(const color_t *color); + static QColor fromColorT(color_t color); + static const color_t toColorT(const QColor color); + static QRgb alphaBlend(const QColor &color1, const QColor &color2, qreal alpha); + static QRgb alphaBlend(const QBrush &brush1, const QBrush &brush2, qreal alpha); + + // ...because they don't really fit anywhere else? + static const QColor expert_color_comment; /* green */ + static const QColor expert_color_chat; /* light blue */ + static const QColor expert_color_note; /* bright turquoise */ + static const QColor expert_color_warn; /* yellow */ + static const QColor expert_color_error; /* pale red */ + static const QColor expert_color_foreground; /* black */ + static const QColor hidden_proto_item; /* gray */ + + static const QList graphColors(); + static QRgb graphColor(int item); + static QRgb sequenceColor(int item); + + /** Checks if our application is in "dark mode". + * Dark mode is determined by comparing the application palette's window + * text color with the window color. + * + * @return true if we're running in dark mode, false otherwise. + */ + static bool themeIsDark(); + /** + * Returns an appropriate link color for the current mode. + * @return A brush suitable for setting a text color. + */ + static QBrush themeLinkBrush(); + /** + * Returns an appropriate HTML+CSS link style for the current mode. + * @return A "" string + */ + static QString themeLinkStyle(); + /** + * Returns either QPalette::Text or QPalette::Base as appropriate for the + * specified foreground color + * + * @param color The background color. + * @return A contrasting foreground color for the current mode / theme. + */ + static const QColor contrastingTextColor(const QColor color); + + /** + * Returns an appropriate background color for hovered abstract items. + * @return The background color. + */ + static const QColor hoverBackground(); + + /** + * Returns an appropriate warning background color for the current mode. + * @return The background color. + */ + static const QColor warningBackground(); + +private: + static QList graph_colors_; + static QList sequence_colors_; +}; + +void color_filter_qt_add_cb(color_filter_t *colorf, gpointer user_data); + +#endif // COLOR_UTILS_H diff --git a/ui/qt/utils/data_printer.cpp b/ui/qt/utils/data_printer.cpp new file mode 100644 index 00000000..36f1abd7 --- /dev/null +++ b/ui/qt/utils/data_printer.cpp @@ -0,0 +1,264 @@ +/* data_printer.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +DataPrinter::DataPrinter(QObject * parent) +: QObject(parent), + byteLineLength_(16) +{} + +void DataPrinter::toClipboard(DataPrinter::DumpType type, IDataPrintable * printable) +{ + const QByteArray printData = printable->printableData(); + + QString clipboard_text; + + switch(type) + { + case DP_CString: + // Beginning quote + clipboard_text += QString("\""); + for (int i = 0; i < printData.length(); i++) { + /* ASCII printable */ + int ch = printData[i]; + if (ch >= 32 && ch <= 126) { + clipboard_text += QChar(ch); + } + else { + clipboard_text += QString("\\x%1").arg((uint8_t) printData[i], 2, 16, QChar('0')); + } + } + // End quote + clipboard_text += QString("\""); + break; + case DP_HexStream: + for (int i = 0; i < printData.length(); i++) + clipboard_text += QString("%1").arg((uint8_t) printData[i], 2, 16, QChar('0')); + break; + case DP_Base64: +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_OFF(stringop-overread) +#endif + clipboard_text = printData.toBase64(); +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_ON(stringop-overread) +#endif + break; + case DP_MimeData: + binaryDump(printData); + break; + case DP_HexDump: + clipboard_text = hexTextDump(printData, true); + break; + case DP_HexOnly: + clipboard_text = hexTextDump(printData, false); + break; + default: + break; + } + + if (!clipboard_text.isEmpty()) { + qApp->clipboard()->setText(clipboard_text); + } +} + +void DataPrinter::binaryDump(const QByteArray printData) +{ + if (!printData.isEmpty()) { + QMimeData *mime_data = new QMimeData; + // gtk/gui_utils.c:copy_binary_to_clipboard says: + /* XXX - this is not understood by most applications, + * but can be pasted into the better hex editors - is + * there something better that we can do? + */ + // As of 2015-07-30, pasting into Frhed works on Windows. Pasting into + // Hex Editor Neo and HxD does not. + mime_data->setData("application/octet-stream", printData); + qApp->clipboard()->setMimeData(mime_data); + } +} + +void DataPrinter::setByteLineLength(int bll) +{ + byteLineLength_ = bll; +} + +int DataPrinter::byteLineLength() const +{ + return byteLineLength_; +} + +int DataPrinter::hexChars() +{ + int row_width, chars_per_byte; + + switch (recent.gui_bytes_view) { + case BYTES_HEX: + row_width = 16; + chars_per_byte = 3; + break; + case BYTES_BITS: + row_width = 8; + chars_per_byte = 9; + break; + case BYTES_DEC: + case BYTES_OCT: + row_width = 16; + chars_per_byte = 4; + break; + default: + ws_assert_not_reached(); + } + return (row_width * chars_per_byte) + ((row_width - 1) / separatorInterval()); +} + +QString DataPrinter::hexTextDump(const QByteArray printData, bool showASCII) +{ + QString clipboard_text; + + QString byteStr; + QString dataStr; + + int cnt = 0; + while (cnt < printData.length()) + { + byteStr += QString(" %1").arg((uint8_t) printData[cnt], 2, 16, QChar('0')); + if (showASCII) + { + QChar ch(printData[cnt]); + if (g_ascii_isprint(printData[cnt])) + dataStr += printData[cnt]; + else + dataStr += '.'; + } + cnt++; + } + + int lines = static_cast(printData.length()) / byteLineLength_; + if (printData.length() % byteLineLength_ > 0) + lines++; + + for (cnt = 0; cnt < lines; cnt++) + { + int offset = cnt * 0x10; + + clipboard_text += QString("%1 ").arg(offset, 4, 16, QChar('0')); + clipboard_text += byteStr.mid(offset * 3, byteLineLength_ * 3); + + if (showASCII) + { + /* separation bytes for byte and text */ + clipboard_text += QString(3, ' '); + + /* separation bytes last line */ + if (cnt == (lines - 1) ) + { + int remSpace = byteLineLength_ - static_cast(dataStr.mid(offset, byteLineLength_).length()); + clipboard_text += QString(remSpace * 3, ' '); + } + + /* text representation */ + clipboard_text += dataStr.mid(offset, byteLineLength_); + } + + clipboard_text += "\n"; + } + + return clipboard_text; +} + +DataPrinter * DataPrinter::instance() +{ + static DataPrinter * inst = Q_NULLPTR; + if (inst == Q_NULLPTR) + inst = new DataPrinter(); + return inst; +} + +QActionGroup * DataPrinter::copyActions(QObject * copyClass, QObject * data) +{ + QActionGroup * actions = new QActionGroup(copyClass); + + if (! data && ! dynamic_cast(copyClass)) + return actions; + + DataPrinter * dpi = DataPrinter::instance(); + + if (data) + actions->setProperty("idataprintable", VariantPointer::asQVariant(data)); + else + actions->setProperty("idataprintable", VariantPointer::asQVariant(copyClass)); + + // Mostly duplicated from main_window.ui + QAction * action = new QAction(tr("Copy Bytes as Hex + ASCII Dump"), actions); + action->setToolTip(tr("Copy packet bytes as a hex and ASCII dump.")); + action->setProperty("printertype", DataPrinter::DP_HexDump); + connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes); + + action = new QAction(tr("…as Hex Dump"), actions); + action->setToolTip(tr("Copy packet bytes as a hex dump.")); + action->setProperty("printertype", DataPrinter::DP_HexOnly); + connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes); + + action = new QAction(tr("…as a Hex Stream"), actions); + action->setToolTip(tr("Copy packet bytes as a stream of hex.")); + action->setProperty("printertype", DataPrinter::DP_HexStream); + connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes); + + action = new QAction(tr("…as a Base64 String"), actions); + action->setToolTip(tr("Copy packet bytes as a base64 encoded string.")); + action->setProperty("printertype", DataPrinter::DP_Base64); + connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes); + + action = new QAction(tr("…as MIME Data"), actions); + action->setToolTip(tr("Copy packet bytes as application/octet-stream MIME data.")); + action->setProperty("printertype", DataPrinter::DP_MimeData); + connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes); + + action = new QAction(tr("…as C String"), actions); + action->setToolTip(tr("Copy packet bytes as printable ASCII characters and escape sequences.")); + action->setProperty("printertype", DataPrinter::DP_CString); + connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes); + + return actions; +} + +void DataPrinter::copyIDataBytes(bool /* state */) +{ + if (! dynamic_cast(sender())) + return; + + QAction * sendingAction = dynamic_cast(sender()); + if (! sendingAction->actionGroup() || ! sendingAction->actionGroup()->property("idataprintable").isValid()) + return; + + QObject * dataObject = VariantPointer::asPtr(sendingAction->actionGroup()->property("idataprintable")); + if (! dataObject || ! dynamic_cast(dataObject)) + return; + + int dump_type = sendingAction->property("printertype").toInt(); + + if (dump_type >= 0 && dump_type <= DataPrinter::DP_Base64) { + DataPrinter printer; + printer.toClipboard((DataPrinter::DumpType) dump_type, dynamic_cast(dataObject)); + } +} diff --git a/ui/qt/utils/data_printer.h b/ui/qt/utils/data_printer.h new file mode 100644 index 00000000..85f11c36 --- /dev/null +++ b/ui/qt/utils/data_printer.h @@ -0,0 +1,60 @@ +/** @file + * + * Used by ByteView and others, to create data dumps in printable + * form + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DATA_PRINTER_H +#define DATA_PRINTER_H + +#include + +#include +#include + +#include + +class DataPrinter : public QObject +{ + Q_OBJECT +public: + explicit DataPrinter(QObject *parent = 0); + + enum DumpType { + DP_HexDump, + DP_HexOnly, + DP_HexStream, + DP_CString, + DP_MimeData, + DP_Base64 + }; + + void toClipboard(DataPrinter::DumpType type, IDataPrintable * printable); + + void setByteLineLength(int); + int byteLineLength() const; + // Insert a space after this many bytes + static int separatorInterval() { return 8; } + // The number of hexadecimal characters per line + static int hexChars(); + + static QActionGroup * copyActions(QObject * copyClass, QObject * data = Q_NULLPTR); + static DataPrinter * instance(); + +protected slots: + void copyIDataBytes(bool); + +private: + QString hexTextDump(const QByteArray printData, bool showASCII); + void binaryDump(const QByteArray printData); + + int byteLineLength_; +}; + +#endif // DATA_PRINTER_H diff --git a/ui/qt/utils/field_information.cpp b/ui/qt/utils/field_information.cpp new file mode 100644 index 00000000..395bde2f --- /dev/null +++ b/ui/qt/utils/field_information.cpp @@ -0,0 +1,214 @@ +/* field_information.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +FieldInformation::FieldInformation(field_info *fi, QObject * parent) +:QObject(parent) +{ + fi_ = fi; + parent_fi_ = NULL; +} + +FieldInformation::FieldInformation(const ProtoNode *node, QObject * parent) +:QObject(parent) +{ + fi_ = NULL; + if (node && node->isValid()) { + fi_ = node->protoNode()->finfo; + } + parent_fi_ = NULL; +} + +bool FieldInformation::isValid() const +{ + bool ret = false; + + if (fi_ && fi_->hfinfo) + { + if (fi_->hfinfo->blurb != NULL && fi_->hfinfo->blurb[0] != '\0') { + ret = true; + } else { + ret = QString((fi_->hfinfo->name)).length() > 0; + } + } + + return ret; +} + +bool FieldInformation::isLink() const +{ + if (fi_ && fi_->hfinfo) { + if ((fi_->hfinfo->type == FT_FRAMENUM) || + (FI_GET_FLAG(fi_, FI_URL) && FT_IS_STRING(fi_->hfinfo->type))) { + return true; + } + } + return false; +} + +void FieldInformation::setParentField(field_info * par_fi) +{ + parent_fi_ = par_fi; +} + +int FieldInformation::treeType() +{ + if (fi_) { + Q_ASSERT(fi_->tree_type >= -1 && fi_->tree_type < num_tree_types); + return fi_->tree_type; + } + return -1; +} + +field_info * FieldInformation::fieldInfo() const +{ + return fi_; +} + +FieldInformation::HeaderInfo FieldInformation::headerInfo() const +{ + HeaderInfo header; + + if (fi_ && fi_->hfinfo) + { + header.name = fi_->hfinfo->name; + header.description = fi_->hfinfo->blurb; + header.abbreviation = fi_->hfinfo->abbrev; + header.isValid = true; + header.type = fi_->hfinfo->type; + header.parent = fi_->hfinfo->parent; + header.id = fi_->hfinfo->id; + } + else + { + header.name = ""; + header.description = ""; + header.abbreviation = ""; + header.isValid = false; + header.type = FT_NONE; + header.parent = 0; + header.id = 0; + } + + return header; +} + +FieldInformation * FieldInformation::parentField() const +{ + return new FieldInformation(parent_fi_, parent()); +} + +bool FieldInformation::tvbContains(FieldInformation *child) +{ + if (fi_ && child && fi_->ds_tvb == child->fieldInfo()->ds_tvb) + return true; + + return false; +} + +unsigned FieldInformation::flag(unsigned mask) +{ + if (fi_) { + return FI_GET_FLAG(fi_, mask); + } + return 0; +} + +const QString FieldInformation::moduleName() +{ + QString module_name; + if (isValid()) { + if (headerInfo().parent == -1) { + module_name = fi_->hfinfo->abbrev; + } else { + module_name = proto_registrar_get_abbrev(headerInfo().parent); + } + } + return module_name; +} + +QString FieldInformation::toString() +{ + QByteArray display_label; + + display_label.resize(80); // Arbitrary. + int label_len = proto_item_fill_display_label(fi_, display_label.data(), static_cast(display_label.size())-1); + display_label.resize(label_len); + + if (display_label.isEmpty()) { + return "[no value for field]"; + } + return QString(display_label); +} + +QString FieldInformation::url() +{ + QString url; + if (flag(FI_URL) && headerInfo().isValid && FT_IS_STRING(fi_->hfinfo->type)) { + url = toString(); + } + return url; +} + +FieldInformation::Position FieldInformation::position() const +{ + Position pos = {-1, -1}; + if (fi_ && fi_->ds_tvb) + { + int len = (int) tvb_captured_length(fi_->ds_tvb); + + pos.start = fi_->start; + pos.length = fi_->length; + + if (pos.start < 0 || pos.length < 0 || pos.start >= len) + { + if (fi_->appendix_start >= 0 && fi_->appendix_length > 0 && fi_->appendix_start < len) + { + pos.start = fi_->appendix_start; + pos.length = fi_->appendix_length; + } + } + } + + return pos; +} + +FieldInformation::Position FieldInformation::appendix() const +{ + Position pos = {-1, -1}; + if (fi_ && fi_->ds_tvb) + { + pos.start = fi_->appendix_start; + pos.length = fi_->appendix_length; + } + + return pos; +} + +const QByteArray FieldInformation::printableData() +{ + QByteArray data; + + if (fi_ && fi_->ds_tvb) + { + FieldInformation::Position pos = position(); + int rem_length = tvb_captured_length_remaining(fi_->ds_tvb, pos.start); + + int length = pos.length; + if (length > rem_length) + length = rem_length; + uint8_t * dataSet = (uint8_t *)tvb_memdup(wmem_file_scope(), fi_->ds_tvb, pos.start, length); + data = QByteArray::fromRawData((char *)dataSet, length); + } + + return data; +} diff --git a/ui/qt/utils/field_information.h b/ui/qt/utils/field_information.h new file mode 100644 index 00000000..b0001b71 --- /dev/null +++ b/ui/qt/utils/field_information.h @@ -0,0 +1,76 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FIELD_INFORMATION_H_ +#define FIELD_INFORMATION_H_ + +#include + +#include + +#include +#include "data_printer.h" + +#include + +class FieldInformation : public QObject, public IDataPrintable +{ + Q_OBJECT + Q_INTERFACES(IDataPrintable) + +public: + + struct HeaderInfo + { + QString name; + QString description; + QString abbreviation; + bool isValid; + enum ftenum type; + int parent; + int id; + }; + + struct Position + { + int start; + int length; + }; + + explicit FieldInformation(field_info * fi, QObject * parent = Q_NULLPTR); + explicit FieldInformation(const ProtoNode * node, QObject * parent = Q_NULLPTR); + + bool isValid() const; + bool isLink() const ; + + field_info * fieldInfo() const; + + HeaderInfo headerInfo() const; + Position position() const; + Position appendix() const; + + void setParentField(field_info * fi); + int treeType(); + FieldInformation * parentField() const; + bool tvbContains(FieldInformation *); + unsigned flag(unsigned mask); + const QString moduleName(); + QString toString(); + QString url(); + + const QByteArray printableData(); + +private: + + field_info * fi_; + field_info * parent_fi_; +}; + + +#endif // FIELD_INFORMATION_H_ diff --git a/ui/qt/utils/frame_information.cpp b/ui/qt/utils/frame_information.cpp new file mode 100644 index 00000000..d344bc04 --- /dev/null +++ b/ui/qt/utils/frame_information.cpp @@ -0,0 +1,103 @@ +/* frame_information.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include +#include "epan/epan.h" +#include "epan/column.h" +#include "epan/ftypes/ftypes.h" + +#include "wiretap/wtap.h" + +#include "cfile.h" +#include "file.h" +#include + +#include "frame_tvbuff.h" + +#include + +#include + +FrameInformation::FrameInformation(CaptureFile * capfile, frame_data * fi, QObject * parent) +:QObject(parent), + fi_(fi), + cap_file_(capfile), + edt_(Q_NULLPTR) +{ + wtap_rec_init(&rec_); + ws_buffer_init(&buf_, 1514); + loadFrameTree(); +} + +void FrameInformation::loadFrameTree() +{ + if (! fi_ || ! cap_file_ || !cap_file_->capFile()) + return; + + if (!cf_read_record(cap_file_->capFile(), fi_, &rec_, &buf_)) + return; + + edt_ = g_new0(epan_dissect_t, 1); + + /* proto tree, visible. We need a proto tree if there's custom columns */ + epan_dissect_init(edt_, cap_file_->capFile()->epan, TRUE, TRUE); + col_custom_prime_edt(edt_, &(cap_file_->capFile()->cinfo)); + + epan_dissect_run(edt_, cap_file_->capFile()->cd_t, &rec_, + frame_tvbuff_new_buffer(&cap_file_->capFile()->provider, fi_, &buf_), + fi_, &(cap_file_->capFile()->cinfo)); + epan_dissect_fill_in_columns(edt_, TRUE, TRUE); +} + +FrameInformation::~FrameInformation() +{ + if (edt_) { + epan_dissect_cleanup(edt_); + g_free(edt_); + } + wtap_rec_cleanup(&rec_); + ws_buffer_free(&buf_); +} + +bool FrameInformation::isValid() +{ + bool ret = false; + + if (fi_ && cap_file_ && edt_ && edt_->tvb) + { + ret = true; + } + + return ret; +} + +frame_data * FrameInformation::frameData() const +{ + return fi_; +} + +int FrameInformation::frameNum() const +{ + if (! fi_) + return -1; + return fi_->num; +} + +const QByteArray FrameInformation::printableData() +{ + if (!fi_ || !edt_) + return QByteArray(); + + + int length = tvb_captured_length(edt_->tvb); + const char *data = (const char *)tvb_get_ptr(edt_->tvb, 0, length); + return QByteArray(data, length); +} diff --git a/ui/qt/utils/frame_information.h b/ui/qt/utils/frame_information.h new file mode 100644 index 00000000..7bff3cb4 --- /dev/null +++ b/ui/qt/utils/frame_information.h @@ -0,0 +1,57 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FRAME_INFORMATION_H_ +#define FRAME_INFORMATION_H_ + +#include + +#include +#include +#include "epan/epan.h" +#include "epan/column.h" +#include "epan/ftypes/ftypes.h" + +#include + +#include "data_printer.h" + +#include + +class FrameInformation : public QObject, public IDataPrintable +{ + Q_OBJECT + Q_INTERFACES(IDataPrintable) + +public: + + explicit FrameInformation(CaptureFile * cfile, frame_data * fi, QObject * parent = Q_NULLPTR); + virtual ~FrameInformation(); + + bool isValid(); + + frame_data * frameData() const; + int frameNum() const; + + const QByteArray printableData(); + +private: + + frame_data * fi_; + CaptureFile * cap_file_; + epan_dissect_t * edt_; + wtap_rec rec_; /* Record metadata */ + Buffer buf_; /* Record data */ + + void loadFrameTree(); + +}; + + +#endif // FRAME_INFORMATION_H_ diff --git a/ui/qt/utils/idata_printable.h b/ui/qt/utils/idata_printable.h new file mode 100644 index 00000000..daeddf19 --- /dev/null +++ b/ui/qt/utils/idata_printable.h @@ -0,0 +1,34 @@ +/** @file + * + * Interface class for classes, which provide an interface to + * print objects + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IDATA_PRINTABLE_H +#define IDATA_PRINTABLE_H + +#include + +#include +#include +#include + +class IDataPrintable +{ +public: + virtual ~IDataPrintable() {} + + virtual const QByteArray printableData() = 0; +}; + +#define IDataPrintable_iid "org.wireshark.Qt.UI.IDataPrintable" + +Q_DECLARE_INTERFACE(IDataPrintable, IDataPrintable_iid) + +#endif // IDATA_PRINTABLE_H diff --git a/ui/qt/utils/proto_node.cpp b/ui/qt/utils/proto_node.cpp new file mode 100644 index 00000000..68d1f4c7 --- /dev/null +++ b/ui/qt/utils/proto_node.cpp @@ -0,0 +1,166 @@ +/* proto_node.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include + +ProtoNode::ProtoNode(proto_node *node, ProtoNode *parent) : + node_(node), parent_(parent) +{ + if (node_) { + + int num_children = 0; + for (proto_node *child = node_->first_child; child; child = child->next) { + if (!isHidden(child)) { + num_children++; + } + } + + m_children.reserve(num_children); + + for (proto_node *child = node_->first_child; child; child = child->next) { + if (!isHidden(child)) { + m_children.append(new ProtoNode(child, this)); + } + } + } +} + +ProtoNode::~ProtoNode() +{ + qDeleteAll(m_children); +} + +bool ProtoNode::isValid() const +{ + return node_; +} + +bool ProtoNode::isChild() const +{ + return node_ && node_->parent; +} + +ProtoNode* ProtoNode::parentNode() +{ + return parent_; +} + +QString ProtoNode::labelText() const +{ + if (!node_) { + return QString(); + } + field_info *fi = PNODE_FINFO(node_); + if (!fi) { + return QString(); + } + + QString label; + /* was a free format label produced? */ + if (fi->rep) { + label = fi->rep->representation; + } + else { /* no, make a generic label */ + gchar label_str[ITEM_LABEL_LENGTH]; + proto_item_fill_label(fi, label_str); + label = label_str; + } + + // Generated takes precedence. + if (proto_item_is_generated(node_)) { + label.prepend("["); + label.append("]"); + } + if (proto_item_is_hidden(node_)) { + label.prepend("<"); + label.append(">"); + } + return label; +} + +int ProtoNode::childrenCount() const +{ + if (!node_) return 0; + + return (int)m_children.count(); +} + +int ProtoNode::row() +{ + if (!isChild()) { + return -1; + } + + return (int)parent_->m_children.indexOf(const_cast(this)); +} + +bool ProtoNode::isExpanded() const +{ + if (node_ && node_->finfo && node_->first_child && tree_expanded(node_->finfo->tree_type)) { + return true; + } + return false; +} + +proto_node * ProtoNode::protoNode() const +{ + return node_; +} + +ProtoNode* ProtoNode::child(int row) +{ + if (row < 0 || row >= m_children.size()) + return nullptr; + return m_children.at(row); +} + +ProtoNode::ChildIterator ProtoNode::children() const +{ + /* XXX: Iterate over m_children instead? + * Somewhat faster as m_children already excludes any hidden items. */ + proto_node *child = node_->first_child; + while (child && isHidden(child)) { + child = child->next; + } + + return ProtoNode::ChildIterator(child); +} + +ProtoNode::ChildIterator::ChildIterator(ProtoNode::ChildIterator::NodePtr n) +{ + node = n; +} + +bool ProtoNode::ChildIterator::hasNext() +{ + if (! node || node->next == Q_NULLPTR) + return false; + return true; +} + +ProtoNode::ChildIterator ProtoNode::ChildIterator::next() +{ + do { + node = node->next; + } while (node && isHidden(node)); + return *this; +} + +ProtoNode ProtoNode::ChildIterator::element() +{ + return ProtoNode(node); +} + +bool ProtoNode::isHidden(proto_node * node) +{ + return proto_item_is_hidden(node) && !prefs.display_hidden_proto_items; +} diff --git a/ui/qt/utils/proto_node.h b/ui/qt/utils/proto_node.h new file mode 100644 index 00000000..d6cf3797 --- /dev/null +++ b/ui/qt/utils/proto_node.h @@ -0,0 +1,63 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PROTO_NODE_H_ +#define PROTO_NODE_H_ + +#include + +#include + +#include +#include + +class ProtoNode +{ +public: + + class ChildIterator { + public: + typedef struct _proto_node * NodePtr; + + ChildIterator(NodePtr n = Q_NULLPTR); + + bool hasNext(); + ChildIterator next(); + ProtoNode element(); + + protected: + NodePtr node; + }; + + explicit ProtoNode(proto_node * node = NULL, ProtoNode *parent = nullptr); + ~ProtoNode(); + + bool isValid() const; + bool isChild() const; + bool isExpanded() const; + + proto_node *protoNode() const; + ProtoNode *child(int row); + int childrenCount() const; + int row(); + ProtoNode *parentNode(); + + QString labelText() const; + + ChildIterator children() const; + +private: + proto_node * node_; + QVectorm_children; + ProtoNode *parent_; + static bool isHidden(proto_node * node); +}; + + +#endif // PROTO_NODE_H_ diff --git a/ui/qt/utils/qt_ui_utils.cpp b/ui/qt/utils/qt_ui_utils.cpp new file mode 100644 index 00000000..9dad9dd9 --- /dev/null +++ b/ui/qt/utils/qt_ui_utils.cpp @@ -0,0 +1,347 @@ +/* qt_ui_utils.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include "ui/ws_ui_util.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * We might want to create our own "wsstring" class with convenience + * methods for handling g_malloc()ed strings, GStrings, and a shortcut + * to .toUtf8().constData(). + */ + +gchar *qstring_strdup(QString q_string) { + return g_strdup(q_string.toUtf8().constData()); +} + +QString gchar_free_to_qstring(gchar *glib_string) { + return QString(gchar_free_to_qbytearray(glib_string)); +} + +QByteArray gchar_free_to_qbytearray(gchar *glib_string) +{ + QByteArray qt_bytearray(glib_string); + g_free(glib_string); + return qt_bytearray; +} + +QByteArray gstring_free_to_qbytearray(GString *glib_gstring) +{ + QByteArray qt_ba(glib_gstring->str); + g_string_free(glib_gstring, TRUE); + return qt_ba; +} + +QByteArray gbytearray_free_to_qbytearray(GByteArray *glib_array) +{ + QByteArray qt_ba(reinterpret_cast(glib_array->data), glib_array->len); + g_byte_array_free(glib_array, TRUE); + return qt_ba; +} + +const QString int_to_qstring(qint64 value, int field_width, int base) +{ + // Qt deprecated QString::sprintf in Qt 5.0, then added ::asprintf in + // Qt 5.5. Rather than navigate a maze of QT_VERSION_CHECKs, just use + // QString::arg. + QString int_qstr; + + switch (base) { + case 8: + int_qstr = "0"; + break; + case 16: + int_qstr = "0x"; + break; + default: + break; + } + + int_qstr += QString("%1").arg(value, field_width, base, QChar('0')); + return int_qstr; +} + +const QString address_to_qstring(const _address *address, bool enclose) +{ + QString address_qstr = QString(); + if (address) { + if (enclose && address->type == AT_IPv6) address_qstr += "["; + gchar *address_gchar_p = address_to_str(NULL, address); + address_qstr += address_gchar_p; + wmem_free(NULL, address_gchar_p); + if (enclose && address->type == AT_IPv6) address_qstr += "]"; + } + return address_qstr; +} + +const QString address_to_display_qstring(const _address *address) +{ + QString address_qstr = QString(); + if (address) { + gchar *address_gchar_p = address_to_display(NULL, address); + address_qstr = address_gchar_p; + wmem_free(NULL, address_gchar_p); + } + return address_qstr; +} + +const QString val_to_qstring(const guint32 val, const value_string *vs, const char *fmt) +{ + QString val_qstr; + gchar* gchar_p = val_to_str_wmem(NULL, val, vs, fmt); + val_qstr = gchar_p; + wmem_free(NULL, gchar_p); + + return val_qstr; +} + +const QString val_ext_to_qstring(const guint32 val, value_string_ext *vse, const char *fmt) +{ + QString val_qstr; + gchar* gchar_p = val_to_str_ext_wmem(NULL, val, vse, fmt); + val_qstr = gchar_p; + wmem_free(NULL, gchar_p); + + return val_qstr; +} + +const QString range_to_qstring(const range_string *range) +{ + QString range_qstr = QString(); + if (range) { + range_qstr += QString("%1-%2").arg(range->value_min).arg(range->value_max); + } + return range_qstr; +} + +const QString bits_s_to_qstring(const double bits_s) +{ + return gchar_free_to_qstring( + format_size(bits_s, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)); +} + +const QString file_size_to_qstring(const gint64 size) +{ + return gchar_free_to_qstring( + format_size(size, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI)); +} + +const QString time_t_to_qstring(time_t ti_time) +{ + QDateTime date_time = QDateTime::fromSecsSinceEpoch(qint64(ti_time)); + QString time_str = date_time.toLocalTime().toString("yyyy-MM-dd hh:mm:ss"); + return time_str; +} + +QString html_escape(const QString plain_string) { + return plain_string.toHtmlEscaped(); +} + + +void smooth_font_size(QFont &font) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QList size_list = QFontDatabase::smoothSizes(font.family(), font.styleName()); +#else + QFontDatabase fdb; + QList size_list = fdb.smoothSizes(font.family(), font.styleName()); +#endif + + if (size_list.size() < 2) return; + + int last_size = size_list.takeFirst(); + foreach (int cur_size, size_list) { + if (font.pointSize() > last_size && font.pointSize() <= cur_size) { + font.setPointSize(cur_size); + return; + } + last_size = cur_size; + } +} + +bool qActionLessThan(const QAction * a1, const QAction * a2) { + return a1->text().compare(a2->text()) < 0; +} + +bool qStringCaseLessThan(const QString &s1, const QString &s2) +{ + return s1.compare(s2, Qt::CaseInsensitive) < 0; +} + +void desktop_show_in_folder(const QString file_path) +{ + bool success = false; + + // https://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt + +#if defined(Q_OS_WIN) + QString command = "explorer.exe"; + QStringList arguments; + QString path = QDir::toNativeSeparators(file_path); + arguments << "/select," << path + ""; + success = QProcess::startDetached(command, arguments); +#elif defined(Q_OS_MAC) + QStringList script_args; + QString escaped_path = file_path; + + escaped_path.replace('"', "\\\""); + script_args << "-e" + << QString("tell application \"Finder\" to reveal POSIX file \"%1\"") + .arg(escaped_path); + if (QProcess::execute("/usr/bin/osascript", script_args) == 0) { + success = true; + script_args.clear(); + script_args << "-e" + << "tell application \"Finder\" to activate"; + QProcess::execute("/usr/bin/osascript", script_args); + } +#else + // Is there a way to highlight the file using xdg-open? +#endif + if (!success) { + QFileInfo file_info(file_path); + QDesktopServices::openUrl(QUrl::fromLocalFile(file_info.dir().absolutePath())); + } +} + +bool rect_on_screen(const QRect &rect) +{ + foreach (QScreen *screen, qApp->screens()) { + if (screen->availableGeometry().contains(rect)) { + return true; + } + } + + return false; +} + +void set_action_shortcuts_visible_in_context_menu(QList actions) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) && QT_VERSION < QT_VERSION_CHECK(5, 13, 0) + // For QT_VERSION >= 5.13.0 we call styleHints()->setShowShortcutsInContextMenus(true) + // in WiresharkApplication. + // QTBUG-71471 + // QTBUG-61181 + foreach (QAction *action, actions) { + action->setShortcutVisibleInContextMenu(true); + } +#else + Q_UNUSED(actions) +#endif +} + +QVectorqvector_rtpstream_ids_copy(QVector stream_ids) +{ + QVectornew_ids; + + foreach(rtpstream_id_t *id, stream_ids) { + rtpstream_id_t *new_id = g_new0(rtpstream_id_t, 1); + rtpstream_id_copy(id, new_id); + new_ids << new_id; + } + + return new_ids; +} + +void qvector_rtpstream_ids_free(QVector stream_ids) +{ + foreach(rtpstream_id_t *id, stream_ids) { + rtpstream_id_free(id); + } +} + +QString make_filter_based_on_rtpstream_id(QVector stream_ids) +{ + QStringList stream_filters; + QString filter; + + foreach(rtpstream_id_t *id, stream_ids) { + QString ip_proto = id->src_addr.type == AT_IPv6 ? "ipv6" : "ip"; + stream_filters << QString("(%1.src==%2 && udp.srcport==%3 && %1.dst==%4 && udp.dstport==%5 && rtp.ssrc==0x%6)") + .arg(ip_proto) // %1 + .arg(address_to_qstring(&id->src_addr)) // %2 + .arg(id->src_port) // %3 + .arg(address_to_qstring(&id->dst_addr)) // %4 + .arg(id->dst_port) // %5 + .arg(id->ssrc, 0, 16); + } + if (stream_filters.length() > 0) { + filter = stream_filters.join(" || "); + } + + return filter; +} + +QString openDialogInitialDir() +{ + QString result; + + switch (prefs.gui_fileopen_style) { + + case FO_STYLE_LAST_OPENED: + /* The user has specified that we should start out in the last directory + we looked in. If we've already opened a file, use its containing + directory, if we could determine it, as the directory, otherwise + use the "last opened" directory saved in the preferences file if + there was one. */ + /* This is now the default behaviour in file_selection_new() */ + result = QString(get_open_dialog_initial_dir()); + break; + + case FO_STYLE_SPECIFIED: + /* The user has specified that we should always start out in a + specified directory; if they've specified that directory, + start out by showing the files in that dir. */ + if (prefs.gui_fileopen_dir[0] != '\0') + result = QString(prefs.gui_fileopen_dir); + break; + } + + QDir ld(result); + if (ld.exists()) + return result; + + return QString(); +} + +void storeLastDir(QString dir) +{ + /* XXX - printable? */ + if (dir.length() > 0) + set_last_open_dir(qUtf8Printable(dir)); +} + diff --git a/ui/qt/utils/qt_ui_utils.h b/ui/qt/utils/qt_ui_utils.h new file mode 100644 index 00000000..60ccf31d --- /dev/null +++ b/ui/qt/utils/qt_ui_utils.h @@ -0,0 +1,280 @@ +/** @file + * + * Declarations of Qt-specific UI utility routines + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __QT_UI_UTILS_H__ +#define __QT_UI_UTILS_H__ + +// xxx - copied from ui/gtk/gui_utils.h + +/** @file + * Utility functions for working with the Wireshark and GLib APIs. + */ + +#include + +#include + +#include "ui/rtp_stream.h" + +#include + +class QAction; +class QFont; +class QRect; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +// These are defined elsewhere in ../gtk/ +#define RECENT_KEY_CAPTURE_FILE "recent.capture_file" +#define RECENT_KEY_REMOTE_HOST "recent.remote_host" + +struct _address; +struct epan_range; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/* + * Helper macro, to prevent old-style-cast warnings, when using GList in c++ code + */ +#define gxx_list_next(list) ((list) ? ((reinterpret_cast(list))->next) : Q_NULLPTR) +#define gxx_constlist_next(list) ((list) ? ((reinterpret_cast(list))->next) : Q_NULLPTR) +#define gxx_list_previous(list) ((list) ? ((reinterpret_cast(list))->prev) : Q_NULLPTR) +#define gxx_constlist_previous(list) ((list) ? ((reinterpret_cast(list))->prev) : Q_NULLPTR) + +#define gxx_list_data(type, list) ((list) ? ((reinterpret_cast(list->data))) : Q_NULLPTR) + +/** Create a glib-compatible copy of a QString. + * + * @param q_string A QString. + * + * @return A copy of the QString. UTF-8 allocated with g_malloc(). + */ +gchar *qstring_strdup(QString q_string); + +/** Transfer ownership of a GLib character string to a newly constructed QString + * + * @param glib_string A string allocated with g_malloc() or NULL. Will be + * freed. + * + * @return A QString instance created from the input string. + */ +QString gchar_free_to_qstring(gchar *glib_string); + +/** Transfer ownership of a GLib character string to a newly constructed QString + * + * @param glib_string A string allocated with g_malloc() or NULL. Will be + * freed. + * + * @return A QByteArray instance created from the input string. + */ +QByteArray gchar_free_to_qbytearray(gchar *glib_string); + +/** Transfer ownership of a GLib character string to a newly constructed QByteArray + * + * @param glib_gstring A string allocated with g_malloc() or NULL. Will be + * freed. + * + * @return A QByteArray instance created from the input string. + */ +QByteArray gstring_free_to_qbytearray(GString *glib_gstring); + +/** Transfer ownership of a GbyteArray to a newly constructed QByteArray + * + * @param glib_array A GByteArray or NULL. Will be freed. + * @return A QByteArray instance created from the input array. + */ +QByteArray gbytearray_free_to_qbytearray(GByteArray *glib_array); + +/** Convert an integer to a formatted string representation. + * + * @param value The integer to format. + * @param field_width Width of the output, not including any base prefix. + * Output will be zero-padded. + * @param base Number base between 2 and 36 (limited by QString::arg). + * + * @return A QString representation of the integer + */ +const QString int_to_qstring(qint64 value, int field_width = 0, int base = 10); + +/** Convert an address to a QString using address_to_str(). + * + * @param address A pointer to an address. + * @param enclose Enclose IPv6 addresses in square brackets. + * + * @return A QString representation of the address. May be the null string (QString()) + */ +const QString address_to_qstring(const struct _address *address, bool enclose = false); + +/** Convert an address to a QString using address_to_display(). + * + * @param address A pointer to an address. + * + * @return A QString representation of the address. May be the null string (QString()) + */ +const QString address_to_display_qstring(const struct _address *address); + +/** Convert a value_string to a QString using val_to_str_wmem(). + * + * @param val The value to convert to string. + * @param vs value_string array. + * @param fmt Formatting for value not in array. + * + * @return A QString representation of the value_string. + */ +const QString val_to_qstring(const guint32 val, const struct _value_string *vs, const char *fmt) +G_GNUC_PRINTF(3, 0); + +/** Convert a value_string_ext to a QString using val_to_str_ext_wmem(). + * + * @param val The value to convert to string. + * @param vse value_string_ext array. + * @param fmt Formatting for value not in array. + * + * @return A QString representation of the value_string_ext. + */ +const QString val_ext_to_qstring(const guint32 val, struct _value_string_ext *vse, const char *fmt) +G_GNUC_PRINTF(3, 0); + +/** Convert a range to a QString using range_convert_range(). + * + * @param range A pointer to a range_string struct. + * + * @return A QString representation of the range_string. May be the null string (QString()) + */ +const QString range_to_qstring(const range_string *range); + +/** Convert a bits per second value to a human-readable QString using format_size(). + * + * @param bits_s The value to convert to string. + * + * @return A QString representation of the data rate in SI units. + */ +const QString bits_s_to_qstring(const double bits_s); + +/** Convert a file size value to a human-readable QString using format_size(). + * + * @param size The value to convert to string. + * + * @return A QString representation of the file size in SI units. + */ +const QString file_size_to_qstring(const gint64 size); + +/** Convert a time_t value to a human-readable QString using QDateTime. + * + * @param ti_time The value to convert. + * + * @return A QString representation of the file size in SI units. + */ +const QString time_t_to_qstring(time_t ti_time); + +/** Escape HTML metacharacters in a string. + * + * @param plain_string String to convert. + * + * @return A QString with escaped metacharacters. + */ +QString html_escape(const QString plain_string); + +/** + * Round the current size of a font up to its next "smooth" size. + * If a smooth size can't be found the font is left unchanged. + * + * @param font The font to smooth. + */ +void smooth_font_size(QFont &font); + +/** + * Compare the text of two QActions. Useful for passing to std::sort. + * + * @param a1 First action + * @param a2 Second action + */ +bool qActionLessThan(const QAction *a1, const QAction *a2); + +/** + * Compare two QStrings, ignoring case. Useful for passing to std::sort. + * + * @param s1 First string + * @param s2 Second string + */ +bool qStringCaseLessThan(const QString &s1, const QString &s2); + +/** + * Given the path to a file, open its containing folder in the desktop + * shell. Highlight the file if possible. + * + * @param file_path Path to the file. + */ +void desktop_show_in_folder(const QString file_path); + +/** + * Test to see if a rect is visible on screen. + * + * @param rect The rect to test, typically a "recent.gui_geometry_*" setting. + * @return true if the rect is completely enclosed by one of the display + * screens, false otherwise. + */ +bool rect_on_screen(const QRect &rect); + +/** + * Set the "shortcutVisibleInContextMenu" property to true for + * a list of qactions. + * + * @param actions The actions to make visible. + */ +void set_action_shortcuts_visible_in_context_menu(QList actions); + +/** + * Create copy of all rtpstream_ids to new QVector + * => caller must release it with qvector_rtpstream_ids_free() + * + * @param stream_ids List of infos + * @return Vector of rtpstream_ids + */ +QVectorqvector_rtpstream_ids_copy(QVector stream_ids); + +/** + * Free all rtpstream_ids in QVector + * + * @param stream_ids List of infos + */ +void qvector_rtpstream_ids_free(QVector stream_ids); + +/** + * Make display filter from list of rtpstream_id + * + * @param stream_ids List of ids + * @return Filter or empty string + */ +QString make_filter_based_on_rtpstream_id(QVector stream_ids); + +/** + * @brief Return the last directory that had been opened. + * + * This can be influenced by prefs.gui_fileopen_style which will allow to either + * open the real last dir or have the user set one specifically. + * + * @return a reference to that directory. + */ +QString openDialogInitialDir(); + +/** + * @brief Store the directory as last directory being used + */ +void storeLastDir(QString dir); + +#endif /* __QT_UI_UTILS__H__ */ + +// XXX Add a routine to fetch the HWND corresponding to a widget using QPlatformIntegration diff --git a/ui/qt/utils/rtp_audio_file.cpp b/ui/qt/utils/rtp_audio_file.cpp new file mode 100644 index 00000000..591a63bb --- /dev/null +++ b/ui/qt/utils/rtp_audio_file.cpp @@ -0,0 +1,384 @@ +/* rtp_audio_file.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * RTP samples are stored in "sparse" file. File knows where are silence gaps + * and they are handled special way (not stored). + * + * File uses Frame as piece of information. One Frame match audio of one + * decoded packet or audio silence in between them. Frame holds information + * about frame type (audio/silence), its length and realtime position and + * sample possition (where decoded audio is really stored, with gaps omitted). + * + * There are three stages of the object use + * - writing data by frames during decoding of the stream + * - reading data by frames during creating the visual waveform + * - reading data by bytes/samples during audio play or audio save + * + * There is no stage indication in the object, but there are different calls + * used by the code. For last stage the object looks like QIODevice therefore + * any read of it looks like reading of sequence of bytes. + * + * If audio starts later than start of the file, first Frame contains silence + * record. It is leaved out at some cases. + */ + +#include "rtp_audio_file.h" +#include + +RtpAudioFile::RtpAudioFile(bool use_disk_for_temp, bool use_disk_for_frames): + real_pos_(0) + , real_size_(0) + , sample_pos_(0) + , sample_size_(0) +{ + QString tempname; + + // ReadOnly because we write different way + QIODevice::open(QIODevice::ReadOnly); + + tempname = "memory"; + if (use_disk_for_temp) { + tempname = QString("%1/wireshark_rtp_stream").arg(QDir::tempPath()); + sample_file_ = new QTemporaryFile(tempname, this); + } else { + sample_file_ = new QBuffer(this); + } + if (!sample_file_->open(QIODevice::ReadWrite)) { + // We are out of file resources + delete sample_file_; + qWarning() << "Can't create temp file in " << tempname; + throw -1; + } + + tempname = "memory"; + if (use_disk_for_frames) { + tempname = QString("%1/wireshark_rtp_frames").arg(QDir::tempPath()); + sample_file_frame_ = new QTemporaryFile(tempname, this); + } else { + sample_file_frame_ = new QBuffer(this); + } + if (!sample_file_frame_->open(QIODevice::ReadWrite)) { + // We are out of file resources + delete sample_file_; + delete sample_file_frame_; + qWarning() << "Can't create frame file in " << tempname; + throw -1; + } +} + +RtpAudioFile::~RtpAudioFile() +{ + if (sample_file_) delete sample_file_; + if (sample_file_frame_) delete sample_file_frame_; +} + +/* + * Functions for writing Frames + */ +void RtpAudioFile::setFrameWriteStage() +{ + sample_file_->seek(0); + sample_file_frame_->seek(0); + real_pos_ = 0; + real_size_ = 0; + sample_pos_ = 0; + sample_size_ = 0; +} + +void RtpAudioFile::frameUpdateRealCounters(qint64 written_bytes) +{ + if (real_pos_ < real_size_) { + // We are writing before end, calculate if we are over real_size_ + qint64 diff = real_pos_ + written_bytes - real_size_; + + if (diff > 0) { + // Update size + real_size_ += diff; + } + } else { + real_size_ += written_bytes; + } + real_pos_ += written_bytes; +} + +void RtpAudioFile::frameUpdateSampleCounters(qint64 written_bytes) +{ + if (sample_pos_ < sample_size_) { + // We are writing before end, calculate if we are over sample_size_ + qint64 diff = sample_pos_ + written_bytes - sample_size_; + + if (diff > 0) { + // Update size + sample_size_ += diff; + } + } else { + sample_size_ += written_bytes; + } + sample_pos_ += written_bytes; +} + +qint64 RtpAudioFile::frameWriteFrame(guint32 frame_num, qint64 real_pos, qint64 sample_pos, qint64 len, rtp_frame_type type) +{ + rtp_frame_info frame_info; + + frame_info.real_pos = real_pos; + frame_info.sample_pos = sample_pos; + frame_info.len = len; + frame_info.frame_num = frame_num; + frame_info.type = type; + + return sample_file_frame_->write((char *)&frame_info, sizeof(frame_info)); +} + +void RtpAudioFile::frameWriteSilence(guint32 frame_num, qint64 samples) +{ + if (samples < 1) return; + + qint64 silence_bytes = samples * SAMPLE_BYTES; + + frameWriteFrame(frame_num, real_pos_, sample_pos_, silence_bytes, RTP_FRAME_SILENCE); + frameUpdateRealCounters(silence_bytes); +} + +qint64 RtpAudioFile::frameWriteSamples(guint32 frame_num, const char *data, qint64 max_size) +{ + gint64 written; + + written = sample_file_->write(data, max_size); + + if (written != -1) { + frameWriteFrame(frame_num, real_pos_, sample_pos_, written, RTP_FRAME_AUDIO); + frameUpdateRealCounters(written); + frameUpdateSampleCounters(written); + } + + return written; +} + +/* + * Functions for reading Frames + */ + +void RtpAudioFile::setFrameReadStage(qint64 prepend_samples) +{ + sample_file_frame_->seek(0); + if (prepend_samples > 0) { + // Skip first frame which contains openning silence + sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_)); + } +} + +bool RtpAudioFile::readFrameSamples(gint32 *read_buff_bytes, SAMPLE **read_buff, spx_uint32_t *read_len, guint32 *frame_num, rtp_frame_type *type) +{ + rtp_frame_info frame_info; + guint64 read_bytes = 0; + + if (!sample_file_frame_->read((char *)&frame_info, sizeof(frame_info))) { + // Can't read frame, some error occurred + return false; + } + + *frame_num = frame_info.frame_num; + *type = frame_info.type; + + if (frame_info.type == RTP_FRAME_AUDIO) { + // Resize buffer when needed + if (frame_info.len > *read_buff_bytes) { + while ((frame_info.len > *read_buff_bytes)) { + *read_buff_bytes *= 2; + } + *read_buff = (SAMPLE *) g_realloc(*read_buff, *read_buff_bytes); + } + + sample_file_->seek(frame_info.sample_pos); + read_bytes = sample_file_->read((char *)*read_buff, frame_info.len); + } else { + // For silence we do nothing + read_bytes = frame_info.len; + } + + *read_len = (spx_uint32_t)(read_bytes / SAMPLE_BYTES); + + return true; +} + +/* + * Functions for reading data during play + */ +void RtpAudioFile::setDataReadStage() +{ + sample_file_frame_->seek(0); + sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_)); + real_pos_ = cur_frame_.real_pos; +} + +bool RtpAudioFile::open(QIODevice::OpenMode mode) +{ + if (mode == QIODevice::ReadOnly) { + return true; + } + + return false; +} + +qint64 RtpAudioFile::size() const +{ + return real_size_; +} + +qint64 RtpAudioFile::pos() const +{ + return real_pos_; +} + +/* + * Seek starts from beginning of Frames and search one where offset belongs + * to. It looks inefficient, but seek is used usually just to jump to 0 or + * to skip first Frame where silence is stored. + */ +bool RtpAudioFile::seek(qint64 off) +{ + if (real_size_ <= off) { + // Can't seek above end of file + return false; + } + + // Search for correct offset from first frame + sample_file_frame_->seek(0); + while (1) { + // Read frame + if (!sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_))) { + // Can't read frame, some error occurred + return false; + } + + if ((cur_frame_.real_pos + cur_frame_.len) > off) { + // We found correct frame + // Calculate offset in frame + qint64 diff = off - cur_frame_.real_pos; + qint64 new_real_pos = cur_frame_.real_pos + diff; + qint64 new_sample_pos = cur_frame_.sample_pos + diff; + + if (cur_frame_.type == RTP_FRAME_AUDIO) { + // For audio frame we should to seek to correct place + if (!sample_file_->seek(new_sample_pos)) { + return false; + } + // Real seek was successful + real_pos_ = new_real_pos; + return true; + } else { + // For silence frame we blindly confirm it + real_pos_ = new_real_pos; + return true; + } + } + } + return false; +} + +qint64 RtpAudioFile::sampleFileSize() +{ + return real_size_; +} + +void RtpAudioFile::seekSample(qint64 samples) +{ + seek(sizeof(SAMPLE) * samples); +} + +qint64 RtpAudioFile::readFrameData(char *data , qint64 want_read) +{ + // Calculate remaining data in frame + qint64 remaining = cur_frame_.len - (real_pos_ - cur_frame_.real_pos); + qint64 was_read; + + if (remaining < want_read) { + // Incorrect call, can't read more than is stored in frame + return -1; + } + + if (cur_frame_.type == RTP_FRAME_AUDIO) { + was_read = sample_file_->read(data, want_read); + real_pos_ += was_read; + } else { + memset(data, 0, want_read); + real_pos_ += want_read; + was_read = want_read; + } + + return was_read; +} + +qint64 RtpAudioFile::readSample(SAMPLE *sample) +{ + return read((char *)sample, sizeof(SAMPLE)); +} + +qint64 RtpAudioFile::getTotalSamples() +{ + return (real_size_/(qint64)sizeof(SAMPLE)); +} + +qint64 RtpAudioFile::getEndOfSilenceSample() +{ + if (cur_frame_.type == RTP_FRAME_SILENCE) { + return (cur_frame_.real_pos + cur_frame_.len) / (qint64)sizeof(SAMPLE); + } else { + return -1; + } +} + +qint64 RtpAudioFile::readData(char *data, qint64 maxSize) +{ + qint64 to_read = maxSize; + qint64 can_read; + qint64 was_read = 0; + qint64 remaining; + + while (1) { + // Calculate remaining data in frame + remaining = cur_frame_.len - (real_pos_ - cur_frame_.real_pos); + if (remaining > to_read) { + // Even we want to read more, we can read just till end of frame + can_read = to_read; + } else { + can_read = remaining; + } + if (can_read==readFrameData(data, can_read)) { + to_read -= can_read; + data += can_read; + was_read += can_read; + if (real_pos_ >= cur_frame_.real_pos + cur_frame_.len) { + // We exhausted the frame, read next one + if (!sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_))) { + // We are at the end of the file + return was_read; + } + if ((cur_frame_.type == RTP_FRAME_AUDIO) && (!sample_file_->seek(cur_frame_.sample_pos))) { + // We tried to seek to correct place, but it failed + return -1; + } + } + if (to_read == 0) { + return was_read; + } + } else { + return -1; + } + } +} + +qint64 RtpAudioFile::writeData(const char *data _U_, qint64 maxSize _U_) +{ + // Writing is not supported + return -1; +} + diff --git a/ui/qt/utils/rtp_audio_file.h b/ui/qt/utils/rtp_audio_file.h new file mode 100644 index 00000000..addc3015 --- /dev/null +++ b/ui/qt/utils/rtp_audio_file.h @@ -0,0 +1,93 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTP_AUDIO_FILE_H +#define RTP_AUDIO_FILE_H + +#include "config.h" +#include + +#include + +#include +#include +#include +#include +#include + +struct _rtp_info; + +typedef enum { + RTP_FRAME_AUDIO = 0, + RTP_FRAME_SILENCE +} rtp_frame_type; + +// Structure used for storing frame num during visual waveform decoding +typedef struct { + qint64 real_pos; + qint64 sample_pos; + qint64 len; + guint32 frame_num; + rtp_frame_type type; +} rtp_frame_info; + + +class RtpAudioFile: public QIODevice +{ +public: + explicit RtpAudioFile(bool use_disk_for_temp, bool use_disk_for_frames); + ~RtpAudioFile(); + + // Functions for writing Frames + void setFrameWriteStage(); + void frameWriteSilence(guint32 frame_num, qint64 samples); + qint64 frameWriteSamples(guint32 frame_num, const char *data, qint64 max_size); + + // Functions for reading Frames + void setFrameReadStage(qint64 prepend_samples); + bool readFrameSamples(gint32 *read_buff_bytes, SAMPLE **read_buff, spx_uint32_t *read_len, guint32 *frame_num, rtp_frame_type *type); + + // Functions for reading data during play + void setDataReadStage(); + bool open(QIODevice::OpenMode mode) override; + qint64 size() const override; + qint64 pos() const override; + bool seek(qint64 off) override; + qint64 sampleFileSize(); + void seekSample(qint64 samples); + qint64 readSample(SAMPLE *sample); + qint64 getTotalSamples(); + qint64 getEndOfSilenceSample(); + +protected: + // Functions for reading data during play + qint64 readData(char *data, qint64 maxSize) override; + qint64 writeData(const char *data, qint64 maxSize) override; + +private: + QIODevice *sample_file_; // Stores waveform samples + QIODevice *sample_file_frame_; // Stores rtp_packet_info per packet + qint64 real_pos_; + qint64 real_size_; + qint64 sample_pos_; + qint64 sample_size_; + rtp_frame_info cur_frame_; + + // Functions for writing Frames + qint64 frameWriteFrame(guint32 frame_num, qint64 real_pos, qint64 sample_pos, qint64 len, rtp_frame_type type); + void frameUpdateRealCounters(qint64 written_bytes); + void frameUpdateSampleCounters(qint64 written_bytes); + + // Functions for reading Frames + + // Functions for reading data during play + qint64 readFrameData(char *data , qint64 want_read); +}; + +#endif // RTP_AUDIO_FILE_H diff --git a/ui/qt/utils/rtp_audio_routing.cpp b/ui/qt/utils/rtp_audio_routing.cpp new file mode 100644 index 00000000..04ced435 --- /dev/null +++ b/ui/qt/utils/rtp_audio_routing.cpp @@ -0,0 +1,110 @@ +/* rtp_audio_routing.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "rtp_audio_routing.h" + +AudioRouting::AudioRouting(bool muted, audio_routing_channel_t channel): + muted_(muted), + channel_(channel) +{ +} + +char const *AudioRouting::formatAudioRoutingToString() +{ + if (muted_) { + return "Muted"; + } else { + switch (channel_) { + case channel_any: + // Should not happen ever + return "ERR"; + case channel_mono: + return "Play"; + case channel_stereo_left: + return "L"; + case channel_stereo_right: + return "R"; + case channel_stereo_both: + return "L+R"; + } + } + + // Should not happen ever + return "ERR"; +} + +AudioRouting AudioRouting::getNextChannel(bool stereo_available) +{ + if (stereo_available) { + // Stereo + if (muted_) { + return AudioRouting(AUDIO_UNMUTED, channel_stereo_left); + } else { + switch (channel_) { + case channel_stereo_left: + return AudioRouting(AUDIO_UNMUTED, channel_stereo_both); + case channel_stereo_both: + return AudioRouting(AUDIO_UNMUTED, channel_stereo_right); + case channel_stereo_right: + return AudioRouting(AUDIO_MUTED, channel_stereo_right); + default: + return AudioRouting(AUDIO_UNMUTED, channel_stereo_left); + } + } + } else { + // Mono + if (muted_) { + return AudioRouting(AUDIO_UNMUTED, channel_mono); + } else { + return AudioRouting(AUDIO_MUTED, channel_mono); + } + } +} + +AudioRouting AudioRouting::convert(bool stereo_available) +{ + // Muting is not touched by conversion + + if (stereo_available) { + switch (channel_) { + case channel_mono: + // Mono -> Stereo + return AudioRouting(muted_, channel_stereo_both); + case channel_any: + // Unknown -> Unknown + return AudioRouting(muted_, channel_any); + default: + // Stereo -> Stereo + return AudioRouting(muted_, channel_); + } + } else { + switch (channel_) { + case channel_mono: + // Mono -> Mono + return AudioRouting(muted_, channel_mono); + case channel_any: + // Unknown -> Unknown + return AudioRouting(muted_, channel_any); + default: + // Stereo -> Mono + return AudioRouting(muted_, channel_mono); + } + } +} + +void AudioRouting::mergeAudioRouting(AudioRouting new_audio_routing) +{ + if (new_audio_routing.getChannel() == channel_any) { + muted_ = new_audio_routing.isMuted(); + } else { + muted_ = new_audio_routing.isMuted(); + channel_ = new_audio_routing.getChannel(); + } +} + diff --git a/ui/qt/utils/rtp_audio_routing.h b/ui/qt/utils/rtp_audio_routing.h new file mode 100644 index 00000000..51117b16 --- /dev/null +++ b/ui/qt/utils/rtp_audio_routing.h @@ -0,0 +1,55 @@ +/** @file + * + * Declarations of RTP audio routing class + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTP_AUDIO_ROUTING_H +#define RTP_AUDIO_ROUTING_H + +#include "config.h" + +#include + +typedef enum { + channel_any, // Used just for changes of mute + channel_mono, // Play + channel_stereo_left, // L + channel_stereo_right, // R + channel_stereo_both // L+R +} audio_routing_channel_t; + +class AudioRouting +{ +public: + AudioRouting() = default; + ~AudioRouting() = default; + AudioRouting(const AudioRouting &) = default; + AudioRouting &operator=(const AudioRouting &) = default; + + AudioRouting(bool muted, audio_routing_channel_t channel); + bool isMuted() { return muted_; } + void setMuted(bool muted) { muted_ = muted; } + audio_routing_channel_t getChannel() { return channel_; } + void setChannel(audio_routing_channel_t channel) { channel_ = channel; } + char const *formatAudioRoutingToString(); + AudioRouting getNextChannel(bool stereo_available); + AudioRouting convert(bool stereo_available); + void mergeAudioRouting(AudioRouting new_audio_routing); + +private: + bool muted_; + audio_routing_channel_t channel_; +}; +Q_DECLARE_METATYPE(AudioRouting) + +#define AUDIO_MUTED true +#define AUDIO_UNMUTED false + + +#endif // RTP_AUDIO_ROUTING_H diff --git a/ui/qt/utils/rtp_audio_routing_filter.cpp b/ui/qt/utils/rtp_audio_routing_filter.cpp new file mode 100644 index 00000000..8362d3f9 --- /dev/null +++ b/ui/qt/utils/rtp_audio_routing_filter.cpp @@ -0,0 +1,114 @@ +/* rtp_audio_routing_filter.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "rtp_audio_routing_filter.h" + +AudioRoutingFilter::AudioRoutingFilter(QIODevice *input, bool stereo_required, AudioRouting audio_routing) : + QIODevice(input), + input_(input), + stereo_required_(stereo_required), + audio_routing_(audio_routing) +{ + QIODevice::open(input_->openMode()); +} + +void AudioRoutingFilter::close() +{ + input_->close(); +} + +qint64 AudioRoutingFilter::size() const +{ + if (!stereo_required_) + { + return input_->size(); + } else { + // For stereo we must return twice more bytes + return input_->size() * 2; + } +} + +qint64 AudioRoutingFilter::pos() const +{ + if (!stereo_required_) + { + return input_->pos(); + } else { + // For stereo we must return twice more bytes + return input_->pos() * 2; + } +} + +bool AudioRoutingFilter::seek(qint64 off) +{ + if (!stereo_required_) + { + return input_->seek(off); + } else { + // For stereo we must return half of offset + return input_->seek(off / 2); + } +} + +qint64 AudioRoutingFilter::readData(char *data, qint64 maxSize) +{ + if (!stereo_required_) + { + // For mono we just return data + return input_->read(data, maxSize); + } else { + // For stereo + gint64 silence = 0; + + // Read half of data + qint64 readBytes = input_->read(data, maxSize/SAMPLE_BYTES); + + // If error or no data available, just return + if (readBytes < 1) + return readBytes; + + // Expand it + for(qint64 i = (readBytes / SAMPLE_BYTES) - 1; i > 0; i--) { + qint64 j = SAMPLE_BYTES * i; + if (audio_routing_.getChannel() == channel_stereo_left) { + memcpy(&data[j*2], &data[j], SAMPLE_BYTES); + memcpy(&data[j*2+SAMPLE_BYTES], &silence, SAMPLE_BYTES); + } else if (audio_routing_.getChannel() == channel_stereo_right) { + memcpy(&data[j*2], &silence, SAMPLE_BYTES); + memcpy(&data[j*2+SAMPLE_BYTES], &data[j], SAMPLE_BYTES); + } else if (audio_routing_.getChannel() == channel_stereo_both) { + memcpy(&data[j*2], &data[j], SAMPLE_BYTES); + memcpy(&data[j*2+SAMPLE_BYTES], &data[j], SAMPLE_BYTES); + } else { + // Should not happen ever + memcpy(&data[j*2], &silence, SAMPLE_BYTES*2); + } + } + + return readBytes * 2; + } +} + +qint64 AudioRoutingFilter::writeData(const char *data, qint64 maxSize) +{ + return input_->write(data, maxSize); +} + + +/* +bool AudioRoutingFilter::atEnd() const +{ + return input_->atEnd(); +} + +bool AudioRoutingFilter::canReadLine() const +{ + return input_->canReadLine(); +} +*/ diff --git a/ui/qt/utils/rtp_audio_routing_filter.h b/ui/qt/utils/rtp_audio_routing_filter.h new file mode 100644 index 00000000..de48edae --- /dev/null +++ b/ui/qt/utils/rtp_audio_routing_filter.h @@ -0,0 +1,42 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTP_AUDIO_ROUTING_FILTER_H +#define RTP_AUDIO_ROUTING_FILTER_H + +#include "config.h" + +#include +#include + +#include +#include + +class AudioRoutingFilter: public QIODevice +{ +public: + explicit AudioRoutingFilter(QIODevice *input, bool stereo_required, AudioRouting audio_routing); + ~AudioRoutingFilter() { } + + void close() override; + qint64 size() const override; + qint64 pos() const override; + bool seek(qint64 off) override; + +protected: + qint64 readData(char *data, qint64 maxSize) override; + qint64 writeData(const char *data, qint64 maxSize) override; + +private: + QIODevice *input_; + bool stereo_required_; + AudioRouting audio_routing_; +}; + +#endif // RTP_AUDIO_ROUTING_FILTER_H diff --git a/ui/qt/utils/rtp_audio_silence_generator.cpp b/ui/qt/utils/rtp_audio_silence_generator.cpp new file mode 100644 index 00000000..15289f3e --- /dev/null +++ b/ui/qt/utils/rtp_audio_silence_generator.cpp @@ -0,0 +1,47 @@ +/* rtp_audio_silence_stream.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "rtp_audio_silence_generator.h" +#include + +AudioSilenceGenerator::AudioSilenceGenerator(QObject *parent) : + QIODevice(parent), + pos_(0) +{ + QIODevice::open(QIODevice::ReadOnly); +} + +qint64 AudioSilenceGenerator::size() const +{ + return std::numeric_limits ::max(); +} + +qint64 AudioSilenceGenerator::pos() const +{ + return pos_; +} + +bool AudioSilenceGenerator::seek(qint64 off) +{ + pos_ = off; + return true; +} + +qint64 AudioSilenceGenerator::readData(char *data, qint64 maxSize) +{ + memset(data, 0, maxSize); + pos_ += maxSize; + + return maxSize; +} + +qint64 AudioSilenceGenerator::writeData(const char *data _U_, qint64 maxSize) +{ + return maxSize; +} diff --git a/ui/qt/utils/rtp_audio_silence_generator.h b/ui/qt/utils/rtp_audio_silence_generator.h new file mode 100644 index 00000000..48a22856 --- /dev/null +++ b/ui/qt/utils/rtp_audio_silence_generator.h @@ -0,0 +1,35 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTP_AUDIO_SILENCE_GENERATOR_H +#define RTP_AUDIO_SILENCE_GENERATOR_H + +#include "config.h" + +#include + +class AudioSilenceGenerator: public QIODevice +{ +public: + explicit AudioSilenceGenerator(QObject *parent = nullptr); + ~AudioSilenceGenerator() { } + + qint64 size() const override; + qint64 pos() const override; + bool seek(qint64 off) override; + +protected: + qint64 readData(char *data, qint64 maxSize) override; + qint64 writeData(const char *data, qint64 maxSize) override; + +private: + quint64 pos_; +}; + +#endif // RTP_AUDIO_SILENCE_GENERATOR_H diff --git a/ui/qt/utils/stock_icon.cpp b/ui/qt/utils/stock_icon.cpp new file mode 100644 index 00000000..dc5fb01a --- /dev/null +++ b/ui/qt/utils/stock_icon.cpp @@ -0,0 +1,253 @@ +/* stock_icon.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +// Stock icons. Based on gtk/stock_icons.h + +// Toolbar icon sizes: +// macOS freestanding: 32x32, 32x32@2x, segmented (inside a button): <= 19x19 +// Windows: 16x16, 24x24, 32x32 +// GNOME: 24x24 (default), 48x48 + +// References: +// +// https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html +// https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html +// +// https://mithatkonar.com/wiki/doku.php/qt/icons +// +// https://web.archive.org/web/20140829010224/https://developer.apple.com/library/mac/documentation/userexperience/conceptual/applehiguidelines/IconsImages/IconsImages.html +// https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/ +// https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/app-icon/ +// https://docs.microsoft.com/en-us/windows/win32/uxguide/vis-icons +// https://developer.gnome.org/hig/stable/icons-and-artwork.html.en +// https://docs.microsoft.com/en-us/visualstudio/designers/the-visual-studio-image-library + +// To do: +// - 32x32, 48x48, 64x64, and unscaled (.svg) icons. +// - Indent find & go actions when those panes are open. +// - Replace or remove: +// WIRESHARK_STOCK_CAPTURE_FILTER x-capture-filter +// WIRESHARK_STOCK_DISPLAY_FILTER x-display-filter +// GTK_STOCK_SELECT_COLOR x-coloring-rules +// GTK_STOCK_PREFERENCES preferences-system +// GTK_STOCK_HELP help-contents + +#include +#include +#include +#include +#include +#include +#include +#include + +static const QString path_pfx_ = ":/stock_icons/"; + +// Map FreeDesktop icon names to Qt standard pixmaps. +static QMap icon_name_to_standard_pixmap_; + +StockIcon::StockIcon(const QString icon_name) : + QIcon() +{ + if (icon_name_to_standard_pixmap_.isEmpty()) { + fillIconNameMap(); + } + + // Does our theme contain this icon? + // X11 only as per the QIcon documentation. + if (hasThemeIcon(icon_name)) { + QIcon theme_icon = fromTheme(icon_name); + swap(theme_icon); + return; + } + + // Is this is an icon we've manually mapped to a standard pixmap below? + if (icon_name_to_standard_pixmap_.contains(icon_name)) { + QIcon standard_icon = qApp->style()->standardIcon(icon_name_to_standard_pixmap_[icon_name]); + swap(standard_icon); + return; + } + + // Is this one of our locally sourced, cage-free, organic icons? + QStringList types = QStringList() << "8x8" << "14x14" << "16x16" << "24x14" << "24x24"; + QList icon_modes = QList() + << QIcon::Disabled + << QIcon::Active + << QIcon::Selected; + foreach (QString type, types) { + // First, check for a template (mask) icon + // Templates should be monochrome as described at + // https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/custom-icons/ + // Transparency is supported. + QString icon_path_template = path_pfx_ + QString("%1/%2.template.png").arg(type).arg(icon_name); + if (QFile::exists(icon_path_template)) { + QIcon mask_icon = QIcon(); + mask_icon.addFile(icon_path_template); + + foreach(QSize sz, mask_icon.availableSizes()) { + QPixmap mask_pm = mask_icon.pixmap(sz); + QImage normal_img(sz, QImage::Format_ARGB32); + QPainter painter(&normal_img); + QBrush br(qApp->palette().color(QPalette::Active, QPalette::WindowText)); + painter.fillRect(0, 0, sz.width(), sz.height(), br); + painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + painter.drawPixmap(0, 0, mask_pm); + + QPixmap normal_pm = QPixmap::fromImage(normal_img); + addPixmap(normal_pm, QIcon::Normal, QIcon::On); + addPixmap(normal_pm, QIcon::Normal, QIcon::Off); + + QStyleOption opt = {}; + opt.palette = qApp->palette(); + foreach (QIcon::Mode icon_mode, icon_modes) { + QPixmap mode_pm = qApp->style()->generatedIconPixmap(icon_mode, normal_pm, &opt); + addPixmap(mode_pm, icon_mode, QIcon::On); + addPixmap(mode_pm, icon_mode, QIcon::Off); + } + } + continue; + } + + // Regular full-color icons + QString icon_path = path_pfx_ + QString("%1/%2.png").arg(type).arg(icon_name); + if (QFile::exists(icon_path)) { + addFile(icon_path); + } + + // Along with each name check for ".active" and + // ".selected" for the Active and Selected modes, and + // ".on" to use for the on (checked) state. + // XXX Allow more (or all) combinations. + QString icon_path_active = path_pfx_ + QString("%1/%2.active.png").arg(type).arg(icon_name); + if (QFile::exists(icon_path_active)) { + addFile(icon_path_active, QSize(), QIcon::Active, QIcon::On); + } + + QString icon_path_selected = path_pfx_ + QString("%1/%2.selected.png").arg(type).arg(icon_name); + if (QFile::exists(icon_path_selected)) { + addFile(icon_path_selected, QSize(), QIcon::Selected, QIcon::On); + } + + QString icon_path_on = path_pfx_ + QString("%1/%2.on.png").arg(type).arg(icon_name); + if (QFile::exists(icon_path_on)) { + addFile(icon_path_on, QSize(), QIcon::Normal, QIcon::On); + } + } +} + +// Create a square icon filled with the specified color. +QIcon StockIcon::colorIcon(const QRgb bg_color, const QRgb fg_color, const QString glyph) +{ + QList sizes = QList() << 12 << 16 << 24 << 32 << 48; + QIcon color_icon; + + foreach (int size, sizes) { + QPixmap pm(size, size); + QPainter painter(&pm); + QRect border(0, 0, size - 1, size - 1); + painter.setPen(fg_color); + painter.setBrush(QColor(bg_color)); + painter.drawRect(border); + + if (!glyph.isEmpty()) { + QFont font(qApp->font()); + font.setPointSizeF(size / 2.0); + painter.setFont(font); + QRectF bounding = painter.boundingRect(pm.rect(), glyph, Qt::AlignHCenter | Qt::AlignVCenter); + painter.drawText(bounding, glyph); + } + + color_icon.addPixmap(pm); + } + return color_icon; +} + +// Create a triangle icon filled with the specified color. +QIcon StockIcon::colorIconTriangle(const QRgb bg_color, const QRgb fg_color) +{ + QList sizes = QList() << 12 << 16 << 24 << 32 << 48; + QIcon color_icon; + + foreach (int size, sizes) { + QPixmap pm(size, size); + QPainter painter(&pm); + QPainterPath triangle; + pm.fill(); + painter.fillRect(0, 0, size-1, size-1, Qt::transparent); + painter.setPen(fg_color); + painter.setBrush(QColor(bg_color)); + triangle.moveTo(0, size-1); + triangle.lineTo(size-1, size-1); + triangle.lineTo((size-1)/2, 0); + triangle.closeSubpath(); + painter.fillPath(triangle, QColor(bg_color)); + + color_icon.addPixmap(pm); + } + return color_icon; +} + +// Create a cross icon filled with the specified color. +QIcon StockIcon::colorIconCross(const QRgb bg_color, const QRgb fg_color) +{ + QList sizes = QList() << 12 << 16 << 24 << 32 << 48; + QIcon color_icon; + + foreach (int size, sizes) { + QPixmap pm(size, size); + QPainter painter(&pm); + QPainterPath cross; + pm.fill(); + painter.fillRect(0, 0, size-1, size-1, Qt::transparent); + painter.setPen(QPen(QBrush(bg_color), 3)); + painter.setBrush(QColor(fg_color)); + cross.moveTo(0, 0); + cross.lineTo(size-1, size-1); + cross.moveTo(0, size-1); + cross.lineTo(size-1, 0); + painter.drawPath(cross); + + color_icon.addPixmap(pm); + } + return color_icon; +} + +// Create a circle icon filled with the specified color. +QIcon StockIcon::colorIconCircle(const QRgb bg_color, const QRgb fg_color) +{ + QList sizes = QList() << 12 << 16 << 24 << 32 << 48; + QIcon color_icon; + + foreach (int size, sizes) { + QPixmap pm(size, size); + QPainter painter(&pm); + QRect border(2, 2, size - 3, size - 3); + pm.fill(); + painter.fillRect(0, 0, size-1, size-1, Qt::transparent); + painter.setPen(QPen(QBrush(bg_color), 3)); + painter.setBrush(QColor(fg_color)); + painter.setBrush(QColor(bg_color)); + painter.drawEllipse(border); + + color_icon.addPixmap(pm); + } + return color_icon; +} + +void StockIcon::fillIconNameMap() +{ + // Note that some of Qt's standard pixmaps are awful. We shouldn't add an + // entry just because a match can be made. + icon_name_to_standard_pixmap_["document-open"] = QStyle::SP_DirIcon; + icon_name_to_standard_pixmap_["media-playback-pause"] = QStyle::SP_MediaPause; + icon_name_to_standard_pixmap_["media-playback-start"] = QStyle::SP_MediaPlay; + icon_name_to_standard_pixmap_["media-playback-stop"] = QStyle::SP_MediaStop; +} diff --git a/ui/qt/utils/stock_icon.h b/ui/qt/utils/stock_icon.h new file mode 100644 index 00000000..5b333736 --- /dev/null +++ b/ui/qt/utils/stock_icon.h @@ -0,0 +1,40 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef STOCK_ICON_H +#define STOCK_ICON_H + +#include + +/** @file + * Goal: Beautiful icons appropriate for each of our supported platforms. + */ + +// Supported standard names: +// document-open + +// Supported custom names (see images/toolbar): +// x-capture-file-close +// x-capture-file-save + +class StockIcon : public QIcon +{ +public: + explicit StockIcon(const QString icon_name); + + static QIcon colorIcon(const QRgb bg_color, const QRgb fg_color, const QString glyph = QString()); + static QIcon colorIconTriangle(const QRgb bg_color, const QRgb fg_color); + static QIcon colorIconCross(const QRgb bg_color, const QRgb fg_color); + static QIcon colorIconCircle(const QRgb bg_color, const QRgb fg_color); + +private: + void fillIconNameMap(); +}; + +#endif // STOCK_ICON_H diff --git a/ui/qt/utils/tango_colors.h b/ui/qt/utils/tango_colors.h new file mode 100644 index 00000000..fb63dd3a --- /dev/null +++ b/ui/qt/utils/tango_colors.h @@ -0,0 +1,75 @@ +/** @file + * + * Tango theme colors + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __TANGO_COLORS_H__ +#define __TANGO_COLORS_H__ + +// http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines +// with added hues from http://emilis.info/other/extended_tango/ +// (all colors except aluminium) + +const QRgb tango_aluminium_1 = 0xeeeeec; +const QRgb tango_aluminium_2 = 0xd3d7cf; +const QRgb tango_aluminium_3 = 0xbabdb6; +const QRgb tango_aluminium_4 = 0x888a85; +const QRgb tango_aluminium_5 = 0x555753; +const QRgb tango_aluminium_6 = 0x2e3436; + +const QRgb tango_butter_1 = 0xfeffd0; +const QRgb tango_butter_2 = 0xfffc9c; +const QRgb tango_butter_3 = 0xfce94f; +const QRgb tango_butter_4 = 0xedd400; +const QRgb tango_butter_5 = 0xc4a000; +const QRgb tango_butter_6 = 0x725000; + +const QRgb tango_chameleon_1 = 0xe4ffc7; +const QRgb tango_chameleon_2 = 0xb7f774; +const QRgb tango_chameleon_3 = 0x8ae234; +const QRgb tango_chameleon_4 = 0x73d216; +const QRgb tango_chameleon_5 = 0x4e9a06; +const QRgb tango_chameleon_6 = 0x2a5703; + +const QRgb tango_chocolate_1 = 0xfaf0d7; +const QRgb tango_chocolate_2 = 0xefd0a7; +const QRgb tango_chocolate_3 = 0xe9b96e; +const QRgb tango_chocolate_4 = 0xc17d11; +const QRgb tango_chocolate_5 = 0x8f5902; +const QRgb tango_chocolate_6 = 0x503000; + +const QRgb tango_orange_1 = 0xfff0d7; +const QRgb tango_orange_2 = 0xffd797; +const QRgb tango_orange_3 = 0xfcaf3e; +const QRgb tango_orange_4 = 0xf57900; +const QRgb tango_orange_5 = 0xce5c00; +const QRgb tango_orange_6 = 0x8c3700; + +const QRgb tango_plum_1 = 0xfce0ff; +const QRgb tango_plum_2 = 0xe0c0e4; +const QRgb tango_plum_3 = 0xad7fa8; +const QRgb tango_plum_4 = 0x75507b; +const QRgb tango_plum_5 = 0x5c3566; +const QRgb tango_plum_6 = 0x371740; + +const QRgb tango_scarlet_red_1 = 0xffcccc; +const QRgb tango_scarlet_red_2 = 0xf78787; +const QRgb tango_scarlet_red_3 = 0xef2929; +const QRgb tango_scarlet_red_4 = 0xcc0000; +const QRgb tango_scarlet_red_5 = 0xa40000; +const QRgb tango_scarlet_red_6 = 0x600000; + +const QRgb tango_sky_blue_1 = 0xdaeeff; +const QRgb tango_sky_blue_2 = 0x97c4f0; +const QRgb tango_sky_blue_3 = 0x729fcf; +const QRgb tango_sky_blue_4 = 0x3465a4; +const QRgb tango_sky_blue_5 = 0x204a87; +const QRgb tango_sky_blue_6 = 0x0a3050; + +#endif // __TANGO_COLORS_H__ diff --git a/ui/qt/utils/variant_pointer.h b/ui/qt/utils/variant_pointer.h new file mode 100644 index 00000000..7a95a94c --- /dev/null +++ b/ui/qt/utils/variant_pointer.h @@ -0,0 +1,34 @@ +/** @file + * + * Range routines + * + * Roland Knall + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_VARIANT_POINTER_H_ +#define UI_QT_VARIANT_POINTER_H_ + +#include + +template class VariantPointer +{ + +public: + static T* asPtr(QVariant v) + { + return (T *) v.value(); + } + + static QVariant asQVariant(T* ptr) + { + return QVariant::fromValue((void *) ptr); + } +}; + +#endif /* UI_QT_VARIANT_POINTER_H_ */ diff --git a/ui/qt/utils/wireshark_mime_data.cpp b/ui/qt/utils/wireshark_mime_data.cpp new file mode 100644 index 00000000..f1b588d0 --- /dev/null +++ b/ui/qt/utils/wireshark_mime_data.cpp @@ -0,0 +1,52 @@ +/* wireshark_mime_data.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +const QString WiresharkMimeData::ColoringRulesMimeType = "application/vnd.wireshark.coloringrules"; +const QString WiresharkMimeData::ColumnListMimeType = "application/vnd.wireshark.columnlist"; +const QString WiresharkMimeData::FilterListMimeType = "application/vnd.wireshark.filterlist"; +const QString WiresharkMimeData::DisplayFilterMimeType = "application/vnd.wireshark.displayfilter"; + +void WiresharkMimeData::allowPlainText() +{ + setText(labelText()); +} + +ToolbarEntryMimeData::ToolbarEntryMimeData(QString element, int pos) : + WiresharkMimeData(), + element_(element), + filter_(QString()), + pos_(pos) +{} + +QString ToolbarEntryMimeData::element() const +{ + return element_; +} + +QString ToolbarEntryMimeData::labelText() const +{ + return QString("%1").arg(element_); +} + +int ToolbarEntryMimeData::position() const +{ + return pos_; +} + +void ToolbarEntryMimeData::setFilter(QString text) +{ + filter_ = text; +} + +QString ToolbarEntryMimeData::filter() const +{ + return filter_; +} diff --git a/ui/qt/utils/wireshark_mime_data.h b/ui/qt/utils/wireshark_mime_data.h new file mode 100644 index 00000000..4732f39a --- /dev/null +++ b/ui/qt/utils/wireshark_mime_data.h @@ -0,0 +1,47 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_UTILS_WIRESHARK_MIME_DATA_H_ +#define UI_QT_UTILS_WIRESHARK_MIME_DATA_H_ + +#include + +class WiresharkMimeData: public QMimeData { +public: + virtual QString labelText() const = 0; + virtual void allowPlainText(); + + static const QString ColoringRulesMimeType; + static const QString ColumnListMimeType; + static const QString FilterListMimeType; + static const QString DisplayFilterMimeType; +}; + +class ToolbarEntryMimeData: public WiresharkMimeData { + Q_OBJECT +public: + + ToolbarEntryMimeData(QString element, int pos); + + int position() const; + QString element() const; + QString filter() const; + void setFilter(QString); + + QString labelText() const override; + +private: + + QString element_; + QString filter_; + int pos_; + +}; + +#endif /* UI_QT_UTILS_WIRESHARK_MIME_DATA_H_ */ diff --git a/ui/qt/utils/wireshark_zip_helper.cpp b/ui/qt/utils/wireshark_zip_helper.cpp new file mode 100644 index 00000000..7bfa0101 --- /dev/null +++ b/ui/qt/utils/wireshark_zip_helper.cpp @@ -0,0 +1,283 @@ +/* wireshark_zip_helper.cpp + * + * Definitions for zip / unzip of files + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#ifdef HAVE_MINIZIP +#include "config.h" + +#include "glib.h" + +#include +#include +#include // For Z_DEFLATED, etc. +#include +#include + +#include "epan/prefs.h" +#include "wsutil/file_util.h" + +#include +#include +#include +#include +#include +#include + +/* Whether we are using minizip-ng and it uses an incompatible 'dos_date' + * struct member. */ +#ifdef HAVE_MZCOMPAT_DOS_DATE +#define _MZDOSDATE dos_date +#else +#define _MZDOSDATE dosDate +#endif + +bool WiresharkZipHelper::unzip(QString zipFile, QString directory, bool (*fileCheck)(QString, int), QString (*cleanName)(QString)) +{ + unzFile uf = Q_NULLPTR; + QFileInfo fi(zipFile); + QDir di(directory); + int files = 0; + + if (! fi.exists() || ! di.exists()) + return false; + + if ((uf = unzOpen64(zipFile.toUtf8().constData())) == Q_NULLPTR) + return false; + + unz_global_info64 gi; + int err; + unzGetGlobalInfo64(uf,&gi); + unsigned int nmbr = static_cast(gi.number_entry); + if (nmbr <= 0) + return false; + + QMap cleanPaths; + + for (unsigned int cnt = 0; cnt < nmbr; cnt++) + { + char filename_inzip[256]; + unz_file_info64 file_info; + err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip), + Q_NULLPTR, 0, Q_NULLPTR, 0); + if (err == UNZ_OK) + { + QString fileInZip(filename_inzip); + int fileSize = static_cast(file_info.uncompressed_size); + + /* Sanity check for the file */ + if (fileInZip.length() == 0 || (fileCheck && ! fileCheck(fileInZip, fileSize)) ) + { + if ((cnt + 1) < nmbr) + { + err = unzGoToNextFile(uf); + if (err != UNZ_OK) + { + break; + } + } + continue; + } + + if (di.exists()) + { +#ifdef _WIN32 + /* This is an additional fix for bug 16608, in which exports did contain the full path they + * where exported from, leading to imports not possible if the path does not exist on that + * machine */ + + if (fileInZip.contains(":/") || fileInZip.contains(":\\")) + { + QFileInfo fileName(fileInZip); + QFileInfo path(fileName.dir(), ""); + QString newFile = path.baseName() + "/" + fileName.baseName(); + fileInZip = newFile; + } +#endif + + QString fullPath = di.path() + "/" + fileInZip; + QFileInfo fi(fullPath); + QString dirPath = fi.absolutePath(); + + /* clean up name from import. e.g. illegal characters in name */ + if (cleanName) + { + if (! cleanPaths.keys().contains(dirPath)) + { + QString tempPath = cleanName(dirPath); + int cnt = 1; + while (QFile::exists(tempPath)) + { + tempPath = cleanName(dirPath) + QString::number(cnt); + cnt++; + } + cleanPaths.insert(dirPath, tempPath); + } + + dirPath = cleanPaths[dirPath]; + if (dirPath.length() == 0) + continue; + + fi = QFileInfo(dirPath + "/" + fi.fileName()); + fullPath = fi.absoluteFilePath(); + } + if (fullPath.length() == 0) + continue; + + QDir tP(fi.absolutePath()); + if (! tP.exists()) + di.mkpath(fi.absolutePath()); + + if (fileInZip.contains("/")) + { + QString filePath = fi.absoluteFilePath(); + QFile file(filePath); + if (! file.exists()) + { + err = unzOpenCurrentFile(uf); + if (err == UNZ_OK) + { + if (file.open(QIODevice::WriteOnly)) + { + QByteArray buf; + buf.resize(IO_BUF_SIZE); + while ((err = unzReadCurrentFile(uf, buf.data(), static_cast(buf.size()))) != UNZ_EOF) + file.write(buf.constData(), err); + + file.close(); + } + unzCloseCurrentFile(uf); + + files++; + } + } + } + } + } + + if ((cnt+1) < nmbr) + { + err = unzGoToNextFile(uf); + if (err!=UNZ_OK) + { + break; + } + } + } + + unzClose(uf); + + return files > 0 ? true : false; +} + +#ifndef UINT32_MAX +#define UINT32_MAX (0xffffffff) +#endif + +static uint32_t qDateToDosDate(QDateTime time) +{ + QDate ld = time.toLocalTime().date(); + + int year = ld.year() - 1900; + if (year >= 1980) + year -= 1980; + else if (year >= 80) + year -= 80; + else + year += 20; + + int month = ld.month() - 1; + int day = ld.day(); + + if (year < 0 || year > 127 || month < 1 || month > 31) + return 0; + + QTime lt = time.toLocalTime().time(); + + unsigned int dosDate = static_cast((day + (32 * (month + 1)) + (512 * year))); + unsigned int dosTime = static_cast((lt.second() / 2) + (32 * lt.minute()) + (2048 * lt.hour())); + + return (uint32_t)(dosDate << 16 | dosTime); +} + +void WiresharkZipHelper::addFileToZip(zipFile zf, QString filepath, QString fileInZip) +{ + QFileInfo fi(filepath); + zip_fileinfo zi; + int err = ZIP_OK; + + memset(&zi, 0, sizeof(zi)); + + QDateTime fTime = fi.lastModified(); + zi._MZDOSDATE = qDateToDosDate(fTime); + + QFile fh(filepath); + /* Checks if a large file block has to be written */ + bool isLarge = (fh.size() > UINT32_MAX); + + err = zipOpenNewFileInZip3_64(zf, fileInZip.toUtf8().constData(), &zi, + Q_NULLPTR, 0, Q_NULLPTR, 0, Q_NULLPTR, Z_DEFLATED, 9 , 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + Q_NULLPTR, 0, static_cast(isLarge)); + + if (err != ZIP_OK) + return; + + if (fh.open(QIODevice::ReadOnly)) + { + QByteArray buf; + buf.resize(IO_BUF_SIZE); + while (! fh.atEnd() && err == ZIP_OK) + { + qint64 bytesIn = fh.read(buf.data(), buf.size()); + if (bytesIn > 0 && bytesIn <= buf.size()) + { + err = zipWriteInFileInZip(zf, buf, (unsigned int) bytesIn); + } + } + fh.close(); + } + + zipCloseFileInZip(zf); +} + +bool WiresharkZipHelper::zip(QString fileName, QStringList files, QString relativeTo) +{ + + QFileInfo fi(fileName); + if (fi.exists()) + QFile::remove(fileName); + + zipFile zf = zipOpen(fileName.toUtf8().constData(), APPEND_STATUS_CREATE); + if (zf == Q_NULLPTR) + return false; + + for (int cnt = 0; cnt < files.count(); cnt++) + { + QFileInfo sf(files.at(cnt)); + QString fileInZip = sf.absoluteFilePath(); + QFileInfo relat(relativeTo); + fileInZip.replace(relat.absoluteFilePath(), ""); + /* Windows cannot open zip files, if the filenames starts with a separator */ + while (fileInZip.length() > 0 && fileInZip.startsWith("/")) + fileInZip = fileInZip.right(fileInZip.length() - 1); + + WiresharkZipHelper::addFileToZip(zf, sf.absoluteFilePath(), fileInZip); + + } + + if (zipClose(zf, Q_NULLPTR)) + return false; + + return true; +} + +#endif diff --git a/ui/qt/utils/wireshark_zip_helper.h b/ui/qt/utils/wireshark_zip_helper.h new file mode 100644 index 00000000..2f8e39f8 --- /dev/null +++ b/ui/qt/utils/wireshark_zip_helper.h @@ -0,0 +1,36 @@ +/** @file + * + * Definitions for zip / unzip of files + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WS_ZIP_HELPER_H +#define WS_ZIP_HELPER_H + +#include "config.h" + +#include + +#ifdef HAVE_MINIZIP + +#include "minizip/zip.h" + +class WiresharkZipHelper +{ +public: + static bool zip(QString zipFile, QStringList files, QString relativeTo = QString()); + static bool unzip(QString zipFile, QString directory, bool (*fileCheck)(QString fileName, int fileSize) = Q_NULLPTR, QString (*cleanName)(QString name) = Q_NULLPTR); + +protected: + static void addFileToZip(zipFile zf, QString filepath, QString fileInZip); + +}; + +#endif + +#endif // WS_ZIP_HELPER_H diff --git a/ui/qt/voip_calls_dialog.cpp b/ui/qt/voip_calls_dialog.cpp new file mode 100644 index 00000000..b8a54bd7 --- /dev/null +++ b/ui/qt/voip_calls_dialog.cpp @@ -0,0 +1,842 @@ +/* voip_calls_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "voip_calls_dialog.h" +#include + +#include "file.h" + +#include "epan/addr_resolv.h" +#include "epan/dissectors/packet-h225.h" + +#include "ui/rtp_stream.h" +#include "ui/rtp_stream_id.h" + +#include +#include "rtp_player_dialog.h" +#include "sequence_dialog.h" +#include +#include "progress_frame.h" +#include "main_application.h" +#include + +#include +#include +#include + +// To do: +// - More context menu items +// - 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. + +enum { voip_calls_type_ = 1000 }; + +VoipCallsDialog *VoipCallsDialog::pinstance_voip_{nullptr}; +VoipCallsDialog *VoipCallsDialog::pinstance_sip_{nullptr}; +std::mutex VoipCallsDialog::init_mutex_; + +VoipCallsDialog *VoipCallsDialog::openVoipCallsDialogVoip(QWidget &parent, CaptureFile &cf, QObject *packet_list) +{ + std::lock_guard lock(init_mutex_); + if (pinstance_voip_ == nullptr) + { + pinstance_voip_ = new VoipCallsDialog(parent, cf, false); + connect(pinstance_voip_, SIGNAL(goToPacket(int)), + packet_list, SLOT(goToPacket(int))); + } + return pinstance_voip_; +} + +VoipCallsDialog *VoipCallsDialog::openVoipCallsDialogSip(QWidget &parent, CaptureFile &cf, QObject *packet_list) +{ + std::lock_guard lock(init_mutex_); + if (pinstance_sip_ == nullptr) + { + pinstance_sip_ = new VoipCallsDialog(parent, cf, true); + connect(pinstance_sip_, SIGNAL(goToPacket(int)), + packet_list, SLOT(goToPacket(int))); + } + return pinstance_sip_; +} + +VoipCallsDialog::VoipCallsDialog(QWidget &parent, CaptureFile &cf, bool all_flows) : + WiresharkDialog(parent, cf), + all_flows_(all_flows), + ui(new Ui::VoipCallsDialog), + parent_(parent), + voip_calls_tap_listeners_removed_(false) +{ + ui->setupUi(this); + loadGeometry(parent.width() * 4 / 5, parent.height() * 2 / 3); + ui->callTreeView->installEventFilter(this); + + // 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); + cache_model_ = new CacheProxyModel(this); + cache_model_->setSourceModel(call_infos_model_); + sorted_model_ = new VoipCallsInfoSortedModel(this); + sorted_model_->setSourceModel(cache_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")); + + sequence_button_ = ui->buttonBox->addButton(ui->actionFlowSequence->text(), QDialogButtonBox::ActionRole); + sequence_button_->setToolTip(ui->actionFlowSequence->toolTip()); + prepare_button_ = ui->buttonBox->addButton(ui->actionPrepareFilter->text(), QDialogButtonBox::ActionRole); + prepare_button_->setToolTip(ui->actionPrepareFilter->toolTip()); + player_button_ = RtpPlayerDialog::addPlayerButton(ui->buttonBox, this); + + connect (ui->todCheckBox, &QAbstractButton::toggled, this, &VoipCallsDialog::switchTimeOfDay); + + copy_button_ = ui->buttonBox->addButton(ui->actionCopyButton->text(), QDialogButtonBox::ActionRole); + copy_button_->setToolTip(ui->actionCopyButton->toolTip()); + QMenu *copy_menu = new QMenu(copy_button_); + QAction *ca; + ca = copy_menu->addAction(tr("as CSV")); + connect(ca, SIGNAL(triggered()), this, SLOT(copyAsCSV())); + ca = copy_menu->addAction(tr("as YAML")); + connect(ca, SIGNAL(triggered()), this, SLOT(copyAsYAML())); + copy_button_->setMenu(copy_menu); + connect(&cap_file_, SIGNAL(captureEvent(CaptureEvent)), + this, SLOT(captureEvent(CaptureEvent))); + + connect(this, SIGNAL(rtpStreamsDialogSelectRtpStreams(QVector)), &parent_, SLOT(rtpStreamsDialogSelectRtpStreams(QVector))); + connect(this, SIGNAL(rtpStreamsDialogDeselectRtpStreams(QVector)), &parent_, SLOT(rtpStreamsDialogDeselectRtpStreams(QVector))); + + memset (&tapinfo_, 0, sizeof(tapinfo_)); + tapinfo_.tap_packet = tapPacket; + tapinfo_.tap_reset = tapReset; + tapinfo_.tap_draw = tapDraw; + tapinfo_.tap_data = this; + tapinfo_.callsinfos = g_queue_new(); + tapinfo_.h225_cstype = H225_OTHER; + tapinfo_.fs_option = all_flows_ ? FLOW_ALL : FLOW_ONLY_INVITES; /* flow show option */ + tapinfo_.graph_analysis = sequence_analysis_info_new(); + tapinfo_.graph_analysis->name = "voip"; + sequence_info_ = new SequenceInfo(tapinfo_.graph_analysis); + shown_callsinfos_ = g_queue_new(); + + voip_calls_init_all_taps(&tapinfo_); + if (cap_file_.isValid() && cap_file_.capFile()->dfilter) { + // Activate display filter checking + tapinfo_.apply_display_filter = true; + ui->displayFilterCheckBox->setChecked(true); + } + + connect(ui->displayFilterCheckBox, &QCheckBox::toggled, + this, &VoipCallsDialog::displayFilterCheckBoxToggled); + connect(this, SIGNAL(updateFilter(QString, bool)), + &parent, SLOT(filterPackets(QString, bool))); + connect(&parent, SIGNAL(displayFilterSuccess(bool)), + this, SLOT(displayFilterSuccess(bool))); + connect(this, SIGNAL(rtpPlayerDialogReplaceRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogReplaceRtpStreams(QVector))); + connect(this, SIGNAL(rtpPlayerDialogAddRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogAddRtpStreams(QVector))); + connect(this, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector)), + &parent, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector))); + + ProgressFrame::addToButtonBox(ui->buttonBox, &parent); + + updateWidgets(); + + if (cap_file_.isValid()) { + tapinfo_.session = cap_file_.capFile()->epan; + cap_file_.delayedRetapPackets(); + } +} + +bool VoipCallsDialog::eventFilter(QObject *, QEvent *event) +{ + if (ui->callTreeView->hasFocus() && event->type() == QEvent::KeyPress) { + QKeyEvent &keyEvent = static_cast(*event); + switch(keyEvent.key()) { + case Qt::Key_I: + if (keyEvent.modifiers() == Qt::ControlModifier) { + // Ctrl+I + on_actionSelectInvert_triggered(); + return true; + } + break; + case Qt::Key_A: + if (keyEvent.modifiers() == Qt::ControlModifier) { + // Ctrl+A + on_actionSelectAll_triggered(); + return true; + } else if (keyEvent.modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) { + // Ctrl+Shift+A + on_actionSelectNone_triggered(); + return true; + } + break; + case Qt::Key_S: + on_actionSelectRtpStreams_triggered(); + break; + case Qt::Key_D: + on_actionDeselectRtpStreams_triggered(); + break; + default: + break; + } + } + return false; +} + +VoipCallsDialog::~VoipCallsDialog() +{ + std::lock_guard lock(init_mutex_); + if ((all_flows_ && (pinstance_sip_ != nullptr)) + || (!all_flows_ && (pinstance_voip_ != nullptr)) + ) { + delete ui; + + voip_calls_reset_all_taps(&tapinfo_); + if (!voip_calls_tap_listeners_removed_) { + voip_calls_remove_all_tap_listeners(&tapinfo_); + voip_calls_tap_listeners_removed_ = true; + } + sequence_info_->unref(); + g_queue_free(tapinfo_.callsinfos); + // We don't need to clear shown_callsinfos_ data, it was shared + // with tapinfo_.callsinfos and was cleared + // during voip_calls_reset_all_taps + g_queue_free(shown_callsinfos_); + if (all_flows_) { + pinstance_sip_ = nullptr; + } else { + pinstance_voip_ = nullptr; + } + } +} + +void VoipCallsDialog::removeTapListeners() +{ + if (!voip_calls_tap_listeners_removed_) { + voip_calls_remove_all_tap_listeners(&tapinfo_); + voip_calls_tap_listeners_removed_ = true; + } + WiresharkDialog::removeTapListeners(); +} + +void VoipCallsDialog::captureFileClosing() +{ + // The time formatting is currently provided by VoipCallsInfoModel, but when + // the cache is active, the ToD cannot be modified. + cache_model_->setSourceModel(NULL); + if (!voip_calls_tap_listeners_removed_) { + voip_calls_remove_all_tap_listeners(&tapinfo_); + voip_calls_tap_listeners_removed_ = true; + } + tapinfo_.session = NULL; + + WiresharkDialog::captureFileClosing(); +} + +void VoipCallsDialog::captureFileClosed() +{ + // The time formatting is currently provided by VoipCallsInfoModel, but when + // the cache is active, the ToD cannot be modified. + ui->todCheckBox->setEnabled(false); + ui->displayFilterCheckBox->setEnabled(false); + + WiresharkDialog::captureFileClosed(); +} + +void VoipCallsDialog::contextMenuEvent(QContextMenuEvent *event) +{ + bool selected = ui->callTreeView->selectionModel()->hasSelection(); + + if (! selected) + return; + + QMenu *popupMenu = new QMenu(this); + QAction *action; + + popupMenu->setAttribute(Qt::WA_DeleteOnClose); + popupMenu->addMenu(ui->menuSelect); + action = popupMenu->addAction(tr("Display time as time of day"), this, SLOT(switchTimeOfDay())); + action->setCheckable(true); + action->setChecked(call_infos_model_->timeOfDay()); + action->setEnabled(!file_closed_); + popupMenu->addSeparator(); + action = popupMenu->addAction(tr("Copy as CSV"), this, SLOT(copyAsCSV())); + action->setToolTip(tr("Copy stream list as CSV.")); + action = popupMenu->addAction(tr("Copy as YAML"), this, SLOT(copyAsYAML())); + action->setToolTip(tr("Copy stream list as YAML.")); + popupMenu->addSeparator(); + popupMenu->addAction(ui->actionSelectRtpStreams); + popupMenu->addAction(ui->actionDeselectRtpStreams); + + popupMenu->popup(event->globalPos()); +} + +void VoipCallsDialog::changeEvent(QEvent *event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + +void VoipCallsDialog::captureEvent(CaptureEvent e) +{ + if (e.captureContext() == CaptureEvent::Retap) + { + switch (e.eventType()) + { + case CaptureEvent::Started: + ui->displayFilterCheckBox->setEnabled(false); + break; + case CaptureEvent::Finished: + ui->displayFilterCheckBox->setEnabled(true); + break; + default: + break; + } + } + +} + +void VoipCallsDialog::tapReset(void *tapinfo_ptr) +{ + voip_calls_tapinfo_t *tapinfo = static_cast(tapinfo_ptr); + VoipCallsDialog *voip_calls_dialog = static_cast(tapinfo->tap_data); + + // Create new callsinfos queue in tapinfo. Current callsinfos are + // in shown_callsinfos_, so don't free the [shared] data stored in + // the queue, but do free the queue itself. (Do this before calling + // voip_calls_reset_all_taps(), as that frees the data in the queue.) + g_queue_free(voip_calls_dialog->tapinfo_.callsinfos); + voip_calls_dialog->tapinfo_.callsinfos = g_queue_new(); + voip_calls_reset_all_taps(tapinfo); + + // Leave old graph_analysis as is and allocate new one + voip_calls_dialog->sequence_info_->unref(); + voip_calls_dialog->tapinfo_.graph_analysis = sequence_analysis_info_new(); + voip_calls_dialog->tapinfo_.graph_analysis->name = "voip"; + voip_calls_dialog->sequence_info_ = new SequenceInfo(voip_calls_dialog->tapinfo_.graph_analysis); +} + +tap_packet_status VoipCallsDialog::tapPacket(void *, packet_info *, epan_dissect_t *, const void *, tap_flags_t) +{ +#ifdef QT_MULTIMEDIA_LIB +// voip_calls_tapinfo_t *tapinfo = (voip_calls_tapinfo_t *) tapinfo_ptr; + // add_rtp_packet for voip player. +// return TAP_PACKET_REDRAW; +#endif + return TAP_PACKET_DONT_REDRAW; +} + +void VoipCallsDialog::tapDraw(void *tapinfo_ptr) +{ + voip_calls_tapinfo_t *tapinfo = static_cast(tapinfo_ptr); + + if (!tapinfo || !tapinfo->redraw) { + return; + } + + GList *graph_item = g_queue_peek_nth_link(tapinfo->graph_analysis->items, 0); + for (; graph_item; graph_item = gxx_list_next(graph_item)) { + for (GList *rsi_entry = g_list_first(tapinfo->rtpstream_list); rsi_entry; rsi_entry = gxx_list_next(rsi_entry)) { + seq_analysis_item_t * sai = gxx_list_data(seq_analysis_item_t *, graph_item); + rtpstream_info_t *rsi = gxx_list_data(rtpstream_info_t *, rsi_entry); + + if (rsi->start_fd->num == sai->frame_number) { + rsi->call_num = sai->conv_num; + // VOIP_CALLS_DEBUG("setting conv num %u for frame %u", sai->conv_num, sai->frame_number); + } + } + } + + VoipCallsDialog *voip_calls_dialog = static_cast(tapinfo->tap_data); + if (voip_calls_dialog) { + voip_calls_dialog->updateCalls(); + } +} + +gint VoipCallsDialog::compareCallNums(gconstpointer a, gconstpointer b) +{ + const voip_calls_info_t *call_a = (const voip_calls_info_t *)a; + const voip_calls_info_t *call_b = (const voip_calls_info_t *)b; + + return (call_a->call_num != call_b->call_num); +} + +void VoipCallsDialog::updateCalls() +{ + voip_calls_info_t *new_callsinfo; + voip_calls_info_t *old_callsinfo; + GList *found; + + ui->callTreeView->setSortingEnabled(false); + + // Merge new callsinfos with old ones + // It keeps list of calls visible including selected items + GList *list = g_queue_peek_nth_link(tapinfo_.callsinfos, 0); + while (list) { + // Find new callsinfo + new_callsinfo = gxx_list_data(voip_calls_info_t*, list); + found = g_queue_find_custom(shown_callsinfos_, new_callsinfo, VoipCallsDialog::compareCallNums); + if (!found) { + // New call, add it to list for show + g_queue_push_tail(shown_callsinfos_, new_callsinfo); + } else { + // Existing call + old_callsinfo = (voip_calls_info_t *)found->data; + if (new_callsinfo != old_callsinfo) { + // Replace it + voip_calls_free_callsinfo(old_callsinfo); + found->data = new_callsinfo; + } + } + + list = gxx_list_next(list); + } + + // Update model + call_infos_model_->updateCalls(shown_callsinfos_); + + // Resize columns + for (int i = 0; i < call_infos_model_->columnCount(); i++) { + ui->callTreeView->resizeColumnToContents(i); + } + + ui->callTreeView->setSortingEnabled(true); + + updateWidgets(); +} + +void VoipCallsDialog::updateWidgets() +{ + bool selected = ui->callTreeView->selectionModel()->hasSelection(); + bool have_ga_items = false; + + if (tapinfo_.graph_analysis && tapinfo_.graph_analysis->items) { + have_ga_items = true; + } + + bool enable = selected && have_ga_items && !file_closed_; + + prepare_button_->setEnabled(enable); + sequence_button_->setEnabled(enable); + ui->actionSelectRtpStreams->setEnabled(enable); + ui->actionDeselectRtpStreams->setEnabled(enable); +#if defined(QT_MULTIMEDIA_LIB) + player_button_->setEnabled(enable); +#endif + + WiresharkDialog::updateWidgets(); +} + +void VoipCallsDialog::prepareFilter() +{ + if (!ui->callTreeView->selectionModel()->hasSelection() || !tapinfo_.graph_analysis) { + return; + } + + QString filter_str; + QSet selected_calls; + QString frame_numbers; + QList rows; + + /* Build a new filter based on frame numbers */ + foreach (QModelIndex index, ui->callTreeView->selectionModel()->selectedIndexes()) { + if (index.isValid() && ! rows.contains(index.row())) + { + voip_calls_info_t *call_info = VoipCallsInfoModel::indexToCallInfo(index); + if (!call_info) { + return; + } + + selected_calls << call_info->call_num; + rows << index.row(); + } + } + + GList *cur_ga_item = g_queue_peek_nth_link(tapinfo_.graph_analysis->items, 0); + while (cur_ga_item && cur_ga_item->data) { + seq_analysis_item_t *ga_item = gxx_list_data(seq_analysis_item_t*, cur_ga_item); + if (selected_calls.contains(ga_item->conv_num)) { + frame_numbers += QString("%1,").arg(ga_item->frame_number); + } + cur_ga_item = gxx_list_next(cur_ga_item); + } + + if (!frame_numbers.isEmpty()) { + frame_numbers.chop(1); + filter_str = QString("frame.number in {%1} or rtp.setup-frame in {%1}").arg(frame_numbers); + } + +#if 0 + // XXX The GTK+ UI falls back to building a filter based on protocols if the filter + // length is too long. Leaving this here for the time being in case we need to do + // the same in the Qt UI. + const sip_calls_info_t *sipinfo; + const isup_calls_info_t *isupinfo; + const h323_calls_info_t *h323info; + const h245_address_t *h245_add = NULL; + const gcp_ctx_t* ctx; + char *guid_str; + + if (filter_length < max_filter_length) { + gtk_editable_insert_text(GTK_EDITABLE(main_display_filter_widget), filter_string_fwd->str, -1, &pos); + } else { + g_string_free(filter_string_fwd, TRUE); + filter_string_fwd = g_string_new(filter_prepend); + + g_string_append_printf(filter_string_fwd, "("); + is_first = TRUE; + /* Build a new filter based on protocol fields */ + lista = g_queue_peek_nth_link(voip_calls_get_info()->callsinfos, 0); + while (lista) { + listinfo = gxx_list_data(voip_calls_info_t *, lista); + if (listinfo->selected) { + if (!is_first) + g_string_append_printf(filter_string_fwd, " or "); + switch (listinfo->protocol) { + case VOIP_SIP: + sipinfo = (sip_calls_info_t *)listinfo->prot_info; + g_string_append_printf(filter_string_fwd, + "(sip.Call-ID == \"%s\")", + sipinfo->call_identifier + ); + break; + case VOIP_ISUP: + isupinfo = (isup_calls_info_t *)listinfo->prot_info; + g_string_append_printf(filter_string_fwd, + "(isup.cic == %i and frame.number >= %i and frame.number <= %i and mtp3.network_indicator == %i and ((mtp3.dpc == %i) and (mtp3.opc == %i)) or ((mtp3.dpc == %i) and (mtp3.opc == %i)))", + isupinfo->cic, listinfo->start_fd->num, + listinfo->stop_fd->num, + isupinfo->ni, isupinfo->dpc, isupinfo->opc, + isupinfo->opc, isupinfo->dpc + ); + break; + case VOIP_H323: + { + h323info = (h323_calls_info_t *)listinfo->prot_info; + guid_str = guid_to_str(NULL, &h323info->guid[0]); + g_string_append_printf(filter_string_fwd, + "((h225.guid == %s || q931.call_ref == %x:%x || q931.call_ref == %x:%x)", + guid_str, + (guint8) (h323info->q931_crv & 0x00ff), + (guint8)((h323info->q931_crv & 0xff00)>>8), + (guint8) (h323info->q931_crv2 & 0x00ff), + (guint8)((h323info->q931_crv2 & 0xff00)>>8)); + listb = g_list_first(h323info->h245_list); + wmem_free(NULL, guid_str); + while (listb) { + h245_add = gxx_list_data(h245_address_t *, listb); + g_string_append_printf(filter_string_fwd, + " || (ip.addr == %s && tcp.port == %d && h245)", + address_to_qstring(&h245_add->h245_address), h245_add->h245_port); + listb = gxx_list_next(listb); + } + g_string_append_printf(filter_string_fwd, ")"); + } + break; + case TEL_H248: + ctx = (gcp_ctx_t *)listinfo->prot_info; + g_string_append_printf(filter_string_fwd, + "(h248.ctx == 0x%x)", ctx->id); + break; + default: + /* placeholder to assure valid display filter expression */ + g_string_append_printf(filter_string_fwd, + "(frame)"); + break; + } + is_first = FALSE; + } + lista = gxx_list_next(lista); + } + + g_string_append_printf(filter_string_fwd, ")"); + gtk_editable_insert_text(GTK_EDITABLE(main_display_filter_widget), filter_string_fwd->str, -1, &pos); + } +#endif + + emit updateFilter(filter_str); +} + +void VoipCallsDialog::showSequence() +{ + if (file_closed_) return; + + QSet selected_calls; + foreach (QModelIndex index, ui->callTreeView->selectionModel()->selectedIndexes()) { + voip_calls_info_t *call_info = VoipCallsInfoModel::indexToCallInfo(index); + if (!call_info) { + return; + } + selected_calls << call_info->call_num; + } + + sequence_analysis_list_sort(tapinfo_.graph_analysis); + GList *cur_ga_item = g_queue_peek_nth_link(tapinfo_.graph_analysis->items, 0); + while (cur_ga_item && cur_ga_item->data) { + seq_analysis_item_t *ga_item = gxx_list_data(seq_analysis_item_t*, cur_ga_item); + ga_item->display = selected_calls.contains(ga_item->conv_num); + cur_ga_item = gxx_list_next(cur_ga_item); + } + + SequenceDialog *sequence_dialog = new SequenceDialog(parent_, cap_file_, sequence_info_); + // Bypass this dialog and forward signals to parent + connect(sequence_dialog, SIGNAL(rtpStreamsDialogSelectRtpStreams(QVector)), &parent_, SLOT(rtpStreamsDialogSelectRtpStreams(QVector))); + connect(sequence_dialog, SIGNAL(rtpStreamsDialogDeselectRtpStreams(QVector)), &parent_, SLOT(rtpStreamsDialogDeselectRtpStreams(QVector))); + connect(sequence_dialog, SIGNAL(rtpPlayerDialogReplaceRtpStreams(QVector)), &parent_, SLOT(rtpPlayerDialogReplaceRtpStreams(QVector))); + connect(sequence_dialog, SIGNAL(rtpPlayerDialogAddRtpStreams(QVector)), &parent_, SLOT(rtpPlayerDialogAddRtpStreams(QVector))); + connect(sequence_dialog, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector)), &parent_, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector))); + + sequence_dialog->setAttribute(Qt::WA_DeleteOnClose); + sequence_dialog->enableVoIPFeatures(); + sequence_dialog->show(); +} + +QVectorVoipCallsDialog::getSelectedRtpIds() +{ + QVector stream_ids; + 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_.rtpstream_list); rsi_entry; rsi_entry = gxx_list_next(rsi_entry)) { + rtpstream_info_t *rsi = gxx_list_data(rtpstream_info_t *, rsi_entry); + if (!rsi) continue; + + //VOIP_CALLS_DEBUG("checking call %u, start frame %u == stream call %u, start frame %u, setup frame %u", + // vci->call_num, vci->start_fd->num, + // rsi->call_num, rsi->start_fd->num, rsi->setup_frame_number); + if (vci->call_num == static_cast(rsi->call_num)) { + //VOIP_CALLS_DEBUG("adding call number %u", vci->call_num); + if (-1 == stream_ids.indexOf(&(rsi->id))) { + // Add only new stream + stream_ids << &(rsi->id); + } + } + } + } + + return stream_ids; +} + +void VoipCallsDialog::rtpPlayerReplace() +{ + if (ui->callTreeView->selectionModel()->selectedIndexes().count() < 1) return; + + emit rtpPlayerDialogReplaceRtpStreams(getSelectedRtpIds()); +} + +void VoipCallsDialog::rtpPlayerAdd() +{ + if (ui->callTreeView->selectionModel()->selectedIndexes().count() < 1) return; + + emit rtpPlayerDialogAddRtpStreams(getSelectedRtpIds()); +} + +void VoipCallsDialog::rtpPlayerRemove() +{ + if (ui->callTreeView->selectionModel()->selectedIndexes().count() < 1) return; + + emit rtpPlayerDialogRemoveRtpStreams(getSelectedRtpIds()); +} + +QList VoipCallsDialog::streamRowData(int row) const +{ + QList row_data; + + if (row >= sorted_model_->rowCount()) { + return row_data; + } + + for (int col = 0; col < sorted_model_->columnCount(); col++) { + if (row < 0) { + row_data << sorted_model_->headerData(col, Qt::Horizontal); + } else { + row_data << sorted_model_->index(row, col).data(); + } + } + return row_data; +} + +void VoipCallsDialog::on_callTreeView_activated(const QModelIndex &index) +{ + voip_calls_info_t *call_info = VoipCallsInfoModel::indexToCallInfo(index); + if (!call_info) { + return; + } + emit goToPacket(call_info->start_fd->num); +} + +void VoipCallsDialog::selectAll() +{ + ui->callTreeView->selectAll(); +} + +void VoipCallsDialog::selectNone() +{ + ui->callTreeView->clearSelection(); +} + +void VoipCallsDialog::copyAsCSV() +{ + QString csv; + QTextStream stream(&csv, QIODevice::Text); + for (int row = -1; row < sorted_model_->rowCount(); row++) { + QStringList rdsl; + foreach (QVariant v, streamRowData(row)) { + QString strval = v.toString(); + // XXX should quotes (") in strval be stripped/sanitized? + rdsl << QString("\"%1\"").arg(strval); + } + stream << rdsl.join(",") << '\n'; + } + mainApp->clipboard()->setText(stream.readAll()); +} + +void VoipCallsDialog::copyAsYAML() +{ + QString yaml; + QTextStream stream(&yaml, QIODevice::Text); + stream << "---" << '\n'; + for (int row = -1; row < sorted_model_->rowCount(); row++) { + stream << "-" << '\n'; + foreach (QVariant v, streamRowData(row)) { + stream << " - " << v.toString() << '\n'; + } + } + mainApp->clipboard()->setText(stream.readAll()); +} + +void VoipCallsDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + if (button == prepare_button_) { + prepareFilter(); + } else if (button == sequence_button_) { + showSequence(); + } +} + +void VoipCallsDialog::removeAllCalls() +{ + voip_calls_info_t *callsinfo; + GList *list = NULL; + + call_infos_model_->removeAllCalls(); + + /* Free shown callsinfos */ + list = g_queue_peek_nth_link(shown_callsinfos_, 0); + while (list) + { + callsinfo = (voip_calls_info_t *)list->data; + voip_calls_free_callsinfo(callsinfo); + list = g_list_next(list); + } + g_queue_clear(shown_callsinfos_); +} + +void VoipCallsDialog::displayFilterCheckBoxToggled(bool checked) +{ + if (!cap_file_.isValid()) { + return; + } + + tapinfo_.apply_display_filter = checked; + removeAllCalls(); + + cap_file_.retapPackets(); +} + +void VoipCallsDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_TELEPHONY_VOIP_CALLS_DIALOG); +} + +void VoipCallsDialog::switchTimeOfDay() +{ + bool checked = ! call_infos_model_->timeOfDay(); + + ui->todCheckBox->setChecked(checked); + call_infos_model_->setTimeOfDay(checked); + ui->callTreeView->resizeColumnToContents(VoipCallsInfoModel::StartTime); + ui->callTreeView->resizeColumnToContents(VoipCallsInfoModel::StopTime); +} + +void VoipCallsDialog::displayFilterSuccess(bool success) +{ + if (success && ui->displayFilterCheckBox->isChecked()) { + removeAllCalls(); + cap_file_.retapPackets(); + } +} + +void VoipCallsDialog::invertSelection() +{ + QModelIndex rootIndex = ui->callTreeView->rootIndex(); + QModelIndex first = sorted_model_->index(0, 0, QModelIndex()); + int numOfItems = sorted_model_->rowCount(rootIndex); + int numOfCols = sorted_model_->columnCount(rootIndex); + QModelIndex last = sorted_model_->index(numOfItems - 1, numOfCols - 1, QModelIndex()); + + QItemSelection selection(first, last); + ui->callTreeView->selectionModel()->select(selection, QItemSelectionModel::Toggle); +} + +void VoipCallsDialog::on_actionSelectAll_triggered() +{ + ui->callTreeView->selectAll(); +} + +void VoipCallsDialog::on_actionSelectInvert_triggered() +{ + invertSelection(); +} + +void VoipCallsDialog::on_actionSelectNone_triggered() +{ + ui->callTreeView->clearSelection(); +} + +void VoipCallsDialog::on_actionSelectRtpStreams_triggered() +{ + QVectorstream_ids = qvector_rtpstream_ids_copy(getSelectedRtpIds()); + + emit rtpStreamsDialogSelectRtpStreams(stream_ids); + + qvector_rtpstream_ids_free(stream_ids); + raise(); +} + +void VoipCallsDialog::on_actionDeselectRtpStreams_triggered() +{ + QVectorstream_ids = qvector_rtpstream_ids_copy(getSelectedRtpIds()); + + emit rtpStreamsDialogDeselectRtpStreams(stream_ids); + + qvector_rtpstream_ids_free(stream_ids); + raise(); +} + diff --git a/ui/qt/voip_calls_dialog.h b/ui/qt/voip_calls_dialog.h new file mode 100644 index 00000000..0386dfa7 --- /dev/null +++ b/ui/qt/voip_calls_dialog.h @@ -0,0 +1,144 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef VOIP_CALLS_DIALOG_H +#define VOIP_CALLS_DIALOG_H + +#include + +#include +#include + +#include "cfile.h" + +#include "ui/voip_calls.h" +#include "ui/rtp_stream.h" +#include "ui/rtp_stream_id.h" + +#include +#include +#include "ui/rtp_stream_id.h" +#include "wireshark_dialog.h" + +#include +#include +#include +#include + +class SequenceInfo; + +namespace Ui { +class VoipCallsDialog; +} + +// Singleton by https://refactoring.guru/design-patterns/singleton/cpp/example#example-1 +class VoipCallsDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + /** + * Returns singleton + */ + static VoipCallsDialog *openVoipCallsDialogVoip(QWidget &parent, CaptureFile &cf, QObject *packet_list); + static VoipCallsDialog *openVoipCallsDialogSip(QWidget &parent, CaptureFile &cf, QObject *packet_list); + + /** + * Should not be clonnable and assignable + */ + VoipCallsDialog(VoipCallsDialog &other) = delete; + void operator=(const VoipCallsDialog &) = delete; + +signals: + void updateFilter(QString filter, bool force = false); + void captureFileChanged(capture_file *cf); + void goToPacket(int packet_num); + void rtpPlayerDialogReplaceRtpStreams(QVector stream_ids); + void rtpPlayerDialogAddRtpStreams(QVector stream_ids); + void rtpPlayerDialogRemoveRtpStreams(QVector stream_ids); + void rtpStreamsDialogSelectRtpStreams(QVector stream_ids); + void rtpStreamsDialogDeselectRtpStreams(QVector stream_ids); + +public slots: + void displayFilterSuccess(bool success); + void rtpPlayerReplace(); + void rtpPlayerAdd(); + void rtpPlayerRemove(); + +protected: + explicit VoipCallsDialog(QWidget &parent, CaptureFile &cf, bool all_flows = false); + ~VoipCallsDialog(); + + void contextMenuEvent(QContextMenuEvent *event); + virtual void removeTapListeners(); + void captureFileClosing(); + void captureFileClosed(); + bool eventFilter(QObject *obj, QEvent *event); + +protected slots: + void changeEvent(QEvent* event); + +private: + // We have two singletones - one for all protocols, one for sip protocol + static VoipCallsDialog *pinstance_voip_; + static VoipCallsDialog *pinstance_sip_; + bool all_flows_; + static std::mutex init_mutex_; + + Ui::VoipCallsDialog *ui; + VoipCallsInfoModel *call_infos_model_; + CacheProxyModel *cache_model_; + QSortFilterProxyModel *sorted_model_; + + QWidget &parent_; + voip_calls_tapinfo_t tapinfo_; + SequenceInfo *sequence_info_; + QPushButton *prepare_button_; + QPushButton *sequence_button_; + QToolButton *player_button_; + QPushButton *copy_button_; + bool voip_calls_tap_listeners_removed_; + GQueue* shown_callsinfos_; /* queue with all shown calls (voip_calls_info_t) */ + + // Tap callbacks + static void tapReset(void *tapinfo_ptr); + static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data, tap_flags_t flags); + static void tapDraw(void *tapinfo_ptr); + static gint compareCallNums(gconstpointer a, gconstpointer b); + + void updateCalls(); + void prepareFilter(); + void showSequence(); + void showPlayer(); + void removeAllCalls(); + void invertSelection(); + + QList streamRowData(int row) const; + QVectorgetSelectedRtpIds(); + +private slots: + void selectAll(); + void selectNone(); + void copyAsCSV(); + void copyAsYAML(); + void switchTimeOfDay(); + void on_callTreeView_activated(const QModelIndex &index); + void on_buttonBox_clicked(QAbstractButton *button); + void on_buttonBox_helpRequested(); + void updateWidgets(); + void captureEvent(CaptureEvent e); + void displayFilterCheckBoxToggled(bool checked); + void on_actionSelectAll_triggered(); + void on_actionSelectInvert_triggered(); + void on_actionSelectNone_triggered(); + void on_actionSelectRtpStreams_triggered(); + void on_actionDeselectRtpStreams_triggered(); +}; + +#endif // VOIP_CALLS_DIALOG_H diff --git a/ui/qt/voip_calls_dialog.ui b/ui/qt/voip_calls_dialog.ui new file mode 100644 index 00000000..cd9bf1db --- /dev/null +++ b/ui/qt/voip_calls_dialog.ui @@ -0,0 +1,214 @@ + + + VoipCallsDialog + + + + 0 + 0 + 750 + 430 + + + + VoIP Calls + + + + + + QAbstractItemView::ExtendedSelection + + + Qt::ElideMiddle + + + false + + + true + + + false + + + + + + + <small></small> + + + + + + + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + Limit to display filter + + + + + + + Time of Day + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + Flow &Sequence + + + Show flow sequence for selected call(s). + + + + + Prepare &Filter + + + Prepare a filter matching the selected calls(s). + + + + + Cop&y + + + Open copy menu + + + + + Select + + + true + + + + + + + + All + + + Select all + + + Ctrl+A + + + + + None + + + Clear selection + + + Ctrl+Shift+A + + + + + Invert + + + Invert selection + + + Ctrl+I + + + + + Select related RTP streams + + + Select RTP streams related to selected calls in RTP Streams dialog + + + S + + + + + Deselect related RTP Streams + + + Select RTP streams related to selected calls in RTP Streams dialog + + + D + + + + + + + buttonBox + accepted() + VoipCallsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + VoipCallsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/qt/welcome_page.cpp b/ui/qt/welcome_page.cpp new file mode 100644 index 00000000..ac37a6d9 --- /dev/null +++ b/ui/qt/welcome_page.cpp @@ -0,0 +1,541 @@ +/* welcome_page.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#include "ui/capture_globals.h" +#include "ui/urls.h" + +#include "wsutil/version_info.h" + +#include "welcome_page.h" +#include +#include +#include +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef VERSION_FLAVOR +#define VERSION_FLAVOR "" +#endif + +#include + +WelcomePage::WelcomePage(QWidget *parent) : + QFrame(parent), + welcome_ui_(new Ui::WelcomePage), + flavor_(tr(VERSION_FLAVOR)), + #ifdef Q_OS_MAC + show_in_str_(tr("Show in Finder")), + #else + show_in_str_(tr("Show in Folder")), + #endif + splash_overlay_(NULL) + +{ + welcome_ui_->setupUi(this); + + recent_files_ = welcome_ui_->recentList; + + welcome_ui_->captureFilterComboBox->setEnabled(false); + + welcome_ui_->mainWelcomeBanner->setText(tr("Welcome to %1").arg(mainApp->applicationName())); + + updateStyleSheets(); + + +#ifdef Q_OS_MAC + recent_files_->setAttribute(Qt::WA_MacShowFocusRect, false); +#endif + + welcome_ui_->openFrame->hide(); + recent_files_->setTextElideMode(Qt::ElideLeft); + + welcome_ui_->recentList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(recent_files_, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(showRecentContextMenu(QPoint))); + + connect(mainApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(appInitialized())); + connect(mainApp, SIGNAL(localInterfaceListChanged()), this, SLOT(interfaceListChanged())); + connect(welcome_ui_->interfaceFrame, SIGNAL(itemSelectionChanged()), + welcome_ui_->captureFilterComboBox, SIGNAL(interfacesChanged())); + connect(welcome_ui_->interfaceFrame, SIGNAL(typeSelectionChanged()), + this, SLOT(interfaceListChanged())); + connect(welcome_ui_->interfaceFrame, SIGNAL(itemSelectionChanged()), this, SLOT(interfaceSelected())); + connect(welcome_ui_->captureFilterComboBox->lineEdit(), SIGNAL(textEdited(QString)), + this, SLOT(captureFilterTextEdited(QString))); + connect(welcome_ui_->captureFilterComboBox, SIGNAL(captureFilterSyntaxChanged(bool)), + this, SIGNAL(captureFilterSyntaxChanged(bool))); + connect(welcome_ui_->captureFilterComboBox, SIGNAL(startCapture()), + this, SLOT(captureStarting())); + connect(recent_files_, SIGNAL(itemActivated(QListWidgetItem *)), this, SLOT(openRecentItem(QListWidgetItem *))); + updateRecentCaptures(); + + splash_overlay_ = new SplashOverlay(this); +} + +WelcomePage::~WelcomePage() +{ + delete welcome_ui_; +} + +InterfaceFrame *WelcomePage::getInterfaceFrame() +{ + return welcome_ui_->interfaceFrame; +} + +const QString WelcomePage::captureFilter() +{ + return welcome_ui_->captureFilterComboBox->currentText(); +} + +void WelcomePage::setCaptureFilter(const QString capture_filter) +{ + // capture_filter comes from the current filter in + // CaptureInterfacesDialog. We need to find a good way to handle + // multiple filters. + welcome_ui_->captureFilterComboBox->lineEdit()->setText(capture_filter); +} + +void WelcomePage::interfaceListChanged() +{ + QString btnText = tr("All interfaces shown"); + if (welcome_ui_->interfaceFrame->interfacesHidden() > 0) { + btnText = tr("%n interface(s) shown, %1 hidden", "", + welcome_ui_->interfaceFrame->interfacesPresent()) + .arg(welcome_ui_->interfaceFrame->interfacesHidden()); + } + welcome_ui_->btnInterfaceType->setText(btnText); + welcome_ui_->btnInterfaceType->setMenu(welcome_ui_->interfaceFrame->getSelectionMenu()); +} + +void WelcomePage::setReleaseLabel() +{ + // XXX Add a "check for updates" link? + QString full_release; + QDate today = QDate::currentDate(); + if ((today.month() == 4 && today.day() == 1) || (today.month() == 7 && today.day() == 14)) { + full_release = tr("You are sniffing the glue that holds the Internet together using Wireshark "); + } else { + full_release = tr("You are running Wireshark "); + } + full_release += get_ws_vcs_version_info(); + full_release += "."; +#ifdef HAVE_SOFTWARE_UPDATE + if (prefs.gui_update_enabled) { + full_release += tr(" You receive automatic updates."); + } else { + full_release += tr(" You have disabled automatic updates."); + } +#else + // XXX Is there a way to tell if the user installed Wireshark via an + // external package manager? If so we could say so here. We could + // also add a link to the download page. +#endif + welcome_ui_->fullReleaseLabel->setText(full_release); +} + +void WelcomePage::appInitialized() +{ + setReleaseLabel(); + +#ifdef HAVE_LIBPCAP + welcome_ui_->captureFilterComboBox->lineEdit()->setText(global_capture_opts.default_options.cfilter); +#endif // HAVE_LIBPCAP + + welcome_ui_->captureFilterComboBox->setEnabled(true); + + interfaceListChanged(); + + welcome_ui_->interfaceFrame->ensureSelectedInterface(); + + delete splash_overlay_; + splash_overlay_ = NULL; +} + +#ifdef HAVE_LIBPCAP +// Update each selected device cfilter when the user changes the contents +// of the capture filter lineedit. We do so here so that we don't clobber +// filters set in the Capture Options / Interfaces dialog or ones set via +// the command line. +void WelcomePage::captureFilterTextEdited(const QString capture_filter) +{ + if (global_capture_opts.num_selected > 0) { + interface_t *device; + + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (!device->selected) { + continue; + } + // if (device->active_dlt == -1) { + // simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The link type of interface %s was not specified.", device->name); + // continue; /* Programming error: somehow managed to select an "unsupported" entry */ + // } + g_free(device->cfilter); + if (capture_filter.isEmpty()) { + device->cfilter = NULL; + } else { + device->cfilter = qstring_strdup(capture_filter); + } + // update_filter_string(device->name, filter_text); + } + } +} +#else +// No-op if we don't have capturing. +void WelcomePage::captureFilterTextEdited(const QString) +{ +} +#endif + +// The interface list selection has changed. At this point the user might +// have entered a filter or we might have pre-filled one from a number of +// sources such as our remote connection, the command line, or a previous +// selection. +// Must not change any interface data. +void WelcomePage::interfaceSelected() +{ + QPair sf_pair = CaptureFilterEdit::getSelectedFilter(); + const QString user_filter = sf_pair.first; + bool conflict = sf_pair.second; + + if (conflict) { + welcome_ui_->captureFilterComboBox->lineEdit()->clear(); + welcome_ui_->captureFilterComboBox->setConflict(true); + } else { + welcome_ui_->captureFilterComboBox->lineEdit()->setText(user_filter); + } + + // Notify others (capture options dialog) that the selection has changed. + emit interfacesChanged(); +} + +bool WelcomePage::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + updateStyleSheets(); + break; + default: + break; + + } + return QFrame::event(event); +} + +void WelcomePage::on_interfaceFrame_showExtcapOptions(QString device_name, bool startCaptureOnClose) +{ + emit showExtcapOptions(device_name, startCaptureOnClose); +} + +void WelcomePage::on_interfaceFrame_startCapture(QStringList ifaces) +{ + emit startCapture(ifaces); +} + +void WelcomePage::captureStarting() +{ + welcome_ui_->interfaceFrame->ensureSelectedInterface(); + emit startCapture(QStringList()); +} + +void WelcomePage::updateRecentCaptures() { + QString itemLabel; + QListWidgetItem *rfItem; + QFont rfFont; + QString selectedFilename; + + if (!recent_files_->selectedItems().isEmpty()) { + rfItem = recent_files_->selectedItems().first(); + selectedFilename = rfItem->data(Qt::UserRole).toString(); + } + + if (mainApp->recentItems().count() == 0) { + // Recent menu has been cleared, remove all recent files. + while (recent_files_->count()) { + delete recent_files_->item(0); + } + } + + int rfRow = 0; + foreach (recent_item_status *ri, mainApp->recentItems()) { + itemLabel = ri->filename; + + if (rfRow >= recent_files_->count()) { + recent_files_->addItem(itemLabel); + } + + itemLabel.append(" ("); + if (ri->accessible) { + if (ri->size/1024/1024/1024 > 10) { + itemLabel.append(QString("%1 GB").arg(ri->size/1024/1024/1024)); + } else if (ri->size/1024/1024 > 10) { + itemLabel.append(QString("%1 MB").arg(ri->size/1024/1024)); + } else if (ri->size/1024 > 10) { + itemLabel.append(QString("%1 KB").arg(ri->size/1024)); + } else { + itemLabel.append(QString("%1 Bytes").arg(ri->size)); + } + } else { + itemLabel.append(tr("not found")); + } + itemLabel.append(")"); + rfFont.setItalic(!ri->accessible); + rfItem = recent_files_->item(rfRow); + rfItem->setText(itemLabel); + rfItem->setData(Qt::AccessibleTextRole, itemLabel); + rfItem->setData(Qt::UserRole, ri->filename); + rfItem->setFlags(ri->accessible ? Qt::ItemIsSelectable | Qt::ItemIsEnabled : Qt::NoItemFlags); + rfItem->setFont(rfFont); + if (ri->filename == selectedFilename) { + rfItem->setSelected(true); + } + rfRow++; + } + + int row = recent_files_->count(); + while (row > 0 && (row > (int) prefs.gui_recent_files_count_max || row > rfRow)) { + row--; + delete recent_files_->item(row); + } + if (recent_files_->count() > 0) { + welcome_ui_->openFrame->animatedShow(); + } else { + welcome_ui_->openFrame->animatedHide(); + } +} + +void WelcomePage::openRecentItem(QListWidgetItem *item) { + QString cfPath = item->data(Qt::UserRole).toString(); + emit recentFileActivated(cfPath); +} + +void WelcomePage::resizeEvent(QResizeEvent *event) +{ + if (splash_overlay_) + splash_overlay_->resize(event->size()); +// event->accept(); + + QFrame::resizeEvent(event); +} + +void WelcomePage::setCaptureFilterText(const QString capture_filter) +{ + welcome_ui_->captureFilterComboBox->lineEdit()->setText(capture_filter); + captureFilterTextEdited(capture_filter); +} + +void WelcomePage::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + welcome_ui_->retranslateUi(this); + welcome_ui_->flavorBanner->setText(flavor_); + interfaceListChanged(); + setReleaseLabel(); + break; + default: + break; + } + } + QFrame::changeEvent(event); +} + +void WelcomePage::showRecentContextMenu(QPoint pos) +{ + QListWidgetItem *li = recent_files_->itemAt(pos); + if (!li) return; + + QMenu *recent_ctx_menu = new QMenu(this); + recent_ctx_menu->setAttribute(Qt::WA_DeleteOnClose); + + QString cf_path = li->data(Qt::UserRole).toString(); + + QAction *show_action = recent_ctx_menu->addAction(show_in_str_); + show_action->setData(cf_path); + connect(show_action, SIGNAL(triggered(bool)), this, SLOT(showRecentFolder())); + + QAction *copy_action = recent_ctx_menu->addAction(tr("Copy file path")); + copy_action->setData(cf_path); + connect(copy_action, SIGNAL(triggered(bool)), this, SLOT(copyRecentPath())); + + recent_ctx_menu->addSeparator(); + + QAction *remove_action = recent_ctx_menu->addAction(tr("Remove from list")); + remove_action->setData(cf_path); + connect(remove_action, SIGNAL(triggered(bool)), this, SLOT(removeRecentPath())); + + recent_ctx_menu->popup(recent_files_->mapToGlobal(pos)); +} + +void WelcomePage::showRecentFolder() +{ + QAction *ria = qobject_cast(sender()); + if (!ria) return; + + QString cf_path = ria->data().toString(); + if (cf_path.isEmpty()) return; + + desktop_show_in_folder(cf_path); +} + +void WelcomePage::copyRecentPath() +{ + QAction *ria = qobject_cast(sender()); + if (!ria) return; + + QString cf_path = ria->data().toString(); + if (cf_path.isEmpty()) return; + + mainApp->clipboard()->setText(cf_path); +} + +void WelcomePage::removeRecentPath() +{ + QAction *ria = qobject_cast(sender()); + if (!ria) return; + + QString cf_path = ria->data().toString(); + if (cf_path.isEmpty()) return; + + mainApp->removeRecentItem(cf_path); +} + +void WelcomePage::on_captureLabel_clicked() +{ + mainApp->doTriggerMenuItem(MainApplication::CaptureOptionsDialog); +} + +void WelcomePage::on_helpLabel_clicked() +{ + QDesktopServices::openUrl(QUrl(WS_DOCS_URL)); +} + +void WelcomePage::updateStyleSheets() +{ + QString welcome_ss = QString( + "WelcomePage {" + " padding: 1em;" + " }" + "WelcomePage, QAbstractItemView {" + " background-color: palette(base);" + " color: palette(text);" + " }" + "QAbstractItemView {" + " border: 0;" + "}" + ); +#if !defined(Q_OS_WIN) + welcome_ss += QString( + "QAbstractItemView:item:hover {" + " background-color: %1;" + " color: palette(text);" + "}" + ) + .arg(ColorUtils::hoverBackground().name(QColor::HexArgb)); +#endif + setStyleSheet(welcome_ss); + + QString banner_ss = QString( + "QLabel {" + " border-radius: 0.33em;" + " color: %1;" + " background-color: %2;" + " padding: 0.33em;" + "}" + ) + .arg(QColor(tango_aluminium_6).name()) // Text color + .arg(QColor(tango_sky_blue_2).name()); // Background color + welcome_ui_->mainWelcomeBanner->setStyleSheet(banner_ss); + + QString title_button_ss = QString( + "QLabel {" + " color: %1;" + "}" + "QLabel::hover {" + " color: %2;" + "}" + ) + .arg(QColor(tango_aluminium_4).name()) // Text color + .arg(QColor(tango_sky_blue_4).name()); // Hover color + + // XXX Is there a better term than "flavor"? Provider? Admonition (a la DocBook)? + // Release_source? + // Typical use cases are automated builds from wireshark.org and private, + // not-for-redistribution packages. + if (flavor_.isEmpty()) { + welcome_ui_->flavorBanner->hide(); + } else { + // If needed there are a couple of ways we can make this customizable. + // - Add one or more classes, e.g. "note" or "warning" similar to + // SyntaxLineEdit, which we can then expose vi #defines. + // - Just expose direct color values via #defines. + QString flavor_ss = QString( + "QLabel {" + " border-radius: 0.25em;" + " color: %1;" + " background-color: %2;" + " padding: 0.25em;" + "}" + ) + .arg("white") // Text color + .arg("#2c4bc4"); // Background color. Matches capture start button. + // .arg(QColor(tango_butter_5).name()); // "Warning" background + + welcome_ui_->flavorBanner->setText(flavor_); + welcome_ui_->flavorBanner->setStyleSheet(flavor_ss); + } + welcome_ui_->captureLabel->setStyleSheet(title_button_ss); + welcome_ui_->recentLabel->setStyleSheet(title_button_ss); + welcome_ui_->helpLabel->setStyleSheet(title_button_ss); + + recent_files_->setStyleSheet( + "QListWidget::item {" + " padding-top: 0.2em;" + " padding-bottom: 0.2em;" + "}" + "QListWidget::item::first {" + " padding-top: 0;" + "}" + "QListWidget::item::last {" + " padding-bottom: 0;" + "}" + ); + + // The helpLinks markup includes its own section. + // Replacing it with a stylesheet and reapplying it like we do above + // doesn't work, but this does. + QString hl_text = welcome_ui_->helpLinks->text(); + welcome_ui_->helpLinks->clear(); + welcome_ui_->helpLinks->setText(hl_text); +} + +void WelcomePage::on_recentLabel_clicked() +{ + mainApp->doTriggerMenuItem(MainApplication::FileOpenDialog); +} diff --git a/ui/qt/welcome_page.h b/ui/qt/welcome_page.h new file mode 100644 index 00000000..d6243d1a --- /dev/null +++ b/ui/qt/welcome_page.h @@ -0,0 +1,87 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WELCOME_PAGE_H +#define WELCOME_PAGE_H + +#include + +class QListWidget; +class QListWidgetItem; +class QMenu; + +#include +#include "interface_frame.h" + +namespace Ui { + class WelcomePage; +} + +class WelcomePage : public QFrame +{ + Q_OBJECT +public: + explicit WelcomePage(QWidget *parent = 0); + virtual ~WelcomePage(); + InterfaceFrame *getInterfaceFrame(); + const QString captureFilter(); + void setCaptureFilter(const QString capture_filter); + void updateStyleSheets(); + +public slots: + void interfaceSelected(); + +protected: + virtual bool event(QEvent *event); + virtual void resizeEvent(QResizeEvent *event); + virtual void changeEvent(QEvent* event); + +protected slots: + void on_recentLabel_clicked(); + void on_captureLabel_clicked(); + void on_helpLabel_clicked(); + +private: + Ui::WelcomePage *welcome_ui_; + QString flavor_; + QString show_in_str_; + + SplashOverlay *splash_overlay_; + // QListWidget doesn't activate items when the return or enter keys are pressed on macOS. + // We may want to subclass it at some point. + QListWidget *recent_files_; + +signals: + void startCapture(QStringList); + void recentFileActivated(QString cfile); + void captureFilterSyntaxChanged(bool valid); + void showExtcapOptions(QString &device_name, bool startCaptureOnClose); + void interfacesChanged(); + +public slots: + void setCaptureFilterText(const QString capture_filter); + +private slots: + void appInitialized(); + void interfaceListChanged(); + void setReleaseLabel(); + void captureFilterTextEdited(const QString capture_filter); + void updateRecentCaptures(); + void openRecentItem(QListWidgetItem *item); + void showRecentContextMenu(QPoint pos); + void showRecentFolder(); + void copyRecentPath(); + void removeRecentPath(); + + void on_interfaceFrame_showExtcapOptions(QString device_name, bool startCaptureOnClose); + void on_interfaceFrame_startCapture(QStringList); + void captureStarting(); +}; + +#endif // WELCOME_PAGE_H diff --git a/ui/qt/welcome_page.ui b/ui/qt/welcome_page.ui new file mode 100644 index 00000000..afc50f5e --- /dev/null +++ b/ui/qt/welcome_page.ui @@ -0,0 +1,360 @@ + + + WelcomePage + + + + 0 + 0 + 811 + 663 + + + + Form + + + + + + Qt::Horizontal + + + + 44 + 20 + + + + + + + + + 0 + 0 + + + + + + + + 550 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + 0 + 2 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + <html><head/><body><p>Open a file on your file system</p></body></html> + + + <h2>Open</h2> + + + + + + + + 1 + 2 + + + + Recent capture files + + + Capture files that have been opened previously + + + Qt::ScrollBarAlwaysOff + + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + + + <h2>Capture</h2> + + + + + + + + 0 + + + 0 + + + 0 + + + + + …using this filter: + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + + + + + + + + + 0 + 1 + + + + Interface list + + + List of available capture interfaces + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + 0 + 0 + + + + <h2>Learn</h2> + + + + + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 43 + 20 + + + + + + + + + InterfaceFrame + QFrame +
interface_frame.h
+ 1 +
+ + CaptureFilterCombo + QComboBox +
widgets/capture_filter_combo.h
+
+ + AccordionFrame + QFrame +
accordion_frame.h
+ 1 +
+ + ClickableLabel + QLabel +
widgets/clickable_label.h
+
+
+ + +
diff --git a/ui/qt/widgets/additional_toolbar.cpp b/ui/qt/widgets/additional_toolbar.cpp new file mode 100644 index 00000000..c023d149 --- /dev/null +++ b/ui/qt/widgets/additional_toolbar.cpp @@ -0,0 +1,574 @@ +/* additional_toolbar.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char * AdditionalToolbarWidgetAction::propertyName = "additional_toolbar_item"; + +AdditionalToolBar::AdditionalToolBar(ext_toolbar_t * exttoolbar, QWidget * parent) +: QToolBar(parent), + toolbar(exttoolbar) +{ } + +AdditionalToolBar::~AdditionalToolBar() +{ } + +AdditionalToolBar * AdditionalToolBar::create(QWidget * parent, ext_toolbar_t * toolbar) +{ + if (g_list_length(toolbar->children) == 0) + return NULL; + + AdditionalToolBar * result = new AdditionalToolBar(toolbar, parent); + result->setMovable(false); + result->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + result->layout()->setContentsMargins(0, 0, 0, 0); + result->layout()->setSpacing(4); + + GList * walker = toolbar->children; + bool spacerNeeded = true; + + while (walker && walker->data) + { + ext_toolbar_t * item = gxx_list_data(ext_toolbar_t *, walker); + if (item->type == EXT_TOOLBAR_ITEM) + { + if (item->item_type == EXT_TOOLBAR_STRING) + spacerNeeded = false; + + QAction * newAction = new AdditionalToolbarWidgetAction(item, result); + if (newAction) + { + result->addAction(newAction); + /* Necessary, because enable state is reset upon adding the action */ + result->actions()[result->actions().count() - 1]->setEnabled(!item->capture_only); + } + } + + walker = gxx_list_next (walker); + } + + if (result->children().count() == 0) + return Q_NULLPTR; + + if (spacerNeeded) + { + QWidget * empty = new QWidget(); + empty->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred); + result->addWidget(empty); + + } + + return result; +} + +QString AdditionalToolBar::menuName() +{ + return (toolbar && toolbar->name) ? QString(toolbar->name) : QString(); +} + +AdditionalToolbarWidgetAction::AdditionalToolbarWidgetAction(QObject * parent) +: QWidgetAction(parent), + toolbar_item(0) +{ } + +AdditionalToolbarWidgetAction::AdditionalToolbarWidgetAction(ext_toolbar_t * item, QObject * parent) +: QWidgetAction(parent), + toolbar_item(item) +{ + connect(mainApp, &MainApplication::captureActive, this, &AdditionalToolbarWidgetAction::captureActive); +} + +AdditionalToolbarWidgetAction::AdditionalToolbarWidgetAction(const AdditionalToolbarWidgetAction & copy_object) +: QWidgetAction(copy_object.parent()), + toolbar_item(copy_object.toolbar_item) +{ + connect(mainApp, &MainApplication::captureActive, this, &AdditionalToolbarWidgetAction::captureActive); +} + + +void AdditionalToolbarWidgetAction::captureActive(int activeCaptures) +{ + if (toolbar_item && toolbar_item->capture_only) + { + setEnabled(activeCaptures != 0); + } +} + +/* Exists, so a default deconstructor does not call delete on toolbar_item */ +AdditionalToolbarWidgetAction::~AdditionalToolbarWidgetAction() { } + +QWidget * AdditionalToolbarWidgetAction::createWidget(QWidget * parent) +{ + QWidget * barItem = 0; + + if (toolbar_item->type != EXT_TOOLBAR_ITEM) + return barItem; + + switch (toolbar_item->item_type) + { + case EXT_TOOLBAR_BUTTON: + barItem = createButton(toolbar_item, parent); + break; + case EXT_TOOLBAR_BOOLEAN: + barItem = createBoolean(toolbar_item, parent); + break; + case EXT_TOOLBAR_STRING: + barItem = createTextEditor(toolbar_item, parent); + break; + case EXT_TOOLBAR_SELECTOR: + barItem = createSelector(toolbar_item, parent); + break; + } + + if (! barItem) + return 0; + + barItem->setToolTip(toolbar_item->tooltip); + barItem->setProperty(propertyName, VariantPointer::asQVariant(toolbar_item)); + +#ifdef Q_OS_MAC + barItem->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + return barItem; +} + +static void +toolbar_button_cb(gpointer item, gpointer item_data, gpointer user_data) +{ + if (! item || ! item_data || ! user_data) + return; + + QPushButton * widget = (QPushButton *)(item_data); + ext_toolbar_update_t * update_entry = (ext_toolbar_update_t *)user_data; + + if (widget) + { + if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE) + widget->setText((gchar *)update_entry->user_data); + else if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE) + { + bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1; + widget->setEnabled(enableState); + } + + } +} + +QWidget * AdditionalToolbarWidgetAction::createButton(ext_toolbar_t * item, QWidget * parent) +{ + if (! item || item->type != EXT_TOOLBAR_ITEM || item->item_type != EXT_TOOLBAR_BUTTON) + return 0; + + QPushButton * button = new QPushButton(item->name, parent); + button->setText(item->name); + connect(button, &QPushButton::clicked, this, &AdditionalToolbarWidgetAction::onButtonClicked); + + ext_toolbar_register_update_cb(item, (ext_toolbar_action_cb)&toolbar_button_cb, (void *)button); + + return button; +} + +static void +toolbar_boolean_cb(gpointer item, gpointer item_data, gpointer user_data) +{ + if (! item || ! item_data || ! user_data) + return; + + QCheckBox * widget = (QCheckBox *)(item_data); + + ext_toolbar_update_t * update_entry = (ext_toolbar_update_t *)user_data; + + if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE) + { + bool oldState = false; + if (update_entry->silent) + oldState = widget->blockSignals(true); + + widget->setCheckState(GPOINTER_TO_INT(update_entry->user_data) == 1 ? Qt::Checked : Qt::Unchecked); + + if (update_entry->silent) + widget->blockSignals(oldState); + } + else if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE) + { + bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1; + widget->setEnabled(enableState); + } +} + +QWidget * AdditionalToolbarWidgetAction::createBoolean(ext_toolbar_t * item, QWidget * parent) +{ + if (! item || item->type != EXT_TOOLBAR_ITEM || item->item_type != EXT_TOOLBAR_BOOLEAN) + return 0; + + QString defValue = toolbar_item->defvalue; + + QCheckBox * checkbox = new QCheckBox(item->name, parent); + checkbox->setText(item->name); + setCheckable(true); + checkbox->setCheckState(defValue.compare("true", Qt::CaseInsensitive) == 0 ? Qt::Checked : Qt::Unchecked); + connect(checkbox, &QCheckBox::stateChanged, this, &AdditionalToolbarWidgetAction::onCheckBoxChecked); + + ext_toolbar_register_update_cb(item, (ext_toolbar_action_cb)&toolbar_boolean_cb, (void *)checkbox); + + return checkbox; +} + +QWidget * AdditionalToolbarWidgetAction::createLabelFrame(ext_toolbar_t * item, QWidget * parent) +{ + if (! item) + return new QWidget(); + + QWidget * frame = new QWidget(parent); + + QHBoxLayout * frameLayout = new QHBoxLayout(frame); + frameLayout->setContentsMargins(0, 0, 0, 0); + frameLayout->setSpacing(0); + + QLabel * strLabel = new QLabel(item->name, frame); + strLabel->setToolTip(item->tooltip); + +#ifdef Q_OS_MAC + frame->setAttribute(Qt::WA_MacSmallSize, true); + strLabel->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + frameLayout->addWidget(strLabel); + + frame->setLayout(frameLayout); + + return frame; +} + +static void +toolbar_string_cb(gpointer item, gpointer item_data, gpointer user_data) +{ + if (! item || ! item_data || ! user_data) + return; + + ApplyLineEdit * edit = (ApplyLineEdit *)(item_data); + + ext_toolbar_update_t * update_entry = (ext_toolbar_update_t *)user_data; + + if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE) + { + bool oldState = false; + if (update_entry->silent) + oldState = edit->blockSignals(true); + + edit->setText((gchar *)update_entry->user_data); + + if (update_entry->silent) + edit->blockSignals(oldState); + } + else if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE) + { + bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1; + edit->setEnabled(enableState); + } +} + +QWidget * AdditionalToolbarWidgetAction::createTextEditor(ext_toolbar_t * item, QWidget * parent) +{ + if (! item || item->type != EXT_TOOLBAR_ITEM || item->item_type != EXT_TOOLBAR_STRING) + return 0; + + QWidget * frame = createLabelFrame(toolbar_item, parent); + + ApplyLineEdit * strEdit = new ApplyLineEdit(toolbar_item->defvalue, frame); + strEdit->setToolTip(toolbar_item->tooltip); + strEdit->setRegEx(toolbar_item->regex); + strEdit->setEmptyAllowed(toolbar_item->is_required); + strEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + +#ifdef Q_OS_MAC + strEdit->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + frame->layout()->addWidget(strEdit); + + connect(strEdit, &ApplyLineEdit::textApplied, this, &AdditionalToolbarWidgetAction::sendTextToCallback); + + ext_toolbar_register_update_cb(item, (ext_toolbar_action_cb)&toolbar_string_cb, (void *)strEdit); + + return frame; +} + +static void +toolbar_selector_cb(gpointer item, gpointer item_data, gpointer user_data) +{ + if (! item || ! item_data || ! user_data) + return; + + QComboBox * comboBox = (QComboBox *)(item_data); + ext_toolbar_update_t * update_entry = (ext_toolbar_update_t *)user_data; + + bool oldState = false; + + if (update_entry->silent) + oldState = comboBox->blockSignals(true); + + QStandardItemModel * sourceModel = (QStandardItemModel *)comboBox->model(); + + if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE) + { + bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1; + comboBox->setEnabled(enableState); + } + else if (update_entry->type != EXT_TOOLBAR_UPDATE_DATA_REMOVE && ! update_entry->user_data) + return; + + if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE) + { + QString data = QString((gchar *)update_entry->user_data); + + for (int i = 0; i < sourceModel->rowCount(); i++) + { + QStandardItem * dataValue = ((QStandardItemModel *)sourceModel)->item(i, 0); + ext_toolbar_value_t * tbValue = VariantPointer::asPtr(dataValue->data(Qt::UserRole)); + if (tbValue && data.compare(QString(tbValue->value)) == 0) + { + comboBox->setCurrentIndex(i); + break; + } + } + } + else if (update_entry->type == EXT_TOOLBAR_UPDATE_DATA) + { + GList * walker = (GList *)update_entry->user_data; + if (g_list_length(walker) == 0) + return; + + sourceModel->clear(); + + while (walker && walker->data) + { + ext_toolbar_value_t * listvalue = gxx_list_data(ext_toolbar_value_t *, walker); + + QStandardItem * si = new QStandardItem(listvalue->display); + si->setData(VariantPointer::asQVariant(listvalue), Qt::UserRole); + sourceModel->appendRow(si); + + walker = gxx_list_next(walker); + } + } + else if (update_entry->type == EXT_TOOLBAR_UPDATE_DATABYINDEX || + update_entry->type == EXT_TOOLBAR_UPDATE_DATA_ADD || + update_entry->type == EXT_TOOLBAR_UPDATE_DATA_REMOVE) + { + if (! update_entry->data_index) + return; + + gchar * idx = (gchar *)update_entry->data_index; + gchar * display = (gchar *)update_entry->user_data; + + if (update_entry->type == EXT_TOOLBAR_UPDATE_DATABYINDEX) + { + for (int i = 0; i < sourceModel->rowCount(); i++) + { + QStandardItem * dataValue = sourceModel->item(i, 0); + ext_toolbar_value_t * entry = VariantPointer::asPtr(dataValue->data(Qt::UserRole)); + if (entry && g_strcmp0(entry->value, idx) == 0) + { + g_free(entry->display); + entry->display = g_strdup(display); + dataValue->setData(VariantPointer::asQVariant(entry), Qt::UserRole); + dataValue->setText(display); + break; + } + } + } + else if (update_entry->type == EXT_TOOLBAR_UPDATE_DATA_ADD) + { + ext_toolbar_value_t * listvalue = g_new0(ext_toolbar_value_t, 1); + listvalue->display = g_strdup(display); + listvalue->value = g_strdup(idx); + + QStandardItem * si = new QStandardItem(listvalue->display); + si->setData(VariantPointer::asQVariant(listvalue), Qt::UserRole); + sourceModel->appendRow(si); + } + else if (update_entry->type == EXT_TOOLBAR_UPDATE_DATA_REMOVE) + { + QList entryList = sourceModel->findItems(display); + /* Search for index if display did not find anything */ + if (entryList.size() == 0) + entryList = sourceModel->findItems(idx); + + foreach(QStandardItem *entry, entryList) + { + QModelIndex index = sourceModel->indexFromItem(entry); + if (index.isValid()) + sourceModel->removeRow(index.row()); + } + } + } + + if (update_entry->silent) + comboBox->blockSignals(oldState); + +} + +QWidget * AdditionalToolbarWidgetAction::createSelector(ext_toolbar_t * item, QWidget * parent) +{ + if (! item || item->type != EXT_TOOLBAR_ITEM || item->item_type != EXT_TOOLBAR_SELECTOR) + return 0; + + if (g_list_length(item->values) == 0) + return 0; + + QWidget * frame = createLabelFrame(item, parent); + + QComboBox * myBox = new QComboBox(parent); + myBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + QStandardItemModel * sourceModel = new QStandardItemModel(); + + GList * walker = item->values; + int selIndex = 0; + while (walker && walker->data) + { + ext_toolbar_value_t * listvalue = gxx_list_data(ext_toolbar_value_t *, walker); + + QStandardItem * si = new QStandardItem(listvalue->display); + si->setData(VariantPointer::asQVariant(listvalue), Qt::UserRole); + sourceModel->appendRow(si); + + if (listvalue->is_default) + selIndex = sourceModel->rowCount(); + + walker = gxx_list_next(walker); + } + + myBox->setModel(sourceModel); + myBox->setCurrentIndex(selIndex); + +#ifdef Q_OS_MAC + myBox->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + frame->layout()->addWidget(myBox); + + connect(myBox, static_cast(&QComboBox::currentIndexChanged), + this, &AdditionalToolbarWidgetAction::onSelectionInWidgetChanged); + + ext_toolbar_register_update_cb(item, (ext_toolbar_action_cb)&toolbar_selector_cb, (void *)myBox); + + return frame; +} + +ext_toolbar_t * AdditionalToolbarWidgetAction::extractToolbarItemFromObject(QObject * object) +{ + QWidget * widget = dynamic_cast(object); + if (! widget) + return 0; + + QVariant propValue = widget->property(propertyName); + + /* If property is invalid, look if our parent has this property */ + if (! propValue.isValid()) + { + QWidget * frame = dynamic_cast(widget->parent()); + if (! frame) + return 0; + + propValue = frame->property(propertyName); + } + + if (! propValue.isValid()) + return 0; + + return VariantPointer::asPtr(propValue); +} + +void AdditionalToolbarWidgetAction::onButtonClicked() +{ + ext_toolbar_t * item = extractToolbarItemFromObject(sender()); + if (! item) + return; + + item->callback(item, 0, item->user_data); +} + +void AdditionalToolbarWidgetAction::onCheckBoxChecked(int checkState) +{ + ext_toolbar_t * item = extractToolbarItemFromObject(sender()); + if (! item) + return; + + gboolean value = checkState == Qt::Checked ? true : false; + + item->callback(item, &value, item->user_data); +} + +void AdditionalToolbarWidgetAction::sendTextToCallback() +{ + ext_toolbar_t * item = extractToolbarItemFromObject(sender()); + if (! item) + return; + + if (item->item_type != EXT_TOOLBAR_STRING) + return; + + ApplyLineEdit * editor = dynamic_cast(sender()); + if (! editor) + { + /* Called from button, searching for acompanying line edit */ + QWidget * parent = dynamic_cast(sender()->parent()); + if (parent) + { + QList children = parent->findChildren(); + if (children.count() >= 0) + editor = children.at(0); + } + } + + if (editor) + item->callback(item, qstring_strdup(editor->text()), item->user_data); +} + +void AdditionalToolbarWidgetAction::onSelectionInWidgetChanged(int idx) +{ + QComboBox * editor = dynamic_cast(sender()); + ext_toolbar_t * item = extractToolbarItemFromObject(editor); + if (! item || item->item_type != EXT_TOOLBAR_SELECTOR) + return; + + QStandardItemModel * sourceModel = (QStandardItemModel *) editor->model(); + if (sourceModel->rowCount() <= idx) + return; + + QModelIndex mdIdx = sourceModel->index(idx, 0); + QVariant dataSet = sourceModel->data(mdIdx, Qt::UserRole); + if (dataSet.isValid()) + { + ext_toolbar_value_t * value_entry = VariantPointer::asPtr(dataSet); + item->callback(item, value_entry, item->user_data); + } +} diff --git a/ui/qt/widgets/additional_toolbar.h b/ui/qt/widgets/additional_toolbar.h new file mode 100644 index 00000000..3ea9d448 --- /dev/null +++ b/ui/qt/widgets/additional_toolbar.h @@ -0,0 +1,76 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_ADDITIONAL_TOOLBAR_H_ +#define UI_QT_ADDITIONAL_TOOLBAR_H_ + +#include + +#include +#include + +/* Class for all display widgets. + * + * Inherits QWidgetAction, otherwise the extension popup might not work for the toolbar + */ +class AdditionalToolbarWidgetAction: public QWidgetAction +{ + Q_OBJECT + +public: + + AdditionalToolbarWidgetAction(QObject * parent = 0); + AdditionalToolbarWidgetAction(ext_toolbar_t * item, QObject * parent = 0); + AdditionalToolbarWidgetAction(const AdditionalToolbarWidgetAction & copy_object); + ~AdditionalToolbarWidgetAction(); + +protected: + virtual QWidget * createWidget(QWidget * parent); + + static const char * propertyName; + +private: + + ext_toolbar_t * toolbar_item; + + QWidget * createButton(ext_toolbar_t * item, QWidget * parent); + QWidget * createBoolean(ext_toolbar_t * item, QWidget * parent); + QWidget * createTextEditor(ext_toolbar_t * item, QWidget * parent); + QWidget * createSelector(ext_toolbar_t * item, QWidget * parent); + + QWidget * createLabelFrame(ext_toolbar_t * item, QWidget * parent); + + ext_toolbar_t * extractToolbarItemFromObject(QObject *); + +private slots: + void onButtonClicked(); + void onCheckBoxChecked(int); + void sendTextToCallback(); + void onSelectionInWidgetChanged(int idx); + + void captureActive(int); +}; + +class AdditionalToolBar: public QToolBar +{ + Q_OBJECT + +public: + AdditionalToolBar(ext_toolbar_t * toolbar, QWidget * parent = 0); + virtual ~AdditionalToolBar(); + + static AdditionalToolBar * create(QWidget * parent, ext_toolbar_t * toolbar); + + QString menuName(); + +private: + ext_toolbar_t * toolbar; +}; + +#endif /* UI_QT_ADDITIONAL_TOOLBAR_H_ */ diff --git a/ui/qt/widgets/apply_line_edit.cpp b/ui/qt/widgets/apply_line_edit.cpp new file mode 100644 index 00000000..a0bf896f --- /dev/null +++ b/ui/qt/widgets/apply_line_edit.cpp @@ -0,0 +1,156 @@ +/* apply_lineedit.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include + +#include +#include +#include + +ApplyLineEdit::ApplyLineEdit(QString linePlaceholderText, QWidget * parent) +: QLineEdit(parent), + apply_button_(0) +{ + emptyAllowed_ = false; + regex_ = QString(); + + apply_button_ = new StockIconToolButton(parent, "x-filter-apply"); + apply_button_->setCursor(Qt::ArrowCursor); + apply_button_->setEnabled(false); + apply_button_->setToolTip(tr("Apply changes")); + apply_button_->setIconSize(QSize(24, 14)); + apply_button_->setMaximumWidth(30); + apply_button_->setStyleSheet( + "QToolButton {" + " border: none;" + " background: transparent;" // Disables platform style on Windows. + " padding: 0 0 0 0;" + "}" + ); + +#ifdef Q_OS_MAC + setAttribute(Qt::WA_MacSmallSize, true); + apply_button_->setAttribute(Qt::WA_MacSmallSize, true); +#endif + + setPlaceholderText(linePlaceholderText); + + connect(this, &ApplyLineEdit::textEdited, this, &ApplyLineEdit::onTextEdited); + connect(this, &ApplyLineEdit::textChanged, this, &ApplyLineEdit::onTextChanged); + + connect(this, &ApplyLineEdit::returnPressed, this, &ApplyLineEdit::onSubmitContent); + connect(apply_button_, &StockIconToolButton::clicked, this, &ApplyLineEdit::onSubmitContent); + + handleValidation(QString(linePlaceholderText)); + + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); +} + +ApplyLineEdit::~ApplyLineEdit() {} + +void ApplyLineEdit::setRegEx(QString regex) +{ + regex_ = regex; +} + +QString ApplyLineEdit::regex() +{ + return regex_; +} + +void ApplyLineEdit::setEmptyAllowed(bool emptyAllowed) +{ + emptyAllowed_ = emptyAllowed; +} + +bool ApplyLineEdit::emptyAllowed() +{ + return emptyAllowed_; +} + +bool ApplyLineEdit::isValidText(QString & text, bool ignoreEmptyCheck) +{ + if (text.length() == 0) + { + if (! ignoreEmptyCheck && ! emptyAllowed_) + return false; + else if (ignoreEmptyCheck) + return true; + } + + if (regex_.length() > 0) + { + QRegularExpression rx (regex_); + QRegularExpressionValidator v(rx, 0); + + int pos = 0; + if (! rx.isValid() || v.validate(text, pos) != QValidator::Acceptable) + return false; + } + + return true; +} + +void ApplyLineEdit::onTextEdited(const QString & text) +{ + QString newText = QString(text); + apply_button_->setEnabled(isValidText(newText)); + handleValidation(newText); +} + +void ApplyLineEdit::onTextChanged(const QString & text) +{ + handleValidation(QString(text)); +} + +void ApplyLineEdit::handleValidation(QString newText) +{ + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + + QString style_sheet = QString( + "ApplyLineEdit {" + " padding-left: %1px;" + " padding-right: %2px;" + " background-color: %3;" + "}" + ) + .arg(frameWidth + 1) + .arg(apply_button_->sizeHint().width() + frameWidth) + .arg(isValidText(newText, true) ? QString("") : ColorUtils::fromColorT(prefs.gui_text_invalid).name()); + + setStyleSheet(style_sheet); +} + +void ApplyLineEdit::resizeEvent(QResizeEvent *) +{ + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + QSize apsz = apply_button_->sizeHint(); + + apply_button_->move((contentsRect().right() + pos().x()) - (frameWidth + apsz.width()) - 2, + contentsRect().top() + pos().y()); + + apply_button_->setMinimumHeight(height()); + apply_button_->setMaximumHeight(height()); +} + +void ApplyLineEdit::onSubmitContent() +{ + QString data = text(); + if (! isValidText(data)) + return; + + /* Freeze apply button to signal the text has been sent. Will be unfreezed, if the text in the textbox changes again */ + apply_button_->setEnabled(false); + + emit textApplied(); +} diff --git a/ui/qt/widgets/apply_line_edit.h b/ui/qt/widgets/apply_line_edit.h new file mode 100644 index 00000000..47681eba --- /dev/null +++ b/ui/qt/widgets/apply_line_edit.h @@ -0,0 +1,57 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_APPLY_LINE_EDIT_H_ +#define UI_QT_APPLY_LINE_EDIT_H_ + +#include +#include + +#include + +class ApplyLineEdit : public QLineEdit +{ + Q_OBJECT + +public: + explicit ApplyLineEdit(QString linePlaceholderText, QWidget *parent = 0); + ~ApplyLineEdit(); + + Q_PROPERTY(QString regex READ regex WRITE setRegEx) + Q_PROPERTY(bool emptyAllowed READ emptyAllowed WRITE setEmptyAllowed) + + QString regex(); + void setRegEx(QString); + + bool emptyAllowed(); + void setEmptyAllowed(bool); + +signals: + void textApplied(); + +protected: + void resizeEvent(QResizeEvent *); + +private: + + QString regex_; + bool emptyAllowed_; + + StockIconToolButton *apply_button_; + + bool isValidText(QString &, bool ignoreEmptyCheck = false); + void handleValidation(QString newText); + +private slots: + void onTextEdited(const QString &); + void onTextChanged(const QString &); + void onSubmitContent(); +}; + +#endif /* UI_QT_APPLY_LINE_EDIT_H_ */ diff --git a/ui/qt/widgets/byte_view_text.cpp b/ui/qt/widgets/byte_view_text.cpp new file mode 100644 index 00000000..e81dc0ac --- /dev/null +++ b/ui/qt/widgets/byte_view_text.cpp @@ -0,0 +1,803 @@ +/* byte_view_text.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +// Some code based on QHexView by Evan Teran +// https://github.com/eteran/qhexview/ + +#include "byte_view_text.h" + +#include + +#include + +#include +#include "main_application.h" +#include "ui/recent.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// To do: +// - Add recent settings and context menu items to show/hide the offset. +// - Add a UTF-8 and possibly UTF-xx option to the ASCII display. +// - Move more common metrics to DataPrinter. + +// Alternative implementations: +// - Pre-draw all of our characters and paint our display using pixmap +// copying? That would make this behave like a terminal screen, which +// is what we ultimately want. +// - Use QGraphicsView + QGraphicsScene + QGraphicsTextItem instead? + +Q_DECLARE_METATYPE(bytes_view_type) +Q_DECLARE_METATYPE(bytes_encoding_type) +Q_DECLARE_METATYPE(DataPrinter::DumpType) + +ByteViewText::ByteViewText(const QByteArray &data, packet_char_enc encoding, QWidget *parent) : + QAbstractScrollArea(parent), + layout_(new QTextLayout()), + data_(data), + encoding_(encoding), + hovered_byte_offset_(-1), + marked_byte_offset_(-1), + proto_start_(0), + proto_len_(0), + field_start_(0), + field_len_(0), + field_a_start_(0), + field_a_len_(0), + show_offset_(true), + show_hex_(true), + show_ascii_(true), + row_width_(recent.gui_bytes_view == BYTES_BITS ? 8 : 16), + em_width_(0), + line_height_(0), + allow_hover_selection_(false) +{ + layout_->setCacheEnabled(true); + + offset_normal_fg_ = ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.35); + offset_field_fg_ = ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.65); + + window()->winId(); // Required for screenChanged? https://phabricator.kde.org/D20171 + connect(window()->windowHandle(), &QWindow::screenChanged, viewport(), [=](const QScreen *) { viewport()->update(); }); + + createContextMenu(); + + setMouseTracking(true); + +#ifdef Q_OS_MAC + setAttribute(Qt::WA_MacShowFocusRect, true); +#endif +} + +ByteViewText::~ByteViewText() +{ + ctx_menu_.clear(); + delete(layout_); +} + +void ByteViewText::createContextMenu() +{ + + action_allow_hover_selection_ = ctx_menu_.addAction(tr("Allow hover highlighting")); + action_allow_hover_selection_->setCheckable(true); + action_allow_hover_selection_->setChecked(true); + connect(action_allow_hover_selection_, &QAction::toggled, this, &ByteViewText::toggleHoverAllowed); + ctx_menu_.addSeparator(); + + QActionGroup * copy_actions = DataPrinter::copyActions(this); + ctx_menu_.addActions(copy_actions->actions()); + ctx_menu_.addSeparator(); + + QActionGroup * format_actions = new QActionGroup(this); + action_bytes_hex_ = format_actions->addAction(tr("Show bytes as hexadecimal")); + action_bytes_hex_->setData(QVariant::fromValue(BYTES_HEX)); + action_bytes_hex_->setCheckable(true); + + action_bytes_dec_ = format_actions->addAction(tr("…as decimal")); + action_bytes_dec_->setData(QVariant::fromValue(BYTES_DEC)); + action_bytes_dec_->setCheckable(true); + + action_bytes_oct_ = format_actions->addAction(tr("…as octal")); + action_bytes_oct_->setData(QVariant::fromValue(BYTES_OCT)); + action_bytes_oct_->setCheckable(true); + + action_bytes_bits_ = format_actions->addAction(tr("…as bits")); + action_bytes_bits_->setData(QVariant::fromValue(BYTES_BITS)); + action_bytes_bits_->setCheckable(true); + + ctx_menu_.addActions(format_actions->actions()); + connect(format_actions, &QActionGroup::triggered, this, &ByteViewText::setHexDisplayFormat); + + ctx_menu_.addSeparator(); + + QActionGroup * encoding_actions = new QActionGroup(this); + action_bytes_enc_from_packet_ = encoding_actions->addAction(tr("Show text based on packet")); + action_bytes_enc_from_packet_->setData(QVariant::fromValue(BYTES_ENC_FROM_PACKET)); + action_bytes_enc_from_packet_->setCheckable(true); + + action_bytes_enc_ascii_ = encoding_actions->addAction(tr("…as ASCII")); + action_bytes_enc_ascii_->setData(QVariant::fromValue(BYTES_ENC_ASCII)); + action_bytes_enc_ascii_->setCheckable(true); + + action_bytes_enc_ebcdic_ = encoding_actions->addAction(tr("…as EBCDIC")); + action_bytes_enc_ebcdic_->setData(QVariant::fromValue(BYTES_ENC_EBCDIC)); + action_bytes_enc_ebcdic_->setCheckable(true); + + updateContextMenu(); + + ctx_menu_.addActions(encoding_actions->actions()); + connect(encoding_actions, &QActionGroup::triggered, this, &ByteViewText::setCharacterEncoding); +} + +void ByteViewText::toggleHoverAllowed(bool checked) +{ + allow_hover_selection_ = ! checked; + recent.gui_allow_hover_selection = checked; +} + +void ByteViewText::updateContextMenu() +{ + + action_allow_hover_selection_->setChecked(recent.gui_allow_hover_selection); + + switch (recent.gui_bytes_view) { + case BYTES_HEX: + action_bytes_hex_->setChecked(true); + break; + case BYTES_BITS: + action_bytes_bits_->setChecked(true); + break; + case BYTES_DEC: + action_bytes_dec_->setChecked(true); + break; + case BYTES_OCT: + action_bytes_oct_->setChecked(true); + break; + } + + switch (recent.gui_bytes_encoding) { + case BYTES_ENC_FROM_PACKET: + action_bytes_enc_from_packet_->setChecked(true); + break; + case BYTES_ENC_ASCII: + action_bytes_enc_ascii_->setChecked(true); + break; + case BYTES_ENC_EBCDIC: + action_bytes_enc_ebcdic_->setChecked(true); + break; + } +} + +bool ByteViewText::isEmpty() const +{ + return data_.isEmpty(); +} + +QSize ByteViewText::minimumSizeHint() const +{ + // Allow panel to shrink to any size + return QSize(); +} + +void ByteViewText::markProtocol(int start, int length) +{ + proto_start_ = start; + proto_len_ = length; + viewport()->update(); +} + +void ByteViewText::markField(int start, int length, bool scroll_to) +{ + field_start_ = start; + field_len_ = length; + // This might be called as a result of (de)selecting a proto tree + // item, so take us out of marked mode. + marked_byte_offset_ = -1; + if (scroll_to) { + scrollToByte(start); + } + viewport()->update(); +} + +void ByteViewText::markAppendix(int start, int length) +{ + field_a_start_ = start; + field_a_len_ = length; + viewport()->update(); +} + +void ByteViewText::unmarkField() +{ + proto_start_ = 0; + proto_len_ = 0; + field_start_ = 0; + field_len_ = 0; + marked_byte_offset_ = -1; + field_a_start_ = 0; + field_a_len_ = 0; + viewport()->update(); +} + +void ByteViewText::setMonospaceFont(const QFont &mono_font) +{ + QFont int_font(mono_font); + + setFont(int_font); + viewport()->setFont(int_font); + layout_->setFont(int_font); + + updateLayoutMetrics(); + + updateScrollbars(); + viewport()->update(); +} + +void ByteViewText::updateByteViewSettings() +{ + row_width_ = recent.gui_bytes_view == BYTES_BITS ? 8 : 16; + + updateContextMenu(); + updateScrollbars(); + viewport()->update(); +} + +void ByteViewText::detachData() +{ + data_.detach(); +} + +void ByteViewText::paintEvent(QPaintEvent *) +{ + updateLayoutMetrics(); + + QPainter painter(viewport()); + painter.translate(-horizontalScrollBar()->value() * em_width_, 0); + + // Pixel offset of this row + int row_y = 0; + + // Starting byte offset + int offset = verticalScrollBar()->value() * row_width_; + + // Clear the area + painter.fillRect(viewport()->rect(), palette().base()); + + // Offset background. We want the entire height to be filled. + if (show_offset_) { + QRect offset_rect = QRect(viewport()->rect()); + offset_rect.setWidth(offsetPixels()); + painter.fillRect(offset_rect, palette().window()); + } + + if (data_.isEmpty()) { + return; + } + + // Data rows + int widget_height = height(); + painter.save(); + + x_pos_to_column_.clear(); + while ((int) (row_y + line_height_) < widget_height && offset < (int) data_.size()) { + drawLine(&painter, offset, row_y); + offset += row_width_; + row_y += line_height_; + } + + painter.restore(); + + // We can't do this in drawLine since the next line might draw over our rect. + // This looks best when our highlight and background have similar lightnesses. + // We might want to set a composition mode when that's not the case. + if (!hover_outlines_.isEmpty()) { + qreal pen_width = 1.0; + qreal hover_alpha = 0.6; + QPen ho_pen; + QColor ho_color = palette().text().color(); + if (marked_byte_offset_ < 0) { + hover_alpha = 0.3; + if (devicePixelRatio() > 1) { + pen_width = 0.5; + } + } + ho_pen.setWidthF(pen_width); + ho_color.setAlphaF(hover_alpha); + ho_pen.setColor(ho_color); + + painter.save(); + painter.setPen(ho_pen); + painter.setBrush(Qt::NoBrush); + foreach (QRect ho_rect, hover_outlines_) { + // These look good on retina and non-retina displays on macOS. + // We might want to use fontMetrics numbers instead. + ho_rect.adjust(-1, 0, -1, -1); + painter.drawRect(ho_rect); + } + painter.restore(); + } + hover_outlines_.clear(); + + QStyleOptionFocusRect option; + option.initFrom(this); + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this); +} + +void ByteViewText::resizeEvent(QResizeEvent *) +{ + updateScrollbars(); +} + +void ByteViewText::mousePressEvent (QMouseEvent *event) { + if (data_.isEmpty() || !event || event->button() != Qt::LeftButton) { + return; + } + + // byteSelected does the following: + // - Triggers selectedFieldChanged in ProtoTree, which clears the + // selection and selects the corresponding (or no) item. + // - The new tree selection triggers markField, which clobbers + // marked_byte_offset_. + + const bool hover_mode = marked_byte_offset_ < 0; + const int byte_offset = byteOffsetAtPixel(event->pos()); + setUpdatesEnabled(false); + emit byteSelected(byte_offset); + if (hover_mode && byte_offset >= 0) { + // Switch to marked mode. + hovered_byte_offset_ = -1; + marked_byte_offset_ = byte_offset; + viewport()->update(); + } else { + // Back to hover mode. + mouseMoveEvent(event); + } + setUpdatesEnabled(true); +} + +void ByteViewText::mouseMoveEvent(QMouseEvent *event) +{ + if (marked_byte_offset_ >= 0 || allow_hover_selection_ || + (!allow_hover_selection_ && event->modifiers() & Qt::ControlModifier)) { + return; + } + + hovered_byte_offset_ = byteOffsetAtPixel(event->pos()); + emit byteHovered(hovered_byte_offset_); + viewport()->update(); +} + +void ByteViewText::leaveEvent(QEvent *event) +{ + hovered_byte_offset_ = -1; + emit byteHovered(hovered_byte_offset_); + + viewport()->update(); + QAbstractScrollArea::leaveEvent(event); +} + +void ByteViewText::contextMenuEvent(QContextMenuEvent *event) +{ + ctx_menu_.popup(event->globalPos()); +} + +// Private + +const int ByteViewText::separator_interval_ = DataPrinter::separatorInterval(); + +void ByteViewText::updateLayoutMetrics() +{ + em_width_ = stringWidth("M"); + // We might want to match ProtoTree::rowHeight. + line_height_ = viewport()->fontMetrics().lineSpacing(); +} + +int ByteViewText::stringWidth(const QString &line) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + return viewport()->fontMetrics().horizontalAdvance(line); +#else + return viewport()->fontMetrics().boundingRect(line).width(); +#endif +} + +// Draw a line of byte view text for a given offset. +// Text highlighting is handled using QTextLayout::FormatRange. +void ByteViewText::drawLine(QPainter *painter, const int offset, const int row_y) +{ + if (data_.isEmpty()) { + return; + } + + // Build our pixel to byte offset vector the first time through. + bool build_x_pos = x_pos_to_column_.empty() ? true : false; + int tvb_len = static_cast(data_.size()); + int max_tvb_pos = qMin(offset + row_width_, tvb_len) - 1; + QList fmt_list; + + static const char hexchars[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + QString line; + HighlightMode offset_mode = ModeOffsetNormal; + + // Offset. + if (show_offset_) { + line = QString(" %1 ").arg(offset, offsetChars(false), 16, QChar('0')); + if (build_x_pos) { + x_pos_to_column_.fill(-1, stringWidth(line)); + } + } + + // Hex + if (show_hex_) { + int ascii_start = static_cast(line.length()) + DataPrinter::hexChars() + 3; + // Extra hover space before and after each byte. + int slop = em_width_ / 2; + unsigned char c; + + if (build_x_pos) { + x_pos_to_column_ += QVector().fill(-1, slop); + } + + for (int tvb_pos = offset; tvb_pos <= max_tvb_pos; tvb_pos++) { + line += ' '; + /* insert a space every separator_interval_ bytes */ + if ((tvb_pos != offset) && ((tvb_pos % separator_interval_) == 0)) { + line += ' '; + x_pos_to_column_ += QVector().fill(tvb_pos - offset - 1, em_width_); + } + + switch (recent.gui_bytes_view) { + case BYTES_HEX: + line += hexchars[(data_[tvb_pos] & 0xf0) >> 4]; + line += hexchars[data_[tvb_pos] & 0x0f]; + break; + case BYTES_BITS: + /* XXX, bitmask */ + for (int j = 7; j >= 0; j--) { + line += (data_[tvb_pos] & (1 << j)) ? '1' : '0'; + } + break; + case BYTES_DEC: + c = data_[tvb_pos]; + line += c < 100 ? ' ' : hexchars[c / 100]; + line += c < 10 ? ' ' : hexchars[(c / 10) % 10]; + line += hexchars[c % 10]; + break; + case BYTES_OCT: + line += hexchars[(data_[tvb_pos] & 0xc0) >> 6]; + line += hexchars[(data_[tvb_pos] & 0x38) >> 3]; + line += hexchars[data_[tvb_pos] & 0x07]; + break; + } + if (build_x_pos) { + x_pos_to_column_ += QVector().fill(tvb_pos - offset, stringWidth(line) - x_pos_to_column_.size() + slop); + } + if (tvb_pos == hovered_byte_offset_ || tvb_pos == marked_byte_offset_) { + int ho_len; + switch (recent.gui_bytes_view) { + case BYTES_HEX: + ho_len = 2; + break; + case BYTES_BITS: + ho_len = 8; + break; + case BYTES_DEC: + case BYTES_OCT: + ho_len = 3; + break; + default: + ws_assert_not_reached(); + } + QRect ho_rect = painter->boundingRect(QRect(), Qt::AlignHCenter|Qt::AlignVCenter, line.right(ho_len)); + ho_rect.moveRight(stringWidth(line)); + ho_rect.moveTop(row_y); + hover_outlines_.append(ho_rect); + } + } + line += QString(ascii_start - line.length(), ' '); + if (build_x_pos) { + x_pos_to_column_ += QVector().fill(-1, stringWidth(line) - x_pos_to_column_.size()); + } + + addHexFormatRange(fmt_list, proto_start_, proto_len_, offset, max_tvb_pos, ModeProtocol); + if (addHexFormatRange(fmt_list, field_start_, field_len_, offset, max_tvb_pos, ModeField)) { + offset_mode = ModeOffsetField; + } + addHexFormatRange(fmt_list, field_a_start_, field_a_len_, offset, max_tvb_pos, ModeField); + } + + // ASCII + if (show_ascii_) { + bool in_non_printable = false; + int np_start = 0; + int np_len = 0; + char c; + + for (int tvb_pos = offset; tvb_pos <= max_tvb_pos; tvb_pos++) { + /* insert a space every separator_interval_ bytes */ + if ((tvb_pos != offset) && ((tvb_pos % separator_interval_) == 0)) { + line += ' '; + if (build_x_pos) { + x_pos_to_column_ += QVector().fill(tvb_pos - offset - 1, em_width_ / 2); + } + } + + if (recent.gui_bytes_encoding != BYTES_ENC_EBCDIC && encoding_ == PACKET_CHAR_ENC_CHAR_ASCII) { + c = data_[tvb_pos]; + } else { + c = EBCDIC_to_ASCII1(data_[tvb_pos]); + } + + if (g_ascii_isprint(c)) { + line += c; + if (in_non_printable) { + in_non_printable = false; + addAsciiFormatRange(fmt_list, np_start, np_len, offset, max_tvb_pos, ModeNonPrintable); + } + } else { + line += UTF8_MIDDLE_DOT; + if (!in_non_printable) { + in_non_printable = true; + np_start = tvb_pos; + np_len = 1; + } else { + np_len++; + } + } + if (build_x_pos) { + x_pos_to_column_ += QVector().fill(tvb_pos - offset, stringWidth(line) - x_pos_to_column_.size()); + } + if (tvb_pos == hovered_byte_offset_ || tvb_pos == marked_byte_offset_) { + QRect ho_rect = painter->boundingRect(QRect(), 0, line.right(1)); + ho_rect.moveRight(stringWidth(line)); + ho_rect.moveTop(row_y); + hover_outlines_.append(ho_rect); + } + } + if (in_non_printable) { + addAsciiFormatRange(fmt_list, np_start, np_len, offset, max_tvb_pos, ModeNonPrintable); + } + addAsciiFormatRange(fmt_list, proto_start_, proto_len_, offset, max_tvb_pos, ModeProtocol); + if (addAsciiFormatRange(fmt_list, field_start_, field_len_, offset, max_tvb_pos, ModeField)) { + offset_mode = ModeOffsetField; + } + addAsciiFormatRange(fmt_list, field_a_start_, field_a_len_, offset, max_tvb_pos, ModeField); + } + + // XXX Fields won't be highlighted if neither hex nor ascii are enabled. + addFormatRange(fmt_list, 0, offsetChars(), offset_mode); + + layout_->clearLayout(); + layout_->clearFormats(); + layout_->setText(line); + layout_->setFormats(fmt_list.toVector()); + layout_->beginLayout(); + QTextLine tl = layout_->createLine(); + tl.setLineWidth(totalPixels()); + tl.setLeadingIncluded(true); + layout_->endLayout(); + layout_->draw(painter, QPointF(0.0, row_y)); +} + +bool ByteViewText::addFormatRange(QList &fmt_list, int start, int length, HighlightMode mode) +{ + if (length < 1) + return false; + + QTextLayout::FormatRange format_range; + format_range.start = start; + format_range.length = length; + switch (mode) { + case ModeNormal: + return false; + case ModeField: + format_range.format.setBackground(palette().highlight()); + format_range.format.setForeground(palette().highlightedText()); + break; + case ModeProtocol: + format_range.format.setBackground(palette().window()); + format_range.format.setForeground(palette().windowText()); + break; + case ModeOffsetNormal: + format_range.format.setForeground(offset_normal_fg_); + break; + case ModeOffsetField: + format_range.format.setForeground(offset_field_fg_); + break; + case ModeNonPrintable: + format_range.format.setForeground(offset_normal_fg_); + break; + } + fmt_list << format_range; + return true; +} + +bool ByteViewText::addHexFormatRange(QList &fmt_list, int mark_start, int mark_length, int tvb_offset, int max_tvb_pos, ByteViewText::HighlightMode mode) +{ + int mark_end = mark_start + mark_length - 1; + if (mark_start < 0 || mark_length < 1) return false; + if (mark_start > max_tvb_pos && mark_end < tvb_offset) return false; + + int chars_per_byte; + switch (recent.gui_bytes_view) { + case BYTES_HEX: + chars_per_byte = 2; + break; + case BYTES_BITS: + chars_per_byte = 8; + break; + case BYTES_DEC: + case BYTES_OCT: + chars_per_byte = 3; + break; + default: + ws_assert_not_reached(); + } + int chars_plus_pad = chars_per_byte + 1; + int byte_start = qMax(tvb_offset, mark_start) - tvb_offset; + int byte_end = qMin(max_tvb_pos, mark_end) - tvb_offset; + int fmt_start = offsetChars() + 1 // offset + spacing + + (byte_start / separator_interval_) + + (byte_start * chars_plus_pad); + int fmt_length = offsetChars() + 1 // offset + spacing + + (byte_end / separator_interval_) + + (byte_end * chars_plus_pad) + + chars_per_byte + - fmt_start; + return addFormatRange(fmt_list, fmt_start, fmt_length, mode); +} + +bool ByteViewText::addAsciiFormatRange(QList &fmt_list, int mark_start, int mark_length, int tvb_offset, int max_tvb_pos, ByteViewText::HighlightMode mode) +{ + int mark_end = mark_start + mark_length - 1; + if (mark_start < 0 || mark_length < 1) return false; + if (mark_start > max_tvb_pos && mark_end < tvb_offset) return false; + + int byte_start = qMax(tvb_offset, mark_start) - tvb_offset; + int byte_end = qMin(max_tvb_pos, mark_end) - tvb_offset; + int fmt_start = offsetChars() + DataPrinter::hexChars() + 3 // offset + hex + spacing + + (byte_start / separator_interval_) + + byte_start; + int fmt_length = offsetChars() + DataPrinter::hexChars() + 3 // offset + hex + spacing + + (byte_end / separator_interval_) + + byte_end + + 1 // Just one character. + - fmt_start; + return addFormatRange(fmt_list, fmt_start, fmt_length, mode); +} + +void ByteViewText::scrollToByte(int byte) +{ + verticalScrollBar()->setValue(byte / row_width_); +} + +// Offset character width +int ByteViewText::offsetChars(bool include_pad) +{ + int padding = include_pad ? 2 : 0; + if (! data_.isEmpty() && data_.size() > 0xffff) { + return 8 + padding; + } + return 4 + padding; +} + +// Offset pixel width +int ByteViewText::offsetPixels() +{ + if (show_offset_) { + // One pad space before and after + QString zeroes = QString(offsetChars(), '0'); + return stringWidth(zeroes); + } + return 0; +} + +// Hex pixel width +int ByteViewText::hexPixels() +{ + if (show_hex_) { + // One pad space before and after + QString zeroes = QString(DataPrinter::hexChars() + 2, '0'); + return stringWidth(zeroes); + } + return 0; +} + +int ByteViewText::asciiPixels() +{ + if (show_ascii_) { + // Two pad spaces before, one after + int ascii_chars = (row_width_ + ((row_width_ - 1) / separator_interval_)); + QString zeroes = QString(ascii_chars + 3, '0'); + return stringWidth(zeroes); + } + return 0; +} + +int ByteViewText::totalPixels() +{ + return offsetPixels() + hexPixels() + asciiPixels(); +} + +void ByteViewText::copyBytes(bool) +{ + QAction* action = qobject_cast(sender()); + if (!action) { + return; + } + + int dump_type = action->data().toInt(); + + if (dump_type <= DataPrinter::DP_MimeData) { + DataPrinter printer; + printer.toClipboard((DataPrinter::DumpType) dump_type, this); + } +} + +// We do chunky (per-character) scrolling because it makes some of the +// math easier. Should we do smooth scrolling? +void ByteViewText::updateScrollbars() +{ + const int length = static_cast(data_.size()); + if (length > 0 && line_height_ > 0 && em_width_ > 0) { + int all_lines_height = length / row_width_ + ((length % row_width_) ? 1 : 0) - viewport()->height() / line_height_; + + verticalScrollBar()->setRange(0, qMax(0, all_lines_height)); + horizontalScrollBar()->setRange(0, qMax(0, int((totalPixels() - viewport()->width()) / em_width_))); + } +} + +int ByteViewText::byteOffsetAtPixel(QPoint pos) +{ + int byte = (verticalScrollBar()->value() + (pos.y() / line_height_)) * row_width_; + int x = (horizontalScrollBar()->value() * em_width_) + pos.x(); + int col = x_pos_to_column_.value(x, -1); + + if (col < 0) { + return -1; + } + + byte += col; + if (byte > data_.size()) { + return -1; + } + return byte; +} + +void ByteViewText::setHexDisplayFormat(QAction *action) +{ + if (!action) { + return; + } + + recent.gui_bytes_view = action->data().value(); + + emit byteViewSettingsChanged(); +} + +void ByteViewText::setCharacterEncoding(QAction *action) +{ + if (!action) { + return; + } + + recent.gui_bytes_encoding = action->data().value(); + + emit byteViewSettingsChanged(); +} diff --git a/ui/qt/widgets/byte_view_text.h b/ui/qt/widgets/byte_view_text.h new file mode 100644 index 00000000..b6a6cce8 --- /dev/null +++ b/ui/qt/widgets/byte_view_text.h @@ -0,0 +1,153 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BYTE_VIEW_TEXT_H +#define BYTE_VIEW_TEXT_H + +#include + +#include "ui/recent.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// XXX - Is there any reason we shouldn't add ByteViewImage, etc? + +class ByteViewText : public QAbstractScrollArea, public IDataPrintable +{ + Q_OBJECT + Q_INTERFACES(IDataPrintable) + +public: + explicit ByteViewText(const QByteArray &data, packet_char_enc encoding = PACKET_CHAR_ENC_CHAR_ASCII, QWidget *parent = 0); + ~ByteViewText(); + + virtual QSize minimumSizeHint() const; + + void setFormat(bytes_view_type format); + bool isEmpty() const; + +signals: + void byteHovered(int pos); + void byteSelected(int pos); + void byteViewSettingsChanged(); + +public slots: + void setMonospaceFont(const QFont &mono_font); + void updateByteViewSettings(); + void detachData(); + + void markProtocol(int start, int length); + void markField(int start, int length, bool scroll_to = true); + void markAppendix(int start, int length); + void unmarkField(); + +protected: + virtual void paintEvent(QPaintEvent *); + virtual void resizeEvent(QResizeEvent *); + virtual void mousePressEvent (QMouseEvent * event); + virtual void mouseMoveEvent (QMouseEvent * event); + virtual void leaveEvent(QEvent *event); + virtual void contextMenuEvent(QContextMenuEvent *event); + +private: + // Text highlight modes. + typedef enum { + ModeNormal, + ModeField, + ModeProtocol, + ModeOffsetNormal, + ModeOffsetField, + ModeNonPrintable + } HighlightMode; + + QTextLayout *layout_; + QByteArray data_; + + void updateLayoutMetrics(); + int stringWidth(const QString &line); + void drawLine(QPainter *painter, const int offset, const int row_y); + bool addFormatRange(QList &fmt_list, int start, int length, HighlightMode mode); + bool addHexFormatRange(QList &fmt_list, int mark_start, int mark_length, int tvb_offset, int max_tvb_pos, HighlightMode mode); + bool addAsciiFormatRange(QList &fmt_list, int mark_start, int mark_length, int tvb_offset, int max_tvb_pos, HighlightMode mode); + void scrollToByte(int byte); + void updateScrollbars(); + int byteOffsetAtPixel(QPoint pos); + + void createContextMenu(); + void updateContextMenu(); + + int offsetChars(bool include_pad = true); + int offsetPixels(); + int hexPixels(); + int asciiPixels(); + int totalPixels(); + const QByteArray printableData() { return data_; } + + static const int separator_interval_; + + // Colors + QColor offset_normal_fg_; + QColor offset_field_fg_; + + // Data + packet_char_enc encoding_; // ASCII or EBCDIC + QMenu ctx_menu_; + + // Data highlight + int hovered_byte_offset_; + int marked_byte_offset_; + int proto_start_; + int proto_len_; + int field_start_; + int field_len_; + int field_a_start_; + int field_a_len_; + + bool show_offset_; // Should we show the byte offset? + bool show_hex_; // Should we show the hex display? + bool show_ascii_; // Should we show the ASCII display? + int row_width_; // Number of bytes per line + int em_width_; // Single character width and text margin. NOTE: Use fontMetrics::width for multiple characters. + int line_height_; // Font line spacing + QList hover_outlines_; // Hovered byte outlines. + + bool allow_hover_selection_; + + // Data selection + QVector x_pos_to_column_; + + // Context menu actions + QAction *action_allow_hover_selection_; + QAction *action_bytes_hex_; + QAction *action_bytes_dec_; + QAction *action_bytes_oct_; + QAction *action_bytes_bits_; + QAction *action_bytes_enc_from_packet_; + QAction *action_bytes_enc_ascii_; + QAction *action_bytes_enc_ebcdic_; + +private slots: + void copyBytes(bool); + void setHexDisplayFormat(QAction *action); + void setCharacterEncoding(QAction *action); + void toggleHoverAllowed(bool); + +}; + +#endif // BYTE_VIEW_TEXT_H diff --git a/ui/qt/widgets/capture_filter_combo.cpp b/ui/qt/widgets/capture_filter_combo.cpp new file mode 100644 index 00000000..70635cef --- /dev/null +++ b/ui/qt/widgets/capture_filter_combo.cpp @@ -0,0 +1,133 @@ +/* capture_filter_combo.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include "ui/recent_utils.h" +#include "ui/recent.h" + +#include + +#include +#include +#include "main_application.h" + +CaptureFilterCombo::CaptureFilterCombo(QWidget *parent, bool plain) : + QComboBox(parent), + cf_edit_(NULL) +{ + cf_edit_ = new CaptureFilterEdit(this, plain); + + setEditable(true); + setLineEdit(cf_edit_); + // setLineEdit will create a new QCompleter that performs inline completion, + // be sure to disable that since our CaptureFilterEdit performs its own + // popup completion. As QLineEdit's completer is designed for full line + // completion, we cannot reuse it for word completion. + setCompleter(0); + // Default is Preferred. + setSizePolicy(QSizePolicy::MinimumExpanding, sizePolicy().verticalPolicy()); + setInsertPolicy(QComboBox::NoInsert); + setAccessibleName(tr("Capture filter selector")); + updateStyleSheet(); + + connect(this, &CaptureFilterCombo::interfacesChanged, cf_edit_, + static_cast(&CaptureFilterEdit::checkFilter)); + connect(cf_edit_, &CaptureFilterEdit::captureFilterSyntaxChanged, + this, &CaptureFilterCombo::captureFilterSyntaxChanged); + connect(cf_edit_, &CaptureFilterEdit::startCapture, this, &CaptureFilterCombo::startCapture); + connect(cf_edit_, &CaptureFilterEdit::startCapture, this, &CaptureFilterCombo::saveAndRebuildFilterList); + connect(mainApp, &MainApplication::appInitialized, this, &CaptureFilterCombo::rebuildFilterList); + connect(mainApp, &MainApplication::preferencesChanged, this, &CaptureFilterCombo::rebuildFilterList); + + rebuildFilterList(); + clearEditText(); +} + +void CaptureFilterCombo::writeRecent(FILE *rf) +{ + int i; + + for (i = 0; i < count(); i++) { + const QByteArray& filter = itemText(i).toUtf8(); + if (!filter.isEmpty()) { + fprintf(rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", filter.constData()); + } + } +} + +bool CaptureFilterCombo::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + updateStyleSheet(); + break; + default: + break; + } + return QComboBox::event(event); +} + +void CaptureFilterCombo::updateStyleSheet() +{ + const char *display_mode = ColorUtils::themeIsDark() ? "dark" : "light"; + + QString ss = QString( + "QComboBox {" +#ifdef Q_OS_MAC + " border: 1px solid gray;" +#else + " border: 1px solid palette(shadow);" +#endif + " border-radius: 3px;" + " padding: 0px 0px 0px 0px;" + " margin-left: 0px;" + " min-width: 20em;" + " }" + + "QComboBox::drop-down {" + " subcontrol-origin: padding;" + " subcontrol-position: top right;" + " width: 14px;" + " border-left-width: 0px;" + " }" + + "QComboBox::down-arrow {" + " image: url(:/stock_icons/14x14/x-filter-dropdown.%1.png);" + " }" + + "QComboBox::down-arrow:on { /* shift the arrow when popup is open */" + " top: 1px;" + " left: 1px;" + "}" + ).arg(display_mode); + setStyleSheet(ss); +} + +void CaptureFilterCombo::saveAndRebuildFilterList() +{ + if (!currentText().isEmpty()) { + recent_add_cfilter(NULL, currentText().toUtf8().constData()); + } + rebuildFilterList(); +} + +void CaptureFilterCombo::rebuildFilterList() +{ + lineEdit()->blockSignals(true); + GList *cfilter_list = recent_get_cfilter_list(NULL); + QString cur_filter = currentText(); + clear(); + for (GList *li = g_list_first(cfilter_list); li != NULL; li = gxx_list_next(li)) { + addItem(gxx_list_data(const gchar *, li)); + } + lineEdit()->setText(cur_filter); + lineEdit()->blockSignals(false); +} diff --git a/ui/qt/widgets/capture_filter_combo.h b/ui/qt/widgets/capture_filter_combo.h new file mode 100644 index 00000000..6e08f4b0 --- /dev/null +++ b/ui/qt/widgets/capture_filter_combo.h @@ -0,0 +1,44 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_FILTER_COMBO_H +#define CAPTURE_FILTER_COMBO_H + +#include + +#include +#include + +class CaptureFilterCombo : public QComboBox +{ + Q_OBJECT +public: + explicit CaptureFilterCombo(QWidget *parent = 0, bool plain = false); + bool addRecentCapture(const char *filter); + void writeRecent(FILE *rf); + void setConflict(bool conflict = false) { cf_edit_->setConflict(conflict); } + +signals: + void interfacesChanged(); + void captureFilterSyntaxChanged(bool valid); + void startCapture(); + +protected: + virtual bool event(QEvent *event); + +private: + void updateStyleSheet(); + CaptureFilterEdit *cf_edit_; + +private slots: + void saveAndRebuildFilterList(); + void rebuildFilterList(); +}; + +#endif // CAPTURE_FILTER_COMBO_H diff --git a/ui/qt/widgets/capture_filter_edit.cpp b/ui/qt/widgets/capture_filter_edit.cpp new file mode 100644 index 00000000..3c72fe41 --- /dev/null +++ b/ui/qt/widgets/capture_filter_edit.cpp @@ -0,0 +1,562 @@ +/* capture_filter_edit.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#include "capture_opts.h" + +#include +#include +#include + +#include +#include "capture_filter_syntax_worker.h" +#include "filter_dialog.h" +#include +#include "main_application.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +// To do: +// - This duplicates some DisplayFilterEdit code. +// - We need simplified (button- and dropdown-free) versions for use in dialogs and field-only checking. + +static const QString libpcap_primitive_chars_ = "-0123456789abcdefghijklmnopqrstuvwxyz"; + +// Primitives are from pcap-filter.manmisc +static const QStringList libpcap_primitives_ = QStringList() + // "Abbreviations for..." + << "ether proto" + << "ip" << "ip6" << "arp" << "rarp" << "atalk" << "aarp" << "decnet" << "iso" << "stp" << "ipx" << "netbeui" + << "moprc" << "mopdl" + // ip proto + << "tcp" << "udp" << "icmp" + // iso proto + << "clnp" << "esis" << "isis" + // IS‐IS PDU types + << "l1" << "l2" << "iih" << "lsp" << "snp" << "csnp" << "psnp" + + // grep -E '^\.IP "\\fB.*\\f(R"|P)$' pcap-filter.manmisc | sed -e 's/^\.IP "\\fB/<< "/' -e 's/ *\\f.*/"/' | sort -u + << "action" + << "clnp" + << "decnet dst" + << "decnet host" + << "decnet src" + << "dir" + << "dst host" + << "dst net" + << "dst port" + << "dst portrange" + << "ether broadcast" + << "ether dst" + << "ether host" + << "ether multicast" + << "ether src" + << "gateway" + << "greater" + << "host" + << "ifname" + << "ip broadcast" + << "ip multicast" + << "ip proto" + << "ip protochain" + << "ip6 multicast" + << "ip6 proto" + << "ip6 protochain" + << "iso proto" + << "l1" + << "lat" + << "less" + << "mpls" + << "net" + << "on" + << "port" + << "portrange" + << "reason" + << "rnr" + << "rset" + << "rulenum" + << "ruleset" + << "src host" + << "src net" + << "src port" + << "src portrange" + << "srnr" + << "subrulenum" + << "subtype" + << "type" + << "vlan" + << "wlan addr1" + << "wlan addr2" + << "wlan addr3" + << "wlan addr4" + << "wlan ra" + << "wlan ta" + ; + +CaptureFilterEdit::CaptureFilterEdit(QWidget *parent, bool plain) : + SyntaxLineEdit(parent), + plain_(plain), + field_name_only_(false), + enable_save_action_(false), + save_action_(NULL), + remove_action_(NULL), + actions_(Q_NULLPTR), + bookmark_button_(NULL), + clear_button_(NULL), + apply_button_(NULL) +{ + setAccessibleName(tr("Capture filter entry")); + + completion_model_ = new QStringListModel(this); + setCompleter(new QCompleter(completion_model_, this)); + setCompletionTokenChars(libpcap_primitive_chars_); + + setConflict(false); + + QString buttonStyle = QString( + "QToolButton {" + " border: none;" + " background: transparent;" // Disables platform style on Windows. + " padding: 0 0 0 0;" + "}" + "QToolButton::menu-indicator {" + " image: none;" + "}" + ); + + if (!plain_) { + bookmark_button_ = new StockIconToolButton(this, "x-capture-filter-bookmark"); + bookmark_button_->setCursor(Qt::ArrowCursor); + bookmark_button_->setMenu(new QMenu(bookmark_button_)); + bookmark_button_->setPopupMode(QToolButton::InstantPopup); + bookmark_button_->setToolTip(tr("Manage saved bookmarks.")); + bookmark_button_->setIconSize(QSize(14, 14)); + bookmark_button_->setStyleSheet(buttonStyle); + connect(bookmark_button_, &StockIconToolButton::clicked, this, &CaptureFilterEdit::bookmarkClicked); + + clear_button_ = new StockIconToolButton(this, "x-filter-clear"); + clear_button_->setCursor(Qt::ArrowCursor); + clear_button_->setToolTip(QString()); + clear_button_->setIconSize(QSize(14, 14)); + clear_button_->setStyleSheet(buttonStyle); + connect(clear_button_, &StockIconToolButton::clicked, this, &CaptureFilterEdit::clearFilter); + } + + connect(this, &CaptureFilterEdit::textChanged, this, + static_cast(&CaptureFilterEdit::checkFilter)); + +#if 0 + // Disable the apply button for now + if (!plain_) { + apply_button_ = new StockIconToolButton(this, "x-filter-apply"); + apply_button_->setCursor(Qt::ArrowCursor); + apply_button_->setEnabled(false); + apply_button_->setToolTip(tr("Apply this filter string to the display.")); + apply_button_->setIconSize(QSize(24, 14)); + apply_button_->setStyleSheet(buttonStyle); + connect(apply_button_, &StockIconToolButton::clicked, this, &CaptureFilterEdit::applyCaptureFilter); + } +#endif + connect(this, &CaptureFilterEdit::returnPressed, this, &CaptureFilterEdit::applyCaptureFilter); + + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + QSize bksz(0, 0); + if (bookmark_button_) bksz = bookmark_button_->sizeHint(); + QSize cbsz(0, 0); + if (clear_button_) cbsz = clear_button_->sizeHint(); + QSize apsz(0, 0); + if (apply_button_) apsz = apply_button_->sizeHint(); + + setStyleSheet(QString( + "CaptureFilterEdit {" + " padding-left: %1px;" + " margin-left: %2px;" + " margin-right: %3px;" + "}" + ) + .arg(frameWidth + 1) + .arg(bksz.width()) + .arg(cbsz.width() + apsz.width() + frameWidth + 2) + ); + + QComboBox *cf_combo = qobject_cast(parent); + if (cf_combo) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + connect(cf_combo, static_cast(&QComboBox::textActivated), + this, &CaptureFilterEdit::textEdited); +#else + connect(cf_combo, static_cast(&QComboBox::activated), + this, &CaptureFilterEdit::textEdited); +#endif + } + + syntax_thread_ = new QThread; + syntax_worker_ = new CaptureFilterSyntaxWorker; + syntax_worker_->moveToThread(syntax_thread_); + connect(mainApp, &MainApplication::appInitialized, this, &CaptureFilterEdit::updateBookmarkMenu); + connect(mainApp, &MainApplication::captureFilterListChanged, this, &CaptureFilterEdit::updateBookmarkMenu); + connect(syntax_thread_, &QThread::started, this, + static_cast(&CaptureFilterEdit::checkFilter)); + connect(syntax_worker_, &CaptureFilterSyntaxWorker::syntaxResult, + this, &CaptureFilterEdit::setFilterSyntaxState); + connect(this, &CaptureFilterEdit::captureFilterChanged, syntax_worker_, &CaptureFilterSyntaxWorker::checkFilter); + syntax_thread_->start(); + updateBookmarkMenu(); +} + +CaptureFilterEdit::~CaptureFilterEdit() +{ + syntax_thread_->quit(); + syntax_thread_->wait(); + delete syntax_thread_; + delete syntax_worker_; +} + +void CaptureFilterEdit::paintEvent(QPaintEvent *evt) { + SyntaxLineEdit::paintEvent(evt); + + if (bookmark_button_) { + // Draw the borders by hand. We could try to do this in the + // style sheet but it's a pain. +#ifdef Q_OS_MAC + QColor divider_color = Qt::gray; +#else + QColor divider_color = palette().shadow().color(); +#endif + QPainter painter(this); + painter.setPen(divider_color); + QRect cr = contentsRect(); + QSize bksz = bookmark_button_->size(); + painter.drawLine(bksz.width(), cr.top(), bksz.width(), cr.bottom() + 1); + + if (!text().isEmpty()) { + int xpos = cr.width() - 4; + if (clear_button_ && clear_button_->isVisible()) + xpos -= clear_button_->width(); + if (apply_button_ && apply_button_->isVisible()) + xpos -= apply_button_->width(); + painter.drawLine(xpos, cr.top(), xpos, cr.bottom() + 1); + } + } +} + +void CaptureFilterEdit::resizeEvent(QResizeEvent *) +{ + QSize cbsz(0, 0); + if (clear_button_) cbsz = clear_button_->sizeHint(); + QSize apsz(0, 0); + if (apply_button_) apsz = apply_button_->sizeHint(); + + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + if (clear_button_) { + clear_button_->move(contentsRect().right() - frameWidth - cbsz.width() - apsz.width(), + contentsRect().top()); + clear_button_->setMinimumHeight(contentsRect().height()); + clear_button_->setMaximumHeight(contentsRect().height()); + } + if (apply_button_) { + apply_button_->move(contentsRect().right() - frameWidth - apsz.width(), + contentsRect().top()); + apply_button_->setMinimumHeight(contentsRect().height()); + apply_button_->setMaximumHeight(contentsRect().height()); + } + if (bookmark_button_) { + bookmark_button_->setMinimumHeight(contentsRect().height()); + bookmark_button_->setMaximumHeight(contentsRect().height()); + } +} + +void CaptureFilterEdit::setConflict(bool conflict) +{ + if (conflict) { + //: This is a very long concept that needs to fit into a short space. + placeholder_text_ = tr("Multiple filters selected. Override them here or leave this blank to preserve them."); + setToolTip(tr("

The interfaces you have selected have different capture filters." + " Typing a filter here will override them. Doing nothing will" + " preserve them.

")); + } else { + placeholder_text_ = QString(tr("Enter a capture filter %1")).arg(UTF8_HORIZONTAL_ELLIPSIS); + setToolTip(QString()); + } + setPlaceholderText(placeholder_text_); +} + +// XXX Make this private along with setConflict. +QPair CaptureFilterEdit::getSelectedFilter() +{ + QString user_filter; + bool filter_conflict = false; +#ifdef HAVE_LIBPCAP + int selected_devices = 0; + + for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (device->selected) { + selected_devices++; + if (selected_devices == 1) { + user_filter = device->cfilter; + } else { + if (user_filter.compare(device->cfilter)) { + filter_conflict = true; + } + } + } + } +#endif // HAVE_LIBPCAP + return QPair(user_filter, filter_conflict); +} + +void CaptureFilterEdit::checkFilter(const QString& filter) +{ + if (text().length() == 0 && actions_ && actions_->checkedAction()) + actions_->checkedAction()->setChecked(false); + + setSyntaxState(Busy); + mainApp->popStatus(MainApplication::FilterSyntax); + setToolTip(QString()); + bool empty = filter.isEmpty(); + + setConflict(false); + if (bookmark_button_) { + bool match = false; + + FilterListModel model(FilterListModel::Capture); + QModelIndex idx = model.findByExpression(text()); + if (idx.isValid()) { + match = true; + + bookmark_button_->setStockIcon("x-filter-matching-bookmark"); + if (remove_action_) { + remove_action_->setData(text()); + remove_action_->setEnabled(true); + } + } else { + bookmark_button_->setStockIcon("x-capture-filter-bookmark"); + if (remove_action_) { + remove_action_->setEnabled(false); + } + } + + enable_save_action_ = (!match && !filter.isEmpty()); + if (save_action_) { + save_action_->setEnabled(false); + } + } + + if (apply_button_) { + apply_button_->setEnabled(false); + } + + if (clear_button_) { + clear_button_->setVisible(!empty); + } + + if (empty) { + setFilterSyntaxState(filter, Empty, QString()); + } else { + emit captureFilterChanged(filter); + } +} + +void CaptureFilterEdit::checkFilter() +{ + checkFilter(text()); +} + +void CaptureFilterEdit::updateBookmarkMenu() +{ + if (!bookmark_button_) + return; + + QMenu *bb_menu = bookmark_button_->menu(); + bb_menu->clear(); + + save_action_ = bb_menu->addAction(tr("Save this filter")); + connect(save_action_, &QAction::triggered, this, &CaptureFilterEdit::saveFilter); + remove_action_ = bb_menu->addAction(tr("Remove this filter")); + connect(remove_action_, &QAction::triggered, this, &CaptureFilterEdit::removeFilter); + QAction *manage_action = bb_menu->addAction(tr("Manage Capture Filters")); + connect(manage_action, &QAction::triggered, this, &CaptureFilterEdit::showFilters); + bb_menu->addSeparator(); + + FilterListModel model(FilterListModel::Capture); + QModelIndex idx = model.findByExpression(text()); + + int one_em = bb_menu->fontMetrics().height(); + + if (! actions_) + actions_ = new QActionGroup(this); + + for (int row = 0; row < model.rowCount(); row++) + { + QModelIndex nameIdx = model.index(row, FilterListModel::ColumnName); + QString name = nameIdx.data().toString(); + QString expr = model.index(row, FilterListModel::ColumnExpression).data().toString(); + QString prep_text = QString("%1: %2").arg(name).arg(expr); + + prep_text = bb_menu->fontMetrics().elidedText(prep_text, Qt::ElideRight, one_em * 40); + QAction * prep_action = bb_menu->addAction(prep_text); + prep_action->setCheckable(true); + if (nameIdx == idx) + prep_action->setChecked(true); + + actions_->addAction(prep_action); + prep_action->setProperty("capture_filter", expr); + connect(prep_action, &QAction::triggered, this, &CaptureFilterEdit::prepareFilter); + } + + checkFilter(); +} + +void CaptureFilterEdit::setFilterSyntaxState(QString filter, int state, QString err_msg) +{ + if (filter.compare(text()) == 0) { // The user hasn't changed the filter + setSyntaxState((SyntaxState)state); + if (!err_msg.isEmpty()) { + mainApp->pushStatus(MainApplication::FilterSyntax, err_msg); + setToolTip(err_msg); + } + } + + bool valid = (state != Invalid); + + if (valid) { + if (save_action_) { + save_action_->setEnabled(enable_save_action_); + } + if (apply_button_) { + apply_button_->setEnabled(true); + } + } + + emit captureFilterSyntaxChanged(valid); +} + +void CaptureFilterEdit::bookmarkClicked() +{ + emit addBookmark(text()); +} + +void CaptureFilterEdit::clearFilter() +{ + clear(); + emit textEdited(text()); +} + +void CaptureFilterEdit::buildCompletionList(const QString &primitive_word, const QString &preamble _U_) +{ + if (primitive_word.length() < 1) { + completion_model_->setStringList(QStringList()); + return; + } + + // Grab matching capture filters from our parent combo and from the + // saved capture filters file. Skip ones that look like single fields + // and assume they will be added below. + QStringList complex_list; + QComboBox *cf_combo = qobject_cast(parent()); + if (cf_combo) { + for (int i = 0; i < cf_combo->count() ; i++) { + QString recent_filter = cf_combo->itemText(i); + + if (isComplexFilter(recent_filter)) { + complex_list << recent_filter; + } + } + } + FilterListModel model(FilterListModel::Capture); + for (int row = 0; row < model.rowCount(); row++) + { + QString saved_filter = model.index(row, FilterListModel::ColumnExpression).data().toString(); + + if (isComplexFilter(saved_filter) && !complex_list.contains(saved_filter)) { + complex_list << saved_filter; + } + } + + // libpcap has a small number of primitives so we just add the whole list + // sans the current word. + QStringList primitive_list = libpcap_primitives_; + primitive_list.removeAll(primitive_word); + + completion_model_->setStringList(complex_list + primitive_list); + completer()->setCompletionPrefix(primitive_word); +} + +void CaptureFilterEdit::applyCaptureFilter() +{ + if (syntaxState() == Invalid) { + return; + } + + emit startCapture(); +} + +void CaptureFilterEdit::saveFilter() +{ + FilterDialog *capture_filter_dlg = new FilterDialog(window(), FilterDialog::CaptureFilter, text()); + capture_filter_dlg->setWindowModality(Qt::ApplicationModal); + capture_filter_dlg->setAttribute(Qt::WA_DeleteOnClose); + capture_filter_dlg->show(); +} + +void CaptureFilterEdit::removeFilter() +{ + if (! actions_ || ! actions_->checkedAction()) + return; + + QAction *ra = actions_->checkedAction(); + if (ra->property("capture_filter").toString().isEmpty()) + return; + + QString remove_filter = ra->property("capture_filter").toString(); + + FilterListModel model(FilterListModel::Capture); + QModelIndex idx = model.findByExpression(remove_filter); + if (idx.isValid()) + { + model.removeFilter(idx); + model.saveList(); + } + + updateBookmarkMenu(); +} + +void CaptureFilterEdit::showFilters() +{ + FilterDialog *capture_filter_dlg = new FilterDialog(window(), FilterDialog::CaptureFilter); + capture_filter_dlg->setWindowModality(Qt::ApplicationModal); + capture_filter_dlg->setAttribute(Qt::WA_DeleteOnClose); + capture_filter_dlg->show(); +} + +void CaptureFilterEdit::prepareFilter() +{ + QAction *pa = qobject_cast(sender()); + if (! pa || pa->property("capture_filter").toString().isEmpty()) + return; + + QString filter = pa->property("capture_filter").toString(); + setText(filter); + + emit textEdited(filter); +} diff --git a/ui/qt/widgets/capture_filter_edit.h b/ui/qt/widgets/capture_filter_edit.h new file mode 100644 index 00000000..34ec4609 --- /dev/null +++ b/ui/qt/widgets/capture_filter_edit.h @@ -0,0 +1,79 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CAPTURE_FILTER_EDIT_H +#define CAPTURE_FILTER_EDIT_H + +#include +#include +#include + +#include + +class CaptureFilterSyntaxWorker; +class StockIconToolButton; + +class CaptureFilterEdit : public SyntaxLineEdit +{ + Q_OBJECT +public: + explicit CaptureFilterEdit(QWidget *parent = 0, bool plain = false); + ~CaptureFilterEdit(); + void setConflict(bool conflict = false); + // No selections: (QString(), false) + // Selections, same filter: (filter, false) + // Selections, different filters (QString(), true) + static QPair getSelectedFilter(); + +protected: + void paintEvent(QPaintEvent *evt); + void resizeEvent(QResizeEvent *); + void keyPressEvent(QKeyEvent *event) { completionKeyPressEvent(event); } + void focusInEvent(QFocusEvent *event) { completionFocusInEvent(event); } + +public slots: + void checkFilter(); + void updateBookmarkMenu(); + void saveFilter(); + void removeFilter(); + void showFilters(); + void prepareFilter(); + +private slots: + void applyCaptureFilter(); + void checkFilter(const QString &filter); + void setFilterSyntaxState(QString filter, int state, QString err_msg); + void bookmarkClicked(); + void clearFilter(); + +private: + bool plain_; + bool field_name_only_; + bool enable_save_action_; + QString placeholder_text_; + QAction *save_action_; + QAction *remove_action_; + QActionGroup * actions_; + StockIconToolButton *bookmark_button_; + StockIconToolButton *clear_button_; + StockIconToolButton *apply_button_; + CaptureFilterSyntaxWorker *syntax_worker_; + QThread *syntax_thread_; + + void buildCompletionList(const QString &primitive_word, const QString &preamble); + +signals: + void captureFilterSyntaxChanged(bool valid); + void captureFilterChanged(const QString filter); + void startCapture(); + void addBookmark(const QString filter); + +}; + +#endif // CAPTURE_FILTER_EDIT_H diff --git a/ui/qt/widgets/clickable_label.cpp b/ui/qt/widgets/clickable_label.cpp new file mode 100644 index 00000000..731281c9 --- /dev/null +++ b/ui/qt/widgets/clickable_label.cpp @@ -0,0 +1,55 @@ +/* clickable_label.cpp + * + * Taken from https://wiki.qt.io/Clickable_QLabel and adapted for usage + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +ClickableLabel::ClickableLabel(QWidget* parent) + : QLabel(parent) +{ + setMinimumWidth(0); + setText(QString()); + + setStyleSheet(QString( + "QLabel {" + " margin-left: 0.5em;" + " }" + )); +} + +void ClickableLabel::mouseReleaseEvent(QMouseEvent * event) +{ + /* It has to be ensured, that if the user clicks on the label and then moves away out of + * the scope of the widget, the event does not fire. Otherwise this behavior differs from + * the way, the toolbar buttons work for instance */ + if (event->pos().x() < 0 || event->pos().x() > size().width()) + return; + if (event->pos().y() < 0 || event->pos().y() > size().height()) + return; + + emit clicked(); +} + +void ClickableLabel::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + emit clickedAt(event->globalPosition().toPoint(), Qt::LeftButton); +#else + emit clickedAt(event->globalPos(), Qt::LeftButton); +#endif +} + +void ClickableLabel::contextMenuEvent(QContextMenuEvent *event) +{ + emit clickedAt(QPoint(event->globalPos()), Qt::RightButton); +} diff --git a/ui/qt/widgets/clickable_label.h b/ui/qt/widgets/clickable_label.h new file mode 100644 index 00000000..f958a4d4 --- /dev/null +++ b/ui/qt/widgets/clickable_label.h @@ -0,0 +1,33 @@ +/** @file + * + * Taken from https://wiki.qt.io/Clickable_QLabel and adapted for usage + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CLICKABLE_LABEL_H_ +#define CLICKABLE_LABEL_H_ + +#include + +class ClickableLabel : public QLabel +{ + Q_OBJECT +public: + explicit ClickableLabel(QWidget* parent=0); + +signals: + void clicked(); + void clickedAt(const QPoint &global_pos, Qt::MouseButton button); + +protected: + void mouseReleaseEvent(QMouseEvent* event); + void mousePressEvent(QMouseEvent *event); + void contextMenuEvent(QContextMenuEvent *event); +}; + +#endif /* CLICKABLE_LABEL_H_ */ diff --git a/ui/qt/widgets/copy_from_profile_button.cpp b/ui/qt/widgets/copy_from_profile_button.cpp new file mode 100644 index 00000000..1633ab68 --- /dev/null +++ b/ui/qt/widgets/copy_from_profile_button.cpp @@ -0,0 +1,136 @@ +/* copy_from_profile_button.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +#include +#include +#include +#include + +CopyFromProfileButton::CopyFromProfileButton(QWidget * parent, QString fileName, QString toolTip) : + QPushButton(parent), + buttonMenu_(Q_NULLPTR) +{ + setText(tr("Copy from")); + if (toolTip.length() == 0) + setToolTip(tr("Copy entries from another profile.")); + else { + setToolTip(toolTip); + } + + if (fileName.length() > 0) + setFilename(fileName); +} + +void CopyFromProfileButton::setFilename(QString filename) +{ + setEnabled(false); + + if (filename.length() <= 0) + return; + + ProfileModel model(this); + + QList global; + QList user; + + QAction * pa = systemDefault(filename); + if (pa) + global << pa; + + if (! buttonMenu_) + buttonMenu_ = new QMenu(); + + if (buttonMenu_->actions().count() > 0) + buttonMenu_->clear(); + + for (int cnt = 0; cnt < model.rowCount(); cnt++) + { + QModelIndex idx = model.index(cnt, ProfileModel::COL_NAME); + QString profilePath = idx.data(ProfileModel::DATA_PATH).toString(); + if (! idx.isValid() || profilePath.isEmpty()) + continue; + + if (! idx.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool() || idx.data(ProfileModel::DATA_IS_SELECTED).toBool()) + continue; + + QDir profileDir(profilePath); + if (! profileDir.exists()) + continue; + + QFileInfo fi(profileDir.filePath(filename)); + if (! fi.exists()) + continue; + + if (! config_file_exists_with_entries(fi.absoluteFilePath().toUtf8().constData(), '#')) + continue; + + QString name = idx.data().toString(); + pa = new QAction(name, this); + if (idx.data(ProfileModel::DATA_IS_DEFAULT).toBool()) + buttonMenu_->addAction(pa); + else if (idx.data(ProfileModel::DATA_IS_GLOBAL).toBool()) + global << pa; + else + user << pa; + + pa->setFont(idx.data(Qt::FontRole).value()); + pa->setProperty("profile_name", name); + pa->setProperty("profile_is_global", idx.data(ProfileModel::DATA_IS_GLOBAL)); + pa->setProperty("profile_filename", fi.absoluteFilePath()); + } + + buttonMenu_->addActions(user); + if (global.count() > 0) + { + if (actions().count() > 0) + buttonMenu_->addSeparator(); + buttonMenu_->addActions(global); + } + if (buttonMenu_->actions().count() <= 0) + return; + + connect(buttonMenu_, &QMenu::triggered, this, &CopyFromProfileButton::menuActionTriggered); + setMenu(buttonMenu_); + setEnabled(true); +} + +// "System default" is not a profile. +// Add a special entry for this if the filename exists. +QAction * CopyFromProfileButton::systemDefault(QString filename) +{ + QAction * data = Q_NULLPTR; + + QDir dataDir(get_datafile_dir()); + QString path = dataDir.filePath(filename); + if (QFile::exists(path)) + { + data = new QAction(tr("System default"), this); + data->setData(path); + QFont font = data->font(); + font.setItalic(true); + data->setFont(font); + data->setProperty("profile_filename", path); + } + + return data; +} + +void CopyFromProfileButton::menuActionTriggered(QAction * action) +{ + if (action->property("profile_filename").toString().length() > 0) + { + QString filename = action->property("profile_filename").toString(); + if (QFileInfo::exists(filename)) + emit copyProfile(filename); + } +} diff --git a/ui/qt/widgets/copy_from_profile_button.h b/ui/qt/widgets/copy_from_profile_button.h new file mode 100644 index 00000000..f074bdb8 --- /dev/null +++ b/ui/qt/widgets/copy_from_profile_button.h @@ -0,0 +1,43 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef COPY_FROM_PROFILE_BUTTON_H +#define COPY_FROM_PROFILE_BUTTON_H + +#include +#include + +#include +#include +#include +#include + +class CopyFromProfileButton : public QPushButton +{ + Q_OBJECT + +public: + CopyFromProfileButton(QWidget * parent = Q_NULLPTR, QString profileFile = QString(), QString toolTip = QString()); + + void setFilename(QString filename); + +signals: + void copyProfile(QString filename); + +private: + QString filename_; + QMenu * buttonMenu_; + + QAction * systemDefault(QString filename); + +private slots: + void menuActionTriggered(QAction *); +}; + +#endif // COPY_FROM_PROFILE_BUTTON_H diff --git a/ui/qt/widgets/detachable_tabwidget.cpp b/ui/qt/widgets/detachable_tabwidget.cpp new file mode 100644 index 00000000..caffd743 --- /dev/null +++ b/ui/qt/widgets/detachable_tabwidget.cpp @@ -0,0 +1,212 @@ +/* @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DetachableTabWidget::DetachableTabWidget(QWidget *parent) : + QTabWidget(parent) +{ + DragDropTabBar * tabBar = new DragDropTabBar(this); + connect(tabBar, &DragDropTabBar::onDetachTab, this, &DetachableTabWidget::detachTab); + connect(tabBar, &DragDropTabBar::onMoveTab, this, &DetachableTabWidget::moveTab); + + setMovable(false); + + setTabBar(tabBar); +} + +void DetachableTabWidget::setTabBasename(QString newName) { + _tabBasename = newName; +} + +QString DetachableTabWidget::tabBasename() const { + return _tabBasename; +} + +void DetachableTabWidget::moveTab(int from, int to) +{ + QWidget * contentWidget = widget(from); + QString text = tabText(from); + + removeTab(from); + insertTab(to, contentWidget, text); + setCurrentIndex(to); +} + +void DetachableTabWidget::detachTab(int tabIdx, QPoint pos) +{ + QString name = tabText(tabIdx); + + QWidget * contentWidget = widget(tabIdx); + + /* For the widget to properly show in the dialog, it has to be + * removed properly and unhidden. QTabWidget uses a QStackedWidget for + * all parents of widgets. So we remove it from it's own parent and then + * unhide it to show the widget in the dialog */ + QStackedWidget * par = qobject_cast(contentWidget->parent()); + if (!par) + return; + QRect contentWidgetRect = par->frameGeometry(); + par->removeWidget(contentWidget); + contentWidget->setHidden(false); + + ToolDialog * detachedTab = new ToolDialog(contentWidget, parentWidget()); + detachedTab->setWindowModality(Qt::NonModal); + detachedTab->setWindowTitle(_tabBasename + ": " + name); + detachedTab->setObjectName(name); + detachedTab->setGeometry(contentWidgetRect); + connect(detachedTab, &ToolDialog::onCloseSignal, this, &DetachableTabWidget::attachTab); + detachedTab->move(pos); + detachedTab->show(); +} + +void DetachableTabWidget::attachTab(QWidget * content, QString name) +{ + content->setParent(this); + + int index = addTab(content, name); + if (index > -1) + setCurrentIndex(index); +} + +ToolDialog::ToolDialog(QWidget *contentWidget, QWidget *parent, Qt::WindowFlags f) : + QDialog(parent, f) +{ + _contentWidget = contentWidget; + + _contentWidget->setParent(this); + QVBoxLayout * layout = new QVBoxLayout(this); + layout->addWidget(_contentWidget); + this->setLayout(layout); +} + +bool ToolDialog::event(QEvent *event) +{ + /** + * Capture a double click event on the dialog's window frame + */ + if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) { + event->accept(); + close(); + } + + return QDialog::event(event); +} + +void ToolDialog::closeEvent(QCloseEvent * /*event*/) +{ + emit onCloseSignal(_contentWidget, objectName()); +} + +DragDropTabBar::DragDropTabBar(QWidget *parent) : + QTabBar(parent) +{ + setAcceptDrops(true); + setElideMode(Qt::ElideRight); + setSelectionBehaviorOnRemove(QTabBar::SelectLeftTab); + + _dragStartPos = QPoint(); + _dragDropPos = QPoint(); + _mouseCursor = QCursor(); + _dragInitiated = false; +} + +void DragDropTabBar::mouseDoubleClickEvent(QMouseEvent *event) +{ + event->accept(); + emit onDetachTab(tabAt(event->pos()), _mouseCursor.pos()); +} + +void DragDropTabBar::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + _dragStartPos = event->pos(); + + _dragDropPos = QPoint(0, 0); + _dragInitiated = false; + + QTabBar::mousePressEvent(event); +} + +void DragDropTabBar::mouseMoveEvent(QMouseEvent *event) +{ + if (!_dragStartPos.isNull() && + ((event->pos() - _dragStartPos).manhattanLength() > QApplication::startDragDistance())) + _dragInitiated = true; + + if ((event->buttons() & Qt::LeftButton) && _dragInitiated) { + QMouseEvent * finishMouseMove = new QMouseEvent(QEvent::MouseMove, event->pos(), QCursor::pos(), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QTabBar::mouseMoveEvent(finishMouseMove); + + QDrag * drag = new QDrag(this); + QMimeData * mimeData = new QMimeData(); + mimeData->setData("action", "application/tab-detach"); + drag->setMimeData(mimeData); + + QWidget * original = parentWidget(); + if (qobject_cast(original)) { + DetachableTabWidget * tabWidget = qobject_cast(original); + original = tabWidget->widget(tabWidget->currentIndex()); + } + QPixmap pixmap = original->grab(); + QPixmap targetPixmap = QPixmap(pixmap.size()); + targetPixmap.fill(Qt::transparent); + + QPainter painter(&targetPixmap); + painter.setOpacity(0.85); + painter.drawPixmap(0, 0, pixmap); + painter.end(); + drag->setPixmap(targetPixmap); + + Qt::DropAction dropAction = drag->exec(Qt::MoveAction | Qt::CopyAction); + if (dropAction == Qt::IgnoreAction) { + event->accept(); + emit onDetachTab(tabAt(_dragStartPos), _mouseCursor.pos()); + } if (dropAction == Qt::MoveAction) { + if (! _dragDropPos.isNull()) { + event->accept(); + emit onMoveTab(tabAt(_dragStartPos), tabAt(_dragDropPos)); + } + } + } else + QTabBar::mouseMoveEvent(event); +} + +void DragDropTabBar::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData * mimeData = event->mimeData(); + QStringList formats = mimeData->formats(); + + if (formats.contains("action") && mimeData->data("action") == "application/tab-detach") + event->acceptProposedAction(); +} + +void DragDropTabBar::dropEvent(QDropEvent *event) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + _dragDropPos = event->position().toPoint(); +#else + _dragDropPos = event->pos(); +#endif + QTabBar::dropEvent(event); +} diff --git a/ui/qt/widgets/detachable_tabwidget.h b/ui/qt/widgets/detachable_tabwidget.h new file mode 100644 index 00000000..f5c4970d --- /dev/null +++ b/ui/qt/widgets/detachable_tabwidget.h @@ -0,0 +1,89 @@ +/* @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DETACHABLE_TABWIDGET_H +#define DETACHABLE_TABWIDGET_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +class DetachableTabWidget : public QTabWidget +{ + Q_OBJECT +public: + DetachableTabWidget(QWidget * parent = nullptr); + + QString tabBasename() const; + +protected: + + void setTabBasename(QString newName); + +protected slots: + + virtual void moveTab(int from, int to); + virtual void detachTab(int tabIdx, QPoint pos); + virtual void attachTab(QWidget * content, QString name); + +private: + QString _tabBasename; + +}; + +class ToolDialog : public QDialog +{ + Q_OBJECT +public: + explicit ToolDialog(QWidget * _contentWidget, QWidget * parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + +protected: + + virtual bool event(QEvent *event); + virtual void closeEvent(QCloseEvent *event); + +signals: + void onCloseSignal(QWidget * contentWidget, QString name); + +private: + QWidget * _contentWidget; +}; + +class DragDropTabBar : public QTabBar +{ + Q_OBJECT +public: + explicit DragDropTabBar(QWidget * parent); + +signals: + void onDetachTab(int tabIdx, QPoint pos); + void onMoveTab(int oldIdx, int newIdx); + +protected: + virtual void mouseDoubleClickEvent(QMouseEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dropEvent(QDropEvent *event); + +private: + QPoint _dragStartPos; + QPoint _dragDropPos; + QCursor _mouseCursor; + bool _dragInitiated; + +}; + +#endif // DETACHABLE_TABWIDGET_H diff --git a/ui/qt/widgets/display_filter_combo.cpp b/ui/qt/widgets/display_filter_combo.cpp new file mode 100644 index 00000000..c28afb91 --- /dev/null +++ b/ui/qt/widgets/display_filter_combo.cpp @@ -0,0 +1,189 @@ +/* display_filter_combo.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include "ui/recent_utils.h" +#include "ui/recent.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include "main_application.h" + +// If we ever add support for multiple windows this will need to be replaced. +static DisplayFilterCombo *cur_display_filter_combo = NULL; + +DisplayFilterCombo::DisplayFilterCombo(QWidget *parent) : + QComboBox(parent) +{ + setEditable(true); + setLineEdit(new DisplayFilterEdit(this, DisplayFilterToApply)); + // setLineEdit will create a new QCompleter that performs inline completion, + // be sure to disable that since our DisplayFilterEdit performs its own + // popup completion. As QLineEdit's completer is designed for full line + // completion, we cannot reuse it for word completion. + setCompleter(0); + // When the combobox menu is not entirely populated, pressing Enter would + // normally append entries to the end. However, before doing so it moves the + // cursor position to the end of the field which breaks the completer. + // Therefore disable this and rely on dfilter_combo_add_recent being called. + setInsertPolicy(QComboBox::NoInsert); + // Default is Preferred. + setSizePolicy(QSizePolicy::MinimumExpanding, sizePolicy().verticalPolicy()); + setAccessibleName(tr("Display filter selector")); + cur_display_filter_combo = this; + updateStyleSheet(); + setToolTip(tr("Select from previously used filters.")); + + QStandardItemModel *model = qobject_cast(this->model()); + model->setSortRole(Qt::UserRole); + + connect(mainApp, &MainApplication::preferencesChanged, this, &DisplayFilterCombo::updateMaxCount); + // Ugly cast required (?) + // https://stackoverflow.com/questions/16794695/connecting-overloaded-signals-and-slots-in-qt-5 + connect(this, static_cast(&DisplayFilterCombo::activated), this, &DisplayFilterCombo::onActivated); +} + +extern "C" void dfilter_recent_combo_write_all(FILE *rf) { + if (!cur_display_filter_combo) + return; + + cur_display_filter_combo->writeRecent(rf); +} + +void DisplayFilterCombo::writeRecent(FILE *rf) { + int i; + + for (i = 0; i < count(); i++) { + const QByteArray& filter = itemText(i).toUtf8(); + if (!filter.isEmpty()) { + fprintf(rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", filter.constData()); + } + } +} + +void DisplayFilterCombo::onActivated(int row) +{ + /* Update the row timestamp and resort list. */ + QStandardItemModel *m = qobject_cast(this->model()); + QModelIndex idx = m->index(row, 0); + m->setData(idx, QVariant(QDateTime::currentMSecsSinceEpoch()), Qt::UserRole); + m->sort(0, Qt::DescendingOrder); +} + +bool DisplayFilterCombo::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ToolTip: + { + // Only show a tooltip for the arrow. + QHelpEvent *he = (QHelpEvent *) event; + QStyleOptionComboBox opt; + initStyleOption(&opt); + QRect scr = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, this); + if (!scr.contains(he->pos())) { + return false; + } + break; + } + case QEvent::ApplicationPaletteChange: + updateStyleSheet(); + break; + default: + break; + } + return QComboBox::event(event); +} + +void DisplayFilterCombo::updateStyleSheet() +{ + const char *display_mode = ColorUtils::themeIsDark() ? "dark" : "light"; + + QString ss = QString( + "QComboBox {" +#ifdef Q_OS_MAC + " border: 1px solid gray;" +#else + " border: 1px solid palette(shadow);" +#endif + " border-radius: 3px;" + " padding: 0px 0px 0px 0px;" + " margin-left: 0px;" + " min-width: 20em;" + " }" + + "QComboBox::drop-down {" + " subcontrol-origin: padding;" + " subcontrol-position: top right;" + " width: 14px;" + " border-left-width: 0px;" + " }" + + "QComboBox::down-arrow {" + " image: url(:/stock_icons/14x14/x-filter-dropdown.%1.png);" + " }" + + "QComboBox::down-arrow:on { /* shift the arrow when popup is open */" + " top: 1px;" + " left: 1px;" + "}" + ).arg(display_mode); + setStyleSheet(ss); +} + +bool DisplayFilterCombo::checkDisplayFilter() +{ + DisplayFilterEdit *df_edit = qobject_cast(lineEdit()); + bool state = false; + + if (df_edit) state = df_edit->checkFilter(); + return state; +} + +void DisplayFilterCombo::applyDisplayFilter() +{ + DisplayFilterEdit *df_edit = qobject_cast(lineEdit()); + + if (df_edit) df_edit->applyDisplayFilter(); +} + +void DisplayFilterCombo::setDisplayFilter(QString filter) +{ + lineEdit()->setText(filter); + lineEdit()->setFocus(); +} + +void DisplayFilterCombo::updateMaxCount() +{ + setMaxCount(prefs.gui_recent_df_entries_max); +} + +extern "C" gboolean dfilter_combo_add_recent(const gchar *filter) { + if (!cur_display_filter_combo) + return FALSE; + + // Adding an item to a QComboBox also sets its lineEdit. In our case + // that means we might trigger a temporary status message so we block + // the lineEdit's signals. + // Another approach would be to update QComboBox->model directly. + bool block_state = cur_display_filter_combo->lineEdit()->blockSignals(true); + cur_display_filter_combo->addItem(filter, QVariant(QDateTime::currentMSecsSinceEpoch())); + cur_display_filter_combo->clearEditText(); + cur_display_filter_combo->lineEdit()->blockSignals(block_state); + return TRUE; +} diff --git a/ui/qt/widgets/display_filter_combo.h b/ui/qt/widgets/display_filter_combo.h new file mode 100644 index 00000000..81fa11ba --- /dev/null +++ b/ui/qt/widgets/display_filter_combo.h @@ -0,0 +1,40 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISPLAY_FILTER_COMBO_H +#define DISPLAY_FILTER_COMBO_H + +#include +#include + +class DisplayFilterCombo : public QComboBox +{ + Q_OBJECT +public: + explicit DisplayFilterCombo(QWidget *parent = 0); + bool addRecentCapture(const char *filter); + void writeRecent(FILE *rf); + void updateStyleSheet(); + +protected: + virtual bool event(QEvent *event); + +private: + +public slots: + bool checkDisplayFilter(); + void applyDisplayFilter(); + void setDisplayFilter(QString filter); + +private slots: + void updateMaxCount(); + void onActivated(int index); +}; + +#endif // DISPLAY_FILTER_COMBO_H diff --git a/ui/qt/widgets/display_filter_edit.cpp b/ui/qt/widgets/display_filter_edit.cpp new file mode 100644 index 00000000..839b14fd --- /dev/null +++ b/ui/qt/widgets/display_filter_edit.cpp @@ -0,0 +1,846 @@ +/* display_filter_edit.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#include + +#include + +#include "main_application.h" + +#include +#include "filter_dialog.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// To do: +// - Get rid of shortcuts and replace them with "n most recently applied filters"? +// - We need simplified (button- and dropdown-free) versions for use in dialogs and field-only checking. +// - Add a separator or otherwise distinguish between recent items and fields +// in the completion dropdown. + +#ifdef __APPLE__ +#define DEFAULT_MODIFIER UTF8_PLACE_OF_INTEREST_SIGN +#else +#define DEFAULT_MODIFIER "Ctrl-" +#endif + +// proto.c:fld_abbrev_chars +static const QString fld_abbrev_chars_ = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; + +DisplayFilterEdit::DisplayFilterEdit(QWidget *parent, DisplayFilterEditType type) : + SyntaxLineEdit(parent), + type_(type), + save_action_(NULL), + remove_action_(NULL), + actions_(Q_NULLPTR), + bookmark_button_(NULL), + clear_button_(NULL), + apply_button_(NULL), + leftAlignActions_(false), + last_applied_(QString()), + filter_word_preamble_(QString()), + autocomplete_accepts_field_(true) +{ + setAccessibleName(tr("Display filter entry")); + + completion_model_ = new QStringListModel(this); + setCompleter(new QCompleter(completion_model_, this)); + setCompletionTokenChars(fld_abbrev_chars_); + + QString buttonStyle = QString( + "QToolButton {" + " border: none;" + " background: transparent;" // Disables platform style on Windows. + " padding: 0 0 0 0;" + "}" + "QToolButton::menu-indicator {" + " image: none;" + "}" + ); + + leftAlignActions_ = recent.gui_geometry_leftalign_actions; + + if (type_ == DisplayFilterToApply) { + bookmark_button_ = new StockIconToolButton(this, "x-display-filter-bookmark"); + bookmark_button_->setMenu(new QMenu(bookmark_button_)); + bookmark_button_->setPopupMode(QToolButton::InstantPopup); + bookmark_button_->setToolTip(tr("Manage saved bookmarks.")); + bookmark_button_->setIconSize(QSize(14, 14)); + bookmark_button_->setStyleSheet(buttonStyle); + bookmark_button_->setVisible(false); + + clear_button_ = new StockIconToolButton(this, "x-filter-clear"); + clear_button_->setToolTip(tr("Clear display filter")); + clear_button_->setIconSize(QSize(14, 14)); + clear_button_->setStyleSheet(buttonStyle); + clear_button_->setVisible(false); + + apply_button_ = new StockIconToolButton(this, "x-filter-apply"); + apply_button_->setEnabled(false); + apply_button_->setToolTip(tr("Apply display filter")); + apply_button_->setIconSize(QSize(24, 14)); + apply_button_->setStyleSheet(buttonStyle); + apply_button_->setVisible(false); + + connect(clear_button_, &StockIconToolButton::clicked, this, &DisplayFilterEdit::clearFilter); + connect(apply_button_, &StockIconToolButton::clicked, this, &DisplayFilterEdit::applyDisplayFilter); + connect(this, &DisplayFilterEdit::returnPressed, this, &DisplayFilterEdit::applyDisplayFilter); + } + + connect(this, &DisplayFilterEdit::textChanged, this, + static_cast(&DisplayFilterEdit::checkFilter)); + + connect(mainApp, &MainApplication::appInitialized, this, &DisplayFilterEdit::updateBookmarkMenu); + connect(mainApp, &MainApplication::displayFilterListChanged, this, &DisplayFilterEdit::updateBookmarkMenu); + connect(mainApp, &MainApplication::preferencesChanged, this, [=](){ checkFilter(); }); + + connect(mainApp, &MainApplication::appInitialized, this, &DisplayFilterEdit::connectToMainWindow); +} + +void DisplayFilterEdit::connectToMainWindow() +{ + connect(this, &DisplayFilterEdit::filterPackets, qobject_cast(mainApp->mainWindow()), + &MainWindow::filterPackets); + connect(this, &DisplayFilterEdit::showPreferencesDialog, + qobject_cast(mainApp->mainWindow()), &MainWindow::showPreferencesDialog); + connect(qobject_cast(mainApp->mainWindow()), &MainWindow::displayFilterSuccess, + this, &DisplayFilterEdit::displayFilterSuccess); +} + +void DisplayFilterEdit::contextMenuEvent(QContextMenuEvent *event) { + QMenu *menu = this->createStandardContextMenu(); + menu->setAttribute(Qt::WA_DeleteOnClose); + + if (menu->actions().count() <= 0) { + menu->deleteLater(); + return; + } + + QAction *first = menu->actions().at(0); + + QAction *na = new QAction(tr("Display Filter Expression…"), this); + connect(na, &QAction::triggered, this, &DisplayFilterEdit::displayFilterExpression); + menu->insertAction(first, na); + menu->insertSeparator(first); + + if (type_ == DisplayFilterToApply) { + na = new QAction(tr("Left align buttons"), this); + na->setCheckable(true); + na->setChecked(leftAlignActions_); + connect(na, &QAction::triggered, this, &DisplayFilterEdit::triggerAlignementAction); + menu->addSeparator(); + menu->addAction(na); + } + + menu->popup(event->globalPos()); +} + +void DisplayFilterEdit::triggerAlignementAction() +{ + leftAlignActions_ = ! leftAlignActions_; + if (qobject_cast(sender())) + qobject_cast(sender())->setChecked(leftAlignActions_); + + recent.gui_geometry_leftalign_actions = leftAlignActions_; + write_recent(); + + alignActionButtons(); +} + +void DisplayFilterEdit::alignActionButtons() +{ + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + QSize bksz, cbsz, apsz; + bksz = apsz = cbsz = QSize(0, 0); + + if (type_ == DisplayFilterToApply) { + bookmark_button_->setMinimumHeight(contentsRect().height()); + bookmark_button_->setMaximumHeight(contentsRect().height()); + bksz = bookmark_button_->sizeHint(); + + apsz = apply_button_->sizeHint(); + apply_button_->setMinimumHeight(contentsRect().height()); + apply_button_->setMaximumHeight(contentsRect().height()); + + if (clear_button_->isVisible()) + { + cbsz = clear_button_->sizeHint(); + clear_button_->setMinimumHeight(contentsRect().height()); + clear_button_->setMaximumHeight(contentsRect().height()); + } + } + + int leftPadding = frameWidth + 1; + int leftMargin = bksz.width(); + int rightMargin = cbsz.width() + apsz.width() + frameWidth + 2; + if (leftAlignActions_) + { + leftMargin = rightMargin + bksz.width() - 2; + rightMargin = 0; + } + + SyntaxLineEdit::setStyleSheet(style_sheet_ + QString( + "SyntaxLineEdit {" + " padding-left: %1px;" + " margin-left: %2px;" + " margin-right: %3px;" + "}" + ) + .arg(leftPadding) + .arg(leftMargin) + .arg(rightMargin) + ); + + if (apply_button_) { + if (! leftAlignActions_) + { + apply_button_->move(contentsRect().right() - frameWidth - apsz.width(), + contentsRect().top()); + } else { + apply_button_->move(contentsRect().left() + bookmark_button_->width(), contentsRect().top()); + } + } + + if (clear_button_ && apply_button_) { + if (! leftAlignActions_) + { + clear_button_->move(contentsRect().right() - frameWidth - cbsz.width() - apsz.width(), + contentsRect().top()); + } else { + int width = bookmark_button_->width() + apply_button_->width(); + clear_button_->move(contentsRect().left() + width, contentsRect().top()); + } + } + + update(); +} + +void DisplayFilterEdit::setDefaultPlaceholderText() +{ + switch (type_) { + + case DisplayFilterToApply: + placeholder_text_ = QString(tr("Apply a display filter %1 <%2/>")).arg(UTF8_HORIZONTAL_ELLIPSIS) + .arg(DEFAULT_MODIFIER); + break; + + case DisplayFilterToEnter: + placeholder_text_ = QString(tr("Enter a display filter %1")).arg(UTF8_HORIZONTAL_ELLIPSIS); + break; + + case ReadFilterToApply: + placeholder_text_ = QString(tr("Apply a read filter %1")).arg(UTF8_HORIZONTAL_ELLIPSIS); + break; + } + setPlaceholderText(placeholder_text_); +} + +void DisplayFilterEdit::paintEvent(QPaintEvent *evt) { + SyntaxLineEdit::paintEvent(evt); + + if (bookmark_button_ && isEnabled()) { + + if (! bookmark_button_->isVisible()) + { + bookmark_button_->setVisible(true); + apply_button_->setVisible(true); + setDefaultPlaceholderText(); + alignActionButtons(); + return; + } + + // Draw the borders by hand. We could try to do this in the + // style sheet but it's a pain. +#ifdef Q_OS_MAC + QColor divider_color = Qt::gray; +#else + QColor divider_color = palette().shadow().color(); +#endif + QPainter painter(this); + painter.setPen(divider_color); + QRect cr = contentsRect(); + int left_xpos = 0; + int right_xpos = 0; + + if (leftAlignActions_) + { + left_xpos = 1 + bookmark_button_->width(); + if (clear_button_->isVisible()) + left_xpos += clear_button_->width(); + if (apply_button_->isVisible()) + left_xpos += apply_button_->width(); + right_xpos = cr.width() - 1; + } + else + { + left_xpos = bookmark_button_->width(); + right_xpos = cr.width() - 4; + if (clear_button_->isVisible()) + right_xpos -= clear_button_->width(); + if (apply_button_->isVisible()) + right_xpos -= apply_button_->width(); + } + + painter.drawLine(left_xpos, cr.top(), left_xpos, cr.bottom() + 1); + if (!text().isEmpty()) + painter.drawLine(right_xpos, cr.top(), right_xpos, cr.bottom() + 1); + } +} + +void DisplayFilterEdit::resizeEvent(QResizeEvent *) +{ + alignActionButtons(); +} + +void DisplayFilterEdit::focusOutEvent(QFocusEvent *event) +{ + if (syntaxState() == Valid) { + emit popFilterSyntaxStatus(); + setToolTip(QString()); + } + SyntaxLineEdit::focusOutEvent(event); +} + +bool DisplayFilterEdit::checkFilter() +{ + checkFilter(text()); + + return syntaxState() != Invalid; +} + +void DisplayFilterEdit::checkFilter(const QString& filter_text) +{ + if (text().length() == 0 && actions_ && actions_->checkedAction()) + actions_->checkedAction()->setChecked(false); + + if (clear_button_) { + + if (filter_text.length() > 0) + clear_button_->setVisible(true); + else if (last_applied_.length() > 0) + setPlaceholderText(tr("Current filter: %1").arg(last_applied_)); + else if (filter_text.length() <= 0 && last_applied_.length() <= 0) + clear_button_->setVisible(false); + + alignActionButtons(); + } + + if ((filter_text.length() <= 0) && mainApp->mainWindow()->isActiveWindow()) + mainApp->popStatus(MainApplication::FilterSyntax); + + emit popFilterSyntaxStatus(); + if (!checkDisplayFilter(filter_text)) + return; + + switch (syntaxState()) { + case Deprecated: + { + if (mainApp->mainWindow()->isActiveWindow()) + mainApp->pushStatus(MainApplication::FilterSyntax, syntaxErrorMessage()); + setToolTip(syntaxErrorMessage()); + break; + } + case Invalid: + { + QString invalidMsg = tr("Invalid filter: ").append(syntaxErrorMessage()); + if (mainApp->mainWindow()->isActiveWindow()) + mainApp->pushStatus(MainApplication::FilterSyntax, syntaxErrorMessage()); + setToolTip(invalidMsg); + break; + } + default: + setToolTip(QString()); + break; + } + + if (bookmark_button_) { + + bookmark_button_->setStockIcon("x-display-filter-bookmark"); + if (remove_action_ && save_action_) + { + remove_action_->setEnabled(false); + save_action_->setEnabled(false); + } + + if (filter_text.length() > 0) + { + bool enable_save_action = false; + bool match = false; + + FilterListModel model(FilterListModel::Display); + QModelIndex idx = model.findByExpression(filter_text); + if (idx.isValid()) { + match = true; + + bookmark_button_->setStockIcon("x-filter-matching-bookmark"); + if (remove_action_) { + remove_action_->setData(text()); + remove_action_->setEnabled(true); + } + } else { + bookmark_button_->setStockIcon("x-display-filter-bookmark"); + if (remove_action_) { + remove_action_->setEnabled(false); + } + } + + if (!match && (syntaxState() == Valid || syntaxState() == Deprecated) && !filter_text.isEmpty()) { + enable_save_action = true; + } + if (save_action_) { + save_action_->setEnabled(enable_save_action); + } + } + + apply_button_->setEnabled(syntaxState() != Invalid); + } +} + +void DisplayFilterEdit::updateBookmarkMenu() +{ + if (!bookmark_button_) + return; + + QMenu *bb_menu = bookmark_button_->menu(); + bb_menu->clear(); + + save_action_ = bb_menu->addAction(tr("Save this filter")); + connect(save_action_, &QAction::triggered, this, &DisplayFilterEdit::saveFilter); + remove_action_ = bb_menu->addAction(tr("Remove this filter")); + connect(remove_action_, &QAction::triggered, this, &DisplayFilterEdit::removeFilter); + QAction *manage_action = bb_menu->addAction(tr("Manage Display Filters")); + connect(manage_action, &QAction::triggered, this, &DisplayFilterEdit::showFilters); + QAction *expr_action = bb_menu->addAction(tr("Filter Button Preferences...")); + connect(expr_action, &QAction::triggered, this, &DisplayFilterEdit::showExpressionPrefs); + bb_menu->addSeparator(); + + FilterListModel model(FilterListModel::Display); + QModelIndex idx = model.findByExpression(text()); + + int one_em = bb_menu->fontMetrics().height(); + + if (! actions_) + actions_ = new QActionGroup(this); + + for (int row = 0; row < model.rowCount(); row++) + { + QModelIndex nameIdx = model.index(row, FilterListModel::ColumnName); + QString name = nameIdx.data().toString(); + QString expr = model.index(row, FilterListModel::ColumnExpression).data().toString(); + QString prep_text = QString("%1: %2").arg(name).arg(expr); + + prep_text = bb_menu->fontMetrics().elidedText(prep_text, Qt::ElideRight, one_em * 40); + QAction * prep_action = bb_menu->addAction(prep_text); + prep_action->setCheckable(true); + if (nameIdx == idx) + prep_action->setChecked(true); + + prep_action->setProperty("display_filter", expr); + actions_->addAction(prep_action); + + connect(prep_action, &QAction::triggered, this, &DisplayFilterEdit::applyOrPrepareFilter); + } + + checkFilter(); +} + +// GTK+ behavior: +// - Operates on words (proto.c:fld_abbrev_chars). +// - Popup appears when you enter or remove text. + +// Our behavior: +// - Operates on words (fld_abbrev_chars_). +// - Popup appears when you enter or remove text. +// - Popup appears when you move the cursor. +// - Popup does not appear when text is selected. +// - Recent and saved display filters in popup when editing first word. + +// ui/gtk/filter_autocomplete.c:build_autocompletion_list +void DisplayFilterEdit::buildCompletionList(const QString &field_word, const QString &preamble) +{ + // Push a hint about the current field. + if (syntaxState() == Valid) { + if (mainApp->mainWindow()->isActiveWindow()) + mainApp->popStatus(MainApplication::FilterSyntax); + + header_field_info *hfinfo = proto_registrar_get_byname(field_word.toUtf8().constData()); + if (hfinfo) { + QString cursor_field_msg = QString("%1: %2") + .arg(hfinfo->name) + .arg(ftype_pretty_name(hfinfo->type)); + if (mainApp->mainWindow()->isActiveWindow()) + mainApp->pushStatus(MainApplication::FilterSyntax, cursor_field_msg); + } + } + + if (field_word.length() < 1) { + completion_model_->setStringList(QStringList()); + return; + } + + // Grab matching display filters from our parent combo and from the + // saved display filters file. Skip ones that look like single fields + // and assume they will be added below. + QStringList complex_list; + QComboBox *df_combo = qobject_cast(parent()); + if (df_combo) { + for (int i = 0; i < df_combo->count() ; i++) { + QString recent_filter = df_combo->itemText(i); + + if (isComplexFilter(recent_filter)) { + complex_list << recent_filter; + } + } + } + FilterListModel model(FilterListModel::Display); + for (int row = 0; row < model.rowCount(); row++) + { + QString saved_filter = model.index(row, FilterListModel::ColumnExpression).data().toString(); + + if (isComplexFilter(saved_filter) && !complex_list.contains(saved_filter)) { + complex_list << saved_filter; + } + } + + completion_model_->setStringList(complex_list); + completer()->setCompletionPrefix(field_word); + + // Only add fields to completion if a field is valid at this position. + // Try to compile preamble and check error message. + if (preamble != filter_word_preamble_) { + df_error_t *df_err = NULL; + dfilter_t *test_df = NULL; + if (preamble.size() > 0) { + dfilter_compile_full(qUtf8Printable(preamble), &test_df, &df_err, + DF_EXPAND_MACROS, __func__); + } + if (test_df == NULL || (df_err != NULL && df_err->code == DF_ERROR_UNEXPECTED_END)) { + // Unexpected end of expression means "expected identifier (field) or literal". + autocomplete_accepts_field_ = true; + } + else { + autocomplete_accepts_field_ = false; + } + dfilter_free(test_df); + df_error_free(&df_err); + filter_word_preamble_ = preamble; + } + + QStringList field_list; + if (autocomplete_accepts_field_) { + void *proto_cookie; + + int field_dots = static_cast(field_word.count('.')); // Some protocol names (_ws.expert) contain periods. + for (int proto_id = proto_get_first_protocol(&proto_cookie); proto_id != -1; proto_id = proto_get_next_protocol(&proto_cookie)) { + protocol_t *protocol = find_protocol_by_id(proto_id); + if (!proto_is_protocol_enabled(protocol)) continue; + + const QString pfname = proto_get_protocol_filter_name(proto_id); + field_list << pfname; + + // Add fields only if we're past the protocol name and only for the + // current protocol. + if (field_dots > pfname.count('.')) { + void *field_cookie; + const QByteArray fw_ba = field_word.toUtf8(); // or toLatin1 or toStdString? + const char *fw_utf8 = fw_ba.constData(); + gsize fw_len = (gsize) strlen(fw_utf8); + for (header_field_info *hfinfo = proto_get_first_protocol_field(proto_id, &field_cookie); hfinfo; hfinfo = proto_get_next_protocol_field(proto_id, &field_cookie)) { + if (hfinfo->same_name_prev_id != -1) continue; // Ignore duplicate names. + + if (!g_ascii_strncasecmp(fw_utf8, hfinfo->abbrev, fw_len)) { + if ((gsize) strlen(hfinfo->abbrev) != fw_len) field_list << hfinfo->abbrev; + } + } + } + } + field_list.sort(); + } + + completion_model_->setStringList(complex_list + field_list); + completer()->setCompletionPrefix(field_word); +} + +void DisplayFilterEdit::setStyleSheet(const QString &style_sheet) +{ + style_sheet_ = style_sheet; + SyntaxLineEdit::setStyleSheet(style_sheet_); +} + +void DisplayFilterEdit::clearFilter() +{ + clear(); + + last_applied_ = QString(); + updateClearButton(); + + emit filterPackets(QString(), true); +} + +void DisplayFilterEdit::applyDisplayFilter() +{ + if (completer()->popup()->currentIndex().isValid()) { + // If the popup (not the QCompleter itself) has a currently + // valid QModelIndex, that means that the popup's + // QAbstractItemView::activated() signal has not yet + // been handled, which means that text() has the old value, + // not the one from the completer. + return; + } + + if (syntaxState() == Invalid) + return; + + if (text().length() > 0) + last_applied_ = text(); + + updateClearButton(); + + emit filterPackets(text(), true); +} + +void DisplayFilterEdit::updateClearButton() +{ + setDefaultPlaceholderText(); + clear_button_->setVisible(!text().isEmpty()); + alignActionButtons(); +} + +void DisplayFilterEdit::displayFilterSuccess(bool success) +{ + if (apply_button_) + apply_button_->setEnabled(!success); +} + +void DisplayFilterEdit::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + setDefaultPlaceholderText(); + break; + default: + break; + } + } + SyntaxLineEdit::changeEvent(event); +} + +void DisplayFilterEdit::saveFilter() +{ + FilterDialog *display_filter_dlg = new FilterDialog(window(), FilterDialog::DisplayFilter, text()); + display_filter_dlg->setWindowModality(Qt::ApplicationModal); + display_filter_dlg->setAttribute(Qt::WA_DeleteOnClose); + display_filter_dlg->show(); +} + +void DisplayFilterEdit::removeFilter() +{ + if (! actions_ || ! actions_->checkedAction()) + return; + + QAction *ra = actions_->checkedAction(); + if (ra->property("display_filter").toString().isEmpty()) + return; + + QString remove_filter = ra->property("display_filter").toString(); + + FilterListModel model(FilterListModel::Display); + QModelIndex idx = model.findByExpression(remove_filter); + + if (idx.isValid()) + { + model.removeFilter(idx); + model.saveList(); + } + + updateBookmarkMenu(); +} + +void DisplayFilterEdit::showFilters() +{ + FilterDialog *display_filter_dlg = new FilterDialog(window(), FilterDialog::DisplayFilter); + display_filter_dlg->setWindowModality(Qt::ApplicationModal); + display_filter_dlg->setAttribute(Qt::WA_DeleteOnClose); + display_filter_dlg->show(); +} + +void DisplayFilterEdit::showExpressionPrefs() +{ + emit showPreferencesDialog(PrefsModel::typeToString(PrefsModel::FilterButtons)); +} + +void DisplayFilterEdit::applyOrPrepareFilter() +{ + QAction *pa = qobject_cast(sender()); + if (! pa || pa->property("display_filter").toString().isEmpty()) + return; + + QString filterText = pa->property("display_filter").toString(); + last_applied_ = filterText; + setText(filterText); + + // Holding down the Shift key will only prepare filter. + if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier)) { + applyDisplayFilter(); + } +} + +void DisplayFilterEdit::dragEnterEvent(QDragEnterEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + if (qobject_cast(event->mimeData()) || + event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) { + if (event->source() != this) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void DisplayFilterEdit::dragMoveEvent(QDragMoveEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + if (qobject_cast(event->mimeData()) || + event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) { + if (event->source() != this) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void DisplayFilterEdit::dropEvent(QDropEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + QString filterText = ""; + if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) + { + QByteArray jsonData = event->mimeData()->data(WiresharkMimeData::DisplayFilterMimeType); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + if (! jsonDoc.isObject()) + return; + + QJsonObject data = jsonDoc.object(); + + if ((QApplication::keyboardModifiers() & Qt::AltModifier) && data.contains("field")) + filterText = data["field"].toString(); + else if (data.contains("filter")) + filterText = data["filter"].toString(); + } + else if (qobject_cast(event->mimeData())) + { + const ToolbarEntryMimeData * data = qobject_cast(event->mimeData()); + + filterText = data->filter(); + } + + /* Moving items around */ + if (filterText.length() > 0) { + if (event->source() != this) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + + bool prepare = QApplication::keyboardModifiers() & Qt::ShiftModifier; + + if (text().length() > 0 || QApplication::keyboardModifiers() & Qt::MetaModifier) + { + createFilterTextDropMenu(event, prepare, filterText); + return; + } + + last_applied_ = filterText; + setText(filterText); + + // Holding down the Shift key will only prepare filter. + if (! prepare) { + applyDisplayFilter(); + } + + } else { + event->acceptProposedAction(); + } + + } else { + event->ignore(); + } +} + +void DisplayFilterEdit::createFilterTextDropMenu(QDropEvent *event, bool prepare, QString filterText) +{ + if (filterText.isEmpty()) + return; + + FilterAction::Action filterAct = prepare ? FilterAction::ActionPrepare : FilterAction::ActionApply; + QMenu * applyMenu = FilterAction::createFilterMenu(filterAct, filterText, true, this); + applyMenu->setAttribute(Qt::WA_DeleteOnClose); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + applyMenu->popup(this->mapToGlobal(event->position().toPoint())); +#else + applyMenu->popup(this->mapToGlobal(event->pos())); +#endif +} + +void DisplayFilterEdit::displayFilterExpression() +{ + DisplayFilterExpressionDialog *dfe_dialog = new DisplayFilterExpressionDialog(this); + + connect(dfe_dialog, &DisplayFilterExpressionDialog::insertDisplayFilter, + this, &DisplayFilterEdit::insertFilter); + + dfe_dialog->show(); +} diff --git a/ui/qt/widgets/display_filter_edit.h b/ui/qt/widgets/display_filter_edit.h new file mode 100644 index 00000000..18422c44 --- /dev/null +++ b/ui/qt/widgets/display_filter_edit.h @@ -0,0 +1,100 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISPLAYFILTEREDIT_H +#define DISPLAYFILTEREDIT_H + +#include +#include + +#include + +class QEvent; +class StockIconToolButton; + +typedef enum { + DisplayFilterToApply, + DisplayFilterToEnter, + ReadFilterToApply +} DisplayFilterEditType; + +class DisplayFilterEdit : public SyntaxLineEdit +{ + Q_OBJECT +public: + explicit DisplayFilterEdit(QWidget *parent = 0, DisplayFilterEditType type = DisplayFilterToEnter); + +protected: + void paintEvent(QPaintEvent *evt); + void resizeEvent(QResizeEvent *); + void keyPressEvent(QKeyEvent *event) { completionKeyPressEvent(event); } + void focusInEvent(QFocusEvent *event) { completionFocusInEvent(event); } + void focusOutEvent(QFocusEvent *event); + + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void contextMenuEvent(QContextMenuEvent *menu); + +public slots: + bool checkFilter(); + void updateBookmarkMenu(); + void applyDisplayFilter(); + void displayFilterSuccess(bool success); + void setStyleSheet(const QString &style_sheet); + +private slots: + void checkFilter(const QString &filter_text); + void clearFilter(); + void changeEvent(QEvent* event); + + void displayFilterExpression(); + + void saveFilter(); + void removeFilter(); + void showFilters(); + void showExpressionPrefs(); + void applyOrPrepareFilter(); + + void triggerAlignementAction(); + + void connectToMainWindow(); + +private: + DisplayFilterEditType type_; + QString placeholder_text_; + QAction *save_action_; + QAction *remove_action_; + QActionGroup * actions_; + StockIconToolButton *bookmark_button_; + StockIconToolButton *clear_button_; + StockIconToolButton *apply_button_; + bool leftAlignActions_; + QString last_applied_; + QString filter_word_preamble_; + bool autocomplete_accepts_field_; + QString style_sheet_; + + void setDefaultPlaceholderText(); + void buildCompletionList(const QString &field_word, const QString &preamble); + + void createFilterTextDropMenu(QDropEvent *event, bool prepare, QString filterText = QString()); + + void alignActionButtons(); + void updateClearButton(); + +signals: + void pushFilterSyntaxStatus(const QString&); + void popFilterSyntaxStatus(); + void filterPackets(QString new_filter, bool force); + void showPreferencesDialog(QString pane_name); + +}; + +#endif // DISPLAYFILTEREDIT_H diff --git a/ui/qt/widgets/dissector_syntax_line_edit.cpp b/ui/qt/widgets/dissector_syntax_line_edit.cpp new file mode 100644 index 00000000..ae8a2247 --- /dev/null +++ b/ui/qt/widgets/dissector_syntax_line_edit.cpp @@ -0,0 +1,106 @@ +/* dissector_syntax_line_edit.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +// Ordinary dissector names allow the same characters as display filter +// fields (heuristic dissectors don't allow upper-case.) +static const QString fld_abbrev_chars_ = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; + +DissectorSyntaxLineEdit::DissectorSyntaxLineEdit(QWidget *parent) : + SyntaxLineEdit(parent) +{ + setAccessibleName(tr("Dissector entry")); + + completion_model_ = new QStringListModel(this); + setCompleter(new QCompleter(completion_model_, this)); + setCompletionTokenChars(fld_abbrev_chars_); + + GList *dissector_names = get_dissector_names(); + QStringList dissector_list; + for (GList* l = dissector_names; l != NULL; l = l->next) { + dissector_list << (const char*) l->data; + } + g_list_free(dissector_names); + dissector_list.sort(); + completion_model_->setStringList(dissector_list); + setDefaultPlaceholderText(); + + connect(this, &DissectorSyntaxLineEdit::textChanged, this, + static_cast(&DissectorSyntaxLineEdit::checkDissectorName)); +} + +void DissectorSyntaxLineEdit::setDefaultPlaceholderText() +{ + placeholder_text_ = QString(tr("Enter a dissector %1")).arg(UTF8_HORIZONTAL_ELLIPSIS); + + setPlaceholderText(placeholder_text_); +} + +void DissectorSyntaxLineEdit::checkDissectorName(const QString &dissector) +{ + if (dissector.isEmpty()) { + setSyntaxState(SyntaxLineEdit::Empty); + } else if (find_dissector(dissector.trimmed().toUtf8().constData())) { + setSyntaxState(SyntaxLineEdit::Valid); + } else { + setSyntaxState(SyntaxLineEdit::Invalid); + } +} + +void DissectorSyntaxLineEdit::buildCompletionList(const QString &field_word, const QString &preamble _U_) +{ +#if 0 + // It would be nice to push a hint about the current dissector with + // the description, as we do with the status line in the main window + // with filters. + if (syntaxState() == Valid) { + dissector_handle_t handle = find_dissector(field_word.toUtf8().constData()); + if (handle) { + QString cursor_field_msg = QString("%1: %2") + .arg(dissector_handle_get_dissector_name(handle)) + .arg(dissector_handle_get_description(handle)); + } + } +#endif + + completer()->setCompletionPrefix(field_word); +} + +void DissectorSyntaxLineEdit::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + setDefaultPlaceholderText(); + break; + default: + break; + } + } + SyntaxLineEdit::changeEvent(event); +} diff --git a/ui/qt/widgets/dissector_syntax_line_edit.h b/ui/qt/widgets/dissector_syntax_line_edit.h new file mode 100644 index 00000000..adcee8c7 --- /dev/null +++ b/ui/qt/widgets/dissector_syntax_line_edit.h @@ -0,0 +1,39 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISSECTOR_SYNTAX_LINEEDIT_H +#define DISSECTOR_SYNTAX_LINEEDIT_H + +#include + +class QEvent; +class StockIconToolButton; + +class DissectorSyntaxLineEdit : public SyntaxLineEdit +{ + Q_OBJECT +public: + explicit DissectorSyntaxLineEdit(QWidget *parent = 0); + +protected: + void keyPressEvent(QKeyEvent *event) { completionKeyPressEvent(event); } + void focusInEvent(QFocusEvent *event) { completionFocusInEvent(event); } + +private slots: + void checkDissectorName(const QString &dissector); + void changeEvent(QEvent* event); + +private: + QString placeholder_text_; + + void setDefaultPlaceholderText(); + void buildCompletionList(const QString &field_word, const QString &preamble); +}; + +#endif // DISSECTOR_SYNTAX_LINEEDIT_H diff --git a/ui/qt/widgets/dissector_tables_view.cpp b/ui/qt/widgets/dissector_tables_view.cpp new file mode 100644 index 00000000..10d6fe1d --- /dev/null +++ b/ui/qt/widgets/dissector_tables_view.cpp @@ -0,0 +1,26 @@ +/* dissector_tables_view.cpp + * Tree view of Dissector Table data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "dissector_tables_view.h" +#include + +DissectorTablesTreeView::DissectorTablesTreeView(QWidget *parent) : QTreeView(parent) +{ +} + +void DissectorTablesTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.isValid()) + { + ((DissectorTablesProxyModel*)model())->adjustHeader(current); + } + + QTreeView::currentChanged(current, previous); +} diff --git a/ui/qt/widgets/dissector_tables_view.h b/ui/qt/widgets/dissector_tables_view.h new file mode 100644 index 00000000..a1df54e2 --- /dev/null +++ b/ui/qt/widgets/dissector_tables_view.h @@ -0,0 +1,27 @@ +/** @file + * + * Tree view of Dissector Table data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISSECTOR_TABLES_VIEW_H +#define DISSECTOR_TABLES_VIEW_H + +#include +#include + +class DissectorTablesTreeView : public QTreeView +{ + Q_OBJECT +public: + DissectorTablesTreeView(QWidget *parent = 0); + +protected slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); +}; +#endif // DISSECTOR_TABLES_VIEW_H diff --git a/ui/qt/widgets/drag_drop_toolbar.cpp b/ui/qt/widgets/drag_drop_toolbar.cpp new file mode 100644 index 00000000..88726aab --- /dev/null +++ b/ui/qt/widgets/drag_drop_toolbar.cpp @@ -0,0 +1,295 @@ +/* drag_drop_toolbar.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define drag_drop_toolbar_action_ "drag_drop_toolbar_action_" + +DragDropToolBar::DragDropToolBar(const QString &title, QWidget *parent) : + QToolBar(title, parent) +{ + setupToolbar(); +} + +DragDropToolBar::DragDropToolBar(QWidget *parent) : + QToolBar(parent) +{ + setupToolbar(); +} + +void DragDropToolBar::setupToolbar() +{ + childCounter = 0; + setAcceptDrops(true); + + // Each QToolBar has a QToolBarExtension button. Its icon looks + // terrible. We might want to create our own icon, but the double + // angle quote is a similar, nice-looking shape. + QToolButton *ext_button = findChild(); + if (ext_button) { + ext_button->setIcon(QIcon()); + ext_button->setText(UTF8_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK); + } +} + +DragDropToolBar::~DragDropToolBar() +{ +} + +void DragDropToolBar::childEvent(QChildEvent * event) +{ + /* New action has been added */ + if (event->type() == QEvent::ChildAdded) + { + if (event->child()->isWidgetType()) + { + /* Reset if it has moved underneath lower limit */ + if (childCounter < 0) + childCounter = 0; + + ((QWidget *)event->child())->installEventFilter(this); + event->child()->setProperty(drag_drop_toolbar_action_, QVariant::fromValue(childCounter)); + childCounter++; + } + } + else if (event->type() == QEvent::ChildRemoved) + { + childCounter--; + } + else if (event->type() == QEvent::ChildPolished) + { + /* Polish is called every time a child is added or removed. This is implemented by adding + * all childs again as hidden elements, and afterwards removing the existing ones. Therefore + * we have to reset child counter here, if a widget is being polished. If this is not being + * done, crashes will occur after an item has been removed and other items are moved afterwards */ + if (event->child()->isWidgetType()) + childCounter = 0; + } +} + +void DragDropToolBar::clear() +{ + QToolBar::clear(); + childCounter = 0; +} + +WiresharkMimeData * DragDropToolBar::createMimeData(QString name, int position) +{ + return new ToolbarEntryMimeData(name, position); +} + +bool DragDropToolBar::eventFilter(QObject * obj, QEvent * event) +{ + if (! obj->isWidgetType()) + return QToolBar::eventFilter(obj, event); + + QWidget * elem = qobject_cast(obj); + + if (! elem || (event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseMove) ) + return QToolBar::eventFilter(obj, event); + + QMouseEvent * ev = (QMouseEvent *)event; + + if (event->type() == QEvent::MouseButtonPress) + { + if (ev->buttons() & Qt::LeftButton) + dragStartPosition = ev->pos(); + } + else if (event->type() == QEvent::MouseMove) + { + if ((ev->buttons() & Qt::LeftButton) && (ev->pos() - dragStartPosition).manhattanLength() + > QApplication::startDragDistance()) + { + if (! qobject_cast(elem) || ! elem->property(drag_drop_toolbar_action_).isValid()) + return QToolBar::eventFilter(obj, event); + + WiresharkMimeData * temd = createMimeData(((QToolButton *)elem)->text(), elem->property(drag_drop_toolbar_action_).toInt()); + DragLabel * lbl = new DragLabel(temd->labelText(), this); + QDrag * drag = new QDrag(this); + drag->setMimeData(temd); + + qreal dpr = window()->windowHandle()->devicePixelRatio(); + QPixmap pixmap(lbl->size() * dpr); + pixmap.setDevicePixelRatio(dpr); + + lbl->render(&pixmap); + drag->setPixmap(pixmap); + + drag->exec(Qt::CopyAction | Qt::MoveAction); + + return true; + } + } + + return QToolBar::eventFilter(obj, event); +} + +void DragDropToolBar::dragEnterEvent(QDragEnterEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + if (qobject_cast(event->mimeData())) + { + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) { + if (event->source() != this) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void DragDropToolBar::dragMoveEvent(QDragMoveEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + if (qobject_cast(event->mimeData())) + { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + QAction * actionAtPos = actionAt(event->position().toPoint()); +#else + QAction * actionAtPos = actionAt(event->pos()); +#endif + if (actionAtPos) + { + QWidget * widget = widgetForAction(actionAtPos); + if (widget) + { + bool success = false; + widget->property(drag_drop_toolbar_action_).toInt(&success); + if (! success) + { + event->ignore(); + return; + } + } + } + + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) { + if (event->source() != this) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void DragDropToolBar::dropEvent(QDropEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + /* Moving items around */ + if (qobject_cast(event->mimeData())) + { + const ToolbarEntryMimeData * data = qobject_cast(event->mimeData()); + + int oldPos = data->position(); + int newPos = -1; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + QAction * action = actionAt(event->position().toPoint()); +#else + QAction * action = actionAt(event->pos()); +#endif + if (action && actions().at(oldPos)) + { + widgetForAction(action)->setStyleSheet("QWidget { border: none; };"); + newPos = widgetForAction(action)->property(drag_drop_toolbar_action_).toInt(); + moveToolbarItems(oldPos, newPos); + QAction * moveAction = actions().at(oldPos); + + emit actionMoved(moveAction, oldPos, newPos); + } + + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + + } else if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) { + QByteArray jsonData = event->mimeData()->data(WiresharkMimeData::DisplayFilterMimeType); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + if (jsonDoc.isObject()) + { + QJsonObject data = jsonDoc.object(); + + if (event->source() != this && data.contains("description") && data.contains("filter")) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + + emit newFilterDropped(data["description"].toString(), data["filter"].toString()); + + } else { + event->acceptProposedAction(); + } + } + } else { + event->ignore(); + } +} + +void DragDropToolBar::moveToolbarItems(int fromPos, int newPos) +{ + if (fromPos == newPos) + return; + + setUpdatesEnabled(false); + + QList storedActions = actions(); + + clear(); + childCounter = 0; + + storedActions.move(fromPos, newPos); + foreach (QAction * action, storedActions) + addAction(action); + + setUpdatesEnabled(true); +} diff --git a/ui/qt/widgets/drag_drop_toolbar.h b/ui/qt/widgets/drag_drop_toolbar.h new file mode 100644 index 00000000..c2937d0f --- /dev/null +++ b/ui/qt/widgets/drag_drop_toolbar.h @@ -0,0 +1,54 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DRAG_DROP_TOOLBAR_H +#define DRAG_DROP_TOOLBAR_H + +#include +#include + +class WiresharkMimeData; + +class DragDropToolBar : public QToolBar +{ + Q_OBJECT +public: + explicit DragDropToolBar(const QString &title, QWidget *parent = Q_NULLPTR); + explicit DragDropToolBar(QWidget *parent = Q_NULLPTR); + ~DragDropToolBar(); + + virtual void clear(); + +signals: + void actionMoved(QAction * action, int oldPos, int newPos); + + void newFilterDropped(QString description, QString filter); + +protected: + + virtual WiresharkMimeData * createMimeData(QString name, int position); + + virtual void childEvent(QChildEvent * event); + + virtual bool eventFilter(QObject * obj, QEvent * ev); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dropEvent(QDropEvent *event); + +private: + + QPoint dragStartPosition; + int childCounter; + + void setupToolbar(); + void moveToolbarItems(int fromPos, int toPos); + +}; + +#endif // DRAG_DROP_TOOLBAR_H diff --git a/ui/qt/widgets/drag_label.cpp b/ui/qt/widgets/drag_label.cpp new file mode 100644 index 00000000..952e9262 --- /dev/null +++ b/ui/qt/widgets/drag_label.cpp @@ -0,0 +1,28 @@ +/* drag_label.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +DragLabel::DragLabel(QString txt, QWidget * parent) +: QLabel(txt, parent) +{ + setAutoFillBackground(true); + setFrameShape(QFrame::Panel); + setFrameShadow(QFrame::Raised); + setAttribute(Qt::WA_DeleteOnClose); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + + adjustSize(); +} + +DragLabel::~DragLabel() { + // TODO Auto-generated destructor stub +} diff --git a/ui/qt/widgets/drag_label.h b/ui/qt/widgets/drag_label.h new file mode 100644 index 00000000..ef40d3dd --- /dev/null +++ b/ui/qt/widgets/drag_label.h @@ -0,0 +1,27 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_WIDGETS_DRAG_LABEL_H_ +#define UI_QT_WIDGETS_DRAG_LABEL_H_ + +#include +#include +#include +#include + +class DragLabel: public QLabel +{ + Q_OBJECT + +public: + explicit DragLabel(QString text, QWidget * parent = 0); + virtual ~DragLabel(); +}; + +#endif /* UI_QT_WIDGETS_DRAG_LABEL_H_ */ diff --git a/ui/qt/widgets/editor_file_dialog.cpp b/ui/qt/widgets/editor_file_dialog.cpp new file mode 100644 index 00000000..e9b22a1f --- /dev/null +++ b/ui/qt/widgets/editor_file_dialog.cpp @@ -0,0 +1,109 @@ +/* editor_file_dialog.h + * + * File dialog that can be used as an "inline editor" in a table + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include +#include + +#include "wsutil/utf8_entities.h" + +EditorFileDialog::EditorFileDialog(const QModelIndex& index, enum FileMode mode, QWidget* parent, const QString& caption, const QString& directory, const QString& filter) + : QLineEdit(parent) + , file_dialog_button_(new QPushButton(this)) + , index_(index) + , mode_(mode) + , caption_(caption) + , directory_(directory) + , filter_(filter) + , options_(QFileDialog::Options()) +{ + if (mode_ == Directory) + options_ = QFileDialog::ShowDirsOnly; + + if (!directory.isEmpty()) + setText(directory); + + file_dialog_button_->setText(UTF8_HORIZONTAL_ELLIPSIS); + connect(file_dialog_button_, &QPushButton::clicked, this, &EditorFileDialog::applyFilename); +} + +void EditorFileDialog::setOption(QFileDialog::Option option, bool on) +{ + if (on) + { + options_ |= option; + } + else + { + options_ &= (~option); + } +} + +// QAbstractItemView installs QAbstractItemDelegate's event filter after +// we've been created. We need to install our own event filter after that +// happens so that we can steal tab keypresses. +void EditorFileDialog::focusInEvent(QFocusEvent *event) +{ + installEventFilter(this); + QLineEdit::focusInEvent(event); +} + +void EditorFileDialog::focusOutEvent(QFocusEvent *event) +{ + removeEventFilter(this); + QLineEdit::focusOutEvent(event); +} + +bool EditorFileDialog::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent* key = static_cast(event); + if ((key->key() == Qt::Key_Tab) && !file_dialog_button_->hasFocus()) { + file_dialog_button_->setFocus(); + return true; + } + } + return QLineEdit::eventFilter(obj, event); +} + +void EditorFileDialog::resizeEvent(QResizeEvent *) +{ + // Move the button to the end of the line edit and set its height. + QSize sz = file_dialog_button_->sizeHint(); + int frame_width = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + file_dialog_button_->move(rect().right() - frame_width - sz.width(), + contentsRect().top()); + file_dialog_button_->setMinimumHeight(contentsRect().height()); + file_dialog_button_->setMaximumHeight(contentsRect().height()); + +} + +void EditorFileDialog::applyFilename() +{ + QString file; + + if (mode_ == Directory) + { + file = WiresharkFileDialog::getExistingDirectory(this, caption_, directory_, options_); + } + else + { + file = WiresharkFileDialog::getOpenFileName(this, caption_, directory_, filter_, NULL, options_); + } + + if (!file.isEmpty()) + { + setText(file); + emit acceptEdit(index_); + } +} diff --git a/ui/qt/widgets/editor_file_dialog.h b/ui/qt/widgets/editor_file_dialog.h new file mode 100644 index 00000000..acc0d7e0 --- /dev/null +++ b/ui/qt/widgets/editor_file_dialog.h @@ -0,0 +1,50 @@ +/** @file + * + * File dialog that can be used as an "inline editor" in a table + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EDITOR_FILE_DIALOG_H_ +#define EDITOR_FILE_DIALOG_H_ + +#include +#include +#include +#include + +class EditorFileDialog : public QLineEdit +{ + Q_OBJECT +public: + enum FileMode { ExistingFile, Directory }; + + explicit EditorFileDialog(const QModelIndex& index, enum FileMode mode, QWidget* parent = 0, const QString & caption = QString(), const QString & directory = QString(), const QString & filter = QString()); + + void setOption(QFileDialog::Option option, bool on = true); + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual bool eventFilter(QObject *obj, QEvent *event); + +signals: + void acceptEdit(const QModelIndex& index); + +private slots: + void applyFilename(); + +protected: + void resizeEvent(QResizeEvent *); + QPushButton* file_dialog_button_; + const QModelIndex index_; //saved index of table cell + enum FileMode mode_; + QString caption_; + QString directory_; + QString filter_; + QFileDialog::Options options_; +}; + +#endif /* EDITOR_FILE_DIALOG_H_ */ diff --git a/ui/qt/widgets/elided_label.cpp b/ui/qt/widgets/elided_label.cpp new file mode 100644 index 00000000..99004623 --- /dev/null +++ b/ui/qt/widgets/elided_label.cpp @@ -0,0 +1,79 @@ +/* elided_label.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include + +ElidedLabel::ElidedLabel(QWidget *parent) : + QLabel(parent), + small_text_(false) +{ + QFontMetrics fm(font()); + setMinimumWidth(fm.height() * 5); // em-widths +} + +void ElidedLabel::setUrl(const QString &url) +{ + url_ = url; + updateText(); +} + +bool ElidedLabel::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + updateText(); + break; + default: + break; + + } + return QLabel::event(event); +} + +void ElidedLabel::resizeEvent(QResizeEvent *) +{ + updateText(); +} + +void ElidedLabel::updateText() +{ + // XXX We should probably move text drawing to PaintEvent to match + // LabelStack. + int fudged_width = small_text_ ? width() * 1.2 : width(); + QString elided_text = fontMetrics().elidedText(full_text_, Qt::ElideMiddle, fudged_width); + QString label_text = small_text_ ? "" : ""; + + if (url_.length() > 0) { + label_text.prepend(ColorUtils::themeLinkStyle()); + label_text.append(QString("%2").arg(url_, elided_text)); + } else { + label_text += elided_text; + } + label_text += small_text_ ? " " : " "; + QLabel::setText(label_text); +} + +void ElidedLabel::clear() +{ + full_text_.clear(); + url_.clear(); + setToolTip(""); + updateText(); +} + +void ElidedLabel::setText(const QString &text) +{ + full_text_ = text.toHtmlEscaped(); + updateText(); +} diff --git a/ui/qt/widgets/elided_label.h b/ui/qt/widgets/elided_label.h new file mode 100644 index 00000000..982c82b6 --- /dev/null +++ b/ui/qt/widgets/elided_label.h @@ -0,0 +1,56 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ELIDED_LABEL_H +#define ELIDED_LABEL_H + +#include + +class ElidedLabel : public QLabel +{ + Q_OBJECT +public: + explicit ElidedLabel(QWidget *parent = 0); + /** + * @brief setUrl Set the label's URL. + * @param url The URL to set. + */ + void setUrl(const QString &url); + /** + * @brief setSmallText Specifies a small or normal text size. + * @param small_text Show the text in a smaller font size if true, or a normal size otherwise. + */ + void setSmallText(bool small_text = true) { small_text_ = small_text; } + +protected: + virtual bool event(QEvent *event); + virtual void resizeEvent(QResizeEvent *); + +private: + bool small_text_; + QString full_text_; + QString url_; + + void updateText(); + +signals: + +public slots: + /** + * @brief clear Clear the label. + */ + void clear(); + /** + * @brief setText Set the label's plain text. + * @param text The text to set. HTML will be escaped. + */ + void setText(const QString &text); +}; + +#endif // ELIDED_LABEL_H diff --git a/ui/qt/widgets/expert_info_view.cpp b/ui/qt/widgets/expert_info_view.cpp new file mode 100644 index 00000000..a5e3cb3f --- /dev/null +++ b/ui/qt/widgets/expert_info_view.cpp @@ -0,0 +1,43 @@ +/* expert_info_view.cpp + * Tree view of Expert Info data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "expert_info_view.h" +#include +#include + +#include + +ExpertInfoTreeView::ExpertInfoTreeView(QWidget *parent) : QTreeView(parent) +{ +} + +void ExpertInfoTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.isValid()) + { + if (current.parent().isValid()) { + ((ExpertInfoProxyModel*)model())->setSeverityMode(ExpertInfoProxyModel::Packet); + } else { + ((ExpertInfoProxyModel*)model())->setSeverityMode(ExpertInfoProxyModel::Group); + } + + QModelIndex model_index = ((ExpertInfoProxyModel*)model())->mapToSource(current); + + if (model_index.parent().isValid()) { + ExpertPacketItem* currentItem = static_cast(model_index.internalPointer()); + if (currentItem != NULL) + { + emit goToPacket(currentItem->packetNum(), currentItem->hfId()); + } + } + } + + QTreeView::currentChanged(current, previous); +} diff --git a/ui/qt/widgets/expert_info_view.h b/ui/qt/widgets/expert_info_view.h new file mode 100644 index 00000000..d9558952 --- /dev/null +++ b/ui/qt/widgets/expert_info_view.h @@ -0,0 +1,30 @@ +/** @file + * + * Tree view of Expert Info data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPERT_INFO_VIEW_H +#define EXPERT_INFO_VIEW_H + +#include +#include + +class ExpertInfoTreeView : public QTreeView +{ + Q_OBJECT +public: + ExpertInfoTreeView(QWidget *parent = 0); + +signals: + void goToPacket(int packet_num, int hf_id); + +protected slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); +}; +#endif // EXPERT_INFO_VIEW_H diff --git a/ui/qt/widgets/export_objects_view.cpp b/ui/qt/widgets/export_objects_view.cpp new file mode 100644 index 00000000..89ad7279 --- /dev/null +++ b/ui/qt/widgets/export_objects_view.cpp @@ -0,0 +1,23 @@ +/* export_objects_view.cpp + * Tree view of Export object data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "export_objects_view.h" +#include + +ExportObjectsTreeView::ExportObjectsTreeView(QWidget *parent) : QTreeView(parent) +{ +} + +void ExportObjectsTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + emit currentIndexChanged(current); + + QTreeView::currentChanged(current, previous); +} diff --git a/ui/qt/widgets/export_objects_view.h b/ui/qt/widgets/export_objects_view.h new file mode 100644 index 00000000..9f7a59c5 --- /dev/null +++ b/ui/qt/widgets/export_objects_view.h @@ -0,0 +1,30 @@ +/** @file + * + * Tree view of Export object data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXPORT_OBJECTS_VIEW_H +#define EXPORT_OBJECTS_VIEW_H + +#include +#include + +class ExportObjectsTreeView : public QTreeView +{ + Q_OBJECT +public: + ExportObjectsTreeView(QWidget *parent = 0); + +signals: + void currentIndexChanged(const QModelIndex ¤t); + +protected slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); +}; +#endif // EXPORT_OBJECTS_VIEW_H diff --git a/ui/qt/widgets/field_filter_edit.cpp b/ui/qt/widgets/field_filter_edit.cpp new file mode 100644 index 00000000..7aebf051 --- /dev/null +++ b/ui/qt/widgets/field_filter_edit.cpp @@ -0,0 +1,226 @@ +/* field_filter_edit.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#include +#include + +#include +#include "filter_dialog.h" +#include +#include + +#include +#include +#include +#include + +#include + +// To do: +// - Get rid of shortcuts and replace them with "n most recently applied filters"? +// - We need simplified (button- and dropdown-free) versions for use in dialogs and field-only checking. +// - Add a separator or otherwise distinguish between recent items and fields +// in the completion dropdown. + + +#ifdef __APPLE__ +#define DEFAULT_MODIFIER UTF8_PLACE_OF_INTEREST_SIGN +#else +#define DEFAULT_MODIFIER "Ctrl-" +#endif + +// proto.c:fld_abbrev_chars +static const QString fld_abbrev_chars_ = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; + +FieldFilterEdit::FieldFilterEdit(QWidget *parent) : + SyntaxLineEdit(parent) +{ + setAccessibleName(tr("Display filter entry")); + + completion_model_ = new QStringListModel(this); + setCompleter(new QCompleter(completion_model_, this)); + setCompletionTokenChars(fld_abbrev_chars_); + + setDefaultPlaceholderText(); + + // DFCombo + // Bookmark + // DisplayFilterEdit + // Clear button + // Apply (right arrow) + // Combo drop-down + + connect(this, &FieldFilterEdit::textChanged, this, + static_cast(&FieldFilterEdit::checkFilter)); +// connect(this, &FieldFilterEdit::returnPressed, this, &FieldFilterEdit::applyDisplayFilter); +} + +void FieldFilterEdit::setDefaultPlaceholderText() +{ + placeholder_text_ = QString(tr("Enter a field %1")).arg(UTF8_HORIZONTAL_ELLIPSIS); + + setPlaceholderText(placeholder_text_); +} + +void FieldFilterEdit::focusOutEvent(QFocusEvent *event) +{ + if (syntaxState() == Valid) + emit popFilterSyntaxStatus(); + SyntaxLineEdit::focusOutEvent(event); +} + +bool FieldFilterEdit::checkFilter() +{ + checkFilter(text()); + + return syntaxState() != Invalid; +} + +void FieldFilterEdit::checkFilter(const QString& filter_text) +{ + popFilterSyntaxStatus(); + if (!checkDisplayFilter(filter_text)) + return; + + switch (syntaxState()) { + case Deprecated: + { + emit pushFilterSyntaxWarning(syntaxErrorMessage()); + break; + } + case Invalid: + { + QString invalidMsg(tr("Invalid filter: ")); + invalidMsg.append(syntaxErrorMessage()); + emit pushFilterSyntaxStatus(invalidMsg); + break; + } + default: + break; + } +} + +// GTK+ behavior: +// - Operates on words (proto.c:fld_abbrev_chars). +// - Popup appears when you enter or remove text. + +// Our behavior: +// - Operates on words (fld_abbrev_chars_). +// - Popup appears when you enter or remove text. +// - Popup appears when you move the cursor. +// - Popup does not appear when text is selected. +// - Recent and saved display filters in popup when editing first word. + +// ui/gtk/filter_autocomplete.c:build_autocompletion_list +void FieldFilterEdit::buildCompletionList(const QString &field_word, const QString &preamble _U_) +{ + // Push a hint about the current field. + if (syntaxState() == Valid) { + emit popFilterSyntaxStatus(); + + header_field_info *hfinfo = proto_registrar_get_byname(field_word.toUtf8().constData()); + if (hfinfo) { + QString cursor_field_msg = QString("%1: %2") + .arg(hfinfo->name) + .arg(ftype_pretty_name(hfinfo->type)); + emit pushFilterSyntaxStatus(cursor_field_msg); + } + } + + if (field_word.length() < 1) { + completion_model_->setStringList(QStringList()); + return; + } + + void *proto_cookie; + QStringList field_list; + int field_dots = static_cast(field_word.count('.')); // Some protocol names (_ws.expert) contain periods. + for (int proto_id = proto_get_first_protocol(&proto_cookie); proto_id != -1; proto_id = proto_get_next_protocol(&proto_cookie)) { + protocol_t *protocol = find_protocol_by_id(proto_id); + if (!proto_is_protocol_enabled(protocol)) continue; + + const QString pfname = proto_get_protocol_filter_name(proto_id); + field_list << pfname; + + // Add fields only if we're past the protocol name and only for the + // current protocol. + if (field_dots > pfname.count('.')) { + void *field_cookie; + const QByteArray fw_ba = field_word.toUtf8(); // or toLatin1 or toStdString? + const char *fw_utf8 = fw_ba.constData(); + gsize fw_len = (gsize) strlen(fw_utf8); + for (header_field_info *hfinfo = proto_get_first_protocol_field(proto_id, &field_cookie); hfinfo; hfinfo = proto_get_next_protocol_field(proto_id, &field_cookie)) { + if (hfinfo->same_name_prev_id != -1) continue; // Ignore duplicate names. + + if (!g_ascii_strncasecmp(fw_utf8, hfinfo->abbrev, fw_len)) { + if ((gsize) strlen(hfinfo->abbrev) != fw_len) field_list << hfinfo->abbrev; + } + } + } + } + field_list.sort(); + + completion_model_->setStringList(field_list); + completer()->setCompletionPrefix(field_word); +} + +void FieldFilterEdit::clearFilter() +{ + clear(); + QString new_filter; + emit filterPackets(new_filter, true); +} + +void FieldFilterEdit::applyDisplayFilter() +{ + if (syntaxState() == Invalid) { + return; + } + + QString new_filter = text(); + emit filterPackets(new_filter, true); +} + +void FieldFilterEdit::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + setDefaultPlaceholderText(); + break; + default: + break; + } + } + SyntaxLineEdit::changeEvent(event); +} + +void FieldFilterEdit::showFilters() +{ + FilterDialog *display_filter_dlg = new FilterDialog(window(), FilterDialog::DisplayFilter); + display_filter_dlg->setWindowModality(Qt::ApplicationModal); + display_filter_dlg->setAttribute(Qt::WA_DeleteOnClose); + display_filter_dlg->show(); +} + +void FieldFilterEdit::prepareFilter() +{ + QAction *pa = qobject_cast(sender()); + if (!pa || pa->data().toString().isEmpty()) return; + + setText(pa->data().toString()); +} diff --git a/ui/qt/widgets/field_filter_edit.h b/ui/qt/widgets/field_filter_edit.h new file mode 100644 index 00000000..08c96e04 --- /dev/null +++ b/ui/qt/widgets/field_filter_edit.h @@ -0,0 +1,54 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FIELDFILTEREDIT_H +#define FIELDFILTEREDIT_H + +#include + +class QEvent; +class StockIconToolButton; + +class FieldFilterEdit : public SyntaxLineEdit +{ + Q_OBJECT +public: + explicit FieldFilterEdit(QWidget *parent = 0); + +protected: + void keyPressEvent(QKeyEvent *event) { completionKeyPressEvent(event); } + void focusInEvent(QFocusEvent *event) { completionFocusInEvent(event); } + void focusOutEvent(QFocusEvent *event); + +public slots: + bool checkFilter(); + void applyDisplayFilter(); + +private slots: + void checkFilter(const QString &filter_text); + void clearFilter(); + void changeEvent(QEvent* event); + + void showFilters(); + void prepareFilter(); + +private: + QString placeholder_text_; + + void setDefaultPlaceholderText(); + void buildCompletionList(const QString &field_word, const QString &preamble); + +signals: + void pushFilterSyntaxStatus(const QString&); + void popFilterSyntaxStatus(); + void pushFilterSyntaxWarning(const QString&); + void filterPackets(QString new_filter, bool force); +}; + +#endif // FIELDFILTEREDIT_H diff --git a/ui/qt/widgets/filter_expression_toolbar.cpp b/ui/qt/widgets/filter_expression_toolbar.cpp new file mode 100644 index 00000000..caf91cfa --- /dev/null +++ b/ui/qt/widgets/filter_expression_toolbar.cpp @@ -0,0 +1,465 @@ +/* filter_expression_toolbar.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static const char *dfe_property_ = "display filter expression"; //TODO : Fix Translate +static const char *dfe_property_label_ = "display_filter_expression_label"; +static const char *dfe_property_expression_ = "display_filter_expression_expr"; +static const char *dfe_property_comment_ = "display_filter_expression_comment"; +static const char *dfe_menu_ = "filter_menu"; + +#define PARENT_SEPARATOR "//" + +struct filter_expression_data +{ + FilterExpressionToolBar* toolbar; + bool actions_added; +}; + +FilterExpressionToolBar::FilterExpressionToolBar(QWidget * parent) : + DragDropToolBar(parent) +{ + updateStyleSheet(); + + setContextMenuPolicy(Qt::CustomContextMenu); + /* Give minimum space to the bar, so that drops on an empty bar will work */ + setMinimumWidth(10); + + connect (this, &QWidget::customContextMenuRequested, this, &FilterExpressionToolBar::onCustomMenuHandler); + connect(this, &DragDropToolBar::actionMoved, this, &FilterExpressionToolBar::onActionMoved); + connect(this, &DragDropToolBar::newFilterDropped, this, &FilterExpressionToolBar::onFilterDropped); + + connect(mainApp, &MainApplication::appInitialized, + this, &FilterExpressionToolBar::filterExpressionsChanged); + connect(mainApp, &MainApplication::filterExpressionsChanged, + this, &FilterExpressionToolBar::filterExpressionsChanged); + +} + +bool FilterExpressionToolBar::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + updateStyleSheet(); + break; + default: + break; + + } + return DragDropToolBar::event(event); +} + +void FilterExpressionToolBar::onCustomMenuHandler(const QPoint& pos) +{ + QAction * filterAction = actionAt(pos); + if (! filterAction) + return; + + customMenu(this, filterAction, pos); +} + +void FilterExpressionToolBar::customMenu(FilterExpressionToolBar * target, QAction * filterAction, const QPoint& pos) +{ + QMenu * filterMenu = new QMenu(target); + filterMenu->setAttribute(Qt::WA_DeleteOnClose); + + /* Only display context menu for actual filter actions */ + QString filterText = filterAction->property(dfe_property_expression_).toString().trimmed(); + + if (!filterText.isEmpty()) + { + filterMenu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionApply, filterText, true, target)); + filterMenu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionPrepare, filterText, true, target)); + filterMenu->addSeparator(); + filterMenu->addAction(FilterAction::copyFilterAction(filterText, target)); + filterMenu->addSeparator(); + QAction * actEdit = filterMenu->addAction(tr("Edit")); + connect(actEdit, &QAction::triggered, target, &FilterExpressionToolBar::editFilter); + actEdit->setProperty(dfe_property_label_, filterAction->property(dfe_property_label_)); + actEdit->setProperty(dfe_property_expression_, filterAction->property(dfe_property_expression_)); + actEdit->setData(filterAction->data()); + QAction * actDisable = filterMenu->addAction(tr("Disable")); + connect(actDisable, &QAction::triggered, target, &FilterExpressionToolBar::disableFilter); + actDisable->setProperty(dfe_property_label_, filterAction->property(dfe_property_label_)); + actDisable->setProperty(dfe_property_expression_, filterAction->property(dfe_property_expression_)); + actDisable->setData(filterAction->data()); + QAction * actRemove = filterMenu->addAction(tr("Remove")); + connect(actRemove, &QAction::triggered, target, &FilterExpressionToolBar::removeFilter); + actRemove->setProperty(dfe_property_label_, filterAction->property(dfe_property_label_)); + actRemove->setProperty(dfe_property_expression_, filterAction->property(dfe_property_expression_)); + actRemove->setData(filterAction->data()); + filterMenu->addSeparator(); + } + QAction *actFilter = filterMenu->addAction(tr("Filter Button Preferences...")); + connect(actFilter, &QAction::triggered, target, &FilterExpressionToolBar::toolBarShowPreferences); + + /* Forcing the menus to get closed, no matter which action has been triggered */ + connect(filterMenu, &QMenu::triggered, this, &FilterExpressionToolBar::closeMenu); + + filterMenu->popup(mapToGlobal(pos)); +} + +void FilterExpressionToolBar::filterExpressionsChanged() +{ + struct filter_expression_data data; + + data.toolbar = this; + data.actions_added = false; + + // Hiding and showing seems to be the only way to get the layout to + // work correctly in some cases. See bug 14121 for details. + clear(); + setUpdatesEnabled(false); + hide(); + + // XXX Add a context menu for removing and changing buttons. + filter_expression_iterate_expressions(filter_expression_add_action, &data); + + show(); + setUpdatesEnabled(true); +} + +void FilterExpressionToolBar::removeFilter() +{ + UatModel * uatModel = new UatModel(this, "Display expressions"); + + QString label = ((QAction *)sender())->property(dfe_property_label_).toString(); + QString expr = ((QAction *)sender())->property(dfe_property_expression_).toString(); + + int idx = uatRowIndexForFilter(label, expr); + + QModelIndex rowIndex = uatModel->index(idx, 0); + if (rowIndex.isValid()) { + uatModel->removeRow(rowIndex.row()); + + save_migrated_uat("Display expressions", &prefs.filter_expressions_old); + filterExpressionsChanged(); + } +} + +WiresharkMimeData * FilterExpressionToolBar::createMimeData(QString name, int position) +{ + ToolbarEntryMimeData * element = new ToolbarEntryMimeData(name, position); + UatModel * uatModel = new UatModel(this, "Display expressions"); + + QModelIndex rowIndex; + for (int cnt = 0; cnt < uatModel->rowCount() && ! rowIndex.isValid(); cnt++) + { + if (uatModel->data(uatModel->index(cnt, 1), Qt::DisplayRole).toString().compare(name) == 0) + { + rowIndex = uatModel->index(cnt, 2); + element->setFilter(rowIndex.data().toString()); + } + } + + return element; +} + +void FilterExpressionToolBar::onActionMoved(QAction* action, int oldPos, int newPos) +{ + gchar* err = NULL; + if (oldPos == newPos) + return; + + QString label = action->property(dfe_property_label_).toString(); + QString expr = action->property(dfe_property_expression_).toString(); + + int idx = uatRowIndexForFilter(label, expr); + + if (idx > -1 && oldPos > -1 && newPos > -1) + { + uat_t * table = uat_get_table_by_name("Display expressions"); + uat_move_index(table, oldPos, newPos); + uat_save(table, &err); + + g_free(err); + } +} + +void FilterExpressionToolBar::disableFilter() +{ + QString label = ((QAction *)sender())->property(dfe_property_label_).toString(); + QString expr = ((QAction *)sender())->property(dfe_property_expression_).toString(); + + int idx = uatRowIndexForFilter(label, expr); + UatModel * uatModel = new UatModel(this, "Display expressions"); + + QModelIndex rowIndex = uatModel->index(idx, 0); + if (rowIndex.isValid()) { + uatModel->setData(rowIndex, QVariant::fromValue(false)); + + save_migrated_uat("Display expressions", &prefs.filter_expressions_old); + filterExpressionsChanged(); + } +} + +void FilterExpressionToolBar::editFilter() +{ + if (! sender()) + return; + + QString label = ((QAction *)sender())->property(dfe_property_label_).toString(); + QString expr = ((QAction *)sender())->property(dfe_property_expression_).toString(); + + int idx = uatRowIndexForFilter(label, expr); + + if (idx > -1) + emit filterEdit(idx); +} + +void FilterExpressionToolBar::onFilterDropped(QString description, QString filter) +{ + if (filter.length() == 0) + return; + + filter_expression_new(qUtf8Printable(description), + qUtf8Printable(filter), qUtf8Printable(description), TRUE); + + save_migrated_uat("Display expressions", &prefs.filter_expressions_old); + filterExpressionsChanged(); +} + +void FilterExpressionToolBar::toolBarShowPreferences() +{ + emit filterPreferences(); +} + +void FilterExpressionToolBar::updateStyleSheet() +{ + // Try to draw 1-pixel-wide separator lines from the button label + // ascent to its baseline. + setStyleSheet(QString( + "QToolBar { background: none; border: none; spacing: 1px; }" + "QFrame { background: none; min-width: 1px; max-width: 1px; }" + )); +} + +int FilterExpressionToolBar::uatRowIndexForFilter(QString label, QString expression) +{ + int result = -1; + + if (expression.length() == 0) + return result; + + UatModel * uatModel = new UatModel(this, "Display expressions"); + + QModelIndex rowIndex; + + if (label.length() > 0) + { + for (int cnt = 0; cnt < uatModel->rowCount() && ! rowIndex.isValid(); cnt++) + { + if (uatModel->data(uatModel->index(cnt, 1), Qt::DisplayRole).toString().compare(label) == 0 && + uatModel->data(uatModel->index(cnt, 2), Qt::DisplayRole).toString().compare(expression) == 0) + { + rowIndex = uatModel->index(cnt, 2); + } + } + } + else + { + rowIndex = uatModel->findRowForColumnContent(((QAction *)sender())->data(), 2); + } + + if (rowIndex.isValid()) + result = rowIndex.row(); + + delete uatModel; + + return result; +} + +bool FilterExpressionToolBar::eventFilter(QObject *obj, QEvent *event) +{ + QMenu * qm = qobject_cast(obj); + + if (qm && qm->property(dfe_menu_).toBool()) + { + + if (event->type() == QEvent::ContextMenu) + { + QContextMenuEvent *ctx = static_cast(event); + QAction * filterAction = qm->actionAt(ctx->pos()); + + if (filterAction) + customMenu(this, filterAction, ctx->pos()); + return true; + } + else if (event->type() == QEvent::ToolTip) + { + QHelpEvent *helpEvent = static_cast(event); + QAction * filterAction = qm->actionAt(helpEvent->pos()); + if (filterAction) { + QToolTip::showText(helpEvent->globalPos(), filterAction->property(dfe_property_comment_).toString().trimmed()); + } else { + QToolTip::hideText(); + event->ignore(); + } + + return true; + } + } + + return QToolBar::eventFilter(obj, event); +} + +void FilterExpressionToolBar::closeMenu(QAction * /*sender*/) +{ + foreach(QAction * entry, actions()) + { + QWidget * widget = widgetForAction(entry); + QToolButton * tb = qobject_cast(widget); + if (tb && tb->menu()) + tb->menu()->close(); + } +} + +QMenu * FilterExpressionToolBar::findParentMenu(const QStringList tree, void *fed_data, QMenu *parent ) +{ + if (!fed_data) + return Q_NULLPTR; + + struct filter_expression_data* data = (filter_expression_data*)fed_data; + if (!data->toolbar) + return Q_NULLPTR; + + if (! tree.isEmpty()) + { + if (!parent) + { + /* Searching existing main menus */ + foreach(QAction * entry, data->toolbar->actions()) + { + QWidget * widget = data->toolbar->widgetForAction(entry); + QToolButton * tb = qobject_cast(widget); + if (tb && tb->menu() && tb->text().compare(tree.at(0).trimmed()) == 0) + return findParentMenu(tree.mid(1), fed_data, tb->menu()); + } + } + else if (parent) + { + QString menuName = tree.at(0).trimmed(); + /* Iterate to see, if we next have to jump into another submenu */ + foreach(QAction *entry, parent->actions()) + { + if (entry->menu() && entry->text().compare(menuName) == 0) + return findParentMenu(tree.mid(1), fed_data, entry->menu()); + } + + /* Submenu not found, creating */ + QMenu * subMenu = new QMenu(menuName); + subMenu->installEventFilter(data->toolbar); + subMenu->setProperty(dfe_menu_, QVariant::fromValue(true)); + parent->addMenu(subMenu); + return findParentMenu(tree.mid(1), fed_data, subMenu); + } + + /* No menu has been found, create one */ + QString parentName = tree.at(0).trimmed(); + QToolButton * menuButton = new QToolButton(); + menuButton->setText(parentName); + menuButton->setPopupMode(QToolButton::MenuButtonPopup); + QMenu * parentMenu = new QMenu(menuButton); + parentMenu->installEventFilter(data->toolbar); + parentMenu->setProperty(dfe_menu_, QVariant::fromValue(true)); + menuButton->setMenu(parentMenu); + // Required for QToolButton::MenuButtonPopup. + connect(menuButton, &QToolButton::pressed, menuButton, &QToolButton::showMenu); + data->toolbar->addWidget(menuButton); + + return findParentMenu(tree.mid(1), fed_data, parentMenu); + } + else if (parent) + return parent; + + return Q_NULLPTR; +} + +bool FilterExpressionToolBar::filter_expression_add_action(const void *key _U_, void *value, void *user_data) +{ + filter_expression_t* fe = (filter_expression_t*)value; + struct filter_expression_data* data = (filter_expression_data*)user_data; + + if (!fe->enabled) + return FALSE; + + QString label = QString(fe->label); + + /* Search for parent menu and create if not found */ + QStringList tree = label.split(PARENT_SEPARATOR); + if (!tree.isEmpty()) + tree.removeLast(); + QMenu * parentMenu = findParentMenu(tree, data); + if (parentMenu) + label = label.mid(label.lastIndexOf(PARENT_SEPARATOR) + QString(PARENT_SEPARATOR).length()).trimmed(); + + QAction *dfb_action = new QAction(label, data->toolbar); + if (strlen(fe->comment) > 0) + { + QString tooltip = QString("%1\n%2").arg(fe->comment).arg(fe->expression); + dfb_action->setToolTip(tooltip); + dfb_action->setProperty(dfe_property_comment_, tooltip); + } + else + { + dfb_action->setToolTip(fe->expression); + dfb_action->setProperty(dfe_property_comment_, QString(fe->expression)); + } + dfb_action->setData(QString::fromUtf8(fe->expression)); + dfb_action->setProperty(dfe_property_, true); + dfb_action->setProperty(dfe_property_label_, QString(fe->label)); + dfb_action->setProperty(dfe_property_expression_, QString(fe->expression)); + + if (data->actions_added) { + QFrame *sep = new QFrame(); + sep->setEnabled(false); + data->toolbar->addWidget(sep); + } + + if (parentMenu) + parentMenu->addAction(dfb_action); + else + data->toolbar->addAction(dfb_action); + + connect(dfb_action, &QAction::triggered, data->toolbar, &FilterExpressionToolBar::filterClicked); + data->actions_added = true; + return FALSE; +} + +void FilterExpressionToolBar::filterClicked() +{ + bool prepare = false; + QAction *dfb_action = qobject_cast(sender()); + + if (!dfb_action) + return; + + QString filterText = dfb_action->data().toString(); + prepare = (QApplication::keyboardModifiers() & Qt::ShiftModifier); + + emit filterSelected(filterText, prepare); +} diff --git a/ui/qt/widgets/filter_expression_toolbar.h b/ui/qt/widgets/filter_expression_toolbar.h new file mode 100644 index 00000000..80724a27 --- /dev/null +++ b/ui/qt/widgets/filter_expression_toolbar.h @@ -0,0 +1,63 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include + +#ifndef FILTER_EXPRESSION_TOOLBAR_H +#define FILTER_EXPRESSION_TOOLBAR_H + +class FilterExpressionToolBar : public DragDropToolBar +{ + Q_OBJECT +public: + explicit FilterExpressionToolBar(QWidget * parent = Q_NULLPTR); + +protected: + virtual bool event(QEvent *event) override; + virtual bool eventFilter(QObject *obj, QEvent *ev) override; + + virtual WiresharkMimeData * createMimeData(QString name, int position) override; + +public slots: + void filterExpressionsChanged(); + +signals: + void filterSelected(QString, bool); + void filterPreferences(); + void filterEdit(int uatIndex); + +protected slots: + void onCustomMenuHandler(const QPoint &pos); + void onActionMoved(QAction * action, int oldPos, int newPos); + void onFilterDropped(QString description, QString filter); + +private slots: + void removeFilter(); + void disableFilter(); + void editFilter(); + void filterClicked(); + void toolBarShowPreferences(); + + void closeMenu(QAction *); + +private: + void updateStyleSheet(); + int uatRowIndexForFilter(QString label, QString expression); + + void customMenu(FilterExpressionToolBar * target, QAction * filterAction, const QPoint& pos); + + static bool filter_expression_add_action(const void *key, void *value, void *user_data); + static QMenu * findParentMenu(const QStringList tree, void *fed_data, QMenu *parent = Q_NULLPTR); +}; + +#endif //FILTER_EXPRESSION_TOOLBAR_H diff --git a/ui/qt/widgets/find_line_edit.cpp b/ui/qt/widgets/find_line_edit.cpp new file mode 100644 index 00000000..c564fcb9 --- /dev/null +++ b/ui/qt/widgets/find_line_edit.cpp @@ -0,0 +1,77 @@ +/* find_line_edit.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include "epan/prefs.h" + +#include +#include +#include +#include + +void FindLineEdit::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = createStandardContextMenu(); + QAction *action; + + menu->setAttribute(Qt::WA_DeleteOnClose); + menu->addSeparator(); + + action = menu->addAction(tr("Textual Find")); + action->setCheckable(true); + action->setChecked(!use_regex_); + connect(action, &QAction::triggered, this, &FindLineEdit::setUseTextual); + + action = menu->addAction(tr("Regular Expression Find")); + action->setCheckable(true); + action->setChecked(use_regex_); + connect(action, &QAction::triggered, this, &FindLineEdit::setUseRegex); + + menu->popup(event->globalPos()); +} + +void FindLineEdit::keyPressEvent(QKeyEvent *event) +{ + QLineEdit::keyPressEvent(event); + + if (use_regex_) { + validateText(); + } +} + +void FindLineEdit::validateText() +{ + QString style("QLineEdit { background-color: %1; }"); + + if (!use_regex_ || text().isEmpty()) { + setStyleSheet(style.arg(QString(""))); + } else { + QRegularExpression regexp(text(), QRegularExpression::UseUnicodePropertiesOption); + if (regexp.isValid()) { + setStyleSheet(style.arg(ColorUtils::fromColorT(prefs.gui_text_valid).name())); + } else { + setStyleSheet(style.arg(ColorUtils::fromColorT(prefs.gui_text_invalid).name())); + } + } +} + +void FindLineEdit::setUseTextual() +{ + use_regex_ = false; + validateText(); + emit useRegexFind(use_regex_); +} + +void FindLineEdit::setUseRegex() +{ + use_regex_ = true; + validateText(); + emit useRegexFind(use_regex_); +} diff --git a/ui/qt/widgets/find_line_edit.h b/ui/qt/widgets/find_line_edit.h new file mode 100644 index 00000000..605f91dc --- /dev/null +++ b/ui/qt/widgets/find_line_edit.h @@ -0,0 +1,42 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FIND_LINE_EDIT_H +#define FIND_LINE_EDIT_H + +#include + +namespace Ui { +class FindLineEdit; +} + +class FindLineEdit : public QLineEdit +{ + Q_OBJECT + +public: + explicit FindLineEdit(QWidget *parent = 0) : QLineEdit(parent), use_regex_(false) { } + ~FindLineEdit() { } + +signals: + void useRegexFind(bool); + +private slots: + void setUseTextual(); + void setUseRegex(); + +private: + void contextMenuEvent(QContextMenuEvent *event); + void keyPressEvent(QKeyEvent *event); + void validateText(); + + bool use_regex_; +}; + +#endif // FIND_LINE_EDIT_H diff --git a/ui/qt/widgets/follow_stream_text.cpp b/ui/qt/widgets/follow_stream_text.cpp new file mode 100644 index 00000000..a8e0f2b9 --- /dev/null +++ b/ui/qt/widgets/follow_stream_text.cpp @@ -0,0 +1,52 @@ +/* follow_stream_text.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include + +// To do: +// - Draw text by hand similar to ByteViewText. This would let us add +// extra information, e.g. a timestamp column and get rid of +// max_document_length_ in FollowStreamDialog. + +FollowStreamText::FollowStreamText(QWidget *parent) : + QPlainTextEdit(parent) +{ + setMouseTracking(true); +// setMaximumBlockCount(1); + QTextDocument *text_doc = document(); + text_doc->setDefaultFont(mainApp->monospaceFont()); +} + +void FollowStreamText::mouseMoveEvent(QMouseEvent *event) +{ + emit mouseMovedToTextCursorPosition(cursorForPosition(event->pos()).position()); + // Don't send the mouseMoveEvents with no buttons pushed to the base + // class, effectively turning off mouse tracking for the base class. + // It causes a lot of useless calculations that hurt scroll performance. + if (event->buttons() != Qt::NoButton) { + QPlainTextEdit::mouseMoveEvent(event); + } +} + +void FollowStreamText::mousePressEvent(QMouseEvent *event) +{ + emit mouseClickedOnTextCursorPosition(cursorForPosition(event->pos()).position()); + QPlainTextEdit::mousePressEvent(event); +} + +void FollowStreamText::leaveEvent(QEvent *event) +{ + emit mouseMovedToTextCursorPosition(-1); + QPlainTextEdit::leaveEvent(event); +} diff --git a/ui/qt/widgets/follow_stream_text.h b/ui/qt/widgets/follow_stream_text.h new file mode 100644 index 00000000..0dd8dca5 --- /dev/null +++ b/ui/qt/widgets/follow_stream_text.h @@ -0,0 +1,35 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FOLLOW_STREAM_TEXT_H +#define FOLLOW_STREAM_TEXT_H + +#include + +class FollowStreamText : public QPlainTextEdit +{ + Q_OBJECT +public: + explicit FollowStreamText(QWidget *parent = 0); + +protected: + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void leaveEvent(QEvent *event); + +signals: + // Perhaps this is not descriptive enough. We should add more words. + void mouseMovedToTextCursorPosition(int); + void mouseClickedOnTextCursorPosition(int); + +public slots: + +}; + +#endif // FOLLOW_STREAM_TEXT_H diff --git a/ui/qt/widgets/interface_toolbar_lineedit.cpp b/ui/qt/widgets/interface_toolbar_lineedit.cpp new file mode 100644 index 00000000..02383f58 --- /dev/null +++ b/ui/qt/widgets/interface_toolbar_lineedit.cpp @@ -0,0 +1,132 @@ +/* interface_toolbar_lineedit.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include +#include +#include "epan/prefs.h" +#include + +#include + +// To do: +// - Make a narrower apply button + +InterfaceToolbarLineEdit::InterfaceToolbarLineEdit(QWidget *parent, QString validation_regex, bool is_required) : + QLineEdit(parent), + regex_expr_(validation_regex, QRegularExpression::UseUnicodePropertiesOption), + is_required_(is_required), + text_edited_(false) +{ + apply_button_ = new StockIconToolButton(this, "x-filter-apply"); + apply_button_->setCursor(Qt::ArrowCursor); + apply_button_->setEnabled(false); + apply_button_->setToolTip(tr("Apply changes")); + apply_button_->setIconSize(QSize(24, 14)); + apply_button_->setStyleSheet( + "QToolButton {" + " border: none;" + " background: transparent;" // Disables platform style on Windows. + " padding: 0 0 0 0;" + "}" + ); + + updateStyleSheet(isValid()); + + connect(this, &InterfaceToolbarLineEdit::textChanged, this, &InterfaceToolbarLineEdit::validateText); + connect(this, &InterfaceToolbarLineEdit::textEdited, this, &InterfaceToolbarLineEdit::validateEditedText); + connect(this, &InterfaceToolbarLineEdit::returnPressed, this, &InterfaceToolbarLineEdit::applyEditedText); + connect(apply_button_, &StockIconToolButton::clicked, this, &InterfaceToolbarLineEdit::applyEditedText); +} + +void InterfaceToolbarLineEdit::validateText() +{ + bool valid = isValid(); + + apply_button_->setEnabled(valid); + updateStyleSheet(valid); +} + +void InterfaceToolbarLineEdit::validateEditedText() +{ + text_edited_ = true; +} + +void InterfaceToolbarLineEdit::applyEditedText() +{ + if (text_edited_ && isValid()) + { + emit editedTextApplied(); + disableApplyButton(); + } +} + +void InterfaceToolbarLineEdit::disableApplyButton() +{ + apply_button_->setEnabled(false); + text_edited_ = false; +} + +bool InterfaceToolbarLineEdit::isValid() +{ + bool valid = true; + + if (is_required_ && text().length() == 0) + { + valid = false; + } + + if (!regex_expr_.pattern().isEmpty() && text().length() > 0) + { + if (!regex_expr_.isValid() || !regex_expr_.match(text()).hasMatch()) + { + valid = false; + } + } + + return valid; +} + +void InterfaceToolbarLineEdit::updateStyleSheet(bool is_valid) +{ + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + QSize apsz = apply_button_->sizeHint(); + + QString style_sheet = QString( + "InterfaceToolbarLineEdit {" + " padding-right: %1px;" + " background-color: %2;" + "}" + ) + .arg(apsz.width() + frameWidth) + .arg(is_valid || !isEnabled() ? QString("") : ColorUtils::fromColorT(prefs.gui_text_invalid).name()); + +#ifdef Q_OS_MAC + style_sheet += QString( + "InterfaceToolbarLineEdit {" + " border: 1px solid palette(%1);" + " border-radius: 3px;" + "}" + ).arg(ColorUtils::themeIsDark() ? QString("light") : QString("dark")); +#endif + + setStyleSheet(style_sheet); +} + +void InterfaceToolbarLineEdit::resizeEvent(QResizeEvent *) +{ + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + QSize apsz = apply_button_->sizeHint(); + + apply_button_->move(contentsRect().right() - frameWidth - apsz.width() + 2, + contentsRect().top()); + apply_button_->setMinimumHeight(contentsRect().height()); + apply_button_->setMaximumHeight(contentsRect().height()); +} diff --git a/ui/qt/widgets/interface_toolbar_lineedit.h b/ui/qt/widgets/interface_toolbar_lineedit.h new file mode 100644 index 00000000..4b3aaccd --- /dev/null +++ b/ui/qt/widgets/interface_toolbar_lineedit.h @@ -0,0 +1,47 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INTERFACE_TOOLBAR_LINEEDIT_H +#define INTERFACE_TOOLBAR_LINEEDIT_H + +#include +#include + +class StockIconToolButton; + +class InterfaceToolbarLineEdit : public QLineEdit +{ + Q_OBJECT + +public: + explicit InterfaceToolbarLineEdit(QWidget *parent = 0, QString validation_regex = QString(), bool is_required = false); + void disableApplyButton(); + +protected: + void resizeEvent(QResizeEvent *); + +signals: + void editedTextApplied(); + +private slots: + void validateText(); + void validateEditedText(); + void applyEditedText(); + +private: + bool isValid(); + void updateStyleSheet(bool is_valid); + + StockIconToolButton *apply_button_; + QRegularExpression regex_expr_; + bool is_required_; + bool text_edited_; +}; + +#endif // INTERFACE_TOOLBAR_LINEEDIT_H diff --git a/ui/qt/widgets/label_stack.cpp b/ui/qt/widgets/label_stack.cpp new file mode 100644 index 00000000..4cac15b7 --- /dev/null +++ b/ui/qt/widgets/label_stack.cpp @@ -0,0 +1,178 @@ +/* label_stack.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include + +#include + +/* Temporary message timeouts */ +const int temporary_interval_ = 1000; +const int temporary_msg_timeout_ = temporary_interval_ * 9; +const int temporary_flash_timeout_ = temporary_interval_ / 5; +const int num_flashes_ = 3; + +LabelStack::LabelStack(QWidget *parent) : + QLabel(parent), + temporary_ctx_(-1), + shrinkable_(false) +{ +#ifdef Q_OS_MAC + setAttribute(Qt::WA_MacSmallSize, true); +#endif + fillLabel(); + + connect(&temporary_timer_, &QTimer::timeout, this, &LabelStack::updateTemporaryStatus); +} + +void LabelStack::setTemporaryContext(const int ctx) { + temporary_ctx_ = ctx; +} + +void LabelStack::fillLabel() { + StackItem si; + QString style_sheet; + + style_sheet = + "QLabel {" + " margin-left: 0.5em;"; + + if (labels_.isEmpty()) { + clear(); + return; + } + + si = labels_.first(); + + if (si.ctx == temporary_ctx_) { + style_sheet += QString( + " border-radius: 0.25em;" + " background-color: %2;" + ) + .arg(ColorUtils::warningBackground().name()); + } + + style_sheet += "}"; + if (styleSheet().size() != style_sheet.size()) { + // Can be computationally expensive. + setStyleSheet(style_sheet); + } + setText(si.text); +} + +void LabelStack::pushText(const QString &text, int ctx) { + popText(ctx); + + if (ctx == temporary_ctx_) { + temporary_timer_.stop(); + + temporary_epoch_.start(); + temporary_timer_.start(temporary_flash_timeout_); + emit toggleTemporaryFlash(true); + } + + StackItem si; + si.text = text; + si.ctx = ctx; + labels_.prepend(si); + fillLabel(); +} + +void LabelStack::setShrinkable(bool shrinkable) +{ + shrinkable_ = shrinkable; + int min_width = 0; + + if (shrinkable) { + min_width = fontMetrics().height() * 5; // em-widths + } + setMinimumWidth(min_width); + fillLabel(); +} + +void LabelStack::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + emit mousePressedAt(event->globalPosition().toPoint(), Qt::LeftButton); +#else + emit mousePressedAt(event->globalPos(), Qt::LeftButton); +#endif + } +} + +void LabelStack::mouseReleaseEvent(QMouseEvent *) +{ +} + +void LabelStack::mouseDoubleClickEvent(QMouseEvent *) +{ +} + +void LabelStack::mouseMoveEvent(QMouseEvent *) +{ +} + +void LabelStack::contextMenuEvent(QContextMenuEvent *event) +{ + emit mousePressedAt(QPoint(event->globalPos()), Qt::RightButton); +} + +void LabelStack::paintEvent(QPaintEvent *event) +{ + if (!shrinkable_) { + QLabel::paintEvent(event); + return; + } + + QFrame::paintEvent(event); + + QString elided_text = fontMetrics().elidedText(text(), Qt::ElideMiddle, width()); + QPainter painter(this); + QRect contents_rect = contentsRect(); + QStyleOption opt; + + contents_rect.adjust(margin(), margin(), -margin(), -margin()); + opt.initFrom(this); + + style()->drawItemText(&painter, contents_rect, alignment(), opt.palette, + isEnabled(), elided_text, foregroundRole()); +} + +void LabelStack::popText(int ctx) { + QMutableListIterator iter(labels_); + + while (iter.hasNext()) { + if (iter.next().ctx == ctx) { + iter.remove(); + break; + } + } + + fillLabel(); +} + +void LabelStack::updateTemporaryStatus() { + if (temporary_epoch_.elapsed() >= temporary_msg_timeout_) { + popText(temporary_ctx_); + emit toggleTemporaryFlash(false); + temporary_timer_.stop(); + } else { + for (int i = (num_flashes_ * 2); i > 0; i--) { + if (temporary_epoch_.elapsed() >= temporary_flash_timeout_ * i) { + emit toggleTemporaryFlash(i % 2); + break; + } + } + } +} diff --git a/ui/qt/widgets/label_stack.h b/ui/qt/widgets/label_stack.h new file mode 100644 index 00000000..63657b74 --- /dev/null +++ b/ui/qt/widgets/label_stack.h @@ -0,0 +1,60 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef LABEL_STACK_H +#define LABEL_STACK_H + +#include +#include +#include +#include + +class LabelStack : public QLabel +{ + Q_OBJECT +public: + explicit LabelStack(QWidget *parent = 0); + void setTemporaryContext(const int ctx); + void pushText(const QString &text, int ctx); + void setShrinkable(bool shrinkable = true); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void contextMenuEvent(QContextMenuEvent *event); + void paintEvent (QPaintEvent *event); + +private: + typedef struct _StackItem { + QString text; + int ctx; + } StackItem; + + int temporary_ctx_; + QList labels_; + bool shrinkable_; + QElapsedTimer temporary_epoch_; + QTimer temporary_timer_; + + void fillLabel(); + +signals: + void toggleTemporaryFlash(bool enable); + void mousePressedAt(const QPoint &global_pos, Qt::MouseButton button); + +public slots: + void popText(int ctx); + +private slots: + void updateTemporaryStatus(); +}; + +#endif // LABEL_STACK_H diff --git a/ui/qt/widgets/overlay_scroll_bar.cpp b/ui/qt/widgets/overlay_scroll_bar.cpp new file mode 100644 index 00000000..5e54d53a --- /dev/null +++ b/ui/qt/widgets/overlay_scroll_bar.cpp @@ -0,0 +1,277 @@ +/* overlay_scroll_bar.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) +#include +#include +#endif + +// To do: +// - We could graph something useful (e.g. delay times) in packet_map_img_. +// https://www.wireshark.org/lists/ethereal-dev/200011/msg00122.html +// - Properly handle transience. + +// We want a normal scrollbar with space on either side on which we can draw +// and receive mouse events. Adding space using a stylesheet loses native +// styling on Windows. Overriding QProxyStyle::drawComplexControl (which is +// called by QScrollBar::paintEvent) results in odd behavior on Windows. +// +// The best solution so far seems to be to simply create a normal-sized child +// scrollbar, manually position it, and synchronize it with its parent. We +// can then alter the parent's mouse and paint behavior to our heart's +// content. + +class OsbProxyStyle : public QProxyStyle +{ +public: + // Disable transient behavior. Mainly for macOS but possibly applies to + // other platforms. If we want to enable transience we'll have to + // handle the following at a minimum: + // + // setProperty("visible") from QScrollbarStyleAnimation. + // Other visibility changes. + // HoverEnter & HoverLeave events from QAbstractScrollArea. + // Size (and possibly opacity) changes while painting. + // + // Another approach would be to flip the child-parent relationship + // and make the parent a normal scroll bar with a manually-placed + // packet map child. This might make the packet list geometry a bit + // wonky, however. + + virtual int styleHint(StyleHint hint, const QStyleOption *option = NULL, const QWidget *widget = NULL, QStyleHintReturn *returnData = NULL) const { + if (hint == SH_ScrollBar_Transient) return false; + + return QProxyStyle::styleHint(hint, option, widget, returnData); + } +}; + +OverlayScrollBar::OverlayScrollBar(Qt::Orientation orientation, QWidget *parent) : + QScrollBar(orientation, parent), + child_sb_(orientation, this), + packet_map_img_(QImage()), + packet_map_width_(0), + marked_packet_width_(0), + packet_count_(-1), + start_pos_(-1), + end_pos_(-1), + positions_(QList()) +{ + style_ = new OsbProxyStyle(); + setStyle(style_); + + child_style_ = new OsbProxyStyle(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) + updateChildStyle(); +#else + child_sb_.setStyle(child_style_); +#endif + child_sb_.raise(); + child_sb_.installEventFilter(this); + + // XXX Do we need to connect anything else? + connect(this, &OverlayScrollBar::rangeChanged, this, &OverlayScrollBar::setChildRange); + connect(this, &OverlayScrollBar::valueChanged, &child_sb_, &QScrollBar::setValue); + + connect(&child_sb_, &QScrollBar::valueChanged, this, &OverlayScrollBar::setValue); + connect(&child_sb_, &QScrollBar::actionTriggered, this, &OverlayScrollBar::actionTriggered); +} + +OverlayScrollBar::~OverlayScrollBar() +{ + delete child_style_; + delete style_; +} + +QSize OverlayScrollBar::sizeHint() const +{ + return QSize(packet_map_width_ + child_sb_.sizeHint().width(), + QScrollBar::sizeHint().height()); +} + +int OverlayScrollBar::sliderPosition() +{ + return child_sb_.sliderPosition(); +} + +void OverlayScrollBar::setNearOverlayImage(QImage& overlay_image, int packet_count, int start_pos, int end_pos, QList positions, int rowHeight) +{ + int old_width = packet_map_img_.width(); + packet_map_img_ = overlay_image; + packet_count_ = packet_count; + start_pos_ = start_pos; + end_pos_ = end_pos; + positions_ = positions; + row_height_ = rowHeight > devicePixelRatio() ? rowHeight : devicePixelRatio(); + + if (old_width != packet_map_img_.width()) { + qreal dp_ratio = devicePixelRatio(); + + packet_map_width_ = packet_map_img_.width() / dp_ratio; + + updateGeometry(); + } + update(); +} + +void OverlayScrollBar::setMarkedPacketImage(QImage &mp_image) +{ + qreal dp_ratio = devicePixelRatio(); + + marked_packet_img_ = mp_image; + marked_packet_width_ = mp_image.width() / dp_ratio; + + child_sb_.update(); +} + +QRect OverlayScrollBar::grooveRect() +{ + QStyleOptionSlider opt; + + initStyleOption(&opt); + opt.rect = child_sb_.rect(); + + return child_sb_.style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, &child_sb_); +} + +void OverlayScrollBar::resizeEvent(QResizeEvent *event) +{ + QScrollBar::resizeEvent(event); + + child_sb_.move(packet_map_width_, 0); + child_sb_.resize(child_sb_.sizeHint().width(), height()); +#ifdef Q_OS_MAC + child_sb_.setPageStep(height()); +#endif +} + +void OverlayScrollBar::paintEvent(QPaintEvent *event) +{ + qreal dp_ratio = devicePixelRatio(); + QSize pm_size(packet_map_width_, geometry().height()); + pm_size *= dp_ratio; + + QPainter painter(this); + + painter.fillRect(event->rect(), palette().base()); + + if (!packet_map_img_.isNull()) { + QImage packet_map(pm_size, QImage::Format_ARGB32_Premultiplied); + packet_map.fill(Qt::transparent); + + // Draw the image supplied by the packet list. + QPainter pm_painter(&packet_map); + pm_painter.setPen(Qt::NoPen); + + QRect near_dest(0, 0, pm_size.width(), pm_size.height()); + pm_painter.drawImage(near_dest, packet_map_img_.scaled(near_dest.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + + // Selected packet indicator + if (positions_.count() > 0) + { + foreach (int selected_pos_, positions_) + { + int pmiHeight = packet_map_img_.height(); + if (selected_pos_ >= 0 && selected_pos_ < pmiHeight) { + pm_painter.save(); + int no_pos = near_dest.height() * selected_pos_ / pmiHeight; + pm_painter.setBrush(palette().highlight().color()); + pm_painter.drawRect(0, no_pos, pm_size.width(), row_height_); + pm_painter.restore(); + } + } + } + + // Borders + pm_painter.save(); + QColor border_color(ColorUtils::alphaBlend(palette().text(), palette().window(), 0.25)); + pm_painter.setPen(border_color); + pm_painter.drawLine(near_dest.topLeft(), near_dest.bottomLeft()); + pm_painter.drawLine(near_dest.topRight(), near_dest.bottomRight()); + pm_painter.drawLine(near_dest.bottomLeft(), near_dest.bottomRight()); + pm_painter.restore(); + + // Draw the map. + packet_map.setDevicePixelRatio(dp_ratio); + painter.drawImage(0, 0, packet_map); + } +} + +bool OverlayScrollBar::eventFilter(QObject *watched, QEvent *event) +{ + bool ret = false; + if (watched == &child_sb_ && event->type() == QEvent::Paint) { + // Paint the scrollbar first. + child_sb_.event(event); + ret = true; + + if (!marked_packet_img_.isNull()) { + QRect groove_rect = grooveRect(); + qreal dp_ratio = devicePixelRatio(); + groove_rect.setTopLeft(groove_rect.topLeft() * dp_ratio); + groove_rect.setSize(groove_rect.size() * dp_ratio); + + QImage marked_map(groove_rect.width(), groove_rect.height(), QImage::Format_ARGB32_Premultiplied); + marked_map.fill(Qt::transparent); + + QPainter mm_painter(&marked_map); + mm_painter.setPen(Qt::NoPen); + + QRect far_dest(0, 0, groove_rect.width(), groove_rect.height()); + mm_painter.drawImage(far_dest, marked_packet_img_.scaled(far_dest.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + + marked_map.setDevicePixelRatio(dp_ratio); + QPainter painter(&child_sb_); + painter.drawImage(groove_rect.left(), groove_rect.top(), marked_map); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) + else if (event->type() == QEvent::ApplicationPaletteChange) { + updateChildStyle(); + } +#endif + + return ret; +} + +void OverlayScrollBar::mouseReleaseEvent(QMouseEvent *event) +{ + QRect pm_r(0, 0, packet_map_width_, height()); + + if (pm_r.contains(event->pos()) && geometry().height() > 0 && packet_count_ > 0 && pageStep() > 0) { + double map_ratio = double(end_pos_ - start_pos_) / geometry().height(); + int clicked_packet = (event->pos().y() * map_ratio) + start_pos_; + /* The first packet is at minimum(). The last packet is at + * maximum() + pageStep(). (maximum() corresponds to the first + * packet shown when the scrollbar at at the maximum position.) + * https://doc.qt.io/qt-6/qscrollbar.html#details + */ + double packet_to_sb_value = double(maximum() + pageStep() - minimum()) / packet_count_; + int top_pad = pageStep() / 4; // Land near, but not at, the top. + + setValue((clicked_packet * packet_to_sb_value) - top_pad); + } +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) +void OverlayScrollBar::updateChildStyle() +{ + child_style_->setBaseStyle(QStyleFactory::create(qApp->style()->name())); + child_sb_.setStyle(child_style_); +} +#endif diff --git a/ui/qt/widgets/overlay_scroll_bar.h b/ui/qt/widgets/overlay_scroll_bar.h new file mode 100644 index 00000000..4ad5a17f --- /dev/null +++ b/ui/qt/widgets/overlay_scroll_bar.h @@ -0,0 +1,84 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __OVERLAY_SCROLL_BAR_H__ +#define __OVERLAY_SCROLL_BAR_H__ + +#include + +class QProxyStyle; + +class OverlayScrollBar : public QScrollBar +{ + Q_OBJECT + +public: + OverlayScrollBar(Qt::Orientation orientation, QWidget * parent = 0); + virtual ~OverlayScrollBar(); + + virtual QSize sizeHint() const; + virtual int sliderPosition(); + + /** Set the "near" overlay image. + * @param overlay_image An image containing a 1:1 mapping of nearby + * packet colors to raster lines. It should be sized in device + * pixels. + * @param packet_count Number of packets. + * @param start_pos The first packet number represented by the image. + * -1 means no packet is selected. + * @param end_pos The last packet number represented by the image. -1 + * means no packet is selected. + * @param positions The positions of the selected packets within the + * image. + * @param rowHeight The row height to be used for displaying the mark + */ + void setNearOverlayImage(QImage &overlay_image, int packet_count = -1, int start_pos = -1, int end_pos = -1, QList positions = QList(), int rowHeight = 1); + + /** Set the "far" overlay image. + * @param mp_image An image showing the position of marked, ignored, + * and reference time packets over the entire packet list. It + * should be sized in device pixels. + */ + void setMarkedPacketImage(QImage &mp_image); + + + /** The "groove" area of the child scrollbar. + */ + QRect grooveRect(); + +public slots: + void setChildRange(int min, int max) { child_sb_.setRange(min, max); } + +protected: + virtual void resizeEvent(QResizeEvent * event); + virtual void paintEvent(QPaintEvent * event); + virtual bool eventFilter(QObject *watched, QEvent *event); + virtual void mousePressEvent(QMouseEvent *) { /* No-op */ } + virtual void mouseReleaseEvent(QMouseEvent * event); + +private: + QProxyStyle* style_; + QProxyStyle* child_style_; + QScrollBar child_sb_; + QImage packet_map_img_; + QImage marked_packet_img_; + int packet_map_width_; + int marked_packet_width_; + int packet_count_; + int start_pos_; + int end_pos_; + QList positions_; + int row_height_; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) + void updateChildStyle(); +#endif +}; + +#endif // __OVERLAY_SCROLL_BAR_H__ diff --git a/ui/qt/widgets/packet_list_header.cpp b/ui/qt/widgets/packet_list_header.cpp new file mode 100644 index 00000000..055efae6 --- /dev/null +++ b/ui/qt/widgets/packet_list_header.cpp @@ -0,0 +1,375 @@ +/* packet_list_header.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +PacketListHeader::PacketListHeader(Qt::Orientation orientation, QWidget *parent) : + QHeaderView(orientation, parent), + sectionIdx(-1) +{ + setAcceptDrops(true); + setSectionsMovable(true); + setStretchLastSection(true); + setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter); +} + +void PacketListHeader::dragEnterEvent(QDragEnterEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType) && event->source() != this->parent()) + { + if (event->source() != this) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } + else + QHeaderView::dragEnterEvent(event); +} + +void PacketListHeader::dragMoveEvent(QDragMoveEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) + { + if (event->source() != this) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } + else + QHeaderView::dragMoveEvent(event); +} + +void PacketListHeader::dropEvent(QDropEvent *event) +{ + if (! event || ! event->mimeData()) + return; + + /* Moving items around */ + if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) + { + QByteArray jsonData = event->mimeData()->data(WiresharkMimeData::DisplayFilterMimeType); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + if (! jsonDoc.isObject()) + return; + + QJsonObject data = jsonDoc.object(); + + if ( event->source() != this && data.contains("description") && data.contains("name") ) + { + event->setDropAction(Qt::CopyAction); + event->accept(); + + MainWindow * mw = qobject_cast(mainApp->mainWindow()); + if (mw) + { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + int idx = logicalIndexAt(event->position().toPoint()); +#else + int idx = logicalIndexAt(event->pos()); +#endif + mw->insertColumn(data["description"].toString(), data["name"].toString(), idx); + } + + } else { + event->acceptProposedAction(); + } + } + else + QHeaderView::dropEvent(event); +} + +void PacketListHeader::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton && sectionIdx < 0) + { + /* No move happening yet */ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + int sectIdx = logicalIndexAt(e->position().toPoint().x() - 4, e->position().toPoint().y()); +#else + int sectIdx = logicalIndexAt(e->localPos().x() - 4, e->localPos().y()); +#endif + + QString headerName = model()->headerData(sectIdx, orientation()).toString(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + QToolTip::showText(e->globalPosition().toPoint(), QString("Width: %1").arg(sectionSize(sectIdx))); +#else + QToolTip::showText(e->globalPos(), QString("Width: %1").arg(sectionSize(sectIdx))); +#endif + } + QHeaderView::mousePressEvent(e); +} + +void PacketListHeader::mouseMoveEvent(QMouseEvent *e) +{ + if (e->button() == Qt::NoButton || ! (e->buttons() & Qt::LeftButton)) + { + /* no move is happening */ + sectionIdx = -1; + } + else if (e->buttons() & Qt::LeftButton) + { + /* section being moved */ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + int triggeredSection = logicalIndexAt(e->position().toPoint().x() - 4, e->position().toPoint().y()); +#else + int triggeredSection = logicalIndexAt(e->localPos().x() - 4, e->localPos().y()); +#endif + + if (sectionIdx < 0) + sectionIdx = triggeredSection; + else if (sectionIdx == triggeredSection) + { + /* Only run for the current moving section after a change */ + QString headerName = model()->headerData(sectionIdx, orientation()).toString(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + QToolTip::showText(e->globalPosition().toPoint(), QString("Width: %1").arg(sectionSize(sectionIdx))); +#else + QToolTip::showText(e->globalPos(), QString("Width: %1").arg(sectionSize(sectionIdx))); +#endif + } + } + QHeaderView::mouseMoveEvent(e); +} + +void PacketListHeader::contextMenuEvent(QContextMenuEvent *event) +{ + int sectionIdx = logicalIndexAt(event->pos()); + if (sectionIdx < 0 || sectionIdx >= prefs.num_cols) + return; + + char xalign = recent_get_column_xalign(sectionIdx); + QAction * action = nullptr; + + QMenu * contextMenu = new QMenu(this); + contextMenu->setAttribute(Qt::WA_DeleteOnClose); + contextMenu->setProperty("column", QVariant::fromValue(sectionIdx)); + + QActionGroup * alignmentActions = new QActionGroup(contextMenu); + alignmentActions->setExclusive(false); + alignmentActions->setProperty("column", QVariant::fromValue(sectionIdx)); + action = alignmentActions->addAction(tr("Align Left")); + action->setCheckable(true); + action->setChecked(xalign == COLUMN_XALIGN_LEFT ? true : false); + action->setData(QVariant::fromValue(COLUMN_XALIGN_LEFT)); + action = alignmentActions->addAction(tr("Align Center")); + action->setCheckable(true); + action->setChecked(xalign == COLUMN_XALIGN_CENTER ? true : false); + action->setData(QVariant::fromValue(COLUMN_XALIGN_CENTER)); + action = alignmentActions->addAction(tr("Align Right")); + action->setCheckable(true); + action->setChecked(xalign == COLUMN_XALIGN_RIGHT ? true : false); + action->setData(QVariant::fromValue(COLUMN_XALIGN_RIGHT)); + connect(alignmentActions, &QActionGroup::triggered, this, &PacketListHeader::setAlignment); + + contextMenu->addActions(alignmentActions->actions()); + contextMenu->addSeparator(); + + action = contextMenu->addAction(tr("Column Preferences…")); + connect(action, &QAction::triggered, this, &PacketListHeader::showColumnPrefs); + action = contextMenu->addAction(tr("Edit Column")); + connect(action, &QAction::triggered, this, &PacketListHeader::doEditColumn); + action = contextMenu->addAction(tr("Resize to Contents")); + connect(action, &QAction::triggered, this, &PacketListHeader::resizeToContent); + action = contextMenu->addAction(tr("Resize Column to Width…")); + connect(action, &QAction::triggered, this, &PacketListHeader::resizeToWidth); + + action = contextMenu->addAction(tr("Resolve Names")); + bool canResolve = model()->headerData(sectionIdx, Qt::Horizontal, PacketListModel::HEADER_CAN_RESOLVE).toBool(); + action->setEnabled(canResolve); + action->setCheckable(true); + action->setChecked(canResolve && get_column_resolved(sectionIdx)); + connect(action, &QAction::triggered, this, &PacketListHeader::doResolveNames); + + contextMenu->addSeparator(); + + for (int cnt = 0; cnt < prefs.num_cols; cnt++) { + QString title(get_column_title(cnt)); + QString detail; + if (get_column_format(cnt) == COL_CUSTOM) { + detail = get_column_custom_fields(cnt); + } else { + detail = col_format_desc(get_column_format(cnt)); + } + + if (prefs.gui_packet_header_column_definition) + title.append(QString("\t%1").arg(detail)); + + QAction *action = new QAction(title, this); + action->setToolTip(detail); + action->setCheckable(true); + action->setChecked(get_column_visible(cnt)); + action->setData(QVariant::fromValue(cnt)); + connect(action, &QAction::triggered, this, &PacketListHeader::columnVisibilityTriggered); + contextMenu->addAction(action); + } + contextMenu->setToolTipsVisible(true); + + contextMenu->addSeparator(); + + action = contextMenu->addAction(tr("Remove this Column")); + action->setEnabled(sectionIdx >= 0 && count() > 2); + connect(action, &QAction::triggered, this, &PacketListHeader::removeColumn); + + contextMenu->popup(viewport()->mapToGlobal(event->pos())); +} + +void PacketListHeader::columnVisibilityTriggered() +{ + QAction *ha = qobject_cast(sender()); + if (!ha) return; + + int col = ha->data().toInt(); + set_column_visible(col, ha->isChecked()); + setSectionHidden(col, ha->isChecked() ? false : true); + if (ha->isChecked()) + emit resetColumnWidth(col); + + prefs_main_write(); +} + +void PacketListHeader::setAlignment(QAction *action) +{ + if (!action) + return; + + QActionGroup * group = action->actionGroup(); + if (! group) + return; + + int section = group->property("column").toInt(); + if (section >= 0) + { + QChar data = action->data().toChar(); + recent_set_column_xalign(section, action->isChecked() ? data.toLatin1() : COLUMN_XALIGN_DEFAULT); + emit updatePackets(false); + } +} + +void PacketListHeader::showColumnPrefs() +{ + emit showColumnPreferences(PrefsModel::typeToString(PrefsModel::Columns)); +} + +void PacketListHeader::doEditColumn() +{ + QAction * action = qobject_cast(sender()); + if (!action) + return; + + QMenu * menu = qobject_cast(action->parent()); + if (! menu) + return; + + int section = menu->property("column").toInt(); + emit editColumn(section); +} + +void PacketListHeader::doResolveNames() +{ + QAction * action = qobject_cast(sender()); + if (!action) + return; + + QMenu * menu = qobject_cast(action->parent()); + if (!menu) + return; + + int section = menu->property("column").toInt(); + + set_column_resolved(section, action->isChecked()); + prefs_main_write(); + emit updatePackets(true); +} + +void PacketListHeader::resizeToContent() +{ + QAction * action = qobject_cast(sender()); + if (!action) + return; + + QMenu * menu = qobject_cast(action->parent()); + if (!menu) + return; + + int section = menu->property("column").toInt(); + PacketList * packetList = qobject_cast(parent()); + if (packetList) + packetList->resizeColumnToContents(section); +} + +void PacketListHeader::removeColumn() +{ + QAction * action = qobject_cast(sender()); + if (!action) + return; + + QMenu * menu = qobject_cast(action->parent()); + if (!menu) + return; + + int section = menu->property("column").toInt(); + + if (count() > 2) { + column_prefs_remove_nth(section); + emit columnsChanged(); + prefs_main_write(); + } +} + +void PacketListHeader::resizeToWidth() +{ + QAction * action = qobject_cast(sender()); + if (!action) + return; + + QMenu * menu = qobject_cast(action->parent()); + if (!menu) + return; + + bool ok = false; + int width = -1; + int section = menu->property("column").toInt(); + QString headerName = model()->headerData(section, orientation()).toString(); + width = QInputDialog::getInt(this, tr("Column %1").arg(headerName), tr("Width:"), + sectionSize(section), 0, 1000, 1, &ok); + if (ok) + resizeSection(section, width); +} diff --git a/ui/qt/widgets/packet_list_header.h b/ui/qt/widgets/packet_list_header.h new file mode 100644 index 00000000..4e70e828 --- /dev/null +++ b/ui/qt/widgets/packet_list_header.h @@ -0,0 +1,62 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef UI_QT_WIDGETS_PACKET_LIST_HEADER_H_ +#define UI_QT_WIDGETS_PACKET_LIST_HEADER_H_ + +#include + +#include +#include +#include + +class QEvent; + +class PacketListHeader : public QHeaderView +{ + Q_OBJECT + +public: + PacketListHeader(Qt::Orientation orientation, QWidget *parent = nullptr); + +protected: + virtual void dropEvent(QDropEvent *event) override; + virtual void dragEnterEvent(QDragEnterEvent *event) override; + virtual void dragMoveEvent(QDragMoveEvent *event) override; + + virtual void mouseMoveEvent(QMouseEvent *e) override; + virtual void mousePressEvent(QMouseEvent *e) override; + + virtual void contextMenuEvent(QContextMenuEvent *event) override; + +protected slots: + void columnVisibilityTriggered(); + + void setAlignment(QAction *); + + void showColumnPrefs(); + void doEditColumn(); + void doResolveNames(); + void resizeToContent(); + void removeColumn(); + void resizeToWidth(); + +signals: + void resetColumnWidth(int col); + void updatePackets(bool redraw); + void showColumnPreferences(QString pane_name); + void editColumn(int column); + + void columnsChanged(); + +private: + int sectionIdx; +}; + +#endif diff --git a/ui/qt/widgets/path_selection_edit.cpp b/ui/qt/widgets/path_selection_edit.cpp new file mode 100644 index 00000000..eb30f1d0 --- /dev/null +++ b/ui/qt/widgets/path_selection_edit.cpp @@ -0,0 +1,94 @@ +/* path_chooser_delegate.cpp + * Delegate to select a file path for a treeview entry + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "epan/prefs.h" +#include "ui/util.h" + +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include +#include +#include +#include + +PathSelectionEdit::PathSelectionEdit(QString title, QString path, bool selectFile, QWidget *parent) : + QWidget(parent) +{ + _title = title; + _path = path; + _selectFile = selectFile; + + _edit = new QLineEdit(this); + _edit->setText(_path); + connect(_edit, &QLineEdit::textChanged, this, &PathSelectionEdit::setPath); + + _button = new QToolButton(this); + _button->setText(tr("Browse")); + connect(_button, &QToolButton::clicked, this, &PathSelectionEdit::browseForPath); + + setContentsMargins(0, 0, 0, 0); + QHBoxLayout *hbox = new QHBoxLayout(this); + hbox->setContentsMargins(0, 0, 0, 0); + hbox->addWidget(_edit); + hbox->addWidget(_button); + hbox->setSizeConstraint(QLayout::SetMinimumSize); + + setLayout(hbox); + + setFocusProxy(_edit); + setFocusPolicy(_edit->focusPolicy()); +} + +PathSelectionEdit::PathSelectionEdit(QWidget *parent) : + PathSelectionEdit(tr("Select a path"), QString(), true, parent) +{} + +void PathSelectionEdit::setPath(QString newPath) +{ + _path = newPath; + if (!sender()) { + _edit->blockSignals(true); + _edit->setText(newPath); + _edit->blockSignals(false); + } else { + emit pathChanged(newPath); + } +} + +QString PathSelectionEdit::path() const +{ + return _path; +} + +void PathSelectionEdit::browseForPath() +{ + QString openDir = _path; + + if (openDir.isEmpty()) { + if (prefs.gui_fileopen_style == FO_STYLE_LAST_OPENED) { + openDir = QString(get_open_dialog_initial_dir()); + } else if (prefs.gui_fileopen_style == FO_STYLE_SPECIFIED) { + openDir = QString(prefs.gui_fileopen_dir); + } + } + + QString newPath; + if ( _selectFile ) + newPath = WiresharkFileDialog::getOpenFileName(this, _title, openDir); + else + newPath = WiresharkFileDialog::getExistingDirectory(this, _title, openDir); + + if (!newPath.isEmpty()) { + _edit->setText(newPath); + } +} diff --git a/ui/qt/widgets/path_selection_edit.h b/ui/qt/widgets/path_selection_edit.h new file mode 100644 index 00000000..1c0f9fbb --- /dev/null +++ b/ui/qt/widgets/path_selection_edit.h @@ -0,0 +1,47 @@ +/* path_chooser_delegate.cpp + * Delegate to select a file path for a treeview entry + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PATH_SELECTOR_EDIT_H +#define PATH_SELECTOR_EDIT_H + +#include +#include +#include +#include + +class PathSelectionEdit : public QWidget +{ + Q_OBJECT + +public: + PathSelectionEdit(QString title, QString path, bool selectFile, QWidget *parent = 0); + PathSelectionEdit(QWidget *parent = 0); + + QString path() const; + +public slots: + void setPath(QString newPath = QString()); + +signals: + void pathChanged(QString newPath); + +protected slots: + void browseForPath(); + +private: + QString _title; + QString _path; + bool _selectFile; + + QLineEdit * _edit; + QToolButton * _button; +}; + +#endif // PATH_SELECTOR_EDIT_H diff --git a/ui/qt/widgets/pref_module_view.cpp b/ui/qt/widgets/pref_module_view.cpp new file mode 100644 index 00000000..3a3a5451 --- /dev/null +++ b/ui/qt/widgets/pref_module_view.cpp @@ -0,0 +1,99 @@ +/* pref_module_view.cpp + * Tree view of preference module data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "pref_module_view.h" +#include + +#include + +PrefModuleTreeView::PrefModuleTreeView(QWidget *parent) : QTreeView(parent), + appearanceName_(PrefsModel::typeToString(PrefsModel::Appearance)) +{ +} + +void PrefModuleTreeView::setPane(const QString module_name) +{ + QModelIndex newIndex, modelIndex, appearanceIndex, protocolIndex, statIndex; + QString moduleName; + int row; + + //look for the pane name in the main tree before trying children + for (row = 0; row < model()->rowCount(); row++) + { + modelIndex = model()->index(row, ModulePrefsModel::colName); + moduleName = model()->data(modelIndex, ModulePrefsModel::ModuleName).toString(); + + if (moduleName.compare(appearanceName_) == 0) { + appearanceIndex = modelIndex; + } else if (moduleName.compare("Protocols") == 0) { + protocolIndex = modelIndex; + } else if (moduleName.compare("Statistics") == 0) { + statIndex = modelIndex; + } + + if (moduleName.compare(module_name) == 0) { + newIndex = modelIndex; + break; + } + } + + //Look through appearance children + if (!newIndex.isValid()) { + newIndex = findModule(appearanceIndex, module_name); + } + + //Look through protocol children + if (!newIndex.isValid()) { + newIndex = findModule(protocolIndex, module_name); + } + + //Look through stat children + if (!newIndex.isValid()) { + newIndex = findModule(statIndex, module_name); + } + + setCurrentIndex(newIndex); +} + +QModelIndex PrefModuleTreeView::findModule(QModelIndex& parent, const QString& name) +{ + QModelIndex findIndex, modelIndex; + QString module_name; + + for (int row = 0; row < model()->rowCount(parent); row++) + { + modelIndex = model()->index(row, ModulePrefsModel::colName, parent); + module_name = model()->data(modelIndex, ModulePrefsModel::ModuleName).toString(); + if (name.compare(module_name) == 0) { + findIndex = modelIndex; + break; + } + if (model()->rowCount(modelIndex) > 0) { + findIndex = findModule(modelIndex, name); + if (findIndex.isValid()) + break; + } + } + + return findIndex; +} + + +void PrefModuleTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.isValid()) + { + QString module_name = model()->data(current, ModulePrefsModel::ModuleName).toString(); + + emit goToPane(module_name); + } + + QTreeView::currentChanged(current, previous); +} diff --git a/ui/qt/widgets/pref_module_view.h b/ui/qt/widgets/pref_module_view.h new file mode 100644 index 00000000..2f4a715c --- /dev/null +++ b/ui/qt/widgets/pref_module_view.h @@ -0,0 +1,38 @@ +/** @file + * + * Tree view of preference module data. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PREFERENCE_MODULE_VIEW_H +#define PREFERENCE_MODULE_VIEW_H + +#include +#include + +class PrefModuleTreeView : public QTreeView +{ + Q_OBJECT +public: + PrefModuleTreeView(QWidget *parent = 0); + + void setPane(const QString module_name); + +signals: + void goToPane(QString module_name); + +protected slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +private: + QModelIndex findModule(QModelIndex &parent, const QString& name); + + //cache the translation of the module names we check frequently + QString appearanceName_; +}; +#endif // PREFERENCE_MODULE_VIEW_H diff --git a/ui/qt/widgets/profile_tree_view.cpp b/ui/qt/widgets/profile_tree_view.cpp new file mode 100644 index 00000000..afa86d71 --- /dev/null +++ b/ui/qt/widgets/profile_tree_view.cpp @@ -0,0 +1,119 @@ +/* profile_tree_view.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +ProfileUrlLinkDelegate::ProfileUrlLinkDelegate(QObject *parent) : UrlLinkDelegate (parent) {} + +void ProfileUrlLinkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + /* Only paint links for valid paths */ + if (index.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool()) + UrlLinkDelegate::paint(painter, option, index); + else + QStyledItemDelegate::paint(painter, option, index); + +} + +ProfileTreeEditDelegate::ProfileTreeEditDelegate(QWidget *parent) : QItemDelegate(parent), editor_(Q_NULLPTR) {} + +void ProfileTreeEditDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + if (qobject_cast(editor)) + { + QLineEdit * ql = qobject_cast(editor); + ql->setText(index.data().toString()); + } +} + +ProfileTreeView::ProfileTreeView(QWidget *parent) : + QTreeView (parent) +{ + delegate_ = new ProfileTreeEditDelegate(); + setItemDelegateForColumn(ProfileModel::COL_NAME, delegate_); + + connect(this, &QAbstractItemView::clicked, this, &ProfileTreeView::clicked); + connect(delegate_, &ProfileTreeEditDelegate::commitData, this, &ProfileTreeView::itemUpdated); +} + +ProfileTreeView::~ProfileTreeView() +{ + delete delegate_; +} + +void ProfileTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QTreeView::selectionChanged(selected, deselected); + + if (model()) + { + qsizetype offColumn = model()->columnCount(); + qsizetype idxCount = selectedIndexes().count() / offColumn; + qsizetype dselCount = deselected.count() > 0 ? deselected.at(0).indexes().count() / offColumn : 0; + + /* Ensure, that the last selected row cannot be deselected */ + if (idxCount == 0 && dselCount == 1) + { + QModelIndex idx = deselected.at(0).indexes().at(0); + /* If the last item is no longer valid or the row is out of bounds, select default */ + if (! idx.isValid() || idx.row() >= model()->rowCount()) + idx = model()->index(0, ProfileModel::COL_NAME); + selectRow(idx.row()); + } + else if (selectedIndexes().count() == 0) + selectRow(0); + } +} + +void ProfileTreeView::clicked(const QModelIndex &index) +{ + if (!index.isValid()) + return; + + /* Only paint links for valid paths */ + if (index.data(ProfileModel::DATA_INDEX_VALUE_IS_URL).toBool()) + { + QString path = QDir::toNativeSeparators(index.data().toString()); + QDesktopServices::openUrl(QUrl::fromLocalFile(path)); + } +} + +void ProfileTreeView::selectRow(int row) +{ + if (row < 0) + return; + + setCurrentIndex(model()->index(row, 0)); + + selectionModel()->select( + QItemSelection(model()->index(row, 0), model()->index(row, model()->columnCount() -1)), + QItemSelectionModel::ClearAndSelect); + +} + +void ProfileTreeView::mouseDoubleClickEvent(QMouseEvent *ev) +{ + /* due to the fact, that we allow only row selection, selected rows are always added with all columns */ + if (selectedIndexes().count() <= model()->columnCount()) + QTreeView::mouseDoubleClickEvent(ev); +} + +bool ProfileTreeView::activeEdit() +{ + return (state() == QAbstractItemView::EditingState); +} diff --git a/ui/qt/widgets/profile_tree_view.h b/ui/qt/widgets/profile_tree_view.h new file mode 100644 index 00000000..9684811e --- /dev/null +++ b/ui/qt/widgets/profile_tree_view.h @@ -0,0 +1,69 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PROFILE_TREEVIEW_H +#define PROFILE_TREEVIEW_H + +#include + +#include +#include + +class ProfileUrlLinkDelegate : public UrlLinkDelegate +{ + Q_OBJECT + +public: + explicit ProfileUrlLinkDelegate(QObject *parent = Q_NULLPTR); + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +class ProfileTreeEditDelegate : public QItemDelegate +{ + Q_OBJECT +public: + ProfileTreeEditDelegate(QWidget *parent = Q_NULLPTR); + + // QAbstractItemDelegate interface + virtual void setEditorData(QWidget *editor, const QModelIndex &index) const; + +private: + QWidget * editor_; + QModelIndex index_; +}; + +class ProfileTreeView : public QTreeView +{ + Q_OBJECT +public: + ProfileTreeView(QWidget *parent = nullptr); + ~ProfileTreeView(); + + void selectRow(int row); + bool activeEdit(); + +signals: + void itemUpdated(); + + // QWidget interface +protected: + virtual void mouseDoubleClickEvent(QMouseEvent *event); + + // QAbstractItemView interface +protected slots: + virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + virtual void clicked(const QModelIndex &index); + +private: + ProfileTreeEditDelegate *delegate_; + +}; + +#endif diff --git a/ui/qt/widgets/qcustomplot.cpp b/ui/qt/widgets/qcustomplot.cpp new file mode 100644 index 00000000..e686fce4 --- /dev/null +++ b/ui/qt/widgets/qcustomplot.cpp @@ -0,0 +1,35541 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011-2022 Emanuel Eichhammer ** +** ** +**************************************************************************** +** Author: Emanuel Eichhammer ** +** Website/Contact: https://www.qcustomplot.com/ ** +** Date: 06.11.22 ** +** Version: 2.1.1 ** +** ** +** Emanuel Eichhammer has granted Wireshark permission to use QCustomPlot ** +** under the terms of the GNU General Public License version 2. ** +** Date: 22.12.15 (V1.3.2) ** +** 13.09.19 (V2.0.1) ** +** ** +** SPDX-License-Identifier: GPL-2.0-or-later ** +****************************************************************************/ + +#include "qcustomplot.h" + + +/* including file 'src/vector2d.cpp' */ +/* modified 2022-11-06T12:45:56, size 7973 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPVector2D +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPVector2D + \brief Represents two doubles as a mathematical 2D vector + + This class acts as a replacement for QVector2D with the advantage of double precision instead of + single, and some convenience methods tailored for the QCustomPlot library. +*/ + +/* start documentation of inline functions */ + +/*! \fn void QCPVector2D::setX(double x) + + Sets the x coordinate of this vector to \a x. + + \see setY +*/ + +/*! \fn void QCPVector2D::setY(double y) + + Sets the y coordinate of this vector to \a y. + + \see setX +*/ + +/*! \fn double QCPVector2D::length() const + + Returns the length of this vector. + + \see lengthSquared +*/ + +/*! \fn double QCPVector2D::lengthSquared() const + + Returns the squared length of this vector. In some situations, e.g. when just trying to find the + shortest vector of a group, this is faster than calculating \ref length, because it avoids + calculation of a square root. + + \see length +*/ + +/*! \fn double QCPVector2D::angle() const + + Returns the angle of the vector in radians. The angle is measured between the positive x line and + the vector, counter-clockwise in a mathematical coordinate system (y axis upwards positive). In + screen/widget coordinates where the y axis is inverted, the angle appears clockwise. +*/ + +/*! \fn QPoint QCPVector2D::toPoint() const + + Returns a QPoint which has the x and y coordinates of this vector, truncating any floating point + information. + + \see toPointF +*/ + +/*! \fn QPointF QCPVector2D::toPointF() const + + Returns a QPointF which has the x and y coordinates of this vector. + + \see toPoint +*/ + +/*! \fn bool QCPVector2D::isNull() const + + Returns whether this vector is null. A vector is null if \c qIsNull returns true for both x and y + coordinates, i.e. if both are binary equal to 0. +*/ + +/*! \fn QCPVector2D QCPVector2D::perpendicular() const + + Returns a vector perpendicular to this vector, with the same length. +*/ + +/*! \fn double QCPVector2D::dot() const + + Returns the dot/scalar product of this vector with the specified vector \a vec. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a QCPVector2D object and initializes the x and y coordinates to 0. +*/ +QCPVector2D::QCPVector2D() : + mX(0), + mY(0) +{ +} + +/*! + Creates a QCPVector2D object and initializes the \a x and \a y coordinates with the specified + values. +*/ +QCPVector2D::QCPVector2D(double x, double y) : + mX(x), + mY(y) +{ +} + +/*! + Creates a QCPVector2D object and initializes the x and y coordinates respective coordinates of + the specified \a point. +*/ +QCPVector2D::QCPVector2D(const QPoint &point) : + mX(point.x()), + mY(point.y()) +{ +} + +/*! + Creates a QCPVector2D object and initializes the x and y coordinates respective coordinates of + the specified \a point. +*/ +QCPVector2D::QCPVector2D(const QPointF &point) : + mX(point.x()), + mY(point.y()) +{ +} + +/*! + Normalizes this vector. After this operation, the length of the vector is equal to 1. + + If the vector has both entries set to zero, this method does nothing. + + \see normalized, length, lengthSquared +*/ +void QCPVector2D::normalize() +{ + if (mX == 0.0 && mY == 0.0) return; + const double lenInv = 1.0/length(); + mX *= lenInv; + mY *= lenInv; +} + +/*! + Returns a normalized version of this vector. The length of the returned vector is equal to 1. + + If the vector has both entries set to zero, this method returns the vector unmodified. + + \see normalize, length, lengthSquared +*/ +QCPVector2D QCPVector2D::normalized() const +{ + if (mX == 0.0 && mY == 0.0) return *this; + const double lenInv = 1.0/length(); + return QCPVector2D(mX*lenInv, mY*lenInv); +} + +/*! \overload + + Returns the squared shortest distance of this vector (interpreted as a point) to the finite line + segment given by \a start and \a end. + + \see distanceToStraightLine +*/ +double QCPVector2D::distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const +{ + const QCPVector2D v(end-start); + const double vLengthSqr = v.lengthSquared(); + if (!qFuzzyIsNull(vLengthSqr)) + { + const double mu = v.dot(*this-start)/vLengthSqr; + if (mu < 0) + return (*this-start).lengthSquared(); + else if (mu > 1) + return (*this-end).lengthSquared(); + else + return ((start + mu*v)-*this).lengthSquared(); + } else + return (*this-start).lengthSquared(); +} + +/*! \overload + + Returns the squared shortest distance of this vector (interpreted as a point) to the finite line + segment given by \a line. + + \see distanceToStraightLine +*/ +double QCPVector2D::distanceSquaredToLine(const QLineF &line) const +{ + return distanceSquaredToLine(QCPVector2D(line.p1()), QCPVector2D(line.p2())); +} + +/*! + Returns the shortest distance of this vector (interpreted as a point) to the infinite straight + line given by a \a base point and a \a direction vector. + + \see distanceSquaredToLine +*/ +double QCPVector2D::distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const +{ + return qAbs((*this-base).dot(direction.perpendicular()))/direction.length(); +} + +/*! + Scales this vector by the given \a factor, i.e. the x and y components are multiplied by \a + factor. +*/ +QCPVector2D &QCPVector2D::operator*=(double factor) +{ + mX *= factor; + mY *= factor; + return *this; +} + +/*! + Scales this vector by the given \a divisor, i.e. the x and y components are divided by \a + divisor. +*/ +QCPVector2D &QCPVector2D::operator/=(double divisor) +{ + mX /= divisor; + mY /= divisor; + return *this; +} + +/*! + Adds the given \a vector to this vector component-wise. +*/ +QCPVector2D &QCPVector2D::operator+=(const QCPVector2D &vector) +{ + mX += vector.mX; + mY += vector.mY; + return *this; +} + +/*! + subtracts the given \a vector from this vector component-wise. +*/ +QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector) +{ + mX -= vector.mX; + mY -= vector.mY; + return *this; +} +/* end of 'src/vector2d.cpp' */ + + +/* including file 'src/painter.cpp' */ +/* modified 2022-11-06T12:45:56, size 8656 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPainter +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPainter + \brief QPainter subclass used internally + + This QPainter subclass is used to provide some extended functionality e.g. for tweaking position + consistency between antialiased and non-antialiased painting. Further it provides workarounds + for QPainter quirks. + + \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and + restore. So while it is possible to pass a QCPPainter instance to a function that expects a + QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because + it will call the base class implementations of the functions actually hidden by QCPPainter). +*/ + +/*! + Creates a new QCPPainter instance and sets default values +*/ +QCPPainter::QCPPainter() : + mModes(pmDefault), + mIsAntialiasing(false) +{ + // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and + // a call to begin() will follow +} + +/*! + Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just + like the analogous QPainter constructor, begins painting on \a device immediately. + + Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt versions before Qt5. +*/ +QCPPainter::QCPPainter(QPaintDevice *device) : + QPainter(device), + mModes(pmDefault), + mIsAntialiasing(false) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions. + if (isActive()) + setRenderHint(QPainter::NonCosmeticDefaultPen); +#endif +} + +/*! + Sets the pen of the painter and applies certain fixes to it, depending on the mode of this + QCPPainter. + + \note this function hides the non-virtual base class implementation. +*/ +void QCPPainter::setPen(const QPen &pen) +{ + QPainter::setPen(pen); + if (mModes.testFlag(pmNonCosmetic)) + makeNonCosmetic(); +} + +/*! \overload + + Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of + this QCPPainter. + + \note this function hides the non-virtual base class implementation. +*/ +void QCPPainter::setPen(const QColor &color) +{ + QPainter::setPen(color); + if (mModes.testFlag(pmNonCosmetic)) + makeNonCosmetic(); +} + +/*! \overload + + Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of + this QCPPainter. + + \note this function hides the non-virtual base class implementation. +*/ +void QCPPainter::setPen(Qt::PenStyle penStyle) +{ + QPainter::setPen(penStyle); + if (mModes.testFlag(pmNonCosmetic)) + makeNonCosmetic(); +} + +/*! \overload + + Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when + antialiasing is disabled. Thus when antialiasing is disabled, it rounds the \a line to + integer coordinates and then passes it to the original drawLine. + + \note this function hides the non-virtual base class implementation. +*/ +void QCPPainter::drawLine(const QLineF &line) +{ + if (mIsAntialiasing || mModes.testFlag(pmVectorized)) + QPainter::drawLine(line); + else + QPainter::drawLine(line.toLine()); +} + +/*! + Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint + with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between + antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for + AA/Non-AA painting). +*/ +void QCPPainter::setAntialiasing(bool enabled) +{ + setRenderHint(QPainter::Antialiasing, enabled); + if (mIsAntialiasing != enabled) + { + mIsAntialiasing = enabled; + if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs + { + if (mIsAntialiasing) + translate(0.5, 0.5); + else + translate(-0.5, -0.5); + } + } +} + +/*! + Sets the mode of the painter. This controls whether the painter shall adjust its + fixes/workarounds optimized for certain output devices. +*/ +void QCPPainter::setModes(QCPPainter::PainterModes modes) +{ + mModes = modes; +} + +/*! + Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on \a + device. This is necessary to get cosmetic pen consistency across Qt versions, because since Qt5, + all pens are non-cosmetic by default, and in Qt4 this render hint must be set to get that + behaviour. + + The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts painting also sets + the render hint as appropriate. + + \note this function hides the non-virtual base class implementation. +*/ +bool QCPPainter::begin(QPaintDevice *device) +{ + bool result = QPainter::begin(device); +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions. + if (result) + setRenderHint(QPainter::NonCosmeticDefaultPen); +#endif + return result; +} + +/*! \overload + + Sets the mode of the painter. This controls whether the painter shall adjust its + fixes/workarounds optimized for certain output devices. +*/ +void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled) +{ + if (!enabled && mModes.testFlag(mode)) + mModes &= ~mode; + else if (enabled && !mModes.testFlag(mode)) + mModes |= mode; +} + +/*! + Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to + QPainter, the save/restore functions are reimplemented to also save/restore those members. + + \note this function hides the non-virtual base class implementation. + + \see restore +*/ +void QCPPainter::save() +{ + mAntialiasingStack.push(mIsAntialiasing); + QPainter::save(); +} + +/*! + Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to + QPainter, the save/restore functions are reimplemented to also save/restore those members. + + \note this function hides the non-virtual base class implementation. + + \see save +*/ +void QCPPainter::restore() +{ + if (!mAntialiasingStack.isEmpty()) + mIsAntialiasing = mAntialiasingStack.pop(); + else + qDebug() << Q_FUNC_INFO << "Unbalanced save/restore"; + QPainter::restore(); +} + +/*! + Changes the pen width to 1 if it currently is 0. This function is called in the \ref setPen + overrides when the \ref pmNonCosmetic mode is set. +*/ +void QCPPainter::makeNonCosmetic() +{ + if (qFuzzyIsNull(pen().widthF())) + { + QPen p = pen(); + p.setWidth(1); + QPainter::setPen(p); + } +} +/* end of 'src/painter.cpp' */ + + +/* including file 'src/paintbuffer.cpp' */ +/* modified 2022-11-06T12:45:56, size 18915 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractPaintBuffer +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractPaintBuffer + \brief The abstract base class for paint buffers, which define the rendering backend + + This abstract base class defines the basic interface that a paint buffer needs to provide in + order to be usable by QCustomPlot. + + A paint buffer manages both a surface to draw onto, and the matching paint device. The size of + the surface can be changed via \ref setSize. External classes (\ref QCustomPlot and \ref + QCPLayer) request a painter via \ref startPainting and then perform the draw calls. Once the + painting is complete, \ref donePainting is called, so the paint buffer implementation can do + clean up if necessary. Before rendering a frame, each paint buffer is usually filled with a color + using \ref clear (usually the color is \c Qt::transparent), to remove the contents of the + previous frame. + + The simplest paint buffer implementation is \ref QCPPaintBufferPixmap which allows regular + software rendering via the raster engine. Hardware accelerated rendering via pixel buffers and + frame buffer objects is provided by \ref QCPPaintBufferGlPbuffer and \ref QCPPaintBufferGlFbo. + They are used automatically if \ref QCustomPlot::setOpenGl is enabled. +*/ + +/* start documentation of pure virtual functions */ + +/*! \fn virtual QCPPainter *QCPAbstractPaintBuffer::startPainting() = 0 + + Returns a \ref QCPPainter which is ready to draw to this buffer. The ownership and thus the + responsibility to delete the painter after the painting operations are complete is given to the + caller of this method. + + Once you are done using the painter, delete the painter and call \ref donePainting. + + While a painter generated with this method is active, you must not call \ref setSize, \ref + setDevicePixelRatio or \ref clear. + + This method may return 0, if a painter couldn't be activated on the buffer. This usually + indicates a problem with the respective painting backend. +*/ + +/*! \fn virtual void QCPAbstractPaintBuffer::draw(QCPPainter *painter) const = 0 + + Draws the contents of this buffer with the provided \a painter. This is the method that is used + to finally join all paint buffers and draw them onto the screen. +*/ + +/*! \fn virtual void QCPAbstractPaintBuffer::clear(const QColor &color) = 0 + + Fills the entire buffer with the provided \a color. To have an empty transparent buffer, use the + named color \c Qt::transparent. + + This method must not be called if there is currently a painter (acquired with \ref startPainting) + active. +*/ + +/*! \fn virtual void QCPAbstractPaintBuffer::reallocateBuffer() = 0 + + Reallocates the internal buffer with the currently configured size (\ref setSize) and device + pixel ratio, if applicable (\ref setDevicePixelRatio). It is called as soon as any of those + properties are changed on this paint buffer. + + \note Subclasses of \ref QCPAbstractPaintBuffer must call their reimplementation of this method + in their constructor, to perform the first allocation (this can not be done by the base class + because calling pure virtual methods in base class constructors is not possible). +*/ + +/* end documentation of pure virtual functions */ +/* start documentation of inline functions */ + +/*! \fn virtual void QCPAbstractPaintBuffer::donePainting() + + If you have acquired a \ref QCPPainter to paint onto this paint buffer via \ref startPainting, + call this method as soon as you are done with the painting operations and have deleted the + painter. + + paint buffer subclasses may use this method to perform any type of cleanup that is necessary. The + default implementation does nothing. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a paint buffer and initializes it with the provided \a size and \a devicePixelRatio. + + Subclasses must call their \ref reallocateBuffer implementation in their respective constructors. +*/ +QCPAbstractPaintBuffer::QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio) : + mSize(size), + mDevicePixelRatio(devicePixelRatio), + mInvalidated(true) +{ +} + +QCPAbstractPaintBuffer::~QCPAbstractPaintBuffer() +{ +} + +/*! + Sets the paint buffer size. + + The buffer is reallocated (by calling \ref reallocateBuffer), so any painters that were obtained + by \ref startPainting are invalidated and must not be used after calling this method. + + If \a size is already the current buffer size, this method does nothing. +*/ +void QCPAbstractPaintBuffer::setSize(const QSize &size) +{ + if (mSize != size) + { + mSize = size; + reallocateBuffer(); + } +} + +/*! + Sets the invalidated flag to \a invalidated. + + This mechanism is used internally in conjunction with isolated replotting of \ref QCPLayer + instances (in \ref QCPLayer::lmBuffered mode). If \ref QCPLayer::replot is called on a buffered + layer, i.e. an isolated repaint of only that layer (and its dedicated paint buffer) is requested, + QCustomPlot will decide depending on the invalidated flags of other paint buffers whether it also + replots them, instead of only the layer on which the replot was called. + + The invalidated flag is set to true when \ref QCPLayer association has changed, i.e. if layers + were added or removed from this buffer, or if they were reordered. It is set to false as soon as + all associated \ref QCPLayer instances are drawn onto the buffer. + + Under normal circumstances, it is not necessary to manually call this method. +*/ +void QCPAbstractPaintBuffer::setInvalidated(bool invalidated) +{ + mInvalidated = invalidated; +} + +/*! + Sets the device pixel ratio to \a ratio. This is useful to render on high-DPI output devices. + The ratio is automatically set to the device pixel ratio used by the parent QCustomPlot instance. + + The buffer is reallocated (by calling \ref reallocateBuffer), so any painters that were obtained + by \ref startPainting are invalidated and must not be used after calling this method. + + \note This method is only available for Qt versions 5.4 and higher. +*/ +void QCPAbstractPaintBuffer::setDevicePixelRatio(double ratio) +{ + if (!qFuzzyCompare(ratio, mDevicePixelRatio)) + { +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED + mDevicePixelRatio = ratio; + reallocateBuffer(); +#else + qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4"; + mDevicePixelRatio = 1.0; +#endif + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPaintBufferPixmap +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPaintBufferPixmap + \brief A paint buffer based on QPixmap, using software raster rendering + + This paint buffer is the default and fall-back paint buffer which uses software rendering and + QPixmap as internal buffer. It is used if \ref QCustomPlot::setOpenGl is false. +*/ + +/*! + Creates a pixmap paint buffer instancen with the specified \a size and \a devicePixelRatio, if + applicable. +*/ +QCPPaintBufferPixmap::QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio) : + QCPAbstractPaintBuffer(size, devicePixelRatio) +{ + QCPPaintBufferPixmap::reallocateBuffer(); +} + +QCPPaintBufferPixmap::~QCPPaintBufferPixmap() +{ +} + +/* inherits documentation from base class */ +QCPPainter *QCPPaintBufferPixmap::startPainting() +{ + QCPPainter *result = new QCPPainter(&mBuffer); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + result->setRenderHint(QPainter::Antialiasing); +#endif + return result; +} + +/* inherits documentation from base class */ +void QCPPaintBufferPixmap::draw(QCPPainter *painter) const +{ + if (painter && painter->isActive()) + painter->drawPixmap(0, 0, mBuffer); + else + qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed"; +} + +/* inherits documentation from base class */ +void QCPPaintBufferPixmap::clear(const QColor &color) +{ + mBuffer.fill(color); +} + +/* inherits documentation from base class */ +void QCPPaintBufferPixmap::reallocateBuffer() +{ + setInvalidated(); + if (!qFuzzyCompare(1.0, mDevicePixelRatio)) + { +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED + mBuffer = QPixmap(mSize*mDevicePixelRatio); + mBuffer.setDevicePixelRatio(mDevicePixelRatio); +#else + qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4"; + mDevicePixelRatio = 1.0; + mBuffer = QPixmap(mSize); +#endif + } else + { + mBuffer = QPixmap(mSize); + } +} + + +#ifdef QCP_OPENGL_PBUFFER +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPaintBufferGlPbuffer +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPaintBufferGlPbuffer + \brief A paint buffer based on OpenGL pixel buffers, using hardware accelerated rendering + + This paint buffer is one of the OpenGL paint buffers which facilitate hardware accelerated plot + rendering. It is based on OpenGL pixel buffers (pbuffer) and is used in Qt versions before 5.0. + (See \ref QCPPaintBufferGlFbo used in newer Qt versions.) + + The OpenGL paint buffers are used if \ref QCustomPlot::setOpenGl is set to true, and if they are + supported by the system. +*/ + +/*! + Creates a \ref QCPPaintBufferGlPbuffer instance with the specified \a size and \a + devicePixelRatio, if applicable. + + The parameter \a multisamples defines how many samples are used per pixel. Higher values thus + result in higher quality antialiasing. If the specified \a multisamples value exceeds the + capability of the graphics hardware, the highest supported multisampling is used. +*/ +QCPPaintBufferGlPbuffer::QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples) : + QCPAbstractPaintBuffer(size, devicePixelRatio), + mGlPBuffer(0), + mMultisamples(qMax(0, multisamples)) +{ + QCPPaintBufferGlPbuffer::reallocateBuffer(); +} + +QCPPaintBufferGlPbuffer::~QCPPaintBufferGlPbuffer() +{ + if (mGlPBuffer) + delete mGlPBuffer; +} + +/* inherits documentation from base class */ +QCPPainter *QCPPaintBufferGlPbuffer::startPainting() +{ + if (!mGlPBuffer->isValid()) + { + qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?"; + return 0; + } + + QCPPainter *result = new QCPPainter(mGlPBuffer); + result->setRenderHint(QPainter::Antialiasing); + return result; +} + +/* inherits documentation from base class */ +void QCPPaintBufferGlPbuffer::draw(QCPPainter *painter) const +{ + if (!painter || !painter->isActive()) + { + qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed"; + return; + } + if (!mGlPBuffer->isValid()) + { + qDebug() << Q_FUNC_INFO << "OpenGL pbuffer isn't valid, reallocateBuffer was not called?"; + return; + } + painter->drawImage(0, 0, mGlPBuffer->toImage()); +} + +/* inherits documentation from base class */ +void QCPPaintBufferGlPbuffer::clear(const QColor &color) +{ + if (mGlPBuffer->isValid()) + { + mGlPBuffer->makeCurrent(); + glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + mGlPBuffer->doneCurrent(); + } else + qDebug() << Q_FUNC_INFO << "OpenGL pbuffer invalid or context not current"; +} + +/* inherits documentation from base class */ +void QCPPaintBufferGlPbuffer::reallocateBuffer() +{ + if (mGlPBuffer) + delete mGlPBuffer; + + QGLFormat format; + format.setAlpha(true); + format.setSamples(mMultisamples); + mGlPBuffer = new QGLPixelBuffer(mSize, format); +} +#endif // QCP_OPENGL_PBUFFER + + +#ifdef QCP_OPENGL_FBO +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPaintBufferGlFbo +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPaintBufferGlFbo + \brief A paint buffer based on OpenGL frame buffers objects, using hardware accelerated rendering + + This paint buffer is one of the OpenGL paint buffers which facilitate hardware accelerated plot + rendering. It is based on OpenGL frame buffer objects (fbo) and is used in Qt versions 5.0 and + higher. (See \ref QCPPaintBufferGlPbuffer used in older Qt versions.) + + The OpenGL paint buffers are used if \ref QCustomPlot::setOpenGl is set to true, and if they are + supported by the system. +*/ + +/*! + Creates a \ref QCPPaintBufferGlFbo instance with the specified \a size and \a devicePixelRatio, + if applicable. + + All frame buffer objects shall share one OpenGL context and paint device, which need to be set up + externally and passed via \a glContext and \a glPaintDevice. The set-up is done in \ref + QCustomPlot::setupOpenGl and the context and paint device are managed by the parent QCustomPlot + instance. +*/ +QCPPaintBufferGlFbo::QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer glContext, QWeakPointer glPaintDevice) : + QCPAbstractPaintBuffer(size, devicePixelRatio), + mGlContext(glContext), + mGlPaintDevice(glPaintDevice), + mGlFrameBuffer(0) +{ + QCPPaintBufferGlFbo::reallocateBuffer(); +} + +QCPPaintBufferGlFbo::~QCPPaintBufferGlFbo() +{ + if (mGlFrameBuffer) + delete mGlFrameBuffer; +} + +/* inherits documentation from base class */ +QCPPainter *QCPPaintBufferGlFbo::startPainting() +{ + QSharedPointer paintDevice = mGlPaintDevice.toStrongRef(); + QSharedPointer context = mGlContext.toStrongRef(); + if (!paintDevice) + { + qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist"; + return 0; + } + if (!context) + { + qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist"; + return 0; + } + if (!mGlFrameBuffer) + { + qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?"; + return 0; + } + + if (QOpenGLContext::currentContext() != context.data()) + context->makeCurrent(context->surface()); + mGlFrameBuffer->bind(); + QCPPainter *result = new QCPPainter(paintDevice.data()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + result->setRenderHint(QPainter::Antialiasing); +#endif + return result; +} + +/* inherits documentation from base class */ +void QCPPaintBufferGlFbo::donePainting() +{ + if (mGlFrameBuffer && mGlFrameBuffer->isBound()) + mGlFrameBuffer->release(); + else + qDebug() << Q_FUNC_INFO << "Either OpenGL frame buffer not valid or was not bound"; +} + +/* inherits documentation from base class */ +void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const +{ + if (!painter || !painter->isActive()) + { + qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed"; + return; + } + if (!mGlFrameBuffer) + { + qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?"; + return; + } + painter->drawImage(0, 0, mGlFrameBuffer->toImage()); +} + +/* inherits documentation from base class */ +void QCPPaintBufferGlFbo::clear(const QColor &color) +{ + QSharedPointer context = mGlContext.toStrongRef(); + if (!context) + { + qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist"; + return; + } + if (!mGlFrameBuffer) + { + qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?"; + return; + } + + if (QOpenGLContext::currentContext() != context.data()) + context->makeCurrent(context->surface()); + mGlFrameBuffer->bind(); + glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + mGlFrameBuffer->release(); +} + +/* inherits documentation from base class */ +void QCPPaintBufferGlFbo::reallocateBuffer() +{ + // release and delete possibly existing framebuffer: + if (mGlFrameBuffer) + { + if (mGlFrameBuffer->isBound()) + mGlFrameBuffer->release(); + delete mGlFrameBuffer; + mGlFrameBuffer = 0; + } + + QSharedPointer paintDevice = mGlPaintDevice.toStrongRef(); + QSharedPointer context = mGlContext.toStrongRef(); + if (!paintDevice) + { + qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist"; + return; + } + if (!context) + { + qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist"; + return; + } + + // create new fbo with appropriate size: + context->makeCurrent(context->surface()); + QOpenGLFramebufferObjectFormat frameBufferFormat; + frameBufferFormat.setSamples(context->format().samples()); + frameBufferFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + mGlFrameBuffer = new QOpenGLFramebufferObject(mSize*mDevicePixelRatio, frameBufferFormat); + if (paintDevice->size() != mSize*mDevicePixelRatio) + paintDevice->setSize(mSize*mDevicePixelRatio); +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED + paintDevice->setDevicePixelRatio(mDevicePixelRatio); +#endif +} +#endif // QCP_OPENGL_FBO +/* end of 'src/paintbuffer.cpp' */ + + +/* including file 'src/layer.cpp' */ +/* modified 2022-11-06T12:45:56, size 37615 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayer +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayer + \brief A layer that may contain objects, to control the rendering order + + The Layering system of QCustomPlot is the mechanism to control the rendering order of the + elements inside the plot. + + It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an ordered list of + one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer, + QCustomPlot::moveLayer, etc.). When replotting, QCustomPlot goes through the list of layers + bottom to top and successively draws the layerables of the layers into the paint buffer(s). + + A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract base + class from which almost all visible objects derive, like axes, grids, graphs, items, etc. + + \section qcplayer-defaultlayers Default layers + + Initially, QCustomPlot has six layers: "background", "grid", "main", "axes", "legend" and + "overlay" (in that order). On top is the "overlay" layer, which only contains the QCustomPlot's + selection rect (\ref QCustomPlot::selectionRect). The next two layers "axes" and "legend" contain + the default axes and legend, so they will be drawn above plottables. In the middle, there is the + "main" layer. It is initially empty and set as the current layer (see + QCustomPlot::setCurrentLayer). This means, all new plottables, items etc. are created on this + layer by default. Then comes the "grid" layer which contains the QCPGrid instances (which belong + tightly to QCPAxis, see \ref QCPAxis::grid). The Axis rect background shall be drawn behind + everything else, thus the default QCPAxisRect instance is placed on the "background" layer. Of + course, the layer affiliation of the individual objects can be changed as required (\ref + QCPLayerable::setLayer). + + \section qcplayer-ordering Controlling the rendering order via layers + + Controlling the ordering of layerables in the plot is easy: Create a new layer in the position + you want the layerable to be in, e.g. above "main", with \ref QCustomPlot::addLayer. Then set the + current layer with \ref QCustomPlot::setCurrentLayer to that new layer and finally create the + objects normally. They will be placed on the new layer automatically, due to the current layer + setting. Alternatively you could have also ignored the current layer setting and just moved the + objects with \ref QCPLayerable::setLayer to the desired layer after creating them. + + It is also possible to move whole layers. For example, If you want the grid to be shown in front + of all plottables/items on the "main" layer, just move it above "main" with + QCustomPlot::moveLayer. + + The rendering order within one layer is simply by order of creation or insertion. The item + created last (or added last to the layer), is drawn on top of all other objects on that layer. + + When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below + the deleted layer, see QCustomPlot::removeLayer. + + \section qcplayer-buffering Replotting only a specific layer + + If the layer mode (\ref setMode) is set to \ref lmBuffered, you can replot only this specific + layer by calling \ref replot. In certain situations this can provide better replot performance, + compared with a full replot of all layers. Upon creation of a new layer, the layer mode is + initialized to \ref lmLogical. The only layer that is set to \ref lmBuffered in a new \ref + QCustomPlot instance is the "overlay" layer, containing the selection rect. +*/ + +/* start documentation of inline functions */ + +/*! \fn QList QCPLayer::children() const + + Returns a list of all layerables on this layer. The order corresponds to the rendering order: + layerables with higher indices are drawn above layerables with lower indices. +*/ + +/*! \fn int QCPLayer::index() const + + Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be + accessed via \ref QCustomPlot::layer. + + Layers with higher indices will be drawn above layers with lower indices. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPLayer instance. + + Normally you shouldn't directly instantiate layers, use \ref QCustomPlot::addLayer instead. + + \warning It is not checked that \a layerName is actually a unique layer name in \a parentPlot. + This check is only performed by \ref QCustomPlot::addLayer. +*/ +QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) : + QObject(parentPlot), + mParentPlot(parentPlot), + mName(layerName), + mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function + mVisible(true), + mMode(lmLogical) +{ + // Note: no need to make sure layerName is unique, because layer + // management is done with QCustomPlot functions. +} + +QCPLayer::~QCPLayer() +{ + // If child layerables are still on this layer, detach them, so they don't try to reach back to this + // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted + // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to + // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.) + + while (!mChildren.isEmpty()) + mChildren.last()->setLayer(nullptr); // removes itself from mChildren via removeChild() + + if (mParentPlot->currentLayer() == this) + qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or nullptr beforehand."; +} + +/*! + Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this + layer will be invisible. + + This function doesn't change the visibility property of the layerables (\ref + QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the + visibility of the parent layer into account. +*/ +void QCPLayer::setVisible(bool visible) +{ + mVisible = visible; +} + +/*! + Sets the rendering mode of this layer. + + If \a mode is set to \ref lmBuffered for a layer, it will be given a dedicated paint buffer by + the parent QCustomPlot instance. This means it may be replotted individually by calling \ref + QCPLayer::replot, without needing to replot all other layers. + + Layers which are set to \ref lmLogical (the default) are used only to define the rendering order + and can't be replotted individually. + + Note that each layer which is set to \ref lmBuffered requires additional paint buffers for the + layers below, above and for the layer itself. This increases the memory consumption and + (slightly) decreases the repainting speed because multiple paint buffers need to be joined. So + you should carefully choose which layers benefit from having their own paint buffer. A typical + example would be a layer which contains certain layerables (e.g. items) that need to be changed + and thus replotted regularly, while all other layerables on other layers stay static. By default, + only the topmost layer called "overlay" is in mode \ref lmBuffered, and contains the selection + rect. + + \see replot +*/ +void QCPLayer::setMode(QCPLayer::LayerMode mode) +{ + if (mMode != mode) + { + mMode = mode; + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) + pb->setInvalidated(); + } +} + +/*! \internal + + Draws the contents of this layer with the provided \a painter. + + \see replot, drawToPaintBuffer +*/ +void QCPLayer::draw(QCPPainter *painter) +{ + foreach (QCPLayerable *child, mChildren) + { + if (child->realVisibility()) + { + painter->save(); + painter->setClipRect(child->clipRect().translated(0, -1)); + child->applyDefaultAntialiasingHint(painter); + child->draw(painter); + painter->restore(); + } + } +} + +/*! \internal + + Draws the contents of this layer into the paint buffer which is associated with this layer. The + association is established by the parent QCustomPlot, which manages all paint buffers (see \ref + QCustomPlot::setupPaintBuffers). + + \see draw +*/ +void QCPLayer::drawToPaintBuffer() +{ + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) + { + if (QCPPainter *painter = pb->startPainting()) + { + if (painter->isActive()) + draw(painter); + else + qDebug() << Q_FUNC_INFO << "paint buffer returned inactive painter"; + delete painter; + pb->donePainting(); + } else + qDebug() << Q_FUNC_INFO << "paint buffer returned nullptr painter"; + } else + qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer"; +} + +/*! + If the layer mode (\ref setMode) is set to \ref lmBuffered, this method allows replotting only + the layerables on this specific layer, without the need to replot all other layers (as a call to + \ref QCustomPlot::replot would do). + + QCustomPlot also makes sure to replot all layers instead of only this one, if the layer ordering + or any layerable-layer-association has changed since the last full replot and any other paint + buffers were thus invalidated. + + If the layer mode is \ref lmLogical however, this method simply calls \ref QCustomPlot::replot on + the parent QCustomPlot instance. + + \see draw +*/ +void QCPLayer::replot() +{ + if (mMode == lmBuffered && !mParentPlot->hasInvalidatedPaintBuffers()) + { + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) + { + pb->clear(Qt::transparent); + drawToPaintBuffer(); + pb->setInvalidated(false); // since layer is lmBuffered, we know only this layer is on buffer and we can reset invalidated flag + mParentPlot->update(); + } else + qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer"; + } else + mParentPlot->replot(); +} + +/*! \internal + + Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will + be prepended to the list, i.e. be drawn beneath the other layerables already in the list. + + This function does not change the \a mLayer member of \a layerable to this layer. (Use + QCPLayerable::setLayer to change the layer of an object, not this function.) + + \see removeChild +*/ +void QCPLayer::addChild(QCPLayerable *layerable, bool prepend) +{ + if (!mChildren.contains(layerable)) + { + if (prepend) + mChildren.prepend(layerable); + else + mChildren.append(layerable); + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) + pb->setInvalidated(); + } else + qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast(layerable); +} + +/*! \internal + + Removes the \a layerable from the list of this layer. + + This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer + to change the layer of an object, not this function.) + + \see addChild +*/ +void QCPLayer::removeChild(QCPLayerable *layerable) +{ + if (mChildren.removeOne(layerable)) + { + if (QSharedPointer pb = mPaintBuffer.toStrongRef()) + pb->setInvalidated(); + } else + qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast(layerable); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayerable +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayerable + \brief Base class for all drawable objects + + This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid + etc. + + Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking + the layers accordingly. + + For details about the layering mechanism, see the QCPLayer documentation. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPLayerable *QCPLayerable::parentLayerable() const + + Returns the parent layerable of this layerable. The parent layerable is used to provide + visibility hierarchies in conjunction with the method \ref realVisibility. This way, layerables + only get drawn if their parent layerables are visible, too. + + Note that a parent layerable is not necessarily also the QObject parent for memory management. + Further, a layerable doesn't always have a parent layerable, so this function may return \c + nullptr. + + A parent layerable is set implicitly when placed inside layout elements and doesn't need to be + set manually by the user. +*/ + +/* end documentation of inline functions */ +/* start documentation of pure virtual functions */ + +/*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0 + \internal + + This function applies the default antialiasing setting to the specified \a painter, using the + function \ref applyAntialiasingHint. It is the antialiasing state the painter is put in, when + \ref draw is called on the layerable. If the layerable has multiple entities whose antialiasing + setting may be specified individually, this function should set the antialiasing state of the + most prominent entity. In this case however, the \ref draw function usually calls the specialized + versions of this function before drawing each entity, effectively overriding the setting of the + default antialiasing hint. + + First example: QCPGraph has multiple entities that have an antialiasing setting: The graph + line, fills and scatters. Those can be configured via QCPGraph::setAntialiased, + QCPGraph::setAntialiasedFill and QCPGraph::setAntialiasedScatters. Consequently, there isn't only + the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's + antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and + QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw + calls the respective specialized applyAntialiasingHint function. + + Second example: QCPItemLine consists only of a line so there is only one antialiasing + setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by + all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the + respective layerable subclass.) Consequently it only has the normal + QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to + care about setting any antialiasing states, because the default antialiasing hint is already set + on the painter when the \ref draw function is called, and that's the state it wants to draw the + line with. +*/ + +/*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0 + \internal + + This function draws the layerable with the specified \a painter. It is only called by + QCustomPlot, if the layerable is visible (\ref setVisible). + + Before this function is called, the painter's antialiasing state is set via \ref + applyDefaultAntialiasingHint, see the documentation there. Further, the clipping rectangle was + set to \ref clipRect. +*/ + +/* end documentation of pure virtual functions */ +/* start documentation of signals */ + +/*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer); + + This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to + a different layer. + + \see setLayer +*/ + +/* end documentation of signals */ + +/*! + Creates a new QCPLayerable instance. + + Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the + derived classes. + + If \a plot is provided, it automatically places itself on the layer named \a targetLayer. If \a + targetLayer is an empty string, it places itself on the current layer of the plot (see \ref + QCustomPlot::setCurrentLayer). + + It is possible to provide \c nullptr as \a plot. In that case, you should assign a parent plot at + a later time with \ref initializeParentPlot. + + The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable + parents are mainly used to control visibility in a hierarchy of layerables. This means a + layerable is only drawn, if all its ancestor layerables are also visible. Note that \a + parentLayerable does not become the QObject-parent (for memory management) of this layerable, \a + plot does. It is not uncommon to set the QObject-parent to something else in the constructors of + QCPLayerable subclasses, to guarantee a working destruction hierarchy. +*/ +QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) : + QObject(plot), + mVisible(true), + mParentPlot(plot), + mParentLayerable(parentLayerable), + mLayer(nullptr), + mAntialiased(true) +{ + if (mParentPlot) + { + if (targetLayer.isEmpty()) + setLayer(mParentPlot->currentLayer()); + else if (!setLayer(targetLayer)) + qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed."; + } +} + +QCPLayerable::~QCPLayerable() +{ + if (mLayer) + { + mLayer->removeChild(this); + mLayer = nullptr; + } +} + +/*! + Sets the visibility of this layerable object. If an object is not visible, it will not be drawn + on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not + possible. +*/ +void QCPLayerable::setVisible(bool on) +{ + mVisible = on; +} + +/*! + Sets the \a layer of this layerable object. The object will be placed on top of the other objects + already on \a layer. + + If \a layer is 0, this layerable will not be on any layer and thus not appear in the plot (or + interact/receive events). + + Returns true if the layer of this layerable was successfully changed to \a layer. +*/ +bool QCPLayerable::setLayer(QCPLayer *layer) +{ + return moveToLayer(layer, false); +} + +/*! \overload + Sets the layer of this layerable object by name + + Returns true on success, i.e. if \a layerName is a valid layer name. +*/ +bool QCPLayerable::setLayer(const QString &layerName) +{ + if (!mParentPlot) + { + qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set"; + return false; + } + if (QCPLayer *layer = mParentPlot->layer(layerName)) + { + return setLayer(layer); + } else + { + qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName; + return false; + } +} + +/*! + Sets whether this object will be drawn antialiased or not. + + Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPLayerable::setAntialiased(bool enabled) +{ + mAntialiased = enabled; +} + +/*! + Returns whether this layerable is visible, taking the visibility of the layerable parent and the + visibility of this layerable's layer into account. This is the method that is consulted to decide + whether a layerable shall be drawn or not. + + If this layerable has a direct layerable parent (usually set via hierarchies implemented in + subclasses, like in the case of \ref QCPLayoutElement), this function returns true only if this + layerable has its visibility set to true and the parent layerable's \ref realVisibility returns + true. +*/ +bool QCPLayerable::realVisibility() const +{ + return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility()); +} + +/*! + This function is used to decide whether a click hits a layerable object or not. + + \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the + shortest pixel distance of this point to the object. If the object is either invisible or the + distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the + object is not selectable, -1.0 is returned, too. + + If the object is represented not by single lines but by an area like a \ref QCPItemText or the + bars of a \ref QCPBars plottable, a click inside the area should also be considered a hit. In + these cases this function thus returns a constant value greater zero but still below the parent + plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99). + + Providing a constant value for area objects allows selecting line objects even when they are + obscured by such area objects, by clicking close to the lines (i.e. closer than + 0.99*selectionTolerance). + + The actual setting of the selection state is not done by this function. This is handled by the + parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified + via the \ref selectEvent/\ref deselectEvent methods. + + \a details is an optional output parameter. Every layerable subclass may place any information + in \a details. This information will be passed to \ref selectEvent when the parent QCustomPlot + decides on the basis of this selectTest call, that the object was successfully selected. The + subsequent call to \ref selectEvent will carry the \a details. This is useful for multi-part + objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked + is only done once in \ref selectTest. The result (i.e. the actually clicked part) can then be + placed in \a details. So in the subsequent \ref selectEvent, the decision which part was + selected doesn't have to be done a second time for a single selection operation. + + In the case of 1D Plottables (\ref QCPAbstractPlottable1D, like \ref QCPGraph or \ref QCPBars) \a + details will be set to a \ref QCPDataSelection, describing the closest data point to \a pos. + + You may pass \c nullptr as \a details to indicate that you are not interested in those selection + details. + + \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions, + QCPAbstractPlottable1D::selectTestRect +*/ +double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(pos) + Q_UNUSED(onlySelectable) + Q_UNUSED(details) + return -1.0; +} + +/*! \internal + + Sets the parent plot of this layerable. Use this function once to set the parent plot if you have + passed \c nullptr in the constructor. It can not be used to move a layerable from one QCustomPlot + to another one. + + Note that, unlike when passing a non \c nullptr parent plot in the constructor, this function + does not make \a parentPlot the QObject-parent of this layerable. If you want this, call + QObject::setParent(\a parentPlot) in addition to this function. + + Further, you will probably want to set a layer (\ref setLayer) after calling this function, to + make the layerable appear on the QCustomPlot. + + The parent plot change will be propagated to subclasses via a call to \ref parentPlotInitialized + so they can react accordingly (e.g. also initialize the parent plot of child layerables, like + QCPLayout does). +*/ +void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot) +{ + if (mParentPlot) + { + qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized"; + return; + } + + if (!parentPlot) + qDebug() << Q_FUNC_INFO << "called with parentPlot zero"; + + mParentPlot = parentPlot; + parentPlotInitialized(mParentPlot); +} + +/*! \internal + + Sets the parent layerable of this layerable to \a parentLayerable. Note that \a parentLayerable does not + become the QObject-parent (for memory management) of this layerable. + + The parent layerable has influence on the return value of the \ref realVisibility method. Only + layerables with a fully visible parent tree will return true for \ref realVisibility, and thus be + drawn. + + \see realVisibility +*/ +void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable) +{ + mParentLayerable = parentLayerable; +} + +/*! \internal + + Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to + the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is + false, the object will be appended. + + Returns true on success, i.e. if \a layer is a valid layer. +*/ +bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend) +{ + if (layer && !mParentPlot) + { + qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set"; + return false; + } + if (layer && layer->parentPlot() != mParentPlot) + { + qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable"; + return false; + } + + QCPLayer *oldLayer = mLayer; + if (mLayer) + mLayer->removeChild(this); + mLayer = layer; + if (mLayer) + mLayer->addChild(this, prepend); + if (mLayer != oldLayer) + emit layerChanged(mLayer); + return true; +} + +/*! \internal + + Sets the QCPainter::setAntialiasing state on the provided \a painter, depending on the \a + localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is + controlled via \a overrideElement. +*/ +void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const +{ + if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement)) + painter->setAntialiasing(false); + else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement)) + painter->setAntialiasing(true); + else + painter->setAntialiasing(localAntialiased); +} + +/*! \internal + + This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting + of a parent plot. This is the case when \c nullptr was passed as parent plot in the constructor, + and the parent plot is set at a later time. + + For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any + QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level + element of the hierarchy gets its parent plot initialized with \ref initializeParentPlot. To + propagate the parent plot to all the children of the hierarchy, the top level element then uses + this function to pass the parent plot on to its child elements. + + The default implementation does nothing. + + \see initializeParentPlot +*/ +void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot) +{ + Q_UNUSED(parentPlot) +} + +/*! \internal + + Returns the selection category this layerable shall belong to. The selection category is used in + conjunction with \ref QCustomPlot::setInteractions to control which objects are selectable and + which aren't. + + Subclasses that don't fit any of the normal \ref QCP::Interaction values can use \ref + QCP::iSelectOther. This is what the default implementation returns. + + \see QCustomPlot::setInteractions +*/ +QCP::Interaction QCPLayerable::selectionCategory() const +{ + return QCP::iSelectOther; +} + +/*! \internal + + Returns the clipping rectangle of this layerable object. By default, this is the viewport of the + parent QCustomPlot. Specific subclasses may reimplement this function to provide different + clipping rects. + + The returned clipping rect is set on the painter before the draw function of the respective + object is called. +*/ +QRect QCPLayerable::clipRect() const +{ + if (mParentPlot) + return mParentPlot->viewport(); + else + return {}; +} + +/*! \internal + + This event is called when the layerable shall be selected, as a consequence of a click by the + user. Subclasses should react to it by setting their selection state appropriately. The default + implementation does nothing. + + \a event is the mouse event that caused the selection. \a additive indicates, whether the user + was holding the multi-select-modifier while performing the selection (see \ref + QCustomPlot::setMultiSelectModifier). if \a additive is true, the selection state must be toggled + (i.e. become selected when unselected and unselected when selected). + + Every selectEvent is preceded by a call to \ref selectTest, which has returned positively (i.e. + returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot). + The \a details data you output from \ref selectTest is fed back via \a details here. You may + use it to transport any kind of information from the selectTest to the possibly subsequent + selectEvent. Usually \a details is used to transfer which part was clicked, if it is a layerable + that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need + to do the calculation again to find out which part was actually clicked. + + \a selectionStateChanged is an output parameter. If the pointer is non-null, this function must + set the value either to true or false, depending on whether the selection state of this layerable + was actually changed. For layerables that only are selectable as a whole and not in parts, this + is simple: if \a additive is true, \a selectionStateChanged must also be set to true, because the + selection toggles. If \a additive is false, \a selectionStateChanged is only set to true, if the + layerable was previously unselected and now is switched to the selected state. + + \see selectTest, deselectEvent +*/ +void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(additive) + Q_UNUSED(details) + Q_UNUSED(selectionStateChanged) +} + +/*! \internal + + This event is called when the layerable shall be deselected, either as consequence of a user + interaction or a call to \ref QCustomPlot::deselectAll. Subclasses should react to it by + unsetting their selection appropriately. + + just as in \ref selectEvent, the output parameter \a selectionStateChanged (if non-null), must + return true or false when the selection state of this layerable has changed or not changed, + respectively. + + \see selectTest, selectEvent +*/ +void QCPLayerable::deselectEvent(bool *selectionStateChanged) +{ + Q_UNUSED(selectionStateChanged) +} + +/*! + This event gets called when the user presses a mouse button while the cursor is over the + layerable. Whether a cursor is over the layerable is decided by a preceding call to \ref + selectTest. + + The current pixel position of the cursor on the QCustomPlot widget is accessible via \c + event->pos(). The parameter \a details contains layerable-specific details about the hit, which + were generated in the previous call to \ref selectTest. For example, One-dimensional plottables + like \ref QCPGraph or \ref QCPBars convey the clicked data point in the \a details parameter, as + \ref QCPDataSelection packed as QVariant. Multi-part objects convey the specific \c + SelectablePart that was hit (e.g. \ref QCPAxis::SelectablePart in the case of axes). + + QCustomPlot uses an event propagation system that works the same as Qt's system. If your + layerable doesn't reimplement the \ref mousePressEvent or explicitly calls \c event->ignore() in + its reimplementation, the event will be propagated to the next layerable in the stacking order. + + Once a layerable has accepted the \ref mousePressEvent, it is considered the mouse grabber and + will receive all following calls to \ref mouseMoveEvent or \ref mouseReleaseEvent for this mouse + interaction (a "mouse interaction" in this context ends with the release). + + The default implementation does nothing except explicitly ignoring the event with \c + event->ignore(). + + \see mouseMoveEvent, mouseReleaseEvent, mouseDoubleClickEvent, wheelEvent +*/ +void QCPLayerable::mousePressEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + event->ignore(); +} + +/*! + This event gets called when the user moves the mouse while holding a mouse button, after this + layerable has become the mouse grabber by accepting the preceding \ref mousePressEvent. + + The current pixel position of the cursor on the QCustomPlot widget is accessible via \c + event->pos(). The parameter \a startPos indicates the position where the initial \ref + mousePressEvent occurred, that started the mouse interaction. + + The default implementation does nothing. + + \see mousePressEvent, mouseReleaseEvent, mouseDoubleClickEvent, wheelEvent +*/ +void QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(startPos) + event->ignore(); +} + +/*! + This event gets called when the user releases the mouse button, after this layerable has become + the mouse grabber by accepting the preceding \ref mousePressEvent. + + The current pixel position of the cursor on the QCustomPlot widget is accessible via \c + event->pos(). The parameter \a startPos indicates the position where the initial \ref + mousePressEvent occurred, that started the mouse interaction. + + The default implementation does nothing. + + \see mousePressEvent, mouseMoveEvent, mouseDoubleClickEvent, wheelEvent +*/ +void QCPLayerable::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(startPos) + event->ignore(); +} + +/*! + This event gets called when the user presses the mouse button a second time in a double-click, + while the cursor is over the layerable. Whether a cursor is over the layerable is decided by a + preceding call to \ref selectTest. + + The \ref mouseDoubleClickEvent is called instead of the second \ref mousePressEvent. So in the + case of a double-click, the event succession is + pressEvent – releaseEvent – doubleClickEvent – releaseEvent. + + The current pixel position of the cursor on the QCustomPlot widget is accessible via \c + event->pos(). The parameter \a details contains layerable-specific details about the hit, which + were generated in the previous call to \ref selectTest. For example, One-dimensional plottables + like \ref QCPGraph or \ref QCPBars convey the clicked data point in the \a details parameter, as + \ref QCPDataSelection packed as QVariant. Multi-part objects convey the specific \c + SelectablePart that was hit (e.g. \ref QCPAxis::SelectablePart in the case of axes). + + Similarly to \ref mousePressEvent, once a layerable has accepted the \ref mouseDoubleClickEvent, + it is considered the mouse grabber and will receive all following calls to \ref mouseMoveEvent + and \ref mouseReleaseEvent for this mouse interaction (a "mouse interaction" in this context ends + with the release). + + The default implementation does nothing except explicitly ignoring the event with \c + event->ignore(). + + \see mousePressEvent, mouseMoveEvent, mouseReleaseEvent, wheelEvent +*/ +void QCPLayerable::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + event->ignore(); +} + +/*! + This event gets called when the user turns the mouse scroll wheel while the cursor is over the + layerable. Whether a cursor is over the layerable is decided by a preceding call to \ref + selectTest. + + The current pixel position of the cursor on the QCustomPlot widget is accessible via \c + event->pos(). + + The \c event->angleDelta() indicates how far the mouse wheel was turned, which is usually +/- 120 + for single rotation steps. However, if the mouse wheel is turned rapidly, multiple steps may + accumulate to one event, making the delta larger. On the other hand, if the wheel has very smooth + steps or none at all, the delta may be smaller. + + The default implementation does nothing. + + \see mousePressEvent, mouseMoveEvent, mouseReleaseEvent, mouseDoubleClickEvent +*/ +void QCPLayerable::wheelEvent(QWheelEvent *event) +{ + event->ignore(); +} +/* end of 'src/layer.cpp' */ + + +/* including file 'src/axis/range.cpp' */ +/* modified 2022-11-06T12:45:56, size 12221 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPRange +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPRange + \brief Represents the range an axis is encompassing. + + contains a \a lower and \a upper double value and provides convenience input, output and + modification functions. + + \see QCPAxis::setRange +*/ + +/* start of documentation of inline functions */ + +/*! \fn double QCPRange::size() const + + Returns the size of the range, i.e. \a upper-\a lower +*/ + +/*! \fn double QCPRange::center() const + + Returns the center of the range, i.e. (\a upper+\a lower)*0.5 +*/ + +/*! \fn void QCPRange::normalize() + + Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values are + swapped. +*/ + +/*! \fn bool QCPRange::contains(double value) const + + Returns true when \a value lies within or exactly on the borders of the range. +*/ + +/*! \fn QCPRange &QCPRange::operator+=(const double& value) + + Adds \a value to both boundaries of the range. +*/ + +/*! \fn QCPRange &QCPRange::operator-=(const double& value) + + Subtracts \a value from both boundaries of the range. +*/ + +/*! \fn QCPRange &QCPRange::operator*=(const double& value) + + Multiplies both boundaries of the range by \a value. +*/ + +/*! \fn QCPRange &QCPRange::operator/=(const double& value) + + Divides both boundaries of the range by \a value. +*/ + +/* end of documentation of inline functions */ + +/*! + Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller + intervals would cause errors due to the 11-bit exponent of double precision numbers, + corresponding to a minimum magnitude of roughly 1e-308. + + \warning Do not use this constant to indicate "arbitrarily small" values in plotting logic (as + values that will appear in the plot)! It is intended only as a bound to compare against, e.g. to + prevent axis ranges from obtaining underflowing ranges. + + \see validRange, maxRange +*/ +const double QCPRange::minRange = 1e-280; + +/*! + Maximum values (negative and positive) the range will accept in range-changing functions. + Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers, + corresponding to a maximum magnitude of roughly 1e308. + + \warning Do not use this constant to indicate "arbitrarily large" values in plotting logic (as + values that will appear in the plot)! It is intended only as a bound to compare against, e.g. to + prevent axis ranges from obtaining overflowing ranges. + + \see validRange, minRange +*/ +const double QCPRange::maxRange = 1e250; + +/*! + Constructs a range with \a lower and \a upper set to zero. +*/ +QCPRange::QCPRange() : + lower(0), + upper(0) +{ +} + +/*! \overload + + Constructs a range with the specified \a lower and \a upper values. + + The resulting range will be normalized (see \ref normalize), so if \a lower is not numerically + smaller than \a upper, they will be swapped. +*/ +QCPRange::QCPRange(double lower, double upper) : + lower(lower), + upper(upper) +{ + normalize(); +} + +/*! \overload + + Expands this range such that \a otherRange is contained in the new range. It is assumed that both + this range and \a otherRange are normalized (see \ref normalize). + + If this range contains NaN as lower or upper bound, it will be replaced by the respective bound + of \a otherRange. + + If \a otherRange is already inside the current range, this function does nothing. + + \see expanded +*/ +void QCPRange::expand(const QCPRange &otherRange) +{ + if (lower > otherRange.lower || qIsNaN(lower)) + lower = otherRange.lower; + if (upper < otherRange.upper || qIsNaN(upper)) + upper = otherRange.upper; +} + +/*! \overload + + Expands this range such that \a includeCoord is contained in the new range. It is assumed that + this range is normalized (see \ref normalize). + + If this range contains NaN as lower or upper bound, the respective bound will be set to \a + includeCoord. + + If \a includeCoord is already inside the current range, this function does nothing. + + \see expand +*/ +void QCPRange::expand(double includeCoord) +{ + if (lower > includeCoord || qIsNaN(lower)) + lower = includeCoord; + if (upper < includeCoord || qIsNaN(upper)) + upper = includeCoord; +} + + +/*! \overload + + Returns an expanded range that contains this and \a otherRange. It is assumed that both this + range and \a otherRange are normalized (see \ref normalize). + + If this range contains NaN as lower or upper bound, the returned range's bound will be taken from + \a otherRange. + + \see expand +*/ +QCPRange QCPRange::expanded(const QCPRange &otherRange) const +{ + QCPRange result = *this; + result.expand(otherRange); + return result; +} + +/*! \overload + + Returns an expanded range that includes the specified \a includeCoord. It is assumed that this + range is normalized (see \ref normalize). + + If this range contains NaN as lower or upper bound, the returned range's bound will be set to \a + includeCoord. + + \see expand +*/ +QCPRange QCPRange::expanded(double includeCoord) const +{ + QCPRange result = *this; + result.expand(includeCoord); + return result; +} + +/*! + Returns this range, possibly modified to not exceed the bounds provided as \a lowerBound and \a + upperBound. If possible, the size of the current range is preserved in the process. + + If the range shall only be bounded at the lower side, you can set \a upperBound to \ref + QCPRange::maxRange. If it shall only be bounded at the upper side, set \a lowerBound to -\ref + QCPRange::maxRange. +*/ +QCPRange QCPRange::bounded(double lowerBound, double upperBound) const +{ + if (lowerBound > upperBound) + qSwap(lowerBound, upperBound); + + QCPRange result(lower, upper); + if (result.lower < lowerBound) + { + result.lower = lowerBound; + result.upper = lowerBound + size(); + if (result.upper > upperBound || qFuzzyCompare(size(), upperBound-lowerBound)) + result.upper = upperBound; + } else if (result.upper > upperBound) + { + result.upper = upperBound; + result.lower = upperBound - size(); + if (result.lower < lowerBound || qFuzzyCompare(size(), upperBound-lowerBound)) + result.lower = lowerBound; + } + + return result; +} + +/*! + Returns a sanitized version of the range. Sanitized means for logarithmic scales, that + the range won't span the positive and negative sign domain, i.e. contain zero. Further + \a lower will always be numerically smaller (or equal) to \a upper. + + If the original range does span positive and negative sign domains or contains zero, + the returned range will try to approximate the original range as good as possible. + If the positive interval of the original range is wider than the negative interval, the + returned range will only contain the positive interval, with lower bound set to \a rangeFac or + \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval + is wider than the positive interval, this time by changing the \a upper bound. +*/ +QCPRange QCPRange::sanitizedForLogScale() const +{ + double rangeFac = 1e-3; + QCPRange sanitizedRange(lower, upper); + sanitizedRange.normalize(); + // can't have range spanning negative and positive values in log plot, so change range to fix it + //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1)) + if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0) + { + // case lower is 0 + if (rangeFac < sanitizedRange.upper*rangeFac) + sanitizedRange.lower = rangeFac; + else + sanitizedRange.lower = sanitizedRange.upper*rangeFac; + } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1)) + else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0) + { + // case upper is 0 + if (-rangeFac > sanitizedRange.lower*rangeFac) + sanitizedRange.upper = -rangeFac; + else + sanitizedRange.upper = sanitizedRange.lower*rangeFac; + } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0) + { + // find out whether negative or positive interval is wider to decide which sign domain will be chosen + if (-sanitizedRange.lower > sanitizedRange.upper) + { + // negative is wider, do same as in case upper is 0 + if (-rangeFac > sanitizedRange.lower*rangeFac) + sanitizedRange.upper = -rangeFac; + else + sanitizedRange.upper = sanitizedRange.lower*rangeFac; + } else + { + // positive is wider, do same as in case lower is 0 + if (rangeFac < sanitizedRange.upper*rangeFac) + sanitizedRange.lower = rangeFac; + else + sanitizedRange.lower = sanitizedRange.upper*rangeFac; + } + } + // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper -maxRange && + upper < maxRange && + qAbs(lower-upper) > minRange && + qAbs(lower-upper) < maxRange && + !(lower > 0 && qIsInf(upper/lower)) && + !(upper < 0 && qIsInf(lower/upper))); +} + +/*! + \overload + Checks, whether the specified range is within valid bounds, which are defined + as QCPRange::maxRange and QCPRange::minRange. + A valid range means: + \li range bounds within -maxRange and maxRange + \li range size above minRange + \li range size below maxRange +*/ +bool QCPRange::validRange(const QCPRange &range) +{ + return (range.lower > -maxRange && + range.upper < maxRange && + qAbs(range.lower-range.upper) > minRange && + qAbs(range.lower-range.upper) < maxRange && + !(range.lower > 0 && qIsInf(range.upper/range.lower)) && + !(range.upper < 0 && qIsInf(range.lower/range.upper))); +} +/* end of 'src/axis/range.cpp' */ + + +/* including file 'src/selection.cpp' */ +/* modified 2022-11-06T12:45:56, size 21837 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPDataRange +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPDataRange + \brief Describes a data range given by begin and end index + + QCPDataRange holds two integers describing the begin (\ref setBegin) and end (\ref setEnd) index + of a contiguous set of data points. The \a end index corresponds to the data point just after the + last data point of the data range, like in standard iterators. + + Data Ranges are not bound to a certain plottable, thus they can be freely exchanged, created and + modified. If a non-contiguous data set shall be described, the class \ref QCPDataSelection is + used, which holds and manages multiple instances of \ref QCPDataRange. In most situations, \ref + QCPDataSelection is thus used. + + Both \ref QCPDataRange and \ref QCPDataSelection offer convenience methods to work with them, + e.g. \ref bounded, \ref expanded, \ref intersects, \ref intersection, \ref adjusted, \ref + contains. Further, addition and subtraction operators (defined in \ref QCPDataSelection) can be + used to join/subtract data ranges and data selections (or mixtures), to retrieve a corresponding + \ref QCPDataSelection. + + %QCustomPlot's \ref dataselection "data selection mechanism" is based on \ref QCPDataSelection and + QCPDataRange. + + \note Do not confuse \ref QCPDataRange with \ref QCPRange. A \ref QCPRange describes an interval + in floating point plot coordinates, e.g. the current axis range. +*/ + +/* start documentation of inline functions */ + +/*! \fn int QCPDataRange::size() const + + Returns the number of data points described by this data range. This is equal to the end index + minus the begin index. + + \see length +*/ + +/*! \fn int QCPDataRange::length() const + + Returns the number of data points described by this data range. Equivalent to \ref size. +*/ + +/*! \fn void QCPDataRange::setBegin(int begin) + + Sets the begin of this data range. The \a begin index points to the first data point that is part + of the data range. + + No checks or corrections are made to ensure the resulting range is valid (\ref isValid). + + \see setEnd +*/ + +/*! \fn void QCPDataRange::setEnd(int end) + + Sets the end of this data range. The \a end index points to the data point just after the last + data point that is part of the data range. + + No checks or corrections are made to ensure the resulting range is valid (\ref isValid). + + \see setBegin +*/ + +/*! \fn bool QCPDataRange::isValid() const + + Returns whether this range is valid. A valid range has a begin index greater or equal to 0, and + an end index greater or equal to the begin index. + + \note Invalid ranges should be avoided and are never the result of any of QCustomPlot's methods + (unless they are themselves fed with invalid ranges). Do not pass invalid ranges to QCustomPlot's + methods. The invalid range is not inherently prevented in QCPDataRange, to allow temporary + invalid begin/end values while manipulating the range. An invalid range is not necessarily empty + (\ref isEmpty), since its \ref length can be negative and thus non-zero. +*/ + +/*! \fn bool QCPDataRange::isEmpty() const + + Returns whether this range is empty, i.e. whether its begin index equals its end index. + + \see size, length +*/ + +/*! \fn QCPDataRange QCPDataRange::adjusted(int changeBegin, int changeEnd) const + + Returns a data range where \a changeBegin and \a changeEnd were added to the begin and end + indices, respectively. +*/ + +/* end documentation of inline functions */ + +/*! + Creates an empty QCPDataRange, with begin and end set to 0. +*/ +QCPDataRange::QCPDataRange() : + mBegin(0), + mEnd(0) +{ +} + +/*! + Creates a QCPDataRange, initialized with the specified \a begin and \a end. + + No checks or corrections are made to ensure the resulting range is valid (\ref isValid). +*/ +QCPDataRange::QCPDataRange(int begin, int end) : + mBegin(begin), + mEnd(end) +{ +} + +/*! + Returns a data range that matches this data range, except that parts exceeding \a other are + excluded. + + This method is very similar to \ref intersection, with one distinction: If this range and the \a + other range share no intersection, the returned data range will be empty with begin and end set + to the respective boundary side of \a other, at which this range is residing. (\ref intersection + would just return a range with begin and end set to 0.) +*/ +QCPDataRange QCPDataRange::bounded(const QCPDataRange &other) const +{ + QCPDataRange result(intersection(other)); + if (result.isEmpty()) // no intersection, preserve respective bounding side of otherRange as both begin and end of return value + { + if (mEnd <= other.mBegin) + result = QCPDataRange(other.mBegin, other.mBegin); + else + result = QCPDataRange(other.mEnd, other.mEnd); + } + return result; +} + +/*! + Returns a data range that contains both this data range as well as \a other. +*/ +QCPDataRange QCPDataRange::expanded(const QCPDataRange &other) const +{ + return {qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd)}; +} + +/*! + Returns the data range which is contained in both this data range and \a other. + + This method is very similar to \ref bounded, with one distinction: If this range and the \a other + range share no intersection, the returned data range will be empty with begin and end set to 0. + (\ref bounded would return a range with begin and end set to one of the boundaries of \a other, + depending on which side this range is on.) + + \see QCPDataSelection::intersection +*/ +QCPDataRange QCPDataRange::intersection(const QCPDataRange &other) const +{ + QCPDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd)); + if (result.isValid()) + return result; + else + return {}; +} + +/*! + Returns whether this data range and \a other share common data points. + + \see intersection, contains +*/ +bool QCPDataRange::intersects(const QCPDataRange &other) const +{ + return !( (mBegin > other.mBegin && mBegin >= other.mEnd) || + (mEnd <= other.mBegin && mEnd < other.mEnd) ); +} + +/*! + Returns whether all data points of \a other are also contained inside this data range. + + \see intersects +*/ +bool QCPDataRange::contains(const QCPDataRange &other) const +{ + return mBegin <= other.mBegin && mEnd >= other.mEnd; +} + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPDataSelection +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPDataSelection + \brief Describes a data set by holding multiple QCPDataRange instances + + QCPDataSelection manages multiple instances of QCPDataRange in order to represent any (possibly + disjoint) set of data selection. + + The data selection can be modified with addition and subtraction operators which take + QCPDataSelection and QCPDataRange instances, as well as methods such as \ref addDataRange and + \ref clear. Read access is provided by \ref dataRange, \ref dataRanges, \ref dataRangeCount, etc. + + The method \ref simplify is used to join directly adjacent or even overlapping QCPDataRange + instances. QCPDataSelection automatically simplifies when using the addition/subtraction + operators. The only case when \ref simplify is left to the user, is when calling \ref + addDataRange, with the parameter \a simplify explicitly set to false. This is useful if many data + ranges will be added to the selection successively and the overhead for simplifying after each + iteration shall be avoided. In this case, you should make sure to call \ref simplify after + completing the operation. + + Use \ref enforceType to bring the data selection into a state complying with the constraints for + selections defined in \ref QCP::SelectionType. + + %QCustomPlot's \ref dataselection "data selection mechanism" is based on QCPDataSelection and + QCPDataRange. + + \section qcpdataselection-iterating Iterating over a data selection + + As an example, the following code snippet calculates the average value of a graph's data + \ref QCPAbstractPlottable::selection "selection": + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpdataselection-iterating-1 + +*/ + +/* start documentation of inline functions */ + +/*! \fn int QCPDataSelection::dataRangeCount() const + + Returns the number of ranges that make up the data selection. The ranges can be accessed by \ref + dataRange via their index. + + \see dataRange, dataPointCount +*/ + +/*! \fn QList QCPDataSelection::dataRanges() const + + Returns all data ranges that make up the data selection. If the data selection is simplified (the + usual state of the selection, see \ref simplify), the ranges are sorted by ascending data point + index. + + \see dataRange +*/ + +/*! \fn bool QCPDataSelection::isEmpty() const + + Returns true if there are no data ranges, and thus no data points, in this QCPDataSelection + instance. + + \see dataRangeCount +*/ + +/* end documentation of inline functions */ + +/*! + Creates an empty QCPDataSelection. +*/ +QCPDataSelection::QCPDataSelection() +{ +} + +/*! + Creates a QCPDataSelection containing the provided \a range. +*/ +QCPDataSelection::QCPDataSelection(const QCPDataRange &range) +{ + mDataRanges.append(range); +} + +/*! + Returns true if this selection is identical (contains the same data ranges with the same begin + and end indices) to \a other. + + Note that both data selections must be in simplified state (the usual state of the selection, see + \ref simplify) for this operator to return correct results. +*/ +bool QCPDataSelection::operator==(const QCPDataSelection &other) const +{ + if (mDataRanges.size() != other.mDataRanges.size()) + return false; + for (int i=0; i= other.end()) + break; // since data ranges are sorted after the simplify() call, no ranges which contain other will come after this + + if (thisEnd > other.begin()) // ranges which don't fulfill this are entirely before other and can be ignored + { + if (thisBegin >= other.begin()) // range leading segment is encompassed + { + if (thisEnd <= other.end()) // range fully encompassed, remove completely + { + mDataRanges.removeAt(i); + continue; + } else // only leading segment is encompassed, trim accordingly + mDataRanges[i].setBegin(other.end()); + } else // leading segment is not encompassed + { + if (thisEnd <= other.end()) // only trailing segment is encompassed, trim accordingly + { + mDataRanges[i].setEnd(other.begin()); + } else // other lies inside this range, so split range + { + mDataRanges[i].setEnd(other.begin()); + mDataRanges.insert(i+1, QCPDataRange(other.end(), thisEnd)); + break; // since data ranges are sorted (and don't overlap) after simplify() call, we're done here + } + } + } + ++i; + } + + return *this; +} + +/*! + Returns the total number of data points contained in all data ranges that make up this data + selection. +*/ +int QCPDataSelection::dataPointCount() const +{ + int result = 0; + foreach (QCPDataRange dataRange, mDataRanges) + result += dataRange.length(); + return result; +} + +/*! + Returns the data range with the specified \a index. + + If the data selection is simplified (the usual state of the selection, see \ref simplify), the + ranges are sorted by ascending data point index. + + \see dataRangeCount +*/ +QCPDataRange QCPDataSelection::dataRange(int index) const +{ + if (index >= 0 && index < mDataRanges.size()) + { + return mDataRanges.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of range:" << index; + return {}; + } +} + +/*! + Returns a \ref QCPDataRange which spans the entire data selection, including possible + intermediate segments which are not part of the original data selection. +*/ +QCPDataRange QCPDataSelection::span() const +{ + if (isEmpty()) + return {}; + else + return {mDataRanges.first().begin(), mDataRanges.last().end()}; +} + +/*! + Adds the given \a dataRange to this data selection. This is equivalent to the += operator but + allows disabling immediate simplification by setting \a simplify to false. This can improve + performance if adding a very large amount of data ranges successively. In this case, make sure to + call \ref simplify manually, after the operation. +*/ +void QCPDataSelection::addDataRange(const QCPDataRange &dataRange, bool simplify) +{ + mDataRanges.append(dataRange); + if (simplify) + this->simplify(); +} + +/*! + Removes all data ranges. The data selection then contains no data points. + + \ref isEmpty +*/ +void QCPDataSelection::clear() +{ + mDataRanges.clear(); +} + +/*! + Sorts all data ranges by range begin index in ascending order, and then joins directly adjacent + or overlapping ranges. This can reduce the number of individual data ranges in the selection, and + prevents possible double-counting when iterating over the data points held by the data ranges. + + This method is automatically called when using the addition/subtraction operators. The only case + when \ref simplify is left to the user, is when calling \ref addDataRange, with the parameter \a + simplify explicitly set to false. +*/ +void QCPDataSelection::simplify() +{ + // remove any empty ranges: + for (int i=static_cast(mDataRanges.size())-1; i>=0; --i) + { + if (mDataRanges.at(i).isEmpty()) + mDataRanges.removeAt(i); + } + if (mDataRanges.isEmpty()) + return; + + // sort ranges by starting value, ascending: + std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin); + + // join overlapping/contiguous ranges: + int i = 1; + while (i < mDataRanges.size()) + { + if (mDataRanges.at(i-1).end() >= mDataRanges.at(i).begin()) // range i overlaps/joins with i-1, so expand range i-1 appropriately and remove range i from list + { + mDataRanges[i-1].setEnd(qMax(mDataRanges.at(i-1).end(), mDataRanges.at(i).end())); + mDataRanges.removeAt(i); + } else + ++i; + } +} + +/*! + Makes sure this data selection conforms to the specified \a type selection type. Before the type + is enforced, \ref simplify is called. + + Depending on \a type, enforcing means adding new data points that were previously not part of the + selection, or removing data points from the selection. If the current selection already conforms + to \a type, the data selection is not changed. + + \see QCP::SelectionType +*/ +void QCPDataSelection::enforceType(QCP::SelectionType type) +{ + simplify(); + switch (type) + { + case QCP::stNone: + { + mDataRanges.clear(); + break; + } + case QCP::stWhole: + { + // whole selection isn't defined by data range, so don't change anything (is handled in plottable methods) + break; + } + case QCP::stSingleData: + { + // reduce all data ranges to the single first data point: + if (!mDataRanges.isEmpty()) + { + if (mDataRanges.size() > 1) + mDataRanges = QList() << mDataRanges.first(); + if (mDataRanges.first().length() > 1) + mDataRanges.first().setEnd(mDataRanges.first().begin()+1); + } + break; + } + case QCP::stDataRange: + { + if (!isEmpty()) + mDataRanges = QList() << span(); + break; + } + case QCP::stMultipleDataRanges: + { + // this is the selection type that allows all concievable combinations of ranges, so do nothing + break; + } + } +} + +/*! + Returns true if the data selection \a other is contained entirely in this data selection, i.e. + all data point indices that are in \a other are also in this data selection. + + \see QCPDataRange::contains +*/ +bool QCPDataSelection::contains(const QCPDataSelection &other) const +{ + if (other.isEmpty()) return false; + + int otherIndex = 0; + int thisIndex = 0; + while (thisIndex < mDataRanges.size() && otherIndex < other.mDataRanges.size()) + { + if (mDataRanges.at(thisIndex).contains(other.mDataRanges.at(otherIndex))) + ++otherIndex; + else + ++thisIndex; + } + return thisIndex < mDataRanges.size(); // if thisIndex ran all the way to the end to find a containing range for the current otherIndex, other is not contained in this +} + +/*! + Returns a data selection containing the points which are both in this data selection and in the + data range \a other. + + A common use case is to limit an unknown data selection to the valid range of a data container, + using \ref QCPDataContainer::dataRange as \a other. One can then safely iterate over the returned + data selection without exceeding the data container's bounds. +*/ +QCPDataSelection QCPDataSelection::intersection(const QCPDataRange &other) const +{ + QCPDataSelection result; + foreach (QCPDataRange dataRange, mDataRanges) + result.addDataRange(dataRange.intersection(other), false); + result.simplify(); + return result; +} + +/*! + Returns a data selection containing the points which are both in this data selection and in the + data selection \a other. +*/ +QCPDataSelection QCPDataSelection::intersection(const QCPDataSelection &other) const +{ + QCPDataSelection result; + for (int i=0; iorientation() == Qt::Horizontal) + return {axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width())}; + else + return {axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top())}; + } else + { + qDebug() << Q_FUNC_INFO << "called with axis zero"; + return {}; + } +} + +/*! + Sets the pen that will be used to draw the selection rect outline. + + \see setBrush +*/ +void QCPSelectionRect::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the brush that will be used to fill the selection rect. By default the selection rect is not + filled, i.e. \a brush is Qt::NoBrush. + + \see setPen +*/ +void QCPSelectionRect::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + If there is currently a selection interaction going on (\ref isActive), the interaction is + canceled. The selection rect will emit the \ref canceled signal. +*/ +void QCPSelectionRect::cancel() +{ + if (mActive) + { + mActive = false; + emit canceled(mRect, nullptr); + } +} + +/*! \internal + + This method is called by QCustomPlot to indicate that a selection rect interaction was initiated. + The default implementation sets the selection rect to active, initializes the selection rect + geometry and emits the \ref started signal. +*/ +void QCPSelectionRect::startSelection(QMouseEvent *event) +{ + mActive = true; + mRect = QRect(event->pos(), event->pos()); + emit started(event); +} + +/*! \internal + + This method is called by QCustomPlot to indicate that an ongoing selection rect interaction needs + to update its geometry. The default implementation updates the rect and emits the \ref changed + signal. +*/ +void QCPSelectionRect::moveSelection(QMouseEvent *event) +{ + mRect.setBottomRight(event->pos()); + emit changed(mRect, event); + layer()->replot(); +} + +/*! \internal + + This method is called by QCustomPlot to indicate that an ongoing selection rect interaction has + finished by the user releasing the mouse button. The default implementation deactivates the + selection rect and emits the \ref accepted signal. +*/ +void QCPSelectionRect::endSelection(QMouseEvent *event) +{ + mRect.setBottomRight(event->pos()); + mActive = false; + emit accepted(mRect, event); +} + +/*! \internal + + This method is called by QCustomPlot when a key has been pressed by the user while the selection + rect interaction is active. The default implementation allows to \ref cancel the interaction by + hitting the escape key. +*/ +void QCPSelectionRect::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape && mActive) + { + mActive = false; + emit canceled(mRect, event); + } +} + +/* inherits documentation from base class */ +void QCPSelectionRect::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeOther); +} + +/*! \internal + + If the selection rect is active (\ref isActive), draws the selection rect defined by \a mRect. + + \seebaseclassmethod +*/ +void QCPSelectionRect::draw(QCPPainter *painter) +{ + if (mActive) + { + painter->setPen(mPen); + painter->setBrush(mBrush); + painter->drawRect(mRect); + } +} +/* end of 'src/selectionrect.cpp' */ + + +/* including file 'src/layout.cpp' */ +/* modified 2022-11-06T12:45:56, size 78863 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPMarginGroup +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPMarginGroup + \brief A margin group allows synchronization of margin sides if working with multiple layout elements. + + QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that + they will all have the same size, based on the largest required margin in the group. + + \n + \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup" + \n + + In certain situations it is desirable that margins at specific sides are synchronized across + layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will + provide a cleaner look to the user if the left and right margins of the two axis rects are of the + same size. The left axis of the top axis rect will then be at the same horizontal position as the + left axis of the lower axis rect, making them appear aligned. The same applies for the right + axes. This is what QCPMarginGroup makes possible. + + To add/remove a specific side of a layout element to/from a margin group, use the \ref + QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call + \ref clear, or just delete the margin group. + + \section QCPMarginGroup-example Example + + First create a margin group: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1 + Then set this group on the layout element sides: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2 + Here, we've used the first two axis rects of the plot and synchronized their left margins with + each other and their right margins with each other. +*/ + +/* start documentation of inline functions */ + +/*! \fn QList QCPMarginGroup::elements(QCP::MarginSide side) const + + Returns a list of all layout elements that have their margin \a side associated with this margin + group. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPMarginGroup instance in \a parentPlot. +*/ +QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) : + QObject(parentPlot), + mParentPlot(parentPlot) +{ + mChildren.insert(QCP::msLeft, QList()); + mChildren.insert(QCP::msRight, QList()); + mChildren.insert(QCP::msTop, QList()); + mChildren.insert(QCP::msBottom, QList()); +} + +QCPMarginGroup::~QCPMarginGroup() +{ + clear(); +} + +/*! + Returns whether this margin group is empty. If this function returns true, no layout elements use + this margin group to synchronize margin sides. +*/ +bool QCPMarginGroup::isEmpty() const +{ + QHashIterator > it(mChildren); + while (it.hasNext()) + { + it.next(); + if (!it.value().isEmpty()) + return false; + } + return true; +} + +/*! + Clears this margin group. The synchronization of the margin sides that use this margin group is + lifted and they will use their individual margin sizes again. +*/ +void QCPMarginGroup::clear() +{ + // make all children remove themselves from this margin group: + QHashIterator > it(mChildren); + while (it.hasNext()) + { + it.next(); + const QList elements = it.value(); + for (int i=static_cast(elements.size())-1; i>=0; --i) + elements.at(i)->setMarginGroup(it.key(), nullptr); // removes itself from mChildren via removeChild + } +} + +/*! \internal + + Returns the synchronized common margin for \a side. This is the margin value that will be used by + the layout element on the respective side, if it is part of this margin group. + + The common margin is calculated by requesting the automatic margin (\ref + QCPLayoutElement::calculateAutoMargin) of each element associated with \a side in this margin + group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into + account, too.) +*/ +int QCPMarginGroup::commonMargin(QCP::MarginSide side) const +{ + // query all automatic margins of the layout elements in this margin group side and find maximum: + int result = 0; + foreach (QCPLayoutElement *el, mChildren.value(side)) + { + if (!el->autoMargins().testFlag(side)) + continue; + int m = qMax(el->calculateAutoMargin(side), QCP::getMarginValue(el->minimumMargins(), side)); + if (m > result) + result = m; + } + return result; +} + +/*! \internal + + Adds \a element to the internal list of child elements, for the margin \a side. + + This function does not modify the margin group property of \a element. +*/ +void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element) +{ + if (!mChildren[side].contains(element)) + mChildren[side].append(element); + else + qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast(element); +} + +/*! \internal + + Removes \a element from the internal list of child elements, for the margin \a side. + + This function does not modify the margin group property of \a element. +*/ +void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element) +{ + if (!mChildren[side].removeOne(element)) + qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast(element); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayoutElement +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayoutElement + \brief The abstract base class for all objects that form \ref thelayoutsystem "the layout system". + + This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses. + + A Layout element is a rectangular object which can be placed in layouts. It has an outer rect + (QCPLayoutElement::outerRect) and an inner rect (\ref QCPLayoutElement::rect). The difference + between outer and inner rect is called its margin. The margin can either be set to automatic or + manual (\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be + set explicitly with \ref setMargins and will stay fixed at that value. If it's set to automatic, + the layout element subclass will control the value itself (via \ref calculateAutoMargin). + + Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level + layout is reachable via \ref QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref + QCPLayout itself derives from \ref QCPLayoutElement, layouts can be nested. + + Thus in QCustomPlot one can divide layout elements into two categories: The ones that are + invisible by themselves, because they don't draw anything. Their only purpose is to manage the + position and size of other layout elements. This category of layout elements usually use + QCPLayout as base class. Then there is the category of layout elements which actually draw + something. For example, QCPAxisRect, QCPLegend and QCPTextElement are of this category. This does + not necessarily mean that the latter category can't have child layout elements. QCPLegend for + instance, actually derives from QCPLayoutGrid and the individual legend items are child layout + elements in the grid layout. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPLayout *QCPLayoutElement::layout() const + + Returns the parent layout of this layout element. +*/ + +/*! \fn QRect QCPLayoutElement::rect() const + + Returns the inner rect of this layout element. The inner rect is the outer rect (\ref outerRect, \ref + setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins). + + In some cases, the area between outer and inner rect is left blank. In other cases the margin + area is used to display peripheral graphics while the main content is in the inner rect. This is + where automatic margin calculation becomes interesting because it allows the layout element to + adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect + draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if + \ref setAutoMargins is enabled) according to the space required by the labels of the axes. + + \see outerRect +*/ + +/*! \fn QRect QCPLayoutElement::outerRect() const + + Returns the outer rect of this layout element. The outer rect is the inner rect expanded by the + margins (\ref setMargins, \ref setAutoMargins). The outer rect is used (and set via \ref + setOuterRect) by the parent \ref QCPLayout to control the size of this layout element. + + \see rect +*/ + +/* end documentation of inline functions */ + +/*! + Creates an instance of QCPLayoutElement and sets default values. +*/ +QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) : + QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout) + mParentLayout(nullptr), + mMinimumSize(), + mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), + mSizeConstraintRect(scrInnerRect), + mRect(0, 0, 0, 0), + mOuterRect(0, 0, 0, 0), + mMargins(0, 0, 0, 0), + mMinimumMargins(0, 0, 0, 0), + mAutoMargins(QCP::msAll) +{ +} + +QCPLayoutElement::~QCPLayoutElement() +{ + setMarginGroup(QCP::msAll, nullptr); // unregister at margin groups, if there are any + // unregister at layout: + if (qobject_cast(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor + mParentLayout->take(this); +} + +/*! + Sets the outer rect of this layout element. If the layout element is inside a layout, the layout + sets the position and size of this layout element using this function. + + Calling this function externally has no effect, since the layout will overwrite any changes to + the outer rect upon the next replot. + + The layout element will adapt its inner \ref rect by applying the margins inward to the outer rect. + + \see rect +*/ +void QCPLayoutElement::setOuterRect(const QRect &rect) +{ + if (mOuterRect != rect) + { + mOuterRect = rect; + mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom()); + } +} + +/*! + Sets the margins of this layout element. If \ref setAutoMargins is disabled for some or all + sides, this function is used to manually set the margin on those sides. Sides that are still set + to be handled automatically are ignored and may have any value in \a margins. + + The margin is the distance between the outer rect (controlled by the parent layout via \ref + setOuterRect) and the inner \ref rect (which usually contains the main content of this layout + element). + + \see setAutoMargins +*/ +void QCPLayoutElement::setMargins(const QMargins &margins) +{ + if (mMargins != margins) + { + mMargins = margins; + mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom()); + } +} + +/*! + If \ref setAutoMargins is enabled on some or all margins, this function is used to provide + minimum values for those margins. + + The minimum values are not enforced on margin sides that were set to be under manual control via + \ref setAutoMargins. + + \see setAutoMargins +*/ +void QCPLayoutElement::setMinimumMargins(const QMargins &margins) +{ + if (mMinimumMargins != margins) + { + mMinimumMargins = margins; + } +} + +/*! + Sets on which sides the margin shall be calculated automatically. If a side is calculated + automatically, a minimum margin value may be provided with \ref setMinimumMargins. If a side is + set to be controlled manually, the value may be specified with \ref setMargins. + + Margin sides that are under automatic control may participate in a \ref QCPMarginGroup (see \ref + setMarginGroup), to synchronize (align) it with other layout elements in the plot. + + \see setMinimumMargins, setMargins, QCP::MarginSide +*/ +void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides) +{ + mAutoMargins = sides; +} + +/*! + Sets the minimum size of this layout element. A parent layout tries to respect the \a size here + by changing row/column sizes in the layout accordingly. + + If the parent layout size is not sufficient to satisfy all minimum size constraints of its child + layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot + propagates the layout's size constraints to the outside by setting its own minimum QWidget size + accordingly, so violations of \a size should be exceptions. + + Whether this constraint applies to the inner or the outer rect can be specified with \ref + setSizeConstraintRect (see \ref rect and \ref outerRect). +*/ +void QCPLayoutElement::setMinimumSize(const QSize &size) +{ + if (mMinimumSize != size) + { + mMinimumSize = size; + if (mParentLayout) + mParentLayout->sizeConstraintsChanged(); + } +} + +/*! \overload + + Sets the minimum size of this layout element. + + Whether this constraint applies to the inner or the outer rect can be specified with \ref + setSizeConstraintRect (see \ref rect and \ref outerRect). +*/ +void QCPLayoutElement::setMinimumSize(int width, int height) +{ + setMinimumSize(QSize(width, height)); +} + +/*! + Sets the maximum size of this layout element. A parent layout tries to respect the \a size here + by changing row/column sizes in the layout accordingly. + + Whether this constraint applies to the inner or the outer rect can be specified with \ref + setSizeConstraintRect (see \ref rect and \ref outerRect). +*/ +void QCPLayoutElement::setMaximumSize(const QSize &size) +{ + if (mMaximumSize != size) + { + mMaximumSize = size; + if (mParentLayout) + mParentLayout->sizeConstraintsChanged(); + } +} + +/*! \overload + + Sets the maximum size of this layout element. + + Whether this constraint applies to the inner or the outer rect can be specified with \ref + setSizeConstraintRect (see \ref rect and \ref outerRect). +*/ +void QCPLayoutElement::setMaximumSize(int width, int height) +{ + setMaximumSize(QSize(width, height)); +} + +/*! + Sets to which rect of a layout element the size constraints apply. Size constraints can be set + via \ref setMinimumSize and \ref setMaximumSize. + + The outer rect (\ref outerRect) includes the margins (e.g. in the case of a QCPAxisRect the axis + labels), whereas the inner rect (\ref rect) does not. + + \see setMinimumSize, setMaximumSize +*/ +void QCPLayoutElement::setSizeConstraintRect(SizeConstraintRect constraintRect) +{ + if (mSizeConstraintRect != constraintRect) + { + mSizeConstraintRect = constraintRect; + if (mParentLayout) + mParentLayout->sizeConstraintsChanged(); + } +} + +/*! + Sets the margin \a group of the specified margin \a sides. + + Margin groups allow synchronizing specified margins across layout elements, see the documentation + of \ref QCPMarginGroup. + + To unset the margin group of \a sides, set \a group to \c nullptr. + + Note that margin groups only work for margin sides that are set to automatic (\ref + setAutoMargins). + + \see QCP::MarginSide +*/ +void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group) +{ + QVector sideVector; + if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft); + if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight); + if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop); + if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom); + + foreach (QCP::MarginSide side, sideVector) + { + if (marginGroup(side) != group) + { + QCPMarginGroup *oldGroup = marginGroup(side); + if (oldGroup) // unregister at old group + oldGroup->removeChild(side, this); + + if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there + { + mMarginGroups.remove(side); + } else // setting to a new group + { + mMarginGroups[side] = group; + group->addChild(side, this); + } + } + } +} + +/*! + Updates the layout element and sub-elements. This function is automatically called before every + replot by the parent layout element. It is called multiple times, once for every \ref + UpdatePhase. The phases are run through in the order of the enum values. For details about what + happens at the different phases, see the documentation of \ref UpdatePhase. + + Layout elements that have child elements should call the \ref update method of their child + elements, and pass the current \a phase unchanged. + + The default implementation executes the automatic margin mechanism in the \ref upMargins phase. + Subclasses should make sure to call the base class implementation. +*/ +void QCPLayoutElement::update(UpdatePhase phase) +{ + if (phase == upMargins) + { + if (mAutoMargins != QCP::msNone) + { + // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group: + QMargins newMargins = mMargins; + const QList allMarginSides = QList() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom; + foreach (QCP::MarginSide side, allMarginSides) + { + if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically + { + if (mMarginGroups.contains(side)) + QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group + else + QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly + // apply minimum margin restrictions: + if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side)) + QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side)); + } + } + setMargins(newMargins); + } + } +} + +/*! + Returns the suggested minimum size this layout element (the \ref outerRect) may be compressed to, + if no manual minimum size is set. + + if a minimum size (\ref setMinimumSize) was not set manually, parent layouts use the returned size + (usually indirectly through \ref QCPLayout::getFinalMinimumOuterSize) to determine the minimum + allowed size of this layout element. + + A manual minimum size is considered set if it is non-zero. + + The default implementation simply returns the sum of the horizontal margins for the width and the + sum of the vertical margins for the height. Reimplementations may use their detailed knowledge + about the layout element's content to provide size hints. +*/ +QSize QCPLayoutElement::minimumOuterSizeHint() const +{ + return {mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom()}; +} + +/*! + Returns the suggested maximum size this layout element (the \ref outerRect) may be expanded to, + if no manual maximum size is set. + + if a maximum size (\ref setMaximumSize) was not set manually, parent layouts use the returned + size (usually indirectly through \ref QCPLayout::getFinalMaximumOuterSize) to determine the + maximum allowed size of this layout element. + + A manual maximum size is considered set if it is smaller than Qt's \c QWIDGETSIZE_MAX. + + The default implementation simply returns \c QWIDGETSIZE_MAX for both width and height, implying + no suggested maximum size. Reimplementations may use their detailed knowledge about the layout + element's content to provide size hints. +*/ +QSize QCPLayoutElement::maximumOuterSizeHint() const +{ + return {QWIDGETSIZE_MAX, QWIDGETSIZE_MAX}; +} + +/*! + Returns a list of all child elements in this layout element. If \a recursive is true, all + sub-child elements are included in the list, too. + + \warning There may be \c nullptr entries in the returned list. For example, QCPLayoutGrid may + have empty cells which yield \c nullptr at the respective index. +*/ +QList QCPLayoutElement::elements(bool recursive) const +{ + Q_UNUSED(recursive) + return QList(); +} + +/*! + Layout elements are sensitive to events inside their outer rect. If \a pos is within the outer + rect, this method returns a value corresponding to 0.99 times the parent plot's selection + tolerance. However, layout elements are not selectable by default. So if \a onlySelectable is + true, -1.0 is returned. + + See \ref QCPLayerable::selectTest for a general explanation of this virtual method. + + QCPLayoutElement subclasses may reimplement this method to provide more specific selection test + behaviour. +*/ +double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + + if (onlySelectable) + return -1; + + if (QRectF(mOuterRect).contains(pos)) + { + if (mParentPlot) + return mParentPlot->selectionTolerance()*0.99; + else + { + qDebug() << Q_FUNC_INFO << "parent plot not defined"; + return -1; + } + } else + return -1; +} + +/*! \internal + + propagates the parent plot initialization to all child elements, by calling \ref + QCPLayerable::initializeParentPlot on them. +*/ +void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot) +{ + foreach (QCPLayoutElement *el, elements(false)) + { + if (!el->parentPlot()) + el->initializeParentPlot(parentPlot); + } +} + +/*! \internal + + Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a + side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the + returned value will not be smaller than the specified minimum margin. + + The default implementation just returns the respective manual margin (\ref setMargins) or the + minimum margin, whichever is larger. +*/ +int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side) +{ + return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side)); +} + +/*! \internal + + This virtual method is called when this layout element was moved to a different QCPLayout, or + when this layout element has changed its logical position (e.g. row and/or column) within the + same QCPLayout. Subclasses may use this to react accordingly. + + Since this method is called after the completion of the move, you can access the new parent + layout via \ref layout(). + + The default implementation does nothing. +*/ +void QCPLayoutElement::layoutChanged() +{ +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayout +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayout + \brief The abstract base class for layouts + + This is an abstract base class for layout elements whose main purpose is to define the position + and size of other child layout elements. In most cases, layouts don't draw anything themselves + (but there are exceptions to this, e.g. QCPLegend). + + QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts. + + QCPLayout introduces a common interface for accessing and manipulating the child elements. Those + functions are most notably \ref elementCount, \ref elementAt, \ref takeAt, \ref take, \ref + simplify, \ref removeAt, \ref remove and \ref clear. Individual subclasses may add more functions + to this interface which are more specialized to the form of the layout. For example, \ref + QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid + more conveniently. + + Since this is an abstract base class, you can't instantiate it directly. Rather use one of its + subclasses like QCPLayoutGrid or QCPLayoutInset. + + For a general introduction to the layout system, see the dedicated documentation page \ref + thelayoutsystem "The Layout System". +*/ + +/* start documentation of pure virtual functions */ + +/*! \fn virtual int QCPLayout::elementCount() const = 0 + + Returns the number of elements/cells in the layout. + + \see elements, elementAt +*/ + +/*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0 + + Returns the element in the cell with the given \a index. If \a index is invalid, returns \c + nullptr. + + Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g. + QCPLayoutGrid), so this function may return \c nullptr in those cases. You may use this function + to check whether a cell is empty or not. + + \see elements, elementCount, takeAt +*/ + +/*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0 + + Removes the element with the given \a index from the layout and returns it. + + If the \a index is invalid or the cell with that index is empty, returns \c nullptr. + + Note that some layouts don't remove the respective cell right away but leave an empty cell after + successful removal of the layout element. To collapse empty cells, use \ref simplify. + + \see elementAt, take +*/ + +/*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0 + + Removes the specified \a element from the layout and returns true on success. + + If the \a element isn't in this layout, returns false. + + Note that some layouts don't remove the respective cell right away but leave an empty cell after + successful removal of the layout element. To collapse empty cells, use \ref simplify. + + \see takeAt +*/ + +/* end documentation of pure virtual functions */ + +/*! + Creates an instance of QCPLayout and sets default values. Note that since QCPLayout + is an abstract base class, it can't be instantiated directly. +*/ +QCPLayout::QCPLayout() +{ +} + +/*! + If \a phase is \ref upLayout, calls \ref updateLayout, which subclasses may reimplement to + reposition and resize their cells. + + Finally, the call is propagated down to all child \ref QCPLayoutElement "QCPLayoutElements". + + For details about this method and the update phases, see the documentation of \ref + QCPLayoutElement::update. +*/ +void QCPLayout::update(UpdatePhase phase) +{ + QCPLayoutElement::update(phase); + + // set child element rects according to layout: + if (phase == upLayout) + updateLayout(); + + // propagate update call to child elements: + const int elCount = elementCount(); + for (int i=0; iupdate(phase); + } +} + +/* inherits documentation from base class */ +QList QCPLayout::elements(bool recursive) const +{ + const int c = elementCount(); + QList result; +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + result.reserve(c); +#endif + for (int i=0; ielements(recursive); + } + } + return result; +} + +/*! + Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the + default implementation does nothing. + + Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit + simplification while QCPLayoutGrid does. +*/ +void QCPLayout::simplify() +{ +} + +/*! + Removes and deletes the element at the provided \a index. Returns true on success. If \a index is + invalid or points to an empty cell, returns false. + + This function internally uses \ref takeAt to remove the element from the layout and then deletes + the returned element. Note that some layouts don't remove the respective cell right away but leave an + empty cell after successful removal of the layout element. To collapse empty cells, use \ref + simplify. + + \see remove, takeAt +*/ +bool QCPLayout::removeAt(int index) +{ + if (QCPLayoutElement *el = takeAt(index)) + { + delete el; + return true; + } else + return false; +} + +/*! + Removes and deletes the provided \a element. Returns true on success. If \a element is not in the + layout, returns false. + + This function internally uses \ref takeAt to remove the element from the layout and then deletes + the element. Note that some layouts don't remove the respective cell right away but leave an + empty cell after successful removal of the layout element. To collapse empty cells, use \ref + simplify. + + \see removeAt, take +*/ +bool QCPLayout::remove(QCPLayoutElement *element) +{ + if (take(element)) + { + delete element; + return true; + } else + return false; +} + +/*! + Removes and deletes all layout elements in this layout. Finally calls \ref simplify to make sure + all empty cells are collapsed. + + \see remove, removeAt +*/ +void QCPLayout::clear() +{ + for (int i=elementCount()-1; i>=0; --i) + { + if (elementAt(i)) + removeAt(i); + } + simplify(); +} + +/*! + Subclasses call this method to report changed (minimum/maximum) size constraints. + + If the parent of this layout is again a QCPLayout, forwards the call to the parent's \ref + sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \ref QCustomPlot::plotLayout of + QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout, + it may update itself and resize cells accordingly. +*/ +void QCPLayout::sizeConstraintsChanged() const +{ + if (QWidget *w = qobject_cast(parent())) + w->updateGeometry(); + else if (QCPLayout *l = qobject_cast(parent())) + l->sizeConstraintsChanged(); +} + +/*! \internal + + Subclasses reimplement this method to update the position and sizes of the child elements/cells + via calling their \ref QCPLayoutElement::setOuterRect. The default implementation does nothing. + + The geometry used as a reference is the inner \ref rect of this layout. Child elements should stay + within that rect. + + \ref getSectionSizes may help with the reimplementation of this function. + + \see update +*/ +void QCPLayout::updateLayout() +{ +} + + +/*! \internal + + Associates \a el with this layout. This is done by setting the \ref QCPLayoutElement::layout, the + \ref QCPLayerable::parentLayerable and the QObject parent to this layout. + + Further, if \a el didn't previously have a parent plot, calls \ref + QCPLayerable::initializeParentPlot on \a el to set the paret plot. + + This method is used by subclass specific methods that add elements to the layout. Note that this + method only changes properties in \a el. The removal from the old layout and the insertion into + the new layout must be done additionally. +*/ +void QCPLayout::adoptElement(QCPLayoutElement *el) +{ + if (el) + { + el->mParentLayout = this; + el->setParentLayerable(this); + el->setParent(this); + if (!el->parentPlot()) + el->initializeParentPlot(mParentPlot); + el->layoutChanged(); + } else + qDebug() << Q_FUNC_INFO << "Null element passed"; +} + +/*! \internal + + Disassociates \a el from this layout. This is done by setting the \ref QCPLayoutElement::layout + and the \ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent + QCustomPlot. + + This method is used by subclass specific methods that remove elements from the layout (e.g. \ref + take or \ref takeAt). Note that this method only changes properties in \a el. The removal from + the old layout must be done additionally. +*/ +void QCPLayout::releaseElement(QCPLayoutElement *el) +{ + if (el) + { + el->mParentLayout = nullptr; + el->setParentLayerable(nullptr); + el->setParent(mParentPlot); + // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot + } else + qDebug() << Q_FUNC_INFO << "Null element passed"; +} + +/*! \internal + + This is a helper function for the implementation of \ref updateLayout in subclasses. + + It calculates the sizes of one-dimensional sections with provided constraints on maximum section + sizes, minimum section sizes, relative stretch factors and the final total size of all sections. + + The QVector entries refer to the sections. Thus all QVectors must have the same size. + + \a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size + imposed, set all vector values to Qt's QWIDGETSIZE_MAX. + + \a minSizes gives the minimum allowed size of each section. If there shall be no minimum size + imposed, set all vector values to zero. If the \a minSizes entries add up to a value greater than + \a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words, + not exceeding the allowed total size is taken to be more important than not going below minimum + section sizes.) + + \a stretchFactors give the relative proportions of the sections to each other. If all sections + shall be scaled equally, set all values equal. If the first section shall be double the size of + each individual other section, set the first number of \a stretchFactors to double the value of + the other individual values (e.g. {2, 1, 1, 1}). + + \a totalSize is the value that the final section sizes will add up to. Due to rounding, the + actual sum may differ slightly. If you want the section sizes to sum up to exactly that value, + you could distribute the remaining difference on the sections. + + The return value is a QVector containing the section sizes. +*/ +QVector QCPLayout::getSectionSizes(QVector maxSizes, QVector minSizes, QVector stretchFactors, int totalSize) const +{ + if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size()) + { + qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors; + return QVector(); + } + if (stretchFactors.isEmpty()) + return QVector(); + int sectionCount = static_cast(stretchFactors.size()); + QVector sectionSizes(sectionCount); + // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections): + int minSizeSum = 0; + for (int i=0; i minimumLockedSections; + QList unfinishedSections; + for (int i=0; i result(sectionCount); + for (int i=0; iminimumOuterSizeHint(); + QSize minOuter = el->minimumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset minimum of 0) + if (minOuter.width() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect) + minOuter.rwidth() += el->margins().left() + el->margins().right(); + if (minOuter.height() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect) + minOuter.rheight() += el->margins().top() + el->margins().bottom(); + + return {minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(), + minOuter.height() > 0 ? minOuter.height() : minOuterHint.height()}; +} + +/*! \internal + + This is a helper function for the implementation of subclasses. + + It returns the maximum size that should finally be used for the outer rect of the passed layout + element \a el. + + It takes into account whether a manual maximum size is set (\ref + QCPLayoutElement::setMaximumSize), which size constraint is set (\ref + QCPLayoutElement::setSizeConstraintRect), as well as the maximum size hint, if no manual maximum + size was set (\ref QCPLayoutElement::maximumOuterSizeHint). +*/ +QSize QCPLayout::getFinalMaximumOuterSize(const QCPLayoutElement *el) +{ + QSize maxOuterHint = el->maximumOuterSizeHint(); + QSize maxOuter = el->maximumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset maximum of QWIDGETSIZE_MAX) + if (maxOuter.width() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect) + maxOuter.rwidth() += el->margins().left() + el->margins().right(); + if (maxOuter.height() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect) + maxOuter.rheight() += el->margins().top() + el->margins().bottom(); + + return {maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(), + maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height()}; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayoutGrid +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLayoutGrid + \brief A layout that arranges child elements in a grid + + Elements are laid out in a grid with configurable stretch factors (\ref setColumnStretchFactor, + \ref setRowStretchFactor) and spacing (\ref setColumnSpacing, \ref setRowSpacing). + + Elements can be added to cells via \ref addElement. The grid is expanded if the specified row or + column doesn't exist yet. Whether a cell contains a valid layout element can be checked with \ref + hasElement, that element can be retrieved with \ref element. If rows and columns that only have + empty cells shall be removed, call \ref simplify. Removal of elements is either done by just + adding the element to a different layout or by using the QCPLayout interface \ref take or \ref + remove. + + If you use \ref addElement(QCPLayoutElement*) without explicit parameters for \a row and \a + column, the grid layout will choose the position according to the current \ref setFillOrder and + the wrapping (\ref setWrap). + + Row and column insertion can be performed with \ref insertRow and \ref insertColumn. +*/ + +/* start documentation of inline functions */ + +/*! \fn int QCPLayoutGrid::rowCount() const + + Returns the number of rows in the layout. + + \see columnCount +*/ + +/*! \fn int QCPLayoutGrid::columnCount() const + + Returns the number of columns in the layout. + + \see rowCount +*/ + +/* end documentation of inline functions */ + +/*! + Creates an instance of QCPLayoutGrid and sets default values. +*/ +QCPLayoutGrid::QCPLayoutGrid() : + mColumnSpacing(5), + mRowSpacing(5), + mWrap(0), + mFillOrder(foColumnsFirst) +{ +} + +QCPLayoutGrid::~QCPLayoutGrid() +{ + // clear all child layout elements. This is important because only the specific layouts know how + // to handle removing elements (clear calls virtual removeAt method to do that). + clear(); +} + +/*! + Returns the element in the cell in \a row and \a column. + + Returns \c nullptr if either the row/column is invalid or if the cell is empty. In those cases, a + qDebug message is printed. To check whether a cell exists and isn't empty, use \ref hasElement. + + \see addElement, hasElement +*/ +QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const +{ + if (row >= 0 && row < mElements.size()) + { + if (column >= 0 && column < mElements.first().size()) + { + if (QCPLayoutElement *result = mElements.at(row).at(column)) + return result; + else + qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column; + } else + qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column; + } else + qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column; + return nullptr; +} + + +/*! \overload + + Adds the \a element to cell with \a row and \a column. If \a element is already in a layout, it + is first removed from there. If \a row or \a column don't exist yet, the layout is expanded + accordingly. + + Returns true if the element was added successfully, i.e. if the cell at \a row and \a column + didn't already have an element. + + Use the overload of this method without explicit row/column index to place the element according + to the configured fill order and wrapping settings. + + \see element, hasElement, take, remove +*/ +bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element) +{ + if (!hasElement(row, column)) + { + if (element && element->layout()) // remove from old layout first + element->layout()->take(element); + expandTo(row+1, column+1); + mElements[row][column] = element; + if (element) + adoptElement(element); + return true; + } else + qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column; + return false; +} + +/*! \overload + + Adds the \a element to the next empty cell according to the current fill order (\ref + setFillOrder) and wrapping (\ref setWrap). If \a element is already in a layout, it is first + removed from there. If necessary, the layout is expanded to hold the new element. + + Returns true if the element was added successfully. + + \see setFillOrder, setWrap, element, hasElement, take, remove +*/ +bool QCPLayoutGrid::addElement(QCPLayoutElement *element) +{ + int rowIndex = 0; + int colIndex = 0; + if (mFillOrder == foColumnsFirst) + { + while (hasElement(rowIndex, colIndex)) + { + ++colIndex; + if (colIndex >= mWrap && mWrap > 0) + { + colIndex = 0; + ++rowIndex; + } + } + } else + { + while (hasElement(rowIndex, colIndex)) + { + ++rowIndex; + if (rowIndex >= mWrap && mWrap > 0) + { + rowIndex = 0; + ++colIndex; + } + } + } + return addElement(rowIndex, colIndex, element); +} + +/*! + Returns whether the cell at \a row and \a column exists and contains a valid element, i.e. isn't + empty. + + \see element +*/ +bool QCPLayoutGrid::hasElement(int row, int column) +{ + if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount()) + return mElements.at(row).at(column); + else + return false; +} + +/*! + Sets the stretch \a factor of \a column. + + Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond + their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref + QCPLayoutElement::setSizeConstraintRect.) + + The default stretch factor of newly created rows/columns is 1. + + \see setColumnStretchFactors, setRowStretchFactor +*/ +void QCPLayoutGrid::setColumnStretchFactor(int column, double factor) +{ + if (column >= 0 && column < columnCount()) + { + if (factor > 0) + mColumnStretchFactors[column] = factor; + else + qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor; + } else + qDebug() << Q_FUNC_INFO << "Invalid column:" << column; +} + +/*! + Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount. + + Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond + their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref + QCPLayoutElement::setSizeConstraintRect.) + + The default stretch factor of newly created rows/columns is 1. + + \see setColumnStretchFactor, setRowStretchFactors +*/ +void QCPLayoutGrid::setColumnStretchFactors(const QList &factors) +{ + if (factors.size() == mColumnStretchFactors.size()) + { + mColumnStretchFactors = factors; + for (int i=0; i= 0 && row < rowCount()) + { + if (factor > 0) + mRowStretchFactors[row] = factor; + else + qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor; + } else + qDebug() << Q_FUNC_INFO << "Invalid row:" << row; +} + +/*! + Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount. + + Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond + their minimum and maximum widths/heights, regardless of the stretch factor. (see \ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize, \ref + QCPLayoutElement::setSizeConstraintRect.) + + The default stretch factor of newly created rows/columns is 1. + + \see setRowStretchFactor, setColumnStretchFactors +*/ +void QCPLayoutGrid::setRowStretchFactors(const QList &factors) +{ + if (factors.size() == mRowStretchFactors.size()) + { + mRowStretchFactors = factors; + for (int i=0; i tempElements; + if (rearrange) + { + tempElements.reserve(elCount); + for (int i=0; i()); + mRowStretchFactors.append(1); + } + // go through rows and expand columns as necessary: + int newColCount = qMax(columnCount(), newColumnCount); + for (int i=0; i rowCount()) + newIndex = rowCount(); + + mRowStretchFactors.insert(newIndex, 1); + QList newRow; + for (int col=0; col columnCount()) + newIndex = columnCount(); + + mColumnStretchFactors.insert(newIndex, 1); + for (int row=0; row= 0 && row < rowCount()) + { + if (column >= 0 && column < columnCount()) + { + switch (mFillOrder) + { + case foRowsFirst: return column*rowCount() + row; + case foColumnsFirst: return row*columnCount() + column; + } + } else + qDebug() << Q_FUNC_INFO << "row index out of bounds:" << row; + } else + qDebug() << Q_FUNC_INFO << "column index out of bounds:" << column; + return 0; +} + +/*! + Converts the linear index to row and column indices and writes the result to \a row and \a + column. + + The way the cells are indexed depends on \ref setFillOrder. If it is \ref foRowsFirst, the + indices increase left to right and then top to bottom. If it is \ref foColumnsFirst, the indices + increase top to bottom and then left to right. + + If there are no cells (i.e. column or row count is zero), sets \a row and \a column to -1. + + For the retrieved \a row and \a column to be valid, the passed \a index must be valid itself, + i.e. greater or equal to zero and smaller than the current \ref elementCount. + + \see rowColToIndex +*/ +void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const +{ + row = -1; + column = -1; + const int nCols = columnCount(); + const int nRows = rowCount(); + if (nCols == 0 || nRows == 0) + return; + if (index < 0 || index >= elementCount()) + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return; + } + + switch (mFillOrder) + { + case foRowsFirst: + { + column = index / nRows; + row = index % nRows; + break; + } + case foColumnsFirst: + { + row = index / nCols; + column = index % nCols; + break; + } + } +} + +/* inherits documentation from base class */ +void QCPLayoutGrid::updateLayout() +{ + QVector minColWidths, minRowHeights, maxColWidths, maxRowHeights; + getMinimumRowColSizes(&minColWidths, &minRowHeights); + getMaximumRowColSizes(&maxColWidths, &maxRowHeights); + + int totalRowSpacing = (rowCount()-1) * mRowSpacing; + int totalColSpacing = (columnCount()-1) * mColumnSpacing; + QVector colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing); + QVector rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing); + + // go through cells and set rects accordingly: + int yOffset = mRect.top(); + for (int row=0; row 0) + yOffset += rowHeights.at(row-1)+mRowSpacing; + int xOffset = mRect.left(); + for (int col=0; col 0) + xOffset += colWidths.at(col-1)+mColumnSpacing; + if (mElements.at(row).at(col)) + mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row))); + } + } +} + +/*! + \seebaseclassmethod + + Note that the association of the linear \a index to the row/column based cells depends on the + current setting of \ref setFillOrder. + + \see rowColToIndex +*/ +QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const +{ + if (index >= 0 && index < elementCount()) + { + int row, col; + indexToRowCol(index, row, col); + return mElements.at(row).at(col); + } else + return nullptr; +} + +/*! + \seebaseclassmethod + + Note that the association of the linear \a index to the row/column based cells depends on the + current setting of \ref setFillOrder. + + \see rowColToIndex +*/ +QCPLayoutElement *QCPLayoutGrid::takeAt(int index) +{ + if (QCPLayoutElement *el = elementAt(index)) + { + releaseElement(el); + int row, col; + indexToRowCol(index, row, col); + mElements[row][col] = nullptr; + return el; + } else + { + qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index; + return nullptr; + } +} + +/* inherits documentation from base class */ +bool QCPLayoutGrid::take(QCPLayoutElement *element) +{ + if (element) + { + for (int i=0; i QCPLayoutGrid::elements(bool recursive) const +{ + QList result; + const int elCount = elementCount(); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + result.reserve(elCount); +#endif + for (int i=0; ielements(recursive); + } + } + return result; +} + +/*! + Simplifies the layout by collapsing rows and columns which only contain empty cells. +*/ +void QCPLayoutGrid::simplify() +{ + // remove rows with only empty cells: + for (int row=rowCount()-1; row>=0; --row) + { + bool hasElements = false; + for (int col=0; col=0; --col) + { + bool hasElements = false; + for (int row=0; row minColWidths, minRowHeights; + getMinimumRowColSizes(&minColWidths, &minRowHeights); + QSize result(0, 0); + foreach (int w, minColWidths) + result.rwidth() += w; + foreach (int h, minRowHeights) + result.rheight() += h; + result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing; + result.rheight() += qMax(0, rowCount()-1) * mRowSpacing; + result.rwidth() += mMargins.left()+mMargins.right(); + result.rheight() += mMargins.top()+mMargins.bottom(); + return result; +} + +/* inherits documentation from base class */ +QSize QCPLayoutGrid::maximumOuterSizeHint() const +{ + QVector maxColWidths, maxRowHeights; + getMaximumRowColSizes(&maxColWidths, &maxRowHeights); + + QSize result(0, 0); + foreach (int w, maxColWidths) + result.setWidth(qMin(result.width()+w, QWIDGETSIZE_MAX)); + foreach (int h, maxRowHeights) + result.setHeight(qMin(result.height()+h, QWIDGETSIZE_MAX)); + result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing; + result.rheight() += qMax(0, rowCount()-1) * mRowSpacing; + result.rwidth() += mMargins.left()+mMargins.right(); + result.rheight() += mMargins.top()+mMargins.bottom(); + if (result.height() > QWIDGETSIZE_MAX) + result.setHeight(QWIDGETSIZE_MAX); + if (result.width() > QWIDGETSIZE_MAX) + result.setWidth(QWIDGETSIZE_MAX); + return result; +} + +/*! \internal + + Places the minimum column widths and row heights into \a minColWidths and \a minRowHeights + respectively. + + The minimum height of a row is the largest minimum height of any element's outer rect in that + row. The minimum width of a column is the largest minimum width of any element's outer rect in + that column. + + This is a helper function for \ref updateLayout. + + \see getMaximumRowColSizes +*/ +void QCPLayoutGrid::getMinimumRowColSizes(QVector *minColWidths, QVector *minRowHeights) const +{ + *minColWidths = QVector(columnCount(), 0); + *minRowHeights = QVector(rowCount(), 0); + for (int row=0; rowat(col) < minSize.width()) + (*minColWidths)[col] = minSize.width(); + if (minRowHeights->at(row) < minSize.height()) + (*minRowHeights)[row] = minSize.height(); + } + } + } +} + +/*! \internal + + Places the maximum column widths and row heights into \a maxColWidths and \a maxRowHeights + respectively. + + The maximum height of a row is the smallest maximum height of any element's outer rect in that + row. The maximum width of a column is the smallest maximum width of any element's outer rect in + that column. + + This is a helper function for \ref updateLayout. + + \see getMinimumRowColSizes +*/ +void QCPLayoutGrid::getMaximumRowColSizes(QVector *maxColWidths, QVector *maxRowHeights) const +{ + *maxColWidths = QVector(columnCount(), QWIDGETSIZE_MAX); + *maxRowHeights = QVector(rowCount(), QWIDGETSIZE_MAX); + for (int row=0; rowat(col) > maxSize.width()) + (*maxColWidths)[col] = maxSize.width(); + if (maxRowHeights->at(row) > maxSize.height()) + (*maxRowHeights)[row] = maxSize.height(); + } + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLayoutInset +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPLayoutInset + \brief A layout that places child elements aligned to the border or arbitrarily positioned + + Elements are placed either aligned to the border or at arbitrary position in the area of the + layout. Which placement applies is controlled with the \ref InsetPlacement (\ref + setInsetPlacement). + + Elements are added via \ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or + addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset + placement will default to \ref ipBorderAligned and the element will be aligned according to the + \a alignment parameter. The second method defaults to \ref ipFree and allows placing elements at + arbitrary position and size, defined by \a rect. + + The alignment or rect can be set via \ref setInsetAlignment or \ref setInsetRect, respectively. + + This is the layout that every QCPAxisRect has as \ref QCPAxisRect::insetLayout. +*/ + +/* start documentation of inline functions */ + +/*! \fn virtual void QCPLayoutInset::simplify() + + The QCPInsetLayout does not need simplification since it can never have empty cells due to its + linear index structure. This method does nothing. +*/ + +/* end documentation of inline functions */ + +/*! + Creates an instance of QCPLayoutInset and sets default values. +*/ +QCPLayoutInset::QCPLayoutInset() +{ +} + +QCPLayoutInset::~QCPLayoutInset() +{ + // clear all child layout elements. This is important because only the specific layouts know how + // to handle removing elements (clear calls virtual removeAt method to do that). + clear(); +} + +/*! + Returns the placement type of the element with the specified \a index. +*/ +QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const +{ + if (elementAt(index)) + return mInsetPlacement.at(index); + else + { + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; + return ipFree; + } +} + +/*! + Returns the alignment of the element with the specified \a index. The alignment only has a + meaning, if the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned. +*/ +Qt::Alignment QCPLayoutInset::insetAlignment(int index) const +{ + if (elementAt(index)) + return mInsetAlignment.at(index); + else + { + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; +#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) + return nullptr; +#else + return {}; +#endif + } +} + +/*! + Returns the rect of the element with the specified \a index. The rect only has a + meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree. +*/ +QRectF QCPLayoutInset::insetRect(int index) const +{ + if (elementAt(index)) + return mInsetRect.at(index); + else + { + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; + return {}; + } +} + +/*! + Sets the inset placement type of the element with the specified \a index to \a placement. + + \see InsetPlacement +*/ +void QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement) +{ + if (elementAt(index)) + mInsetPlacement[index] = placement; + else + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; +} + +/*! + If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this function + is used to set the alignment of the element with the specified \a index to \a alignment. + + \a alignment is an or combination of the following alignment flags: Qt::AlignLeft, + Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other + alignment flags will be ignored. +*/ +void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment) +{ + if (elementAt(index)) + mInsetAlignment[index] = alignment; + else + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; +} + +/*! + If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function is used to set the + position and size of the element with the specified \a index to \a rect. + + \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1) + will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right + corner of the layout, with 35% width and height of the parent layout. + + Note that the minimum and maximum sizes of the embedded element (\ref + QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are enforced. +*/ +void QCPLayoutInset::setInsetRect(int index, const QRectF &rect) +{ + if (elementAt(index)) + mInsetRect[index] = rect; + else + qDebug() << Q_FUNC_INFO << "Invalid element index:" << index; +} + +/* inherits documentation from base class */ +void QCPLayoutInset::updateLayout() +{ + for (int i=0; i finalMaxSize.width()) + insetRect.setWidth(finalMaxSize.width()); + if (insetRect.size().height() > finalMaxSize.height()) + insetRect.setHeight(finalMaxSize.height()); + } else if (mInsetPlacement.at(i) == ipBorderAligned) + { + insetRect.setSize(finalMinSize); + Qt::Alignment al = mInsetAlignment.at(i); + if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x()); + else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width()); + else insetRect.moveLeft(int( rect().x()+rect().width()*0.5-finalMinSize.width()*0.5 )); // default to Qt::AlignHCenter + if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y()); + else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height()); + else insetRect.moveTop(int( rect().y()+rect().height()*0.5-finalMinSize.height()*0.5 )); // default to Qt::AlignVCenter + } + mElements.at(i)->setOuterRect(insetRect); + } +} + +/* inherits documentation from base class */ +int QCPLayoutInset::elementCount() const +{ + return static_cast(mElements.size()); +} + +/* inherits documentation from base class */ +QCPLayoutElement *QCPLayoutInset::elementAt(int index) const +{ + if (index >= 0 && index < mElements.size()) + return mElements.at(index); + else + return nullptr; +} + +/* inherits documentation from base class */ +QCPLayoutElement *QCPLayoutInset::takeAt(int index) +{ + if (QCPLayoutElement *el = elementAt(index)) + { + releaseElement(el); + mElements.removeAt(index); + mInsetPlacement.removeAt(index); + mInsetAlignment.removeAt(index); + mInsetRect.removeAt(index); + return el; + } else + { + qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index; + return nullptr; + } +} + +/* inherits documentation from base class */ +bool QCPLayoutInset::take(QCPLayoutElement *element) +{ + if (element) + { + for (int i=0; irealVisibility() && el->selectTest(pos, onlySelectable) >= 0) + return mParentPlot->selectionTolerance()*0.99; + } + return -1; +} + +/*! + Adds the specified \a element to the layout as an inset aligned at the border (\ref + setInsetAlignment is initialized with \ref ipBorderAligned). The alignment is set to \a + alignment. + + \a alignment is an or combination of the following alignment flags: Qt::AlignLeft, + Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other + alignment flags will be ignored. + + \see addElement(QCPLayoutElement *element, const QRectF &rect) +*/ +void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment) +{ + if (element) + { + if (element->layout()) // remove from old layout first + element->layout()->take(element); + mElements.append(element); + mInsetPlacement.append(ipBorderAligned); + mInsetAlignment.append(alignment); + mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4)); + adoptElement(element); + } else + qDebug() << Q_FUNC_INFO << "Can't add nullptr element"; +} + +/*! + Adds the specified \a element to the layout as an inset with free positioning/sizing (\ref + setInsetAlignment is initialized with \ref ipFree). The position and size is set to \a + rect. + + \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1) + will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right + corner of the layout, with 35% width and height of the parent layout. + + \see addElement(QCPLayoutElement *element, Qt::Alignment alignment) +*/ +void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) +{ + if (element) + { + if (element->layout()) // remove from old layout first + element->layout()->take(element); + mElements.append(element); + mInsetPlacement.append(ipFree); + mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop); + mInsetRect.append(rect); + adoptElement(element); + } else + qDebug() << Q_FUNC_INFO << "Can't add nullptr element"; +} +/* end of 'src/layout.cpp' */ + + +/* including file 'src/lineending.cpp' */ +/* modified 2022-11-06T12:45:56, size 11189 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLineEnding +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLineEnding + \brief Handles the different ending decorations for line-like items + + \image html QCPLineEnding.png "The various ending styles currently supported" + + For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine + has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail. + + The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can + be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of + the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item. + For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite + directions, e.g. "outward". This can be changed by \ref setInverted, which would make the + respective arrow point inward. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify a + QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g. + \snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead +*/ + +/*! + Creates a QCPLineEnding instance with default values (style \ref esNone). +*/ +QCPLineEnding::QCPLineEnding() : + mStyle(esNone), + mWidth(8), + mLength(10), + mInverted(false) +{ +} + +/*! + Creates a QCPLineEnding instance with the specified values. +*/ +QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) : + mStyle(style), + mWidth(width), + mLength(length), + mInverted(inverted) +{ +} + +/*! + Sets the style of the ending decoration. +*/ +void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style) +{ + mStyle = style; +} + +/*! + Sets the width of the ending decoration, if the style supports it. On arrows, for example, the + width defines the size perpendicular to the arrow's pointing direction. + + \see setLength +*/ +void QCPLineEnding::setWidth(double width) +{ + mWidth = width; +} + +/*! + Sets the length of the ending decoration, if the style supports it. On arrows, for example, the + length defines the size in pointing direction. + + \see setWidth +*/ +void QCPLineEnding::setLength(double length) +{ + mLength = length; +} + +/*! + Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point + inward when \a inverted is set to true. + + Note that also the \a width direction is inverted. For symmetrical ending styles like arrows or + discs, this doesn't make a difference. However, asymmetric styles like \ref esHalfBar are + affected by it, which can be used to control to which side the half bar points to. +*/ +void QCPLineEnding::setInverted(bool inverted) +{ + mInverted = inverted; +} + +/*! \internal + + Returns the maximum pixel radius the ending decoration might cover, starting from the position + the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item). + + This is relevant for clipping. Only omit painting of the decoration when the position where the + decoration is supposed to be drawn is farther away from the clipping rect than the returned + distance. +*/ +double QCPLineEnding::boundingDistance() const +{ + switch (mStyle) + { + case esNone: + return 0; + + case esFlatArrow: + case esSpikeArrow: + case esLineArrow: + case esSkewedBar: + return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length + + case esDisc: + case esSquare: + case esDiamond: + case esBar: + case esHalfBar: + return mWidth*1.42; // items that only have a width -> width*sqrt(2) + + } + return 0; +} + +/*! + Starting from the origin of this line ending (which is style specific), returns the length + covered by the line ending symbol, in backward direction. + + For example, the \ref esSpikeArrow has a shorter real length than a \ref esFlatArrow, even if + both have the same \ref setLength value, because the spike arrow has an inward curved back, which + reduces the length along its center axis (the drawing origin for arrows is at the tip). + + This function is used for precise, style specific placement of line endings, for example in + QCPAxes. +*/ +double QCPLineEnding::realLength() const +{ + switch (mStyle) + { + case esNone: + case esLineArrow: + case esSkewedBar: + case esBar: + case esHalfBar: + return 0; + + case esFlatArrow: + return mLength; + + case esDisc: + case esSquare: + case esDiamond: + return mWidth*0.5; + + case esSpikeArrow: + return mLength*0.8; + } + return 0; +} + +/*! \internal + + Draws the line ending with the specified \a painter at the position \a pos. The direction of the + line ending is controlled with \a dir. +*/ +void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const +{ + if (mStyle == esNone) + return; + + QCPVector2D lengthVec = dir.normalized() * mLength*(mInverted ? -1 : 1); + if (lengthVec.isNull()) + lengthVec = QCPVector2D(1, 0); + QCPVector2D widthVec = dir.normalized().perpendicular() * mWidth*0.5*(mInverted ? -1 : 1); + + QPen penBackup = painter->pen(); + QBrush brushBackup = painter->brush(); + QPen miterPen = penBackup; + miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey + QBrush brush(painter->pen().color(), Qt::SolidPattern); + switch (mStyle) + { + case esNone: break; + case esFlatArrow: + { + QPointF points[3] = {pos.toPointF(), + (pos-lengthVec+widthVec).toPointF(), + (pos-lengthVec-widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->setBrush(brush); + painter->drawConvexPolygon(points, 3); + painter->setBrush(brushBackup); + painter->setPen(penBackup); + break; + } + case esSpikeArrow: + { + QPointF points[4] = {pos.toPointF(), + (pos-lengthVec+widthVec).toPointF(), + (pos-lengthVec*0.8).toPointF(), + (pos-lengthVec-widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->setBrush(brush); + painter->drawConvexPolygon(points, 4); + painter->setBrush(brushBackup); + painter->setPen(penBackup); + break; + } + case esLineArrow: + { + QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(), + pos.toPointF(), + (pos-lengthVec-widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->drawPolyline(points, 3); + painter->setPen(penBackup); + break; + } + case esDisc: + { + painter->setBrush(brush); + painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5); + painter->setBrush(brushBackup); + break; + } + case esSquare: + { + QCPVector2D widthVecPerp = widthVec.perpendicular(); + QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(), + (pos-widthVecPerp-widthVec).toPointF(), + (pos+widthVecPerp-widthVec).toPointF(), + (pos+widthVecPerp+widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->setBrush(brush); + painter->drawConvexPolygon(points, 4); + painter->setBrush(brushBackup); + painter->setPen(penBackup); + break; + } + case esDiamond: + { + QCPVector2D widthVecPerp = widthVec.perpendicular(); + QPointF points[4] = {(pos-widthVecPerp).toPointF(), + (pos-widthVec).toPointF(), + (pos+widthVecPerp).toPointF(), + (pos+widthVec).toPointF() + }; + painter->setPen(miterPen); + painter->setBrush(brush); + painter->drawConvexPolygon(points, 4); + painter->setBrush(brushBackup); + painter->setPen(penBackup); + break; + } + case esBar: + { + painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF()); + break; + } + case esHalfBar: + { + painter->drawLine((pos+widthVec).toPointF(), pos.toPointF()); + break; + } + case esSkewedBar: + { + QCPVector2D shift; + if (!qFuzzyIsNull(painter->pen().widthF()) || painter->modes().testFlag(QCPPainter::pmNonCosmetic)) + shift = dir.normalized()*qMax(qreal(1.0), painter->pen().widthF())*qreal(0.5); + // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly + painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+shift).toPointF(), + (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+shift).toPointF()); + break; + } + } +} + +/*! \internal + \overload + + Draws the line ending. The direction is controlled with the \a angle parameter in radians. +*/ +void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const +{ + draw(painter, pos, QCPVector2D(qCos(angle), qSin(angle))); +} +/* end of 'src/lineending.cpp' */ + + +/* including file 'src/axis/labelpainter.cpp' */ +/* modified 2022-11-06T12:45:56, size 27519 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLabelPainterPrivate +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! QCPLabelPainterPrivate + + \internal + \brief (Private) + + This is a private class and not part of the public QCustomPlot interface. + +*/ + +const QChar QCPLabelPainterPrivate::SymbolDot(183); +const QChar QCPLabelPainterPrivate::SymbolCross(215); + +/*! + Constructs a QCPLabelPainterPrivate instance. Make sure to not create a new + instance on every redraw, to utilize the caching mechanisms. + + the \a parentPlot does not take ownership of the label painter. Make sure + to delete it appropriately. +*/ +QCPLabelPainterPrivate::QCPLabelPainterPrivate(QCustomPlot *parentPlot) : + mAnchorMode(amRectangular), + mAnchorSide(asLeft), + mAnchorReferenceType(artNormal), + mColor(Qt::black), + mPadding(0), + mRotation(0), + mSubstituteExponent(true), + mMultiplicationSymbol(QChar(215)), + mAbbreviateDecimalPowers(false), + mParentPlot(parentPlot), + mLabelCache(16) +{ + analyzeFontMetrics(); +} + +QCPLabelPainterPrivate::~QCPLabelPainterPrivate() +{ +} + +void QCPLabelPainterPrivate::setAnchorSide(AnchorSide side) +{ + mAnchorSide = side; +} + +void QCPLabelPainterPrivate::setAnchorMode(AnchorMode mode) +{ + mAnchorMode = mode; +} + +void QCPLabelPainterPrivate::setAnchorReference(const QPointF &pixelPoint) +{ + mAnchorReference = pixelPoint; +} + +void QCPLabelPainterPrivate::setAnchorReferenceType(AnchorReferenceType type) +{ + mAnchorReferenceType = type; +} + +void QCPLabelPainterPrivate::setFont(const QFont &font) +{ + if (mFont != font) + { + mFont = font; + analyzeFontMetrics(); + } +} + +void QCPLabelPainterPrivate::setColor(const QColor &color) +{ + mColor = color; +} + +void QCPLabelPainterPrivate::setPadding(int padding) +{ + mPadding = padding; +} + +void QCPLabelPainterPrivate::setRotation(double rotation) +{ + mRotation = qBound(-90.0, rotation, 90.0); +} + +void QCPLabelPainterPrivate::setSubstituteExponent(bool enabled) +{ + mSubstituteExponent = enabled; +} + +void QCPLabelPainterPrivate::setMultiplicationSymbol(QChar symbol) +{ + mMultiplicationSymbol = symbol; +} + +void QCPLabelPainterPrivate::setAbbreviateDecimalPowers(bool enabled) +{ + mAbbreviateDecimalPowers = enabled; +} + +void QCPLabelPainterPrivate::setCacheSize(int labelCount) +{ + mLabelCache.setMaxCost(labelCount); +} + +int QCPLabelPainterPrivate::cacheSize() const +{ + return static_cast(mLabelCache.maxCost()); +} + +void QCPLabelPainterPrivate::drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text) +{ + double realRotation = mRotation; + + AnchorSide realSide = mAnchorSide; + // for circular axes, the anchor side is determined depending on the quadrant of tickPos with respect to mCircularReference + if (mAnchorMode == amSkewedUpright) + { + realSide = skewedAnchorSide(tickPos, 0.2, 0.3); + } else if (mAnchorMode == amSkewedRotated) // in this mode every label is individually rotated to match circle tangent + { + realSide = skewedAnchorSide(tickPos, 0, 0); + realRotation += QCPVector2D(tickPos-mAnchorReference).angle()/M_PI*180.0; + if (realRotation > 90) realRotation -= 180; + else if (realRotation < -90) realRotation += 180; + } + + realSide = rotationCorrectedSide(realSide, realRotation); // rotation angles may change the true anchor side of the label + drawLabelMaybeCached(painter, mFont, mColor, getAnchorPos(tickPos), realSide, realRotation, text); +} + +/*! \internal + + Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone + direction) needed to fit the axis. +*/ +/* TODO: needed? +int QCPLabelPainterPrivate::size() const +{ + int result = 0; + // get length of tick marks pointing outwards: + if (!tickPositions.isEmpty()) + result += qMax(0, qMax(tickLengthOut, subTickLengthOut)); + + // calculate size of tick labels: + if (tickLabelSide == QCPAxis::lsOutside) + { + QSize tickLabelsSize(0, 0); + if (!tickLabels.isEmpty()) + { + for (int i=0; ibufferDevicePixelRatio())); + result.append(QByteArray::number(mRotation)); + //result.append(QByteArray::number(int(tickLabelSide))); TODO: check whether this is really a cache-invalidating property + result.append(QByteArray::number(int(mSubstituteExponent))); + result.append(QString(mMultiplicationSymbol).toUtf8()); + result.append(mColor.name().toLatin1()+QByteArray::number(mColor.alpha(), 16)); + result.append(mFont.toString().toLatin1()); + return result; +} + +/*! \internal + + Draws a single tick label with the provided \a painter, utilizing the internal label cache to + significantly speed up drawing of labels that were drawn in previous calls. The tick label is + always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in + pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence + for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate), + at which the label should be drawn. + + In order to later draw the axis label in a place that doesn't overlap with the tick labels, the + largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref + drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a + tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently + holds. + + The label is drawn with the font and pen that are currently set on the \a painter. To draw + superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref + getTickLabelData). +*/ +void QCPLabelPainterPrivate::drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text) +{ + // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly! + if (text.isEmpty()) return; + QSize finalSize; + + if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled + { + QByteArray key = cacheKey(text, color, rotation, side); + CachedLabel *cachedLabel = mLabelCache.take(QString::fromUtf8(key)); // attempt to take label from cache (don't use object() because we want ownership/prevent deletion during our operations, we re-insert it afterwards) + if (!cachedLabel) // no cached label existed, create it + { + LabelData labelData = getTickLabelData(font, color, rotation, side, text); + cachedLabel = createCachedLabel(labelData); + } + // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; + /* + if (tickLabelSide == QCPAxis::lsOutside) + { + if (QCPAxis::orientation(type) == Qt::Horizontal) + labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left(); + else + labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top(); + } + */ + if (!labelClippedByBorder) + { + painter->drawPixmap(pos+cachedLabel->offset, cachedLabel->pixmap); + finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio(); // TODO: collect this in a member rect list? + } + mLabelCache.insert(QString::fromUtf8(key), cachedLabel); + } else // label caching disabled, draw text directly on surface: + { + LabelData labelData = getTickLabelData(font, color, rotation, side, text); + // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; + /* + if (tickLabelSide == QCPAxis::lsOutside) + { + if (QCPAxis::orientation(type) == Qt::Horizontal) + labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left(); + else + labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top(); + } + */ + if (!labelClippedByBorder) + { + drawText(painter, pos, labelData); + finalSize = labelData.rotatedTotalBounds.size(); + } + } + /* + // expand passed tickLabelsSize if current tick label is larger: + if (finalSize.width() > tickLabelsSize->width()) + tickLabelsSize->setWidth(finalSize.width()); + if (finalSize.height() > tickLabelsSize->height()) + tickLabelsSize->setHeight(finalSize.height()); + */ +} + +QPointF QCPLabelPainterPrivate::getAnchorPos(const QPointF &tickPos) +{ + switch (mAnchorMode) + { + case amRectangular: + { + switch (mAnchorSide) + { + case asLeft: return tickPos+QPointF(mPadding, 0); + case asRight: return tickPos+QPointF(-mPadding, 0); + case asTop: return tickPos+QPointF(0, mPadding); + case asBottom: return tickPos+QPointF(0, -mPadding); + case asTopLeft: return tickPos+QPointF(mPadding*M_SQRT1_2, mPadding*M_SQRT1_2); + case asTopRight: return tickPos+QPointF(-mPadding*M_SQRT1_2, mPadding*M_SQRT1_2); + case asBottomRight: return tickPos+QPointF(-mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2); + case asBottomLeft: return tickPos+QPointF(mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2); + default: qDebug() << Q_FUNC_INFO << "invalid mode for anchor side: " << mAnchorSide; break; + } + break; + } + case amSkewedUpright: + // fall through + case amSkewedRotated: + { + QCPVector2D anchorNormal(tickPos-mAnchorReference); + if (mAnchorReferenceType == artTangent) + anchorNormal = anchorNormal.perpendicular(); + anchorNormal.normalize(); + return tickPos+(anchorNormal*mPadding).toPointF(); + } + default: qDebug() << Q_FUNC_INFO << "invalid mode for anchor mode: " << mAnchorMode; break; + } + return tickPos; +} + +/*! \internal + + This is a \ref placeTickLabel helper function. + + Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a + y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to + directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when + QCP::phCacheLabels plotting hint is not set. +*/ +void QCPLabelPainterPrivate::drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const +{ + // backup painter settings that we're about to change: + QTransform oldTransform = painter->transform(); + QFont oldFont = painter->font(); + QPen oldPen = painter->pen(); + + // transform painter to position/rotation: + painter->translate(pos); + painter->setTransform(labelData.transform, true); + + // draw text: + painter->setFont(labelData.baseFont); + painter->setPen(QPen(labelData.color)); + if (!labelData.expPart.isEmpty()) // use superscripted exponent typesetting + { + painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart); + if (!labelData.suffixPart.isEmpty()) + painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart); + painter->setFont(labelData.expFont); + painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart); + } else + { + painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart); + } + + /* Debug code to draw label bounding boxes, baseline, and capheight + painter->save(); + painter->setPen(QPen(QColor(0, 0, 0, 150))); + painter->drawRect(labelData.totalBounds); + const int baseline = labelData.totalBounds.height()-mLetterDescent; + painter->setPen(QPen(QColor(255, 0, 0, 150))); + painter->drawLine(QLineF(0, baseline, labelData.totalBounds.width(), baseline)); + painter->setPen(QPen(QColor(0, 0, 255, 150))); + painter->drawLine(QLineF(0, baseline-mLetterCapHeight, labelData.totalBounds.width(), baseline-mLetterCapHeight)); + painter->restore(); + */ + + // reset painter settings to what it was before: + painter->setTransform(oldTransform); + painter->setFont(oldFont); + painter->setPen(oldPen); +} + +/*! \internal + + This is a \ref placeTickLabel helper function. + + Transforms the passed \a text and \a font to a tickLabelData structure that can then be further + processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and + exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes. +*/ +QCPLabelPainterPrivate::LabelData QCPLabelPainterPrivate::getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const +{ + LabelData result; + result.rotation = rotation; + result.side = side; + result.color = color; + + // determine whether beautiful decimal powers should be used + bool useBeautifulPowers = false; + int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart + int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart + if (mSubstituteExponent) + { + ePos = static_cast(text.indexOf(QLatin1Char('e'))); + if (ePos > 0 && text.at(ePos-1).isDigit()) + { + eLast = ePos; + while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit())) + ++eLast; + if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power + useBeautifulPowers = true; + } + } + + // calculate text bounding rects and do string preparation for beautiful decimal powers: + result.baseFont = font; + if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line + result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding + + QFontMetrics baseFontMetrics(result.baseFont); + if (useBeautifulPowers) + { + // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent: + result.basePart = text.left(ePos); + result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent + // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base: + if (mAbbreviateDecimalPowers && result.basePart == QLatin1String("1")) + result.basePart = QLatin1String("10"); + else + result.basePart += QString(mMultiplicationSymbol) + QLatin1String("10"); + result.expPart = text.mid(ePos+1, eLast-ePos); + // clip "+" and leading zeros off expPart: + while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e' + result.expPart.remove(1, 1); + if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+')) + result.expPart.remove(0, 1); + // prepare smaller font for exponent: + result.expFont = font; + if (result.expFont.pointSize() > 0) + result.expFont.setPointSize(result.expFont.pointSize()*0.75); + else + result.expFont.setPixelSize(result.expFont.pixelSize()*0.75); + // calculate bounding rects of base part(s), exponent part and total one: + result.baseBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart); + result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart); + if (!result.suffixPart.isEmpty()) + result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart); + result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA + } else // useBeautifulPowers == false + { + result.basePart = text; + result.totalBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart); + } + result.totalBounds.moveTopLeft(QPoint(0, 0)); + applyAnchorTransform(result); + result.rotatedTotalBounds = result.transform.mapRect(result.totalBounds); + + return result; +} + +void QCPLabelPainterPrivate::applyAnchorTransform(LabelData &labelData) const +{ + if (!qFuzzyIsNull(labelData.rotation)) + labelData.transform.rotate(labelData.rotation); // rotates effectively clockwise (due to flipped y axis of painter vs widget coordinate system) + + // from now on we translate in rotated label-local coordinate system. + // shift origin of coordinate system to appropriate point on label: + labelData.transform.translate(0, -labelData.totalBounds.height()+mLetterDescent+mLetterCapHeight); // shifts origin to true top of capital (or number) characters + + if (labelData.side == asLeft || labelData.side == asRight) // anchor is centered vertically + labelData.transform.translate(0, -mLetterCapHeight/2.0); + else if (labelData.side == asTop || labelData.side == asBottom) // anchor is centered horizontally + labelData.transform.translate(-labelData.totalBounds.width()/2.0, 0); + + if (labelData.side == asTopRight || labelData.side == asRight || labelData.side == asBottomRight) // anchor is at right + labelData.transform.translate(-labelData.totalBounds.width(), 0); + if (labelData.side == asBottomLeft || labelData.side == asBottom || labelData.side == asBottomRight) // anchor is at bottom (no elseif!) + labelData.transform.translate(0, -mLetterCapHeight); +} + +/*! \internal + + Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label + to be drawn, depending on number format etc. Since only the largest tick label is wanted for the + margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a + smaller width/height. +*/ +/* +void QCPLabelPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const +{ + // note: this function must return the same tick label sizes as the placeTickLabel function. + QSize finalSize; + if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label + { + const CachedLabel *cachedLabel = mLabelCache.object(text); + finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio(); + } else // label caching disabled or no label with this text cached: + { + // TODO: LabelData labelData = getTickLabelData(font, text); + // TODO: finalSize = labelData.rotatedTotalBounds.size(); + } + + // expand passed tickLabelsSize if current tick label is larger: + if (finalSize.width() > tickLabelsSize->width()) + tickLabelsSize->setWidth(finalSize.width()); + if (finalSize.height() > tickLabelsSize->height()) + tickLabelsSize->setHeight(finalSize.height()); +} +*/ + +QCPLabelPainterPrivate::CachedLabel *QCPLabelPainterPrivate::createCachedLabel(const LabelData &labelData) const +{ + CachedLabel *result = new CachedLabel; + + // allocate pixmap with the correct size and pixel ratio: + if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio())) + { + result->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio()); +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED +# ifdef QCP_DEVICEPIXELRATIO_FLOAT + result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF()); +# else + result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio()); +# endif +#endif + } else + result->pixmap = QPixmap(labelData.rotatedTotalBounds.size()); + result->pixmap.fill(Qt::transparent); + + // draw the label into the pixmap + // offset is between label anchor and topleft of cache pixmap, so pixmap can be drawn at pos+offset to make the label anchor appear at pos. + // We use rotatedTotalBounds.topLeft() because rotatedTotalBounds is in a coordinate system where the label anchor is at (0, 0) + result->offset = labelData.rotatedTotalBounds.topLeft(); + QCPPainter cachePainter(&result->pixmap); + drawText(&cachePainter, -result->offset, labelData); + return result; +} + +QByteArray QCPLabelPainterPrivate::cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const +{ + return text.toUtf8()+ + QByteArray::number(color.red()+256*color.green()+65536*color.blue(), 36)+ + QByteArray::number(color.alpha()+256*int(side), 36)+ + QByteArray::number(int(rotation*100), 36); +} + +QCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const +{ + QCPVector2D anchorNormal = QCPVector2D(tickPos-mAnchorReference); + if (mAnchorReferenceType == artTangent) + anchorNormal = anchorNormal.perpendicular(); + const double radius = anchorNormal.length(); + const double sideHorz = sideExpandHorz*radius; + const double sideVert = sideExpandVert*radius; + if (anchorNormal.x() > sideHorz) + { + if (anchorNormal.y() > sideVert) return asTopLeft; + else if (anchorNormal.y() < -sideVert) return asBottomLeft; + else return asLeft; + } else if (anchorNormal.x() < -sideHorz) + { + if (anchorNormal.y() > sideVert) return asTopRight; + else if (anchorNormal.y() < -sideVert) return asBottomRight; + else return asRight; + } else + { + if (anchorNormal.y() > 0) return asTop; + else return asBottom; + } + return asBottom; // should never be reached +} + +QCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::rotationCorrectedSide(AnchorSide side, double rotation) const +{ + AnchorSide result = side; + const bool rotateClockwise = rotation > 0; + if (!qFuzzyIsNull(rotation)) + { + if (!qFuzzyCompare(qAbs(rotation), 90)) // avoid graphical collision with anchor tangent (e.g. axis line) when rotating, so change anchor side appropriately: + { + if (side == asTop) result = rotateClockwise ? asLeft : asRight; + else if (side == asBottom) result = rotateClockwise ? asRight : asLeft; + else if (side == asTopLeft) result = rotateClockwise ? asLeft : asTop; + else if (side == asTopRight) result = rotateClockwise ? asTop : asRight; + else if (side == asBottomLeft) result = rotateClockwise ? asBottom : asLeft; + else if (side == asBottomRight) result = rotateClockwise ? asRight : asBottom; + } else // for full rotation by +/-90 degrees, other sides are more appropriate for centering on anchor: + { + if (side == asLeft) result = rotateClockwise ? asBottom : asTop; + else if (side == asRight) result = rotateClockwise ? asTop : asBottom; + else if (side == asTop) result = rotateClockwise ? asLeft : asRight; + else if (side == asBottom) result = rotateClockwise ? asRight : asLeft; + else if (side == asTopLeft) result = rotateClockwise ? asBottomLeft : asTopRight; + else if (side == asTopRight) result = rotateClockwise ? asTopLeft : asBottomRight; + else if (side == asBottomLeft) result = rotateClockwise ? asBottomRight : asTopLeft; + else if (side == asBottomRight) result = rotateClockwise ? asTopRight : asBottomLeft; + } + } + return result; +} + +void QCPLabelPainterPrivate::analyzeFontMetrics() +{ + const QFontMetrics fm(mFont); + mLetterCapHeight = fm.tightBoundingRect(QLatin1String("8")).height(); // this method is slow, that's why we query it only upon font change + mLetterDescent = fm.descent(); +} +/* end of 'src/axis/labelpainter.cpp' */ + + +/* including file 'src/axis/axisticker.cpp' */ +/* modified 2022-11-06T12:45:56, size 18693 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTicker +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTicker + \brief The base class tick generator used by QCPAxis to create tick positions and tick labels + + Each QCPAxis has an internal QCPAxisTicker (or a subclass) in order to generate tick positions + and tick labels for the current axis range. The ticker of an axis can be set via \ref + QCPAxis::setTicker. Since that method takes a QSharedPointer, multiple + axes can share the same ticker instance. + + This base class generates normal tick coordinates and numeric labels for linear axes. It picks a + reasonable tick step (the separation between ticks) which results in readable tick labels. The + number of ticks that should be approximately generated can be set via \ref setTickCount. + Depending on the current tick step strategy (\ref setTickStepStrategy), the algorithm either + sacrifices readability to better match the specified tick count (\ref + QCPAxisTicker::tssMeetTickCount) or relaxes the tick count in favor of better tick steps (\ref + QCPAxisTicker::tssReadability), which is the default. + + The following more specialized axis ticker subclasses are available, see details in the + respective class documentation: + +
+ + + + + + + +
QCPAxisTickerFixed\image html axisticker-fixed.png
QCPAxisTickerLog\image html axisticker-log.png
QCPAxisTickerPi\image html axisticker-pi.png
QCPAxisTickerText\image html axisticker-text.png
QCPAxisTickerDateTime\image html axisticker-datetime.png
QCPAxisTickerTime\image html axisticker-time.png + \image html axisticker-time2.png
+
+ + \section axisticker-subclassing Creating own axis tickers + + Creating own axis tickers can be achieved very easily by sublassing QCPAxisTicker and + reimplementing some or all of the available virtual methods. + + In the simplest case you might wish to just generate different tick steps than the other tickers, + so you only reimplement the method \ref getTickStep. If you additionally want control over the + string that will be shown as tick label, reimplement \ref getTickLabel. + + If you wish to have complete control, you can generate the tick vectors and tick label vectors + yourself by reimplementing \ref createTickVector and \ref createLabelVector. The default + implementations use the previously mentioned virtual methods \ref getTickStep and \ref + getTickLabel, but your reimplementations don't necessarily need to do so. For example in the case + of unequal tick steps, the method \ref getTickStep loses its usefulness and can be ignored. + + The sub tick count between major ticks can be controlled with \ref getSubTickCount. Full sub tick + placement control is obtained by reimplementing \ref createSubTickVector. + + See the documentation of all these virtual methods in QCPAxisTicker for detailed information + about the parameters and expected return values. +*/ + +/*! + Constructs the ticker and sets reasonable default values. Axis tickers are commonly created + managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. +*/ +QCPAxisTicker::QCPAxisTicker() : + mTickStepStrategy(tssReadability), + mTickCount(5), + mTickOrigin(0) +{ +} + +QCPAxisTicker::~QCPAxisTicker() +{ + +} + +/*! + Sets which strategy the axis ticker follows when choosing the size of the tick step. For the + available strategies, see \ref TickStepStrategy. +*/ +void QCPAxisTicker::setTickStepStrategy(QCPAxisTicker::TickStepStrategy strategy) +{ + mTickStepStrategy = strategy; +} + +/*! + Sets how many ticks this ticker shall aim to generate across the axis range. Note that \a count + is not guaranteed to be matched exactly, as generating readable tick intervals may conflict with + the requested number of ticks. + + Whether the readability has priority over meeting the requested \a count can be specified with + \ref setTickStepStrategy. +*/ +void QCPAxisTicker::setTickCount(int count) +{ + if (count > 0) + mTickCount = count; + else + qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count; +} + +/*! + Sets the mathematical coordinate (or "offset") of the zeroth tick. This tick coordinate is just a + concept and doesn't need to be inside the currently visible axis range. + + By default \a origin is zero, which for example yields ticks {-5, 0, 5, 10, 15,...} when the tick + step is five. If \a origin is now set to 1 instead, the correspondingly generated ticks would be + {-4, 1, 6, 11, 16,...}. +*/ +void QCPAxisTicker::setTickOrigin(double origin) +{ + mTickOrigin = origin; +} + +/*! + This is the method called by QCPAxis in order to actually generate tick coordinates (\a ticks), + tick label strings (\a tickLabels) and sub tick coordinates (\a subTicks). + + The ticks are generated for the specified \a range. The generated labels typically follow the + specified \a locale, \a formatChar and number \a precision, however this might be different (or + even irrelevant) for certain QCPAxisTicker subclasses. + + The output parameter \a ticks is filled with the generated tick positions in axis coordinates. + The output parameters \a subTicks and \a tickLabels are optional (set them to \c nullptr if not + needed) and are respectively filled with sub tick coordinates, and tick label strings belonging + to \a ticks by index. +*/ +void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector &ticks, QVector *subTicks, QVector *tickLabels) +{ + // generate (major) ticks: + double tickStep = getTickStep(range); + ticks = createTickVector(tickStep, range); + trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more) + + // generate sub ticks between major ticks: + if (subTicks) + { + if (!ticks.isEmpty()) + { + *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks); + trimTicks(range, *subTicks, false); + } else + *subTicks = QVector(); + } + + // finally trim also outliers (no further clipping happens in axis drawing): + trimTicks(range, ticks, false); + // generate labels for visible ticks if requested: + if (tickLabels) + *tickLabels = createLabelVector(ticks, locale, formatChar, precision); +} + +/*! \internal + + Takes the entire currently visible axis range and returns a sensible tick step in + order to provide readable tick labels as well as a reasonable number of tick counts (see \ref + setTickCount, \ref setTickStepStrategy). + + If a QCPAxisTicker subclass only wants a different tick step behaviour than the default + implementation, it should reimplement this method. See \ref cleanMantissa for a possible helper + function. +*/ +double QCPAxisTicker::getTickStep(const QCPRange &range) +{ + double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + return cleanMantissa(exactStep); +} + +/*! \internal + + Takes the \a tickStep, i.e. the distance between two consecutive ticks, and returns + an appropriate number of sub ticks for that specific tick step. + + Note that a returned sub tick count of e.g. 4 will split each tick interval into 5 sections. +*/ +int QCPAxisTicker::getSubTickCount(double tickStep) +{ + int result = 1; // default to 1, if no proper value can be found + + // separate integer and fractional part of mantissa: + double epsilon = 0.01; + double intPartf; + int intPart; + double fracPart = modf(getMantissa(tickStep), &intPartf); + intPart = int(intPartf); + + // handle cases with (almost) integer mantissa: + if (fracPart < epsilon || 1.0-fracPart < epsilon) + { + if (1.0-fracPart < epsilon) + ++intPart; + switch (intPart) + { + case 1: result = 4; break; // 1.0 -> 0.2 substep + case 2: result = 3; break; // 2.0 -> 0.5 substep + case 3: result = 2; break; // 3.0 -> 1.0 substep + case 4: result = 3; break; // 4.0 -> 1.0 substep + case 5: result = 4; break; // 5.0 -> 1.0 substep + case 6: result = 2; break; // 6.0 -> 2.0 substep + case 7: result = 6; break; // 7.0 -> 1.0 substep + case 8: result = 3; break; // 8.0 -> 2.0 substep + case 9: result = 2; break; // 9.0 -> 3.0 substep + } + } else + { + // handle cases with significantly fractional mantissa: + if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa + { + switch (intPart) + { + case 1: result = 2; break; // 1.5 -> 0.5 substep + case 2: result = 4; break; // 2.5 -> 0.5 substep + case 3: result = 4; break; // 3.5 -> 0.7 substep + case 4: result = 2; break; // 4.5 -> 1.5 substep + case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on) + case 6: result = 4; break; // 6.5 -> 1.3 substep + case 7: result = 2; break; // 7.5 -> 2.5 substep + case 8: result = 4; break; // 8.5 -> 1.7 substep + case 9: result = 4; break; // 9.5 -> 1.9 substep + } + } + // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default + } + + return result; +} + +/*! \internal + + This method returns the tick label string as it should be printed under the \a tick coordinate. + If a textual number is returned, it should respect the provided \a locale, \a formatChar and \a + precision. + + If the returned value contains exponentials of the form "2e5" and beautifully typeset powers is + enabled in the QCPAxis number format (\ref QCPAxis::setNumberFormat), the exponential part will + be formatted accordingly using multiplication symbol and superscript during rendering of the + label automatically. +*/ +QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) +{ + return locale.toString(tick, formatChar.toLatin1(), precision); +} + +/*! \internal + + Returns a vector containing all coordinates of sub ticks that should be drawn. It generates \a + subTickCount sub ticks between each tick pair given in \a ticks. + + If a QCPAxisTicker subclass needs maximal control over the generated sub ticks, it should + reimplement this method. Depending on the purpose of the subclass it doesn't necessarily need to + base its result on \a subTickCount or \a ticks. +*/ +QVector QCPAxisTicker::createSubTickVector(int subTickCount, const QVector &ticks) +{ + QVector result; + if (subTickCount <= 0 || ticks.size() < 2) + return result; + + result.reserve((ticks.size()-1)*subTickCount); + for (int i=1; i QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range) +{ + QVector result; + // Generate tick positions according to tickStep: + qint64 firstStep = qint64(floor((range.lower-mTickOrigin)/tickStep)); // do not use qFloor here, or we'll lose 64 bit precision + qint64 lastStep = qint64(ceil((range.upper-mTickOrigin)/tickStep)); // do not use qCeil here, or we'll lose 64 bit precision + int tickcount = int(lastStep-firstStep+1); + if (tickcount < 0) tickcount = 0; + result.resize(tickcount); + for (int i=0; i QCPAxisTicker::createLabelVector(const QVector &ticks, const QLocale &locale, QChar formatChar, int precision) +{ + QVector result; + result.reserve(ticks.size()); + foreach (double tickCoord, ticks) + result.append(getTickLabel(tickCoord, locale, formatChar, precision)); + return result; +} + +/*! \internal + + Removes tick coordinates from \a ticks which lie outside the specified \a range. If \a + keepOneOutlier is true, it preserves one tick just outside the range on both sides, if present. + + The passed \a ticks must be sorted in ascending order. +*/ +void QCPAxisTicker::trimTicks(const QCPRange &range, QVector &ticks, bool keepOneOutlier) const +{ + bool lowFound = false; + bool highFound = false; + int lowIndex = 0; + int highIndex = -1; + + for (int i=0; i < ticks.size(); ++i) + { + if (ticks.at(i) >= range.lower) + { + lowFound = true; + lowIndex = i; + break; + } + } + for (int i=static_cast(ticks.size())-1; i >= 0; --i) + { + if (ticks.at(i) <= range.upper) + { + highFound = true; + highIndex = i; + break; + } + } + + if (highFound && lowFound) + { + int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0)); + int trimBack = qMax(0, static_cast(ticks.size())-(keepOneOutlier ? 2 : 1)-highIndex); + if (trimFront > 0 || trimBack > 0) + ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack); + } else // all ticks are either all below or all above the range + ticks.clear(); +} + +/*! \internal + + Returns the coordinate contained in \a candidates which is closest to the provided \a target. + + This method assumes \a candidates is not empty and sorted in ascending order. +*/ +double QCPAxisTicker::pickClosest(double target, const QVector &candidates) const +{ + if (candidates.size() == 1) + return candidates.first(); + QVector::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target); + if (it == candidates.constEnd()) + return *(it-1); + else if (it == candidates.constBegin()) + return *it; + else + return target-*(it-1) < *it-target ? *(it-1) : *it; +} + +/*! \internal + + Returns the decimal mantissa of \a input. Optionally, if \a magnitude is not set to zero, it also + returns the magnitude of \a input as a power of 10. + + For example, an input of 142.6 will return a mantissa of 1.426 and a magnitude of 100. +*/ +double QCPAxisTicker::getMantissa(double input, double *magnitude) const +{ + const double mag = std::pow(10.0, std::floor(std::log10(input))); + if (magnitude) *magnitude = mag; + return input/mag; +} + +/*! \internal + + Returns a number that is close to \a input but has a clean, easier human readable mantissa. How + strongly the mantissa is altered, and thus how strong the result deviates from the original \a + input, depends on the current tick step strategy (see \ref setTickStepStrategy). +*/ +double QCPAxisTicker::cleanMantissa(double input) const +{ + double magnitude; + const double mantissa = getMantissa(input, &magnitude); + switch (mTickStepStrategy) + { + case tssReadability: + { + return pickClosest(mantissa, QVector() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude; + } + case tssMeetTickCount: + { + // this gives effectively a mantissa of 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0 + if (mantissa <= 5.0) + return int(mantissa*2)/2.0*magnitude; // round digit after decimal point to 0.5 + else + return int(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2 + } + } + return input; +} +/* end of 'src/axis/axisticker.cpp' */ + + +/* including file 'src/axis/axistickerdatetime.cpp' */ +/* modified 2022-11-06T12:45:56, size 18829 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTickerDateTime +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTickerDateTime + \brief Specialized axis ticker for calendar dates and times as axis ticks + + \image html axisticker-datetime.png + + This QCPAxisTicker subclass generates ticks that correspond to real calendar dates and times. The + plot axis coordinate is interpreted as Unix Time, so seconds since Epoch (January 1, 1970, 00:00 + UTC). This is also used for example by QDateTime in the toTime_t()/setTime_t() methods + with a precision of one second. Since Qt 4.7, millisecond accuracy can be obtained from QDateTime + by using QDateTime::fromMSecsSinceEpoch()/1000.0. The static methods \ref dateTimeToKey + and \ref keyToDateTime conveniently perform this conversion achieving a precision of one + millisecond on all Qt versions. + + The format of the date/time display in the tick labels is controlled with \ref setDateTimeFormat. + If a different time spec or time zone shall be used for the tick label appearance, see \ref + setDateTimeSpec or \ref setTimeZone, respectively. + + This ticker produces unequal tick spacing in order to provide intuitive date and time-of-day + ticks. For example, if the axis range spans a few years such that there is one tick per year, + ticks will be positioned on 1. January of every year. This is intuitive but, due to leap years, + will result in slightly unequal tick intervals (visually unnoticeable). The same can be seen in + the image above: even though the number of days varies month by month, this ticker generates + ticks on the same day of each month. + + If you would like to change the date/time that is used as a (mathematical) starting date for the + ticks, use the \ref setTickOrigin(const QDateTime &origin) method overload, which takes a + QDateTime. If you pass 15. July, 9:45 to this method, the yearly ticks will end up on 15. July at + 9:45 of every year. + + The ticker can be created and assigned to an axis like this: + \snippet documentation/doc-image-generator/mainwindow.cpp axistickerdatetime-creation + + \note If you rather wish to display relative times in terms of days, hours, minutes, seconds and + milliseconds, and are not interested in the intricacies of real calendar dates with months and + (leap) years, have a look at QCPAxisTickerTime instead. +*/ + +/*! + Constructs the ticker and sets reasonable default values. Axis tickers are commonly created + managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. +*/ +QCPAxisTickerDateTime::QCPAxisTickerDateTime() : + mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")), + mDateTimeSpec(Qt::LocalTime), + mDateStrategy(dsNone) +{ + setTickCount(4); +} + +/*! + Sets the format in which dates and times are displayed as tick labels. For details about the \a + format string, see the documentation of QDateTime::toString(). + + Typical expressions are + + + + + + + + + + + + + + + + + + + + + + + + +
\c dThe day as a number without a leading zero (1 to 31)
\c ddThe day as a number with a leading zero (01 to 31)
\c dddThe abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name, i.e. QLocale::system().
\c ddddThe long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name, i.e. QLocale::system().
\c MThe month as a number without a leading zero (1 to 12)
\c MMThe month as a number with a leading zero (01 to 12)
\c MMMThe abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name, i.e. QLocale::system().
\c MMMMThe long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name, i.e. QLocale::system().
\c yyThe year as a two digit number (00 to 99)
\c yyyyThe year as a four digit number. If the year is negative, a minus sign is prepended, making five characters.
\c hThe hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)
\c hhThe hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)
\c HThe hour without a leading zero (0 to 23, even with AM/PM display)
\c HHThe hour with a leading zero (00 to 23, even with AM/PM display)
\c mThe minute without a leading zero (0 to 59)
\c mmThe minute with a leading zero (00 to 59)
\c sThe whole second, without any leading zero (0 to 59)
\c ssThe whole second, with a leading zero where applicable (00 to 59)
\c zThe fractional part of the second, to go after a decimal point, without trailing zeroes (0 to 999). Thus "s.z" reports the seconds to full available (millisecond) precision without trailing zeroes.
\c zzzThe fractional part of the second, to millisecond precision, including trailing zeroes where applicable (000 to 999).
\c AP or \c AUse AM/PM display. A/AP will be replaced by an upper-case version of either QLocale::amText() or QLocale::pmText().
\c ap or \c aUse am/pm display. a/ap will be replaced by a lower-case version of either QLocale::amText() or QLocale::pmText().
\c tThe timezone (for example "CEST")
+ + Newlines can be inserted with \c "\n", literal strings (even when containing above expressions) + by encapsulating them using single-quotes. A literal single quote can be generated by using two + consecutive single quotes in the format. + + \see setDateTimeSpec, setTimeZone +*/ +void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format) +{ + mDateTimeFormat = format; +} + +/*! + Sets the time spec that is used for creating the tick labels from corresponding dates/times. + + The default value of QDateTime objects (and also QCPAxisTickerDateTime) is + Qt::LocalTime. However, if the displayed tick labels shall be given in UTC, set \a spec + to Qt::UTC. + + Tick labels corresponding to other time zones can be achieved with \ref setTimeZone (which sets + \a spec to \c Qt::TimeZone internally). Note that if \a spec is afterwards set to not be \c + Qt::TimeZone again, the \ref setTimeZone setting will be ignored accordingly. + + \see setDateTimeFormat, setTimeZone +*/ +void QCPAxisTickerDateTime::setDateTimeSpec(Qt::TimeSpec spec) +{ + mDateTimeSpec = spec; +} + +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) +/*! + Sets the time zone that is used for creating the tick labels from corresponding dates/times. The + time spec (\ref setDateTimeSpec) is set to \c Qt::TimeZone. + + \see setDateTimeFormat, setTimeZone +*/ +void QCPAxisTickerDateTime::setTimeZone(const QTimeZone &zone) +{ + mTimeZone = zone; + mDateTimeSpec = Qt::TimeZone; +} +#endif + +/*! + Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) in seconds since Epoch (1. Jan 1970, + 00:00 UTC). For the date time ticker it might be more intuitive to use the overload which + directly takes a QDateTime, see \ref setTickOrigin(const QDateTime &origin). + + This is useful to define the month/day/time recurring at greater tick interval steps. For + example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick + per year, the ticks will end up on 15. July at 9:45 of every year. +*/ +void QCPAxisTickerDateTime::setTickOrigin(double origin) +{ + QCPAxisTicker::setTickOrigin(origin); +} + +/*! + Sets the tick origin (see \ref QCPAxisTicker::setTickOrigin) as a QDateTime \a origin. + + This is useful to define the month/day/time recurring at greater tick interval steps. For + example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick + per year, the ticks will end up on 15. July at 9:45 of every year. +*/ +void QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin) +{ + setTickOrigin(dateTimeToKey(origin)); +} + +/*! \internal + + Returns a sensible tick step with intervals appropriate for a date-time-display, such as weekly, + monthly, bi-monthly, etc. + + Note that this tick step isn't used exactly when generating the tick vector in \ref + createTickVector, but only as a guiding value requiring some correction for each individual tick + interval. Otherwise this would lead to unintuitive date displays, e.g. jumping between first day + in the month to the last day in the previous month from tick to tick, due to the non-uniform + length of months. The same problem arises with leap years. + + \seebaseclassmethod +*/ +double QCPAxisTickerDateTime::getTickStep(const QCPRange &range) +{ + double result = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + + mDateStrategy = dsNone; // leaving it at dsNone means tick coordinates will not be tuned in any special way in createTickVector + if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds + { + result = cleanMantissa(result); + } else if (result < 86400*30.4375*12) // below a year + { + result = pickClosest(result, QVector() + << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5*60 << 5*60 << 10*60 << 15*60 << 30*60 << 60*60 // second, minute, hour range + << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range + << 86400*2 << 86400*5 << 86400*7 << 86400*14 << 86400*30.4375 << 86400*30.4375*2 << 86400*30.4375*3 << 86400*30.4375*6 << 86400*30.4375*12); // day, week, month range (avg. days per month includes leap years) + if (result > 86400*30.4375-1) // month tick intervals or larger + mDateStrategy = dsUniformDayInMonth; + else if (result > 3600*24-1) // day tick intervals or larger + mDateStrategy = dsUniformTimeInDay; + } else // more than a year, go back to normal clean mantissa algorithm but in units of years + { + const double secondsPerYear = 86400*30.4375*12; // average including leap years + result = cleanMantissa(result/secondsPerYear)*secondsPerYear; + mDateStrategy = dsUniformDayInMonth; + } + return result; +} + +/*! \internal + + Returns a sensible sub tick count with intervals appropriate for a date-time-display, such as weekly, + monthly, bi-monthly, etc. + + \seebaseclassmethod +*/ +int QCPAxisTickerDateTime::getSubTickCount(double tickStep) +{ + int result = QCPAxisTicker::getSubTickCount(tickStep); + switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep) + { + case 5*60: result = 4; break; + case 10*60: result = 1; break; + case 15*60: result = 2; break; + case 30*60: result = 1; break; + case 60*60: result = 3; break; + case 3600*2: result = 3; break; + case 3600*3: result = 2; break; + case 3600*6: result = 1; break; + case 3600*12: result = 3; break; + case 3600*24: result = 3; break; + case 86400*2: result = 1; break; + case 86400*5: result = 4; break; + case 86400*7: result = 6; break; + case 86400*14: result = 1; break; + case int(86400*30.4375+0.5): result = 3; break; + case int(86400*30.4375*2+0.5): result = 1; break; + case int(86400*30.4375*3+0.5): result = 2; break; + case int(86400*30.4375*6+0.5): result = 5; break; + case int(86400*30.4375*12+0.5): result = 3; break; + } + return result; +} + +/*! \internal + + Generates a date/time tick label for tick coordinate \a tick, based on the currently set format + (\ref setDateTimeFormat), time spec (\ref setDateTimeSpec), and possibly time zone (\ref + setTimeZone). + + \seebaseclassmethod +*/ +QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) +{ + Q_UNUSED(precision) + Q_UNUSED(formatChar) +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + if (mDateTimeSpec == Qt::TimeZone) + return locale.toString(keyToDateTime(tick).toTimeZone(mTimeZone), mDateTimeFormat); + else + return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat); +# else + return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat); +# endif +} + +/*! \internal + + Uses the passed \a tickStep as a guiding value and applies corrections in order to obtain + non-uniform tick intervals but intuitive tick labels, e.g. falling on the same day of each month. + + \seebaseclassmethod +*/ +QVector QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range) +{ + QVector result = QCPAxisTicker::createTickVector(tickStep, range); + if (!result.isEmpty()) + { + if (mDateStrategy == dsUniformTimeInDay) + { + QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // the time of this datetime will be set for all other ticks, if possible + QDateTime tickDateTime; + for (int i=0; i 15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day + tickDateTime = tickDateTime.addMonths(-1); + tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay)); + result[i] = dateTimeToKey(tickDateTime); + } + } + } + return result; +} + +/*! + A convenience method which turns \a key (in seconds since Epoch 1. Jan 1970, 00:00 UTC) into a + QDateTime object. This can be used to turn axis coordinates to actual QDateTimes. + + The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it + works around the lack of a QDateTime::fromMSecsSinceEpoch in Qt 4.6) + + \see dateTimeToKey +*/ +QDateTime QCPAxisTickerDateTime::keyToDateTime(double key) +{ +# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) + return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000); +# else + return QDateTime::fromMSecsSinceEpoch(qint64(key*1000.0)); +# endif +} + +/*! \overload + + A convenience method which turns a QDateTime object into a double value that corresponds to + seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the format used as axis coordinates by + QCPAxisTickerDateTime. + + The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it + works around the lack of a QDateTime::toMSecsSinceEpoch in Qt 4.6) + + \see keyToDateTime +*/ +double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime &dateTime) +{ +# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) + return dateTime.toTime_t()+dateTime.time().msec()/1000.0; +# else + return dateTime.toMSecsSinceEpoch()/1000.0; +# endif +} + +/*! \overload + + A convenience method which turns a QDate object into a double value that corresponds to seconds + since Epoch (1. Jan 1970, 00:00 UTC). This is the format used + as axis coordinates by QCPAxisTickerDateTime. + + The returned value will be the start of the passed day of \a date, interpreted in the given \a + timeSpec. + + \see keyToDateTime +*/ +double QCPAxisTickerDateTime::dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec) +{ +# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) + return QDateTime(date, QTime(0, 0), timeSpec).toTime_t(); +# elif QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + return QDateTime(date, QTime(0, 0), timeSpec).toMSecsSinceEpoch()/1000.0; +# else + return date.startOfDay(timeSpec).toMSecsSinceEpoch()/1000.0; +# endif +} +/* end of 'src/axis/axistickerdatetime.cpp' */ + + +/* including file 'src/axis/axistickertime.cpp' */ +/* modified 2022-11-06T12:45:56, size 11745 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTickerTime +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTickerTime + \brief Specialized axis ticker for time spans in units of milliseconds to days + + \image html axisticker-time.png + + This QCPAxisTicker subclass generates ticks that corresponds to time intervals. + + The format of the time display in the tick labels is controlled with \ref setTimeFormat and \ref + setFieldWidth. The time coordinate is in the unit of seconds with respect to the time coordinate + zero. Unlike with QCPAxisTickerDateTime, the ticks don't correspond to a specific calendar date + and time. + + The time can be displayed in milliseconds, seconds, minutes, hours and days. Depending on the + largest available unit in the format specified with \ref setTimeFormat, any time spans above will + be carried in that largest unit. So for example if the format string is "%m:%s" and a tick at + coordinate value 7815 (being 2 hours, 10 minutes and 15 seconds) is created, the resulting tick + label will show "130:15" (130 minutes, 15 seconds). If the format string is "%h:%m:%s", the hour + unit will be used and the label will thus be "02:10:15". Negative times with respect to the axis + zero will carry a leading minus sign. + + The ticker can be created and assigned to an axis like this: + \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation + + Here is an example of a time axis providing time information in days, hours and minutes. Due to + the axis range spanning a few days and the wanted tick count (\ref setTickCount), the ticker + decided to use tick steps of 12 hours: + + \image html axisticker-time2.png + + The format string for this example is + \snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation-2 + + \note If you rather wish to display calendar dates and times, have a look at QCPAxisTickerDateTime + instead. +*/ + +/*! + Constructs the ticker and sets reasonable default values. Axis tickers are commonly created + managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. +*/ +QCPAxisTickerTime::QCPAxisTickerTime() : + mTimeFormat(QLatin1String("%h:%m:%s")), + mSmallestUnit(tuSeconds), + mBiggestUnit(tuHours) +{ + setTickCount(4); + mFieldWidth[tuMilliseconds] = 3; + mFieldWidth[tuSeconds] = 2; + mFieldWidth[tuMinutes] = 2; + mFieldWidth[tuHours] = 2; + mFieldWidth[tuDays] = 1; + + mFormatPattern[tuMilliseconds] = QLatin1String("%z"); + mFormatPattern[tuSeconds] = QLatin1String("%s"); + mFormatPattern[tuMinutes] = QLatin1String("%m"); + mFormatPattern[tuHours] = QLatin1String("%h"); + mFormatPattern[tuDays] = QLatin1String("%d"); +} + +/*! + Sets the format that will be used to display time in the tick labels. + + The available patterns are: + - %%z for milliseconds + - %%s for seconds + - %%m for minutes + - %%h for hours + - %%d for days + + The field width (zero padding) can be controlled for each unit with \ref setFieldWidth. + + The largest unit that appears in \a format will carry all the remaining time of a certain tick + coordinate, even if it overflows the natural limit of the unit. For example, if %%m is the + largest unit it might become larger than 59 in order to consume larger time values. If on the + other hand %%h is available, the minutes will wrap around to zero after 59 and the time will + carry to the hour digit. +*/ +void QCPAxisTickerTime::setTimeFormat(const QString &format) +{ + mTimeFormat = format; + + // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest + // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59) + mSmallestUnit = tuMilliseconds; + mBiggestUnit = tuMilliseconds; + bool hasSmallest = false; + for (int i = tuMilliseconds; i <= tuDays; ++i) + { + TimeUnit unit = static_cast(i); + if (mTimeFormat.contains(mFormatPattern.value(unit))) + { + if (!hasSmallest) + { + mSmallestUnit = unit; + hasSmallest = true; + } + mBiggestUnit = unit; + } + } +} + +/*! + Sets the field widh of the specified \a unit to be \a width digits, when displayed in the tick + label. If the number for the specific unit is shorter than \a width, it will be padded with an + according number of zeros to the left in order to reach the field width. + + \see setTimeFormat +*/ +void QCPAxisTickerTime::setFieldWidth(QCPAxisTickerTime::TimeUnit unit, int width) +{ + mFieldWidth[unit] = qMax(width, 1); +} + +/*! \internal + + Returns the tick step appropriate for time displays, depending on the provided \a range and the + smallest available time unit in the current format (\ref setTimeFormat). For example if the unit + of seconds isn't available in the format, this method will not generate steps (like 2.5 minutes) + that require sub-minute precision to be displayed correctly. + + \seebaseclassmethod +*/ +double QCPAxisTickerTime::getTickStep(const QCPRange &range) +{ + double result = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + + if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds + { + if (mSmallestUnit == tuMilliseconds) + result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond + else // have no milliseconds available in format, so stick with 1 second tickstep + result = 1.0; + } else if (result < 3600*24) // below a day + { + // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run + QVector availableSteps; + // seconds range: + if (mSmallestUnit <= tuSeconds) + availableSteps << 1; + if (mSmallestUnit == tuMilliseconds) + availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it + else if (mSmallestUnit == tuSeconds) + availableSteps << 2; + if (mSmallestUnit <= tuSeconds) + availableSteps << 5 << 10 << 15 << 30; + // minutes range: + if (mSmallestUnit <= tuMinutes) + availableSteps << 1*60; + if (mSmallestUnit <= tuSeconds) + availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it + else if (mSmallestUnit == tuMinutes) + availableSteps << 2*60; + if (mSmallestUnit <= tuMinutes) + availableSteps << 5*60 << 10*60 << 15*60 << 30*60; + // hours range: + if (mSmallestUnit <= tuHours) + availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600; + // pick available step that is most appropriate to approximate ideal step: + result = pickClosest(result, availableSteps); + } else // more than a day, go back to normal clean mantissa algorithm but in units of days + { + const double secondsPerDay = 3600*24; + result = cleanMantissa(result/secondsPerDay)*secondsPerDay; + } + return result; +} + +/*! \internal + + Returns the sub tick count appropriate for the provided \a tickStep and time displays. + + \seebaseclassmethod +*/ +int QCPAxisTickerTime::getSubTickCount(double tickStep) +{ + int result = QCPAxisTicker::getSubTickCount(tickStep); + switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep) + { + case 5*60: result = 4; break; + case 10*60: result = 1; break; + case 15*60: result = 2; break; + case 30*60: result = 1; break; + case 60*60: result = 3; break; + case 3600*2: result = 3; break; + case 3600*3: result = 2; break; + case 3600*6: result = 1; break; + case 3600*12: result = 3; break; + case 3600*24: result = 3; break; + } + return result; +} + +/*! \internal + + Returns the tick label corresponding to the provided \a tick and the configured format and field + widths (\ref setTimeFormat, \ref setFieldWidth). + + \seebaseclassmethod +*/ +QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) +{ + Q_UNUSED(precision) + Q_UNUSED(formatChar) + Q_UNUSED(locale) + bool negative = tick < 0; + if (negative) tick *= -1; + double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59) + double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time + + restValues[tuMilliseconds] = tick*1000; + values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000; + values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60; + values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60; + values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24; + // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time) + + QString result = mTimeFormat; + for (int i = mSmallestUnit; i <= mBiggestUnit; ++i) + { + TimeUnit iUnit = static_cast(i); + replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit])); + } + if (negative) + result.prepend(QLatin1Char('-')); + return result; +} + +/*! \internal + + Replaces all occurrences of the format pattern belonging to \a unit in \a text with the specified + \a value, using the field width as specified with \ref setFieldWidth for the \a unit. +*/ +void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const +{ + QString valueStr = QString::number(value); + while (valueStr.size() < mFieldWidth.value(unit)) + valueStr.prepend(QLatin1Char('0')); + + text.replace(mFormatPattern.value(unit), valueStr); +} +/* end of 'src/axis/axistickertime.cpp' */ + + +/* including file 'src/axis/axistickerfixed.cpp' */ +/* modified 2022-11-06T12:45:56, size 5575 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTickerFixed +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTickerFixed + \brief Specialized axis ticker with a fixed tick step + + \image html axisticker-fixed.png + + This QCPAxisTicker subclass generates ticks with a fixed tick step set with \ref setTickStep. It + is also possible to allow integer multiples and integer powers of the specified tick step with + \ref setScaleStrategy. + + A typical application of this ticker is to make an axis only display integers, by setting the + tick step of the ticker to 1.0 and the scale strategy to \ref ssMultiples. + + Another case is when a certain number has a special meaning and axis ticks should only appear at + multiples of that value. In this case you might also want to consider \ref QCPAxisTickerPi + because despite the name it is not limited to only pi symbols/values. + + The ticker can be created and assigned to an axis like this: + \snippet documentation/doc-image-generator/mainwindow.cpp axistickerfixed-creation +*/ + +/*! + Constructs the ticker and sets reasonable default values. Axis tickers are commonly created + managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. +*/ +QCPAxisTickerFixed::QCPAxisTickerFixed() : + mTickStep(1.0), + mScaleStrategy(ssNone) +{ +} + +/*! + Sets the fixed tick interval to \a step. + + The axis ticker will only use this tick step when generating axis ticks. This might cause a very + high tick density and overlapping labels if the axis range is zoomed out. Using \ref + setScaleStrategy it is possible to relax the fixed step and also allow multiples or powers of \a + step. This will enable the ticker to reduce the number of ticks to a reasonable amount (see \ref + setTickCount). +*/ +void QCPAxisTickerFixed::setTickStep(double step) +{ + if (step > 0) + mTickStep = step; + else + qDebug() << Q_FUNC_INFO << "tick step must be greater than zero:" << step; +} + +/*! + Sets whether the specified tick step (\ref setTickStep) is absolutely fixed or whether + modifications may be applied to it before calculating the finally used tick step, such as + permitting multiples or powers. See \ref ScaleStrategy for details. + + The default strategy is \ref ssNone, which means the tick step is absolutely fixed. +*/ +void QCPAxisTickerFixed::setScaleStrategy(QCPAxisTickerFixed::ScaleStrategy strategy) +{ + mScaleStrategy = strategy; +} + +/*! \internal + + Determines the actually used tick step from the specified tick step and scale strategy (\ref + setTickStep, \ref setScaleStrategy). + + This method either returns the specified tick step exactly, or, if the scale strategy is not \ref + ssNone, a modification of it to allow varying the number of ticks in the current axis range. + + \seebaseclassmethod +*/ +double QCPAxisTickerFixed::getTickStep(const QCPRange &range) +{ + switch (mScaleStrategy) + { + case ssNone: + { + return mTickStep; + } + case ssMultiples: + { + double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + if (exactStep < mTickStep) + return mTickStep; + else + return qint64(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep; + } + case ssPowers: + { + double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + return qPow(mTickStep, int(qLn(exactStep)/qLn(mTickStep)+0.5)); + } + } + return mTickStep; +} +/* end of 'src/axis/axistickerfixed.cpp' */ + + +/* including file 'src/axis/axistickertext.cpp' */ +/* modified 2022-11-06T12:45:56, size 8742 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTickerText +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTickerText + \brief Specialized axis ticker which allows arbitrary labels at specified coordinates + + \image html axisticker-text.png + + This QCPAxisTicker subclass generates ticks which can be directly specified by the user as + coordinates and associated strings. They can be passed as a whole with \ref setTicks or one at a + time with \ref addTick. Alternatively you can directly access the internal storage via \ref ticks + and modify the tick/label data there. + + This is useful for cases where the axis represents categories rather than numerical values. + + If you are updating the ticks of this ticker regularly and in a dynamic fasion (e.g. dependent on + the axis range), it is a sign that you should probably create an own ticker by subclassing + QCPAxisTicker, instead of using this one. + + The ticker can be created and assigned to an axis like this: + \snippet documentation/doc-image-generator/mainwindow.cpp axistickertext-creation +*/ + +/* start of documentation of inline functions */ + +/*! \fn QMap &QCPAxisTickerText::ticks() + + Returns a non-const reference to the internal map which stores the tick coordinates and their + labels. + + You can access the map directly in order to add, remove or manipulate ticks, as an alternative to + using the methods provided by QCPAxisTickerText, such as \ref setTicks and \ref addTick. +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs the ticker and sets reasonable default values. Axis tickers are commonly created + managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. +*/ +QCPAxisTickerText::QCPAxisTickerText() : + mSubTickCount(0) +{ +} + +/*! \overload + + Sets the ticks that shall appear on the axis. The map key of \a ticks corresponds to the axis + coordinate, and the map value is the string that will appear as tick label. + + An alternative to manipulate ticks is to directly access the internal storage with the \ref ticks + getter. + + \see addTicks, addTick, clear +*/ +void QCPAxisTickerText::setTicks(const QMap &ticks) +{ + mTicks = ticks; +} + +/*! \overload + + Sets the ticks that shall appear on the axis. The entries of \a positions correspond to the axis + coordinates, and the entries of \a labels are the respective strings that will appear as tick + labels. + + \see addTicks, addTick, clear +*/ +void QCPAxisTickerText::setTicks(const QVector &positions, const QVector &labels) +{ + clear(); + addTicks(positions, labels); +} + +/*! + Sets the number of sub ticks that shall appear between ticks. For QCPAxisTickerText, there is no + automatic sub tick count calculation. So if sub ticks are needed, they must be configured with this + method. +*/ +void QCPAxisTickerText::setSubTickCount(int subTicks) +{ + if (subTicks >= 0) + mSubTickCount = subTicks; + else + qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks; +} + +/*! + Clears all ticks. + + An alternative to manipulate ticks is to directly access the internal storage with the \ref ticks + getter. + + \see setTicks, addTicks, addTick +*/ +void QCPAxisTickerText::clear() +{ + mTicks.clear(); +} + +/*! + Adds a single tick to the axis at the given axis coordinate \a position, with the provided tick \a + label. + + \see addTicks, setTicks, clear +*/ +void QCPAxisTickerText::addTick(double position, const QString &label) +{ + mTicks.insert(position, label); +} + +/*! \overload + + Adds the provided \a ticks to the ones already existing. The map key of \a ticks corresponds to + the axis coordinate, and the map value is the string that will appear as tick label. + + An alternative to manipulate ticks is to directly access the internal storage with the \ref ticks + getter. + + \see addTick, setTicks, clear +*/ +void QCPAxisTickerText::addTicks(const QMap &ticks) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + mTicks.unite(ticks); +#else + mTicks.insert(ticks); +#endif +} + +/*! \overload + + Adds the provided ticks to the ones already existing. The entries of \a positions correspond to + the axis coordinates, and the entries of \a labels are the respective strings that will appear as + tick labels. + + An alternative to manipulate ticks is to directly access the internal storage with the \ref ticks + getter. + + \see addTick, setTicks, clear +*/ +void QCPAxisTickerText::addTicks(const QVector &positions, const QVector &labels) +{ + if (positions.size() != labels.size()) + qDebug() << Q_FUNC_INFO << "passed unequal length vectors for positions and labels:" << positions.size() << labels.size(); + int n = static_cast(qMin(positions.size(), labels.size())); + for (int i=0; i QCPAxisTickerText::createTickVector(double tickStep, const QCPRange &range) +{ + Q_UNUSED(tickStep) + QVector result; + if (mTicks.isEmpty()) + return result; + + QMap::const_iterator start = mTicks.lowerBound(range.lower); + QMap::const_iterator end = mTicks.upperBound(range.upper); + // this method should try to give one tick outside of range so proper subticks can be generated: + if (start != mTicks.constBegin()) --start; + if (end != mTicks.constEnd()) ++end; + for (QMap::const_iterator it = start; it != end; ++it) + result.append(it.key()); + + return result; +} +/* end of 'src/axis/axistickertext.cpp' */ + + +/* including file 'src/axis/axistickerpi.cpp' */ +/* modified 2022-11-06T12:45:56, size 11177 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTickerPi +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTickerPi + \brief Specialized axis ticker to display ticks in units of an arbitrary constant, for example pi + + \image html axisticker-pi.png + + This QCPAxisTicker subclass generates ticks that are expressed with respect to a given symbolic + constant with a numerical value specified with \ref setPiValue and an appearance in the tick + labels specified with \ref setPiSymbol. + + Ticks may be generated at fractions of the symbolic constant. How these fractions appear in the + tick label can be configured with \ref setFractionStyle. + + The ticker can be created and assigned to an axis like this: + \snippet documentation/doc-image-generator/mainwindow.cpp axistickerpi-creation +*/ + +/*! + Constructs the ticker and sets reasonable default values. Axis tickers are commonly created + managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. +*/ +QCPAxisTickerPi::QCPAxisTickerPi() : + mPiSymbol(QLatin1String(" ")+QChar(0x03C0)), + mPiValue(M_PI), + mPeriodicity(0), + mFractionStyle(fsUnicodeFractions), + mPiTickStep(0) +{ + setTickCount(4); +} + +/*! + Sets how the symbol part (which is always a suffix to the number) shall appear in the axis tick + label. + + If a space shall appear between the number and the symbol, make sure the space is contained in \a + symbol. +*/ +void QCPAxisTickerPi::setPiSymbol(QString symbol) +{ + mPiSymbol = symbol; +} + +/*! + Sets the numerical value that the symbolic constant has. + + This will be used to place the appropriate fractions of the symbol at the respective axis + coordinates. +*/ +void QCPAxisTickerPi::setPiValue(double pi) +{ + mPiValue = pi; +} + +/*! + Sets whether the axis labels shall appear periodicly and if so, at which multiplicity of the + symbolic constant. + + To disable periodicity, set \a multiplesOfPi to zero. + + For example, an axis that identifies 0 with 2pi would set \a multiplesOfPi to two. +*/ +void QCPAxisTickerPi::setPeriodicity(int multiplesOfPi) +{ + mPeriodicity = qAbs(multiplesOfPi); +} + +/*! + Sets how the numerical/fractional part preceding the symbolic constant is displayed in tick + labels. See \ref FractionStyle for the various options. +*/ +void QCPAxisTickerPi::setFractionStyle(QCPAxisTickerPi::FractionStyle style) +{ + mFractionStyle = style; +} + +/*! \internal + + Returns the tick step, using the constant's value (\ref setPiValue) as base unit. In consequence + the numerical/fractional part preceding the symbolic constant is made to have a readable + mantissa. + + \seebaseclassmethod +*/ +double QCPAxisTickerPi::getTickStep(const QCPRange &range) +{ + mPiTickStep = range.size()/mPiValue/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers + mPiTickStep = cleanMantissa(mPiTickStep); + return mPiTickStep*mPiValue; +} + +/*! \internal + + Returns the sub tick count, using the constant's value (\ref setPiValue) as base unit. In + consequence the sub ticks divide the numerical/fractional part preceding the symbolic constant + reasonably, and not the total tick coordinate. + + \seebaseclassmethod +*/ +int QCPAxisTickerPi::getSubTickCount(double tickStep) +{ + return QCPAxisTicker::getSubTickCount(tickStep/mPiValue); +} + +/*! \internal + + Returns the tick label as a fractional/numerical part and a symbolic string as suffix. The + formatting of the fraction is done according to the specified \ref setFractionStyle. The appended + symbol is specified with \ref setPiSymbol. + + \seebaseclassmethod +*/ +QString QCPAxisTickerPi::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) +{ + double tickInPis = tick/mPiValue; + if (mPeriodicity > 0) + tickInPis = fmod(tickInPis, mPeriodicity); + + if (mFractionStyle != fsFloatingPoint && mPiTickStep > 0.09 && mPiTickStep < 50) + { + // simply construct fraction from decimal like 1.234 -> 1234/1000 and then simplify fraction, smaller digits are irrelevant due to mPiTickStep conditional above + int denominator = 1000; + int numerator = qRound(tickInPis*denominator); + simplifyFraction(numerator, denominator); + if (qAbs(numerator) == 1 && denominator == 1) + return (numerator < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed(); + else if (numerator == 0) + return QLatin1String("0"); + else + return fractionToString(numerator, denominator) + mPiSymbol; + } else + { + if (qFuzzyIsNull(tickInPis)) + return QLatin1String("0"); + else if (qFuzzyCompare(qAbs(tickInPis), 1.0)) + return (tickInPis < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed(); + else + return QCPAxisTicker::getTickLabel(tickInPis, locale, formatChar, precision) + mPiSymbol; + } +} + +/*! \internal + + Takes the fraction given by \a numerator and \a denominator and modifies the values to make sure + the fraction is in irreducible form, i.e. numerator and denominator don't share any common + factors which could be cancelled. +*/ +void QCPAxisTickerPi::simplifyFraction(int &numerator, int &denominator) const +{ + if (numerator == 0 || denominator == 0) + return; + + int num = numerator; + int denom = denominator; + while (denom != 0) // euclidean gcd algorithm + { + int oldDenom = denom; + denom = num % denom; + num = oldDenom; + } + // num is now gcd of numerator and denominator + numerator /= num; + denominator /= num; +} + +/*! \internal + + Takes the fraction given by \a numerator and \a denominator and returns a string representation. + The result depends on the configured fraction style (\ref setFractionStyle). + + This method is used to format the numerical/fractional part when generating tick labels. It + simplifies the passed fraction to an irreducible form using \ref simplifyFraction and factors out + any integer parts of the fraction (e.g. "10/4" becomes "2 1/2"). +*/ +QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const +{ + if (denominator == 0) + { + qDebug() << Q_FUNC_INFO << "called with zero denominator"; + return QString(); + } + if (mFractionStyle == fsFloatingPoint) // should never be the case when calling this function + { + qDebug() << Q_FUNC_INFO << "shouldn't be called with fraction style fsDecimal"; + return QString::number(numerator/double(denominator)); // failsafe + } + int sign = numerator*denominator < 0 ? -1 : 1; + numerator = qAbs(numerator); + denominator = qAbs(denominator); + + if (denominator == 1) + { + return QString::number(sign*numerator); + } else + { + int integerPart = numerator/denominator; + int remainder = numerator%denominator; + if (remainder == 0) + { + return QString::number(sign*integerPart); + } else + { + if (mFractionStyle == fsAsciiFractions) + { + return QString(QLatin1String("%1%2%3/%4")) + .arg(sign == -1 ? QLatin1String("-") : QLatin1String("")) + .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QString(QLatin1String(""))) + .arg(remainder) + .arg(denominator); + } else if (mFractionStyle == fsUnicodeFractions) + { + return QString(QLatin1String("%1%2%3")) + .arg(sign == -1 ? QLatin1String("-") : QLatin1String("")) + .arg(integerPart > 0 ? QString::number(integerPart) : QLatin1String("")) + .arg(unicodeFraction(remainder, denominator)); + } + } + } + return QString(); +} + +/*! \internal + + Returns the unicode string representation of the fraction given by \a numerator and \a + denominator. This is the representation used in \ref fractionToString when the fraction style + (\ref setFractionStyle) is \ref fsUnicodeFractions. + + This method doesn't use the single-character common fractions but builds each fraction from a + superscript unicode number, the unicode fraction character, and a subscript unicode number. +*/ +QString QCPAxisTickerPi::unicodeFraction(int numerator, int denominator) const +{ + return unicodeSuperscript(numerator)+QChar(0x2044)+unicodeSubscript(denominator); +} + +/*! \internal + + Returns the unicode string representing \a number as superscript. This is used to build + unicode fractions in \ref unicodeFraction. +*/ +QString QCPAxisTickerPi::unicodeSuperscript(int number) const +{ + if (number == 0) + return QString(QChar(0x2070)); + + QString result; + while (number > 0) + { + const int digit = number%10; + switch (digit) + { + case 1: { result.prepend(QChar(0x00B9)); break; } + case 2: { result.prepend(QChar(0x00B2)); break; } + case 3: { result.prepend(QChar(0x00B3)); break; } + default: { result.prepend(QChar(0x2070+digit)); break; } + } + number /= 10; + } + return result; +} + +/*! \internal + + Returns the unicode string representing \a number as subscript. This is used to build unicode + fractions in \ref unicodeFraction. +*/ +QString QCPAxisTickerPi::unicodeSubscript(int number) const +{ + if (number == 0) + return QString(QChar(0x2080)); + + QString result; + while (number > 0) + { + result.prepend(QChar(0x2080+number%10)); + number /= 10; + } + return result; +} +/* end of 'src/axis/axistickerpi.cpp' */ + + +/* including file 'src/axis/axistickerlog.cpp' */ +/* modified 2022-11-06T12:45:56, size 7890 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisTickerLog +//////////////////////////////////////////////////////////////////////////////////////////////////// +/*! \class QCPAxisTickerLog + \brief Specialized axis ticker suited for logarithmic axes + + \image html axisticker-log.png + + This QCPAxisTicker subclass generates ticks with unequal tick intervals suited for logarithmic + axis scales. The ticks are placed at powers of the specified log base (\ref setLogBase). + + Especially in the case of a log base equal to 10 (the default), it might be desirable to have + tick labels in the form of powers of ten without mantissa display. To achieve this, set the + number precision (\ref QCPAxis::setNumberPrecision) to zero and the number format (\ref + QCPAxis::setNumberFormat) to scientific (exponential) display with beautifully typeset decimal + powers, so a format string of "eb". This will result in the following axis tick labels: + + \image html axisticker-log-powers.png + + The ticker can be created and assigned to an axis like this: + \snippet documentation/doc-image-generator/mainwindow.cpp axistickerlog-creation + + Note that the nature of logarithmic ticks imply that there exists a smallest possible tick step, + corresponding to one multiplication by the log base. If the user zooms in further than that, no + new ticks would appear, leading to very sparse or even no axis ticks on the axis. To prevent this + situation, this ticker falls back to regular tick generation if the axis range would be covered + by too few logarithmically placed ticks. +*/ + +/*! + Constructs the ticker and sets reasonable default values. Axis tickers are commonly created + managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker. +*/ +QCPAxisTickerLog::QCPAxisTickerLog() : + mLogBase(10.0), + mSubTickCount(8), // generates 10 intervals + mLogBaseLnInv(1.0/qLn(mLogBase)) +{ +} + +/*! + Sets the logarithm base used for tick coordinate generation. The ticks will be placed at integer + powers of \a base. +*/ +void QCPAxisTickerLog::setLogBase(double base) +{ + if (base > 0) + { + mLogBase = base; + mLogBaseLnInv = 1.0/qLn(mLogBase); + } else + qDebug() << Q_FUNC_INFO << "log base has to be greater than zero:" << base; +} + +/*! + Sets the number of sub ticks in a tick interval. Within each interval, the sub ticks are spaced + linearly to provide a better visual guide, so the sub tick density increases toward the higher + tick. + + Note that \a subTicks is the number of sub ticks (not sub intervals) in one tick interval. So in + the case of logarithm base 10 an intuitive sub tick spacing would be achieved with eight sub + ticks (the default). This means e.g. between the ticks 10 and 100 there will be eight ticks, + namely at 20, 30, 40, 50, 60, 70, 80 and 90. +*/ +void QCPAxisTickerLog::setSubTickCount(int subTicks) +{ + if (subTicks >= 0) + mSubTickCount = subTicks; + else + qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks; +} + +/*! \internal + + Returns the sub tick count specified in \ref setSubTickCount. For QCPAxisTickerLog, there is no + automatic sub tick count calculation necessary. + + \seebaseclassmethod +*/ +int QCPAxisTickerLog::getSubTickCount(double tickStep) +{ + Q_UNUSED(tickStep) + return mSubTickCount; +} + +/*! \internal + + Creates ticks with a spacing given by the logarithm base and an increasing integer power in the + provided \a range. The step in which the power increases tick by tick is chosen in order to keep + the total number of ticks as close as possible to the tick count (\ref setTickCount). + + The parameter \a tickStep is ignored for the normal logarithmic ticker generation. Only when + zoomed in very far such that not enough logarithmically placed ticks would be visible, this + function falls back to the regular QCPAxisTicker::createTickVector, which then uses \a tickStep. + + \seebaseclassmethod +*/ +QVector QCPAxisTickerLog::createTickVector(double tickStep, const QCPRange &range) +{ + QVector result; + if (range.lower > 0 && range.upper > 0) // positive range + { + const double baseTickCount = qLn(range.upper/range.lower)*mLogBaseLnInv; + if (baseTickCount < 1.6) // if too few log ticks would be visible in axis range, fall back to regular tick vector generation + return QCPAxisTicker::createTickVector(tickStep, range); + const double exactPowerStep = baseTickCount/double(mTickCount+1e-10); + const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1)); + double currentTick = qPow(newLogBase, qFloor(qLn(range.lower)/qLn(newLogBase))); + result.append(currentTick); + while (currentTick < range.upper && currentTick > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case + { + currentTick *= newLogBase; + result.append(currentTick); + } + } else if (range.lower < 0 && range.upper < 0) // negative range + { + const double baseTickCount = qLn(range.lower/range.upper)*mLogBaseLnInv; + if (baseTickCount < 1.6) // if too few log ticks would be visible in axis range, fall back to regular tick vector generation + return QCPAxisTicker::createTickVector(tickStep, range); + const double exactPowerStep = baseTickCount/double(mTickCount+1e-10); + const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1)); + double currentTick = -qPow(newLogBase, qCeil(qLn(-range.lower)/qLn(newLogBase))); + result.append(currentTick); + while (currentTick < range.upper && currentTick < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case + { + currentTick /= newLogBase; + result.append(currentTick); + } + } else // invalid range for logarithmic scale, because lower and upper have different sign + { + qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << range.lower << ".." << range.upper; + } + + return result; +} +/* end of 'src/axis/axistickerlog.cpp' */ + + +/* including file 'src/axis/axis.cpp' */ +/* modified 2022-11-06T12:45:56, size 99911 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPGrid +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPGrid + \brief Responsible for drawing the grid of a QCPAxis. + + This class is tightly bound to QCPAxis. Every axis owns a grid instance and uses it to draw the + grid lines, sub grid lines and zero-line. You can interact with the grid of an axis via \ref + QCPAxis::grid. Normally, you don't need to create an instance of QCPGrid yourself. + + The axis and grid drawing was split into two classes to allow them to be placed on different + layers (both QCPAxis and QCPGrid inherit from QCPLayerable). Thus it is possible to have the grid + in the background and the axes in the foreground, and any plottables/items in between. This + described situation is the default setup, see the QCPLayer documentation. +*/ + +/*! + Creates a QCPGrid instance and sets default values. + + You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid. +*/ +QCPGrid::QCPGrid(QCPAxis *parentAxis) : + QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis), + mSubGridVisible{}, + mAntialiasedSubGrid{}, + mAntialiasedZeroLine{}, + mParentAxis(parentAxis) +{ + // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called + setParent(parentAxis); + setPen(QPen(QColor(200,200,200), 0, Qt::DotLine)); + setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine)); + setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine)); + setSubGridVisible(false); + setAntialiased(false); + setAntialiasedSubGrid(false); + setAntialiasedZeroLine(false); +} + +/*! + Sets whether grid lines at sub tick marks are drawn. + + \see setSubGridPen +*/ +void QCPGrid::setSubGridVisible(bool visible) +{ + mSubGridVisible = visible; +} + +/*! + Sets whether sub grid lines are drawn antialiased. +*/ +void QCPGrid::setAntialiasedSubGrid(bool enabled) +{ + mAntialiasedSubGrid = enabled; +} + +/*! + Sets whether zero lines are drawn antialiased. +*/ +void QCPGrid::setAntialiasedZeroLine(bool enabled) +{ + mAntialiasedZeroLine = enabled; +} + +/*! + Sets the pen with which (major) grid lines are drawn. +*/ +void QCPGrid::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen with which sub grid lines are drawn. +*/ +void QCPGrid::setSubGridPen(const QPen &pen) +{ + mSubGridPen = pen; +} + +/*! + Sets the pen with which zero lines are drawn. + + Zero lines are lines at value coordinate 0 which may be drawn with a different pen than other grid + lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen. +*/ +void QCPGrid::setZeroLinePen(const QPen &pen) +{ + mZeroLinePen = pen; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing the major grid lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased +*/ +void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid); +} + +/*! \internal + + Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning + over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen). +*/ +void QCPGrid::draw(QCPPainter *painter) +{ + if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; } + + if (mParentAxis->subTicks() && mSubGridVisible) + drawSubGridLines(painter); + drawGridLines(painter); +} + +/*! \internal + + Draws the main grid lines and possibly a zero line with the specified painter. + + This is a helper function called by \ref draw. +*/ +void QCPGrid::drawGridLines(QCPPainter *painter) const +{ + if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; } + + const int tickCount = static_cast(mParentAxis->mTickVector.size()); + double t; // helper variable, result of coordinate-to-pixel transforms + if (mParentAxis->orientation() == Qt::Horizontal) + { + // draw zeroline: + int zeroLineIndex = -1; + if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0) + { + applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine); + painter->setPen(mZeroLinePen); + double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero + for (int i=0; imTickVector.at(i)) < epsilon) + { + zeroLineIndex = i; + t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x + painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top())); + break; + } + } + } + // draw grid lines: + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + for (int i=0; icoordToPixel(mParentAxis->mTickVector.at(i)); // x + painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top())); + } + } else + { + // draw zeroline: + int zeroLineIndex = -1; + if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0) + { + applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine); + painter->setPen(mZeroLinePen); + double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero + for (int i=0; imTickVector.at(i)) < epsilon) + { + zeroLineIndex = i; + t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y + painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t)); + break; + } + } + } + // draw grid lines: + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + for (int i=0; icoordToPixel(mParentAxis->mTickVector.at(i)); // y + painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t)); + } + } +} + +/*! \internal + + Draws the sub grid lines with the specified painter. + + This is a helper function called by \ref draw. +*/ +void QCPGrid::drawSubGridLines(QCPPainter *painter) const +{ + if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; } + + applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid); + double t; // helper variable, result of coordinate-to-pixel transforms + painter->setPen(mSubGridPen); + if (mParentAxis->orientation() == Qt::Horizontal) + { + foreach (double tickCoord, mParentAxis->mSubTickVector) + { + t = mParentAxis->coordToPixel(tickCoord); // x + painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top())); + } + } else + { + foreach (double tickCoord, mParentAxis->mSubTickVector) + { + t = mParentAxis->coordToPixel(tickCoord); // y + painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t)); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxis +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAxis + \brief Manages a single axis inside a QCustomPlot. + + Usually doesn't need to be instantiated externally. Access %QCustomPlot's default four axes via + QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and + QCustomPlot::yAxis2 (right). + + Axes are always part of an axis rect, see QCPAxisRect. + \image html AxisNamesOverview.png +
Naming convention of axis parts
+ \n + + \image html AxisRectSpacingOverview.png +
Overview of the spacings and paddings that define the geometry of an axis. The dashed gray line + on the left represents the QCustomPlot widget border.
+ + Each axis holds an instance of QCPAxisTicker which is used to generate the tick coordinates and + tick labels. You can access the currently installed \ref ticker or set a new one (possibly one of + the specialized subclasses, or your own subclass) via \ref setTicker. For details, see the + documentation of QCPAxisTicker. +*/ + +/* start of documentation of inline functions */ + +/*! \fn Qt::Orientation QCPAxis::orientation() const + + Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced + from the axis type (left, top, right or bottom). + + \see orientation(AxisType type), pixelOrientation +*/ + +/*! \fn QCPGrid *QCPAxis::grid() const + + Returns the \ref QCPGrid instance belonging to this axis. Access it to set details about the way the + grid is displayed. +*/ + +/*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type) + + Returns the orientation of the specified axis type + + \see orientation(), pixelOrientation +*/ + +/*! \fn int QCPAxis::pixelOrientation() const + + Returns which direction points towards higher coordinate values/keys, in pixel space. + + This method returns either 1 or -1. If it returns 1, then going in the positive direction along + the orientation of the axis in pixels corresponds to going from lower to higher axis coordinates. + On the other hand, if this method returns -1, going to smaller pixel values corresponds to going + from lower to higher axis coordinates. + + For example, this is useful to easily shift axis coordinates by a certain amount given in pixels, + without having to care about reversed or vertically aligned axes: + + \code + double newKey = keyAxis->pixelToCoord(keyAxis->coordToPixel(oldKey)+10*keyAxis->pixelOrientation()); + \endcode + + \a newKey will then contain a key that is ten pixels towards higher keys, starting from \a oldKey. +*/ + +/*! \fn QSharedPointer QCPAxis::ticker() const + + Returns a modifiable shared pointer to the currently installed axis ticker. The axis ticker is + responsible for generating the tick positions and tick labels of this axis. You can access the + \ref QCPAxisTicker with this method and modify basic properties such as the approximate tick count + (\ref QCPAxisTicker::setTickCount). + + You can gain more control over the axis ticks by setting a different \ref QCPAxisTicker subclass, see + the documentation there. A new axis ticker can be set with \ref setTicker. + + Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis + ticker simply by passing the same shared pointer to multiple axes. + + \see setTicker +*/ + +/* end of documentation of inline functions */ +/* start of documentation of signals */ + +/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange) + + This signal is emitted when the range of this axis has changed. You can connect it to the \ref + setRange slot of another axis to communicate the new range to the other axis, in order for it to + be synchronized. + + You may also manipulate/correct the range with \ref setRange in a slot connected to this signal. + This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper + range shouldn't go beyond certain values (see \ref QCPRange::bounded). For example, the following + slot would limit the x axis to ranges between 0 and 10: + \code + customPlot->xAxis->setRange(newRange.bounded(0, 10)) + \endcode +*/ + +/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange) + \overload + + Additionally to the new range, this signal also provides the previous range held by the axis as + \a oldRange. +*/ + +/*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType); + + This signal is emitted when the scale type changes, by calls to \ref setScaleType +*/ + +/*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection) + + This signal is emitted when the selection state of this axis has changed, either by user interaction + or by a direct call to \ref setSelectedParts. +*/ + +/*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts); + + This signal is emitted when the selectability changes, by calls to \ref setSelectableParts +*/ + +/* end of documentation of signals */ + +/*! + Constructs an Axis instance of Type \a type for the axis rect \a parent. + + Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create + them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however, + create them manually and then inject them also via \ref QCPAxisRect::addAxis. +*/ +QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) : + QCPLayerable(parent->parentPlot(), QString(), parent), + // axis base: + mAxisType(type), + mAxisRect(parent), + mPadding(5), + mOrientation(orientation(type)), + mSelectableParts(spAxis | spTickLabels | spAxisLabel), + mSelectedParts(spNone), + mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedBasePen(QPen(Qt::blue, 2)), + // axis label: + mLabel(), + mLabelFont(mParentPlot->font()), + mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)), + mLabelColor(Qt::black), + mSelectedLabelColor(Qt::blue), + // tick labels: + mTickLabels(true), + mTickLabelFont(mParentPlot->font()), + mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)), + mTickLabelColor(Qt::black), + mSelectedTickLabelColor(Qt::blue), + mNumberPrecision(6), + mNumberFormatChar('g'), + mNumberBeautifulPowers(true), + // ticks and subticks: + mTicks(true), + mSubTicks(true), + mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedTickPen(QPen(Qt::blue, 2)), + mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedSubTickPen(QPen(Qt::blue, 2)), + // scale and range: + mRange(0, 5), + mRangeReversed(false), + mScaleType(stLinear), + // internal members: + mGrid(new QCPGrid(this)), + mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())), + mTicker(new QCPAxisTicker), + mCachedMarginValid(false), + mCachedMargin(0), + mDragging(false) +{ + setParent(parent); + mGrid->setVisible(false); + setAntialiased(false); + setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again + + if (type == atTop) + { + setTickLabelPadding(3); + setLabelPadding(6); + } else if (type == atRight) + { + setTickLabelPadding(7); + setLabelPadding(12); + } else if (type == atBottom) + { + setTickLabelPadding(3); + setLabelPadding(3); + } else if (type == atLeft) + { + setTickLabelPadding(5); + setLabelPadding(10); + } +} + +QCPAxis::~QCPAxis() +{ + delete mAxisPainter; + delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order +} + +/* No documentation as it is a property getter */ +int QCPAxis::tickLabelPadding() const +{ + return mAxisPainter->tickLabelPadding; +} + +/* No documentation as it is a property getter */ +double QCPAxis::tickLabelRotation() const +{ + return mAxisPainter->tickLabelRotation; +} + +/* No documentation as it is a property getter */ +QCPAxis::LabelSide QCPAxis::tickLabelSide() const +{ + return mAxisPainter->tickLabelSide; +} + +/* No documentation as it is a property getter */ +QString QCPAxis::numberFormat() const +{ + QString result; + result.append(mNumberFormatChar); + if (mNumberBeautifulPowers) + { + result.append(QLatin1Char('b')); + if (mAxisPainter->numberMultiplyCross) + result.append(QLatin1Char('c')); + } + return result; +} + +/* No documentation as it is a property getter */ +int QCPAxis::tickLengthIn() const +{ + return mAxisPainter->tickLengthIn; +} + +/* No documentation as it is a property getter */ +int QCPAxis::tickLengthOut() const +{ + return mAxisPainter->tickLengthOut; +} + +/* No documentation as it is a property getter */ +int QCPAxis::subTickLengthIn() const +{ + return mAxisPainter->subTickLengthIn; +} + +/* No documentation as it is a property getter */ +int QCPAxis::subTickLengthOut() const +{ + return mAxisPainter->subTickLengthOut; +} + +/* No documentation as it is a property getter */ +int QCPAxis::labelPadding() const +{ + return mAxisPainter->labelPadding; +} + +/* No documentation as it is a property getter */ +int QCPAxis::offset() const +{ + return mAxisPainter->offset; +} + +/* No documentation as it is a property getter */ +QCPLineEnding QCPAxis::lowerEnding() const +{ + return mAxisPainter->lowerEnding; +} + +/* No documentation as it is a property getter */ +QCPLineEnding QCPAxis::upperEnding() const +{ + return mAxisPainter->upperEnding; +} + +/*! + Sets whether the axis uses a linear scale or a logarithmic scale. + + Note that this method controls the coordinate transformation. For logarithmic scales, you will + likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting + the axis ticker to an instance of \ref QCPAxisTickerLog : + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation + + See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick + creation. + + \ref setNumberPrecision +*/ +void QCPAxis::setScaleType(QCPAxis::ScaleType type) +{ + if (mScaleType != type) + { + mScaleType = type; + if (mScaleType == stLogarithmic) + setRange(mRange.sanitizedForLogScale()); + mCachedMarginValid = false; + emit scaleTypeChanged(mScaleType); + } +} + +/*! + Sets the range of the axis. + + This slot may be connected with the \ref rangeChanged signal of another axis so this axis + is always synchronized with the other axis range, when it changes. + + To invert the direction of an axis, use \ref setRangeReversed. +*/ +void QCPAxis::setRange(const QCPRange &range) +{ + if (range.lower == mRange.lower && range.upper == mRange.upper) + return; + + if (!QCPRange::validRange(range)) return; + QCPRange oldRange = mRange; + if (mScaleType == stLogarithmic) + { + mRange = range.sanitizedForLogScale(); + } else + { + mRange = range.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains iSelectAxes.) + + However, even when \a selectable is set to a value not allowing the selection of a specific part, + it is still possible to set the selection of this part manually, by calling \ref setSelectedParts + directly. + + \see SelectablePart, setSelectedParts +*/ +void QCPAxis::setSelectableParts(const SelectableParts &selectable) +{ + if (mSelectableParts != selectable) + { + mSelectableParts = selectable; + emit selectableChanged(mSelectableParts); + } +} + +/*! + Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part + is selected, it uses a different pen/font. + + The entire selection mechanism for axes is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you + wish to change the selection state manually. + + This function can change the selection state of a part, independent of the \ref setSelectableParts setting. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen, + setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor +*/ +void QCPAxis::setSelectedParts(const SelectableParts &selected) +{ + if (mSelectedParts != selected) + { + mSelectedParts = selected; + emit selectionChanged(mSelectedParts); + } +} + +/*! + \overload + + Sets the lower and upper bound of the axis range. + + To invert the direction of an axis, use \ref setRangeReversed. + + There is also a slot to set a range, see \ref setRange(const QCPRange &range). +*/ +void QCPAxis::setRange(double lower, double upper) +{ + if (lower == mRange.lower && upper == mRange.upper) + return; + + if (!QCPRange::validRange(lower, upper)) return; + QCPRange oldRange = mRange; + mRange.lower = lower; + mRange.upper = upper; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + \overload + + Sets the range of the axis. + + The \a position coordinate indicates together with the \a alignment parameter, where the new + range will be positioned. \a size defines the size of the new axis range. \a alignment may be + Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border, + or center of the range to be aligned with \a position. Any other values of \a alignment will + default to Qt::AlignCenter. +*/ +void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment) +{ + if (alignment == Qt::AlignLeft) + setRange(position, position+size); + else if (alignment == Qt::AlignRight) + setRange(position-size, position); + else // alignment == Qt::AlignCenter + setRange(position-size/2.0, position+size/2.0); +} + +/*! + Sets the lower bound of the axis range. The upper bound is not changed. + \see setRange +*/ +void QCPAxis::setRangeLower(double lower) +{ + if (mRange.lower == lower) + return; + + QCPRange oldRange = mRange; + mRange.lower = lower; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets the upper bound of the axis range. The lower bound is not changed. + \see setRange +*/ +void QCPAxis::setRangeUpper(double upper) +{ + if (mRange.upper == upper) + return; + + QCPRange oldRange = mRange; + mRange.upper = upper; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal + axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the + direction of increasing values is inverted. + + Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part + of the \ref setRange interface will still reference the mathematically smaller number than the \a + upper part. +*/ +void QCPAxis::setRangeReversed(bool reversed) +{ + mRangeReversed = reversed; +} + +/*! + The axis ticker is responsible for generating the tick positions and tick labels. See the + documentation of QCPAxisTicker for details on how to work with axis tickers. + + You can change the tick positioning/labeling behaviour of this axis by setting a different + QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis + ticker, access it via \ref ticker. + + Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis + ticker simply by passing the same shared pointer to multiple axes. + + \see ticker +*/ +void QCPAxis::setTicker(QSharedPointer ticker) +{ + if (ticker) + mTicker = ticker; + else + qDebug() << Q_FUNC_INFO << "can not set nullptr as axis ticker"; + // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector +} + +/*! + Sets whether tick marks are displayed. + + Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve + that, see \ref setTickLabels. + + \see setSubTicks +*/ +void QCPAxis::setTicks(bool show) +{ + if (mTicks != show) + { + mTicks = show; + mCachedMarginValid = false; + } +} + +/*! + Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks. +*/ +void QCPAxis::setTickLabels(bool show) +{ + if (mTickLabels != show) + { + mTickLabels = show; + mCachedMarginValid = false; + if (!mTickLabels) + mTickVectorLabels.clear(); + } +} + +/*! + Sets the distance between the axis base line (including any outward ticks) and the tick labels. + \see setLabelPadding, setPadding +*/ +void QCPAxis::setTickLabelPadding(int padding) +{ + if (mAxisPainter->tickLabelPadding != padding) + { + mAxisPainter->tickLabelPadding = padding; + mCachedMarginValid = false; + } +} + +/*! + Sets the font of the tick labels. + + \see setTickLabels, setTickLabelColor +*/ +void QCPAxis::setTickLabelFont(const QFont &font) +{ + if (font != mTickLabelFont) + { + mTickLabelFont = font; + mCachedMarginValid = false; + } +} + +/*! + Sets the color of the tick labels. + + \see setTickLabels, setTickLabelFont +*/ +void QCPAxis::setTickLabelColor(const QColor &color) +{ + mTickLabelColor = color; +} + +/*! + Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else, + the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values + from -90 to 90 degrees. + + If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For + other angles, the label is drawn with an offset such that it seems to point toward or away from + the tick mark. +*/ +void QCPAxis::setTickLabelRotation(double degrees) +{ + if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation)) + { + mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0); + mCachedMarginValid = false; + } +} + +/*! + Sets whether the tick labels (numbers) shall appear inside or outside the axis rect. + + The usual and default setting is \ref lsOutside. Very compact plots sometimes require tick labels + to be inside the axis rect, to save space. If \a side is set to \ref lsInside, the tick labels + appear on the inside are additionally clipped to the axis rect. +*/ +void QCPAxis::setTickLabelSide(LabelSide side) +{ + mAxisPainter->tickLabelSide = side; + mCachedMarginValid = false; +} + +/*! + Sets the number format for the numbers in tick labels. This \a formatCode is an extended version + of the format code used e.g. by QString::number() and QLocale::toString(). For reference about + that, see the "Argument Formats" section in the detailed description of the QString class. + + \a formatCode is a string of one, two or three characters. + + The first character is identical to + the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed + format, 'g'/'G' scientific or fixed, whichever is shorter. For the 'e', 'E', and 'f' formats, + the precision set by \ref setNumberPrecision represents the number of digits after the decimal + point. For the 'g' and 'G' formats, the precision represents the maximum number of significant + digits, trailing zeroes are omitted. + + The second and third characters are optional and specific to QCustomPlot:\n + If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g. + "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for + "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5 + [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot. + If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can + be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the + cross and 183 (0xB7) for the dot. + + Examples for \a formatCode: + \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large, + normal scientific format is used + \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with + beautifully typeset decimal powers and a dot as multiplication sign + \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as + multiplication sign + \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal + powers. Format code will be reduced to 'f'. + \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format + code will not be changed. +*/ +void QCPAxis::setNumberFormat(const QString &formatCode) +{ + if (formatCode.isEmpty()) + { + qDebug() << Q_FUNC_INFO << "Passed formatCode is empty"; + return; + } + mCachedMarginValid = false; + + // interpret first char as number format char: + QString allowedFormatChars(QLatin1String("eEfgG")); + if (allowedFormatChars.contains(formatCode.at(0))) + { + mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1()); + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode; + return; + } + if (formatCode.length() < 2) + { + mNumberBeautifulPowers = false; + mAxisPainter->numberMultiplyCross = false; + return; + } + + // interpret second char as indicator for beautiful decimal powers: + if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g'))) + { + mNumberBeautifulPowers = true; + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode; + return; + } + if (formatCode.length() < 3) + { + mAxisPainter->numberMultiplyCross = false; + return; + } + + // interpret third char as indicator for dot or cross multiplication symbol: + if (formatCode.at(2) == QLatin1Char('c')) + { + mAxisPainter->numberMultiplyCross = true; + } else if (formatCode.at(2) == QLatin1Char('d')) + { + mAxisPainter->numberMultiplyCross = false; + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode; + return; + } +} + +/*! + Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec) + for details. The effect of precisions are most notably for number Formats starting with 'e', see + \ref setNumberFormat +*/ +void QCPAxis::setNumberPrecision(int precision) +{ + if (mNumberPrecision != precision) + { + mNumberPrecision = precision; + mCachedMarginValid = false; + } +} + +/*! + Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the + plot and \a outside is the length they will reach outside the plot. If \a outside is greater than + zero, the tick labels and axis label will increase their distance to the axis accordingly, so + they won't collide with the ticks. + + \see setSubTickLength, setTickLengthIn, setTickLengthOut +*/ +void QCPAxis::setTickLength(int inside, int outside) +{ + setTickLengthIn(inside); + setTickLengthOut(outside); +} + +/*! + Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach + inside the plot. + + \see setTickLengthOut, setTickLength, setSubTickLength +*/ +void QCPAxis::setTickLengthIn(int inside) +{ + if (mAxisPainter->tickLengthIn != inside) + { + mAxisPainter->tickLengthIn = inside; + } +} + +/*! + Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach + outside the plot. If \a outside is greater than zero, the tick labels and axis label will + increase their distance to the axis accordingly, so they won't collide with the ticks. + + \see setTickLengthIn, setTickLength, setSubTickLength +*/ +void QCPAxis::setTickLengthOut(int outside) +{ + if (mAxisPainter->tickLengthOut != outside) + { + mAxisPainter->tickLengthOut = outside; + mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets whether sub tick marks are displayed. + + Sub ticks are only potentially visible if (major) ticks are also visible (see \ref setTicks) + + \see setTicks +*/ +void QCPAxis::setSubTicks(bool show) +{ + if (mSubTicks != show) + { + mSubTicks = show; + mCachedMarginValid = false; + } +} + +/*! + Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside + the plot and \a outside is the length they will reach outside the plot. If \a outside is greater + than zero, the tick labels and axis label will increase their distance to the axis accordingly, + so they won't collide with the ticks. + + \see setTickLength, setSubTickLengthIn, setSubTickLengthOut +*/ +void QCPAxis::setSubTickLength(int inside, int outside) +{ + setSubTickLengthIn(inside); + setSubTickLengthOut(outside); +} + +/*! + Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside + the plot. + + \see setSubTickLengthOut, setSubTickLength, setTickLength +*/ +void QCPAxis::setSubTickLengthIn(int inside) +{ + if (mAxisPainter->subTickLengthIn != inside) + { + mAxisPainter->subTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach + outside the plot. If \a outside is greater than zero, the tick labels will increase their + distance to the axis accordingly, so they won't collide with the ticks. + + \see setSubTickLengthIn, setSubTickLength, setTickLength +*/ +void QCPAxis::setSubTickLengthOut(int outside) +{ + if (mAxisPainter->subTickLengthOut != outside) + { + mAxisPainter->subTickLengthOut = outside; + mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets the pen, the axis base line is drawn with. + + \see setTickPen, setSubTickPen +*/ +void QCPAxis::setBasePen(const QPen &pen) +{ + mBasePen = pen; +} + +/*! + Sets the pen, tick marks will be drawn with. + + \see setTickLength, setBasePen +*/ +void QCPAxis::setTickPen(const QPen &pen) +{ + mTickPen = pen; +} + +/*! + Sets the pen, subtick marks will be drawn with. + + \see setSubTickCount, setSubTickLength, setBasePen +*/ +void QCPAxis::setSubTickPen(const QPen &pen) +{ + mSubTickPen = pen; +} + +/*! + Sets the font of the axis label. + + \see setLabelColor +*/ +void QCPAxis::setLabelFont(const QFont &font) +{ + if (mLabelFont != font) + { + mLabelFont = font; + mCachedMarginValid = false; + } +} + +/*! + Sets the color of the axis label. + + \see setLabelFont +*/ +void QCPAxis::setLabelColor(const QColor &color) +{ + mLabelColor = color; +} + +/*! + Sets the text of the axis label that will be shown below/above or next to the axis, depending on + its orientation. To disable axis labels, pass an empty string as \a str. +*/ +void QCPAxis::setLabel(const QString &str) +{ + if (mLabel != str) + { + mLabel = str; + mCachedMarginValid = false; + } +} + +/*! + Sets the distance between the tick labels and the axis label. + + \see setTickLabelPadding, setPadding +*/ +void QCPAxis::setLabelPadding(int padding) +{ + if (mAxisPainter->labelPadding != padding) + { + mAxisPainter->labelPadding = padding; + mCachedMarginValid = false; + } +} + +/*! + Sets the padding of the axis. + + When \ref QCPAxisRect::setAutoMargins is enabled, the padding is the additional outer most space, + that is left blank. + + The axis padding has no meaning if \ref QCPAxisRect::setAutoMargins is disabled. + + \see setLabelPadding, setTickLabelPadding +*/ +void QCPAxis::setPadding(int padding) +{ + if (mPadding != padding) + { + mPadding = padding; + mCachedMarginValid = false; + } +} + +/*! + Sets the offset the axis has to its axis rect side. + + If an axis rect side has multiple axes and automatic margin calculation is enabled for that side, + only the offset of the inner most axis has meaning (even if it is set to be invisible). The + offset of the other, outer axes is controlled automatically, to place them at appropriate + positions. +*/ +void QCPAxis::setOffset(int offset) +{ + mAxisPainter->offset = offset; +} + +/*! + Sets the font that is used for tick labels when they are selected. + + \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedTickLabelFont(const QFont &font) +{ + if (font != mSelectedTickLabelFont) + { + mSelectedTickLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts + } +} + +/*! + Sets the font that is used for the axis label when it is selected. + + \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedLabelFont(const QFont &font) +{ + mSelectedLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts +} + +/*! + Sets the color that is used for tick labels when they are selected. + + \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedTickLabelColor(const QColor &color) +{ + if (color != mSelectedTickLabelColor) + { + mSelectedTickLabelColor = color; + } +} + +/*! + Sets the color that is used for the axis label when it is selected. + + \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedLabelColor(const QColor &color) +{ + mSelectedLabelColor = color; +} + +/*! + Sets the pen that is used to draw the axis base line when selected. + + \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedBasePen(const QPen &pen) +{ + mSelectedBasePen = pen; +} + +/*! + Sets the pen that is used to draw the (major) ticks when selected. + + \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedTickPen(const QPen &pen) +{ + mSelectedTickPen = pen; +} + +/*! + Sets the pen that is used to draw the subticks when selected. + + \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAxis::setSelectedSubTickPen(const QPen &pen) +{ + mSelectedSubTickPen = pen; +} + +/*! + Sets the style for the lower axis ending. See the documentation of QCPLineEnding for available + styles. + + For horizontal axes, this method refers to the left ending, for vertical axes the bottom ending. + Note that this meaning does not change when the axis range is reversed with \ref + setRangeReversed. + + \see setUpperEnding +*/ +void QCPAxis::setLowerEnding(const QCPLineEnding &ending) +{ + mAxisPainter->lowerEnding = ending; +} + +/*! + Sets the style for the upper axis ending. See the documentation of QCPLineEnding for available + styles. + + For horizontal axes, this method refers to the right ending, for vertical axes the top ending. + Note that this meaning does not change when the axis range is reversed with \ref + setRangeReversed. + + \see setLowerEnding +*/ +void QCPAxis::setUpperEnding(const QCPLineEnding &ending) +{ + mAxisPainter->upperEnding = ending; +} + +/*! + If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper + bounds of the range. The range is simply moved by \a diff. + + If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This + corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff). +*/ +void QCPAxis::moveRange(double diff) +{ + QCPRange oldRange = mRange; + if (mScaleType == stLinear) + { + mRange.lower += diff; + mRange.upper += diff; + } else // mScaleType == stLogarithmic + { + mRange.lower *= diff; + mRange.upper *= diff; + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Scales the range of this axis by \a factor around the center of the current axis range. For + example, if \a factor is 2.0, then the axis range will double its size, and the point at the axis + range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around + the center will have moved symmetrically closer). + + If you wish to scale around a different coordinate than the current axis range center, use the + overload \ref scaleRange(double factor, double center). +*/ +void QCPAxis::scaleRange(double factor) +{ + scaleRange(factor, range().center()); +} + +/*! \overload + + Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a + factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at + coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates + around 1.0 will have moved symmetrically closer to 1.0). + + \see scaleRange(double factor) +*/ +void QCPAxis::scaleRange(double factor, double center) +{ + QCPRange oldRange = mRange; + if (mScaleType == stLinear) + { + QCPRange newRange; + newRange.lower = (mRange.lower-center)*factor + center; + newRange.upper = (mRange.upper-center)*factor + center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLinScale(); + } else // mScaleType == stLogarithmic + { + if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range + { + QCPRange newRange; + newRange.lower = qPow(mRange.lower/center, factor)*center; + newRange.upper = qPow(mRange.upper/center, factor)*center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLogScale(); + } else + qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center; + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Scales the range of this axis to have a certain scale \a ratio to \a otherAxis. The scaling will + be done around the center of the current axis range. + + For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs + plotted with those axes will appear in a 1:1 aspect ratio, independent of the aspect ratio the + axis rect has. + + This is an operation that changes the range of this axis once, it doesn't fix the scale ratio + indefinitely. Note that calling this function in the constructor of the QCustomPlot's parent + won't have the desired effect, since the widget dimensions aren't defined yet, and a resizeEvent + will follow. +*/ +void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio) +{ + int otherPixelSize, ownPixelSize; + + if (otherAxis->orientation() == Qt::Horizontal) + otherPixelSize = otherAxis->axisRect()->width(); + else + otherPixelSize = otherAxis->axisRect()->height(); + + if (orientation() == Qt::Horizontal) + ownPixelSize = axisRect()->width(); + else + ownPixelSize = axisRect()->height(); + + double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/double(otherPixelSize); + setRange(range().center(), newRangeSize, Qt::AlignCenter); +} + +/*! + Changes the axis range such that all plottables associated with this axis are fully visible in + that dimension. + + \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes +*/ +void QCPAxis::rescale(bool onlyVisiblePlottables) +{ + QCPRange newRange; + bool haveRange = false; + foreach (QCPAbstractPlottable *plottable, plottables()) + { + if (!plottable->realVisibility() && onlyVisiblePlottables) + continue; + QCPRange plottableRange; + bool currentFoundRange; + QCP::SignDomain signDomain = QCP::sdBoth; + if (mScaleType == stLogarithmic) + signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive); + if (plottable->keyAxis() == this) + plottableRange = plottable->getKeyRange(currentFoundRange, signDomain); + else + plottableRange = plottable->getValueRange(currentFoundRange, signDomain); + if (currentFoundRange) + { + if (!haveRange) + newRange = plottableRange; + else + newRange.expand(plottableRange); + haveRange = true; + } + } + if (haveRange) + { + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (mScaleType == stLinear) + { + newRange.lower = center-mRange.size()/2.0; + newRange.upper = center+mRange.size()/2.0; + } else // mScaleType == stLogarithmic + { + newRange.lower = center/qSqrt(mRange.upper/mRange.lower); + newRange.upper = center*qSqrt(mRange.upper/mRange.lower); + } + } + setRange(newRange); + } +} + +/*! + Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates. +*/ +double QCPAxis::pixelToCoord(double value) const +{ + if (orientation() == Qt::Horizontal) + { + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (value-mAxisRect->left())/double(mAxisRect->width())*mRange.size()+mRange.lower; + else + return -(value-mAxisRect->left())/double(mAxisRect->width())*mRange.size()+mRange.upper; + } else // mScaleType == stLogarithmic + { + if (!mRangeReversed) + return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/double(mAxisRect->width()))*mRange.lower; + else + return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/double(mAxisRect->width()))*mRange.upper; + } + } else // orientation() == Qt::Vertical + { + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (mAxisRect->bottom()-value)/double(mAxisRect->height())*mRange.size()+mRange.lower; + else + return -(mAxisRect->bottom()-value)/double(mAxisRect->height())*mRange.size()+mRange.upper; + } else // mScaleType == stLogarithmic + { + if (!mRangeReversed) + return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/double(mAxisRect->height()))*mRange.lower; + else + return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/double(mAxisRect->height()))*mRange.upper; + } + } +} + +/*! + Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget. +*/ +double QCPAxis::coordToPixel(double value) const +{ + if (orientation() == Qt::Horizontal) + { + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left(); + else + return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left(); + } else // mScaleType == stLogarithmic + { + if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range + return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200; + else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range + return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200; + else + { + if (!mRangeReversed) + return qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left(); + else + return qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left(); + } + } + } else // orientation() == Qt::Vertical + { + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height(); + else + return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height(); + } else // mScaleType == stLogarithmic + { + if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range + return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200; + else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range + return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200; + else + { + if (!mRangeReversed) + return mAxisRect->bottom()-qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->height(); + else + return mAxisRect->bottom()-qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->height(); + } + } + } +} + +/*! + Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function + is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this + function does not change the current selection state of the axis. + + If the axis is not visible (\ref setVisible), this function always returns \ref spNone. + + \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions +*/ +QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const +{ + if (!mVisible) + return spNone; + + if (mAxisPainter->axisSelectionBox().contains(pos.toPoint())) + return spAxis; + else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint())) + return spTickLabels; + else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint())) + return spAxisLabel; + else + return spNone; +} + +/* inherits documentation from base class */ +double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if (!mParentPlot) return -1; + SelectablePart part = getPartAt(pos); + if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone) + return -1; + + if (details) + details->setValue(part); + return mParentPlot->selectionTolerance()*0.99; +} + +/*! + Returns a list of all the plottables that have this axis as key or value axis. + + If you are only interested in plottables of type QCPGraph, see \ref graphs. + + \see graphs, items +*/ +QList QCPAxis::plottables() const +{ + QList result; + if (!mParentPlot) return result; + + foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables) + { + if (plottable->keyAxis() == this || plottable->valueAxis() == this) + result.append(plottable); + } + return result; +} + +/*! + Returns a list of all the graphs that have this axis as key or value axis. + + \see plottables, items +*/ +QList QCPAxis::graphs() const +{ + QList result; + if (!mParentPlot) return result; + + foreach (QCPGraph *graph, mParentPlot->mGraphs) + { + if (graph->keyAxis() == this || graph->valueAxis() == this) + result.append(graph); + } + return result; +} + +/*! + Returns a list of all the items that are associated with this axis. An item is considered + associated with an axis if at least one of its positions uses the axis as key or value axis. + + \see plottables, graphs +*/ +QList QCPAxis::items() const +{ + QList result; + if (!mParentPlot) return result; + + foreach (QCPAbstractItem *item, mParentPlot->mItems) + { + foreach (QCPItemPosition *position, item->positions()) + { + if (position->keyAxis() == this || position->valueAxis() == this) + { + result.append(item); + break; + } + } + } + return result; +} + +/*! + Transforms a margin side to the logically corresponding axis type. (QCP::msLeft to + QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.) +*/ +QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side) +{ + switch (side) + { + case QCP::msLeft: return atLeft; + case QCP::msRight: return atRight; + case QCP::msTop: return atTop; + case QCP::msBottom: return atBottom; + default: break; + } + qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << static_cast(side); + return atLeft; +} + +/*! + Returns the axis type that describes the opposite axis of an axis with the specified \a type. +*/ +QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type) +{ + switch (type) + { + case atLeft: return atRight; + case atRight: return atLeft; + case atBottom: return atTop; + case atTop: return atBottom; + } + qDebug() << Q_FUNC_INFO << "invalid axis type"; + return atLeft; +} + +/* inherits documentation from base class */ +void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + SelectablePart part = details.value(); + if (mSelectableParts.testFlag(part)) + { + SelectableParts selBefore = mSelectedParts; + setSelectedParts(additive ? mSelectedParts^part : part); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPAxis::deselectEvent(bool *selectionStateChanged) +{ + SelectableParts selBefore = mSelectedParts; + setSelectedParts(mSelectedParts & ~mSelectableParts); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + For the axis to accept this event and perform the single axis drag, the parent \ref QCPAxisRect + must be configured accordingly, i.e. it must allow range dragging in the orientation of this axis + (\ref QCPAxisRect::setRangeDrag) and this axis must be a draggable axis (\ref + QCPAxisRect::setRangeDragAxes) + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. +*/ +void QCPAxis::mousePressEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag) || + !mAxisRect->rangeDrag().testFlag(orientation()) || + !mAxisRect->rangeDragAxes(orientation()).contains(this)) + { + event->ignore(); + return; + } + + if (event->buttons() & Qt::LeftButton) + { + mDragging = true; + // initialize antialiasing backup in case we start dragging: + if (mParentPlot->noAntialiasingOnDrag()) + { + mAADragBackup = mParentPlot->antialiasedElements(); + mNotAADragBackup = mParentPlot->notAntialiasedElements(); + } + // Mouse range dragging interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + mDragStartRange = mRange; + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. + + \see QCPAxis::mousePressEvent +*/ +void QCPAxis::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + if (mDragging) + { + const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y(); + const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y(); + if (mScaleType == QCPAxis::stLinear) + { + const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel); + setRange(mDragStartRange.lower+diff, mDragStartRange.upper+diff); + } else if (mScaleType == QCPAxis::stLogarithmic) + { + const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel); + setRange(mDragStartRange.lower*diff, mDragStartRange.upper*diff); + } + + if (mParentPlot->noAntialiasingOnDrag()) + mParentPlot->setNotAntialiasedElements(QCP::aeAll); + mParentPlot->replot(QCustomPlot::rpQueuedReplot); + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. + + \see QCPAxis::mousePressEvent +*/ +void QCPAxis::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(event) + Q_UNUSED(startPos) + mDragging = false; + if (mParentPlot->noAntialiasingOnDrag()) + { + mParentPlot->setAntialiasedElements(mAADragBackup); + mParentPlot->setNotAntialiasedElements(mNotAADragBackup); + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user zoom individual axes + exclusively, by performing the wheel event on top of the axis. + + For the axis to accept this event and perform the single axis zoom, the parent \ref QCPAxisRect + must be configured accordingly, i.e. it must allow range zooming in the orientation of this axis + (\ref QCPAxisRect::setRangeZoom) and this axis must be a zoomable axis (\ref + QCPAxisRect::setRangeZoomAxes) + + \seebaseclassmethod + + \note The zooming of possibly multiple axes at once by performing the wheel event anywhere in the + axis rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::wheelEvent. +*/ +void QCPAxis::wheelEvent(QWheelEvent *event) +{ + // Mouse range zooming interaction: + if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom) || + !mAxisRect->rangeZoom().testFlag(orientation()) || + !mAxisRect->rangeZoomAxes(orientation()).contains(this)) + { + event->ignore(); + return; + } + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + const double delta = event->delta(); +#else + const double delta = event->angleDelta().y(); +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPointF pos = event->pos(); +#else + const QPointF pos = event->position(); +#endif + + const double wheelSteps = delta/120.0; // a single step delta is +/-120 usually + const double factor = qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps); + scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? pos.x() : pos.y())); + mParentPlot->replot(); +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing axis lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \seebaseclassmethod + + \see setAntialiased +*/ +void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes); +} + +/*! \internal + + Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance. + + \seebaseclassmethod +*/ +void QCPAxis::draw(QCPPainter *painter) +{ + QVector subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter + QVector tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter + QVector tickLabels; // the final vector passed to QCPAxisPainter + tickPositions.reserve(mTickVector.size()); + tickLabels.reserve(mTickVector.size()); + subTickPositions.reserve(mSubTickVector.size()); + + if (mTicks) + { + for (int i=0; i(mSubTickVector.size()); + for (int i=0; itype = mAxisType; + mAxisPainter->basePen = getBasePen(); + mAxisPainter->labelFont = getLabelFont(); + mAxisPainter->labelColor = getLabelColor(); + mAxisPainter->label = mLabel; + mAxisPainter->substituteExponent = mNumberBeautifulPowers; + mAxisPainter->tickPen = getTickPen(); + mAxisPainter->subTickPen = getSubTickPen(); + mAxisPainter->tickLabelFont = getTickLabelFont(); + mAxisPainter->tickLabelColor = getTickLabelColor(); + mAxisPainter->axisRect = mAxisRect->rect(); + mAxisPainter->viewportRect = mParentPlot->viewport(); + mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic; + mAxisPainter->reversedEndings = mRangeReversed; + mAxisPainter->tickPositions = tickPositions; + mAxisPainter->tickLabels = tickLabels; + mAxisPainter->subTickPositions = subTickPositions; + mAxisPainter->draw(painter); +} + +/*! \internal + + Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling + QCPAxisTicker::generate on the currently installed ticker. + + If a change in the label text/count is detected, the cached axis margin is invalidated to make + sure the next margin calculation recalculates the label sizes and returns an up-to-date value. +*/ +void QCPAxis::setupTickVectors() +{ + if (!mParentPlot) return; + if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return; + + QVector oldLabels = mTickVectorLabels; + mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : nullptr, mTickLabels ? &mTickVectorLabels : nullptr); + mCachedMarginValid &= mTickVectorLabels == oldLabels; // if labels have changed, margin might have changed, too +} + +/*! \internal + + Returns the pen that is used to draw the axis base line. Depending on the selection state, this + is either mSelectedBasePen or mBasePen. +*/ +QPen QCPAxis::getBasePen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen; +} + +/*! \internal + + Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this + is either mSelectedTickPen or mTickPen. +*/ +QPen QCPAxis::getTickPen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen; +} + +/*! \internal + + Returns the pen that is used to draw the subticks. Depending on the selection state, this + is either mSelectedSubTickPen or mSubTickPen. +*/ +QPen QCPAxis::getSubTickPen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen; +} + +/*! \internal + + Returns the font that is used to draw the tick labels. Depending on the selection state, this + is either mSelectedTickLabelFont or mTickLabelFont. +*/ +QFont QCPAxis::getTickLabelFont() const +{ + return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont; +} + +/*! \internal + + Returns the font that is used to draw the axis label. Depending on the selection state, this + is either mSelectedLabelFont or mLabelFont. +*/ +QFont QCPAxis::getLabelFont() const +{ + return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont; +} + +/*! \internal + + Returns the color that is used to draw the tick labels. Depending on the selection state, this + is either mSelectedTickLabelColor or mTickLabelColor. +*/ +QColor QCPAxis::getTickLabelColor() const +{ + return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor; +} + +/*! \internal + + Returns the color that is used to draw the axis label. Depending on the selection state, this + is either mSelectedLabelColor or mLabelColor. +*/ +QColor QCPAxis::getLabelColor() const +{ + return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor; +} + +/*! \internal + + Returns the appropriate outward margin for this axis. It is needed if \ref + QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis with axis type \ref + atLeft will return an appropriate left margin, \ref atBottom will return an appropriate bottom + margin and so forth. For the calculation, this function goes through similar steps as \ref draw, + so changing one function likely requires the modification of the other one as well. + + The margin consists of the outward tick length, tick label padding, tick label size, label + padding, label size, and padding. + + The margin is cached internally, so repeated calls while leaving the axis range, fonts, etc. + unchanged are very fast. +*/ +int QCPAxis::calculateMargin() +{ + if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis + return 0; + + if (mCachedMarginValid) + return mCachedMargin; + + // run through similar steps as QCPAxis::draw, and calculate margin needed to fit axis and its labels + int margin = 0; + + QVector tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter + QVector tickLabels; // the final vector passed to QCPAxisPainter + tickPositions.reserve(mTickVector.size()); + tickLabels.reserve(mTickVector.size()); + + if (mTicks) + { + for (int i=0; itype = mAxisType; + mAxisPainter->labelFont = getLabelFont(); + mAxisPainter->label = mLabel; + mAxisPainter->tickLabelFont = mTickLabelFont; + mAxisPainter->axisRect = mAxisRect->rect(); + mAxisPainter->viewportRect = mParentPlot->viewport(); + mAxisPainter->tickPositions = tickPositions; + mAxisPainter->tickLabels = tickLabels; + margin += mAxisPainter->size(); + margin += mPadding; + + mCachedMargin = margin; + mCachedMarginValid = true; + return margin; +} + +/* inherits documentation from base class */ +QCP::Interaction QCPAxis::selectionCategory() const +{ + return QCP::iSelectAxes; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisPainterPrivate +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAxisPainterPrivate + + \internal + \brief (Private) + + This is a private class and not part of the public QCustomPlot interface. + + It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and + axis label. It also buffers the labels to reduce replot times. The parameters are configured by + directly accessing the public member variables. +*/ + +/*! + Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every + redraw, to utilize the caching mechanisms. +*/ +QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) : + type(QCPAxis::atLeft), + basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + lowerEnding(QCPLineEnding::esNone), + upperEnding(QCPLineEnding::esNone), + labelPadding(0), + tickLabelPadding(0), + tickLabelRotation(0), + tickLabelSide(QCPAxis::lsOutside), + substituteExponent(true), + numberMultiplyCross(false), + tickLengthIn(5), + tickLengthOut(0), + subTickLengthIn(2), + subTickLengthOut(0), + tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + offset(0), + abbreviateDecimalPowers(false), + reversedEndings(false), + mParentPlot(parentPlot), + mLabelCache(16) // cache at most 16 (tick) labels +{ +} + +QCPAxisPainterPrivate::~QCPAxisPainterPrivate() +{ +} + +/*! \internal + + Draws the axis with the specified \a painter. + + The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set + here, too. +*/ +void QCPAxisPainterPrivate::draw(QCPPainter *painter) +{ + QByteArray newHash = generateLabelParameterHash(); + if (newHash != mLabelParameterHash) + { + mLabelCache.clear(); + mLabelParameterHash = newHash; + } + + QPoint origin; + switch (type) + { + case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break; + case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break; + case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break; + case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break; + } + + double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes) + switch (type) + { + case QCPAxis::atTop: yCor = -1; break; + case QCPAxis::atRight: xCor = 1; break; + default: break; + } + int margin = 0; + // draw baseline: + QLineF baseLine; + painter->setPen(basePen); + if (QCPAxis::orientation(type) == Qt::Horizontal) + baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor)); + else + baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor)); + if (reversedEndings) + baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later + painter->drawLine(baseLine); + + // draw ticks: + if (!tickPositions.isEmpty()) + { + painter->setPen(tickPen); + int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis) + if (QCPAxis::orientation(type) == Qt::Horizontal) + { + foreach (double tickPos, tickPositions) + painter->drawLine(QLineF(tickPos+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPos+xCor, origin.y()+tickLengthIn*tickDir+yCor)); + } else + { + foreach (double tickPos, tickPositions) + painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPos+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPos+yCor)); + } + } + + // draw subticks: + if (!subTickPositions.isEmpty()) + { + painter->setPen(subTickPen); + // direction of ticks ("inward" is right for left axis and left for right axis) + int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; + if (QCPAxis::orientation(type) == Qt::Horizontal) + { + foreach (double subTickPos, subTickPositions) + painter->drawLine(QLineF(subTickPos+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPos+xCor, origin.y()+subTickLengthIn*tickDir+yCor)); + } else + { + foreach (double subTickPos, subTickPositions) + painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPos+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPos+yCor)); + } + } + margin += qMax(0, qMax(tickLengthOut, subTickLengthOut)); + + // draw axis base endings: + bool antialiasingBackup = painter->antialiasing(); + painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't + painter->setBrush(QBrush(basePen.color())); + QCPVector2D baseLineVector(baseLine.dx(), baseLine.dy()); + if (lowerEnding.style() != QCPLineEnding::esNone) + lowerEnding.draw(painter, QCPVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector); + if (upperEnding.style() != QCPLineEnding::esNone) + upperEnding.draw(painter, QCPVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector); + painter->setAntialiasing(antialiasingBackup); + + // tick labels: + QRect oldClipRect; + if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect + { + oldClipRect = painter->clipRegion().boundingRect(); + painter->setClipRect(axisRect); + } + QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label + if (!tickLabels.isEmpty()) + { + if (tickLabelSide == QCPAxis::lsOutside) + margin += tickLabelPadding; + painter->setFont(tickLabelFont); + painter->setPen(QPen(tickLabelColor)); + const int maxLabelIndex = static_cast(qMin(tickPositions.size(), tickLabels.size())); + int distanceToAxis = margin; + if (tickLabelSide == QCPAxis::lsInside) + distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding); + for (int i=0; isetClipRect(oldClipRect); + + // axis label: + QRect labelBounds; + if (!label.isEmpty()) + { + margin += labelPadding; + painter->setFont(labelFont); + painter->setPen(QPen(labelColor)); + labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label); + if (type == QCPAxis::atLeft) + { + QTransform oldTransform = painter->transform(); + painter->translate((origin.x()-margin-labelBounds.height()), origin.y()); + painter->rotate(-90); + painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label); + painter->setTransform(oldTransform); + } + else if (type == QCPAxis::atRight) + { + QTransform oldTransform = painter->transform(); + painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height()); + painter->rotate(90); + painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label); + painter->setTransform(oldTransform); + } + else if (type == QCPAxis::atTop) + painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label); + else if (type == QCPAxis::atBottom) + painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label); + } + + // set selection boxes: + int selectionTolerance = 0; + if (mParentPlot) + selectionTolerance = mParentPlot->selectionTolerance(); + else + qDebug() << Q_FUNC_INFO << "mParentPlot is null"; + int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance); + int selAxisInSize = selectionTolerance; + int selTickLabelSize; + int selTickLabelOffset; + if (tickLabelSide == QCPAxis::lsOutside) + { + selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width()); + selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding; + } else + { + selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width()); + selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding); + } + int selLabelSize = labelBounds.height(); + int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding; + if (type == QCPAxis::atLeft) + { + mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom()); + mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom()); + mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom()); + } else if (type == QCPAxis::atRight) + { + mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom()); + mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom()); + mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom()); + } else if (type == QCPAxis::atTop) + { + mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize); + mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset); + mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset); + } else if (type == QCPAxis::atBottom) + { + mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize); + mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset); + mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset); + } + mAxisSelectionBox = mAxisSelectionBox.normalized(); + mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized(); + mLabelSelectionBox = mLabelSelectionBox.normalized(); + // draw hitboxes for debug purposes: + //painter->setBrush(Qt::NoBrush); + //painter->drawRects(QVector() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox); +} + +/*! \internal + + Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone + direction) needed to fit the axis. +*/ +int QCPAxisPainterPrivate::size() +{ + int result = 0; + + QByteArray newHash = generateLabelParameterHash(); + if (newHash != mLabelParameterHash) + { + mLabelCache.clear(); + mLabelParameterHash = newHash; + } + + // get length of tick marks pointing outwards: + if (!tickPositions.isEmpty()) + result += qMax(0, qMax(tickLengthOut, subTickLengthOut)); + + // calculate size of tick labels: + if (tickLabelSide == QCPAxis::lsOutside) + { + QSize tickLabelsSize(0, 0); + if (!tickLabels.isEmpty()) + { + foreach (const QString &tickLabel, tickLabels) + getMaxTickLabelSize(tickLabelFont, tickLabel, &tickLabelsSize); + result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width(); + result += tickLabelPadding; + } + } + + // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees): + if (!label.isEmpty()) + { + QFontMetrics fontMetrics(labelFont); + QRect bounds; + bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label); + result += bounds.height() + labelPadding; + } + + return result; +} + +/*! \internal + + Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This + method is called automatically in \ref draw, if any parameters have changed that invalidate the + cached labels, such as font, color, etc. +*/ +void QCPAxisPainterPrivate::clearCache() +{ + mLabelCache.clear(); +} + +/*! \internal + + Returns a hash that allows uniquely identifying whether the label parameters have changed such + that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the + return value of this method hasn't changed since the last redraw, the respective label parameters + haven't changed and cached labels may be used. +*/ +QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const +{ + QByteArray result; + result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio())); + result.append(QByteArray::number(tickLabelRotation)); + result.append(QByteArray::number(int(tickLabelSide))); + result.append(QByteArray::number(int(substituteExponent))); + result.append(QByteArray::number(int(numberMultiplyCross))); + result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16)); + result.append(tickLabelFont.toString().toLatin1()); + return result; +} + +/*! \internal + + Draws a single tick label with the provided \a painter, utilizing the internal label cache to + significantly speed up drawing of labels that were drawn in previous calls. The tick label is + always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in + pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence + for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate), + at which the label should be drawn. + + In order to later draw the axis label in a place that doesn't overlap with the tick labels, the + largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref + drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a + tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently + holds. + + The label is drawn with the font and pen that are currently set on the \a painter. To draw + superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref + getTickLabelData). +*/ +void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize) +{ + // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly! + if (text.isEmpty()) return; + QSize finalSize; + QPointF labelAnchor; + switch (type) + { + case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break; + case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break; + case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break; + case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break; + } + if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled + { + CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache + if (!cachedLabel) // no cached label existed, create it + { + cachedLabel = new CachedLabel; + TickLabelData labelData = getTickLabelData(painter->font(), text); + cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft(); + if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio())) + { + cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio()); +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED +# ifdef QCP_DEVICEPIXELRATIO_FLOAT + cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF()); +# else + cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio()); +# endif +#endif + } else + cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()); + cachedLabel->pixmap.fill(Qt::transparent); + QCPPainter cachePainter(&cachedLabel->pixmap); + cachePainter.setPen(painter->pen()); + drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData); + } + // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; + if (tickLabelSide == QCPAxis::lsOutside) + { + if (QCPAxis::orientation(type) == Qt::Horizontal) + labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left(); + else + labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top(); + } + if (!labelClippedByBorder) + { + painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap); + finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio(); + } + mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created + } else // label caching disabled, draw text directly on surface: + { + TickLabelData labelData = getTickLabelData(painter->font(), text); + QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData); + // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; + if (tickLabelSide == QCPAxis::lsOutside) + { + if (QCPAxis::orientation(type) == Qt::Horizontal) + labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left(); + else + labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top(); + } + if (!labelClippedByBorder) + { + drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData); + finalSize = labelData.rotatedTotalBounds.size(); + } + } + + // expand passed tickLabelsSize if current tick label is larger: + if (finalSize.width() > tickLabelsSize->width()) + tickLabelsSize->setWidth(finalSize.width()); + if (finalSize.height() > tickLabelsSize->height()) + tickLabelsSize->setHeight(finalSize.height()); +} + +/*! \internal + + This is a \ref placeTickLabel helper function. + + Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a + y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to + directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when + QCP::phCacheLabels plotting hint is not set. +*/ +void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const +{ + // backup painter settings that we're about to change: + QTransform oldTransform = painter->transform(); + QFont oldFont = painter->font(); + + // transform painter to position/rotation: + painter->translate(x, y); + if (!qFuzzyIsNull(tickLabelRotation)) + painter->rotate(tickLabelRotation); + + // draw text: + if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used + { + painter->setFont(labelData.baseFont); + painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart); + if (!labelData.suffixPart.isEmpty()) + painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart); + painter->setFont(labelData.expFont); + painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart); + } else + { + painter->setFont(labelData.baseFont); + painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart); + } + + // reset painter settings to what it was before: + painter->setTransform(oldTransform); + painter->setFont(oldFont); +} + +/*! \internal + + This is a \ref placeTickLabel helper function. + + Transforms the passed \a text and \a font to a tickLabelData structure that can then be further + processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and + exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes. +*/ +QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const +{ + TickLabelData result; + + // determine whether beautiful decimal powers should be used + bool useBeautifulPowers = false; + int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart + int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart + if (substituteExponent) + { + ePos = static_cast(text.indexOf(QString(mParentPlot->locale().exponential()))); + if (ePos > 0 && text.at(ePos-1).isDigit()) + { + eLast = ePos; + while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit())) + ++eLast; + if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power + useBeautifulPowers = true; + } + } + + // calculate text bounding rects and do string preparation for beautiful decimal powers: + result.baseFont = font; + if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line + result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding + if (useBeautifulPowers) + { + // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent: + result.basePart = text.left(ePos); + result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent + // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base: + if (abbreviateDecimalPowers && result.basePart == QLatin1String("1")) + result.basePart = QLatin1String("10"); + else + result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10"); + result.expPart = text.mid(ePos+1, eLast-ePos); + // clip "+" and leading zeros off expPart: + while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e' + result.expPart.remove(1, 1); + if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+')) + result.expPart.remove(0, 1); + // prepare smaller font for exponent: + result.expFont = font; + if (result.expFont.pointSize() > 0) + result.expFont.setPointSize(int(result.expFont.pointSize()*0.75)); + else + result.expFont.setPixelSize(int(result.expFont.pixelSize()*0.75)); + // calculate bounding rects of base part(s), exponent part and total one: + result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart); + result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart); + if (!result.suffixPart.isEmpty()) + result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart); + result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA + } else // useBeautifulPowers == false + { + result.basePart = text; + result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart); + } + result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler + + // calculate possibly different bounding rect after rotation: + result.rotatedTotalBounds = result.totalBounds; + if (!qFuzzyIsNull(tickLabelRotation)) + { + QTransform transform; + transform.rotate(tickLabelRotation); + result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds); + } + + return result; +} + +/*! \internal + + This is a \ref placeTickLabel helper function. + + Calculates the offset at which the top left corner of the specified tick label shall be drawn. + The offset is relative to a point right next to the tick the label belongs to. + + This function is thus responsible for e.g. centering tick labels under ticks and positioning them + appropriately when they are rotated. +*/ +QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const +{ + /* + calculate label offset from base point at tick (non-trivial, for best visual appearance): short + explanation for bottom axis: The anchor, i.e. the point in the label that is placed + horizontally under the corresponding tick is always on the label side that is closer to the + axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height + is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text + will be centered under the tick (i.e. displaced horizontally by half its height). At the same + time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick + labels. + */ + bool doRotation = !qFuzzyIsNull(tickLabelRotation); + bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes. + double radians = tickLabelRotation/180.0*M_PI; + double x = 0; + double y = 0; + if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label + { + if (doRotation) + { + if (tickLabelRotation > 0) + { + x = -qCos(radians)*labelData.totalBounds.width(); + y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0; + } else + { + x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height(); + y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0; + } + } else + { + x = -labelData.totalBounds.width(); + y = -labelData.totalBounds.height()/2.0; + } + } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label + { + if (doRotation) + { + if (tickLabelRotation > 0) + { + x = +qSin(radians)*labelData.totalBounds.height(); + y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0; + } else + { + x = 0; + y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0; + } + } else + { + x = 0; + y = -labelData.totalBounds.height()/2.0; + } + } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label + { + if (doRotation) + { + if (tickLabelRotation > 0) + { + x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0; + y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height(); + } else + { + x = -qSin(-radians)*labelData.totalBounds.height()/2.0; + y = -qCos(-radians)*labelData.totalBounds.height(); + } + } else + { + x = -labelData.totalBounds.width()/2.0; + y = -labelData.totalBounds.height(); + } + } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label + { + if (doRotation) + { + if (tickLabelRotation > 0) + { + x = +qSin(radians)*labelData.totalBounds.height()/2.0; + y = 0; + } else + { + x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0; + y = +qSin(-radians)*labelData.totalBounds.width(); + } + } else + { + x = -labelData.totalBounds.width()/2.0; + y = 0; + } + } + + return {x, y}; +} + +/*! \internal + + Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label + to be drawn, depending on number format etc. Since only the largest tick label is wanted for the + margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a + smaller width/height. +*/ +void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const +{ + // note: this function must return the same tick label sizes as the placeTickLabel function. + QSize finalSize; + if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label + { + const CachedLabel *cachedLabel = mLabelCache.object(text); + finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio(); + } else // label caching disabled or no label with this text cached: + { + TickLabelData labelData = getTickLabelData(font, text); + finalSize = labelData.rotatedTotalBounds.size(); + } + + // expand passed tickLabelsSize if current tick label is larger: + if (finalSize.width() > tickLabelsSize->width()) + tickLabelsSize->setWidth(finalSize.width()); + if (finalSize.height() > tickLabelsSize->height()) + tickLabelsSize->setHeight(finalSize.height()); +} +/* end of 'src/axis/axis.cpp' */ + + +/* including file 'src/scatterstyle.cpp' */ +/* modified 2022-11-06T12:45:56, size 17466 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPScatterStyle +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPScatterStyle + \brief Represents the visual appearance of scatter points + + This class holds information about shape, color and size of scatter points. In plottables like + QCPGraph it is used to store how scatter points shall be drawn. For example, \ref + QCPGraph::setScatterStyle takes a QCPScatterStyle instance. + + A scatter style consists of a shape (\ref setShape), a line color (\ref setPen) and possibly a + fill (\ref setBrush), if the shape provides a fillable area. Further, the size of the shape can + be controlled with \ref setSize. + + \section QCPScatterStyle-defining Specifying a scatter style + + You can set all these configurations either by calling the respective functions on an instance: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1 + + Or you can use one of the various constructors that take different parameter combinations, making + it easy to specify a scatter style in a single call, like so: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-2 + + \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the plottable + + There are two constructors which leave the pen undefined: \ref QCPScatterStyle() and \ref + QCPScatterStyle(ScatterShape shape, double size). If those constructors are used, a call to \ref + isPenDefined will return false. It leads to scatter points that inherit the pen from the + plottable that uses the scatter style. Thus, if such a scatter style is passed to QCPGraph, the line + color of the graph (\ref QCPGraph::setPen) will be used by the scatter points. This makes + it very convenient to set up typical scatter settings: + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-shortcreation + + Notice that it wasn't even necessary to explicitly call a QCPScatterStyle constructor. This works + because QCPScatterStyle provides a constructor that can transform a \ref ScatterShape directly + into a QCPScatterStyle instance (that's the \ref QCPScatterStyle(ScatterShape shape, double size) + constructor with a default for \a size). In those cases, C++ allows directly supplying a \ref + ScatterShape, where actually a QCPScatterStyle is expected. + + \section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps + + QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as scatter points. + + For custom shapes, you can provide a QPainterPath with the desired shape to the \ref + setCustomPath function or call the constructor that takes a painter path. The scatter shape will + automatically be set to \ref ssCustom. + + For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively you can use the + constructor that takes a QPixmap. The scatter shape will automatically be set to \ref ssPixmap. + Note that \ref setSize does not influence the appearance of the pixmap. +*/ + +/* start documentation of inline functions */ + +/*! \fn bool QCPScatterStyle::isNone() const + + Returns whether the scatter shape is \ref ssNone. + + \see setShape +*/ + +/*! \fn bool QCPScatterStyle::isPenDefined() const + + Returns whether a pen has been defined for this scatter style. + + The pen is undefined if a constructor is called that does not carry \a pen as parameter. Those + are \ref QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size). If the pen + is undefined, the pen of the respective plottable will be used for drawing scatters. + + If a pen was defined for this scatter style instance, and you now wish to undefine the pen, call + \ref undefinePen. + + \see setPen +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or brush is defined. + + Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited + from the plottable that uses this scatter style. +*/ +QCPScatterStyle::QCPScatterStyle() : + mSize(6), + mShape(ssNone), + mPen(Qt::NoPen), + mBrush(Qt::NoBrush), + mPenDefined(false) +{ +} + +/*! + Creates a new QCPScatterStyle instance with shape set to \a shape and size to \a size. No pen or + brush is defined. + + Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited + from the plottable that uses this scatter style. +*/ +QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) : + mSize(size), + mShape(shape), + mPen(Qt::NoPen), + mBrush(Qt::NoBrush), + mPenDefined(false) +{ +} + +/*! + Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color, + and size to \a size. No brush is defined, i.e. the scatter point will not be filled. +*/ +QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) : + mSize(size), + mShape(shape), + mPen(QPen(color)), + mBrush(Qt::NoBrush), + mPenDefined(true) +{ +} + +/*! + Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color, + the brush color to \a fill (with a solid pattern), and size to \a size. +*/ +QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) : + mSize(size), + mShape(shape), + mPen(QPen(color)), + mBrush(QBrush(fill)), + mPenDefined(true) +{ +} + +/*! + Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set to \a pen, the + brush to \a brush, and size to \a size. + + \warning In some cases it might be tempting to directly use a pen style like Qt::NoPen as \a pen + and a color like Qt::blue as \a brush. Notice however, that the corresponding call\n + QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)\n + doesn't necessarily lead C++ to use this constructor in some cases, but might mistake + Qt::NoPen for a QColor and use the + \ref QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) + constructor instead (which will lead to an unexpected look of the scatter points). To prevent + this, be more explicit with the parameter types. For example, use QBrush(Qt::blue) + instead of just Qt::blue, to clearly point out to the compiler that this constructor is + wanted. +*/ +QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) : + mSize(size), + mShape(shape), + mPen(pen), + mBrush(brush), + mPenDefined(pen.style() != Qt::NoPen) +{ +} + +/*! + Creates a new QCPScatterStyle instance which will show the specified \a pixmap. The scatter shape + is set to \ref ssPixmap. +*/ +QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) : + mSize(5), + mShape(ssPixmap), + mPen(Qt::NoPen), + mBrush(Qt::NoBrush), + mPixmap(pixmap), + mPenDefined(false) +{ +} + +/*! + Creates a new QCPScatterStyle instance with a custom shape that is defined via \a customPath. The + scatter shape is set to \ref ssCustom. + + The custom shape line will be drawn with \a pen and filled with \a brush. The size has a slightly + different meaning than for built-in scatter points: The custom path will be drawn scaled by a + factor of \a size/6.0. Since the default \a size is 6, the custom path will appear in its + original size by default. To for example double the size of the path, set \a size to 12. +*/ +QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) : + mSize(size), + mShape(ssCustom), + mPen(pen), + mBrush(brush), + mCustomPath(customPath), + mPenDefined(pen.style() != Qt::NoPen) +{ +} + +/*! + Copies the specified \a properties from the \a other scatter style to this scatter style. +*/ +void QCPScatterStyle::setFromOther(const QCPScatterStyle &other, ScatterProperties properties) +{ + if (properties.testFlag(spPen)) + { + setPen(other.pen()); + if (!other.isPenDefined()) + undefinePen(); + } + if (properties.testFlag(spBrush)) + setBrush(other.brush()); + if (properties.testFlag(spSize)) + setSize(other.size()); + if (properties.testFlag(spShape)) + { + setShape(other.shape()); + if (other.shape() == ssPixmap) + setPixmap(other.pixmap()); + else if (other.shape() == ssCustom) + setCustomPath(other.customPath()); + } +} + +/*! + Sets the size (pixel diameter) of the drawn scatter points to \a size. + + \see setShape +*/ +void QCPScatterStyle::setSize(double size) +{ + mSize = size; +} + +/*! + Sets the shape to \a shape. + + Note that the calls \ref setPixmap and \ref setCustomPath automatically set the shape to \ref + ssPixmap and \ref ssCustom, respectively. + + \see setSize +*/ +void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape) +{ + mShape = shape; +} + +/*! + Sets the pen that will be used to draw scatter points to \a pen. + + If the pen was previously undefined (see \ref isPenDefined), the pen is considered defined after + a call to this function, even if \a pen is Qt::NoPen. If you have defined a pen + previously by calling this function and now wish to undefine the pen, call \ref undefinePen. + + \see setBrush +*/ +void QCPScatterStyle::setPen(const QPen &pen) +{ + mPenDefined = true; + mPen = pen; +} + +/*! + Sets the brush that will be used to fill scatter points to \a brush. Note that not all scatter + shapes have fillable areas. For example, \ref ssPlus does not while \ref ssCircle does. + + \see setPen +*/ +void QCPScatterStyle::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the pixmap that will be drawn as scatter point to \a pixmap. + + Note that \ref setSize does not influence the appearance of the pixmap. + + The scatter shape is automatically set to \ref ssPixmap. +*/ +void QCPScatterStyle::setPixmap(const QPixmap &pixmap) +{ + setShape(ssPixmap); + mPixmap = pixmap; +} + +/*! + Sets the custom shape that will be drawn as scatter point to \a customPath. + + The scatter shape is automatically set to \ref ssCustom. +*/ +void QCPScatterStyle::setCustomPath(const QPainterPath &customPath) +{ + setShape(ssCustom); + mCustomPath = customPath; +} + +/*! + Sets this scatter style to have an undefined pen (see \ref isPenDefined for what an undefined pen + implies). + + A call to \ref setPen will define a pen. +*/ +void QCPScatterStyle::undefinePen() +{ + mPenDefined = false; +} + +/*! + Applies the pen and the brush of this scatter style to \a painter. If this scatter style has an + undefined pen (\ref isPenDefined), sets the pen of \a painter to \a defaultPen instead. + + This function is used by plottables (or any class that wants to draw scatters) just before a + number of scatters with this style shall be drawn with the \a painter. + + \see drawShape +*/ +void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const +{ + painter->setPen(mPenDefined ? mPen : defaultPen); + painter->setBrush(mBrush); +} + +/*! + Draws the scatter shape with \a painter at position \a pos. + + This function does not modify the pen or the brush on the painter, as \ref applyTo is meant to be + called before scatter points are drawn with \ref drawShape. + + \see applyTo +*/ +void QCPScatterStyle::drawShape(QCPPainter *painter, const QPointF &pos) const +{ + drawShape(painter, pos.x(), pos.y()); +} + +/*! \overload + Draws the scatter shape with \a painter at position \a x and \a y. +*/ +void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const +{ + double w = mSize/2.0; + switch (mShape) + { + case ssNone: break; + case ssDot: + { + painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y)); + break; + } + case ssCross: + { + painter->drawLine(QLineF(x-w, y-w, x+w, y+w)); + painter->drawLine(QLineF(x-w, y+w, x+w, y-w)); + break; + } + case ssPlus: + { + painter->drawLine(QLineF(x-w, y, x+w, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); + break; + } + case ssCircle: + { + painter->drawEllipse(QPointF(x , y), w, w); + break; + } + case ssDisc: + { + QBrush b = painter->brush(); + painter->setBrush(painter->pen().color()); + painter->drawEllipse(QPointF(x , y), w, w); + painter->setBrush(b); + break; + } + case ssSquare: + { + painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + break; + } + case ssDiamond: + { + QPointF lineArray[4] = {QPointF(x-w, y), + QPointF( x, y-w), + QPointF(x+w, y), + QPointF( x, y+w)}; + painter->drawPolygon(lineArray, 4); + break; + } + case ssStar: + { + painter->drawLine(QLineF(x-w, y, x+w, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); + painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707)); + painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707)); + break; + } + case ssTriangle: + { + QPointF lineArray[3] = {QPointF(x-w, y+0.755*w), + QPointF(x+w, y+0.755*w), + QPointF( x, y-0.977*w)}; + painter->drawPolygon(lineArray, 3); + break; + } + case ssTriangleInverted: + { + QPointF lineArray[3] = {QPointF(x-w, y-0.755*w), + QPointF(x+w, y-0.755*w), + QPointF( x, y+0.977*w)}; + painter->drawPolygon(lineArray, 3); + break; + } + case ssCrossSquare: + { + painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95)); + painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w)); + break; + } + case ssPlusSquare: + { + painter->drawRect(QRectF(x-w, y-w, mSize, mSize)); + painter->drawLine(QLineF(x-w, y, x+w*0.95, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); + break; + } + case ssCrossCircle: + { + painter->drawEllipse(QPointF(x, y), w, w); + painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670)); + painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707)); + break; + } + case ssPlusCircle: + { + painter->drawEllipse(QPointF(x, y), w, w); + painter->drawLine(QLineF(x-w, y, x+w, y)); + painter->drawLine(QLineF( x, y+w, x, y-w)); + break; + } + case ssPeace: + { + painter->drawEllipse(QPointF(x, y), w, w); + painter->drawLine(QLineF(x, y-w, x, y+w)); + painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707)); + painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707)); + break; + } + case ssPixmap: + { + const double widthHalf = mPixmap.width()*0.5; + const double heightHalf = mPixmap.height()*0.5; +#if QT_VERSION < QT_VERSION_CHECK(4, 8, 0) + const QRectF clipRect = painter->clipRegion().boundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf); +#else + const QRectF clipRect = painter->clipBoundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf); +#endif + if (clipRect.contains(x, y)) + painter->drawPixmap(qRound(x-widthHalf), qRound(y-heightHalf), mPixmap); + break; + } + case ssCustom: + { + QTransform oldTransform = painter->transform(); + painter->translate(x, y); + painter->scale(mSize/6.0, mSize/6.0); + painter->drawPath(mCustomPath); + painter->setTransform(oldTransform); + break; + } + } +} +/* end of 'src/scatterstyle.cpp' */ + + +/* including file 'src/plottable.cpp' */ +/* modified 2022-11-06T12:45:56, size 38818 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPSelectionDecorator +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPSelectionDecorator + \brief Controls how a plottable's data selection is drawn + + Each \ref QCPAbstractPlottable instance has one \ref QCPSelectionDecorator (accessible via \ref + QCPAbstractPlottable::selectionDecorator) and uses it when drawing selected segments of its data. + + The selection decorator controls both pen (\ref setPen) and brush (\ref setBrush), as well as the + scatter style (\ref setScatterStyle) if the plottable draws scatters. Since a \ref + QCPScatterStyle is itself composed of different properties such as color shape and size, the + decorator allows specifying exactly which of those properties shall be used for the selected data + point, via \ref setUsedScatterProperties. + + A \ref QCPSelectionDecorator subclass instance can be passed to a plottable via \ref + QCPAbstractPlottable::setSelectionDecorator, allowing greater customizability of the appearance + of selected segments. + + Use \ref copyFrom to easily transfer the settings of one decorator to another one. This is + especially useful since plottables take ownership of the passed selection decorator, and thus the + same decorator instance can not be passed to multiple plottables. + + Selection decorators can also themselves perform drawing operations by reimplementing \ref + drawDecoration, which is called by the plottable's draw method. The base class \ref + QCPSelectionDecorator does not make use of this however. For example, \ref + QCPSelectionDecoratorBracket draws brackets around selected data segments. +*/ + +/*! + Creates a new QCPSelectionDecorator instance with default values +*/ +QCPSelectionDecorator::QCPSelectionDecorator() : + mPen(QColor(80, 80, 255), 2.5), + mBrush(Qt::NoBrush), + mUsedScatterProperties(QCPScatterStyle::spNone), + mPlottable(nullptr) +{ +} + +QCPSelectionDecorator::~QCPSelectionDecorator() +{ +} + +/*! + Sets the pen that will be used by the parent plottable to draw selected data segments. +*/ +void QCPSelectionDecorator::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the brush that will be used by the parent plottable to draw selected data segments. +*/ +void QCPSelectionDecorator::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the scatter style that will be used by the parent plottable to draw scatters in selected + data segments. + + \a usedProperties specifies which parts of the passed \a scatterStyle will be used by the + plottable. The used properties can also be changed via \ref setUsedScatterProperties. +*/ +void QCPSelectionDecorator::setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties) +{ + mScatterStyle = scatterStyle; + setUsedScatterProperties(usedProperties); +} + +/*! + Use this method to define which properties of the scatter style (set via \ref setScatterStyle) + will be used for selected data segments. All properties of the scatter style that are not + specified in \a properties will remain as specified in the plottable's original scatter style. + + \see QCPScatterStyle::ScatterProperty +*/ +void QCPSelectionDecorator::setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties) +{ + mUsedScatterProperties = properties; +} + +/*! + Sets the pen of \a painter to the pen of this selection decorator. + + \see applyBrush, getFinalScatterStyle +*/ +void QCPSelectionDecorator::applyPen(QCPPainter *painter) const +{ + painter->setPen(mPen); +} + +/*! + Sets the brush of \a painter to the brush of this selection decorator. + + \see applyPen, getFinalScatterStyle +*/ +void QCPSelectionDecorator::applyBrush(QCPPainter *painter) const +{ + painter->setBrush(mBrush); +} + +/*! + Returns the scatter style that the parent plottable shall use for selected scatter points. The + plottable's original (unselected) scatter style must be passed as \a unselectedStyle. Depending + on the setting of \ref setUsedScatterProperties, the returned scatter style is a mixture of this + selecion decorator's scatter style (\ref setScatterStyle), and \a unselectedStyle. + + \see applyPen, applyBrush, setScatterStyle +*/ +QCPScatterStyle QCPSelectionDecorator::getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const +{ + QCPScatterStyle result(unselectedStyle); + result.setFromOther(mScatterStyle, mUsedScatterProperties); + + // if style shall inherit pen from plottable (has no own pen defined), give it the selected + // plottable pen explicitly, so it doesn't use the unselected plottable pen when used in the + // plottable: + if (!result.isPenDefined()) + result.setPen(mPen); + + return result; +} + +/*! + Copies all properties (e.g. color, fill, scatter style) of the \a other selection decorator to + this selection decorator. +*/ +void QCPSelectionDecorator::copyFrom(const QCPSelectionDecorator *other) +{ + setPen(other->pen()); + setBrush(other->brush()); + setScatterStyle(other->scatterStyle(), other->usedScatterProperties()); +} + +/*! + This method is called by all plottables' draw methods to allow custom selection decorations to be + drawn. Use the passed \a painter to perform the drawing operations. \a selection carries the data + selection for which the decoration shall be drawn. + + The default base class implementation of \ref QCPSelectionDecorator has no special decoration, so + this method does nothing. +*/ +void QCPSelectionDecorator::drawDecoration(QCPPainter *painter, QCPDataSelection selection) +{ + Q_UNUSED(painter) + Q_UNUSED(selection) +} + +/*! \internal + + This method is called as soon as a selection decorator is associated with a plottable, by a call + to \ref QCPAbstractPlottable::setSelectionDecorator. This way the selection decorator can obtain a pointer to the plottable that uses it (e.g. to access + data points via the \ref QCPAbstractPlottable::interface1D interface). + + If the selection decorator was already added to a different plottable before, this method aborts + the registration and returns false. +*/ +bool QCPSelectionDecorator::registerWithPlottable(QCPAbstractPlottable *plottable) +{ + if (!mPlottable) + { + mPlottable = plottable; + return true; + } else + { + qDebug() << Q_FUNC_INFO << "This selection decorator is already registered with plottable:" << reinterpret_cast(mPlottable); + return false; + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractPlottable +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractPlottable + \brief The abstract base class for all data representing objects in a plot. + + It defines a very basic interface like name, pen, brush, visibility etc. Since this class is + abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself to + create new ways of displaying data (see "Creating own plottables" below). Plottables that display + one-dimensional data (i.e. data points have a single key dimension and one or multiple values at + each key) are based off of the template subclass \ref QCPAbstractPlottable1D, see details + there. + + All further specifics are in the subclasses, for example: + \li A normal graph with possibly a line and/or scatter points \ref QCPGraph + (typically created with \ref QCustomPlot::addGraph) + \li A parametric curve: \ref QCPCurve + \li A bar chart: \ref QCPBars + \li A statistical box plot: \ref QCPStatisticalBox + \li A color encoded two-dimensional map: \ref QCPColorMap + \li An OHLC/Candlestick chart: \ref QCPFinancial + + \section plottables-subclassing Creating own plottables + + Subclassing directly from QCPAbstractPlottable is only recommended if you wish to display + two-dimensional data like \ref QCPColorMap, i.e. two logical key dimensions and one (or more) + data dimensions. If you want to display data with only one logical key dimension, you should + rather derive from \ref QCPAbstractPlottable1D. + + If subclassing QCPAbstractPlottable directly, these are the pure virtual functions you must + implement: + \li \ref selectTest + \li \ref draw + \li \ref drawLegendIcon + \li \ref getKeyRange + \li \ref getValueRange + + See the documentation of those functions for what they need to do. + + For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot + coordinates to pixel coordinates. This function is quite convenient, because it takes the + orientation of the key and value axes into account for you (x and y are swapped when the key axis + is vertical and the value axis horizontal). If you are worried about performance (i.e. you need + to translate many points in a loop like QCPGraph), you can directly use \ref + QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis + yourself. + + Here are some important members you inherit from QCPAbstractPlottable: + + + + + + + + + + + + + + + + + + + + + + + + + + +
QCustomPlot *\b mParentPlotA pointer to the parent QCustomPlot instance. The parent plot is inferred from the axes that are passed in the constructor.
QString \b mNameThe name of the plottable.
QPen \b mPenThe generic pen of the plottable. You should use this pen for the most prominent data representing lines in the plottable + (e.g QCPGraph uses this pen for its graph lines and scatters)
QBrush \b mBrushThe generic brush of the plottable. You should use this brush for the most prominent fillable structures in the plottable + (e.g. QCPGraph uses this brush to control filling under the graph)
QPointer<\ref QCPAxis> \b mKeyAxis, \b mValueAxisThe key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates + to pixels in either the key or value dimension. Make sure to check whether the pointer is \c nullptr before using it. If one of + the axes is null, don't draw the plottable.
\ref QCPSelectionDecorator \b mSelectionDecoratorThe currently set selection decorator which specifies how selected data of the plottable shall be drawn and decorated. + When drawing your data, you must consult this decorator for the appropriate pen/brush before drawing unselected/selected data segments. + Finally, you should call its \ref QCPSelectionDecorator::drawDecoration method at the end of your \ref draw implementation.
\ref QCP::SelectionType \b mSelectableIn which composition, if at all, this plottable's data may be selected. Enforcing this setting on the data selection is done + by QCPAbstractPlottable automatically.
\ref QCPDataSelection \b mSelectionHolds the current selection state of the plottable's data, i.e. the selected data ranges (\ref QCPDataRange).
+*/ + +/* start of documentation of inline functions */ + +/*! \fn QCPSelectionDecorator *QCPAbstractPlottable::selectionDecorator() const + + Provides access to the selection decorator of this plottable. The selection decorator controls + how selected data ranges are drawn (e.g. their pen color and fill), see \ref + QCPSelectionDecorator for details. + + If you wish to use an own \ref QCPSelectionDecorator subclass, pass an instance of it to \ref + setSelectionDecorator. +*/ + +/*! \fn bool QCPAbstractPlottable::selected() const + + Returns true if there are any data points of the plottable currently selected. Use \ref selection + to retrieve the current \ref QCPDataSelection. +*/ + +/*! \fn QCPDataSelection QCPAbstractPlottable::selection() const + + Returns a \ref QCPDataSelection encompassing all the data points that are currently selected on + this plottable. + + \see selected, setSelection, setSelectable +*/ + +/*! \fn virtual QCPPlottableInterface1D *QCPAbstractPlottable::interface1D() + + If this plottable is a one-dimensional plottable, i.e. it implements the \ref + QCPPlottableInterface1D, returns the \a this pointer with that type. Otherwise (e.g. in the case + of a \ref QCPColorMap) returns zero. + + You can use this method to gain read access to data coordinates while holding a pointer to the + abstract base class only. +*/ + +/* end of documentation of inline functions */ +/* start of documentation of pure virtual functions */ + +/*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0 + \internal + + called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation + of this plottable inside \a rect, next to the plottable name. + + The passed \a painter has its cliprect set to \a rect, so painting outside of \a rect won't + appear outside the legend icon border. +*/ + +/*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const = 0 + + Returns the coordinate range that all data in this plottable span in the key axis dimension. For + logarithmic plots, one can set \a inSignDomain to either \ref QCP::sdNegative or \ref + QCP::sdPositive in order to restrict the returned range to that sign domain. E.g. when only + negative range is wanted, set \a inSignDomain to \ref QCP::sdNegative and all positive points + will be ignored for range calculation. For no restriction, just set \a inSignDomain to \ref + QCP::sdBoth (default). \a foundRange is an output parameter that indicates whether a range could + be found or not. If this is false, you shouldn't use the returned range (e.g. no points in data). + + Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by + this function may have size zero (e.g. when there is only one data point). In this case \a + foundRange would return true, but the returned range is not a valid range in terms of \ref + QCPRange::validRange. + + \see rescaleAxes, getValueRange +*/ + +/*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const = 0 + + Returns the coordinate range that the data points in the specified key range (\a inKeyRange) span + in the value axis dimension. For logarithmic plots, one can set \a inSignDomain to either \ref + QCP::sdNegative or \ref QCP::sdPositive in order to restrict the returned range to that sign + domain. E.g. when only negative range is wanted, set \a inSignDomain to \ref QCP::sdNegative and + all positive points will be ignored for range calculation. For no restriction, just set \a + inSignDomain to \ref QCP::sdBoth (default). \a foundRange is an output parameter that indicates + whether a range could be found or not. If this is false, you shouldn't use the returned range + (e.g. no points in data). + + If \a inKeyRange has both lower and upper bound set to zero (is equal to QCPRange()), + all data points are considered, without any restriction on the keys. + + Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by + this function may have size zero (e.g. when there is only one data point). In this case \a + foundRange would return true, but the returned range is not a valid range in terms of \ref + QCPRange::validRange. + + \see rescaleAxes, getKeyRange +*/ + +/* end of documentation of pure virtual functions */ +/* start of documentation of signals */ + +/*! \fn void QCPAbstractPlottable::selectionChanged(bool selected) + + This signal is emitted when the selection state of this plottable has changed, either by user + interaction or by a direct call to \ref setSelection. The parameter \a selected indicates whether + there are any points selected or not. + + \see selectionChanged(const QCPDataSelection &selection) +*/ + +/*! \fn void QCPAbstractPlottable::selectionChanged(const QCPDataSelection &selection) + + This signal is emitted when the selection state of this plottable has changed, either by user + interaction or by a direct call to \ref setSelection. The parameter \a selection holds the + currently selected data ranges. + + \see selectionChanged(bool selected) +*/ + +/*! \fn void QCPAbstractPlottable::selectableChanged(QCP::SelectionType selectable); + + This signal is emitted when the selectability of this plottable has changed. + + \see setSelectable +*/ + +/* end of documentation of signals */ + +/*! + Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as + its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance + and have perpendicular orientations. If either of these restrictions is violated, a corresponding + message is printed to the debug output (qDebug), the construction is not aborted, though. + + Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables, + it can't be directly instantiated. + + You probably want one of the subclasses like \ref QCPGraph or \ref QCPCurve instead. +*/ +QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()), + mName(), + mAntialiasedFill(true), + mAntialiasedScatters(true), + mPen(Qt::black), + mBrush(Qt::NoBrush), + mKeyAxis(keyAxis), + mValueAxis(valueAxis), + mSelectable(QCP::stWhole), + mSelectionDecorator(nullptr) +{ + if (keyAxis->parentPlot() != valueAxis->parentPlot()) + qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis."; + if (keyAxis->orientation() == valueAxis->orientation()) + qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other."; + + mParentPlot->registerPlottable(this); + setSelectionDecorator(new QCPSelectionDecorator); +} + +QCPAbstractPlottable::~QCPAbstractPlottable() +{ + if (mSelectionDecorator) + { + delete mSelectionDecorator; + mSelectionDecorator = nullptr; + } +} + +/*! + The name is the textual representation of this plottable as it is displayed in the legend + (\ref QCPLegend). It may contain any UTF-8 characters, including newlines. +*/ +void QCPAbstractPlottable::setName(const QString &name) +{ + mName = name; +} + +/*! + Sets whether fills of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPAbstractPlottable::setAntialiasedFill(bool enabled) +{ + mAntialiasedFill = enabled; +} + +/*! + Sets whether the scatter symbols of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPAbstractPlottable::setAntialiasedScatters(bool enabled) +{ + mAntialiasedScatters = enabled; +} + +/*! + The pen is used to draw basic lines that make up the plottable representation in the + plot. + + For example, the \ref QCPGraph subclass draws its graph lines with this pen. + + \see setBrush +*/ +void QCPAbstractPlottable::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + The brush is used to draw basic fills of the plottable representation in the + plot. The Fill can be a color, gradient or texture, see the usage of QBrush. + + For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when + it's not set to Qt::NoBrush. + + \see setPen +*/ +void QCPAbstractPlottable::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal + to the plottable's value axis. This function performs no checks to make sure this is the case. + The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the + y-axis (QCustomPlot::yAxis) as value axis. + + Normally, the key and value axes are set in the constructor of the plottable (or \ref + QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface). + + \see setValueAxis +*/ +void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis) +{ + mKeyAxis = axis; +} + +/*! + The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is + orthogonal to the plottable's key axis. This function performs no checks to make sure this is the + case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and + the y-axis (QCustomPlot::yAxis) as value axis. + + Normally, the key and value axes are set in the constructor of the plottable (or \ref + QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface). + + \see setKeyAxis +*/ +void QCPAbstractPlottable::setValueAxis(QCPAxis *axis) +{ + mValueAxis = axis; +} + + +/*! + Sets which data ranges of this plottable are selected. Selected data ranges are drawn differently + (e.g. color) in the plot. This can be controlled via the selection decorator (see \ref + selectionDecorator). + + The entire selection mechanism for plottables is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when + you wish to change the selection state programmatically. + + Using \ref setSelectable you can further specify for each plottable whether and to which + granularity it is selectable. If \a selection is not compatible with the current \ref + QCP::SelectionType set via \ref setSelectable, the resulting selection will be adjusted + accordingly (see \ref QCPDataSelection::enforceType). + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see setSelectable, selectTest +*/ +void QCPAbstractPlottable::setSelection(QCPDataSelection selection) +{ + selection.enforceType(mSelectable); + if (mSelection != selection) + { + mSelection = selection; + emit selectionChanged(selected()); + emit selectionChanged(mSelection); + } +} + +/*! + Use this method to set an own QCPSelectionDecorator (subclass) instance. This allows you to + customize the visual representation of selected data ranges further than by using the default + QCPSelectionDecorator. + + The plottable takes ownership of the \a decorator. + + The currently set decorator can be accessed via \ref selectionDecorator. +*/ +void QCPAbstractPlottable::setSelectionDecorator(QCPSelectionDecorator *decorator) +{ + if (decorator) + { + if (decorator->registerWithPlottable(this)) + { + delete mSelectionDecorator; // delete old decorator if necessary + mSelectionDecorator = decorator; + } + } else if (mSelectionDecorator) // just clear decorator + { + delete mSelectionDecorator; + mSelectionDecorator = nullptr; + } +} + +/*! + Sets whether and to which granularity this plottable can be selected. + + A selection can happen by clicking on the QCustomPlot surface (When \ref + QCustomPlot::setInteractions contains \ref QCP::iSelectPlottables), by dragging a selection rect + (When \ref QCustomPlot::setSelectionRectMode is \ref QCP::srmSelect), or programmatically by + calling \ref setSelection. + + \see setSelection, QCP::SelectionType +*/ +void QCPAbstractPlottable::setSelectable(QCP::SelectionType selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + QCPDataSelection oldSelection = mSelection; + mSelection.enforceType(mSelectable); + emit selectableChanged(mSelectable); + if (mSelection != oldSelection) + { + emit selectionChanged(selected()); + emit selectionChanged(mSelection); + } + } +} + + +/*! + Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface, + taking the orientations of the axes associated with this plottable into account (e.g. whether key + represents x or y). + + \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y. + + \see pixelsToCoords, QCPAxis::coordToPixel +*/ +void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + if (keyAxis->orientation() == Qt::Horizontal) + { + x = keyAxis->coordToPixel(key); + y = valueAxis->coordToPixel(value); + } else + { + y = keyAxis->coordToPixel(key); + x = valueAxis->coordToPixel(value); + } +} + +/*! \overload + + Transforms the given \a key and \a value to pixel coordinates and returns them in a QPointF. +*/ +const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); } + + if (keyAxis->orientation() == Qt::Horizontal) + return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value)); + else + return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key)); +} + +/*! + Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates, + taking the orientations of the axes associated with this plottable into account (e.g. whether key + represents x or y). + + \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value. + + \see coordsToPixels, QCPAxis::coordToPixel +*/ +void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + if (keyAxis->orientation() == Qt::Horizontal) + { + key = keyAxis->pixelToCoord(x); + value = valueAxis->pixelToCoord(y); + } else + { + key = keyAxis->pixelToCoord(y); + value = valueAxis->pixelToCoord(x); + } +} + +/*! \overload + + Returns the pixel input \a pixelPos as plot coordinates \a key and \a value. +*/ +void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const +{ + pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value); +} + +/*! + Rescales the key and value axes associated with this plottable to contain all displayed data, so + the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make + sure not to rescale to an illegal range i.e. a range containing different signs and/or zero. + Instead it will stay in the current sign domain and ignore all parts of the plottable that lie + outside of that domain. + + \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show + multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has + \a onlyEnlarge set to false (the default), and all subsequent set to true. + + \see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes, QCPAxis::rescale +*/ +void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const +{ + rescaleKeyAxis(onlyEnlarge); + rescaleValueAxis(onlyEnlarge); +} + +/*! + Rescales the key axis of the plottable so the whole plottable is visible. + + See \ref rescaleAxes for detailed behaviour. +*/ +void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + + QCP::SignDomain signDomain = QCP::sdBoth; + if (keyAxis->scaleType() == QCPAxis::stLogarithmic) + signDomain = (keyAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive); + + bool foundRange; + QCPRange newRange = getKeyRange(foundRange, signDomain); + if (foundRange) + { + if (onlyEnlarge) + newRange.expand(keyAxis->range()); + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (keyAxis->scaleType() == QCPAxis::stLinear) + { + newRange.lower = center-keyAxis->range().size()/2.0; + newRange.upper = center+keyAxis->range().size()/2.0; + } else // scaleType() == stLogarithmic + { + newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower); + newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower); + } + } + keyAxis->setRange(newRange); + } +} + +/*! + Rescales the value axis of the plottable so the whole plottable is visible. If \a inKeyRange is + set to true, only the data points which are in the currently visible key axis range are + considered. + + Returns true if the axis was actually scaled. This might not be the case if this plottable has an + invalid range, e.g. because it has no data points. + + See \ref rescaleAxes for detailed behaviour. +*/ +void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + QCP::SignDomain signDomain = QCP::sdBoth; + if (valueAxis->scaleType() == QCPAxis::stLogarithmic) + signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive); + + bool foundRange; + QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange()); + if (foundRange) + { + if (onlyEnlarge) + newRange.expand(valueAxis->range()); + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (valueAxis->scaleType() == QCPAxis::stLinear) + { + newRange.lower = center-valueAxis->range().size()/2.0; + newRange.upper = center+valueAxis->range().size()/2.0; + } else // scaleType() == stLogarithmic + { + newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower); + newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower); + } + } + valueAxis->setRange(newRange); + } +} + +/*! \overload + + Adds this plottable to the specified \a legend. + + Creates a QCPPlottableLegendItem which is inserted into the legend. Returns true on success, i.e. + when the legend exists and a legend item associated with this plottable isn't already in the + legend. + + If the plottable needs a more specialized representation in the legend, you can create a + corresponding subclass of \ref QCPPlottableLegendItem and add it to the legend manually instead + of calling this method. + + \see removeFromLegend, QCPLegend::addItem +*/ +bool QCPAbstractPlottable::addToLegend(QCPLegend *legend) +{ + if (!legend) + { + qDebug() << Q_FUNC_INFO << "passed legend is null"; + return false; + } + if (legend->parentPlot() != mParentPlot) + { + qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable"; + return false; + } + + if (!legend->hasItemWithPlottable(this)) + { + legend->addItem(new QCPPlottableLegendItem(legend, this)); + return true; + } else + return false; +} + +/*! \overload + + Adds this plottable to the legend of the parent QCustomPlot (\ref QCustomPlot::legend). + + \see removeFromLegend +*/ +bool QCPAbstractPlottable::addToLegend() +{ + if (!mParentPlot || !mParentPlot->legend) + return false; + else + return addToLegend(mParentPlot->legend); +} + +/*! \overload + + Removes the plottable from the specifed \a legend. This means the \ref QCPPlottableLegendItem + that is associated with this plottable is removed. + + Returns true on success, i.e. if the legend exists and a legend item associated with this + plottable was found and removed. + + \see addToLegend, QCPLegend::removeItem +*/ +bool QCPAbstractPlottable::removeFromLegend(QCPLegend *legend) const +{ + if (!legend) + { + qDebug() << Q_FUNC_INFO << "passed legend is null"; + return false; + } + + if (QCPPlottableLegendItem *lip = legend->itemWithPlottable(this)) + return legend->removeItem(lip); + else + return false; +} + +/*! \overload + + Removes the plottable from the legend of the parent QCustomPlot. + + \see addToLegend +*/ +bool QCPAbstractPlottable::removeFromLegend() const +{ + if (!mParentPlot || !mParentPlot->legend) + return false; + else + return removeFromLegend(mParentPlot->legend); +} + +/* inherits documentation from base class */ +QRect QCPAbstractPlottable::clipRect() const +{ + if (mKeyAxis && mValueAxis) + return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect(); + else + return {}; +} + +/* inherits documentation from base class */ +QCP::Interaction QCPAbstractPlottable::selectionCategory() const +{ + return QCP::iSelectPlottables; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing plottable lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \seebaseclassmethod + + \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint +*/ +void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables); +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing plottable fills. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint +*/ +void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills); +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing plottable scatter points. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint +*/ +void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters); +} + +/* inherits documentation from base class */ +void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + + if (mSelectable != QCP::stNone) + { + QCPDataSelection newSelection = details.value(); + QCPDataSelection selectionBefore = mSelection; + if (additive) + { + if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit + { + if (selected()) + setSelection(QCPDataSelection()); + else + setSelection(newSelection); + } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments + { + if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection + setSelection(mSelection-newSelection); + else + setSelection(mSelection+newSelection); + } + } else + setSelection(newSelection); + if (selectionStateChanged) + *selectionStateChanged = mSelection != selectionBefore; + } +} + +/* inherits documentation from base class */ +void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable != QCP::stNone) + { + QCPDataSelection selectionBefore = mSelection; + setSelection(QCPDataSelection()); + if (selectionStateChanged) + *selectionStateChanged = mSelection != selectionBefore; + } +} +/* end of 'src/plottable.cpp' */ + + +/* including file 'src/item.cpp' */ +/* modified 2022-11-06T12:45:56, size 49486 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemAnchor +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemAnchor + \brief An anchor of an item to which positions can be attached to. + + An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't + control anything on its item, but provides a way to tie other items via their positions to the + anchor. + + For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight. + Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can + attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by + calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the + QCPItemRect. This way the start of the line will now always follow the respective anchor location + on the rect item. + + Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an + anchor to other positions. + + To learn how to provide anchors in your own item subclasses, see the subclassing section of the + QCPAbstractItem documentation. +*/ + +/* start documentation of inline functions */ + +/*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition() + + Returns \c nullptr if this instance is merely a QCPItemAnchor, and a valid pointer of type + QCPItemPosition* if it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor). + + This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids + dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with + gcc compiler). +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if + you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as + explained in the subclassing section of the QCPAbstractItem documentation. +*/ +QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId) : + mName(name), + mParentPlot(parentPlot), + mParentItem(parentItem), + mAnchorId(anchorId) +{ +} + +QCPItemAnchor::~QCPItemAnchor() +{ + // unregister as parent at children: + foreach (QCPItemPosition *child, mChildrenX.values()) + { + if (child->parentAnchorX() == this) + child->setParentAnchorX(nullptr); // this acts back on this anchor and child removes itself from mChildrenX + } + foreach (QCPItemPosition *child, mChildrenY.values()) + { + if (child->parentAnchorY() == this) + child->setParentAnchorY(nullptr); // this acts back on this anchor and child removes itself from mChildrenY + } +} + +/*! + Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface. + + The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the + parent item, QCPItemAnchor is just an intermediary. +*/ +QPointF QCPItemAnchor::pixelPosition() const +{ + if (mParentItem) + { + if (mAnchorId > -1) + { + return mParentItem->anchorPixelPosition(mAnchorId); + } else + { + qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId; + return {}; + } + } else + { + qDebug() << Q_FUNC_INFO << "no parent item set"; + return {}; + } +} + +/*! \internal + + Adds \a pos to the childX list of this anchor, which keeps track of which children use this + anchor as parent anchor for the respective coordinate. This is necessary to notify the children + prior to destruction of the anchor. + + Note that this function does not change the parent setting in \a pos. +*/ +void QCPItemAnchor::addChildX(QCPItemPosition *pos) +{ + if (!mChildrenX.contains(pos)) + mChildrenX.insert(pos); + else + qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast(pos); +} + +/*! \internal + + Removes \a pos from the childX list of this anchor. + + Note that this function does not change the parent setting in \a pos. +*/ +void QCPItemAnchor::removeChildX(QCPItemPosition *pos) +{ + if (!mChildrenX.remove(pos)) + qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast(pos); +} + +/*! \internal + + Adds \a pos to the childY list of this anchor, which keeps track of which children use this + anchor as parent anchor for the respective coordinate. This is necessary to notify the children + prior to destruction of the anchor. + + Note that this function does not change the parent setting in \a pos. +*/ +void QCPItemAnchor::addChildY(QCPItemPosition *pos) +{ + if (!mChildrenY.contains(pos)) + mChildrenY.insert(pos); + else + qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast(pos); +} + +/*! \internal + + Removes \a pos from the childY list of this anchor. + + Note that this function does not change the parent setting in \a pos. +*/ +void QCPItemAnchor::removeChildY(QCPItemPosition *pos) +{ + if (!mChildrenY.remove(pos)) + qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast(pos); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemPosition +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemPosition + \brief Manages the position of an item. + + Every item has at least one public QCPItemPosition member pointer which provides ways to position the + item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two: + \a topLeft and \a bottomRight. + + QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type + defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel + coordinates, as plot coordinates of certain axes (\ref QCPItemPosition::setAxes), as fractions of + the axis rect (\ref QCPItemPosition::setAxisRect), etc. For more advanced plots it is also + possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref + setTypeY). This way an item could be positioned for example at a fixed pixel distance from the + top in the Y direction, while following a plot coordinate in the X direction. + + A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie + multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords) + are considered to be absolute pixels in the reference frame of the parent anchor, where (0, 0) + means directly ontop of the parent anchor. For example, You could attach the \a start position of + a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting point of the line + always be centered under the text label, no matter where the text is moved to. For more advanced + plots, it is possible to assign different parent anchors per X/Y coordinate of the position, see + \ref setParentAnchorX, \ref setParentAnchorY. This way an item could follow another item in the X + direction but stay at a fixed position in the Y direction. Or even follow item A in X, and item B + in Y. + + Note that every QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent + anchor for other positions. + + To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPosition. This + works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref + setPixelPosition transforms the coordinates appropriately, to make the position appear at the specified + pixel values. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPItemPosition::PositionType *QCPItemPosition::type() const + + Returns the current position type. + + If different types were set for X and Y (\ref setTypeX, \ref setTypeY), this method returns the + type of the X coordinate. In that case rather use \a typeX() and \a typeY(). + + \see setType +*/ + +/*! \fn QCPItemAnchor *QCPItemPosition::parentAnchor() const + + Returns the current parent anchor. + + If different parent anchors were set for X and Y (\ref setParentAnchorX, \ref setParentAnchorY), + this method returns the parent anchor of the Y coordinate. In that case rather use \a + parentAnchorX() and \a parentAnchorY(). + + \see setParentAnchor +*/ + +/* end documentation of inline functions */ + +/*! + Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if + you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as + explained in the subclassing section of the QCPAbstractItem documentation. +*/ +QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name) : + QCPItemAnchor(parentPlot, parentItem, name), + mPositionTypeX(ptAbsolute), + mPositionTypeY(ptAbsolute), + mKey(0), + mValue(0), + mParentAnchorX(nullptr), + mParentAnchorY(nullptr) +{ +} + +QCPItemPosition::~QCPItemPosition() +{ + // unregister as parent at children: + // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then + // the setParentAnchor(0) call the correct QCPItemPosition::pixelPosition function instead of QCPItemAnchor::pixelPosition + foreach (QCPItemPosition *child, mChildrenX.values()) + { + if (child->parentAnchorX() == this) + child->setParentAnchorX(nullptr); // this acts back on this anchor and child removes itself from mChildrenX + } + foreach (QCPItemPosition *child, mChildrenY.values()) + { + if (child->parentAnchorY() == this) + child->setParentAnchorY(nullptr); // this acts back on this anchor and child removes itself from mChildrenY + } + // unregister as child in parent: + if (mParentAnchorX) + mParentAnchorX->removeChildX(this); + if (mParentAnchorY) + mParentAnchorY->removeChildY(this); +} + +/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */ +QCPAxisRect *QCPItemPosition::axisRect() const +{ + return mAxisRect.data(); +} + +/*! + Sets the type of the position. The type defines how the coordinates passed to \ref setCoords + should be handled and how the QCPItemPosition should behave in the plot. + + The possible values for \a type can be separated in two main categories: + + \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords + and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes. + By default, the QCustomPlot's x- and yAxis are used. + + \li The position is fixed on the QCustomPlot surface, i.e. independent of axis ranges. This + corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref + ptAxisRectRatio. They differ only in the way the absolute position is described, see the + documentation of \ref PositionType for details. For \ref ptAxisRectRatio, note that you can specify + the axis rect with \ref setAxisRect. By default this is set to the main axis rect. + + Note that the position type \ref ptPlotCoords is only available (and sensible) when the position + has no parent anchor (\ref setParentAnchor). + + If the type is changed, the apparent pixel position on the plot is preserved. This means + the coordinates as retrieved with coords() and set with \ref setCoords may change in the process. + + This method sets the type for both X and Y directions. It is also possible to set different types + for X and Y, see \ref setTypeX, \ref setTypeY. +*/ +void QCPItemPosition::setType(QCPItemPosition::PositionType type) +{ + setTypeX(type); + setTypeY(type); +} + +/*! + This method sets the position type of the X coordinate to \a type. + + For a detailed description of what a position type is, see the documentation of \ref setType. + + \see setType, setTypeY +*/ +void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type) +{ + if (mPositionTypeX != type) + { + // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect + // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning. + bool retainPixelPosition = true; + if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis)) + retainPixelPosition = false; + if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect)) + retainPixelPosition = false; + + QPointF pixel; + if (retainPixelPosition) + pixel = pixelPosition(); + + mPositionTypeX = type; + + if (retainPixelPosition) + setPixelPosition(pixel); + } +} + +/*! + This method sets the position type of the Y coordinate to \a type. + + For a detailed description of what a position type is, see the documentation of \ref setType. + + \see setType, setTypeX +*/ +void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type) +{ + if (mPositionTypeY != type) + { + // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect + // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning. + bool retainPixelPosition = true; + if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis)) + retainPixelPosition = false; + if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect)) + retainPixelPosition = false; + + QPointF pixel; + if (retainPixelPosition) + pixel = pixelPosition(); + + mPositionTypeY = type; + + if (retainPixelPosition) + setPixelPosition(pixel); + } +} + +/*! + Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now + follow any position changes of the anchor. The local coordinate system of positions with a parent + anchor always is absolute pixels, with (0, 0) being exactly on top of the parent anchor. (Hence + the type shouldn't be set to \ref ptPlotCoords for positions with parent anchors.) + + if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved + during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position + will be exactly on top of the parent anchor. + + To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to \c nullptr. + + If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is + set to \ref ptAbsolute, to keep the position in a valid state. + + This method sets the parent anchor for both X and Y directions. It is also possible to set + different parents for X and Y, see \ref setParentAnchorX, \ref setParentAnchorY. +*/ +bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition) +{ + bool successX = setParentAnchorX(parentAnchor, keepPixelPosition); + bool successY = setParentAnchorY(parentAnchor, keepPixelPosition); + return successX && successY; +} + +/*! + This method sets the parent anchor of the X coordinate to \a parentAnchor. + + For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor. + + \see setParentAnchor, setParentAnchorY +*/ +bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition) +{ + // make sure self is not assigned as parent: + if (parentAnchor == this) + { + qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast(parentAnchor); + return false; + } + // make sure no recursive parent-child-relationships are created: + QCPItemAnchor *currentParent = parentAnchor; + while (currentParent) + { + if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition()) + { + // is a QCPItemPosition, might have further parent, so keep iterating + if (currentParentPos == this) + { + qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast(parentAnchor); + return false; + } + currentParent = currentParentPos->parentAnchorX(); + } else + { + // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the + // same, to prevent a position being child of an anchor which itself depends on the position, + // because they're both on the same item: + if (currentParent->mParentItem == mParentItem) + { + qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast(parentAnchor); + return false; + } + break; + } + } + + // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute: + if (!mParentAnchorX && mPositionTypeX == ptPlotCoords) + setTypeX(ptAbsolute); + + // save pixel position: + QPointF pixelP; + if (keepPixelPosition) + pixelP = pixelPosition(); + // unregister at current parent anchor: + if (mParentAnchorX) + mParentAnchorX->removeChildX(this); + // register at new parent anchor: + if (parentAnchor) + parentAnchor->addChildX(this); + mParentAnchorX = parentAnchor; + // restore pixel position under new parent: + if (keepPixelPosition) + setPixelPosition(pixelP); + else + setCoords(0, coords().y()); + return true; +} + +/*! + This method sets the parent anchor of the Y coordinate to \a parentAnchor. + + For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor. + + \see setParentAnchor, setParentAnchorX +*/ +bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition) +{ + // make sure self is not assigned as parent: + if (parentAnchor == this) + { + qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast(parentAnchor); + return false; + } + // make sure no recursive parent-child-relationships are created: + QCPItemAnchor *currentParent = parentAnchor; + while (currentParent) + { + if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition()) + { + // is a QCPItemPosition, might have further parent, so keep iterating + if (currentParentPos == this) + { + qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast(parentAnchor); + return false; + } + currentParent = currentParentPos->parentAnchorY(); + } else + { + // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the + // same, to prevent a position being child of an anchor which itself depends on the position, + // because they're both on the same item: + if (currentParent->mParentItem == mParentItem) + { + qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast(parentAnchor); + return false; + } + break; + } + } + + // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute: + if (!mParentAnchorY && mPositionTypeY == ptPlotCoords) + setTypeY(ptAbsolute); + + // save pixel position: + QPointF pixelP; + if (keepPixelPosition) + pixelP = pixelPosition(); + // unregister at current parent anchor: + if (mParentAnchorY) + mParentAnchorY->removeChildY(this); + // register at new parent anchor: + if (parentAnchor) + parentAnchor->addChildY(this); + mParentAnchorY = parentAnchor; + // restore pixel position under new parent: + if (keepPixelPosition) + setPixelPosition(pixelP); + else + setCoords(coords().x(), 0); + return true; +} + +/*! + Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type + (\ref setType, \ref setTypeX, \ref setTypeY). + + For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position + on the QCustomPlot surface. In that case the origin (0, 0) is in the top left corner of the + QCustomPlot viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the + plot coordinate system defined by the axes set by \ref setAxes. By default those are the + QCustomPlot's xAxis and yAxis. See the documentation of \ref setType for other available + coordinate types and their meaning. + + If different types were configured for X and Y (\ref setTypeX, \ref setTypeY), \a key and \a + value must also be provided in the different coordinate systems. Here, the X type refers to \a + key, and the Y type refers to \a value. + + \see setPixelPosition +*/ +void QCPItemPosition::setCoords(double key, double value) +{ + mKey = key; + mValue = value; +} + +/*! \overload + + Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the + meaning of \a value of the \ref setCoords(double key, double value) method. +*/ +void QCPItemPosition::setCoords(const QPointF &pos) +{ + setCoords(pos.x(), pos.y()); +} + +/*! + Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It + includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor). + + \see setPixelPosition +*/ +QPointF QCPItemPosition::pixelPosition() const +{ + QPointF result; + + // determine X: + switch (mPositionTypeX) + { + case ptAbsolute: + { + result.rx() = mKey; + if (mParentAnchorX) + result.rx() += mParentAnchorX->pixelPosition().x(); + break; + } + case ptViewportRatio: + { + result.rx() = mKey*mParentPlot->viewport().width(); + if (mParentAnchorX) + result.rx() += mParentAnchorX->pixelPosition().x(); + else + result.rx() += mParentPlot->viewport().left(); + break; + } + case ptAxisRectRatio: + { + if (mAxisRect) + { + result.rx() = mKey*mAxisRect.data()->width(); + if (mParentAnchorX) + result.rx() += mParentAnchorX->pixelPosition().x(); + else + result.rx() += mAxisRect.data()->left(); + } else + qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined"; + break; + } + case ptPlotCoords: + { + if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal) + result.rx() = mKeyAxis.data()->coordToPixel(mKey); + else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal) + result.rx() = mValueAxis.data()->coordToPixel(mValue); + else + qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined"; + break; + } + } + + // determine Y: + switch (mPositionTypeY) + { + case ptAbsolute: + { + result.ry() = mValue; + if (mParentAnchorY) + result.ry() += mParentAnchorY->pixelPosition().y(); + break; + } + case ptViewportRatio: + { + result.ry() = mValue*mParentPlot->viewport().height(); + if (mParentAnchorY) + result.ry() += mParentAnchorY->pixelPosition().y(); + else + result.ry() += mParentPlot->viewport().top(); + break; + } + case ptAxisRectRatio: + { + if (mAxisRect) + { + result.ry() = mValue*mAxisRect.data()->height(); + if (mParentAnchorY) + result.ry() += mParentAnchorY->pixelPosition().y(); + else + result.ry() += mAxisRect.data()->top(); + } else + qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined"; + break; + } + case ptPlotCoords: + { + if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical) + result.ry() = mKeyAxis.data()->coordToPixel(mKey); + else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical) + result.ry() = mValueAxis.data()->coordToPixel(mValue); + else + qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined"; + break; + } + } + + return result; +} + +/*! + When \ref setType is \ref ptPlotCoords, this function may be used to specify the axes the + coordinates set with \ref setCoords relate to. By default they are set to the initial xAxis and + yAxis of the QCustomPlot. +*/ +void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis) +{ + mKeyAxis = keyAxis; + mValueAxis = valueAxis; +} + +/*! + When \ref setType is \ref ptAxisRectRatio, this function may be used to specify the axis rect the + coordinates set with \ref setCoords relate to. By default this is set to the main axis rect of + the QCustomPlot. +*/ +void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect) +{ + mAxisRect = axisRect; +} + +/*! + Sets the apparent pixel position. This works no matter what type (\ref setType) this + QCPItemPosition is or what parent-child situation it is in, as coordinates are transformed + appropriately, to make the position finally appear at the specified pixel values. + + Only if the type is \ref ptAbsolute and no parent anchor is set, this function's effect is + identical to that of \ref setCoords. + + \see pixelPosition, setCoords +*/ +void QCPItemPosition::setPixelPosition(const QPointF &pixelPosition) +{ + double x = pixelPosition.x(); + double y = pixelPosition.y(); + + switch (mPositionTypeX) + { + case ptAbsolute: + { + if (mParentAnchorX) + x -= mParentAnchorX->pixelPosition().x(); + break; + } + case ptViewportRatio: + { + if (mParentAnchorX) + x -= mParentAnchorX->pixelPosition().x(); + else + x -= mParentPlot->viewport().left(); + x /= double(mParentPlot->viewport().width()); + break; + } + case ptAxisRectRatio: + { + if (mAxisRect) + { + if (mParentAnchorX) + x -= mParentAnchorX->pixelPosition().x(); + else + x -= mAxisRect.data()->left(); + x /= double(mAxisRect.data()->width()); + } else + qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined"; + break; + } + case ptPlotCoords: + { + if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal) + x = mKeyAxis.data()->pixelToCoord(x); + else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal) + y = mValueAxis.data()->pixelToCoord(x); + else + qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined"; + break; + } + } + + switch (mPositionTypeY) + { + case ptAbsolute: + { + if (mParentAnchorY) + y -= mParentAnchorY->pixelPosition().y(); + break; + } + case ptViewportRatio: + { + if (mParentAnchorY) + y -= mParentAnchorY->pixelPosition().y(); + else + y -= mParentPlot->viewport().top(); + y /= double(mParentPlot->viewport().height()); + break; + } + case ptAxisRectRatio: + { + if (mAxisRect) + { + if (mParentAnchorY) + y -= mParentAnchorY->pixelPosition().y(); + else + y -= mAxisRect.data()->top(); + y /= double(mAxisRect.data()->height()); + } else + qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined"; + break; + } + case ptPlotCoords: + { + if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical) + x = mKeyAxis.data()->pixelToCoord(y); + else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical) + y = mValueAxis.data()->pixelToCoord(y); + else + qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined"; + break; + } + } + + setCoords(x, y); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractItem +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractItem + \brief The abstract base class for all items in a plot. + + In QCustomPlot, items are supplemental graphical elements that are neither plottables + (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus + plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each + specific item has at least one QCPItemPosition member which controls the positioning. Some items + are defined by more than one coordinate and thus have two or more QCPItemPosition members (For + example, QCPItemRect has \a topLeft and \a bottomRight). + + This abstract base class defines a very basic interface like visibility and clipping. Since this + class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass + yourself to create new items. + + The built-in items are: + + + + + + + + + + +
QCPItemLineA line defined by a start and an end point. May have different ending styles on each side (e.g. arrows).
QCPItemStraightLineA straight line defined by a start and a direction point. Unlike QCPItemLine, the straight line is infinitely long and has no endings.
QCPItemCurveA curve defined by start, end and two intermediate control points. May have different ending styles on each side (e.g. arrows).
QCPItemRectA rectangle
QCPItemEllipseAn ellipse
QCPItemPixmapAn arbitrary pixmap
QCPItemTextA text label
QCPItemBracketA bracket which may be used to reference/highlight certain parts in the plot.
QCPItemTracerAn item that can be attached to a QCPGraph and sticks to its data points, given a key coordinate.
+ + \section items-clipping Clipping + + Items are by default clipped to the main axis rect (they are only visible inside the axis rect). + To make an item visible outside that axis rect, disable clipping via \ref setClipToAxisRect + "setClipToAxisRect(false)". + + On the other hand if you want the item to be clipped to a different axis rect, specify it via + \ref setClipAxisRect. This clipAxisRect property of an item is only used for clipping behaviour, and + in principle is independent of the coordinate axes the item might be tied to via its position + members (\ref QCPItemPosition::setAxes). However, it is common that the axis rect for clipping + also contains the axes used for the item positions. + + \section items-using Using items + + First you instantiate the item you want to use and add it to the plot: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1 + by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just + set the plot coordinates where the line should start/end: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2 + If we don't want the line to be positioned in plot coordinates but a different coordinate system, + e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3 + Then we can set the coordinates, this time in pixels: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4 + and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-5 + + For more advanced plots, it is even possible to set different types and parent anchors per X/Y + coordinate of an item position, using for example \ref QCPItemPosition::setTypeX or \ref + QCPItemPosition::setParentAnchorX. For details, see the documentation of \ref QCPItemPosition. + + \section items-subclassing Creating own items + + To create an own item, you implement a subclass of QCPAbstractItem. These are the pure + virtual functions, you must implement: + \li \ref selectTest + \li \ref draw + + See the documentation of those functions for what they need to do. + + \subsection items-positioning Allowing the item to be positioned + + As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall + have only one point as its position (as opposed to two like a rect or multiple like a polygon). You then add + a public member of type QCPItemPosition like so: + + \code QCPItemPosition * const myPosition;\endcode + + the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition + instance it points to, can be modified, of course). + The initialization of this pointer is made easy with the \ref createPosition function. Just assign + the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition + takes a string which is the name of the position, typically this is identical to the variable name. + For example, the constructor of QCPItemExample could look like this: + + \code + QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + myPosition(createPosition("myPosition")) + { + // other constructor code + } + \endcode + + \subsection items-drawing The draw function + + To give your item a visual representation, reimplement the \ref draw function and use the passed + QCPPainter to draw the item. You can retrieve the item position in pixel coordinates from the + position member(s) via \ref QCPItemPosition::pixelPosition. + + To optimize performance you should calculate a bounding rect first (don't forget to take the pen + width into account), check whether it intersects the \ref clipRect, and only draw the item at all + if this is the case. + + \subsection items-selection The selectTest function + + Your implementation of the \ref selectTest function may use the helpers \ref + QCPVector2D::distanceSquaredToLine and \ref rectDistance. With these, the implementation of the + selection test becomes significantly simpler for most items. See the documentation of \ref + selectTest for what the function parameters mean and what the function should return. + + \subsection anchors Providing anchors + + Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public + member, e.g. + + \code QCPItemAnchor * const bottom;\endcode + + and create it in the constructor with the \ref createAnchor function, assigning it a name and an + anchor id (an integer enumerating all anchors on the item, you may create an own enum for this). + Since anchors can be placed anywhere, relative to the item's position(s), your item needs to + provide the position of every anchor with the reimplementation of the \ref anchorPixelPosition(int + anchorId) function. + + In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel + position when anything attached to the anchor needs to know the coordinates. +*/ + +/* start of documentation of inline functions */ + +/*! \fn QList QCPAbstractItem::positions() const + + Returns all positions of the item in a list. + + \see anchors, position +*/ + +/*! \fn QList QCPAbstractItem::anchors() const + + Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always + also an anchor, the list will also contain the positions of this item. + + \see positions, anchor +*/ + +/* end of documentation of inline functions */ +/* start documentation of pure virtual functions */ + +/*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0 + \internal + + Draws this item with the provided \a painter. + + The cliprect of the provided painter is set to the rect returned by \ref clipRect before this + function is called. The clipRect depends on the clipping settings defined by \ref + setClipToAxisRect and \ref setClipAxisRect. +*/ + +/* end documentation of pure virtual functions */ +/* start documentation of signals */ + +/*! \fn void QCPAbstractItem::selectionChanged(bool selected) + This signal is emitted when the selection state of this item has changed, either by user interaction + or by a direct call to \ref setSelected. +*/ + +/* end documentation of signals */ + +/*! + Base class constructor which initializes base class members. +*/ +QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) : + QCPLayerable(parentPlot), + mClipToAxisRect(false), + mSelectable(true), + mSelected(false) +{ + parentPlot->registerItem(this); + + QList rects = parentPlot->axisRects(); + if (!rects.isEmpty()) + { + setClipToAxisRect(true); + setClipAxisRect(rects.first()); + } +} + +QCPAbstractItem::~QCPAbstractItem() +{ + // don't delete mPositions because every position is also an anchor and thus in mAnchors + qDeleteAll(mAnchors); +} + +/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */ +QCPAxisRect *QCPAbstractItem::clipAxisRect() const +{ + return mClipAxisRect.data(); +} + +/*! + Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the + entire QCustomPlot. The axis rect can be set with \ref setClipAxisRect. + + \see setClipAxisRect +*/ +void QCPAbstractItem::setClipToAxisRect(bool clip) +{ + mClipToAxisRect = clip; + if (mClipToAxisRect) + setParentLayerable(mClipAxisRect.data()); +} + +/*! + Sets the clip axis rect. It defines the rect that will be used to clip the item when \ref + setClipToAxisRect is set to true. + + \see setClipToAxisRect +*/ +void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect) +{ + mClipAxisRect = rect; + if (mClipToAxisRect) + setParentLayerable(mClipAxisRect.data()); +} + +/*! + Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.) + + However, even when \a selectable was set to false, it is possible to set the selection manually, + by calling \ref setSelected. + + \see QCustomPlot::setInteractions, setSelected +*/ +void QCPAbstractItem::setSelectable(bool selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + emit selectableChanged(mSelectable); + } +} + +/*! + Sets whether this item is selected or not. When selected, it might use a different visual + appearance (e.g. pen and brush), this depends on the specific item though. + + The entire selection mechanism for items is handled automatically when \ref + QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this + function when you wish to change the selection state manually. + + This function can change the selection state even when \ref setSelectable was set to false. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see setSelectable, selectTest +*/ +void QCPAbstractItem::setSelected(bool selected) +{ + if (mSelected != selected) + { + mSelected = selected; + emit selectionChanged(mSelected); + } +} + +/*! + Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by + that name, returns \c nullptr. + + This function provides an alternative way to access item positions. Normally, you access + positions direcly by their member pointers (which typically have the same variable name as \a + name). + + \see positions, anchor +*/ +QCPItemPosition *QCPAbstractItem::position(const QString &name) const +{ + foreach (QCPItemPosition *position, mPositions) + { + if (position->name() == name) + return position; + } + qDebug() << Q_FUNC_INFO << "position with name not found:" << name; + return nullptr; +} + +/*! + Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by + that name, returns \c nullptr. + + This function provides an alternative way to access item anchors. Normally, you access + anchors direcly by their member pointers (which typically have the same variable name as \a + name). + + \see anchors, position +*/ +QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const +{ + foreach (QCPItemAnchor *anchor, mAnchors) + { + if (anchor->name() == name) + return anchor; + } + qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name; + return nullptr; +} + +/*! + Returns whether this item has an anchor with the specified \a name. + + Note that you can check for positions with this function, too. This is because every position is + also an anchor (QCPItemPosition inherits from QCPItemAnchor). + + \see anchor, position +*/ +bool QCPAbstractItem::hasAnchor(const QString &name) const +{ + foreach (QCPItemAnchor *anchor, mAnchors) + { + if (anchor->name() == name) + return true; + } + return false; +} + +/*! \internal + + Returns the rect the visual representation of this item is clipped to. This depends on the + current setting of \ref setClipToAxisRect as well as the axis rect set with \ref setClipAxisRect. + + If the item is not clipped to an axis rect, QCustomPlot's viewport rect is returned. + + \see draw +*/ +QRect QCPAbstractItem::clipRect() const +{ + if (mClipToAxisRect && mClipAxisRect) + return mClipAxisRect.data()->rect(); + else + return mParentPlot->viewport(); +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing item lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased +*/ +void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeItems); +} + +/*! \internal + + A convenience function which returns the selectTest value for a specified \a rect and a specified + click position \a pos. \a filledRect defines whether a click inside the rect should also be + considered a hit or whether only the rect border is sensitive to hits. + + This function may be used to help with the implementation of the \ref selectTest function for + specific items. + + For example, if your item consists of four rects, call this function four times, once for each + rect, in your \ref selectTest reimplementation. Finally, return the minimum (non -1) of all four + returned values. +*/ +double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const +{ + double result = -1; + + // distance to border: + const QList lines = QList() << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight()) + << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight()); + const QCPVector2D posVec(pos); + double minDistSqr = (std::numeric_limits::max)(); + foreach (const QLineF &line, lines) + { + double distSqr = posVec.distanceSquaredToLine(line.p1(), line.p2()); + if (distSqr < minDistSqr) + minDistSqr = distSqr; + } + result = qSqrt(minDistSqr); + + // filled rect, allow click inside to count as hit: + if (filledRect && result > mParentPlot->selectionTolerance()*0.99) + { + if (rect.contains(pos)) + result = mParentPlot->selectionTolerance()*0.99; + } + return result; +} + +/*! \internal + + Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in + item subclasses if they want to provide anchors (QCPItemAnchor). + + For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor + ids and returns the respective pixel points of the specified anchor. + + \see createAnchor +*/ +QPointF QCPAbstractItem::anchorPixelPosition(int anchorId) const +{ + qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId; + return {}; +} + +/*! \internal + + Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified + \a name must be a unique string that is usually identical to the variable name of the position + member (This is needed to provide the name-based \ref position access to positions). + + Don't delete positions created by this function manually, as the item will take care of it. + + Use this function in the constructor (initialization list) of the specific item subclass to + create each position member. Don't create QCPItemPositions with \b new yourself, because they + won't be registered with the item properly. + + \see createAnchor +*/ +QCPItemPosition *QCPAbstractItem::createPosition(const QString &name) +{ + if (hasAnchor(name)) + qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name; + QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name); + mPositions.append(newPosition); + mAnchors.append(newPosition); // every position is also an anchor + newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis); + newPosition->setType(QCPItemPosition::ptPlotCoords); + if (mParentPlot->axisRect()) + newPosition->setAxisRect(mParentPlot->axisRect()); + newPosition->setCoords(0, 0); + return newPosition; +} + +/*! \internal + + Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified + \a name must be a unique string that is usually identical to the variable name of the anchor + member (This is needed to provide the name based \ref anchor access to anchors). + + The \a anchorId must be a number identifying the created anchor. It is recommended to create an + enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor + to identify itself when it calls QCPAbstractItem::anchorPixelPosition. That function then returns + the correct pixel coordinates for the passed anchor id. + + Don't delete anchors created by this function manually, as the item will take care of it. + + Use this function in the constructor (initialization list) of the specific item subclass to + create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they + won't be registered with the item properly. + + \see createPosition +*/ +QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId) +{ + if (hasAnchor(name)) + qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name; + QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId); + mAnchors.append(newAnchor); + return newAnchor; +} + +/* inherits documentation from base class */ +void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(details) + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(additive ? !mSelected : true); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPAbstractItem::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(false); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +QCP::Interaction QCPAbstractItem::selectionCategory() const +{ + return QCP::iSelectItems; +} +/* end of 'src/item.cpp' */ + + +/* including file 'src/core.cpp' */ +/* modified 2022-11-06T12:45:56, size 127625 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCustomPlot +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCustomPlot + + \brief The central class of the library. This is the QWidget which displays the plot and + interacts with the user. + + For tutorials on how to use QCustomPlot, see the website\n + https://www.qcustomplot.com/ +*/ + +/* start of documentation of inline functions */ + +/*! \fn QCPSelectionRect *QCustomPlot::selectionRect() const + + Allows access to the currently used QCPSelectionRect instance (or subclass thereof), that is used + to handle and draw selection rect interactions (see \ref setSelectionRectMode). + + \see setSelectionRect +*/ + +/*! \fn QCPLayoutGrid *QCustomPlot::plotLayout() const + + Returns the top level layout of this QCustomPlot instance. It is a \ref QCPLayoutGrid, initially containing just + one cell with the main QCPAxisRect inside. +*/ + +/* end of documentation of inline functions */ +/* start of documentation of signals */ + +/*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse double click event. +*/ + +/*! \fn void QCustomPlot::mousePress(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse press event. + + It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot + connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref + QCPAxisRect::setRangeDragAxes. +*/ + +/*! \fn void QCustomPlot::mouseMove(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse move event. + + It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot + connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref + QCPAxisRect::setRangeDragAxes. + + \warning It is discouraged to change the drag-axes with \ref QCPAxisRect::setRangeDragAxes here, + because the dragging starting point was saved the moment the mouse was pressed. Thus it only has + a meaning for the range drag axes that were set at that moment. If you want to change the drag + axes, consider doing this in the \ref mousePress signal instead. +*/ + +/*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse release event. + + It is emitted before QCustomPlot handles any other mechanisms like object selection. So a + slot connected to this signal can still influence the behaviour e.g. with \ref setInteractions or + \ref QCPAbstractPlottable::setSelectable. +*/ + +/*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event) + + This signal is emitted when the QCustomPlot receives a mouse wheel event. + + It is emitted before QCustomPlot handles any other mechanisms like range zooming. So a slot + connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeZoom, \ref + QCPAxisRect::setRangeZoomAxes or \ref QCPAxisRect::setRangeZoomFactor. +*/ + +/*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event) + + This signal is emitted when a plottable is clicked. + + \a event is the mouse event that caused the click and \a plottable is the plottable that received + the click. The parameter \a dataIndex indicates the data point that was closest to the click + position. + + \see plottableDoubleClick +*/ + +/*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event) + + This signal is emitted when a plottable is double clicked. + + \a event is the mouse event that caused the click and \a plottable is the plottable that received + the click. The parameter \a dataIndex indicates the data point that was closest to the click + position. + + \see plottableClick +*/ + +/*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event) + + This signal is emitted when an item is clicked. + + \a event is the mouse event that caused the click and \a item is the item that received the + click. + + \see itemDoubleClick +*/ + +/*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event) + + This signal is emitted when an item is double clicked. + + \a event is the mouse event that caused the click and \a item is the item that received the + click. + + \see itemClick +*/ + +/*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event) + + This signal is emitted when an axis is clicked. + + \a event is the mouse event that caused the click, \a axis is the axis that received the click and + \a part indicates the part of the axis that was clicked. + + \see axisDoubleClick +*/ + +/*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event) + + This signal is emitted when an axis is double clicked. + + \a event is the mouse event that caused the click, \a axis is the axis that received the click and + \a part indicates the part of the axis that was clicked. + + \see axisClick +*/ + +/*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event) + + This signal is emitted when a legend (item) is clicked. + + \a event is the mouse event that caused the click, \a legend is the legend that received the + click and \a item is the legend item that received the click. If only the legend and no item is + clicked, \a item is \c nullptr. This happens for a click inside the legend padding or the space + between two items. + + \see legendDoubleClick +*/ + +/*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event) + + This signal is emitted when a legend (item) is double clicked. + + \a event is the mouse event that caused the click, \a legend is the legend that received the + click and \a item is the legend item that received the click. If only the legend and no item is + clicked, \a item is \c nullptr. This happens for a click inside the legend padding or the space + between two items. + + \see legendClick +*/ + +/*! \fn void QCustomPlot::selectionChangedByUser() + + This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by + clicking. It is not emitted when the selection state of an object has changed programmatically by + a direct call to setSelected()/setSelection() on an object or by calling \ref + deselectAll. + + In addition to this signal, selectable objects also provide individual signals, for example \ref + QCPAxis::selectionChanged or \ref QCPAbstractPlottable::selectionChanged. Note that those signals + are emitted even if the selection state is changed programmatically. + + See the documentation of \ref setInteractions for details about the selection mechanism. + + \see selectedPlottables, selectedGraphs, selectedItems, selectedAxes, selectedLegends +*/ + +/*! \fn void QCustomPlot::beforeReplot() + + This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref + replot). + + It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them + replot synchronously, it won't cause an infinite recursion. + + \see replot, afterReplot, afterLayout +*/ + +/*! \fn void QCustomPlot::afterLayout() + + This signal is emitted immediately after the layout step has been completed, which occurs right + before drawing the plot. This is typically during a call to \ref replot, and in such cases this + signal is emitted in between the signals \ref beforeReplot and \ref afterReplot. Unlike those + signals however, this signal is also emitted during off-screen painting, such as when calling + \ref toPixmap or \ref savePdf. + + The layout step queries all layouts and layout elements in the plot for their proposed size and + arranges the objects accordingly as preparation for the subsequent drawing step. Through this + signal, you have the opportunity to update certain things in your plot that depend crucially on + the exact dimensions/positioning of layout elements such as axes and axis rects. + + \warning However, changing any parameters of this QCustomPlot instance which would normally + affect the layouting (e.g. axis range order of magnitudes, tick label sizes, etc.) will not issue + a second run of the layout step. It will propagate directly to the draw step and may cause + graphical inconsistencies such as overlapping objects, if sizes or positions have changed. + + \see updateLayout, beforeReplot, afterReplot +*/ + +/*! \fn void QCustomPlot::afterReplot() + + This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref + replot). + + It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them + replot synchronously, it won't cause an infinite recursion. + + \see replot, beforeReplot, afterLayout +*/ + +/* end of documentation of signals */ +/* start of documentation of public members */ + +/*! \var QCPAxis *QCustomPlot::xAxis + + A pointer to the primary x Axis (bottom) of the main axis rect of the plot. + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref + QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the + default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointers become \c nullptr. + + If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding + axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to + the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend + is added after the main legend was removed before. +*/ + +/*! \var QCPAxis *QCustomPlot::yAxis + + A pointer to the primary y Axis (left) of the main axis rect of the plot. + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref + QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the + default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointers become \c nullptr. + + If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding + axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to + the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend + is added after the main legend was removed before. +*/ + +/*! \var QCPAxis *QCustomPlot::xAxis2 + + A pointer to the secondary x Axis (top) of the main axis rect of the plot. Secondary axes are + invisible by default. Use QCPAxis::setVisible to change this (or use \ref + QCPAxisRect::setupFullAxesBox). + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref + QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the + default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointers become \c nullptr. + + If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding + axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to + the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend + is added after the main legend was removed before. +*/ + +/*! \var QCPAxis *QCustomPlot::yAxis2 + + A pointer to the secondary y Axis (right) of the main axis rect of the plot. Secondary axes are + invisible by default. Use QCPAxis::setVisible to change this (or use \ref + QCPAxisRect::setupFullAxesBox). + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref + QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the + default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointers become \c nullptr. + + If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding + axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to + the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend + is added after the main legend was removed before. +*/ + +/*! \var QCPLegend *QCustomPlot::legend + + A pointer to the default legend of the main axis rect. The legend is invisible by default. Use + QCPLegend::setVisible to change this. + + QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref + yAxis2) and the \ref legend. They make it very easy working with plots that only have a single + axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the + layout system\endlink to add multiple legends to the plot, use the layout system interface to + access the new legend. For example, legends can be placed inside an axis rect's \ref + QCPAxisRect::insetLayout "inset layout", and must then also be accessed via the inset layout. If + the default legend is removed due to manipulation of the layout system (e.g. by removing the main + axis rect), the corresponding pointer becomes \c nullptr. + + If an axis convenience pointer is currently \c nullptr and a new axis rect or a corresponding + axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to + the according new axes. Similarly the \ref legend convenience pointer will be reset if a legend + is added after the main legend was removed before. +*/ + +/* end of documentation of public members */ + +/*! + Constructs a QCustomPlot and sets reasonable default values. +*/ +QCustomPlot::QCustomPlot(QWidget *parent) : + QWidget(parent), + xAxis(nullptr), + yAxis(nullptr), + xAxis2(nullptr), + yAxis2(nullptr), + legend(nullptr), + mBufferDevicePixelRatio(1.0), // will be adapted to true value below + mPlotLayout(nullptr), + mAutoAddPlottableToLegend(true), + mAntialiasedElements(QCP::aeNone), + mNotAntialiasedElements(QCP::aeNone), + mInteractions(QCP::iNone), + mSelectionTolerance(8), + mNoAntialiasingOnDrag(false), + mBackgroundBrush(Qt::white, Qt::SolidPattern), + mBackgroundScaled(true), + mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding), + mCurrentLayer(nullptr), + mPlottingHints(QCP::phCacheLabels|QCP::phImmediateRefresh), + mMultiSelectModifier(Qt::ControlModifier), + mSelectionRectMode(QCP::srmNone), + mSelectionRect(nullptr), + mOpenGl(false), + mMouseHasMoved(false), + mMouseEventLayerable(nullptr), + mMouseSignalLayerable(nullptr), + mReplotting(false), + mReplotQueued(false), + mReplotTime(0), + mReplotTimeAverage(0), + mOpenGlMultisamples(16), + mOpenGlAntialiasedElementsBackup(QCP::aeNone), + mOpenGlCacheLabelsBackup(true) +{ + setAttribute(Qt::WA_NoMousePropagation); + setFocusPolicy(Qt::ClickFocus); + setMouseTracking(true); + QLocale currentLocale = locale(); + currentLocale.setNumberOptions(QLocale::OmitGroupSeparator); + setLocale(currentLocale); +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED +# ifdef QCP_DEVICEPIXELRATIO_FLOAT + setBufferDevicePixelRatio(QWidget::devicePixelRatioF()); +# else + setBufferDevicePixelRatio(QWidget::devicePixelRatio()); +# endif +#endif + + mOpenGlAntialiasedElementsBackup = mAntialiasedElements; + mOpenGlCacheLabelsBackup = mPlottingHints.testFlag(QCP::phCacheLabels); + // create initial layers: + mLayers.append(new QCPLayer(this, QLatin1String("background"))); + mLayers.append(new QCPLayer(this, QLatin1String("grid"))); + mLayers.append(new QCPLayer(this, QLatin1String("main"))); + mLayers.append(new QCPLayer(this, QLatin1String("axes"))); + mLayers.append(new QCPLayer(this, QLatin1String("legend"))); + mLayers.append(new QCPLayer(this, QLatin1String("overlay"))); + updateLayerIndices(); + setCurrentLayer(QLatin1String("main")); + layer(QLatin1String("overlay"))->setMode(QCPLayer::lmBuffered); + + // create initial layout, axis rect and legend: + mPlotLayout = new QCPLayoutGrid; + mPlotLayout->initializeParentPlot(this); + mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry + mPlotLayout->setLayer(QLatin1String("main")); + QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true); + mPlotLayout->addElement(0, 0, defaultAxisRect); + xAxis = defaultAxisRect->axis(QCPAxis::atBottom); + yAxis = defaultAxisRect->axis(QCPAxis::atLeft); + xAxis2 = defaultAxisRect->axis(QCPAxis::atTop); + yAxis2 = defaultAxisRect->axis(QCPAxis::atRight); + legend = new QCPLegend; + legend->setVisible(false); + defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop); + defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12)); + + defaultAxisRect->setLayer(QLatin1String("background")); + xAxis->setLayer(QLatin1String("axes")); + yAxis->setLayer(QLatin1String("axes")); + xAxis2->setLayer(QLatin1String("axes")); + yAxis2->setLayer(QLatin1String("axes")); + xAxis->grid()->setLayer(QLatin1String("grid")); + yAxis->grid()->setLayer(QLatin1String("grid")); + xAxis2->grid()->setLayer(QLatin1String("grid")); + yAxis2->grid()->setLayer(QLatin1String("grid")); + legend->setLayer(QLatin1String("legend")); + + // create selection rect instance: + mSelectionRect = new QCPSelectionRect(this); + mSelectionRect->setLayer(QLatin1String("overlay")); + + setViewport(rect()); // needs to be called after mPlotLayout has been created + + replot(rpQueuedReplot); +} + +QCustomPlot::~QCustomPlot() +{ + clearPlottables(); + clearItems(); + + if (mPlotLayout) + { + delete mPlotLayout; + mPlotLayout = nullptr; + } + + mCurrentLayer = nullptr; + qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed + mLayers.clear(); +} + +/*! + Sets which elements are forcibly drawn antialiased as an \a or combination of QCP::AntialiasedElement. + + This overrides the antialiasing settings for whole element groups, normally controlled with the + \a setAntialiasing function on the individual elements. If an element is neither specified in + \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on + each individual element instance is used. + + For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be + drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set + to. + + if an element in \a antialiasedElements is already set in \ref setNotAntialiasedElements, it is + removed from there. + + \see setNotAntialiasedElements +*/ +void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements) +{ + mAntialiasedElements = antialiasedElements; + + // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously: + if ((mNotAntialiasedElements & mAntialiasedElements) != 0) + mNotAntialiasedElements |= ~mAntialiasedElements; +} + +/*! + Sets whether the specified \a antialiasedElement is forcibly drawn antialiased. + + See \ref setAntialiasedElements for details. + + \see setNotAntialiasedElement +*/ +void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled) +{ + if (!enabled && mAntialiasedElements.testFlag(antialiasedElement)) + mAntialiasedElements &= ~antialiasedElement; + else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement)) + mAntialiasedElements |= antialiasedElement; + + // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously: + if ((mNotAntialiasedElements & mAntialiasedElements) != 0) + mNotAntialiasedElements |= ~mAntialiasedElements; +} + +/*! + Sets which elements are forcibly drawn not antialiased as an \a or combination of + QCP::AntialiasedElement. + + This overrides the antialiasing settings for whole element groups, normally controlled with the + \a setAntialiasing function on the individual elements. If an element is neither specified in + \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on + each individual element instance is used. + + For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be + drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set + to. + + if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is + removed from there. + + \see setAntialiasedElements +*/ +void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements ¬AntialiasedElements) +{ + mNotAntialiasedElements = notAntialiasedElements; + + // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously: + if ((mNotAntialiasedElements & mAntialiasedElements) != 0) + mAntialiasedElements |= ~mNotAntialiasedElements; +} + +/*! + Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased. + + See \ref setNotAntialiasedElements for details. + + \see setAntialiasedElement +*/ +void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled) +{ + if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement)) + mNotAntialiasedElements &= ~notAntialiasedElement; + else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement)) + mNotAntialiasedElements |= notAntialiasedElement; + + // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously: + if ((mNotAntialiasedElements & mAntialiasedElements) != 0) + mAntialiasedElements |= ~mNotAntialiasedElements; +} + +/*! + If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the + plottable to the legend (QCustomPlot::legend). + + \see addGraph, QCPLegend::addItem +*/ +void QCustomPlot::setAutoAddPlottableToLegend(bool on) +{ + mAutoAddPlottableToLegend = on; +} + +/*! + Sets the possible interactions of this QCustomPlot as an or-combination of \ref QCP::Interaction + enums. There are the following types of interactions: + + Axis range manipulation is controlled via \ref QCP::iRangeDrag and \ref QCP::iRangeZoom. When the + respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel. + For details how to control which axes the user may drag/zoom and in what orientations, see \ref + QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeDragAxes, + \ref QCPAxisRect::setRangeZoomAxes. + + Plottable data selection is controlled by \ref QCP::iSelectPlottables. If \ref + QCP::iSelectPlottables is set, the user may select plottables (graphs, curves, bars,...) and + their data by clicking on them or in their vicinity (\ref setSelectionTolerance). Whether the + user can actually select a plottable and its data can further be restricted with the \ref + QCPAbstractPlottable::setSelectable method on the specific plottable. For details, see the + special page about the \ref dataselection "data selection mechanism". To retrieve a list of all + currently selected plottables, call \ref selectedPlottables. If you're only interested in + QCPGraphs, you may use the convenience function \ref selectedGraphs. + + Item selection is controlled by \ref QCP::iSelectItems. If \ref QCP::iSelectItems is set, the user + may select items (QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To find + out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a list of + all currently selected items, call \ref selectedItems. + + Axis selection is controlled with \ref QCP::iSelectAxes. If \ref QCP::iSelectAxes is set, the user + may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick + labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectableParts for + each axis. To retrieve a list of all axes that currently contain selected parts, call \ref + selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selectedParts(). + + Legend selection is controlled with \ref QCP::iSelectLegend. If this is set, the user may + select the legend itself or individual items by clicking on them. What parts exactly are + selectable can be controlled via \ref QCPLegend::setSelectableParts. To find out whether the + legend or any of its child items are selected, check the value of QCPLegend::selectedParts. To + find out which child items are selected, call \ref QCPLegend::selectedItems. + + All other selectable elements The selection of all other selectable objects (e.g. + QCPTextElement, or your own layerable subclasses) is controlled with \ref QCP::iSelectOther. If set, the + user may select those objects by clicking on them. To find out which are currently selected, you + need to check their selected state explicitly. + + If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is + emitted. Each selectable object additionally emits an individual selectionChanged signal whenever + their selection state has changed, i.e. not only by user interaction. + + To allow multiple objects to be selected by holding the selection modifier (\ref + setMultiSelectModifier), set the flag \ref QCP::iMultiSelect. + + \note In addition to the selection mechanism presented here, QCustomPlot always emits + corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and + \ref plottableDoubleClick for example. + + \see setInteraction, setSelectionTolerance +*/ +void QCustomPlot::setInteractions(const QCP::Interactions &interactions) +{ + mInteractions = interactions; +} + +/*! + Sets the single \a interaction of this QCustomPlot to \a enabled. + + For details about the interaction system, see \ref setInteractions. + + \see setInteractions +*/ +void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled) +{ + if (!enabled && mInteractions.testFlag(interaction)) + mInteractions &= ~interaction; + else if (enabled && !mInteractions.testFlag(interaction)) + mInteractions |= interaction; +} + +/*! + Sets the tolerance that is used to decide whether a click selects an object (e.g. a plottable) or + not. + + If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only regarded as a + potential selection when the minimum distance between the click position and the graph line is + smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks + directly inside the area and ignore this selection tolerance. In other words, it only has meaning + for parts of objects that are too thin to exactly hit with a click and thus need such a + tolerance. + + \see setInteractions, QCPLayerable::selectTest +*/ +void QCustomPlot::setSelectionTolerance(int pixels) +{ + mSelectionTolerance = pixels; +} + +/*! + Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes + ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves + performance during dragging. Thus it creates a more responsive user experience. As soon as the + user stops dragging, the last replot is done with normal antialiasing, to restore high image + quality. + + \see setAntialiasedElements, setNotAntialiasedElements +*/ +void QCustomPlot::setNoAntialiasingOnDrag(bool enabled) +{ + mNoAntialiasingOnDrag = enabled; +} + +/*! + Sets the plotting hints for this QCustomPlot instance as an \a or combination of QCP::PlottingHint. + + \see setPlottingHint +*/ +void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints) +{ + mPlottingHints = hints; +} + +/*! + Sets the specified plotting \a hint to \a enabled. + + \see setPlottingHints +*/ +void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled) +{ + QCP::PlottingHints newHints = mPlottingHints; + if (!enabled) + newHints &= ~hint; + else + newHints |= hint; + + if (newHints != mPlottingHints) + setPlottingHints(newHints); +} + +/*! + Sets the keyboard modifier that will be recognized as multi-select-modifier. + + If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple + objects (or data points) by clicking on them one after the other while holding down \a modifier. + + By default the multi-select-modifier is set to Qt::ControlModifier. + + \see setInteractions +*/ +void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier) +{ + mMultiSelectModifier = modifier; +} + +/*! + Sets how QCustomPlot processes mouse click-and-drag interactions by the user. + + If \a mode is \ref QCP::srmNone, the mouse drag is forwarded to the underlying objects. For + example, QCPAxisRect may process a mouse drag by dragging axis ranges, see \ref + QCPAxisRect::setRangeDrag. If \a mode is not \ref QCP::srmNone, the current selection rect (\ref + selectionRect) becomes activated and allows e.g. rect zooming and data point selection. + + If you wish to provide your user both with axis range dragging and data selection/range zooming, + use this method to switch between the modes just before the interaction is processed, e.g. in + reaction to the \ref mousePress or \ref mouseMove signals. For example you could check whether + the user is holding a certain keyboard modifier, and then decide which \a mode shall be set. + + If a selection rect interaction is currently active, and \a mode is set to \ref QCP::srmNone, the + interaction is canceled (\ref QCPSelectionRect::cancel). Switching between any of the other modes + will keep the selection rect active. Upon completion of the interaction, the behaviour is as + defined by the currently set \a mode, not the mode that was set when the interaction started. + + \see setInteractions, setSelectionRect, QCPSelectionRect +*/ +void QCustomPlot::setSelectionRectMode(QCP::SelectionRectMode mode) +{ + if (mSelectionRect) + { + if (mode == QCP::srmNone) + mSelectionRect->cancel(); // when switching to none, we immediately want to abort a potentially active selection rect + + // disconnect old connections: + if (mSelectionRectMode == QCP::srmSelect) + disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*))); + else if (mSelectionRectMode == QCP::srmZoom) + disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*))); + + // establish new ones: + if (mode == QCP::srmSelect) + connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*))); + else if (mode == QCP::srmZoom) + connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*))); + } + + mSelectionRectMode = mode; +} + +/*! + Sets the \ref QCPSelectionRect instance that QCustomPlot will use if \a mode is not \ref + QCP::srmNone and the user performs a click-and-drag interaction. QCustomPlot takes ownership of + the passed \a selectionRect. It can be accessed later via \ref selectionRect. + + This method is useful if you wish to replace the default QCPSelectionRect instance with an + instance of a QCPSelectionRect subclass, to introduce custom behaviour of the selection rect. + + \see setSelectionRectMode +*/ +void QCustomPlot::setSelectionRect(QCPSelectionRect *selectionRect) +{ + delete mSelectionRect; + + mSelectionRect = selectionRect; + + if (mSelectionRect) + { + // establish connections with new selection rect: + if (mSelectionRectMode == QCP::srmSelect) + connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*))); + else if (mSelectionRectMode == QCP::srmZoom) + connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*))); + } +} + +/*! + \warning This is still an experimental feature and its performance depends on the system that it + runs on. Having multiple QCustomPlot widgets in one application with enabled OpenGL rendering + might cause context conflicts on some systems. + + This method allows to enable OpenGL plot rendering, for increased plotting performance of + graphically demanding plots (thick lines, translucent fills, etc.). + + If \a enabled is set to true, QCustomPlot will try to initialize OpenGL and, if successful, + continue plotting with hardware acceleration. The parameter \a multisampling controls how many + samples will be used per pixel, it essentially controls the antialiasing quality. If \a + multisampling is set too high for the current graphics hardware, the maximum allowed value will + be used. + + You can test whether switching to OpenGL rendering was successful by checking whether the + according getter \a QCustomPlot::openGl() returns true. If the OpenGL initialization fails, + rendering continues with the regular software rasterizer, and an according qDebug output is + generated. + + If switching to OpenGL was successful, this method disables label caching (\ref setPlottingHint + "setPlottingHint(QCP::phCacheLabels, false)") and turns on QCustomPlot's antialiasing override + for all elements (\ref setAntialiasedElements "setAntialiasedElements(QCP::aeAll)"), leading to a + higher quality output. The antialiasing override allows for pixel-grid aligned drawing in the + OpenGL paint device. As stated before, in OpenGL rendering the actual antialiasing of the plot is + controlled with \a multisampling. If \a enabled is set to false, the antialiasing/label caching + settings are restored to what they were before OpenGL was enabled, if they weren't altered in the + meantime. + + \note OpenGL support is only enabled if QCustomPlot is compiled with the macro \c QCUSTOMPLOT_USE_OPENGL + defined. This define must be set before including the QCustomPlot header both during compilation + of the QCustomPlot library as well as when compiling your application. It is best to just include + the line DEFINES += QCUSTOMPLOT_USE_OPENGL in the respective qmake project files. + \note If you are using a Qt version before 5.0, you must also add the module "opengl" to your \c + QT variable in the qmake project files. For Qt versions 5.0 and higher, QCustomPlot switches to a + newer OpenGL interface which is already in the "gui" module. +*/ +void QCustomPlot::setOpenGl(bool enabled, int multisampling) +{ + mOpenGlMultisamples = qMax(0, multisampling); +#ifdef QCUSTOMPLOT_USE_OPENGL + mOpenGl = enabled; + if (mOpenGl) + { + if (setupOpenGl()) + { + // backup antialiasing override and labelcaching setting so we can restore upon disabling OpenGL + mOpenGlAntialiasedElementsBackup = mAntialiasedElements; + mOpenGlCacheLabelsBackup = mPlottingHints.testFlag(QCP::phCacheLabels); + // set antialiasing override to antialias all (aligns gl pixel grid properly), and disable label caching (would use software rasterizer for pixmap caches): + setAntialiasedElements(QCP::aeAll); + setPlottingHint(QCP::phCacheLabels, false); + } else + { + qDebug() << Q_FUNC_INFO << "Failed to enable OpenGL, continuing plotting without hardware acceleration."; + mOpenGl = false; + } + } else + { + // restore antialiasing override and labelcaching to what it was before enabling OpenGL, if nobody changed it in the meantime: + if (mAntialiasedElements == QCP::aeAll) + setAntialiasedElements(mOpenGlAntialiasedElementsBackup); + if (!mPlottingHints.testFlag(QCP::phCacheLabels)) + setPlottingHint(QCP::phCacheLabels, mOpenGlCacheLabelsBackup); + freeOpenGl(); + } + // recreate all paint buffers: + mPaintBuffers.clear(); + setupPaintBuffers(); +#else + Q_UNUSED(enabled) + qDebug() << Q_FUNC_INFO << "QCustomPlot can't use OpenGL because QCUSTOMPLOT_USE_OPENGL was not defined during compilation (add 'DEFINES += QCUSTOMPLOT_USE_OPENGL' to your qmake .pro file)"; +#endif +} + +/*! + Sets the viewport of this QCustomPlot. Usually users of QCustomPlot don't need to change the + viewport manually. + + The viewport is the area in which the plot is drawn. All mechanisms, e.g. margin calculation take + the viewport to be the outer border of the plot. The viewport normally is the rect() of the + QCustomPlot widget, i.e. a rect with top left (0, 0) and size of the QCustomPlot widget. + + Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis rect is typically + an area enclosed by four axes, where the graphs/plottables are drawn in. The viewport is larger + and contains also the axes themselves, their tick numbers, their labels, or even additional axis + rects, color scales and other layout elements. + + This function is used to allow arbitrary size exports with \ref toPixmap, \ref savePng, \ref + savePdf, etc. by temporarily changing the viewport size. +*/ +void QCustomPlot::setViewport(const QRect &rect) +{ + mViewport = rect; + if (mPlotLayout) + mPlotLayout->setOuterRect(mViewport); +} + +/*! + Sets the device pixel ratio used by the paint buffers of this QCustomPlot instance. + + Normally, this doesn't need to be set manually, because it is initialized with the regular \a + QWidget::devicePixelRatio which is configured by Qt to fit the display device (e.g. 1 for normal + displays, 2 for High-DPI displays). + + Device pixel ratios are supported by Qt only for Qt versions since 5.4. If this method is called + when QCustomPlot is being used with older Qt versions, outputs an according qDebug message and + leaves the internal buffer device pixel ratio at 1.0. +*/ +void QCustomPlot::setBufferDevicePixelRatio(double ratio) +{ + if (!qFuzzyCompare(ratio, mBufferDevicePixelRatio)) + { +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED + mBufferDevicePixelRatio = ratio; + foreach (QSharedPointer buffer, mPaintBuffers) + buffer->setDevicePixelRatio(mBufferDevicePixelRatio); + // Note: axis label cache has devicePixelRatio as part of cache hash, so no need to manually clear cache here +#else + qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4"; + mBufferDevicePixelRatio = 1.0; +#endif + } +} + +/*! + Sets \a pm as the viewport background pixmap (see \ref setViewport). The pixmap is always drawn + below all other objects in the plot. + + For cases where the provided pixmap doesn't have the same size as the viewport, scaling can be + enabled with \ref setBackgroundScaled and the scaling mode (whether and how the aspect ratio is + preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call, + consider using the overloaded version of this function. + + If a background brush was set with \ref setBackground(const QBrush &brush), the viewport will + first be filled with that brush, before drawing the background pixmap. This can be useful for + background pixmaps with translucent areas. + + \see setBackgroundScaled, setBackgroundScaledMode +*/ +void QCustomPlot::setBackground(const QPixmap &pm) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); +} + +/*! + Sets the background brush of the viewport (see \ref setViewport). + + Before drawing everything else, the background is filled with \a brush. If a background pixmap + was set with \ref setBackground(const QPixmap &pm), this brush will be used to fill the viewport + before the background pixmap is drawn. This can be useful for background pixmaps with translucent + areas. + + Set \a brush to Qt::NoBrush or Qt::Transparent to leave background transparent. This can be + useful for exporting to image formats which support transparency, e.g. \ref savePng. + + \see setBackgroundScaled, setBackgroundScaledMode +*/ +void QCustomPlot::setBackground(const QBrush &brush) +{ + mBackgroundBrush = brush; +} + +/*! \overload + + Allows setting the background pixmap of the viewport, whether it shall be scaled and how it + shall be scaled in one call. + + \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode +*/ +void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); + mBackgroundScaled = scaled; + mBackgroundScaledMode = mode; +} + +/*! + Sets whether the viewport background pixmap shall be scaled to fit the viewport. If \a scaled is + set to true, control whether and how the aspect ratio of the original pixmap is preserved with + \ref setBackgroundScaledMode. + + Note that the scaled version of the original pixmap is buffered, so there is no performance + penalty on replots. (Except when the viewport dimensions are changed continuously.) + + \see setBackground, setBackgroundScaledMode +*/ +void QCustomPlot::setBackgroundScaled(bool scaled) +{ + mBackgroundScaled = scaled; +} + +/*! + If scaling of the viewport background pixmap is enabled (\ref setBackgroundScaled), use this + function to define whether and how the aspect ratio of the original pixmap is preserved. + + \see setBackground, setBackgroundScaled +*/ +void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode) +{ + mBackgroundScaledMode = mode; +} + +/*! + Returns the plottable with \a index. If the index is invalid, returns \c nullptr. + + There is an overloaded version of this function with no parameter which returns the last added + plottable, see QCustomPlot::plottable() + + \see plottableCount +*/ +QCPAbstractPlottable *QCustomPlot::plottable(int index) +{ + if (index >= 0 && index < mPlottables.size()) + { + return mPlottables.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return nullptr; + } +} + +/*! \overload + + Returns the last plottable that was added to the plot. If there are no plottables in the plot, + returns \c nullptr. + + \see plottableCount +*/ +QCPAbstractPlottable *QCustomPlot::plottable() +{ + if (!mPlottables.isEmpty()) + { + return mPlottables.last(); + } else + return nullptr; +} + +/*! + Removes the specified plottable from the plot and deletes it. If necessary, the corresponding + legend item is also removed from the default legend (QCustomPlot::legend). + + Returns true on success. + + \see clearPlottables +*/ +bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable) +{ + if (!mPlottables.contains(plottable)) + { + qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast(plottable); + return false; + } + + // remove plottable from legend: + plottable->removeFromLegend(); + // special handling for QCPGraphs to maintain the simple graph interface: + if (QCPGraph *graph = qobject_cast(plottable)) + mGraphs.removeOne(graph); + // remove plottable: + delete plottable; + mPlottables.removeOne(plottable); + return true; +} + +/*! \overload + + Removes and deletes the plottable by its \a index. +*/ +bool QCustomPlot::removePlottable(int index) +{ + if (index >= 0 && index < mPlottables.size()) + return removePlottable(mPlottables[index]); + else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return false; + } +} + +/*! + Removes all plottables from the plot and deletes them. Corresponding legend items are also + removed from the default legend (QCustomPlot::legend). + + Returns the number of plottables removed. + + \see removePlottable +*/ +int QCustomPlot::clearPlottables() +{ + int c = static_cast(mPlottables.size()); + for (int i=c-1; i >= 0; --i) + removePlottable(mPlottables[i]); + return c; +} + +/*! + Returns the number of currently existing plottables in the plot + + \see plottable +*/ +int QCustomPlot::plottableCount() const +{ + return static_cast(mPlottables.size()); +} + +/*! + Returns a list of the selected plottables. If no plottables are currently selected, the list is empty. + + There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs. + + \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelection +*/ +QList QCustomPlot::selectedPlottables() const +{ + QList result; + foreach (QCPAbstractPlottable *plottable, mPlottables) + { + if (plottable->selected()) + result.append(plottable); + } + return result; +} + +/*! + Returns any plottable at the pixel position \a pos. Since it can capture all plottables, the + return type is the abstract base class of all plottables, QCPAbstractPlottable. + + For details, and if you wish to specify a certain plottable type (e.g. QCPGraph), see the + template method plottableAt() + + \see plottableAt(), itemAt, layoutElementAt +*/ +QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const +{ + return plottableAt(pos, onlySelectable, dataIndex); +} + +/*! + Returns whether this QCustomPlot instance contains the \a plottable. +*/ +bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const +{ + return mPlottables.contains(plottable); +} + +/*! + Returns the graph with \a index. If the index is invalid, returns \c nullptr. + + There is an overloaded version of this function with no parameter which returns the last created + graph, see QCustomPlot::graph() + + \see graphCount, addGraph +*/ +QCPGraph *QCustomPlot::graph(int index) const +{ + if (index >= 0 && index < mGraphs.size()) + { + return mGraphs.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return nullptr; + } +} + +/*! \overload + + Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot, + returns \c nullptr. + + \see graphCount, addGraph +*/ +QCPGraph *QCustomPlot::graph() const +{ + if (!mGraphs.isEmpty()) + { + return mGraphs.last(); + } else + return nullptr; +} + +/*! + Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the + bottom (xAxis) is used as key and the left (yAxis) is used as value axis. If specified, \a + keyAxis and \a valueAxis must reside in this QCustomPlot. + + \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically + "y") for the graph. + + Returns a pointer to the newly created graph, or \c nullptr if adding the graph failed. + + \see graph, graphCount, removeGraph, clearGraphs +*/ +QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) +{ + if (!keyAxis) keyAxis = xAxis; + if (!valueAxis) valueAxis = yAxis; + if (!keyAxis || !valueAxis) + { + qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)"; + return nullptr; + } + if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this) + { + qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent"; + return nullptr; + } + + QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis); + newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size())); + return newGraph; +} + +/*! + Removes the specified \a graph from the plot and deletes it. If necessary, the corresponding + legend item is also removed from the default legend (QCustomPlot::legend). If any other graphs in + the plot have a channel fill set towards the removed graph, the channel fill property of those + graphs is reset to \c nullptr (no channel fill). + + Returns true on success. + + \see clearGraphs +*/ +bool QCustomPlot::removeGraph(QCPGraph *graph) +{ + return removePlottable(graph); +} + +/*! \overload + + Removes and deletes the graph by its \a index. +*/ +bool QCustomPlot::removeGraph(int index) +{ + if (index >= 0 && index < mGraphs.size()) + return removeGraph(mGraphs[index]); + else + return false; +} + +/*! + Removes all graphs from the plot and deletes them. Corresponding legend items are also removed + from the default legend (QCustomPlot::legend). + + Returns the number of graphs removed. + + \see removeGraph +*/ +int QCustomPlot::clearGraphs() +{ + int c = static_cast(mGraphs.size()); + for (int i=c-1; i >= 0; --i) + removeGraph(mGraphs[i]); + return c; +} + +/*! + Returns the number of currently existing graphs in the plot + + \see graph, addGraph +*/ +int QCustomPlot::graphCount() const +{ + return static_cast(mGraphs.size()); +} + +/*! + Returns a list of the selected graphs. If no graphs are currently selected, the list is empty. + + If you are not only interested in selected graphs but other plottables like QCPCurve, QCPBars, + etc., use \ref selectedPlottables. + + \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelection +*/ +QList QCustomPlot::selectedGraphs() const +{ + QList result; + foreach (QCPGraph *graph, mGraphs) + { + if (graph->selected()) + result.append(graph); + } + return result; +} + +/*! + Returns the item with \a index. If the index is invalid, returns \c nullptr. + + There is an overloaded version of this function with no parameter which returns the last added + item, see QCustomPlot::item() + + \see itemCount +*/ +QCPAbstractItem *QCustomPlot::item(int index) const +{ + if (index >= 0 && index < mItems.size()) + { + return mItems.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return nullptr; + } +} + +/*! \overload + + Returns the last item that was added to this plot. If there are no items in the plot, + returns \c nullptr. + + \see itemCount +*/ +QCPAbstractItem *QCustomPlot::item() const +{ + if (!mItems.isEmpty()) + { + return mItems.last(); + } else + return nullptr; +} + +/*! + Removes the specified item from the plot and deletes it. + + Returns true on success. + + \see clearItems +*/ +bool QCustomPlot::removeItem(QCPAbstractItem *item) +{ + if (mItems.contains(item)) + { + delete item; + mItems.removeOne(item); + return true; + } else + { + qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast(item); + return false; + } +} + +/*! \overload + + Removes and deletes the item by its \a index. +*/ +bool QCustomPlot::removeItem(int index) +{ + if (index >= 0 && index < mItems.size()) + return removeItem(mItems[index]); + else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return false; + } +} + +/*! + Removes all items from the plot and deletes them. + + Returns the number of items removed. + + \see removeItem +*/ +int QCustomPlot::clearItems() +{ + int c = static_cast(mItems.size()); + for (int i=c-1; i >= 0; --i) + removeItem(mItems[i]); + return c; +} + +/*! + Returns the number of currently existing items in the plot + + \see item +*/ +int QCustomPlot::itemCount() const +{ + return static_cast(mItems.size()); +} + +/*! + Returns a list of the selected items. If no items are currently selected, the list is empty. + + \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected +*/ +QList QCustomPlot::selectedItems() const +{ + QList result; + foreach (QCPAbstractItem *item, mItems) + { + if (item->selected()) + result.append(item); + } + return result; +} + +/*! + Returns the item at the pixel position \a pos. Since it can capture all items, the + return type is the abstract base class of all items, QCPAbstractItem. + + For details, and if you wish to specify a certain item type (e.g. QCPItemLine), see the + template method itemAt() + + \see itemAt(), plottableAt, layoutElementAt +*/ +QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const +{ + return itemAt(pos, onlySelectable); +} + +/*! + Returns whether this QCustomPlot contains the \a item. + + \see item +*/ +bool QCustomPlot::hasItem(QCPAbstractItem *item) const +{ + return mItems.contains(item); +} + +/*! + Returns the layer with the specified \a name. If there is no layer with the specified name, \c + nullptr is returned. + + Layer names are case-sensitive. + + \see addLayer, moveLayer, removeLayer +*/ +QCPLayer *QCustomPlot::layer(const QString &name) const +{ + foreach (QCPLayer *layer, mLayers) + { + if (layer->name() == name) + return layer; + } + return nullptr; +} + +/*! \overload + + Returns the layer by \a index. If the index is invalid, \c nullptr is returned. + + \see addLayer, moveLayer, removeLayer +*/ +QCPLayer *QCustomPlot::layer(int index) const +{ + if (index >= 0 && index < mLayers.size()) + { + return mLayers.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return nullptr; + } +} + +/*! + Returns the layer that is set as current layer (see \ref setCurrentLayer). +*/ +QCPLayer *QCustomPlot::currentLayer() const +{ + return mCurrentLayer; +} + +/*! + Sets the layer with the specified \a name to be the current layer. All layerables (\ref + QCPLayerable), e.g. plottables and items, are created on the current layer. + + Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot. + + Layer names are case-sensitive. + + \see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer +*/ +bool QCustomPlot::setCurrentLayer(const QString &name) +{ + if (QCPLayer *newCurrentLayer = layer(name)) + { + return setCurrentLayer(newCurrentLayer); + } else + { + qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name; + return false; + } +} + +/*! \overload + + Sets the provided \a layer to be the current layer. + + Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot. + + \see addLayer, moveLayer, removeLayer +*/ +bool QCustomPlot::setCurrentLayer(QCPLayer *layer) +{ + if (!mLayers.contains(layer)) + { + qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast(layer); + return false; + } + + mCurrentLayer = layer; + return true; +} + +/*! + Returns the number of currently existing layers in the plot + + \see layer, addLayer +*/ +int QCustomPlot::layerCount() const +{ + return static_cast(mLayers.size()); +} + +/*! + Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which + must be unique. Depending on \a insertMode, it is positioned either below or above \a otherLayer. + + Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a + valid layer inside this QCustomPlot. + + If \a otherLayer is 0, the highest layer in the QCustomPlot will be used. + + For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer. + + \see layer, moveLayer, removeLayer +*/ +bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode) +{ + if (!otherLayer) + otherLayer = mLayers.last(); + if (!mLayers.contains(otherLayer)) + { + qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast(otherLayer); + return false; + } + if (layer(name)) + { + qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name; + return false; + } + + QCPLayer *newLayer = new QCPLayer(this, name); + mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer); + updateLayerIndices(); + setupPaintBuffers(); // associates new layer with the appropriate paint buffer + return true; +} + +/*! + Removes the specified \a layer and returns true on success. + + All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below + \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both + cases, the total rendering order of all layerables in the QCustomPlot is preserved. + + If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom + layer) becomes the new current layer. + + It is not possible to remove the last layer of the plot. + + \see layer, addLayer, moveLayer +*/ +bool QCustomPlot::removeLayer(QCPLayer *layer) +{ + if (!mLayers.contains(layer)) + { + qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast(layer); + return false; + } + if (mLayers.size() < 2) + { + qDebug() << Q_FUNC_INFO << "can't remove last layer"; + return false; + } + + // append all children of this layer to layer below (if this is lowest layer, prepend to layer above) + int removedIndex = layer->index(); + bool isFirstLayer = removedIndex==0; + QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1); + QList children = layer->children(); + if (isFirstLayer) // prepend in reverse order (such that relative order stays the same) + std::reverse(children.begin(), children.end()); + foreach (QCPLayerable *child, children) + child->moveToLayer(targetLayer, isFirstLayer); // prepend if isFirstLayer, otherwise append + + // if removed layer is current layer, change current layer to layer below/above: + if (layer == mCurrentLayer) + setCurrentLayer(targetLayer); + + // invalidate the paint buffer that was responsible for this layer: + if (QSharedPointer pb = layer->mPaintBuffer.toStrongRef()) + pb->setInvalidated(); + + // remove layer: + delete layer; + mLayers.removeOne(layer); + updateLayerIndices(); + return true; +} + +/*! + Moves the specified \a layer either above or below \a otherLayer. Whether it's placed above or + below is controlled with \a insertMode. + + Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the + QCustomPlot. + + \see layer, addLayer, moveLayer +*/ +bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode) +{ + if (!mLayers.contains(layer)) + { + qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast(layer); + return false; + } + if (!mLayers.contains(otherLayer)) + { + qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast(otherLayer); + return false; + } + + if (layer->index() > otherLayer->index()) + mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0)); + else if (layer->index() < otherLayer->index()) + mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1)); + + // invalidate the paint buffers that are responsible for the layers: + if (QSharedPointer pb = layer->mPaintBuffer.toStrongRef()) + pb->setInvalidated(); + if (QSharedPointer pb = otherLayer->mPaintBuffer.toStrongRef()) + pb->setInvalidated(); + + updateLayerIndices(); + return true; +} + +/*! + Returns the number of axis rects in the plot. + + All axis rects can be accessed via QCustomPlot::axisRect(). + + Initially, only one axis rect exists in the plot. + + \see axisRect, axisRects +*/ +int QCustomPlot::axisRectCount() const +{ + return static_cast(axisRects().size()); +} + +/*! + Returns the axis rect with \a index. + + Initially, only one axis rect (with index 0) exists in the plot. If multiple axis rects were + added, all of them may be accessed with this function in a linear fashion (even when they are + nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout). + + The order of the axis rects is given by the fill order of the \ref QCPLayout that is holding + them. For example, if the axis rects are in the top level grid layout (accessible via \ref + QCustomPlot::plotLayout), they are ordered from left to right, top to bottom, if the layout's + default \ref QCPLayoutGrid::setFillOrder "setFillOrder" of \ref QCPLayoutGrid::foColumnsFirst + "foColumnsFirst" wasn't changed. + + If you want to access axis rects by their row and column index, use the layout interface. For + example, use \ref QCPLayoutGrid::element of the top level grid layout, and \c qobject_cast the + returned layout element to \ref QCPAxisRect. (See also \ref thelayoutsystem.) + + \see axisRectCount, axisRects, QCPLayoutGrid::setFillOrder +*/ +QCPAxisRect *QCustomPlot::axisRect(int index) const +{ + const QList rectList = axisRects(); + if (index >= 0 && index < rectList.size()) + { + return rectList.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index; + return nullptr; + } +} + +/*! + Returns all axis rects in the plot. + + The order of the axis rects is given by the fill order of the \ref QCPLayout that is holding + them. For example, if the axis rects are in the top level grid layout (accessible via \ref + QCustomPlot::plotLayout), they are ordered from left to right, top to bottom, if the layout's + default \ref QCPLayoutGrid::setFillOrder "setFillOrder" of \ref QCPLayoutGrid::foColumnsFirst + "foColumnsFirst" wasn't changed. + + \see axisRectCount, axisRect, QCPLayoutGrid::setFillOrder +*/ +QList QCustomPlot::axisRects() const +{ + QList result; + QStack elementStack; + if (mPlotLayout) + elementStack.push(mPlotLayout); + + while (!elementStack.isEmpty()) + { + foreach (QCPLayoutElement *element, elementStack.pop()->elements(false)) + { + if (element) + { + elementStack.push(element); + if (QCPAxisRect *ar = qobject_cast(element)) + result.append(ar); + } + } + } + + return result; +} + +/*! + Returns the layout element at pixel position \a pos. If there is no element at that position, + returns \c nullptr. + + Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on + any of its parent elements is set to false, it will not be considered. + + \see itemAt, plottableAt +*/ +QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const +{ + QCPLayoutElement *currentElement = mPlotLayout; + bool searchSubElements = true; + while (searchSubElements && currentElement) + { + searchSubElements = false; + foreach (QCPLayoutElement *subElement, currentElement->elements(false)) + { + if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0) + { + currentElement = subElement; + searchSubElements = true; + break; + } + } + } + return currentElement; +} + +/*! + Returns the layout element of type \ref QCPAxisRect at pixel position \a pos. This method ignores + other layout elements even if they are visually in front of the axis rect (e.g. a \ref + QCPLegend). If there is no axis rect at that position, returns \c nullptr. + + Only visible axis rects are used. If \ref QCPLayoutElement::setVisible on the axis rect itself or + on any of its parent elements is set to false, it will not be considered. + + \see layoutElementAt +*/ +QCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const +{ + QCPAxisRect *result = nullptr; + QCPLayoutElement *currentElement = mPlotLayout; + bool searchSubElements = true; + while (searchSubElements && currentElement) + { + searchSubElements = false; + foreach (QCPLayoutElement *subElement, currentElement->elements(false)) + { + if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0) + { + currentElement = subElement; + searchSubElements = true; + if (QCPAxisRect *ar = qobject_cast(currentElement)) + result = ar; + break; + } + } + } + return result; +} + +/*! + Returns the axes that currently have selected parts, i.e. whose selection state is not \ref + QCPAxis::spNone. + + \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelectedParts, + QCPAxis::setSelectableParts +*/ +QList QCustomPlot::selectedAxes() const +{ + QList result, allAxes; + foreach (QCPAxisRect *rect, axisRects()) + allAxes << rect->axes(); + + foreach (QCPAxis *axis, allAxes) + { + if (axis->selectedParts() != QCPAxis::spNone) + result.append(axis); + } + + return result; +} + +/*! + Returns the legends that currently have selected parts, i.e. whose selection state is not \ref + QCPLegend::spNone. + + \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelectedParts, + QCPLegend::setSelectableParts, QCPLegend::selectedItems +*/ +QList QCustomPlot::selectedLegends() const +{ + QList result; + + QStack elementStack; + if (mPlotLayout) + elementStack.push(mPlotLayout); + + while (!elementStack.isEmpty()) + { + foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false)) + { + if (subElement) + { + elementStack.push(subElement); + if (QCPLegend *leg = qobject_cast(subElement)) + { + if (leg->selectedParts() != QCPLegend::spNone) + result.append(leg); + } + } + } + } + + return result; +} + +/*! + Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot. + + Since calling this function is not a user interaction, this does not emit the \ref + selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the + objects were previously selected. + + \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends +*/ +void QCustomPlot::deselectAll() +{ + foreach (QCPLayer *layer, mLayers) + { + foreach (QCPLayerable *layerable, layer->children()) + layerable->deselectEvent(nullptr); + } +} + +/*! + Causes a complete replot into the internal paint buffer(s). Finally, the widget surface is + refreshed with the new buffer contents. This is the method that must be called to make changes to + the plot, e.g. on the axis ranges or data points of graphs, visible. + + The parameter \a refreshPriority can be used to fine-tune the timing of the replot. For example + if your application calls \ref replot very quickly in succession (e.g. multiple independent + functions change some aspects of the plot and each wants to make sure the change gets replotted), + it is advisable to set \a refreshPriority to \ref QCustomPlot::rpQueuedReplot. This way, the + actual replotting is deferred to the next event loop iteration. Multiple successive calls of \ref + replot with this priority will only cause a single replot, avoiding redundant replots and + improving performance. + + Under a few circumstances, QCustomPlot causes a replot by itself. Those are resize events of the + QCustomPlot widget and user interactions (object selection and range dragging/zooming). + + Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref + afterReplot is emitted. It is safe to mutually connect the replot slot with any of those two + signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite + recursion. + + If a layer is in mode \ref QCPLayer::lmBuffered (\ref QCPLayer::setMode), it is also possible to + replot only that specific layer via \ref QCPLayer::replot. See the documentation there for + details. + + \see replotTime +*/ +void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority) +{ + if (refreshPriority == QCustomPlot::rpQueuedReplot) + { + if (!mReplotQueued) + { + mReplotQueued = true; + QTimer::singleShot(0, this, SLOT(replot())); + } + return; + } + + if (mReplotting) // incase signals loop back to replot slot + return; + mReplotting = true; + mReplotQueued = false; + emit beforeReplot(); + +# if QT_VERSION < QT_VERSION_CHECK(4, 8, 0) + QTime replotTimer; + replotTimer.start(); +# else + QElapsedTimer replotTimer; + replotTimer.start(); +# endif + + updateLayout(); + // draw all layered objects (grid, axes, plottables, items, legend,...) into their buffers: + setupPaintBuffers(); + foreach (QCPLayer *layer, mLayers) + layer->drawToPaintBuffer(); + foreach (QSharedPointer buffer, mPaintBuffers) + buffer->setInvalidated(false); + + if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh) + repaint(); + else + update(); + +# if QT_VERSION < QT_VERSION_CHECK(4, 8, 0) + mReplotTime = replotTimer.elapsed(); +# else + mReplotTime = replotTimer.nsecsElapsed()*1e-6; +# endif + if (!qFuzzyIsNull(mReplotTimeAverage)) + mReplotTimeAverage = mReplotTimeAverage*0.9 + mReplotTime*0.1; // exponential moving average with a time constant of 10 last replots + else + mReplotTimeAverage = mReplotTime; // no previous replots to average with, so initialize with replot time + + emit afterReplot(); + mReplotting = false; +} + +/*! + Returns the time in milliseconds that the last replot took. If \a average is set to true, an + exponential moving average over the last couple of replots is returned. + + \see replot +*/ +double QCustomPlot::replotTime(bool average) const +{ + return average ? mReplotTimeAverage : mReplotTime; +} + +/*! + Rescales the axes such that all plottables (like graphs) in the plot are fully visible. + + if \a onlyVisiblePlottables is set to true, only the plottables that have their visibility set to true + (QCPLayerable::setVisible), will be used to rescale the axes. + + \see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale +*/ +void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables) +{ + QList allAxes; + foreach (QCPAxisRect *rect, axisRects()) + allAxes << rect->axes(); + + foreach (QCPAxis *axis, allAxes) + axis->rescale(onlyVisiblePlottables); +} + +/*! + Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale + of texts and lines will be derived from the specified \a width and \a height. This means, the + output will look like the normal on-screen output of a QCustomPlot widget with the corresponding + pixel width and height. If either \a width or \a height is zero, the exported image will have the + same dimensions as the QCustomPlot widget currently has. + + Setting \a exportPen to \ref QCP::epNoCosmetic allows to disable the use of cosmetic pens when + drawing to the PDF file. Cosmetic pens are pens with numerical width 0, which are always drawn as + a one pixel wide line, no matter what zoom factor is set in the PDF-Viewer. For more information + about cosmetic pens, see the QPainter and QPen documentation. + + The objects of the plot will appear in the current selection state. If you don't want any + selected objects to be painted in their selected look, deselect everything with \ref deselectAll + before calling this function. + + Returns true on success. + + \li If you plan on editing the exported PDF file with a vector graphics editor like Inkscape, it + is advised to set \a exportPen to \ref QCP::epNoCosmetic to avoid losing those cosmetic lines + (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks). + \li If calling this function inside the constructor of the parent of the QCustomPlot widget + (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide + explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this + function uses the current width and height of the QCustomPlot widget. However, in Qt, these + aren't defined yet inside the constructor, so you would get an image that has strange + widths/heights. + + \a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting + PDF file. + + \note On Android systems, this method does nothing and issues an according qDebug warning + message. This is also the case if for other reasons the define flag \c QT_NO_PRINTER is set. + + \see savePng, saveBmp, saveJpg, saveRastered +*/ +bool QCustomPlot::savePdf(const QString &fileName, int width, int height, QCP::ExportPen exportPen, const QString &pdfCreator, const QString &pdfTitle) +{ + bool success = false; +#ifdef QT_NO_PRINTER + Q_UNUSED(fileName) + Q_UNUSED(exportPen) + Q_UNUSED(width) + Q_UNUSED(height) + Q_UNUSED(pdfCreator) + Q_UNUSED(pdfTitle) + qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created."; +#else + int newWidth, newHeight; + if (width == 0 || height == 0) + { + newWidth = this->width(); + newHeight = this->height(); + } else + { + newWidth = width; + newHeight = height; + } + + QPrinter printer(QPrinter::ScreenResolution); + printer.setOutputFileName(fileName); + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setColorMode(QPrinter::Color); + printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator); + printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle); + QRect oldViewport = viewport(); + setViewport(QRect(0, 0, newWidth, newHeight)); +#if QT_VERSION < QT_VERSION_CHECK(5, 3, 0) + printer.setFullPage(true); + printer.setPaperSize(viewport().size(), QPrinter::DevicePixel); +#else + QPageLayout pageLayout; + pageLayout.setMode(QPageLayout::FullPageMode); + pageLayout.setOrientation(QPageLayout::Portrait); + pageLayout.setMargins(QMarginsF(0, 0, 0, 0)); + pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch)); + printer.setPageLayout(pageLayout); +#endif + QCPPainter printpainter; + if (printpainter.begin(&printer)) + { + printpainter.setMode(QCPPainter::pmVectorized); + printpainter.setMode(QCPPainter::pmNoCaching); + printpainter.setMode(QCPPainter::pmNonCosmetic, exportPen==QCP::epNoCosmetic); + printpainter.setWindow(mViewport); + if (mBackgroundBrush.style() != Qt::NoBrush && + mBackgroundBrush.color() != Qt::white && + mBackgroundBrush.color() != Qt::transparent && + mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent + printpainter.fillRect(viewport(), mBackgroundBrush); + draw(&printpainter); + printpainter.end(); + success = true; + } + setViewport(oldViewport); +#endif // QT_NO_PRINTER + return success; +} + +/*! + Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width + and \a height in pixels, multiplied by \a scale. If either \a width or \a height is zero, the + current width and height of the QCustomPlot widget is used instead. Line widths and texts etc. + are not scaled up when larger widths/heights are used. If you want that effect, use the \a scale + parameter. + + For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an + image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths, + texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full + 200*200 pixel resolution. + + If you use a high scaling factor, it is recommended to enable antialiasing for all elements by + temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows + QCustomPlot to place objects with sub-pixel accuracy. + + image compression can be controlled with the \a quality parameter which must be between 0 and 100 + or -1 to use the default setting. + + The \a resolution will be written to the image file header and has no direct consequence for the + quality or the pixel size. However, if opening the image with a tool which respects the metadata, + it will be able to scale the image to match either a given size in real units of length (inch, + centimeters, etc.), or the target display DPI. You can specify in which units \a resolution is + given, by setting \a resolutionUnit. The \a resolution is converted to the format's expected + resolution unit internally. + + Returns true on success. If this function fails, most likely the PNG format isn't supported by + the system, see Qt docs about QImageWriter::supportedImageFormats(). + + The objects of the plot will appear in the current selection state. If you don't want any selected + objects to be painted in their selected look, deselect everything with \ref deselectAll before calling + this function. + + If you want the PNG to have a transparent background, call \ref setBackground(const QBrush &brush) + with no brush (Qt::NoBrush) or a transparent color (Qt::transparent), before saving. + + \warning If calling this function inside the constructor of the parent of the QCustomPlot widget + (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide + explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this + function uses the current width and height of the QCustomPlot widget. However, in Qt, these + aren't defined yet inside the constructor, so you would get an image that has strange + widths/heights. + + \see savePdf, saveBmp, saveJpg, saveRastered +*/ +bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit) +{ + return saveRastered(fileName, width, height, scale, "PNG", quality, resolution, resolutionUnit); +} + +/*! + Saves a JPEG image file to \a fileName on disc. The output plot will have the dimensions \a width + and \a height in pixels, multiplied by \a scale. If either \a width or \a height is zero, the + current width and height of the QCustomPlot widget is used instead. Line widths and texts etc. + are not scaled up when larger widths/heights are used. If you want that effect, use the \a scale + parameter. + + For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an + image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths, + texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full + 200*200 pixel resolution. + + If you use a high scaling factor, it is recommended to enable antialiasing for all elements by + temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows + QCustomPlot to place objects with sub-pixel accuracy. + + image compression can be controlled with the \a quality parameter which must be between 0 and 100 + or -1 to use the default setting. + + The \a resolution will be written to the image file header and has no direct consequence for the + quality or the pixel size. However, if opening the image with a tool which respects the metadata, + it will be able to scale the image to match either a given size in real units of length (inch, + centimeters, etc.), or the target display DPI. You can specify in which units \a resolution is + given, by setting \a resolutionUnit. The \a resolution is converted to the format's expected + resolution unit internally. + + Returns true on success. If this function fails, most likely the JPEG format isn't supported by + the system, see Qt docs about QImageWriter::supportedImageFormats(). + + The objects of the plot will appear in the current selection state. If you don't want any selected + objects to be painted in their selected look, deselect everything with \ref deselectAll before calling + this function. + + \warning If calling this function inside the constructor of the parent of the QCustomPlot widget + (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide + explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this + function uses the current width and height of the QCustomPlot widget. However, in Qt, these + aren't defined yet inside the constructor, so you would get an image that has strange + widths/heights. + + \see savePdf, savePng, saveBmp, saveRastered +*/ +bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit) +{ + return saveRastered(fileName, width, height, scale, "JPG", quality, resolution, resolutionUnit); +} + +/*! + Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width + and \a height in pixels, multiplied by \a scale. If either \a width or \a height is zero, the + current width and height of the QCustomPlot widget is used instead. Line widths and texts etc. + are not scaled up when larger widths/heights are used. If you want that effect, use the \a scale + parameter. + + For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an + image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths, + texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full + 200*200 pixel resolution. + + If you use a high scaling factor, it is recommended to enable antialiasing for all elements by + temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows + QCustomPlot to place objects with sub-pixel accuracy. + + The \a resolution will be written to the image file header and has no direct consequence for the + quality or the pixel size. However, if opening the image with a tool which respects the metadata, + it will be able to scale the image to match either a given size in real units of length (inch, + centimeters, etc.), or the target display DPI. You can specify in which units \a resolution is + given, by setting \a resolutionUnit. The \a resolution is converted to the format's expected + resolution unit internally. + + Returns true on success. If this function fails, most likely the BMP format isn't supported by + the system, see Qt docs about QImageWriter::supportedImageFormats(). + + The objects of the plot will appear in the current selection state. If you don't want any selected + objects to be painted in their selected look, deselect everything with \ref deselectAll before calling + this function. + + \warning If calling this function inside the constructor of the parent of the QCustomPlot widget + (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide + explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this + function uses the current width and height of the QCustomPlot widget. However, in Qt, these + aren't defined yet inside the constructor, so you would get an image that has strange + widths/heights. + + \see savePdf, savePng, saveJpg, saveRastered +*/ +bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale, int resolution, QCP::ResolutionUnit resolutionUnit) +{ + return saveRastered(fileName, width, height, scale, "BMP", -1, resolution, resolutionUnit); +} + +/*! \internal + + Returns a minimum size hint that corresponds to the minimum size of the top level layout + (\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width zero, set a minimum + size (setMinimumSize) either on the whole QCustomPlot or on any layout elements inside the plot. + This is especially important, when placed in a QLayout where other components try to take in as + much space as possible (e.g. QMdiArea). +*/ +QSize QCustomPlot::minimumSizeHint() const +{ + return mPlotLayout->minimumOuterSizeHint(); +} + +/*! \internal + + Returns a size hint that is the same as \ref minimumSizeHint. + +*/ +QSize QCustomPlot::sizeHint() const +{ + return mPlotLayout->minimumOuterSizeHint(); +} + +/*! \internal + + Event handler for when the QCustomPlot widget needs repainting. This does not cause a \ref replot, but + draws the internal buffer on the widget surface. +*/ +void QCustomPlot::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + // detect if the device pixel ratio has changed (e.g. moving window between different DPI screens), and adapt buffers if necessary: +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED +# ifdef QCP_DEVICEPIXELRATIO_FLOAT + double newDpr = devicePixelRatioF(); +# else + double newDpr = devicePixelRatio(); +# endif + if (!qFuzzyCompare(mBufferDevicePixelRatio, newDpr)) + { + setBufferDevicePixelRatio(newDpr); + replot(QCustomPlot::rpQueuedRefresh); + return; + } +#endif + + QCPPainter painter(this); + if (painter.isActive()) + { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + painter.setRenderHint(QPainter::Antialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem +#endif + if (mBackgroundBrush.style() != Qt::NoBrush) + painter.fillRect(mViewport, mBackgroundBrush); + drawBackground(&painter); + foreach (QSharedPointer buffer, mPaintBuffers) + buffer->draw(&painter); + } +} + +/*! \internal + + Event handler for a resize of the QCustomPlot widget. The viewport (which becomes the outer rect + of mPlotLayout) is resized appropriately. Finally a \ref replot is performed. +*/ +void QCustomPlot::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event) + // resize and repaint the buffer: + setViewport(rect()); + replot(rpQueuedRefresh); // queued refresh is important here, to prevent painting issues in some contexts (e.g. MDI subwindow) +} + +/*! \internal + + Event handler for when a double click occurs. Emits the \ref mouseDoubleClick signal, then + determines the layerable under the cursor and forwards the event to it. Finally, emits the + specialized signals when certain objecs are clicked (e.g. \ref plottableDoubleClick, \ref + axisDoubleClick, etc.). + + \see mousePressEvent, mouseReleaseEvent +*/ +void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) +{ + emit mouseDoubleClick(event); + mMouseHasMoved = false; + mMousePressPos = event->pos(); + + // determine layerable under the cursor (this event is called instead of the second press event in a double-click): + QList details; + QList candidates = layerableListAt(mMousePressPos, false, &details); + for (int i=0; iaccept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list + candidates.at(i)->mouseDoubleClickEvent(event, details.at(i)); + if (event->isAccepted()) + { + mMouseEventLayerable = candidates.at(i); + mMouseEventLayerableDetails = details.at(i); + break; + } + } + + // emit specialized object double click signals: + if (!candidates.isEmpty()) + { + if (QCPAbstractPlottable *ap = qobject_cast(candidates.first())) + { + int dataIndex = 0; + if (!details.first().value().isEmpty()) + dataIndex = details.first().value().dataRange().begin(); + emit plottableDoubleClick(ap, dataIndex, event); + } else if (QCPAxis *ax = qobject_cast(candidates.first())) + emit axisDoubleClick(ax, details.first().value(), event); + else if (QCPAbstractItem *ai = qobject_cast(candidates.first())) + emit itemDoubleClick(ai, event); + else if (QCPLegend *lg = qobject_cast(candidates.first())) + emit legendDoubleClick(lg, nullptr, event); + else if (QCPAbstractLegendItem *li = qobject_cast(candidates.first())) + emit legendDoubleClick(li->parentLegend(), li, event); + } + + event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event. +} + +/*! \internal + + Event handler for when a mouse button is pressed. Emits the mousePress signal. + + If the current \ref setSelectionRectMode is not \ref QCP::srmNone, passes the event to the + selection rect. Otherwise determines the layerable under the cursor and forwards the event to it. + + \see mouseMoveEvent, mouseReleaseEvent +*/ +void QCustomPlot::mousePressEvent(QMouseEvent *event) +{ + emit mousePress(event); + // save some state to tell in releaseEvent whether it was a click: + mMouseHasMoved = false; + mMousePressPos = event->pos(); + + if (mSelectionRect && mSelectionRectMode != QCP::srmNone) + { + if (mSelectionRectMode != QCP::srmZoom || qobject_cast(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect + mSelectionRect->startSelection(event); + } else + { + // no selection rect interaction, prepare for click signal emission and forward event to layerable under the cursor: + QList details; + QList candidates = layerableListAt(mMousePressPos, false, &details); + if (!candidates.isEmpty()) + { + mMouseSignalLayerable = candidates.first(); // candidate for signal emission is always topmost hit layerable (signal emitted in release event) + mMouseSignalLayerableDetails = details.first(); + } + // forward event to topmost candidate which accepts the event: + for (int i=0; iaccept(); // default impl of QCPLayerable's mouse events call ignore() on the event, in that case propagate to next candidate in list + candidates.at(i)->mousePressEvent(event, details.at(i)); + if (event->isAccepted()) + { + mMouseEventLayerable = candidates.at(i); + mMouseEventLayerableDetails = details.at(i); + break; + } + } + } + + event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event. +} + +/*! \internal + + Event handler for when the cursor is moved. Emits the \ref mouseMove signal. + + If the selection rect (\ref setSelectionRect) is currently active, the event is forwarded to it + in order to update the rect geometry. + + Otherwise, if a layout element has mouse capture focus (a mousePressEvent happened on top of the + layout element before), the mouseMoveEvent is forwarded to that element. + + \see mousePressEvent, mouseReleaseEvent +*/ +void QCustomPlot::mouseMoveEvent(QMouseEvent *event) +{ + emit mouseMove(event); + + if (!mMouseHasMoved && (mMousePressPos-event->pos()).manhattanLength() > 3) + mMouseHasMoved = true; // moved too far from mouse press position, don't handle as click on mouse release + + if (mSelectionRect && mSelectionRect->isActive()) + mSelectionRect->moveSelection(event); + else if (mMouseEventLayerable) // call event of affected layerable: + mMouseEventLayerable->mouseMoveEvent(event, mMousePressPos); + + event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event. +} + +/*! \internal + + Event handler for when a mouse button is released. Emits the \ref mouseRelease signal. + + If the mouse was moved less than a certain threshold in any direction since the \ref + mousePressEvent, it is considered a click which causes the selection mechanism (if activated via + \ref setInteractions) to possibly change selection states accordingly. Further, specialized mouse + click signals are emitted (e.g. \ref plottableClick, \ref axisClick, etc.) + + If a layerable is the mouse capturer (a \ref mousePressEvent happened on top of the layerable + before), the \ref mouseReleaseEvent is forwarded to that element. + + \see mousePressEvent, mouseMoveEvent +*/ +void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) +{ + emit mouseRelease(event); + + if (!mMouseHasMoved) // mouse hasn't moved (much) between press and release, so handle as click + { + if (mSelectionRect && mSelectionRect->isActive()) // a simple click shouldn't successfully finish a selection rect, so cancel it here + mSelectionRect->cancel(); + if (event->button() == Qt::LeftButton) + processPointSelection(event); + + // emit specialized click signals of QCustomPlot instance: + if (QCPAbstractPlottable *ap = qobject_cast(mMouseSignalLayerable)) + { + int dataIndex = 0; + if (!mMouseSignalLayerableDetails.value().isEmpty()) + dataIndex = mMouseSignalLayerableDetails.value().dataRange().begin(); + emit plottableClick(ap, dataIndex, event); + } else if (QCPAxis *ax = qobject_cast(mMouseSignalLayerable)) + emit axisClick(ax, mMouseSignalLayerableDetails.value(), event); + else if (QCPAbstractItem *ai = qobject_cast(mMouseSignalLayerable)) + emit itemClick(ai, event); + else if (QCPLegend *lg = qobject_cast(mMouseSignalLayerable)) + emit legendClick(lg, nullptr, event); + else if (QCPAbstractLegendItem *li = qobject_cast(mMouseSignalLayerable)) + emit legendClick(li->parentLegend(), li, event); + mMouseSignalLayerable = nullptr; + } + + if (mSelectionRect && mSelectionRect->isActive()) // Note: if a click was detected above, the selection rect is canceled there + { + // finish selection rect, the appropriate action will be taken via signal-slot connection: + mSelectionRect->endSelection(event); + } else + { + // call event of affected layerable: + if (mMouseEventLayerable) + { + mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos); + mMouseEventLayerable = nullptr; + } + } + + if (noAntialiasingOnDrag()) + replot(rpQueuedReplot); + + event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event. +} + +/*! \internal + + Event handler for mouse wheel events. First, the \ref mouseWheel signal is emitted. Then + determines the affected layerable and forwards the event to it. +*/ +void QCustomPlot::wheelEvent(QWheelEvent *event) +{ + emit mouseWheel(event); + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPointF pos = event->pos(); +#else + const QPointF pos = event->position(); +#endif + + // forward event to layerable under cursor: + foreach (QCPLayerable *candidate, layerableListAt(pos, false)) + { + event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list + candidate->wheelEvent(event); + if (event->isAccepted()) + break; + } + event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event. +} + +/*! \internal + + This function draws the entire plot, including background pixmap, with the specified \a painter. + It does not make use of the paint buffers like \ref replot, so this is the function typically + used by saving/exporting methods such as \ref savePdf or \ref toPainter. + + Note that it does not fill the background with the background brush (as the user may specify with + \ref setBackground(const QBrush &brush)), this is up to the respective functions calling this + method. +*/ +void QCustomPlot::draw(QCPPainter *painter) +{ + updateLayout(); + + // draw viewport background pixmap: + drawBackground(painter); + + // draw all layered objects (grid, axes, plottables, items, legend,...): + foreach (QCPLayer *layer, mLayers) + layer->draw(painter); + + /* Debug code to draw all layout element rects + foreach (QCPLayoutElement *el, findChildren()) + { + painter->setBrush(Qt::NoBrush); + painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine)); + painter->drawRect(el->rect()); + painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine)); + painter->drawRect(el->outerRect()); + } + */ +} + +/*! \internal + + Performs the layout update steps defined by \ref QCPLayoutElement::UpdatePhase, by calling \ref + QCPLayoutElement::update on the main plot layout. + + Here, the layout elements calculate their positions and margins, and prepare for the following + draw call. +*/ +void QCustomPlot::updateLayout() +{ + // run through layout phases: + mPlotLayout->update(QCPLayoutElement::upPreparation); + mPlotLayout->update(QCPLayoutElement::upMargins); + mPlotLayout->update(QCPLayoutElement::upLayout); + + emit afterLayout(); +} + +/*! \internal + + Draws the viewport background pixmap of the plot. + + If a pixmap was provided via \ref setBackground, this function buffers the scaled version + depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside + the viewport with the provided \a painter. The scaled version is buffered in + mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when + the axis rect has changed in a way that requires a rescale of the background pixmap (this is + dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was + set. + + Note that this function does not draw a fill with the background brush + (\ref setBackground(const QBrush &brush)) beneath the pixmap. + + \see setBackground, setBackgroundScaled, setBackgroundScaledMode +*/ +void QCustomPlot::drawBackground(QCPPainter *painter) +{ + // Note: background color is handled in individual replot/save functions + + // draw background pixmap (on top of fill, if brush specified): + if (!mBackgroundPixmap.isNull()) + { + if (mBackgroundScaled) + { + // check whether mScaledBackground needs to be updated: + QSize scaledSize(mBackgroundPixmap.size()); + scaledSize.scale(mViewport.size(), mBackgroundScaledMode); + if (mScaledBackgroundPixmap.size() != scaledSize) + mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation); + painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect()); + } else + { + painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height())); + } + } +} + +/*! \internal + + Goes through the layers and makes sure this QCustomPlot instance holds the correct number of + paint buffers and that they have the correct configuration (size, pixel ratio, etc.). + Allocations, reallocations and deletions of paint buffers are performed as necessary. It also + associates the paint buffers with the layers, so they draw themselves into the right buffer when + \ref QCPLayer::drawToPaintBuffer is called. This means it associates adjacent \ref + QCPLayer::lmLogical layers to a mutual paint buffer and creates dedicated paint buffers for + layers in \ref QCPLayer::lmBuffered mode. + + This method uses \ref createPaintBuffer to create new paint buffers. + + After this method, the paint buffers are empty (filled with \c Qt::transparent) and invalidated + (so an attempt to replot only a single buffered layer causes a full replot). + + This method is called in every \ref replot call, prior to actually drawing the layers (into their + associated paint buffer). If the paint buffers don't need changing/reallocating, this method + basically leaves them alone and thus finishes very fast. +*/ +void QCustomPlot::setupPaintBuffers() +{ + int bufferIndex = 0; + if (mPaintBuffers.isEmpty()) + mPaintBuffers.append(QSharedPointer(createPaintBuffer())); + + for (int layerIndex = 0; layerIndex < mLayers.size(); ++layerIndex) + { + QCPLayer *layer = mLayers.at(layerIndex); + if (layer->mode() == QCPLayer::lmLogical) + { + layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef(); + } else if (layer->mode() == QCPLayer::lmBuffered) + { + ++bufferIndex; + if (bufferIndex >= mPaintBuffers.size()) + mPaintBuffers.append(QSharedPointer(createPaintBuffer())); + layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef(); + if (layerIndex < mLayers.size()-1 && mLayers.at(layerIndex+1)->mode() == QCPLayer::lmLogical) // not last layer, and next one is logical, so prepare another buffer for next layerables + { + ++bufferIndex; + if (bufferIndex >= mPaintBuffers.size()) + mPaintBuffers.append(QSharedPointer(createPaintBuffer())); + } + } + } + // remove unneeded buffers: + while (mPaintBuffers.size()-1 > bufferIndex) + mPaintBuffers.removeLast(); + // resize buffers to viewport size and clear contents: + foreach (QSharedPointer buffer, mPaintBuffers) + { + buffer->setSize(viewport().size()); // won't do anything if already correct size + buffer->clear(Qt::transparent); + buffer->setInvalidated(); + } +} + +/*! \internal + + This method is used by \ref setupPaintBuffers when it needs to create new paint buffers. + + Depending on the current setting of \ref setOpenGl, and the current Qt version, different + backends (subclasses of \ref QCPAbstractPaintBuffer) are created, initialized with the proper + size and device pixel ratio, and returned. +*/ +QCPAbstractPaintBuffer *QCustomPlot::createPaintBuffer() +{ + if (mOpenGl) + { +#if defined(QCP_OPENGL_FBO) + return new QCPPaintBufferGlFbo(viewport().size(), mBufferDevicePixelRatio, mGlContext, mGlPaintDevice); +#elif defined(QCP_OPENGL_PBUFFER) + return new QCPPaintBufferGlPbuffer(viewport().size(), mBufferDevicePixelRatio, mOpenGlMultisamples); +#else + qDebug() << Q_FUNC_INFO << "OpenGL enabled even though no support for it compiled in, this shouldn't have happened. Falling back to pixmap paint buffer."; + return new QCPPaintBufferPixmap(viewport().size(), mBufferDevicePixelRatio); +#endif + } else + return new QCPPaintBufferPixmap(viewport().size(), mBufferDevicePixelRatio); +} + +/*! + This method returns whether any of the paint buffers held by this QCustomPlot instance are + invalidated. + + If any buffer is invalidated, a partial replot (\ref QCPLayer::replot) is not allowed and always + causes a full replot (\ref QCustomPlot::replot) of all layers. This is the case when for example + the layer order has changed, new layers were added or removed, layer modes were changed (\ref + QCPLayer::setMode), or layerables were added or removed. + + \see QCPAbstractPaintBuffer::setInvalidated +*/ +bool QCustomPlot::hasInvalidatedPaintBuffers() +{ + foreach (QSharedPointer buffer, mPaintBuffers) + { + if (buffer->invalidated()) + return true; + } + return false; +} + +/*! \internal + + When \ref setOpenGl is set to true, this method is used to initialize OpenGL (create a context, + surface, paint device). + + Returns true on success. + + If this method is successful, all paint buffers should be deleted and then reallocated by calling + \ref setupPaintBuffers, so the OpenGL-based paint buffer subclasses (\ref + QCPPaintBufferGlPbuffer, \ref QCPPaintBufferGlFbo) are used for subsequent replots. + + \see freeOpenGl +*/ +bool QCustomPlot::setupOpenGl() +{ +#ifdef QCP_OPENGL_FBO + freeOpenGl(); + QSurfaceFormat proposedSurfaceFormat; + proposedSurfaceFormat.setSamples(mOpenGlMultisamples); +#ifdef QCP_OPENGL_OFFSCREENSURFACE + QOffscreenSurface *surface = new QOffscreenSurface; +#else + QWindow *surface = new QWindow; + surface->setSurfaceType(QSurface::OpenGLSurface); +#endif + surface->setFormat(proposedSurfaceFormat); + surface->create(); + mGlSurface = QSharedPointer(surface); + mGlContext = QSharedPointer(new QOpenGLContext); + mGlContext->setFormat(mGlSurface->format()); + if (!mGlContext->create()) + { + qDebug() << Q_FUNC_INFO << "Failed to create OpenGL context"; + mGlContext.clear(); + mGlSurface.clear(); + return false; + } + if (!mGlContext->makeCurrent(mGlSurface.data())) // context needs to be current to create paint device + { + qDebug() << Q_FUNC_INFO << "Failed to make opengl context current"; + mGlContext.clear(); + mGlSurface.clear(); + return false; + } + if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) + { + qDebug() << Q_FUNC_INFO << "OpenGL of this system doesn't support frame buffer objects"; + mGlContext.clear(); + mGlSurface.clear(); + return false; + } + mGlPaintDevice = QSharedPointer(new QOpenGLPaintDevice); + return true; +#elif defined(QCP_OPENGL_PBUFFER) + return QGLFormat::hasOpenGL(); +#else + return false; +#endif +} + +/*! \internal + + When \ref setOpenGl is set to false, this method is used to deinitialize OpenGL (releases the + context and frees resources). + + After OpenGL is disabled, all paint buffers should be deleted and then reallocated by calling + \ref setupPaintBuffers, so the standard software rendering paint buffer subclass (\ref + QCPPaintBufferPixmap) is used for subsequent replots. + + \see setupOpenGl +*/ +void QCustomPlot::freeOpenGl() +{ +#ifdef QCP_OPENGL_FBO + mGlPaintDevice.clear(); + mGlContext.clear(); + mGlSurface.clear(); +#endif +} + +/*! \internal + + This method is used by \ref QCPAxisRect::removeAxis to report removed axes to the QCustomPlot + so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and yAxis2 members accordingly. +*/ +void QCustomPlot::axisRemoved(QCPAxis *axis) +{ + if (xAxis == axis) + xAxis = nullptr; + if (xAxis2 == axis) + xAxis2 = nullptr; + if (yAxis == axis) + yAxis = nullptr; + if (yAxis2 == axis) + yAxis2 = nullptr; + + // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers +} + +/*! \internal + + This method is used by the QCPLegend destructor to report legend removal to the QCustomPlot so + it may clear its QCustomPlot::legend member accordingly. +*/ +void QCustomPlot::legendRemoved(QCPLegend *legend) +{ + if (this->legend == legend) + this->legend = nullptr; +} + +/*! \internal + + This slot is connected to the selection rect's \ref QCPSelectionRect::accepted signal when \ref + setSelectionRectMode is set to \ref QCP::srmSelect. + + First, it determines which axis rect was the origin of the selection rect judging by the starting + point of the selection. Then it goes through the plottables (\ref QCPAbstractPlottable1D to be + precise) associated with that axis rect and finds the data points that are in \a rect. It does + this by querying their \ref QCPAbstractPlottable1D::selectTestRect method. + + Then, the actual selection is done by calling the plottables' \ref + QCPAbstractPlottable::selectEvent, placing the found selected data points in the \a details + parameter as QVariant(QCPDataSelection). All plottables that weren't touched by \a + rect receive a \ref QCPAbstractPlottable::deselectEvent. + + \see processRectZoom +*/ +void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event) +{ + typedef QPair SelectionCandidate; + typedef QMultiMap SelectionCandidates; // map key is number of selected data points, so we have selections sorted by size + + bool selectionStateChanged = false; + + if (mInteractions.testFlag(QCP::iSelectPlottables)) + { + SelectionCandidates potentialSelections; + QRectF rectF(rect.normalized()); + if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft())) + { + // determine plottables that were hit by the rect and thus are candidates for selection: + foreach (QCPAbstractPlottable *plottable, affectedAxisRect->plottables()) + { + if (QCPPlottableInterface1D *plottableInterface = plottable->interface1D()) + { + QCPDataSelection dataSel = plottableInterface->selectTestRect(rectF, true); + if (!dataSel.isEmpty()) + potentialSelections.insert(dataSel.dataPointCount(), SelectionCandidate(plottable, dataSel)); + } + } + + if (!mInteractions.testFlag(QCP::iMultiSelect)) + { + // only leave plottable with most selected points in map, since we will only select a single plottable: + if (!potentialSelections.isEmpty()) + { + SelectionCandidates::iterator it = potentialSelections.begin(); + while (it != std::prev(potentialSelections.end())) // erase all except last element + it = potentialSelections.erase(it); + } + } + + bool additive = event->modifiers().testFlag(mMultiSelectModifier); + // deselect all other layerables if not additive selection: + if (!additive) + { + // emit deselection except to those plottables who will be selected afterwards: + foreach (QCPLayer *layer, mLayers) + { + foreach (QCPLayerable *layerable, layer->children()) + { + if ((potentialSelections.isEmpty() || potentialSelections.constBegin()->first != layerable) && mInteractions.testFlag(layerable->selectionCategory())) + { + bool selChanged = false; + layerable->deselectEvent(&selChanged); + selectionStateChanged |= selChanged; + } + } + } + } + + // go through selections in reverse (largest selection first) and emit select events: + SelectionCandidates::const_iterator it = potentialSelections.constEnd(); + while (it != potentialSelections.constBegin()) + { + --it; + if (mInteractions.testFlag(it.value().first->selectionCategory())) + { + bool selChanged = false; + it.value().first->selectEvent(event, additive, QVariant::fromValue(it.value().second), &selChanged); + selectionStateChanged |= selChanged; + } + } + } + } + + if (selectionStateChanged) + { + emit selectionChangedByUser(); + replot(rpQueuedReplot); + } else if (mSelectionRect) + mSelectionRect->layer()->replot(); +} + +/*! \internal + + This slot is connected to the selection rect's \ref QCPSelectionRect::accepted signal when \ref + setSelectionRectMode is set to \ref QCP::srmZoom. + + It determines which axis rect was the origin of the selection rect judging by the starting point + of the selection, and then zooms the axes defined via \ref QCPAxisRect::setRangeZoomAxes to the + provided \a rect (see \ref QCPAxisRect::zoom). + + \see processRectSelection +*/ +void QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event) +{ + Q_UNUSED(event) + if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft())) + { + QList affectedAxes = QList() << axisRect->rangeZoomAxes(Qt::Horizontal) << axisRect->rangeZoomAxes(Qt::Vertical); + affectedAxes.removeAll(static_cast(nullptr)); + axisRect->zoom(QRectF(rect), affectedAxes); + } + replot(rpQueuedReplot); // always replot to make selection rect disappear +} + +/*! \internal + + This method is called when a simple left mouse click was detected on the QCustomPlot surface. + + It first determines the layerable that was hit by the click, and then calls its \ref + QCPLayerable::selectEvent. All other layerables receive a QCPLayerable::deselectEvent (unless the + multi-select modifier was pressed, see \ref setMultiSelectModifier). + + In this method the hit layerable is determined a second time using \ref layerableAt (after the + one in \ref mousePressEvent), because we want \a onlySelectable set to true this time. This + implies that the mouse event grabber (mMouseEventLayerable) may be a different one from the + clicked layerable determined here. For example, if a non-selectable layerable is in front of a + selectable layerable at the click position, the front layerable will receive mouse events but the + selectable one in the back will receive the \ref QCPLayerable::selectEvent. + + \see processRectSelection, QCPLayerable::selectTest +*/ +void QCustomPlot::processPointSelection(QMouseEvent *event) +{ + QVariant details; + QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details); + bool selectionStateChanged = false; + bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier); + // deselect all other layerables if not additive selection: + if (!additive) + { + foreach (QCPLayer *layer, mLayers) + { + foreach (QCPLayerable *layerable, layer->children()) + { + if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory())) + { + bool selChanged = false; + layerable->deselectEvent(&selChanged); + selectionStateChanged |= selChanged; + } + } + } + } + if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory())) + { + // a layerable was actually clicked, call its selectEvent: + bool selChanged = false; + clickedLayerable->selectEvent(event, additive, details, &selChanged); + selectionStateChanged |= selChanged; + } + if (selectionStateChanged) + { + emit selectionChangedByUser(); + replot(rpQueuedReplot); + } +} + +/*! \internal + + Registers the specified plottable with this QCustomPlot and, if \ref setAutoAddPlottableToLegend + is enabled, adds it to the legend (QCustomPlot::legend). QCustomPlot takes ownership of the + plottable. + + Returns true on success, i.e. when \a plottable isn't already in this plot and the parent plot of + \a plottable is this QCustomPlot. + + This method is called automatically in the QCPAbstractPlottable base class constructor. +*/ +bool QCustomPlot::registerPlottable(QCPAbstractPlottable *plottable) +{ + if (mPlottables.contains(plottable)) + { + qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast(plottable); + return false; + } + if (plottable->parentPlot() != this) + { + qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast(plottable); + return false; + } + + mPlottables.append(plottable); + // possibly add plottable to legend: + if (mAutoAddPlottableToLegend) + plottable->addToLegend(); + if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor) + plottable->setLayer(currentLayer()); + return true; +} + +/*! \internal + + In order to maintain the simplified graph interface of QCustomPlot, this method is called by the + QCPGraph constructor to register itself with this QCustomPlot's internal graph list. Returns true + on success, i.e. if \a graph is valid and wasn't already registered with this QCustomPlot. + + This graph specific registration happens in addition to the call to \ref registerPlottable by the + QCPAbstractPlottable base class. +*/ +bool QCustomPlot::registerGraph(QCPGraph *graph) +{ + if (!graph) + { + qDebug() << Q_FUNC_INFO << "passed graph is zero"; + return false; + } + if (mGraphs.contains(graph)) + { + qDebug() << Q_FUNC_INFO << "graph already registered with this QCustomPlot"; + return false; + } + + mGraphs.append(graph); + return true; +} + + +/*! \internal + + Registers the specified item with this QCustomPlot. QCustomPlot takes ownership of the item. + + Returns true on success, i.e. when \a item wasn't already in the plot and the parent plot of \a + item is this QCustomPlot. + + This method is called automatically in the QCPAbstractItem base class constructor. +*/ +bool QCustomPlot::registerItem(QCPAbstractItem *item) +{ + if (mItems.contains(item)) + { + qDebug() << Q_FUNC_INFO << "item already added to this QCustomPlot:" << reinterpret_cast(item); + return false; + } + if (item->parentPlot() != this) + { + qDebug() << Q_FUNC_INFO << "item not created with this QCustomPlot as parent:" << reinterpret_cast(item); + return false; + } + + mItems.append(item); + if (!item->layer()) // usually the layer is already set in the constructor of the item (via QCPLayerable constructor) + item->setLayer(currentLayer()); + return true; +} + +/*! \internal + + Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This method is thus called + after every operation that changes the layer indices, like layer removal, layer creation, layer + moving. +*/ +void QCustomPlot::updateLayerIndices() const +{ + for (int i=0; imIndex = i; +} + +/*! \internal + + Returns the top-most layerable at pixel position \a pos. If \a onlySelectable is set to true, + only those layerables that are selectable will be considered. (Layerable subclasses communicate + their selectability via the QCPLayerable::selectTest method, by returning -1.) + + \a selectionDetails is an output parameter that contains selection specifics of the affected + layerable. This is useful if the respective layerable shall be given a subsequent + QCPLayerable::selectEvent (like in \ref mouseReleaseEvent). \a selectionDetails usually contains + information about which part of the layerable was hit, in multi-part layerables (e.g. + QCPAxis::SelectablePart). If the layerable is a plottable, \a selectionDetails contains a \ref + QCPDataSelection instance with the single data point which is closest to \a pos. + + \see layerableListAt, layoutElementAt, axisRectAt +*/ +QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const +{ + QList details; + QList candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : nullptr); + if (selectionDetails && !details.isEmpty()) + *selectionDetails = details.first(); + if (!candidates.isEmpty()) + return candidates.first(); + else + return nullptr; +} + +/*! \internal + + Returns the layerables at pixel position \a pos. If \a onlySelectable is set to true, only those + layerables that are selectable will be considered. (Layerable subclasses communicate their + selectability via the QCPLayerable::selectTest method, by returning -1.) + + The returned list is sorted by the layerable/drawing order such that the layerable that appears + on top in the plot is at index 0 of the returned list. If you only need to know the top + layerable, rather use \ref layerableAt. + + \a selectionDetails is an output parameter that contains selection specifics of the affected + layerable. This is useful if the respective layerable shall be given a subsequent + QCPLayerable::selectEvent (like in \ref mouseReleaseEvent). \a selectionDetails usually contains + information about which part of the layerable was hit, in multi-part layerables (e.g. + QCPAxis::SelectablePart). If the layerable is a plottable, \a selectionDetails contains a \ref + QCPDataSelection instance with the single data point which is closest to \a pos. + + \see layerableAt, layoutElementAt, axisRectAt +*/ +QList QCustomPlot::layerableListAt(const QPointF &pos, bool onlySelectable, QList *selectionDetails) const +{ + QList result; + for (int layerIndex=static_cast(mLayers.size())-1; layerIndex>=0; --layerIndex) + { + const QList layerables = mLayers.at(layerIndex)->children(); + for (int i=static_cast(layerables.size())-1; i>=0; --i) + { + if (!layerables.at(i)->realVisibility()) + continue; + QVariant details; + double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : nullptr); + if (dist >= 0 && dist < selectionTolerance()) + { + result.append(layerables.at(i)); + if (selectionDetails) + selectionDetails->append(details); + } + } + } + return result; +} + +/*! + Saves the plot to a rastered image file \a fileName in the image format \a format. The plot is + sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and scale 2.0 lead + to a full resolution file with width 200.) If the \a format supports compression, \a quality may + be between 0 and 100 to control it. + + Returns true on success. If this function fails, most likely the given \a format isn't supported + by the system, see Qt docs about QImageWriter::supportedImageFormats(). + + The \a resolution will be written to the image file header (if the file format supports this) and + has no direct consequence for the quality or the pixel size. However, if opening the image with a + tool which respects the metadata, it will be able to scale the image to match either a given size + in real units of length (inch, centimeters, etc.), or the target display DPI. You can specify in + which units \a resolution is given, by setting \a resolutionUnit. The \a resolution is converted + to the format's expected resolution unit internally. + + \see saveBmp, saveJpg, savePng, savePdf +*/ +bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality, int resolution, QCP::ResolutionUnit resolutionUnit) +{ + QImage buffer = toPixmap(width, height, scale).toImage(); + + int dotsPerMeter = 0; + switch (resolutionUnit) + { + case QCP::ruDotsPerMeter: dotsPerMeter = resolution; break; + case QCP::ruDotsPerCentimeter: dotsPerMeter = resolution*100; break; + case QCP::ruDotsPerInch: dotsPerMeter = int(resolution/0.0254); break; + } + buffer.setDotsPerMeterX(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools + buffer.setDotsPerMeterY(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools + if (!buffer.isNull()) + return buffer.save(fileName, format, quality); + else + return false; +} + +/*! + Renders the plot to a pixmap and returns it. + + The plot is sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and + scale 2.0 lead to a full resolution pixmap with width 200.) + + \see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf +*/ +QPixmap QCustomPlot::toPixmap(int width, int height, double scale) +{ + // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too. + int newWidth, newHeight; + if (width == 0 || height == 0) + { + newWidth = this->width(); + newHeight = this->height(); + } else + { + newWidth = width; + newHeight = height; + } + int scaledWidth = qRound(scale*newWidth); + int scaledHeight = qRound(scale*newHeight); + + QPixmap result(scaledWidth, scaledHeight); + result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later + QCPPainter painter; + painter.begin(&result); + if (painter.isActive()) + { + QRect oldViewport = viewport(); + setViewport(QRect(0, 0, newWidth, newHeight)); + painter.setMode(QCPPainter::pmNoCaching); + if (!qFuzzyCompare(scale, 1.0)) + { + if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales + painter.setMode(QCPPainter::pmNonCosmetic); + painter.scale(scale, scale); + } + if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill + painter.fillRect(mViewport, mBackgroundBrush); + draw(&painter); + setViewport(oldViewport); + painter.end(); + } else // might happen if pixmap has width or height zero + { + qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap"; + return QPixmap(); + } + return result; +} + +/*! + Renders the plot using the passed \a painter. + + The plot is sized to \a width and \a height in pixels. If the \a painter's scale is not 1.0, the resulting plot will + appear scaled accordingly. + + \note If you are restricted to using a QPainter (instead of QCPPainter), create a temporary QPicture and open a QCPPainter + on it. Then call \ref toPainter with this QCPPainter. After ending the paint operation on the picture, draw it with + the QPainter. This will reproduce the painter actions the QCPPainter took, with a QPainter. + + \see toPixmap +*/ +void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) +{ + // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too. + int newWidth, newHeight; + if (width == 0 || height == 0) + { + newWidth = this->width(); + newHeight = this->height(); + } else + { + newWidth = width; + newHeight = height; + } + + if (painter->isActive()) + { + QRect oldViewport = viewport(); + setViewport(QRect(0, 0, newWidth, newHeight)); + painter->setMode(QCPPainter::pmNoCaching); + if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here + painter->fillRect(mViewport, mBackgroundBrush); + draw(painter); + setViewport(oldViewport); + } else + qDebug() << Q_FUNC_INFO << "Passed painter is not active"; +} +/* end of 'src/core.cpp' */ + + +/* including file 'src/colorgradient.cpp' */ +/* modified 2022-11-06T12:45:56, size 25408 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorGradient +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorGradient + \brief Defines a color gradient for use with e.g. \ref QCPColorMap + + This class describes a color gradient which can be used to encode data with color. For example, + QCPColorMap and QCPColorScale have \ref QCPColorMap::setGradient "setGradient" methods which + take an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color) + with a \a position from 0 to 1. In between these defined color positions, the + color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation. + + Alternatively, load one of the preset color gradients shown in the image below, with \ref + loadPreset, or by directly specifying the preset in the constructor. + + Apart from red, green and blue components, the gradient also interpolates the alpha values of the + configured color stops. This allows to display some portions of the data range as transparent in + the plot. + + How NaN values are interpreted can be configured with \ref setNanHandling. + + \image html QCPColorGradient.png + + The constructor \ref QCPColorGradient(GradientPreset preset) allows directly converting a \ref + GradientPreset to a QCPColorGradient. This means that you can directly pass \ref GradientPreset + to all the \a setGradient methods, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient + + The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the + color gradient shall be applied periodically (wrapping around) to data values that lie outside + the data range specified on the plottable instance can be controlled with \ref setPeriodic. +*/ + +/*! + Constructs a new, empty QCPColorGradient with no predefined color stops. You can add own color + stops with \ref setColorStopAt. + + The color level count is initialized to 350. +*/ +QCPColorGradient::QCPColorGradient() : + mLevelCount(350), + mColorInterpolation(ciRGB), + mNanHandling(nhNone), + mNanColor(Qt::black), + mPeriodic(false), + mColorBufferInvalidated(true) +{ + mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount); +} + +/*! + Constructs a new QCPColorGradient initialized with the colors and color interpolation according + to \a preset. + + The color level count is initialized to 350. +*/ +QCPColorGradient::QCPColorGradient(GradientPreset preset) : + mLevelCount(350), + mColorInterpolation(ciRGB), + mNanHandling(nhNone), + mNanColor(Qt::black), + mPeriodic(false), + mColorBufferInvalidated(true) +{ + mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount); + loadPreset(preset); +} + +/* undocumented operator */ +bool QCPColorGradient::operator==(const QCPColorGradient &other) const +{ + return ((other.mLevelCount == this->mLevelCount) && + (other.mColorInterpolation == this->mColorInterpolation) && + (other.mNanHandling == this ->mNanHandling) && + (other.mNanColor == this->mNanColor) && + (other.mPeriodic == this->mPeriodic) && + (other.mColorStops == this->mColorStops)); +} + +/*! + Sets the number of discretization levels of the color gradient to \a n. The default is 350 which + is typically enough to create a smooth appearance. The minimum number of levels is 2. + + \image html QCPColorGradient-levelcount.png +*/ +void QCPColorGradient::setLevelCount(int n) +{ + if (n < 2) + { + qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n; + n = 2; + } + if (n != mLevelCount) + { + mLevelCount = n; + mColorBufferInvalidated = true; + } +} + +/*! + Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the + colors are the values of the passed QMap \a colorStops. In between these color stops, the color + is interpolated according to \ref setColorInterpolation. + + A more convenient way to create a custom gradient may be to clear all color stops with \ref + clearColorStops (or creating a new, empty QCPColorGradient) and then adding them one by one with + \ref setColorStopAt. + + \see clearColorStops +*/ +void QCPColorGradient::setColorStops(const QMap &colorStops) +{ + mColorStops = colorStops; + mColorBufferInvalidated = true; +} + +/*! + Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between + these color stops, the color is interpolated according to \ref setColorInterpolation. + + \see setColorStops, clearColorStops +*/ +void QCPColorGradient::setColorStopAt(double position, const QColor &color) +{ + mColorStops.insert(position, color); + mColorBufferInvalidated = true; +} + +/*! + Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be + interpolated linearly in RGB or in HSV color space. + + For example, a sweep in RGB space from red to green will have a muddy brown intermediate color, + whereas in HSV space the intermediate color is yellow. +*/ +void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation) +{ + if (interpolation != mColorInterpolation) + { + mColorInterpolation = interpolation; + mColorBufferInvalidated = true; + } +} + +/*! + Sets how NaNs in the data are displayed in the plot. + + \see setNanColor +*/ +void QCPColorGradient::setNanHandling(QCPColorGradient::NanHandling handling) +{ + mNanHandling = handling; +} + +/*! + Sets the color that NaN data is represented by, if \ref setNanHandling is set + to ref nhNanColor. + + \see setNanHandling +*/ +void QCPColorGradient::setNanColor(const QColor &color) +{ + mNanColor = color; +} + +/*! + Sets whether data points that are outside the configured data range (e.g. \ref + QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether + they all have the same color, corresponding to the respective gradient boundary color. + + \image html QCPColorGradient-periodic.png + + As shown in the image above, gradients that have the same start and end color are especially + suitable for a periodic gradient mapping, since they produce smooth color transitions throughout + the color map. A preset that has this property is \ref gpHues. + + In practice, using periodic color gradients makes sense when the data corresponds to a periodic + dimension, such as an angle or a phase. If this is not the case, the color encoding might become + ambiguous, because multiple different data values are shown as the same color. +*/ +void QCPColorGradient::setPeriodic(bool enabled) +{ + mPeriodic = enabled; +} + +/*! \overload + + This method is used to quickly convert a \a data array to colors. The colors will be output in + the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this + function. The data range that shall be used for mapping the data value to the gradient is passed + in \a range. \a logarithmic indicates whether the data values shall be mapped to colors + logarithmically. + + if \a data actually contains 2D-data linearized via [row*columnCount + column], you can + set \a dataIndexFactor to columnCount to convert a column instead of a row of the data + array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data + is addressed data[i*dataIndexFactor]. + + Use the overloaded method to additionally provide alpha map data. + + The QRgb values that are placed in \a scanLine have their r, g, and b components premultiplied + with alpha (see QImage::Format_ARGB32_Premultiplied). +*/ +void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic) +{ + // If you change something here, make sure to also adapt color() and the other colorize() overload + if (!data) + { + qDebug() << Q_FUNC_INFO << "null pointer given as data"; + return; + } + if (!scanLine) + { + qDebug() << Q_FUNC_INFO << "null pointer given as scanLine"; + return; + } + if (mColorBufferInvalidated) + updateColorBuffer(); + + const bool skipNanCheck = mNanHandling == nhNone; + const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower); + for (int i=0; i(index)); +#else + scanLine[i] = mColorBuffer.at(index); +#endif + } else + { + switch(mNanHandling) + { + case nhLowestColor: scanLine[i] = mColorBuffer.first(); break; + case nhHighestColor: scanLine[i] = mColorBuffer.last(); break; + case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break; + case nhNanColor: scanLine[i] = mNanColor.rgba(); break; + case nhNone: break; // shouldn't happen + } + } + } +} + +/*! \overload + + Additionally to the other overload of \ref colorize, this method takes the array \a alpha, which + has the same size and structure as \a data and encodes the alpha information per data point. + + The QRgb values that are placed in \a scanLine have their r, g and b components premultiplied + with alpha (see QImage::Format_ARGB32_Premultiplied). +*/ +void QCPColorGradient::colorize(const double *data, const unsigned char *alpha, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic) +{ + // If you change something here, make sure to also adapt color() and the other colorize() overload + if (!data) + { + qDebug() << Q_FUNC_INFO << "null pointer given as data"; + return; + } + if (!alpha) + { + qDebug() << Q_FUNC_INFO << "null pointer given as alpha"; + return; + } + if (!scanLine) + { + qDebug() << Q_FUNC_INFO << "null pointer given as scanLine"; + return; + } + if (mColorBufferInvalidated) + updateColorBuffer(); + + const bool skipNanCheck = mNanHandling == nhNone; + const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower); + for (int i=0; i(index)); +#else + scanLine[i] = mColorBuffer.at(index); +#endif + } else + { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + const QRgb rgb = mColorBuffer.at(static_cast(index)); +#else + const QRgb rgb = mColorBuffer.at(index); +#endif + const float alphaF = alpha[dataIndexFactor*i]/255.0f; + scanLine[i] = qRgba(int(qRed(rgb)*alphaF), int(qGreen(rgb)*alphaF), int(qBlue(rgb)*alphaF), int(qAlpha(rgb)*alphaF)); // also multiply r,g,b with alpha, to conform to Format_ARGB32_Premultiplied + } + } else + { + switch(mNanHandling) + { + case nhLowestColor: scanLine[i] = mColorBuffer.first(); break; + case nhHighestColor: scanLine[i] = mColorBuffer.last(); break; + case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break; + case nhNanColor: scanLine[i] = mNanColor.rgba(); break; + case nhNone: break; // shouldn't happen + } + } + } +} + +/*! \internal + + This method is used to colorize a single data value given in \a position, to colors. The data + range that shall be used for mapping the data value to the gradient is passed in \a range. \a + logarithmic indicates whether the data value shall be mapped to a color logarithmically. + + If an entire array of data values shall be converted, rather use \ref colorize, for better + performance. + + The returned QRgb has its r, g and b components premultiplied with alpha (see + QImage::Format_ARGB32_Premultiplied). +*/ +QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic) +{ + // If you change something here, make sure to also adapt ::colorize() + if (mColorBufferInvalidated) + updateColorBuffer(); + + const bool skipNanCheck = mNanHandling == nhNone; + if (!skipNanCheck && std::isnan(position)) + { + switch(mNanHandling) + { + case nhLowestColor: return mColorBuffer.first(); + case nhHighestColor: return mColorBuffer.last(); + case nhTransparent: return qRgba(0, 0, 0, 0); + case nhNanColor: return mNanColor.rgba(); + case nhNone: return qRgba(0, 0, 0, 0); // shouldn't happen + } + } + + const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower); + int index = int((!logarithmic ? position-range.lower : qLn(position/range.lower)) * posToIndexFactor); + if (!mPeriodic) + { + index = qBound(0, index, mLevelCount-1); + } else + { + index %= mLevelCount; + if (index < 0) + index += mLevelCount; + } + return mColorBuffer.at(index); +} + +/*! + Clears the current color stops and loads the specified \a preset. A preset consists of predefined + color stops and the corresponding color interpolation method. + + The available presets are: + \image html QCPColorGradient.png +*/ +void QCPColorGradient::loadPreset(GradientPreset preset) +{ + clearColorStops(); + switch (preset) + { + case gpGrayscale: + setColorInterpolation(ciRGB); + setColorStopAt(0, Qt::black); + setColorStopAt(1, Qt::white); + break; + case gpHot: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(50, 0, 0)); + setColorStopAt(0.2, QColor(180, 10, 0)); + setColorStopAt(0.4, QColor(245, 50, 0)); + setColorStopAt(0.6, QColor(255, 150, 10)); + setColorStopAt(0.8, QColor(255, 255, 50)); + setColorStopAt(1, QColor(255, 255, 255)); + break; + case gpCold: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(0, 0, 50)); + setColorStopAt(0.2, QColor(0, 10, 180)); + setColorStopAt(0.4, QColor(0, 50, 245)); + setColorStopAt(0.6, QColor(10, 150, 255)); + setColorStopAt(0.8, QColor(50, 255, 255)); + setColorStopAt(1, QColor(255, 255, 255)); + break; + case gpNight: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(10, 20, 30)); + setColorStopAt(1, QColor(250, 255, 250)); + break; + case gpCandy: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(0, 0, 255)); + setColorStopAt(1, QColor(255, 250, 250)); + break; + case gpGeography: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(70, 170, 210)); + setColorStopAt(0.20, QColor(90, 160, 180)); + setColorStopAt(0.25, QColor(45, 130, 175)); + setColorStopAt(0.30, QColor(100, 140, 125)); + setColorStopAt(0.5, QColor(100, 140, 100)); + setColorStopAt(0.6, QColor(130, 145, 120)); + setColorStopAt(0.7, QColor(140, 130, 120)); + setColorStopAt(0.9, QColor(180, 190, 190)); + setColorStopAt(1, QColor(210, 210, 230)); + break; + case gpIon: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(50, 10, 10)); + setColorStopAt(0.45, QColor(0, 0, 255)); + setColorStopAt(0.8, QColor(0, 255, 255)); + setColorStopAt(1, QColor(0, 255, 0)); + break; + case gpThermal: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(0, 0, 50)); + setColorStopAt(0.15, QColor(20, 0, 120)); + setColorStopAt(0.33, QColor(200, 30, 140)); + setColorStopAt(0.6, QColor(255, 100, 0)); + setColorStopAt(0.85, QColor(255, 255, 40)); + setColorStopAt(1, QColor(255, 255, 255)); + break; + case gpPolar: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(50, 255, 255)); + setColorStopAt(0.18, QColor(10, 70, 255)); + setColorStopAt(0.28, QColor(10, 10, 190)); + setColorStopAt(0.5, QColor(0, 0, 0)); + setColorStopAt(0.72, QColor(190, 10, 10)); + setColorStopAt(0.82, QColor(255, 70, 10)); + setColorStopAt(1, QColor(255, 255, 50)); + break; + case gpSpectrum: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(50, 0, 50)); + setColorStopAt(0.15, QColor(0, 0, 255)); + setColorStopAt(0.35, QColor(0, 255, 255)); + setColorStopAt(0.6, QColor(255, 255, 0)); + setColorStopAt(0.75, QColor(255, 30, 0)); + setColorStopAt(1, QColor(50, 0, 0)); + break; + case gpJet: + setColorInterpolation(ciRGB); + setColorStopAt(0, QColor(0, 0, 100)); + setColorStopAt(0.15, QColor(0, 50, 255)); + setColorStopAt(0.35, QColor(0, 255, 255)); + setColorStopAt(0.65, QColor(255, 255, 0)); + setColorStopAt(0.85, QColor(255, 30, 0)); + setColorStopAt(1, QColor(100, 0, 0)); + break; + case gpHues: + setColorInterpolation(ciHSV); + setColorStopAt(0, QColor(255, 0, 0)); + setColorStopAt(1.0/3.0, QColor(0, 0, 255)); + setColorStopAt(2.0/3.0, QColor(0, 255, 0)); + setColorStopAt(1, QColor(255, 0, 0)); + break; + } +} + +/*! + Clears all color stops. + + \see setColorStops, setColorStopAt +*/ +void QCPColorGradient::clearColorStops() +{ + mColorStops.clear(); + mColorBufferInvalidated = true; +} + +/*! + Returns an inverted gradient. The inverted gradient has all properties as this \ref + QCPColorGradient, but the order of the color stops is inverted. + + \see setColorStops, setColorStopAt +*/ +QCPColorGradient QCPColorGradient::inverted() const +{ + QCPColorGradient result(*this); + result.clearColorStops(); + for (QMap::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it) + result.setColorStopAt(1.0-it.key(), it.value()); + return result; +} + +/*! \internal + + Returns true if the color gradient uses transparency, i.e. if any of the configured color stops + has an alpha value below 255. +*/ +bool QCPColorGradient::stopsUseAlpha() const +{ + for (QMap::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it) + { + if (it.value().alpha() < 255) + return true; + } + return false; +} + +/*! \internal + + Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly + convert positions to colors. This is where the interpolation between color stops is calculated. +*/ +void QCPColorGradient::updateColorBuffer() +{ + if (mColorBuffer.size() != mLevelCount) + mColorBuffer.resize(mLevelCount); + if (mColorStops.size() > 1) + { + double indexToPosFactor = 1.0/double(mLevelCount-1); + const bool useAlpha = stopsUseAlpha(); + for (int i=0; i::const_iterator it = const_cast*>(&mColorStops)->lowerBound(position); // force using the const lowerBound method + if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop + { + if (useAlpha) + { + const QColor col = std::prev(it).value(); + const double alphaPremultiplier = col.alpha()/255.0; // since we use QImage::Format_ARGB32_Premultiplied + mColorBuffer[i] = qRgba(int(col.red()*alphaPremultiplier), + int(col.green()*alphaPremultiplier), + int(col.blue()*alphaPremultiplier), + col.alpha()); + } else + mColorBuffer[i] = std::prev(it).value().rgba(); + } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop + { + if (useAlpha) + { + const QColor &col = it.value(); + const double alphaPremultiplier = col.alpha()/255.0; // since we use QImage::Format_ARGB32_Premultiplied + mColorBuffer[i] = qRgba(int(col.red()*alphaPremultiplier), + int(col.green()*alphaPremultiplier), + int(col.blue()*alphaPremultiplier), + col.alpha()); + } else + mColorBuffer[i] = it.value().rgba(); + } else // position is in between stops (or on an intermediate stop), interpolate color + { + QMap::const_iterator high = it; + QMap::const_iterator low = std::prev(it); + double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1 + switch (mColorInterpolation) + { + case ciRGB: + { + if (useAlpha) + { + const int alpha = int((1-t)*low.value().alpha() + t*high.value().alpha()); + const double alphaPremultiplier = alpha/255.0; // since we use QImage::Format_ARGB32_Premultiplied + mColorBuffer[i] = qRgba(int( ((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier ), + int( ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier ), + int( ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier ), + alpha); + } else + { + mColorBuffer[i] = qRgb(int( ((1-t)*low.value().red() + t*high.value().red()) ), + int( ((1-t)*low.value().green() + t*high.value().green()) ), + int( ((1-t)*low.value().blue() + t*high.value().blue())) ); + } + break; + } + case ciHSV: + { + QColor lowHsv = low.value().toHsv(); + QColor highHsv = high.value().toHsv(); + double hue = 0; + double hueDiff = highHsv.hueF()-lowHsv.hueF(); + if (hueDiff > 0.5) + hue = lowHsv.hueF() - t*(1.0-hueDiff); + else if (hueDiff < -0.5) + hue = lowHsv.hueF() + t*(1.0+hueDiff); + else + hue = lowHsv.hueF() + t*hueDiff; + if (hue < 0) hue += 1.0; + else if (hue >= 1.0) hue -= 1.0; + if (useAlpha) + { + const QRgb rgb = QColor::fromHsvF(hue, + (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), + (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb(); + const double alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF(); + mColorBuffer[i] = qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha)); + } + else + { + mColorBuffer[i] = QColor::fromHsvF(hue, + (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), + (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb(); + } + break; + } + } + } + } + } else if (mColorStops.size() == 1) + { + const QRgb rgb = mColorStops.constBegin().value().rgb(); + const double alpha = mColorStops.constBegin().value().alphaF(); + mColorBuffer.fill(qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha))); + } else // mColorStops is empty, fill color buffer with black + { + mColorBuffer.fill(qRgb(0, 0, 0)); + } + mColorBufferInvalidated = false; +} +/* end of 'src/colorgradient.cpp' */ + + +/* including file 'src/selectiondecorator-bracket.cpp' */ +/* modified 2022-11-06T12:45:56, size 12308 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPSelectionDecoratorBracket +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPSelectionDecoratorBracket + \brief A selection decorator which draws brackets around each selected data segment + + Additionally to the regular highlighting of selected segments via color, fill and scatter style, + this \ref QCPSelectionDecorator subclass draws markers at the begin and end of each selected data + segment of the plottable. + + The shape of the markers can be controlled with \ref setBracketStyle, \ref setBracketWidth and + \ref setBracketHeight. The color/fill can be controlled with \ref setBracketPen and \ref + setBracketBrush. + + To introduce custom bracket styles, it is only necessary to sublcass \ref + QCPSelectionDecoratorBracket and reimplement \ref drawBracket. The rest will be managed by the + base class. +*/ + +/*! + Creates a new QCPSelectionDecoratorBracket instance with default values. +*/ +QCPSelectionDecoratorBracket::QCPSelectionDecoratorBracket() : + mBracketPen(QPen(Qt::black)), + mBracketBrush(Qt::NoBrush), + mBracketWidth(5), + mBracketHeight(50), + mBracketStyle(bsSquareBracket), + mTangentToData(false), + mTangentAverage(2) +{ + +} + +QCPSelectionDecoratorBracket::~QCPSelectionDecoratorBracket() +{ +} + +/*! + Sets the pen that will be used to draw the brackets at the beginning and end of each selected + data segment. +*/ +void QCPSelectionDecoratorBracket::setBracketPen(const QPen &pen) +{ + mBracketPen = pen; +} + +/*! + Sets the brush that will be used to draw the brackets at the beginning and end of each selected + data segment. +*/ +void QCPSelectionDecoratorBracket::setBracketBrush(const QBrush &brush) +{ + mBracketBrush = brush; +} + +/*! + Sets the width of the drawn bracket. The width dimension is always parallel to the key axis of + the data, or the tangent direction of the current data slope, if \ref setTangentToData is + enabled. +*/ +void QCPSelectionDecoratorBracket::setBracketWidth(int width) +{ + mBracketWidth = width; +} + +/*! + Sets the height of the drawn bracket. The height dimension is always perpendicular to the key axis + of the data, or the tangent direction of the current data slope, if \ref setTangentToData is + enabled. +*/ +void QCPSelectionDecoratorBracket::setBracketHeight(int height) +{ + mBracketHeight = height; +} + +/*! + Sets the shape that the bracket/marker will have. + + \see setBracketWidth, setBracketHeight +*/ +void QCPSelectionDecoratorBracket::setBracketStyle(QCPSelectionDecoratorBracket::BracketStyle style) +{ + mBracketStyle = style; +} + +/*! + Sets whether the brackets will be rotated such that they align with the slope of the data at the + position that they appear in. + + For noisy data, it might be more visually appealing to average the slope over multiple data + points. This can be configured via \ref setTangentAverage. +*/ +void QCPSelectionDecoratorBracket::setTangentToData(bool enabled) +{ + mTangentToData = enabled; +} + +/*! + Controls over how many data points the slope shall be averaged, when brackets shall be aligned + with the data (if \ref setTangentToData is true). + + From the position of the bracket, \a pointCount points towards the selected data range will be + taken into account. The smallest value of \a pointCount is 1, which is effectively equivalent to + disabling \ref setTangentToData. +*/ +void QCPSelectionDecoratorBracket::setTangentAverage(int pointCount) +{ + mTangentAverage = pointCount; + if (mTangentAverage < 1) + mTangentAverage = 1; +} + +/*! + Draws the bracket shape with \a painter. The parameter \a direction is either -1 or 1 and + indicates whether the bracket shall point to the left or the right (i.e. is a closing or opening + bracket, respectively). + + The passed \a painter already contains all transformations that are necessary to position and + rotate the bracket appropriately. Painting operations can be performed as if drawing upright + brackets on flat data with horizontal key axis, with (0, 0) being the center of the bracket. + + If you wish to sublcass \ref QCPSelectionDecoratorBracket in order to provide custom bracket + shapes (see \ref QCPSelectionDecoratorBracket::bsUserStyle), this is the method you should + reimplement. +*/ +void QCPSelectionDecoratorBracket::drawBracket(QCPPainter *painter, int direction) const +{ + switch (mBracketStyle) + { + case bsSquareBracket: + { + painter->drawLine(QLineF(mBracketWidth*direction, -mBracketHeight*0.5, 0, -mBracketHeight*0.5)); + painter->drawLine(QLineF(mBracketWidth*direction, mBracketHeight*0.5, 0, mBracketHeight*0.5)); + painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5)); + break; + } + case bsHalfEllipse: + { + painter->drawArc(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight), -90*16, -180*16*direction); + break; + } + case bsEllipse: + { + painter->drawEllipse(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight)); + break; + } + case bsPlus: + { + painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5)); + painter->drawLine(QLineF(-mBracketWidth*0.5, 0, mBracketWidth*0.5, 0)); + break; + } + default: + { + qDebug() << Q_FUNC_INFO << "unknown/custom bracket style can't be handled by default implementation:" << static_cast(mBracketStyle); + break; + } + } +} + +/*! + Draws the bracket decoration on the data points at the begin and end of each selected data + segment given in \a seletion. + + It uses the method \ref drawBracket to actually draw the shapes. + + \seebaseclassmethod +*/ +void QCPSelectionDecoratorBracket::drawDecoration(QCPPainter *painter, QCPDataSelection selection) +{ + if (!mPlottable || selection.isEmpty()) return; + + if (QCPPlottableInterface1D *interface1d = mPlottable->interface1D()) + { + foreach (const QCPDataRange &dataRange, selection.dataRanges()) + { + // determine position and (if tangent mode is enabled) angle of brackets: + int openBracketDir = (mPlottable->keyAxis() && !mPlottable->keyAxis()->rangeReversed()) ? 1 : -1; + int closeBracketDir = -openBracketDir; + QPointF openBracketPos = getPixelCoordinates(interface1d, dataRange.begin()); + QPointF closeBracketPos = getPixelCoordinates(interface1d, dataRange.end()-1); + double openBracketAngle = 0; + double closeBracketAngle = 0; + if (mTangentToData) + { + openBracketAngle = getTangentAngle(interface1d, dataRange.begin(), openBracketDir); + closeBracketAngle = getTangentAngle(interface1d, dataRange.end()-1, closeBracketDir); + } + // draw opening bracket: + QTransform oldTransform = painter->transform(); + painter->setPen(mBracketPen); + painter->setBrush(mBracketBrush); + painter->translate(openBracketPos); + painter->rotate(openBracketAngle/M_PI*180.0); + drawBracket(painter, openBracketDir); + painter->setTransform(oldTransform); + // draw closing bracket: + painter->setPen(mBracketPen); + painter->setBrush(mBracketBrush); + painter->translate(closeBracketPos); + painter->rotate(closeBracketAngle/M_PI*180.0); + drawBracket(painter, closeBracketDir); + painter->setTransform(oldTransform); + } + } +} + +/*! \internal + + If \ref setTangentToData is enabled, brackets need to be rotated according to the data slope. + This method returns the angle in radians by which a bracket at the given \a dataIndex must be + rotated. + + The parameter \a direction must be set to either -1 or 1, representing whether it is an opening + or closing bracket. Since for slope calculation multiple data points are required, this defines + the direction in which the algorithm walks, starting at \a dataIndex, to average those data + points. (see \ref setTangentToData and \ref setTangentAverage) + + \a interface1d is the interface to the plottable's data which is used to query data coordinates. +*/ +double QCPSelectionDecoratorBracket::getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const +{ + if (!interface1d || dataIndex < 0 || dataIndex >= interface1d->dataCount()) + return 0; + direction = direction < 0 ? -1 : 1; // enforce direction is either -1 or 1 + + // how many steps we can actually go from index in the given direction without exceeding data bounds: + int averageCount; + if (direction < 0) + averageCount = qMin(mTangentAverage, dataIndex); + else + averageCount = qMin(mTangentAverage, interface1d->dataCount()-1-dataIndex); + qDebug() << averageCount; + // calculate point average of averageCount points: + QVector points(averageCount); + QPointF pointsAverage; + int currentIndex = dataIndex; + for (int i=0; ikeyAxis(); + QCPAxis *valueAxis = mPlottable->valueAxis(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {0, 0}; } + + if (keyAxis->orientation() == Qt::Horizontal) + return {keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex))}; + else + return {valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex))}; +} +/* end of 'src/selectiondecorator-bracket.cpp' */ + + +/* including file 'src/layoutelements/layoutelement-axisrect.cpp' */ +/* modified 2022-11-06T12:45:56, size 47193 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAxisRect +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAxisRect + \brief Holds multiple axes and arranges them in a rectangular shape. + + This class represents an axis rect, a rectangular area that is bounded on all sides with an + arbitrary number of axes. + + Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the + layout system allows to have multiple axis rects, e.g. arranged in a grid layout + (QCustomPlot::plotLayout). + + By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be + accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index. + If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be + invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref + addAxes. To remove an axis, use \ref removeAxis. + + The axis rect layerable itself only draws a background pixmap or color, if specified (\ref + setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an + explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be + placed on other layers, independently of the axis rect. + + Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref + insetLayout and can be used to have other layout elements (or even other layouts with multiple + elements) hovering inside the axis rect. + + If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The + behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel + is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable + via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are + only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref + QCP::iRangeZoom. + + \image html AxisRectSpacingOverview.png +
Overview of the spacings and paddings that define the geometry of an axis. The dashed + line on the far left indicates the viewport/widget border.
+*/ + +/* start documentation of inline functions */ + +/*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const + + Returns the inset layout of this axis rect. It can be used to place other layout elements (or + even layouts with multiple other elements) inside/on top of an axis rect. + + \see QCPLayoutInset +*/ + +/*! \fn int QCPAxisRect::left() const + + Returns the pixel position of the left border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::right() const + + Returns the pixel position of the right border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::top() const + + Returns the pixel position of the top border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::bottom() const + + Returns the pixel position of the bottom border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::width() const + + Returns the pixel width of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPAxisRect::height() const + + Returns the pixel height of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QSize QCPAxisRect::size() const + + Returns the pixel size of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::topLeft() const + + Returns the top left corner of this axis rect in pixels. Margins are not taken into account here, + so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::topRight() const + + Returns the top right corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::bottomLeft() const + + Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::bottomRight() const + + Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPAxisRect::center() const + + Returns the center of this axis rect in pixels. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four + sides, the top and right axes are set invisible initially. +*/ +QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) : + QCPLayoutElement(parentPlot), + mBackgroundBrush(Qt::NoBrush), + mBackgroundScaled(true), + mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding), + mInsetLayout(new QCPLayoutInset), + mRangeDrag(Qt::Horizontal|Qt::Vertical), + mRangeZoom(Qt::Horizontal|Qt::Vertical), + mRangeZoomFactorHorz(0.85), + mRangeZoomFactorVert(0.85), + mDragging(false) +{ + mInsetLayout->initializeParentPlot(mParentPlot); + mInsetLayout->setParentLayerable(this); + mInsetLayout->setParent(this); + + setMinimumSize(50, 50); + setMinimumMargins(QMargins(15, 15, 15, 15)); + mAxes.insert(QCPAxis::atLeft, QList()); + mAxes.insert(QCPAxis::atRight, QList()); + mAxes.insert(QCPAxis::atTop, QList()); + mAxes.insert(QCPAxis::atBottom, QList()); + + if (setupDefaultAxes) + { + QCPAxis *xAxis = addAxis(QCPAxis::atBottom); + QCPAxis *yAxis = addAxis(QCPAxis::atLeft); + QCPAxis *xAxis2 = addAxis(QCPAxis::atTop); + QCPAxis *yAxis2 = addAxis(QCPAxis::atRight); + setRangeDragAxes(xAxis, yAxis); + setRangeZoomAxes(xAxis, yAxis); + xAxis2->setVisible(false); + yAxis2->setVisible(false); + xAxis->grid()->setVisible(true); + yAxis->grid()->setVisible(true); + xAxis2->grid()->setVisible(false); + yAxis2->grid()->setVisible(false); + xAxis2->grid()->setZeroLinePen(Qt::NoPen); + yAxis2->grid()->setZeroLinePen(Qt::NoPen); + xAxis2->grid()->setVisible(false); + yAxis2->grid()->setVisible(false); + } +} + +QCPAxisRect::~QCPAxisRect() +{ + delete mInsetLayout; + mInsetLayout = nullptr; + + foreach (QCPAxis *axis, axes()) + removeAxis(axis); +} + +/*! + Returns the number of axes on the axis rect side specified with \a type. + + \see axis +*/ +int QCPAxisRect::axisCount(QCPAxis::AxisType type) const +{ + return static_cast(mAxes.value(type).size()); +} + +/*! + Returns the axis with the given \a index on the axis rect side specified with \a type. + + \see axisCount, axes +*/ +QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const +{ + QList ax(mAxes.value(type)); + if (index >= 0 && index < ax.size()) + { + return ax.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index; + return nullptr; + } +} + +/*! + Returns all axes on the axis rect sides specified with \a types. + + \a types may be a single \ref QCPAxis::AxisType or an or-combination, to get the axes of + multiple sides. + + \see axis +*/ +QList QCPAxisRect::axes(QCPAxis::AxisTypes types) const +{ + QList result; + if (types.testFlag(QCPAxis::atLeft)) + result << mAxes.value(QCPAxis::atLeft); + if (types.testFlag(QCPAxis::atRight)) + result << mAxes.value(QCPAxis::atRight); + if (types.testFlag(QCPAxis::atTop)) + result << mAxes.value(QCPAxis::atTop); + if (types.testFlag(QCPAxis::atBottom)) + result << mAxes.value(QCPAxis::atBottom); + return result; +} + +/*! \overload + + Returns all axes of this axis rect. +*/ +QList QCPAxisRect::axes() const +{ + QList result; + QHashIterator > it(mAxes); + while (it.hasNext()) + { + it.next(); + result << it.value(); + } + return result; +} + +/*! + Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a + new QCPAxis instance is created internally. QCustomPlot owns the returned axis, so if you want to + remove an axis, use \ref removeAxis instead of deleting it manually. + + You may inject QCPAxis instances (or subclasses of QCPAxis) by setting \a axis to an axis that was + previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership + of the axis, so you may not delete it afterwards. Further, the \a axis must have been created + with this axis rect as parent and with the same axis type as specified in \a type. If this is not + the case, a debug output is generated, the axis is not added, and the method returns \c nullptr. + + This method can not be used to move \a axis between axis rects. The same \a axis instance must + not be added multiple times to the same or different axis rects. + + If an axis rect side already contains one or more axes, the lower and upper endings of the new + axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref + QCPLineEnding::esHalfBar. + + \see addAxes, setupFullAxesBox +*/ +QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis) +{ + QCPAxis *newAxis = axis; + if (!newAxis) + { + newAxis = new QCPAxis(this, type); + } else // user provided existing axis instance, do some sanity checks + { + if (newAxis->axisType() != type) + { + qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter"; + return nullptr; + } + if (newAxis->axisRect() != this) + { + qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect"; + return nullptr; + } + if (axes().contains(newAxis)) + { + qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect"; + return nullptr; + } + } + if (!mAxes[type].isEmpty()) // multiple axes on one side, add half-bar axis ending to additional axes with offset + { + bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom); + newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert)); + newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert)); + } + mAxes[type].append(newAxis); + + // reset convenience axis pointers on parent QCustomPlot if they are unset: + if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this) + { + switch (type) + { + case QCPAxis::atBottom: { if (!mParentPlot->xAxis) mParentPlot->xAxis = newAxis; break; } + case QCPAxis::atLeft: { if (!mParentPlot->yAxis) mParentPlot->yAxis = newAxis; break; } + case QCPAxis::atTop: { if (!mParentPlot->xAxis2) mParentPlot->xAxis2 = newAxis; break; } + case QCPAxis::atRight: { if (!mParentPlot->yAxis2) mParentPlot->yAxis2 = newAxis; break; } + } + } + + return newAxis; +} + +/*! + Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an + or-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once. + + Returns a list of the added axes. + + \see addAxis, setupFullAxesBox +*/ +QList QCPAxisRect::addAxes(QCPAxis::AxisTypes types) +{ + QList result; + if (types.testFlag(QCPAxis::atLeft)) + result << addAxis(QCPAxis::atLeft); + if (types.testFlag(QCPAxis::atRight)) + result << addAxis(QCPAxis::atRight); + if (types.testFlag(QCPAxis::atTop)) + result << addAxis(QCPAxis::atTop); + if (types.testFlag(QCPAxis::atBottom)) + result << addAxis(QCPAxis::atBottom); + return result; +} + +/*! + Removes the specified \a axis from the axis rect and deletes it. + + Returns true on success, i.e. if \a axis was a valid axis in this axis rect. + + \see addAxis +*/ +bool QCPAxisRect::removeAxis(QCPAxis *axis) +{ + // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers: + QHashIterator > it(mAxes); + while (it.hasNext()) + { + it.next(); + if (it.value().contains(axis)) + { + if (it.value().first() == axis && it.value().size() > 1) // if removing first axis, transfer axis offset to the new first axis (which at this point is the second axis, if it exists) + it.value()[1]->setOffset(axis->offset()); + mAxes[it.key()].removeOne(axis); + if (qobject_cast(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot) + parentPlot()->axisRemoved(axis); + delete axis; + return true; + } + } + qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast(axis); + return false; +} + +/*! + Zooms in (or out) to the passed rectangular region \a pixelRect, given in pixel coordinates. + + All axes of this axis rect will have their range zoomed accordingly. If you only wish to zoom + specific axes, use the overloaded version of this method. + + \see QCustomPlot::setSelectionRectMode +*/ +void QCPAxisRect::zoom(const QRectF &pixelRect) +{ + zoom(pixelRect, axes()); +} + +/*! \overload + + Zooms in (or out) to the passed rectangular region \a pixelRect, given in pixel coordinates. + + Only the axes passed in \a affectedAxes will have their ranges zoomed accordingly. + + \see QCustomPlot::setSelectionRectMode +*/ +void QCPAxisRect::zoom(const QRectF &pixelRect, const QList &affectedAxes) +{ + foreach (QCPAxis *axis, affectedAxes) + { + if (!axis) + { + qDebug() << Q_FUNC_INFO << "a passed axis was zero"; + continue; + } + QCPRange pixelRange; + if (axis->orientation() == Qt::Horizontal) + pixelRange = QCPRange(pixelRect.left(), pixelRect.right()); + else + pixelRange = QCPRange(pixelRect.top(), pixelRect.bottom()); + axis->setRange(axis->pixelToCoord(pixelRange.lower), axis->pixelToCoord(pixelRange.upper)); + } +} + +/*! + Convenience function to create an axis on each side that doesn't have any axes yet and set their + visibility to true. Further, the top/right axes are assigned the following properties of the + bottom/left axes: + + \li range (\ref QCPAxis::setRange) + \li range reversed (\ref QCPAxis::setRangeReversed) + \li scale type (\ref QCPAxis::setScaleType) + \li tick visibility (\ref QCPAxis::setTicks) + \li number format (\ref QCPAxis::setNumberFormat) + \li number precision (\ref QCPAxis::setNumberPrecision) + \li tick count of ticker (\ref QCPAxisTicker::setTickCount) + \li tick origin of ticker (\ref QCPAxisTicker::setTickOrigin) + + Tick label visibility (\ref QCPAxis::setTickLabels) of the right and top axes are set to false. + + If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom + and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes. +*/ +void QCPAxisRect::setupFullAxesBox(bool connectRanges) +{ + QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2; + if (axisCount(QCPAxis::atBottom) == 0) + xAxis = addAxis(QCPAxis::atBottom); + else + xAxis = axis(QCPAxis::atBottom); + + if (axisCount(QCPAxis::atLeft) == 0) + yAxis = addAxis(QCPAxis::atLeft); + else + yAxis = axis(QCPAxis::atLeft); + + if (axisCount(QCPAxis::atTop) == 0) + xAxis2 = addAxis(QCPAxis::atTop); + else + xAxis2 = axis(QCPAxis::atTop); + + if (axisCount(QCPAxis::atRight) == 0) + yAxis2 = addAxis(QCPAxis::atRight); + else + yAxis2 = axis(QCPAxis::atRight); + + xAxis->setVisible(true); + yAxis->setVisible(true); + xAxis2->setVisible(true); + yAxis2->setVisible(true); + xAxis2->setTickLabels(false); + yAxis2->setTickLabels(false); + + xAxis2->setRange(xAxis->range()); + xAxis2->setRangeReversed(xAxis->rangeReversed()); + xAxis2->setScaleType(xAxis->scaleType()); + xAxis2->setTicks(xAxis->ticks()); + xAxis2->setNumberFormat(xAxis->numberFormat()); + xAxis2->setNumberPrecision(xAxis->numberPrecision()); + xAxis2->ticker()->setTickCount(xAxis->ticker()->tickCount()); + xAxis2->ticker()->setTickOrigin(xAxis->ticker()->tickOrigin()); + + yAxis2->setRange(yAxis->range()); + yAxis2->setRangeReversed(yAxis->rangeReversed()); + yAxis2->setScaleType(yAxis->scaleType()); + yAxis2->setTicks(yAxis->ticks()); + yAxis2->setNumberFormat(yAxis->numberFormat()); + yAxis2->setNumberPrecision(yAxis->numberPrecision()); + yAxis2->ticker()->setTickCount(yAxis->ticker()->tickCount()); + yAxis2->ticker()->setTickOrigin(yAxis->ticker()->tickOrigin()); + + if (connectRanges) + { + connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange))); + connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange))); + } +} + +/*! + Returns a list of all the plottables that are associated with this axis rect. + + A plottable is considered associated with an axis rect if its key or value axis (or both) is in + this axis rect. + + \see graphs, items +*/ +QList QCPAxisRect::plottables() const +{ + // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries + QList result; + foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables) + { + if (plottable->keyAxis()->axisRect() == this || plottable->valueAxis()->axisRect() == this) + result.append(plottable); + } + return result; +} + +/*! + Returns a list of all the graphs that are associated with this axis rect. + + A graph is considered associated with an axis rect if its key or value axis (or both) is in + this axis rect. + + \see plottables, items +*/ +QList QCPAxisRect::graphs() const +{ + // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries + QList result; + foreach (QCPGraph *graph, mParentPlot->mGraphs) + { + if (graph->keyAxis()->axisRect() == this || graph->valueAxis()->axisRect() == this) + result.append(graph); + } + return result; +} + +/*! + Returns a list of all the items that are associated with this axis rect. + + An item is considered associated with an axis rect if any of its positions has key or value axis + set to an axis that is in this axis rect, or if any of its positions has \ref + QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref + QCPAbstractItem::setClipAxisRect) is set to this axis rect. + + \see plottables, graphs +*/ +QList QCPAxisRect::items() const +{ + // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries + // and miss those items that have this axis rect as clipAxisRect. + QList result; + foreach (QCPAbstractItem *item, mParentPlot->mItems) + { + if (item->clipAxisRect() == this) + { + result.append(item); + continue; + } + foreach (QCPItemPosition *position, item->positions()) + { + if (position->axisRect() == this || + position->keyAxis()->axisRect() == this || + position->valueAxis()->axisRect() == this) + { + result.append(item); + break; + } + } + } + return result; +} + +/*! + This method is called automatically upon replot and doesn't need to be called by users of + QCPAxisRect. + + Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update), + and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its + QCPInsetLayout::update function. + + \seebaseclassmethod +*/ +void QCPAxisRect::update(UpdatePhase phase) +{ + QCPLayoutElement::update(phase); + + switch (phase) + { + case upPreparation: + { + foreach (QCPAxis *axis, axes()) + axis->setupTickVectors(); + break; + } + case upLayout: + { + mInsetLayout->setOuterRect(rect()); + break; + } + default: break; + } + + // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout): + mInsetLayout->update(phase); +} + +/* inherits documentation from base class */ +QList QCPAxisRect::elements(bool recursive) const +{ + QList result; + if (mInsetLayout) + { + result << mInsetLayout; + if (recursive) + result << mInsetLayout->elements(recursive); + } + return result; +} + +/* inherits documentation from base class */ +void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + painter->setAntialiasing(false); +} + +/* inherits documentation from base class */ +void QCPAxisRect::draw(QCPPainter *painter) +{ + drawBackground(painter); +} + +/*! + Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the + axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect + backgrounds are usually drawn below everything else. + + For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be + enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio + is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call, + consider using the overloaded version of this function. + + Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref + setBackground(const QBrush &brush). + + \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush) +*/ +void QCPAxisRect::setBackground(const QPixmap &pm) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); +} + +/*! \overload + + Sets \a brush as the background brush. The axis rect background will be filled with this brush. + Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds + are usually drawn below everything else. + + The brush will be drawn before (under) any background pixmap, which may be specified with \ref + setBackground(const QPixmap &pm). + + To disable drawing of a background brush, set \a brush to Qt::NoBrush. + + \see setBackground(const QPixmap &pm) +*/ +void QCPAxisRect::setBackground(const QBrush &brush) +{ + mBackgroundBrush = brush; +} + +/*! \overload + + Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it + shall be scaled in one call. + + \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode +*/ +void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); + mBackgroundScaled = scaled; + mBackgroundScaledMode = mode; +} + +/*! + Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled + is set to true, you may control whether and how the aspect ratio of the original pixmap is + preserved with \ref setBackgroundScaledMode. + + Note that the scaled version of the original pixmap is buffered, so there is no performance + penalty on replots. (Except when the axis rect dimensions are changed continuously.) + + \see setBackground, setBackgroundScaledMode +*/ +void QCPAxisRect::setBackgroundScaled(bool scaled) +{ + mBackgroundScaled = scaled; +} + +/*! + If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to + define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved. + \see setBackground, setBackgroundScaled +*/ +void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode) +{ + mBackgroundScaledMode = mode; +} + +/*! + Returns the range drag axis of the \a orientation provided. If multiple axes were set, returns + the first one (use \ref rangeDragAxes to retrieve a list with all set axes). + + \see setRangeDragAxes +*/ +QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation) +{ + if (orientation == Qt::Horizontal) + return mRangeDragHorzAxis.isEmpty() ? nullptr : mRangeDragHorzAxis.first().data(); + else + return mRangeDragVertAxis.isEmpty() ? nullptr : mRangeDragVertAxis.first().data(); +} + +/*! + Returns the range zoom axis of the \a orientation provided. If multiple axes were set, returns + the first one (use \ref rangeZoomAxes to retrieve a list with all set axes). + + \see setRangeZoomAxes +*/ +QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation) +{ + if (orientation == Qt::Horizontal) + return mRangeZoomHorzAxis.isEmpty() ? nullptr : mRangeZoomHorzAxis.first().data(); + else + return mRangeZoomVertAxis.isEmpty() ? nullptr : mRangeZoomVertAxis.first().data(); +} + +/*! + Returns all range drag axes of the \a orientation provided. + + \see rangeZoomAxis, setRangeZoomAxes +*/ +QList QCPAxisRect::rangeDragAxes(Qt::Orientation orientation) +{ + QList result; + if (orientation == Qt::Horizontal) + { + foreach (QPointer axis, mRangeDragHorzAxis) + { + if (!axis.isNull()) + result.append(axis.data()); + } + } else + { + foreach (QPointer axis, mRangeDragVertAxis) + { + if (!axis.isNull()) + result.append(axis.data()); + } + } + return result; +} + +/*! + Returns all range zoom axes of the \a orientation provided. + + \see rangeDragAxis, setRangeDragAxes +*/ +QList QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation) +{ + QList result; + if (orientation == Qt::Horizontal) + { + foreach (QPointer axis, mRangeZoomHorzAxis) + { + if (!axis.isNull()) + result.append(axis.data()); + } + } else + { + foreach (QPointer axis, mRangeZoomVertAxis) + { + if (!axis.isNull()) + result.append(axis.data()); + } + } + return result; +} + +/*! + Returns the range zoom factor of the \a orientation provided. + + \see setRangeZoomFactor +*/ +double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation) +{ + return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert); +} + +/*! + Sets which axis orientation may be range dragged by the user with mouse interaction. + What orientation corresponds to which specific axis can be set with + \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By + default, the horizontal axis is the bottom axis (xAxis) and the vertical axis + is the left axis (yAxis). + + To disable range dragging entirely, pass \c nullptr as \a orientations or remove \ref + QCP::iRangeDrag from \ref QCustomPlot::setInteractions. To enable range dragging for both + directions, pass Qt::Horizontal | Qt::Vertical as \a orientations. + + In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions + contains \ref QCP::iRangeDrag to enable the range dragging interaction. + + \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag +*/ +void QCPAxisRect::setRangeDrag(Qt::Orientations orientations) +{ + mRangeDrag = orientations; +} + +/*! + Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation + corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal, + QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical + axis is the left axis (yAxis). + + To disable range zooming entirely, pass \c nullptr as \a orientations or remove \ref + QCP::iRangeZoom from \ref QCustomPlot::setInteractions. To enable range zooming for both + directions, pass Qt::Horizontal | Qt::Vertical as \a orientations. + + In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions + contains \ref QCP::iRangeZoom to enable the range zooming interaction. + + \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag +*/ +void QCPAxisRect::setRangeZoom(Qt::Orientations orientations) +{ + mRangeZoom = orientations; +} + +/*! \overload + + Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging on + the QCustomPlot widget. Pass \c nullptr if no axis shall be dragged in the respective + orientation. + + Use the overload taking a list of axes, if multiple axes (more than one per orientation) shall + react to dragging interactions. + + \see setRangeZoomAxes +*/ +void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical) +{ + QList horz, vert; + if (horizontal) + horz.append(horizontal); + if (vertical) + vert.append(vertical); + setRangeDragAxes(horz, vert); +} + +/*! \overload + + This method allows to set up multiple axes to react to horizontal and vertical dragging. The drag + orientation that the respective axis will react to is deduced from its orientation (\ref + QCPAxis::orientation). + + In the unusual case that you wish to e.g. drag a vertically oriented axis with a horizontal drag + motion, use the overload taking two separate lists for horizontal and vertical dragging. +*/ +void QCPAxisRect::setRangeDragAxes(QList axes) +{ + QList horz, vert; + foreach (QCPAxis *ax, axes) + { + if (ax->orientation() == Qt::Horizontal) + horz.append(ax); + else + vert.append(ax); + } + setRangeDragAxes(horz, vert); +} + +/*! \overload + + This method allows to set multiple axes up to react to horizontal and vertical dragging, and + define specifically which axis reacts to which drag orientation (irrespective of the axis + orientation). +*/ +void QCPAxisRect::setRangeDragAxes(QList horizontal, QList vertical) +{ + mRangeDragHorzAxis.clear(); + foreach (QCPAxis *ax, horizontal) + { + QPointer axPointer(ax); + if (!axPointer.isNull()) + mRangeDragHorzAxis.append(axPointer); + else + qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast(ax); + } + mRangeDragVertAxis.clear(); + foreach (QCPAxis *ax, vertical) + { + QPointer axPointer(ax); + if (!axPointer.isNull()) + mRangeDragVertAxis.append(axPointer); + else + qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast(ax); + } +} + +/*! + Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on + the QCustomPlot widget. Pass \c nullptr if no axis shall be zoomed in the respective orientation. + + The two axes can be zoomed with different strengths, when different factors are passed to \ref + setRangeZoomFactor(double horizontalFactor, double verticalFactor). + + Use the overload taking a list of axes, if multiple axes (more than one per orientation) shall + react to zooming interactions. + + \see setRangeDragAxes +*/ +void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical) +{ + QList horz, vert; + if (horizontal) + horz.append(horizontal); + if (vertical) + vert.append(vertical); + setRangeZoomAxes(horz, vert); +} + +/*! \overload + + This method allows to set up multiple axes to react to horizontal and vertical range zooming. The + zoom orientation that the respective axis will react to is deduced from its orientation (\ref + QCPAxis::orientation). + + In the unusual case that you wish to e.g. zoom a vertically oriented axis with a horizontal zoom + interaction, use the overload taking two separate lists for horizontal and vertical zooming. +*/ +void QCPAxisRect::setRangeZoomAxes(QList axes) +{ + QList horz, vert; + foreach (QCPAxis *ax, axes) + { + if (ax->orientation() == Qt::Horizontal) + horz.append(ax); + else + vert.append(ax); + } + setRangeZoomAxes(horz, vert); +} + +/*! \overload + + This method allows to set multiple axes up to react to horizontal and vertical zooming, and + define specifically which axis reacts to which zoom orientation (irrespective of the axis + orientation). +*/ +void QCPAxisRect::setRangeZoomAxes(QList horizontal, QList vertical) +{ + mRangeZoomHorzAxis.clear(); + foreach (QCPAxis *ax, horizontal) + { + QPointer axPointer(ax); + if (!axPointer.isNull()) + mRangeZoomHorzAxis.append(axPointer); + else + qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast(ax); + } + mRangeZoomVertAxis.clear(); + foreach (QCPAxis *ax, vertical) + { + QPointer axPointer(ax); + if (!axPointer.isNull()) + mRangeZoomVertAxis.append(axPointer); + else + qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast(ax); + } +} + +/*! + Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with + \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to + let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal + and which is vertical, can be set with \ref setRangeZoomAxes. + + When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user) + will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the + same scrolling direction will zoom out. +*/ +void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor) +{ + mRangeZoomFactorHorz = horizontalFactor; + mRangeZoomFactorVert = verticalFactor; +} + +/*! \overload + + Sets both the horizontal and vertical zoom \a factor. +*/ +void QCPAxisRect::setRangeZoomFactor(double factor) +{ + mRangeZoomFactorHorz = factor; + mRangeZoomFactorVert = factor; +} + +/*! \internal + + Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a + pixmap. + + If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an + according filling inside the axis rect with the provided \a painter. + + Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version + depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside + the axis rect with the provided \a painter. The scaled version is buffered in + mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when + the axis rect has changed in a way that requires a rescale of the background pixmap (this is + dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was + set. + + \see setBackground, setBackgroundScaled, setBackgroundScaledMode +*/ +void QCPAxisRect::drawBackground(QCPPainter *painter) +{ + // draw background fill: + if (mBackgroundBrush != Qt::NoBrush) + painter->fillRect(mRect, mBackgroundBrush); + + // draw background pixmap (on top of fill, if brush specified): + if (!mBackgroundPixmap.isNull()) + { + if (mBackgroundScaled) + { + // check whether mScaledBackground needs to be updated: + QSize scaledSize(mBackgroundPixmap.size()); + scaledSize.scale(mRect.size(), mBackgroundScaledMode); + if (mScaledBackgroundPixmap.size() != scaledSize) + mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation); + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect()); + } else + { + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height())); + } + } +} + +/*! \internal + + This function makes sure multiple axes on the side specified with \a type don't collide, but are + distributed according to their respective space requirement (QCPAxis::calculateMargin). + + It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the + one with index zero. + + This function is called by \ref calculateAutoMargin. +*/ +void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type) +{ + const QList axesList = mAxes.value(type); + if (axesList.isEmpty()) + return; + + bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false + for (int i=1; ioffset() + axesList.at(i-1)->calculateMargin(); + if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible) + { + if (!isFirstVisible) + offset += axesList.at(i)->tickLengthIn(); + isFirstVisible = false; + } + axesList.at(i)->setOffset(offset); + } +} + +/* inherits documentation from base class */ +int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side) +{ + if (!mAutoMargins.testFlag(side)) + qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin"; + + updateAxesOffset(QCPAxis::marginSideToAxisType(side)); + + // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call + const QList axesList = mAxes.value(QCPAxis::marginSideToAxisType(side)); + if (!axesList.isEmpty()) + return axesList.last()->offset() + axesList.last()->calculateMargin(); + else + return 0; +} + +/*! \internal + + Reacts to a change in layout to potentially set the convenience axis pointers \ref + QCustomPlot::xAxis, \ref QCustomPlot::yAxis, etc. of the parent QCustomPlot to the respective + axes of this axis rect. This is only done if the respective convenience pointer is currently zero + and if there is no QCPAxisRect at position (0, 0) of the plot layout. + + This automation makes it simpler to replace the main axis rect with a newly created one, without + the need to manually reset the convenience pointers. +*/ +void QCPAxisRect::layoutChanged() +{ + if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this) + { + if (axisCount(QCPAxis::atBottom) > 0 && !mParentPlot->xAxis) + mParentPlot->xAxis = axis(QCPAxis::atBottom); + if (axisCount(QCPAxis::atLeft) > 0 && !mParentPlot->yAxis) + mParentPlot->yAxis = axis(QCPAxis::atLeft); + if (axisCount(QCPAxis::atTop) > 0 && !mParentPlot->xAxis2) + mParentPlot->xAxis2 = axis(QCPAxis::atTop); + if (axisCount(QCPAxis::atRight) > 0 && !mParentPlot->yAxis2) + mParentPlot->yAxis2 = axis(QCPAxis::atRight); + } +} + +/*! \internal + + Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is + pressed, the range dragging interaction is initialized (the actual range manipulation happens in + the \ref mouseMoveEvent). + + The mDragging flag is set to true and some anchor points are set that are needed to determine the + distance the mouse was dragged in the mouse move/release events later. + + \see mouseMoveEvent, mouseReleaseEvent +*/ +void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + if (event->buttons() & Qt::LeftButton) + { + mDragging = true; + // initialize antialiasing backup in case we start dragging: + if (mParentPlot->noAntialiasingOnDrag()) + { + mAADragBackup = mParentPlot->antialiasedElements(); + mNotAADragBackup = mParentPlot->notAntialiasedElements(); + } + // Mouse range dragging interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + mDragStartHorzRange.clear(); + foreach (QPointer axis, mRangeDragHorzAxis) + mDragStartHorzRange.append(axis.isNull() ? QCPRange() : axis->range()); + mDragStartVertRange.clear(); + foreach (QPointer axis, mRangeDragVertAxis) + mDragStartVertRange.append(axis.isNull() ? QCPRange() : axis->range()); + } + } +} + +/*! \internal + + Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a + preceding \ref mousePressEvent, the range is moved accordingly. + + \see mousePressEvent, mouseReleaseEvent +*/ +void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(startPos) + // Mouse range dragging interaction: + if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + + if (mRangeDrag.testFlag(Qt::Horizontal)) + { + for (int i=0; i= mDragStartHorzRange.size()) + break; + if (ax->mScaleType == QCPAxis::stLinear) + { + double diff = ax->pixelToCoord(startPos.x()) - ax->pixelToCoord(event->pos().x()); + ax->setRange(mDragStartHorzRange.at(i).lower+diff, mDragStartHorzRange.at(i).upper+diff); + } else if (ax->mScaleType == QCPAxis::stLogarithmic) + { + double diff = ax->pixelToCoord(startPos.x()) / ax->pixelToCoord(event->pos().x()); + ax->setRange(mDragStartHorzRange.at(i).lower*diff, mDragStartHorzRange.at(i).upper*diff); + } + } + } + + if (mRangeDrag.testFlag(Qt::Vertical)) + { + for (int i=0; i= mDragStartVertRange.size()) + break; + if (ax->mScaleType == QCPAxis::stLinear) + { + double diff = ax->pixelToCoord(startPos.y()) - ax->pixelToCoord(event->pos().y()); + ax->setRange(mDragStartVertRange.at(i).lower+diff, mDragStartVertRange.at(i).upper+diff); + } else if (ax->mScaleType == QCPAxis::stLogarithmic) + { + double diff = ax->pixelToCoord(startPos.y()) / ax->pixelToCoord(event->pos().y()); + ax->setRange(mDragStartVertRange.at(i).lower*diff, mDragStartVertRange.at(i).upper*diff); + } + } + } + + if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot + { + if (mParentPlot->noAntialiasingOnDrag()) + mParentPlot->setNotAntialiasedElements(QCP::aeAll); + mParentPlot->replot(QCustomPlot::rpQueuedReplot); + } + + } +} + +/* inherits documentation from base class */ +void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(event) + Q_UNUSED(startPos) + mDragging = false; + if (mParentPlot->noAntialiasingOnDrag()) + { + mParentPlot->setAntialiasedElements(mAADragBackup); + mParentPlot->setNotAntialiasedElements(mNotAADragBackup); + } +} + +/*! \internal + + Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the + ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of + the scaling operation is the current cursor position inside the axis rect. The scaling factor is + dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural + zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor. + + Note, that event->angleDelta() is usually +/-120 for single rotation steps. However, if the mouse + wheel is turned rapidly, many steps may bunch up to one event, so the delta may then be multiples + of 120. This is taken into account here, by calculating \a wheelSteps and using it as exponent of + the range zoom factor. This takes care of the wheel direction automatically, by inverting the + factor, when the wheel step is negative (f^-1 = 1/f). +*/ +void QCPAxisRect::wheelEvent(QWheelEvent *event) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + const double delta = event->delta(); +#else + const double delta = event->angleDelta().y(); +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPointF pos = event->pos(); +#else + const QPointF pos = event->position(); +#endif + + // Mouse range zooming interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) + { + if (mRangeZoom != 0) + { + double factor; + double wheelSteps = delta/120.0; // a single step delta is +/-120 usually + if (mRangeZoom.testFlag(Qt::Horizontal)) + { + factor = qPow(mRangeZoomFactorHorz, wheelSteps); + foreach (QPointer axis, mRangeZoomHorzAxis) + { + if (!axis.isNull()) + axis->scaleRange(factor, axis->pixelToCoord(pos.x())); + } + } + if (mRangeZoom.testFlag(Qt::Vertical)) + { + factor = qPow(mRangeZoomFactorVert, wheelSteps); + foreach (QPointer axis, mRangeZoomVertAxis) + { + if (!axis.isNull()) + axis->scaleRange(factor, axis->pixelToCoord(pos.y())); + } + } + mParentPlot->replot(); + } + } +} +/* end of 'src/layoutelements/layoutelement-axisrect.cpp' */ + + +/* including file 'src/layoutelements/layoutelement-legend.cpp' */ +/* modified 2022-11-06T12:45:56, size 31762 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractLegendItem +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractLegendItem + \brief The abstract base class for all entries in a QCPLegend. + + It defines a very basic interface for entries in a QCPLegend. For representing plottables in the + legend, the subclass \ref QCPPlottableLegendItem is more suitable. + + Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry + that's not even associated with a plottable). + + You must implement the following pure virtual functions: + \li \ref draw (from QCPLayerable) + + You inherit the following members you may use: + + + + + + + + +
QCPLegend *\b mParentLegendA pointer to the parent QCPLegend.
QFont \b mFontThe generic font of the item. You should use this font for all or at least the most prominent text of the item.
+*/ + +/* start of documentation of signals */ + +/*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected) + + This signal is emitted when the selection state of this legend item has changed, either by user + interaction or by a direct call to \ref setSelected. +*/ + +/* end of documentation of signals */ + +/*! + Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not + cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately. +*/ +QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) : + QCPLayoutElement(parent->parentPlot()), + mParentLegend(parent), + mFont(parent->font()), + mTextColor(parent->textColor()), + mSelectedFont(parent->selectedFont()), + mSelectedTextColor(parent->selectedTextColor()), + mSelectable(true), + mSelected(false) +{ + setLayer(QLatin1String("legend")); + setMargins(QMargins(0, 0, 0, 0)); +} + +/*! + Sets the default font of this specific legend item to \a font. + + \see setTextColor, QCPLegend::setFont +*/ +void QCPAbstractLegendItem::setFont(const QFont &font) +{ + mFont = font; +} + +/*! + Sets the default text color of this specific legend item to \a color. + + \see setFont, QCPLegend::setTextColor +*/ +void QCPAbstractLegendItem::setTextColor(const QColor &color) +{ + mTextColor = color; +} + +/*! + When this legend item is selected, \a font is used to draw generic text, instead of the normal + font set with \ref setFont. + + \see setFont, QCPLegend::setSelectedFont +*/ +void QCPAbstractLegendItem::setSelectedFont(const QFont &font) +{ + mSelectedFont = font; +} + +/*! + When this legend item is selected, \a color is used to draw generic text, instead of the normal + color set with \ref setTextColor. + + \see setTextColor, QCPLegend::setSelectedTextColor +*/ +void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color) +{ + mSelectedTextColor = color; +} + +/*! + Sets whether this specific legend item is selectable. + + \see setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPAbstractLegendItem::setSelectable(bool selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + emit selectableChanged(mSelectable); + } +} + +/*! + Sets whether this specific legend item is selected. + + It is possible to set the selection state of this item by calling this function directly, even if + setSelectable is set to false. + + \see setSelectableParts, QCustomPlot::setInteractions +*/ +void QCPAbstractLegendItem::setSelected(bool selected) +{ + if (mSelected != selected) + { + mSelected = selected; + emit selectionChanged(mSelected); + } +} + +/* inherits documentation from base class */ +double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (!mParentPlot) return -1; + if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems))) + return -1; + + if (mRect.contains(pos.toPoint())) + return mParentPlot->selectionTolerance()*0.99; + else + return -1; +} + +/* inherits documentation from base class */ +void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems); +} + +/* inherits documentation from base class */ +QRect QCPAbstractLegendItem::clipRect() const +{ + return mOuterRect; +} + +/* inherits documentation from base class */ +void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(details) + if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems)) + { + bool selBefore = mSelected; + setSelected(additive ? !mSelected : true); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems)) + { + bool selBefore = mSelected; + setSelected(false); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPlottableLegendItem +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPlottableLegendItem + \brief A legend item representing a plottable with an icon and the plottable name. + + This is the standard legend item for plottables. It displays an icon of the plottable next to the + plottable name. The icon is drawn by the respective plottable itself (\ref + QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable. + For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the + middle. + + Legend items of this type are always associated with one plottable (retrievable via the + plottable() function and settable with the constructor). You may change the font of the plottable + name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref + QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding. + + The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend + creates/removes legend items of this type. + + Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of + QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout + interface, QCPLegend has specialized functions for handling legend items conveniently, see the + documentation of \ref QCPLegend. +*/ + +/*! + Creates a new legend item associated with \a plottable. + + Once it's created, it can be added to the legend via \ref QCPLegend::addItem. + + A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref + QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend. +*/ +QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) : + QCPAbstractLegendItem(parent), + mPlottable(plottable) +{ + setAntialiased(false); +} + +/*! \internal + + Returns the pen that shall be used to draw the icon border, taking into account the selection + state of this item. +*/ +QPen QCPPlottableLegendItem::getIconBorderPen() const +{ + return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen(); +} + +/*! \internal + + Returns the text color that shall be used to draw text, taking into account the selection state + of this item. +*/ +QColor QCPPlottableLegendItem::getTextColor() const +{ + return mSelected ? mSelectedTextColor : mTextColor; +} + +/*! \internal + + Returns the font that shall be used to draw text, taking into account the selection state of this + item. +*/ +QFont QCPPlottableLegendItem::getFont() const +{ + return mSelected ? mSelectedFont : mFont; +} + +/*! \internal + + Draws the item with \a painter. The size and position of the drawn legend item is defined by the + parent layout (typically a \ref QCPLegend) and the \ref minimumOuterSizeHint and \ref + maximumOuterSizeHint of this legend item. +*/ +void QCPPlottableLegendItem::draw(QCPPainter *painter) +{ + if (!mPlottable) return; + painter->setFont(getFont()); + painter->setPen(QPen(getTextColor())); + QSize iconSize = mParentLegend->iconSize(); + QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name()); + QRect iconRect(mRect.topLeft(), iconSize); + int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops + painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name()); + // draw icon: + painter->save(); + painter->setClipRect(iconRect, Qt::IntersectClip); + mPlottable->drawLegendIcon(painter, iconRect); + painter->restore(); + // draw icon border: + if (getIconBorderPen().style() != Qt::NoPen) + { + painter->setPen(getIconBorderPen()); + painter->setBrush(Qt::NoBrush); + int halfPen = qCeil(painter->pen().widthF()*0.5)+1; + painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped + painter->drawRect(iconRect); + } +} + +/*! \internal + + Calculates and returns the size of this item. This includes the icon, the text and the padding in + between. + + \seebaseclassmethod +*/ +QSize QCPPlottableLegendItem::minimumOuterSizeHint() const +{ + if (!mPlottable) return {}; + QSize result(0, 0); + QRect textRect; + QFontMetrics fontMetrics(getFont()); + QSize iconSize = mParentLegend->iconSize(); + textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name()); + result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width()); + result.setHeight(qMax(textRect.height(), iconSize.height())); + result.rwidth() += mMargins.left()+mMargins.right(); + result.rheight() += mMargins.top()+mMargins.bottom(); + return result; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPLegend +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPLegend + \brief Manages a legend inside a QCustomPlot. + + A legend is a small box somewhere in the plot which lists plottables with their name and icon. + + A legend is populated with legend items by calling \ref QCPAbstractPlottable::addToLegend on the + plottable, for which a legend item shall be created. In the case of the main legend (\ref + QCustomPlot::legend), simply adding plottables to the plot while \ref + QCustomPlot::setAutoAddPlottableToLegend is set to true (the default) creates corresponding + legend items. The legend item associated with a certain plottable can be removed with \ref + QCPAbstractPlottable::removeFromLegend. However, QCPLegend also offers an interface to add and + manipulate legend items directly: \ref item, \ref itemWithPlottable, \ref itemCount, \ref + addItem, \ref removeItem, etc. + + Since \ref QCPLegend derives from \ref QCPLayoutGrid, it can be placed in any position a \ref + QCPLayoutElement may be positioned. The legend items are themselves \ref QCPLayoutElement + "QCPLayoutElements" which are placed in the grid layout of the legend. \ref QCPLegend only adds + an interface specialized for handling child elements of type \ref QCPAbstractLegendItem, as + mentioned above. In principle, any other layout elements may also be added to a legend via the + normal \ref QCPLayoutGrid interface. See the special page about \link thelayoutsystem The Layout + System\endlink for examples on how to add other elements to the legend and move it outside the axis + rect. + + Use the methods \ref setFillOrder and \ref setWrap inherited from \ref QCPLayoutGrid to control + in which order (column first or row first) the legend is filled up when calling \ref addItem, and + at which column or row wrapping occurs. The default fill order for legends is \ref foRowsFirst. + + By default, every QCustomPlot has one legend (\ref QCustomPlot::legend) which is placed in the + inset layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another + position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend + outside of the axis rect, place it anywhere else with the \ref QCPLayout/\ref QCPLayoutElement + interface. +*/ + +/* start of documentation of signals */ + +/*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection); + + This signal is emitted when the selection state of this legend has changed. + + \see setSelectedParts, setSelectableParts +*/ + +/* end of documentation of signals */ + +/*! + Constructs a new QCPLegend instance with default values. + + Note that by default, QCustomPlot already contains a legend ready to be used as \ref + QCustomPlot::legend +*/ +QCPLegend::QCPLegend() : + mIconTextPadding{} +{ + setFillOrder(QCPLayoutGrid::foRowsFirst); + setWrap(0); + + setRowSpacing(3); + setColumnSpacing(8); + setMargins(QMargins(7, 5, 7, 4)); + setAntialiased(false); + setIconSize(32, 18); + + setIconTextPadding(7); + + setSelectableParts(spLegendBox | spItems); + setSelectedParts(spNone); + + setBorderPen(QPen(Qt::black, 0)); + setSelectedBorderPen(QPen(Qt::blue, 2)); + setIconBorderPen(Qt::NoPen); + setSelectedIconBorderPen(QPen(Qt::blue, 2)); + setBrush(Qt::white); + setSelectedBrush(Qt::white); + setTextColor(Qt::black); + setSelectedTextColor(Qt::blue); +} + +QCPLegend::~QCPLegend() +{ + clearItems(); + if (qobject_cast(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot) + mParentPlot->legendRemoved(this); +} + +/* no doc for getter, see setSelectedParts */ +QCPLegend::SelectableParts QCPLegend::selectedParts() const +{ + // check whether any legend elements selected, if yes, add spItems to return value + bool hasSelectedItems = false; + for (int i=0; iselected()) + { + hasSelectedItems = true; + break; + } + } + if (hasSelectedItems) + return mSelectedParts | spItems; + else + return mSelectedParts & ~spItems; +} + +/*! + Sets the pen, the border of the entire legend is drawn with. +*/ +void QCPLegend::setBorderPen(const QPen &pen) +{ + mBorderPen = pen; +} + +/*! + Sets the brush of the legend background. +*/ +void QCPLegend::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will + use this font by default. However, a different font can be specified on a per-item-basis by + accessing the specific legend item. + + This function will also set \a font on all already existing legend items. + + \see QCPAbstractLegendItem::setFont +*/ +void QCPLegend::setFont(const QFont &font) +{ + mFont = font; + for (int i=0; isetFont(mFont); + } +} + +/*! + Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph) + will use this color by default. However, a different colors can be specified on a per-item-basis + by accessing the specific legend item. + + This function will also set \a color on all already existing legend items. + + \see QCPAbstractLegendItem::setTextColor +*/ +void QCPLegend::setTextColor(const QColor &color) +{ + mTextColor = color; + for (int i=0; isetTextColor(color); + } +} + +/*! + Sets the size of legend icons. Legend items that draw an icon (e.g. a visual + representation of the graph) will use this size by default. +*/ +void QCPLegend::setIconSize(const QSize &size) +{ + mIconSize = size; +} + +/*! \overload +*/ +void QCPLegend::setIconSize(int width, int height) +{ + mIconSize.setWidth(width); + mIconSize.setHeight(height); +} + +/*! + Sets the horizontal space in pixels between the legend icon and the text next to it. + Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the + name of the graph) will use this space by default. +*/ +void QCPLegend::setIconTextPadding(int padding) +{ + mIconTextPadding = padding; +} + +/*! + Sets the pen used to draw a border around each legend icon. Legend items that draw an + icon (e.g. a visual representation of the graph) will use this pen by default. + + If no border is wanted, set this to \a Qt::NoPen. +*/ +void QCPLegend::setIconBorderPen(const QPen &pen) +{ + mIconBorderPen = pen; +} + +/*! + Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.) + + However, even when \a selectable is set to a value not allowing the selection of a specific part, + it is still possible to set the selection of this part manually, by calling \ref setSelectedParts + directly. + + \see SelectablePart, setSelectedParts +*/ +void QCPLegend::setSelectableParts(const SelectableParts &selectable) +{ + if (mSelectableParts != selectable) + { + mSelectableParts = selectable; + emit selectableChanged(mSelectableParts); + } +} + +/*! + Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part + is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected + doesn't contain \ref spItems, those items become deselected. + + The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions + contains iSelectLegend. You only need to call this function when you wish to change the selection + state manually. + + This function can change the selection state of a part even when \ref setSelectableParts was set to a + value that actually excludes the part. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set + before, because there's no way to specify which exact items to newly select. Do this by calling + \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select. + + \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush, + setSelectedFont +*/ +void QCPLegend::setSelectedParts(const SelectableParts &selected) +{ + SelectableParts newSelected = selected; + mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed + + if (mSelectedParts != newSelected) + { + if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that) + { + qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function"; + newSelected &= ~spItems; + } + if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection + { + for (int i=0; isetSelected(false); + } + } + mSelectedParts = newSelected; + emit selectionChanged(mSelectedParts); + } +} + +/*! + When the legend box is selected, this pen is used to draw the border instead of the normal pen + set via \ref setBorderPen. + + \see setSelectedParts, setSelectableParts, setSelectedBrush +*/ +void QCPLegend::setSelectedBorderPen(const QPen &pen) +{ + mSelectedBorderPen = pen; +} + +/*! + Sets the pen legend items will use to draw their icon borders, when they are selected. + + \see setSelectedParts, setSelectableParts, setSelectedFont +*/ +void QCPLegend::setSelectedIconBorderPen(const QPen &pen) +{ + mSelectedIconBorderPen = pen; +} + +/*! + When the legend box is selected, this brush is used to draw the legend background instead of the normal brush + set via \ref setBrush. + + \see setSelectedParts, setSelectableParts, setSelectedBorderPen +*/ +void QCPLegend::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/*! + Sets the default font that is used by legend items when they are selected. + + This function will also set \a font on all already existing legend items. + + \see setFont, QCPAbstractLegendItem::setSelectedFont +*/ +void QCPLegend::setSelectedFont(const QFont &font) +{ + mSelectedFont = font; + for (int i=0; isetSelectedFont(font); + } +} + +/*! + Sets the default text color that is used by legend items when they are selected. + + This function will also set \a color on all already existing legend items. + + \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor +*/ +void QCPLegend::setSelectedTextColor(const QColor &color) +{ + mSelectedTextColor = color; + for (int i=0; isetSelectedTextColor(color); + } +} + +/*! + Returns the item with index \a i. If non-legend items were added to the legend, and the element + at the specified cell index is not a QCPAbstractLegendItem, returns \c nullptr. + + Note that the linear index depends on the current fill order (\ref setFillOrder). + + \see itemCount, addItem, itemWithPlottable +*/ +QCPAbstractLegendItem *QCPLegend::item(int index) const +{ + return qobject_cast(elementAt(index)); +} + +/*! + Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*). + If such an item isn't in the legend, returns \c nullptr. + + \see hasItemWithPlottable +*/ +QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const +{ + for (int i=0; i(item(i))) + { + if (pli->plottable() == plottable) + return pli; + } + } + return nullptr; +} + +/*! + Returns the number of items currently in the legend. It is identical to the base class + QCPLayoutGrid::elementCount(), and unlike the other "item" interface methods of QCPLegend, + doesn't only address elements which can be cast to QCPAbstractLegendItem. + + Note that if empty cells are in the legend (e.g. by calling methods of the \ref QCPLayoutGrid + base class which allows creating empty cells), they are included in the returned count. + + \see item +*/ +int QCPLegend::itemCount() const +{ + return elementCount(); +} + +/*! + Returns whether the legend contains \a item. + + \see hasItemWithPlottable +*/ +bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const +{ + for (int i=0; iitem(i)) + return true; + } + return false; +} + +/*! + Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*). + If such an item isn't in the legend, returns false. + + \see itemWithPlottable +*/ +bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const +{ + return itemWithPlottable(plottable); +} + +/*! + Adds \a item to the legend, if it's not present already. The element is arranged according to the + current fill order (\ref setFillOrder) and wrapping (\ref setWrap). + + Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added. + + The legend takes ownership of the item. + + \see removeItem, item, hasItem +*/ +bool QCPLegend::addItem(QCPAbstractLegendItem *item) +{ + return addElement(item); +} + +/*! \overload + + Removes the item with the specified \a index from the legend and deletes it. + + After successful removal, the legend is reordered according to the current fill order (\ref + setFillOrder) and wrapping (\ref setWrap), so no empty cell remains where the removed \a item + was. If you don't want this, rather use the raw element interface of \ref QCPLayoutGrid. + + Returns true, if successful. Unlike \ref QCPLayoutGrid::removeAt, this method only removes + elements derived from \ref QCPAbstractLegendItem. + + \see itemCount, clearItems +*/ +bool QCPLegend::removeItem(int index) +{ + if (QCPAbstractLegendItem *ali = item(index)) + { + bool success = remove(ali); + if (success) + setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering + return success; + } else + return false; +} + +/*! \overload + + Removes \a item from the legend and deletes it. + + After successful removal, the legend is reordered according to the current fill order (\ref + setFillOrder) and wrapping (\ref setWrap), so no empty cell remains where the removed \a item + was. If you don't want this, rather use the raw element interface of \ref QCPLayoutGrid. + + Returns true, if successful. + + \see clearItems +*/ +bool QCPLegend::removeItem(QCPAbstractLegendItem *item) +{ + bool success = remove(item); + if (success) + setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering + return success; +} + +/*! + Removes all items from the legend. +*/ +void QCPLegend::clearItems() +{ + for (int i=elementCount()-1; i>=0; --i) + { + if (item(i)) + removeAt(i); // don't use removeItem() because it would unnecessarily reorder the whole legend for each item + } + setFillOrder(fillOrder(), true); // get rid of empty cells by reordering once after all items are removed +} + +/*! + Returns the legend items that are currently selected. If no items are selected, + the list is empty. + + \see QCPAbstractLegendItem::setSelected, setSelectable +*/ +QList QCPLegend::selectedItems() const +{ + QList result; + for (int i=0; iselected()) + result.append(ali); + } + } + return result; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing main legend elements. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \seebaseclassmethod + + \see setAntialiased +*/ +void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend); +} + +/*! \internal + + Returns the pen used to paint the border of the legend, taking into account the selection state + of the legend box. +*/ +QPen QCPLegend::getBorderPen() const +{ + return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen; +} + +/*! \internal + + Returns the brush used to paint the background of the legend, taking into account the selection + state of the legend box. +*/ +QBrush QCPLegend::getBrush() const +{ + return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush; +} + +/*! \internal + + Draws the legend box with the provided \a painter. The individual legend items are layerables + themselves, thus are drawn independently. +*/ +void QCPLegend::draw(QCPPainter *painter) +{ + // draw background rect: + painter->setBrush(getBrush()); + painter->setPen(getBorderPen()); + painter->drawRect(mOuterRect); +} + +/* inherits documentation from base class */ +double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if (!mParentPlot) return -1; + if (onlySelectable && !mSelectableParts.testFlag(spLegendBox)) + return -1; + + if (mOuterRect.contains(pos.toPoint())) + { + if (details) details->setValue(spLegendBox); + return mParentPlot->selectionTolerance()*0.99; + } + return -1; +} + +/* inherits documentation from base class */ +void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + mSelectedParts = selectedParts(); // in case item selection has changed + if (details.value() == spLegendBox && mSelectableParts.testFlag(spLegendBox)) + { + SelectableParts selBefore = mSelectedParts; + setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent) + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPLegend::deselectEvent(bool *selectionStateChanged) +{ + mSelectedParts = selectedParts(); // in case item selection has changed + if (mSelectableParts.testFlag(spLegendBox)) + { + SelectableParts selBefore = mSelectedParts; + setSelectedParts(selectedParts() & ~spLegendBox); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; + } +} + +/* inherits documentation from base class */ +QCP::Interaction QCPLegend::selectionCategory() const +{ + return QCP::iSelectLegend; +} + +/* inherits documentation from base class */ +QCP::Interaction QCPAbstractLegendItem::selectionCategory() const +{ + return QCP::iSelectLegend; +} + +/* inherits documentation from base class */ +void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) +{ + if (parentPlot && !parentPlot->legend) + parentPlot->legend = this; +} +/* end of 'src/layoutelements/layoutelement-legend.cpp' */ + + +/* including file 'src/layoutelements/layoutelement-textelement.cpp' */ +/* modified 2022-11-06T12:45:56, size 12925 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPTextElement +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPTextElement + \brief A layout element displaying a text + + The text may be specified with \ref setText, the formatting can be controlled with \ref setFont, + \ref setTextColor, and \ref setTextFlags. + + A text element can be added as follows: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcptextelement-creation +*/ + +/* start documentation of signals */ + +/*! \fn void QCPTextElement::selectionChanged(bool selected) + + This signal is emitted when the selection state has changed to \a selected, either by user + interaction or by a direct call to \ref setSelected. + + \see setSelected, setSelectable +*/ + +/*! \fn void QCPTextElement::clicked(QMouseEvent *event) + + This signal is emitted when the text element is clicked. + + \see doubleClicked, selectTest +*/ + +/*! \fn void QCPTextElement::doubleClicked(QMouseEvent *event) + + This signal is emitted when the text element is double clicked. + + \see clicked, selectTest +*/ + +/* end documentation of signals */ + +/*! \overload + + Creates a new QCPTextElement instance and sets default values. The initial text is empty (\ref + setText). +*/ +QCPTextElement::QCPTextElement(QCustomPlot *parentPlot) : + QCPLayoutElement(parentPlot), + mText(), + mTextFlags(Qt::AlignCenter), + mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below + mTextColor(Qt::black), + mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below + mSelectedTextColor(Qt::blue), + mSelectable(false), + mSelected(false) +{ + if (parentPlot) + { + mFont = parentPlot->font(); + mSelectedFont = parentPlot->font(); + } + setMargins(QMargins(2, 2, 2, 2)); +} + +/*! \overload + + Creates a new QCPTextElement instance and sets default values. + + The initial text is set to \a text. +*/ +QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text) : + QCPLayoutElement(parentPlot), + mText(text), + mTextFlags(Qt::AlignCenter), + mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below + mTextColor(Qt::black), + mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below + mSelectedTextColor(Qt::blue), + mSelectable(false), + mSelected(false) +{ + if (parentPlot) + { + mFont = parentPlot->font(); + mSelectedFont = parentPlot->font(); + } + setMargins(QMargins(2, 2, 2, 2)); +} + +/*! \overload + + Creates a new QCPTextElement instance and sets default values. + + The initial text is set to \a text with \a pointSize. +*/ +QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize) : + QCPLayoutElement(parentPlot), + mText(text), + mTextFlags(Qt::AlignCenter), + mFont(QFont(QLatin1String("sans serif"), int(pointSize))), // will be taken from parentPlot if available, see below + mTextColor(Qt::black), + mSelectedFont(QFont(QLatin1String("sans serif"), int(pointSize))), // will be taken from parentPlot if available, see below + mSelectedTextColor(Qt::blue), + mSelectable(false), + mSelected(false) +{ + mFont.setPointSizeF(pointSize); // set here again as floating point, because constructor above only takes integer + if (parentPlot) + { + mFont = parentPlot->font(); + mFont.setPointSizeF(pointSize); + mSelectedFont = parentPlot->font(); + mSelectedFont.setPointSizeF(pointSize); + } + setMargins(QMargins(2, 2, 2, 2)); +} + +/*! \overload + + Creates a new QCPTextElement instance and sets default values. + + The initial text is set to \a text with \a pointSize and the specified \a fontFamily. +*/ +QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize) : + QCPLayoutElement(parentPlot), + mText(text), + mTextFlags(Qt::AlignCenter), + mFont(QFont(fontFamily, int(pointSize))), + mTextColor(Qt::black), + mSelectedFont(QFont(fontFamily, int(pointSize))), + mSelectedTextColor(Qt::blue), + mSelectable(false), + mSelected(false) +{ + mFont.setPointSizeF(pointSize); // set here again as floating point, because constructor above only takes integer + setMargins(QMargins(2, 2, 2, 2)); +} + +/*! \overload + + Creates a new QCPTextElement instance and sets default values. + + The initial text is set to \a text with the specified \a font. +*/ +QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QFont &font) : + QCPLayoutElement(parentPlot), + mText(text), + mTextFlags(Qt::AlignCenter), + mFont(font), + mTextColor(Qt::black), + mSelectedFont(font), + mSelectedTextColor(Qt::blue), + mSelectable(false), + mSelected(false) +{ + setMargins(QMargins(2, 2, 2, 2)); +} + +/*! + Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n". + + \see setFont, setTextColor, setTextFlags +*/ +void QCPTextElement::setText(const QString &text) +{ + mText = text; +} + +/*! + Sets options for text alignment and wrapping behaviour. \a flags is a bitwise OR-combination of + \c Qt::AlignmentFlag and \c Qt::TextFlag enums. + + Possible enums are: + - Qt::AlignLeft + - Qt::AlignRight + - Qt::AlignHCenter + - Qt::AlignJustify + - Qt::AlignTop + - Qt::AlignBottom + - Qt::AlignVCenter + - Qt::AlignCenter + - Qt::TextDontClip + - Qt::TextSingleLine + - Qt::TextExpandTabs + - Qt::TextShowMnemonic + - Qt::TextWordWrap + - Qt::TextIncludeTrailingSpaces +*/ +void QCPTextElement::setTextFlags(int flags) +{ + mTextFlags = flags; +} + +/*! + Sets the \a font of the text. + + \see setTextColor, setSelectedFont +*/ +void QCPTextElement::setFont(const QFont &font) +{ + mFont = font; +} + +/*! + Sets the \a color of the text. + + \see setFont, setSelectedTextColor +*/ +void QCPTextElement::setTextColor(const QColor &color) +{ + mTextColor = color; +} + +/*! + Sets the \a font of the text that will be used if the text element is selected (\ref setSelected). + + \see setFont +*/ +void QCPTextElement::setSelectedFont(const QFont &font) +{ + mSelectedFont = font; +} + +/*! + Sets the \a color of the text that will be used if the text element is selected (\ref setSelected). + + \see setTextColor +*/ +void QCPTextElement::setSelectedTextColor(const QColor &color) +{ + mSelectedTextColor = color; +} + +/*! + Sets whether the user may select this text element. + + Note that even when \a selectable is set to false, the selection state may be changed + programmatically via \ref setSelected. +*/ +void QCPTextElement::setSelectable(bool selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + emit selectableChanged(mSelectable); + } +} + +/*! + Sets the selection state of this text element to \a selected. If the selection has changed, \ref + selectionChanged is emitted. + + Note that this function can change the selection state independently of the current \ref + setSelectable state. +*/ +void QCPTextElement::setSelected(bool selected) +{ + if (mSelected != selected) + { + mSelected = selected; + emit selectionChanged(mSelected); + } +} + +/* inherits documentation from base class */ +void QCPTextElement::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeOther); +} + +/* inherits documentation from base class */ +void QCPTextElement::draw(QCPPainter *painter) +{ + painter->setFont(mainFont()); + painter->setPen(QPen(mainTextColor())); + painter->drawText(mRect, mTextFlags, mText, &mTextBoundingRect); +} + +/* inherits documentation from base class */ +QSize QCPTextElement::minimumOuterSizeHint() const +{ + QFontMetrics metrics(mFont); + QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size()); + result.rwidth() += mMargins.left()+mMargins.right(); + result.rheight() += mMargins.top()+mMargins.bottom(); + return result; +} + +/* inherits documentation from base class */ +QSize QCPTextElement::maximumOuterSizeHint() const +{ + QFontMetrics metrics(mFont); + QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size()); + result.setWidth(QWIDGETSIZE_MAX); + result.rheight() += mMargins.top()+mMargins.bottom(); + return result; +} + +/* inherits documentation from base class */ +void QCPTextElement::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + Q_UNUSED(details) + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(additive ? !mSelected : true); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPTextElement::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable) + { + bool selBefore = mSelected; + setSelected(false); + if (selectionStateChanged) + *selectionStateChanged = mSelected != selBefore; + } +} + +/*! + Returns 0.99*selectionTolerance (see \ref QCustomPlot::setSelectionTolerance) when \a pos is + within the bounding box of the text element's text. Note that this bounding box is updated in the + draw call. + + If \a pos is outside the text's bounding box or if \a onlySelectable is true and this text + element is not selectable (\ref setSelectable), returns -1. + + \seebaseclassmethod +*/ +double QCPTextElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + if (mTextBoundingRect.contains(pos.toPoint())) + return mParentPlot->selectionTolerance()*0.99; + else + return -1; +} + +/*! + Accepts the mouse event in order to emit the according click signal in the \ref + mouseReleaseEvent. + + \seebaseclassmethod +*/ +void QCPTextElement::mousePressEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + event->accept(); +} + +/*! + Emits the \ref clicked signal if the cursor hasn't moved by more than a few pixels since the \ref + mousePressEvent. + + \seebaseclassmethod +*/ +void QCPTextElement::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + if ((QPointF(event->pos())-startPos).manhattanLength() <= 3) + emit clicked(event); +} + +/*! + Emits the \ref doubleClicked signal. + + \seebaseclassmethod +*/ +void QCPTextElement::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + emit doubleClicked(event); +} + +/*! \internal + + Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to + true, else mFont is returned. +*/ +QFont QCPTextElement::mainFont() const +{ + return mSelected ? mSelectedFont : mFont; +} + +/*! \internal + + Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to + true, else mTextColor is returned. +*/ +QColor QCPTextElement::mainTextColor() const +{ + return mSelected ? mSelectedTextColor : mTextColor; +} +/* end of 'src/layoutelements/layoutelement-textelement.cpp' */ + + +/* including file 'src/layoutelements/layoutelement-colorscale.cpp' */ +/* modified 2022-11-06T12:45:56, size 26531 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorScale +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorScale + \brief A color scale for use with color coding data such as QCPColorMap + + This layout element can be placed on the plot to correlate a color gradient with data values. It + is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps". + + \image html QCPColorScale.png + + The color scale can be either horizontal or vertical, as shown in the image above. The + orientation and the side where the numbers appear is controlled with \ref setType. + + Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are + connected, they share their gradient, data range and data scale type (\ref setGradient, \ref + setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color + scale, to make them all synchronize these properties. + + To have finer control over the number display and axis behaviour, you can directly access the + \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if + you want to change the number of automatically generated ticks, call + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-tickcount + + Placing a color scale next to the main axis rect works like with any other layout element: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-creation + In this case we have placed it to the right of the default axis rect, so it wasn't necessary to + call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color + scale can be set with \ref setLabel. + + For optimum appearance (like in the image above), it may be desirable to line up the axis rect and + the borders of the color scale. Use a \ref QCPMarginGroup to achieve this: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-margingroup + + Color scales are initialized with a non-zero minimum top and bottom margin (\ref + setMinimumMargins), because vertical color scales are most common and the minimum top/bottom + margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a + horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you + might want to also change the minimum margins accordingly, e.g. setMinimumMargins(QMargins(6, 0, 6, 0)). +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPAxis *QCPColorScale::axis() const + + Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the + appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its + interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref + setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref + QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on + the QCPColorScale or on its QCPAxis. + + If the type of the color scale is changed with \ref setType, the axis returned by this method + will change, too, to either the left, right, bottom or top axis, depending on which type was set. +*/ + +/* end documentation of signals */ +/* start documentation of signals */ + +/*! \fn void QCPColorScale::dataRangeChanged(const QCPRange &newRange); + + This signal is emitted when the data range changes. + + \see setDataRange +*/ + +/*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType); + + This signal is emitted when the data scale type changes. + + \see setDataScaleType +*/ + +/*! \fn void QCPColorScale::gradientChanged(const QCPColorGradient &newGradient); + + This signal is emitted when the gradient changes. + + \see setGradient +*/ + +/* end documentation of signals */ + +/*! + Constructs a new QCPColorScale. +*/ +QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) : + QCPLayoutElement(parentPlot), + mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight + mDataScaleType(QCPAxis::stLinear), + mGradient(QCPColorGradient::gpCold), + mBarWidth(20), + mAxisRect(new QCPColorScaleAxisRectPrivate(this)) +{ + setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used) + setType(QCPAxis::atRight); + setDataRange(QCPRange(0, 6)); +} + +QCPColorScale::~QCPColorScale() +{ + delete mAxisRect; +} + +/* undocumented getter */ +QString QCPColorScale::label() const +{ + if (!mColorAxis) + { + qDebug() << Q_FUNC_INFO << "internal color axis undefined"; + return QString(); + } + + return mColorAxis.data()->label(); +} + +/* undocumented getter */ +bool QCPColorScale::rangeDrag() const +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return false; + } + + return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) && + mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) && + mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType); +} + +/* undocumented getter */ +bool QCPColorScale::rangeZoom() const +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return false; + } + + return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) && + mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) && + mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType); +} + +/*! + Sets at which side of the color scale the axis is placed, and thus also its orientation. + + Note that after setting \a type to a different value, the axis returned by \ref axis() will + be a different one. The new axis will adopt the following properties from the previous axis: The + range, scale type, label and ticker (the latter will be shared and not copied). +*/ +void QCPColorScale::setType(QCPAxis::AxisType type) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + if (mType != type) + { + mType = type; + QCPRange rangeTransfer(0, 6); + QString labelTransfer; + QSharedPointer tickerTransfer; + // transfer/revert some settings on old axis if it exists: + bool doTransfer = !mColorAxis.isNull(); + if (doTransfer) + { + rangeTransfer = mColorAxis.data()->range(); + labelTransfer = mColorAxis.data()->label(); + tickerTransfer = mColorAxis.data()->ticker(); + mColorAxis.data()->setLabel(QString()); + disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); + disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); + } + const QList allAxisTypes = QList() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop; + foreach (QCPAxis::AxisType atype, allAxisTypes) + { + mAxisRect.data()->axis(atype)->setTicks(atype == mType); + mAxisRect.data()->axis(atype)->setTickLabels(atype== mType); + } + // set new mColorAxis pointer: + mColorAxis = mAxisRect.data()->axis(mType); + // transfer settings to new axis: + if (doTransfer) + { + mColorAxis.data()->setRange(rangeTransfer); // range transfer necessary if axis changes from vertical to horizontal or vice versa (axes with same orientation are synchronized via signals) + mColorAxis.data()->setLabel(labelTransfer); + mColorAxis.data()->setTicker(tickerTransfer); + } + connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); + connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); + mAxisRect.data()->setRangeDragAxes(QList() << mColorAxis.data()); + } +} + +/*! + Sets the range spanned by the color gradient and that is shown by the axis in the color scale. + + It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is + also equivalent to directly accessing the \ref axis and setting its range with \ref + QCPAxis::setRange. + + \see setDataScaleType, setGradient, rescaleDataRange +*/ +void QCPColorScale::setDataRange(const QCPRange &dataRange) +{ + if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper) + { + mDataRange = dataRange; + if (mColorAxis) + mColorAxis.data()->setRange(mDataRange); + emit dataRangeChanged(mDataRange); + } +} + +/*! + Sets the scale type of the color scale, i.e. whether values are associated with colors linearly + or logarithmically. + + It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is + also equivalent to directly accessing the \ref axis and setting its scale type with \ref + QCPAxis::setScaleType. + + Note that this method controls the coordinate transformation. For logarithmic scales, you will + likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting + the color scale's \ref axis ticker to an instance of \ref QCPAxisTickerLog : + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-colorscale + + See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick + creation. + + \see setDataRange, setGradient +*/ +void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType) +{ + if (mDataScaleType != scaleType) + { + mDataScaleType = scaleType; + if (mColorAxis) + mColorAxis.data()->setScaleType(mDataScaleType); + if (mDataScaleType == QCPAxis::stLogarithmic) + setDataRange(mDataRange.sanitizedForLogScale()); + emit dataScaleTypeChanged(mDataScaleType); + } +} + +/*! + Sets the color gradient that will be used to represent data values. + + It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps. + + \see setDataRange, setDataScaleType +*/ +void QCPColorScale::setGradient(const QCPColorGradient &gradient) +{ + if (mGradient != gradient) + { + mGradient = gradient; + if (mAxisRect) + mAxisRect.data()->mGradientImageInvalidated = true; + emit gradientChanged(mGradient); + } +} + +/*! + Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on + the internal \ref axis. +*/ +void QCPColorScale::setLabel(const QString &str) +{ + if (!mColorAxis) + { + qDebug() << Q_FUNC_INFO << "internal color axis undefined"; + return; + } + + mColorAxis.data()->setLabel(str); +} + +/*! + Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed + will have. +*/ +void QCPColorScale::setBarWidth(int width) +{ + mBarWidth = width; +} + +/*! + Sets whether the user can drag the data range (\ref setDataRange). + + Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref + QCustomPlot::setInteractions) to allow range dragging. +*/ +void QCPColorScale::setRangeDrag(bool enabled) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + + if (enabled) + { + mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType)); + } else + { +#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) + mAxisRect.data()->setRangeDrag(nullptr); +#else + mAxisRect.data()->setRangeDrag({}); +#endif + } +} + +/*! + Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel. + + Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref + QCustomPlot::setInteractions) to allow range dragging. +*/ +void QCPColorScale::setRangeZoom(bool enabled) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + + if (enabled) + { + mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType)); + } else + { +#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) + mAxisRect.data()->setRangeDrag(nullptr); +#else + mAxisRect.data()->setRangeZoom({}); +#endif + } +} + +/*! + Returns a list of all the color maps associated with this color scale. +*/ +QList QCPColorScale::colorMaps() const +{ + QList result; + for (int i=0; iplottableCount(); ++i) + { + if (QCPColorMap *cm = qobject_cast(mParentPlot->plottable(i))) + if (cm->colorScale() == this) + result.append(cm); + } + return result; +} + +/*! + Changes the data range such that all color maps associated with this color scale are fully mapped + to the gradient in the data dimension. + + \see setDataRange +*/ +void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps) +{ + QList maps = colorMaps(); + QCPRange newRange; + bool haveRange = false; + QCP::SignDomain sign = QCP::sdBoth; + if (mDataScaleType == QCPAxis::stLogarithmic) + sign = (mDataRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive); + foreach (QCPColorMap *map, maps) + { + if (!map->realVisibility() && onlyVisibleMaps) + continue; + QCPRange mapRange; + if (map->colorScale() == this) + { + bool currentFoundRange = true; + mapRange = map->data()->dataBounds(); + if (sign == QCP::sdPositive) + { + if (mapRange.lower <= 0 && mapRange.upper > 0) + mapRange.lower = mapRange.upper*1e-3; + else if (mapRange.lower <= 0 && mapRange.upper <= 0) + currentFoundRange = false; + } else if (sign == QCP::sdNegative) + { + if (mapRange.upper >= 0 && mapRange.lower < 0) + mapRange.upper = mapRange.lower*1e-3; + else if (mapRange.upper >= 0 && mapRange.lower >= 0) + currentFoundRange = false; + } + if (currentFoundRange) + { + if (!haveRange) + newRange = mapRange; + else + newRange.expand(mapRange); + haveRange = true; + } + } + } + if (haveRange) + { + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (mDataScaleType == QCPAxis::stLinear) + { + newRange.lower = center-mDataRange.size()/2.0; + newRange.upper = center+mDataRange.size()/2.0; + } else // mScaleType == stLogarithmic + { + newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower); + newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower); + } + } + setDataRange(newRange); + } +} + +/* inherits documentation from base class */ +void QCPColorScale::update(UpdatePhase phase) +{ + QCPLayoutElement::update(phase); + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + + mAxisRect.data()->update(phase); + + switch (phase) + { + case upMargins: + { + if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop) + { + setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()); + setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()); + } else + { + setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), QWIDGETSIZE_MAX); + setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), 0); + } + break; + } + case upLayout: + { + mAxisRect.data()->setOuterRect(rect()); + break; + } + default: break; + } +} + +/* inherits documentation from base class */ +void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + painter->setAntialiasing(false); +} + +/* inherits documentation from base class */ +void QCPColorScale::mousePressEvent(QMouseEvent *event, const QVariant &details) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + mAxisRect.data()->mousePressEvent(event, details); +} + +/* inherits documentation from base class */ +void QCPColorScale::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + mAxisRect.data()->mouseMoveEvent(event, startPos); +} + +/* inherits documentation from base class */ +void QCPColorScale::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + mAxisRect.data()->mouseReleaseEvent(event, startPos); +} + +/* inherits documentation from base class */ +void QCPColorScale::wheelEvent(QWheelEvent *event) +{ + if (!mAxisRect) + { + qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; + return; + } + mAxisRect.data()->wheelEvent(event); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorScaleAxisRectPrivate +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorScaleAxisRectPrivate + + \internal + \brief An axis rect subclass for use in a QCPColorScale + + This is a private class and not part of the public QCustomPlot interface. + + It provides the axis rect functionality for the QCPColorScale class. +*/ + + +/*! + Creates a new instance, as a child of \a parentColorScale. +*/ +QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) : + QCPAxisRect(parentColorScale->parentPlot(), true), + mParentColorScale(parentColorScale), + mGradientImageInvalidated(true) +{ + setParentLayerable(parentColorScale); + setMinimumMargins(QMargins(0, 0, 0, 0)); + const QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) + { + axis(type)->setVisible(true); + axis(type)->grid()->setVisible(false); + axis(type)->setPadding(0); + connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts))); + connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts))); + } + + connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange))); + connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange))); + connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange))); + connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange))); + connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType))); + connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType))); + connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType))); + connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType))); + + // make layer transfers of color scale transfer to axis rect and axes + // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect: + connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*))); + foreach (QCPAxis::AxisType type, allAxisTypes) + connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*))); +} + +/*! \internal + + Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws + it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation. + + \seebaseclassmethod +*/ +void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter) +{ + if (mGradientImageInvalidated) + updateGradientImage(); + + bool mirrorHorz = false; + bool mirrorVert = false; + if (mParentColorScale->mColorAxis) + { + mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop); + mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight); + } + + painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert)); + QCPAxisRect::draw(painter); +} + +/*! \internal + + Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to + generate a gradient image. This gradient image will be used in the \ref draw method. +*/ +void QCPColorScaleAxisRectPrivate::updateGradientImage() +{ + if (rect().isEmpty()) + return; + + const QImage::Format format = QImage::Format_ARGB32_Premultiplied; + int n = mParentColorScale->mGradient.levelCount(); + int w, h; + QVector data(n); + for (int i=0; imType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop) + { + w = n; + h = rect().height(); + mGradientImage = QImage(w, h, format); + QVector pixels; + for (int y=0; y(mGradientImage.scanLine(y))); + mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n); + for (int y=1; y(mGradientImage.scanLine(y)); + const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1)); + for (int x=0; x allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) + { + if (QCPAxis *senderAxis = qobject_cast(sender())) + if (senderAxis->axisType() == type) + continue; + + if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) + { + if (selectedParts.testFlag(QCPAxis::spAxis)) + axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis); + else + axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis); + } + } +} + +/*! \internal + + This slot is connected to the selectableChanged signals of the four axes in the constructor. It + synchronizes the selectability of the axes. +*/ +void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts) +{ + // synchronize axis base selectability: + const QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) + { + if (QCPAxis *senderAxis = qobject_cast(sender())) + if (senderAxis->axisType() == type) + continue; + + if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis)) + { + if (selectableParts.testFlag(QCPAxis::spAxis)) + axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis); + else + axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis); + } + } +} +/* end of 'src/layoutelements/layoutelement-colorscale.cpp' */ + + +/* including file 'src/plottables/plottable-graph.cpp' */ +/* modified 2022-11-06T12:45:57, size 74926 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPGraphData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPGraphData + \brief Holds the data of one single data point for QCPGraph. + + The stored data is: + \li \a key: coordinate on the key axis of this data point (this is the \a mainKey and the \a sortKey) + \li \a value: coordinate on the value axis of this data point (this is the \a mainValue) + + The container for storing multiple data points is \ref QCPGraphDataContainer. It is a typedef for + \ref QCPDataContainer with \ref QCPGraphData as the DataType template parameter. See the + documentation there for an explanation regarding the data type's generic methods. + + \see QCPGraphDataContainer +*/ + +/* start documentation of inline functions */ + +/*! \fn double QCPGraphData::sortKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static QCPGraphData QCPGraphData::fromSortKey(double sortKey) + + Returns a data point with the specified \a sortKey. All other members are set to zero. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static static bool QCPGraphData::sortKeyIsMainKey() + + Since the member \a key is both the data point key coordinate and the data ordering parameter, + this method returns true. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPGraphData::mainKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPGraphData::mainValue() const + + Returns the \a value member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn QCPRange QCPGraphData::valueRange() const + + Returns a QCPRange with both lower and upper boundary set to \a value of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/* end documentation of inline functions */ + +/*! + Constructs a data point with key and value set to zero. +*/ +QCPGraphData::QCPGraphData() : + key(0), + value(0) +{ +} + +/*! + Constructs a data point with the specified \a key and \a value. +*/ +QCPGraphData::QCPGraphData(double key, double value) : + key(key), + value(value) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPGraph +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPGraph + \brief A plottable representing a graph in a plot. + + \image html QCPGraph.png + + Usually you create new graphs by calling QCustomPlot::addGraph. The resulting instance can be + accessed via QCustomPlot::graph. + + To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can + also access and modify the data via the \ref data method, which returns a pointer to the internal + \ref QCPGraphDataContainer. + + Graphs are used to display single-valued data. Single-valued means that there should only be one + data point per unique key coordinate. In other words, the graph can't have \a loops. If you do + want to plot non-single-valued curves, rather use the QCPCurve plottable. + + Gaps in the graph line can be created by adding data points with NaN as value + (qQNaN() or std::numeric_limits::quiet_NaN()) in between the two data points that shall be + separated. + + \section qcpgraph-appearance Changing the appearance + + The appearance of the graph is mainly determined by the line style, scatter style, brush and pen + of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen). + + \subsection filling Filling under or between graphs + + QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to + the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill, + just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent. + + By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill + between this graph and another one, call \ref setChannelFillGraph with the other graph as + parameter. + + \see QCustomPlot::addGraph, QCustomPlot::graph +*/ + +/* start of documentation of inline functions */ + +/*! \fn QSharedPointer QCPGraph::data() const + + Returns a shared pointer to the internal data storage of type \ref QCPGraphDataContainer. You may + use it to directly manipulate the data, which may be more convenient and faster than using the + regular \ref setData or \ref addData methods. +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + The created QCPGraph is automatically registered with the QCustomPlot instance inferred from \a + keyAxis. This QCustomPlot instance takes ownership of the QCPGraph, so do not delete it manually + but use QCustomPlot::removePlottable() instead. + + To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function. +*/ +QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable1D(keyAxis, valueAxis), + mLineStyle{}, + mScatterSkip{}, + mAdaptiveSampling{} +{ + // special handling for QCPGraphs to maintain the simple graph interface: + mParentPlot->registerGraph(this); + + setPen(QPen(Qt::blue, 0)); + setBrush(Qt::NoBrush); + + setLineStyle(lsLine); + setScatterSkip(0); + setChannelFillGraph(nullptr); + setAdaptiveSampling(true); +} + +QCPGraph::~QCPGraph() +{ +} + +/*! \overload + + Replaces the current data container with the provided \a data container. + + Since a QSharedPointer is used, multiple QCPGraphs may share the same data container safely. + Modifying the data in the container will then affect all graphs that share the container. Sharing + can be achieved by simply exchanging the data containers wrapped in shared pointers: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpgraph-datasharing-1 + + If you do not wish to share containers, but create a copy from an existing container, rather use + the \ref QCPDataContainer::set method on the graph's data container directly: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpgraph-datasharing-2 + + \see addData +*/ +void QCPGraph::setData(QSharedPointer data) +{ + mDataContainer = data; +} + +/*! \overload + + Replaces the current data with the provided points in \a keys and \a values. The provided + vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + \see addData +*/ +void QCPGraph::setData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + mDataContainer->clear(); + addData(keys, values, alreadySorted); +} + +/*! + Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to + \ref lsNone and \ref setScatterStyle to the desired scatter style. + + \see setScatterStyle +*/ +void QCPGraph::setLineStyle(LineStyle ls) +{ + mLineStyle = ls; +} + +/*! + Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points + are drawn (e.g. for line-only-plots with appropriate line style). + + \see QCPScatterStyle, setLineStyle +*/ +void QCPGraph::setScatterStyle(const QCPScatterStyle &style) +{ + mScatterStyle = style; +} + +/*! + If scatters are displayed (scatter style not \ref QCPScatterStyle::ssNone), \a skip number of + scatter points are skipped/not drawn after every drawn scatter point. + + This can be used to make the data appear sparser while for example still having a smooth line, + and to improve performance for very high density plots. + + If \a skip is set to 0 (default), all scatter points are drawn. + + \see setScatterStyle +*/ +void QCPGraph::setScatterSkip(int skip) +{ + mScatterSkip = qMax(0, skip); +} + +/*! + Sets the target graph for filling the area between this graph and \a targetGraph with the current + brush (\ref setBrush). + + When \a targetGraph is set to 0, a normal graph fill to the zero-value-line will be shown. To + disable any filling, set the brush to Qt::NoBrush. + + \see setBrush +*/ +void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph) +{ + // prevent setting channel target to this graph itself: + if (targetGraph == this) + { + qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself"; + mChannelFillGraph = nullptr; + return; + } + // prevent setting channel target to a graph not in the plot: + if (targetGraph && targetGraph->mParentPlot != mParentPlot) + { + qDebug() << Q_FUNC_INFO << "targetGraph not in same plot"; + mChannelFillGraph = nullptr; + return; + } + + mChannelFillGraph = targetGraph; +} + +/*! + Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive + sampling technique can drastically improve the replot performance for graphs with a larger number + of points (e.g. above 10,000), without notably changing the appearance of the graph. + + By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive + sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no + disadvantage in almost all cases. + + \image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling" + + As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are + reproduced reliably, as well as the overall shape of the data set. The replot time reduces + dramatically though. This allows QCustomPlot to display large amounts of data in realtime. + + \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling" + + Care must be taken when using high-density scatter plots in combination with adaptive sampling. + The adaptive sampling algorithm treats scatter plots more carefully than line plots which still + gives a significant reduction of replot times, but not quite as much as for line plots. This is + because scatter plots inherently need more data points to be preserved in order to still resemble + the original, non-adaptive-sampling plot. As shown above, the results still aren't quite + identical, as banding occurs for the outer data points. This is in fact intentional, such that + the boundaries of the data cloud stay visible to the viewer. How strong the banding appears, + depends on the point density, i.e. the number of points in the plot. + + For some situations with scatter plots it might thus be desirable to manually turn adaptive + sampling off. For example, when saving the plot to disk. This can be achieved by setting \a + enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled + back to true afterwards. +*/ +void QCPGraph::setAdaptiveSampling(bool enabled) +{ + mAdaptiveSampling = enabled; +} + +/*! \overload + + Adds the provided points in \a keys and \a values to the current data. The provided vectors + should have equal length. Else, the number of added points will be the size of the smallest + vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPGraph::addData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + if (keys.size() != values.size()) + qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size(); + const int n = static_cast(qMin(keys.size(), values.size())); + QVector tempData(n); + QVector::iterator it = tempData.begin(); + const QVector::iterator itEnd = tempData.end(); + int i = 0; + while (it != itEnd) + { + it->key = keys[i]; + it->value = values[i]; + ++it; + ++i; + } + mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write +} + +/*! \overload + + Adds the provided data point as \a key and \a value to the current data. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPGraph::addData(double key, double value) +{ + mDataContainer->add(QCPGraphData(key, value)); +} + +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ +double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) + { + QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); + double result = pointDistance(pos, closestDataPoint); + if (details) + { + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); + details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); + } + return result; + } else + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + return mDataContainer->keyRange(foundRange, inSignDomain); +} + +/* inherits documentation from base class */ +QCPRange QCPGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange); +} + +/* inherits documentation from base class */ +void QCPGraph::draw(QCPPainter *painter) +{ + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return; + if (mLineStyle == lsNone && mScatterStyle.isNone()) return; + + QVector lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments + + // loop over and draw segments of unselected/selected data: + QList selectedSegments, unselectedSegments, allSegments; + getDataSegments(selectedSegments, unselectedSegments); + allSegments << unselectedSegments << selectedSegments; + for (int i=0; i= unselectedSegments.size(); + // get line pixel points appropriate to line style: + QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getLines takes care) + getLines(&lines, lineDataRange); + + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + QCPGraphDataContainer::const_iterator it; + for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it) + { + if (QCP::isInvalidData(it->key, it->value)) + qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name(); + } +#endif + + // draw fill of graph: + if (isSelectedSegment && mSelectionDecorator) + mSelectionDecorator->applyBrush(painter); + else + painter->setBrush(mBrush); + painter->setPen(Qt::NoPen); + drawFill(painter, &lines); + + // draw line: + if (mLineStyle != lsNone) + { + if (isSelectedSegment && mSelectionDecorator) + mSelectionDecorator->applyPen(painter); + else + painter->setPen(mPen); + painter->setBrush(Qt::NoBrush); + if (mLineStyle == lsImpulse) + drawImpulsePlot(painter, lines); + else + drawLinePlot(painter, lines); // also step plots can be drawn as a line plot + } + + // draw scatters: + QCPScatterStyle finalScatterStyle = mScatterStyle; + if (isSelectedSegment && mSelectionDecorator) + finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle); + if (!finalScatterStyle.isNone()) + { + getScatters(&scatters, allSegments.at(i)); + drawScatterPlot(painter, scatters, finalScatterStyle); + } + } + + // draw other selection decoration that isn't just line/scatter pens and brushes: + if (mSelectionDecorator) + mSelectionDecorator->drawDecoration(painter, selection()); +} + +/* inherits documentation from base class */ +void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + // draw fill: + if (mBrush.style() != Qt::NoBrush) + { + applyFillAntialiasingHint(painter); + painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush); + } + // draw line vertically centered: + if (mLineStyle != lsNone) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens + } + // draw scatter symbol: + if (!mScatterStyle.isNone()) + { + applyScattersAntialiasingHint(painter); + // scale scatter pixmap if it's too large to fit in legend icon rect: + if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height())) + { + QCPScatterStyle scaledStyle(mScatterStyle); + scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + scaledStyle.applyTo(painter, mPen); + scaledStyle.drawShape(painter, QRectF(rect).center()); + } else + { + mScatterStyle.applyTo(painter, mPen); + mScatterStyle.drawShape(painter, QRectF(rect).center()); + } + } +} + +/*! \internal + + This method retrieves an optimized set of data points via \ref getOptimizedLineData, and branches + out to the line style specific functions such as \ref dataToLines, \ref dataToStepLeftLines, etc. + according to the line style of the graph. + + \a lines will be filled with points in pixel coordinates, that can be drawn with the according + draw functions like \ref drawLinePlot and \ref drawImpulsePlot. The points returned in \a lines + aren't necessarily the original data points. For example, step line styles require additional + points to form the steps when drawn. If the line style of the graph is \ref lsNone, the \a + lines vector will be empty. + + \a dataRange specifies the beginning and ending data indices that will be taken into account for + conversion. In this function, the specified range may exceed the total data bounds without harm: + a correspondingly trimmed data range will be used. This takes the burden off the user of this + function to check for valid indices in \a dataRange, e.g. when extending ranges coming from \ref + getDataSegments. + + \see getScatters +*/ +void QCPGraph::getLines(QVector *lines, const QCPDataRange &dataRange) const +{ + if (!lines) return; + QCPGraphDataContainer::const_iterator begin, end; + getVisibleDataBounds(begin, end, dataRange); + if (begin == end) + { + lines->clear(); + return; + } + + QVector lineData; + if (mLineStyle != lsNone) + getOptimizedLineData(&lineData, begin, end); + + if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in lineData (significantly simplifies following processing) + std::reverse(lineData.begin(), lineData.end()); + + switch (mLineStyle) + { + case lsNone: lines->clear(); break; + case lsLine: *lines = dataToLines(lineData); break; + case lsStepLeft: *lines = dataToStepLeftLines(lineData); break; + case lsStepRight: *lines = dataToStepRightLines(lineData); break; + case lsStepCenter: *lines = dataToStepCenterLines(lineData); break; + case lsImpulse: *lines = dataToImpulseLines(lineData); break; + } +} + +/*! \internal + + This method retrieves an optimized set of data points via \ref getOptimizedScatterData and then + converts them to pixel coordinates. The resulting points are returned in \a scatters, and can be + passed to \ref drawScatterPlot. + + \a dataRange specifies the beginning and ending data indices that will be taken into account for + conversion. In this function, the specified range may exceed the total data bounds without harm: + a correspondingly trimmed data range will be used. This takes the burden off the user of this + function to check for valid indices in \a dataRange, e.g. when extending ranges coming from \ref + getDataSegments. +*/ +void QCPGraph::getScatters(QVector *scatters, const QCPDataRange &dataRange) const +{ + if (!scatters) return; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; scatters->clear(); return; } + + QCPGraphDataContainer::const_iterator begin, end; + getVisibleDataBounds(begin, end, dataRange); + if (begin == end) + { + scatters->clear(); + return; + } + + QVector data; + getOptimizedScatterData(&data, begin, end); + + if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in data (significantly simplifies following processing) + std::reverse(data.begin(), data.end()); + + scatters->resize(data.size()); + if (keyAxis->orientation() == Qt::Vertical) + { + for (int i=0; icoordToPixel(data.at(i).value)); + (*scatters)[i].setY(keyAxis->coordToPixel(data.at(i).key)); + } + } + } else + { + for (int i=0; icoordToPixel(data.at(i).key)); + (*scatters)[i].setY(valueAxis->coordToPixel(data.at(i).value)); + } + } + } +} + +/*! \internal + + Takes raw data points in plot coordinates as \a data, and returns a vector containing pixel + coordinate points which are suitable for drawing the line style \ref lsLine. + + The source of \a data is usually \ref getOptimizedLineData, and this method is called in \a + getLines if the line style is set accordingly. + + \see dataToStepLeftLines, dataToStepRightLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot +*/ +QVector QCPGraph::dataToLines(const QVector &data) const +{ + QVector result; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } + + result.resize(data.size()); + + // transform data points to pixels: + if (keyAxis->orientation() == Qt::Vertical) + { + for (int i=0; icoordToPixel(data.at(i).value)); + result[i].setY(keyAxis->coordToPixel(data.at(i).key)); + } + } else // key axis is horizontal + { + for (int i=0; icoordToPixel(data.at(i).key)); + result[i].setY(valueAxis->coordToPixel(data.at(i).value)); + } + } + return result; +} + +/*! \internal + + Takes raw data points in plot coordinates as \a data, and returns a vector containing pixel + coordinate points which are suitable for drawing the line style \ref lsStepLeft. + + The source of \a data is usually \ref getOptimizedLineData, and this method is called in \a + getLines if the line style is set accordingly. + + \see dataToLines, dataToStepRightLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot +*/ +QVector QCPGraph::dataToStepLeftLines(const QVector &data) const +{ + QVector result; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } + + result.resize(data.size()*2); + + // calculate steps from data and transform to pixel coordinates: + if (keyAxis->orientation() == Qt::Vertical) + { + double lastValue = valueAxis->coordToPixel(data.first().value); + for (int i=0; icoordToPixel(data.at(i).key); + result[i*2+0].setX(lastValue); + result[i*2+0].setY(key); + lastValue = valueAxis->coordToPixel(data.at(i).value); + result[i*2+1].setX(lastValue); + result[i*2+1].setY(key); + } + } else // key axis is horizontal + { + double lastValue = valueAxis->coordToPixel(data.first().value); + for (int i=0; icoordToPixel(data.at(i).key); + result[i*2+0].setX(key); + result[i*2+0].setY(lastValue); + lastValue = valueAxis->coordToPixel(data.at(i).value); + result[i*2+1].setX(key); + result[i*2+1].setY(lastValue); + } + } + return result; +} + +/*! \internal + + Takes raw data points in plot coordinates as \a data, and returns a vector containing pixel + coordinate points which are suitable for drawing the line style \ref lsStepRight. + + The source of \a data is usually \ref getOptimizedLineData, and this method is called in \a + getLines if the line style is set accordingly. + + \see dataToLines, dataToStepLeftLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot +*/ +QVector QCPGraph::dataToStepRightLines(const QVector &data) const +{ + QVector result; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } + + result.resize(data.size()*2); + + // calculate steps from data and transform to pixel coordinates: + if (keyAxis->orientation() == Qt::Vertical) + { + double lastKey = keyAxis->coordToPixel(data.first().key); + for (int i=0; icoordToPixel(data.at(i).value); + result[i*2+0].setX(value); + result[i*2+0].setY(lastKey); + lastKey = keyAxis->coordToPixel(data.at(i).key); + result[i*2+1].setX(value); + result[i*2+1].setY(lastKey); + } + } else // key axis is horizontal + { + double lastKey = keyAxis->coordToPixel(data.first().key); + for (int i=0; icoordToPixel(data.at(i).value); + result[i*2+0].setX(lastKey); + result[i*2+0].setY(value); + lastKey = keyAxis->coordToPixel(data.at(i).key); + result[i*2+1].setX(lastKey); + result[i*2+1].setY(value); + } + } + return result; +} + +/*! \internal + + Takes raw data points in plot coordinates as \a data, and returns a vector containing pixel + coordinate points which are suitable for drawing the line style \ref lsStepCenter. + + The source of \a data is usually \ref getOptimizedLineData, and this method is called in \a + getLines if the line style is set accordingly. + + \see dataToLines, dataToStepLeftLines, dataToStepRightLines, dataToImpulseLines, getLines, drawLinePlot +*/ +QVector QCPGraph::dataToStepCenterLines(const QVector &data) const +{ + QVector result; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } + + result.resize(data.size()*2); + + // calculate steps from data and transform to pixel coordinates: + if (keyAxis->orientation() == Qt::Vertical) + { + double lastKey = keyAxis->coordToPixel(data.first().key); + double lastValue = valueAxis->coordToPixel(data.first().value); + result[0].setX(lastValue); + result[0].setY(lastKey); + for (int i=1; icoordToPixel(data.at(i).key)+lastKey)*0.5; + result[i*2-1].setX(lastValue); + result[i*2-1].setY(key); + lastValue = valueAxis->coordToPixel(data.at(i).value); + lastKey = keyAxis->coordToPixel(data.at(i).key); + result[i*2+0].setX(lastValue); + result[i*2+0].setY(key); + } + result[data.size()*2-1].setX(lastValue); + result[data.size()*2-1].setY(lastKey); + } else // key axis is horizontal + { + double lastKey = keyAxis->coordToPixel(data.first().key); + double lastValue = valueAxis->coordToPixel(data.first().value); + result[0].setX(lastKey); + result[0].setY(lastValue); + for (int i=1; icoordToPixel(data.at(i).key)+lastKey)*0.5; + result[i*2-1].setX(key); + result[i*2-1].setY(lastValue); + lastValue = valueAxis->coordToPixel(data.at(i).value); + lastKey = keyAxis->coordToPixel(data.at(i).key); + result[i*2+0].setX(key); + result[i*2+0].setY(lastValue); + } + result[data.size()*2-1].setX(lastKey); + result[data.size()*2-1].setY(lastValue); + } + return result; +} + +/*! \internal + + Takes raw data points in plot coordinates as \a data, and returns a vector containing pixel + coordinate points which are suitable for drawing the line style \ref lsImpulse. + + The source of \a data is usually \ref getOptimizedLineData, and this method is called in \a + getLines if the line style is set accordingly. + + \see dataToLines, dataToStepLeftLines, dataToStepRightLines, dataToStepCenterLines, getLines, drawImpulsePlot +*/ +QVector QCPGraph::dataToImpulseLines(const QVector &data) const +{ + QVector result; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } + + result.resize(data.size()*2); + + // transform data points to pixels: + if (keyAxis->orientation() == Qt::Vertical) + { + for (int i=0; icoordToPixel(current.key); + result[i*2+0].setX(valueAxis->coordToPixel(0)); + result[i*2+0].setY(key); + result[i*2+1].setX(valueAxis->coordToPixel(current.value)); + result[i*2+1].setY(key); + } else + { + result[i*2+0] = QPointF(0, 0); + result[i*2+1] = QPointF(0, 0); + } + } + } else // key axis is horizontal + { + for (int i=0; icoordToPixel(data.at(i).key); + result[i*2+0].setX(key); + result[i*2+0].setY(valueAxis->coordToPixel(0)); + result[i*2+1].setX(key); + result[i*2+1].setY(valueAxis->coordToPixel(data.at(i).value)); + } else + { + result[i*2+0] = QPointF(0, 0); + result[i*2+1] = QPointF(0, 0); + } + } + } + return result; +} + +/*! \internal + + Draws the fill of the graph using the specified \a painter, with the currently set brush. + + Depending on whether a normal fill or a channel fill (\ref setChannelFillGraph) is needed, \ref + getFillPolygon or \ref getChannelFillPolygon are used to find the according fill polygons. + + In order to handle NaN Data points correctly (the fill needs to be split into disjoint areas), + this method first determines a list of non-NaN segments with \ref getNonNanSegments, on which to + operate. In the channel fill case, \ref getOverlappingSegments is used to consolidate the non-NaN + segments of the two involved graphs, before passing the overlapping pairs to \ref + getChannelFillPolygon. + + Pass the points of this graph's line as \a lines, in pixel coordinates. + + \see drawLinePlot, drawImpulsePlot, drawScatterPlot +*/ +void QCPGraph::drawFill(QCPPainter *painter, QVector *lines) const +{ + if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot + if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return; + + applyFillAntialiasingHint(painter); + const QVector segments = getNonNanSegments(lines, keyAxis()->orientation()); + if (!mChannelFillGraph) + { + // draw base fill under graph, fill goes all the way to the zero-value-line: + foreach (QCPDataRange segment, segments) + painter->drawPolygon(getFillPolygon(lines, segment)); + } else + { + // draw fill between this graph and mChannelFillGraph: + QVector otherLines; + mChannelFillGraph->getLines(&otherLines, QCPDataRange(0, mChannelFillGraph->dataCount())); + if (!otherLines.isEmpty()) + { + QVector otherSegments = getNonNanSegments(&otherLines, mChannelFillGraph->keyAxis()->orientation()); + QVector > segmentPairs = getOverlappingSegments(segments, lines, otherSegments, &otherLines); + for (int i=0; idrawPolygon(getChannelFillPolygon(lines, segmentPairs.at(i).first, &otherLines, segmentPairs.at(i).second)); + } + } +} + +/*! \internal + + Draws scatter symbols at every point passed in \a scatters, given in pixel coordinates. The + scatters will be drawn with \a painter and have the appearance as specified in \a style. + + \see drawLinePlot, drawImpulsePlot +*/ +void QCPGraph::drawScatterPlot(QCPPainter *painter, const QVector &scatters, const QCPScatterStyle &style) const +{ + applyScattersAntialiasingHint(painter); + style.applyTo(painter, mPen); + foreach (const QPointF &scatter, scatters) + style.drawShape(painter, scatter.x(), scatter.y()); +} + +/*! \internal + + Draws lines between the points in \a lines, given in pixel coordinates. + + \see drawScatterPlot, drawImpulsePlot, QCPAbstractPlottable1D::drawPolyline +*/ +void QCPGraph::drawLinePlot(QCPPainter *painter, const QVector &lines) const +{ + if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + drawPolyline(painter, lines); + } +} + +/*! \internal + + Draws impulses from the provided data, i.e. it connects all line pairs in \a lines, given in + pixel coordinates. The \a lines necessary for impulses are generated by \ref dataToImpulseLines + from the regular graph data points. + + \see drawLinePlot, drawScatterPlot +*/ +void QCPGraph::drawImpulsePlot(QCPPainter *painter, const QVector &lines) const +{ + if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + QPen oldPen = painter->pen(); + QPen newPen = painter->pen(); + newPen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line + painter->setPen(newPen); + painter->drawLines(lines); + painter->setPen(oldPen); + } +} + +/*! \internal + + Returns via \a lineData the data points that need to be visualized for this graph when plotting + graph lines, taking into consideration the currently visible axis ranges and, if \ref + setAdaptiveSampling is enabled, local point densities. The considered data can be restricted + further by \a begin and \a end, e.g. to only plot a certain segment of the data (see \ref + getDataSegments). + + This method is used by \ref getLines to retrieve the basic working set of data. + + \see getOptimizedScatterData +*/ +void QCPGraph::getOptimizedLineData(QVector *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const +{ + if (!lineData) return; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (begin == end) return; + + int dataCount = int(end-begin); + int maxCount = (std::numeric_limits::max)(); + if (mAdaptiveSampling) + { + double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)); + if (2*keyPixelSpan+2 < static_cast((std::numeric_limits::max)())) + maxCount = int(2*keyPixelSpan+2); + } + + if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average + { + QCPGraphDataContainer::const_iterator it = begin; + double minValue = it->value; + double maxValue = it->value; + QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it; + int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction + int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey + double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound)); + double lastIntervalEndKey = currentIntervalStartKey; + double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates + bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) + int intervalDataCount = 1; + ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect + while (it != end) + { + if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary + { + if (it->value < minValue) + minValue = it->value; + else if (it->value > maxValue) + maxValue = it->value; + ++intervalDataCount; + } else // new pixel interval started + { + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster + { + if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point + lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value)); + lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); + lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); + if (it->key > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point + lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.8, (it-1)->value)); + } else + lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value)); + lastIntervalEndKey = (it-1)->key; + minValue = it->value; + maxValue = it->value; + currentIntervalFirstPoint = it; + currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound)); + if (keyEpsilonVariable) + keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); + intervalDataCount = 1; + } + ++it; + } + // handle last interval: + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster + { + if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point + lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value)); + lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue)); + lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue)); + } else + lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value)); + + } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output + { + lineData->resize(dataCount); + std::copy(begin, end, lineData->begin()); + } +} + +/*! \internal + + Returns via \a scatterData the data points that need to be visualized for this graph when + plotting scatter points, taking into consideration the currently visible axis ranges and, if \ref + setAdaptiveSampling is enabled, local point densities. The considered data can be restricted + further by \a begin and \a end, e.g. to only plot a certain segment of the data (see \ref + getDataSegments). + + This method is used by \ref getScatters to retrieve the basic working set of data. + + \see getOptimizedLineData +*/ +void QCPGraph::getOptimizedScatterData(QVector *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const +{ + if (!scatterData) return; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + const int scatterModulo = mScatterSkip+1; + const bool doScatterSkip = mScatterSkip > 0; + int beginIndex = int(begin-mDataContainer->constBegin()); + int endIndex = int(end-mDataContainer->constBegin()); + while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter + { + ++beginIndex; + ++begin; + } + if (begin == end) return; + int dataCount = int(end-begin); + int maxCount = (std::numeric_limits::max)(); + if (mAdaptiveSampling) + { + int keyPixelSpan = int(qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key))); + maxCount = 2*keyPixelSpan+2; + } + + if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average + { + double valueMaxRange = valueAxis->range().upper; + double valueMinRange = valueAxis->range().lower; + QCPGraphDataContainer::const_iterator it = begin; + int itIndex = int(beginIndex); + double minValue = it->value; + double maxValue = it->value; + QCPGraphDataContainer::const_iterator minValueIt = it; + QCPGraphDataContainer::const_iterator maxValueIt = it; + QCPGraphDataContainer::const_iterator currentIntervalStart = it; + int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction + int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey + double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound)); + double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates + bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes) + int intervalDataCount = 1; + // advance iterator to second (non-skipped) data point because adaptive sampling works in 1 point retrospect: + if (!doScatterSkip) + ++it; + else + { + itIndex += scatterModulo; + if (itIndex < endIndex) // make sure we didn't jump over end + it += scatterModulo; + else + { + it = end; + itIndex = endIndex; + } + } + // main loop over data points: + while (it != end) + { + if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary + { + if (it->value < minValue && it->value > valueMinRange && it->value < valueMaxRange) + { + minValue = it->value; + minValueIt = it; + } else if (it->value > maxValue && it->value > valueMinRange && it->value < valueMaxRange) + { + maxValue = it->value; + maxValueIt = it; + } + ++intervalDataCount; + } else // new pixel started + { + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them + { + // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): + // [ However, make sure that the span is at least 1 pixel ] + double valuePixelSpan = qMax(1.0, qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue))); + double pointsToAdd = valuePixelSpan/4.0; // add approximately one data point for every 4 value pixels + int dataModulo = qMax(1, qRound(intervalDataCount/pointsToAdd)); + QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart; + int c = 0; + while (intervalIt != it) + { + if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange) + scatterData->append(*intervalIt); + ++c; + if (!doScatterSkip) + ++intervalIt; + else + intervalIt += scatterModulo; // since we know indices of "currentIntervalStart", "intervalIt" and "it" are multiples of scatterModulo, we can't accidentally jump over "it" here + } + } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange) + scatterData->append(*currentIntervalStart); + minValue = it->value; + maxValue = it->value; + currentIntervalStart = it; + currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound)); + if (keyEpsilonVariable) + keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); + intervalDataCount = 1; + } + // advance to next data point: + if (!doScatterSkip) + ++it; + else + { + itIndex += scatterModulo; + if (itIndex < endIndex) // make sure we didn't jump over end + it += scatterModulo; + else + { + it = end; + itIndex = endIndex; + } + } + } + // handle last interval: + if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them + { + // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot): + // [ However, make sure that the span is at least 1 pixel ] + double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue)); + double pointsToAdd = valuePixelSpan/4.0; // add approximately one data point for every 4 value pixels + int dataModulo = qMax(1, qRound(intervalDataCount/pointsToAdd)); + QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart; + int intervalItIndex = int(intervalIt-mDataContainer->constBegin()); + int c = 0; + while (intervalIt != it) + { + if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange) + scatterData->append(*intervalIt); + ++c; + if (!doScatterSkip) + ++intervalIt; + else // here we can't guarantee that adding scatterModulo doesn't exceed "it" (because "it" is equal to "end" here, and "end" isn't scatterModulo-aligned), so check via index comparison: + { + intervalItIndex += scatterModulo; + if (intervalItIndex < itIndex) + intervalIt += scatterModulo; + else + { + intervalIt = it; + intervalItIndex = itIndex; + } + } + } + } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange) + scatterData->append(*currentIntervalStart); + + } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output + { + QCPGraphDataContainer::const_iterator it = begin; + int itIndex = beginIndex; + scatterData->reserve(dataCount); + while (it != end) + { + scatterData->append(*it); + // advance to next data point: + if (!doScatterSkip) + ++it; + else + { + itIndex += scatterModulo; + if (itIndex < endIndex) + it += scatterModulo; + else + { + it = end; + itIndex = endIndex; + } + } + } + } +} + +/*! + This method outputs the currently visible data range via \a begin and \a end. The returned range + will also never exceed \a rangeRestriction. + + This method takes into account that the drawing of data lines at the axis rect border always + requires the points just outside the visible axis range. So \a begin and \a end may actually + indicate a range that contains one additional data point to the left and right of the visible + axis range. +*/ +void QCPGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const +{ + if (rangeRestriction.isEmpty()) + { + end = mDataContainer->constEnd(); + begin = end; + } else + { + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + // get visible data range: + begin = mDataContainer->findBegin(keyAxis->range().lower); + end = mDataContainer->findEnd(keyAxis->range().upper); + // limit lower/upperEnd to rangeRestriction: + mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything + } +} + +/*! \internal + + This method goes through the passed points in \a lineData and returns a list of the segments + which don't contain NaN data points. + + \a keyOrientation defines whether the \a x or \a y member of the passed QPointF is used to check + for NaN. If \a keyOrientation is \c Qt::Horizontal, the \a y member is checked, if it is \c + Qt::Vertical, the \a x member is checked. + + \see getOverlappingSegments, drawFill +*/ +QVector QCPGraph::getNonNanSegments(const QVector *lineData, Qt::Orientation keyOrientation) const +{ + QVector result; + const int n = static_cast(lineData->size()); + + QCPDataRange currentSegment(-1, -1); + int i = 0; + + if (keyOrientation == Qt::Horizontal) + { + while (i < n) + { + while (i < n && qIsNaN(lineData->at(i).y())) // seek next non-NaN data point + ++i; + if (i == n) + break; + currentSegment.setBegin(i++); + while (i < n && !qIsNaN(lineData->at(i).y())) // seek next NaN data point or end of data + ++i; + currentSegment.setEnd(i++); + result.append(currentSegment); + } + } else // keyOrientation == Qt::Vertical + { + while (i < n) + { + while (i < n && qIsNaN(lineData->at(i).x())) // seek next non-NaN data point + ++i; + if (i == n) + break; + currentSegment.setBegin(i++); + while (i < n && !qIsNaN(lineData->at(i).x())) // seek next NaN data point or end of data + ++i; + currentSegment.setEnd(i++); + result.append(currentSegment); + } + } + return result; +} + +/*! \internal + + This method takes two segment lists (e.g. created by \ref getNonNanSegments) \a thisSegments and + \a otherSegments, and their associated point data \a thisData and \a otherData. + + It returns all pairs of segments (the first from \a thisSegments, the second from \a + otherSegments), which overlap in plot coordinates. + + This method is useful in the case of a channel fill between two graphs, when only those non-NaN + segments which actually overlap in their key coordinate shall be considered for drawing a channel + fill polygon. + + It is assumed that the passed segments in \a thisSegments are ordered ascending by index, and + that the segments don't overlap themselves. The same is assumed for the segments in \a + otherSegments. This is fulfilled when the segments are obtained via \ref getNonNanSegments. + + \see getNonNanSegments, segmentsIntersect, drawFill, getChannelFillPolygon +*/ +QVector > QCPGraph::getOverlappingSegments(QVector thisSegments, const QVector *thisData, QVector otherSegments, const QVector *otherData) const +{ + QVector > result; + if (thisData->isEmpty() || otherData->isEmpty() || thisSegments.isEmpty() || otherSegments.isEmpty()) + return result; + + int thisIndex = 0; + int otherIndex = 0; + const bool verticalKey = mKeyAxis->orientation() == Qt::Vertical; + while (thisIndex < thisSegments.size() && otherIndex < otherSegments.size()) + { + if (thisSegments.at(thisIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow + { + ++thisIndex; + continue; + } + if (otherSegments.at(otherIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow + { + ++otherIndex; + continue; + } + double thisLower, thisUpper, otherLower, otherUpper; + if (!verticalKey) + { + thisLower = thisData->at(thisSegments.at(thisIndex).begin()).x(); + thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).x(); + otherLower = otherData->at(otherSegments.at(otherIndex).begin()).x(); + otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).x(); + } else + { + thisLower = thisData->at(thisSegments.at(thisIndex).begin()).y(); + thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).y(); + otherLower = otherData->at(otherSegments.at(otherIndex).begin()).y(); + otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).y(); + } + + int bPrecedence; + if (segmentsIntersect(thisLower, thisUpper, otherLower, otherUpper, bPrecedence)) + result.append(QPair(thisSegments.at(thisIndex), otherSegments.at(otherIndex))); + + if (bPrecedence <= 0) // otherSegment doesn't reach as far as thisSegment, so continue with next otherSegment, keeping current thisSegment + ++otherIndex; + else // otherSegment reaches further than thisSegment, so continue with next thisSegment, keeping current otherSegment + ++thisIndex; + } + + return result; +} + +/*! \internal + + Returns whether the segments defined by the coordinates (aLower, aUpper) and (bLower, bUpper) + have overlap. + + The output parameter \a bPrecedence indicates whether the \a b segment reaches farther than the + \a a segment or not. If \a bPrecedence returns 1, segment \a b reaches the farthest to higher + coordinates (i.e. bUpper > aUpper). If it returns -1, segment \a a reaches the farthest. Only if + both segment's upper bounds are identical, 0 is returned as \a bPrecedence. + + It is assumed that the lower bounds always have smaller or equal values than the upper bounds. + + \see getOverlappingSegments +*/ +bool QCPGraph::segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const +{ + bPrecedence = 0; + if (aLower > bUpper) + { + bPrecedence = -1; + return false; + } else if (bLower > aUpper) + { + bPrecedence = 1; + return false; + } else + { + if (aUpper > bUpper) + bPrecedence = -1; + else if (aUpper < bUpper) + bPrecedence = 1; + + return true; + } +} + +/*! \internal + + Returns the point which closes the fill polygon on the zero-value-line parallel to the key axis. + The logarithmic axis scale case is a bit special, since the zero-value-line in pixel coordinates + is in positive or negative infinity. So this case is handled separately by just closing the fill + polygon on the axis which lies in the direction towards the zero value. + + \a matchingDataPoint will provide the key (in pixels) of the returned point. Depending on whether + the key axis of this graph is horizontal or vertical, \a matchingDataPoint will provide the x or + y value of the returned point, respectively. +*/ +QPointF QCPGraph::getFillBasePoint(QPointF matchingDataPoint) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; } + + QPointF result; + if (valueAxis->scaleType() == QCPAxis::stLinear) + { + if (keyAxis->orientation() == Qt::Horizontal) + { + result.setX(matchingDataPoint.x()); + result.setY(valueAxis->coordToPixel(0)); + } else // keyAxis->orientation() == Qt::Vertical + { + result.setX(valueAxis->coordToPixel(0)); + result.setY(matchingDataPoint.y()); + } + } else // valueAxis->mScaleType == QCPAxis::stLogarithmic + { + // In logarithmic scaling we can't just draw to value 0 so we just fill all the way + // to the axis which is in the direction towards 0 + if (keyAxis->orientation() == Qt::Vertical) + { + if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || + (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis + result.setX(keyAxis->axisRect()->right()); + else + result.setX(keyAxis->axisRect()->left()); + result.setY(matchingDataPoint.y()); + } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom) + { + result.setX(matchingDataPoint.x()); + if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) || + (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis + result.setY(keyAxis->axisRect()->top()); + else + result.setY(keyAxis->axisRect()->bottom()); + } + } + return result; +} + +/*! \internal + + Returns the polygon needed for drawing normal fills between this graph and the key axis. + + Pass the graph's data points (in pixel coordinates) as \a lineData, and specify the \a segment + which shall be used for the fill. The collection of \a lineData points described by \a segment + must not contain NaN data points (see \ref getNonNanSegments). + + The returned fill polygon will be closed at the key axis (the zero-value line) for linear value + axes. For logarithmic value axes the polygon will reach just beyond the corresponding axis rect + side (see \ref getFillBasePoint). + + For increased performance (due to implicit sharing), keep the returned QPolygonF const. + + \see drawFill, getNonNanSegments +*/ +const QPolygonF QCPGraph::getFillPolygon(const QVector *lineData, QCPDataRange segment) const +{ + if (segment.size() < 2) + return QPolygonF(); + QPolygonF result(segment.size()+2); + + result[0] = getFillBasePoint(lineData->at(segment.begin())); + std::copy(lineData->constBegin()+segment.begin(), lineData->constBegin()+segment.end(), result.begin()+1); + result[result.size()-1] = getFillBasePoint(lineData->at(segment.end()-1)); + + return result; +} + +/*! \internal + + Returns the polygon needed for drawing (partial) channel fills between this graph and the graph + specified by \ref setChannelFillGraph. + + The data points of this graph are passed as pixel coordinates via \a thisData, the data of the + other graph as \a otherData. The returned polygon will be calculated for the specified data + segments \a thisSegment and \a otherSegment, pertaining to the respective \a thisData and \a + otherData, respectively. + + The passed \a thisSegment and \a otherSegment should correspond to the segment pairs returned by + \ref getOverlappingSegments, to make sure only segments that actually have key coordinate overlap + need to be processed here. + + For increased performance due to implicit sharing, keep the returned QPolygonF const. + + \see drawFill, getOverlappingSegments, getNonNanSegments +*/ +const QPolygonF QCPGraph::getChannelFillPolygon(const QVector *thisData, QCPDataRange thisSegment, const QVector *otherData, QCPDataRange otherSegment) const +{ + if (!mChannelFillGraph) + return QPolygonF(); + + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); } + if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); } + + if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation()) + return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis) + + if (thisData->isEmpty()) return QPolygonF(); + QVector thisSegmentData(thisSegment.size()); + QVector otherSegmentData(otherSegment.size()); + std::copy(thisData->constBegin()+thisSegment.begin(), thisData->constBegin()+thisSegment.end(), thisSegmentData.begin()); + std::copy(otherData->constBegin()+otherSegment.begin(), otherData->constBegin()+otherSegment.end(), otherSegmentData.begin()); + // pointers to be able to swap them, depending which data range needs cropping: + QVector *staticData = &thisSegmentData; + QVector *croppedData = &otherSegmentData; + + // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType): + if (keyAxis->orientation() == Qt::Horizontal) + { + // x is key + // crop lower bound: + if (staticData->first().x() < croppedData->first().x()) // other one must be cropped + qSwap(staticData, croppedData); + const int lowBound = findIndexBelowX(croppedData, staticData->first().x()); + if (lowBound == -1) return QPolygonF(); // key ranges have no overlap + croppedData->remove(0, lowBound); + // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation: + if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation + double slope; + if (!qFuzzyCompare(croppedData->at(1).x(), croppedData->at(0).x())) + slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x()); + else + slope = 0; + (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x())); + (*croppedData)[0].setX(staticData->first().x()); + + // crop upper bound: + if (staticData->last().x() > croppedData->last().x()) // other one must be cropped + qSwap(staticData, croppedData); + int highBound = findIndexAboveX(croppedData, staticData->last().x()); + if (highBound == -1) return QPolygonF(); // key ranges have no overlap + croppedData->remove(highBound+1, croppedData->size()-(highBound+1)); + // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation: + if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation + const int li = static_cast(croppedData->size())-1; // last index + if (!qFuzzyCompare(croppedData->at(li).x(), croppedData->at(li-1).x())) + slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x()); + else + slope = 0; + (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x())); + (*croppedData)[li].setX(staticData->last().x()); + } else // mKeyAxis->orientation() == Qt::Vertical + { + // y is key + // crop lower bound: + if (staticData->first().y() < croppedData->first().y()) // other one must be cropped + qSwap(staticData, croppedData); + int lowBound = findIndexBelowY(croppedData, staticData->first().y()); + if (lowBound == -1) return QPolygonF(); // key ranges have no overlap + croppedData->remove(0, lowBound); + // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation: + if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation + double slope; + if (!qFuzzyCompare(croppedData->at(1).y(), croppedData->at(0).y())) // avoid division by zero in step plots + slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y()); + else + slope = 0; + (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y())); + (*croppedData)[0].setY(staticData->first().y()); + + // crop upper bound: + if (staticData->last().y() > croppedData->last().y()) // other one must be cropped + qSwap(staticData, croppedData); + int highBound = findIndexAboveY(croppedData, staticData->last().y()); + if (highBound == -1) return QPolygonF(); // key ranges have no overlap + croppedData->remove(highBound+1, croppedData->size()-(highBound+1)); + // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation: + if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation + int li = static_cast(croppedData->size())-1; // last index + if (!qFuzzyCompare(croppedData->at(li).y(), croppedData->at(li-1).y())) // avoid division by zero in step plots + slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y()); + else + slope = 0; + (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y())); + (*croppedData)[li].setY(staticData->last().y()); + } + + // return joined: + for (int i=static_cast(otherSegmentData.size())-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted + thisSegmentData << otherSegmentData.at(i); + return QPolygonF(thisSegmentData); +} + +/*! \internal + + Finds the smallest index of \a data, whose points x value is just above \a x. Assumes x values in + \a data points are ordered ascending, as is ensured by \ref getLines/\ref getScatters if the key + axis is horizontal. + + Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. +*/ +int QCPGraph::findIndexAboveX(const QVector *data, double x) const +{ + for (int i=static_cast(data->size())-1; i>=0; --i) + { + if (data->at(i).x() < x) + { + if (isize()-1) + return i+1; + else + return static_cast(data->size())-1; + } + } + return -1; +} + +/*! \internal + + Finds the highest index of \a data, whose points x value is just below \a x. Assumes x values in + \a data points are ordered ascending, as is ensured by \ref getLines/\ref getScatters if the key + axis is horizontal. + + Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. +*/ +int QCPGraph::findIndexBelowX(const QVector *data, double x) const +{ + for (int i=0; isize(); ++i) + { + if (data->at(i).x() > x) + { + if (i>0) + return i-1; + else + return 0; + } + } + return -1; +} + +/*! \internal + + Finds the smallest index of \a data, whose points y value is just above \a y. Assumes y values in + \a data points are ordered ascending, as is ensured by \ref getLines/\ref getScatters if the key + axis is vertical. + + Used to calculate the channel fill polygon, see \ref getChannelFillPolygon. +*/ +int QCPGraph::findIndexAboveY(const QVector *data, double y) const +{ + for (int i=static_cast(data->size())-1; i>=0; --i) + { + if (data->at(i).y() < y) + { + if (isize()-1) + return i+1; + else + return static_cast(data->size())-1; + } + } + return -1; +} + +/*! \internal + + Calculates the minimum distance in pixels the graph's representation has from the given \a + pixelPoint. This is used to determine whether the graph was clicked or not, e.g. in \ref + selectTest. The closest data point to \a pixelPoint is returned in \a closestData. Note that if + the graph has a line representation, the returned distance may be smaller than the distance to + the \a closestData point, since the distance to the graph line is also taken into account. + + If either the graph has no data or if the line style is \ref lsNone and the scatter style's shape + is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns -1.0. +*/ +double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const +{ + closestData = mDataContainer->constEnd(); + if (mDataContainer->isEmpty()) + return -1.0; + if (mLineStyle == lsNone && mScatterStyle.isNone()) + return -1.0; + + // calculate minimum distances to graph data points and find closestData iterator: + double minDistSqr = (std::numeric_limits::max)(); + // determine which key range comes into question, taking selection tolerance around pos into account: + double posKeyMin = 0.0, posKeyMax = 0.0, dummy; + pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy); + pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy); + if (posKeyMin > posKeyMax) + qSwap(posKeyMin, posKeyMax); + // iterate over found data points and then choose the one with the shortest distance to pos: + QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true); + QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true); + for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it) + { + const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared(); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestData = it; + } + } + + // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point): + if (mLineStyle != lsNone) + { + // line displayed, calculate distance to line segments: + QVector lineData; + getLines(&lineData, QCPDataRange(0, dataCount())); // don't limit data range further since with sharp data spikes, line segments may be closer to test point than segments with closer key coordinate + QCPVector2D p(pixelPoint); + const int step = mLineStyle==lsImpulse ? 2 : 1; // impulse plot differs from other line styles in that the lineData points are only pairwise connected + for (int i=0; i *data, double y) const +{ + for (int i=0; isize(); ++i) + { + if (data->at(i).y() > y) + { + if (i>0) + return i-1; + else + return 0; + } + } + return -1; +} +/* end of 'src/plottables/plottable-graph.cpp' */ + + +/* including file 'src/plottables/plottable-curve.cpp' */ +/* modified 2022-11-06T12:45:56, size 63851 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPCurveData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPCurveData + \brief Holds the data of one single data point for QCPCurve. + + The stored data is: + \li \a t: the free ordering parameter of this curve point, like in the mathematical vector (x(t), y(t)). (This is the \a sortKey) + \li \a key: coordinate on the key axis of this curve point (this is the \a mainKey) + \li \a value: coordinate on the value axis of this curve point (this is the \a mainValue) + + The container for storing multiple data points is \ref QCPCurveDataContainer. It is a typedef for + \ref QCPDataContainer with \ref QCPCurveData as the DataType template parameter. See the + documentation there for an explanation regarding the data type's generic methods. + + \see QCPCurveDataContainer +*/ + +/* start documentation of inline functions */ + +/*! \fn double QCPCurveData::sortKey() const + + Returns the \a t member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static QCPCurveData QCPCurveData::fromSortKey(double sortKey) + + Returns a data point with the specified \a sortKey (assigned to the data point's \a t member). + All other members are set to zero. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static static bool QCPCurveData::sortKeyIsMainKey() + + Since the member \a key is the data point key coordinate and the member \a t is the data ordering + parameter, this method returns false. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPCurveData::mainKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPCurveData::mainValue() const + + Returns the \a value member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn QCPRange QCPCurveData::valueRange() const + + Returns a QCPRange with both lower and upper boundary set to \a value of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/* end documentation of inline functions */ + +/*! + Constructs a curve data point with t, key and value set to zero. +*/ +QCPCurveData::QCPCurveData() : + t(0), + key(0), + value(0) +{ +} + +/*! + Constructs a curve data point with the specified \a t, \a key and \a value. +*/ +QCPCurveData::QCPCurveData(double t, double key, double value) : + t(t), + key(key), + value(value) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPCurve +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPCurve + \brief A plottable representing a parametric curve in a plot. + + \image html QCPCurve.png + + Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate, + so their visual representation can have \a loops. This is realized by introducing a third + coordinate \a t, which defines the order of the points described by the other two coordinates \a + x and \a y. + + To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can + also access and modify the curve's data via the \ref data method, which returns a pointer to the + internal \ref QCPCurveDataContainer. + + Gaps in the curve can be created by adding data points with NaN as key and value + (qQNaN() or std::numeric_limits::quiet_NaN()) in between the two data points that shall be + separated. + + \section qcpcurve-appearance Changing the appearance + + The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush). + + \section qcpcurve-usage Usage + + Like all data representing objects in QCustomPlot, the QCPCurve is a plottable + (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies + (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.) + + Usually, you first create an instance: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1 + which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot instance takes + ownership of the plottable, so do not delete it manually but use QCustomPlot::removePlottable() instead. + The newly created plottable can be modified, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2 +*/ + +/* start of documentation of inline functions */ + +/*! \fn QSharedPointer QCPCurve::data() const + + Returns a shared pointer to the internal data storage of type \ref QCPCurveDataContainer. You may + use it to directly manipulate the data, which may be more convenient and faster than using the + regular \ref setData or \ref addData methods. +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + The created QCPCurve is automatically registered with the QCustomPlot instance inferred from \a + keyAxis. This QCustomPlot instance takes ownership of the QCPCurve, so do not delete it manually + but use QCustomPlot::removePlottable() instead. +*/ +QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable1D(keyAxis, valueAxis), + mScatterSkip{}, + mLineStyle{} +{ + // modify inherited properties from abstract plottable: + setPen(QPen(Qt::blue, 0)); + setBrush(Qt::NoBrush); + + setScatterStyle(QCPScatterStyle()); + setLineStyle(lsLine); + setScatterSkip(0); +} + +QCPCurve::~QCPCurve() +{ +} + +/*! \overload + + Replaces the current data container with the provided \a data container. + + Since a QSharedPointer is used, multiple QCPCurves may share the same data container safely. + Modifying the data in the container will then affect all curves that share the container. Sharing + can be achieved by simply exchanging the data containers wrapped in shared pointers: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-datasharing-1 + + If you do not wish to share containers, but create a copy from an existing container, rather use + the \ref QCPDataContainer::set method on the curve's data container directly: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-datasharing-2 + + \see addData +*/ +void QCPCurve::setData(QSharedPointer data) +{ + mDataContainer = data; +} + +/*! \overload + + Replaces the current data with the provided points in \a t, \a keys and \a values. The provided + vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. + + If you can guarantee that the passed data points are sorted by \a t in ascending order, you can + set \a alreadySorted to true, to improve performance by saving a sorting run. + + \see addData +*/ +void QCPCurve::setData(const QVector &t, const QVector &keys, const QVector &values, bool alreadySorted) +{ + mDataContainer->clear(); + addData(t, keys, values, alreadySorted); +} + + +/*! \overload + + Replaces the current data with the provided points in \a keys and \a values. The provided vectors + should have equal length. Else, the number of added points will be the size of the smallest + vector. + + The t parameter of each data point will be set to the integer index of the respective key/value + pair. + + \see addData +*/ +void QCPCurve::setData(const QVector &keys, const QVector &values) +{ + mDataContainer->clear(); + addData(keys, values); +} + +/*! + Sets the visual appearance of single data points in the plot. If set to \ref + QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate + line style). + + \see QCPScatterStyle, setLineStyle +*/ +void QCPCurve::setScatterStyle(const QCPScatterStyle &style) +{ + mScatterStyle = style; +} + +/*! + If scatters are displayed (scatter style not \ref QCPScatterStyle::ssNone), \a skip number of + scatter points are skipped/not drawn after every drawn scatter point. + + This can be used to make the data appear sparser while for example still having a smooth line, + and to improve performance for very high density plots. + + If \a skip is set to 0 (default), all scatter points are drawn. + + \see setScatterStyle +*/ +void QCPCurve::setScatterSkip(int skip) +{ + mScatterSkip = qMax(0, skip); +} + +/*! + Sets how the single data points are connected in the plot or how they are represented visually + apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref + setScatterStyle to the desired scatter style. + + \see setScatterStyle +*/ +void QCPCurve::setLineStyle(QCPCurve::LineStyle style) +{ + mLineStyle = style; +} + +/*! \overload + + Adds the provided points in \a t, \a keys and \a values to the current data. The provided vectors + should have equal length. Else, the number of added points will be the size of the smallest + vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPCurve::addData(const QVector &t, const QVector &keys, const QVector &values, bool alreadySorted) +{ + if (t.size() != keys.size() || t.size() != values.size()) + qDebug() << Q_FUNC_INFO << "ts, keys and values have different sizes:" << t.size() << keys.size() << values.size(); + const int n = static_cast(qMin(qMin(t.size(), keys.size()), values.size())); + QVector tempData(n); + QVector::iterator it = tempData.begin(); + const QVector::iterator itEnd = tempData.end(); + int i = 0; + while (it != itEnd) + { + it->t = t[i]; + it->key = keys[i]; + it->value = values[i]; + ++it; + ++i; + } + mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write +} + +/*! \overload + + Adds the provided points in \a keys and \a values to the current data. The provided vectors + should have equal length. Else, the number of added points will be the size of the smallest + vector. + + The t parameter of each data point will be set to the integer index of the respective key/value + pair. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPCurve::addData(const QVector &keys, const QVector &values) +{ + if (keys.size() != values.size()) + qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size(); + const int n = static_cast(qMin(keys.size(), values.size())); + double tStart; + if (!mDataContainer->isEmpty()) + tStart = (mDataContainer->constEnd()-1)->t + 1.0; + else + tStart = 0; + QVector tempData(n); + QVector::iterator it = tempData.begin(); + const QVector::iterator itEnd = tempData.end(); + int i = 0; + while (it != itEnd) + { + it->t = tStart + i; + it->key = keys[i]; + it->value = values[i]; + ++it; + ++i; + } + mDataContainer->add(tempData, true); // don't modify tempData beyond this to prevent copy on write +} + +/*! \overload + Adds the provided data point as \a t, \a key and \a value to the current data. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPCurve::addData(double t, double key, double value) +{ + mDataContainer->add(QCPCurveData(t, key, value)); +} + +/*! \overload + + Adds the provided data point as \a key and \a value to the current data. + + The t parameter is generated automatically by increments of 1 for each point, starting at the + highest t of previously existing data or 0, if the curve data is empty. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPCurve::addData(double key, double value) +{ + if (!mDataContainer->isEmpty()) + mDataContainer->add(QCPCurveData((mDataContainer->constEnd()-1)->t + 1.0, key, value)); + else + mDataContainer->add(QCPCurveData(0.0, key, value)); +} + +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ +double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) + { + QCPCurveDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); + double result = pointDistance(pos, closestDataPoint); + if (details) + { + int pointIndex = int( closestDataPoint-mDataContainer->constBegin() ); + details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); + } + return result; + } else + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPCurve::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + return mDataContainer->keyRange(foundRange, inSignDomain); +} + +/* inherits documentation from base class */ +QCPRange QCPCurve::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange); +} + +/* inherits documentation from base class */ +void QCPCurve::draw(QCPPainter *painter) +{ + if (mDataContainer->isEmpty()) return; + + // allocate line vector: + QVector lines, scatters; + + // loop over and draw segments of unselected/selected data: + QList selectedSegments, unselectedSegments, allSegments; + getDataSegments(selectedSegments, unselectedSegments); + allSegments << unselectedSegments << selectedSegments; + for (int i=0; i= unselectedSegments.size(); + + // fill with curve data: + QPen finalCurvePen = mPen; // determine the final pen already here, because the line optimization depends on its stroke width + if (isSelectedSegment && mSelectionDecorator) + finalCurvePen = mSelectionDecorator->pen(); + + QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getCurveLines takes care) + getCurveLines(&lines, lineDataRange, finalCurvePen.widthF()); + + // check data validity if flag set: + #ifdef QCUSTOMPLOT_CHECK_DATA + for (QCPCurveDataContainer::const_iterator it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it) + { + if (QCP::isInvalidData(it->t) || + QCP::isInvalidData(it->key, it->value)) + qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name(); + } + #endif + + // draw curve fill: + applyFillAntialiasingHint(painter); + if (isSelectedSegment && mSelectionDecorator) + mSelectionDecorator->applyBrush(painter); + else + painter->setBrush(mBrush); + painter->setPen(Qt::NoPen); + if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0) + painter->drawPolygon(QPolygonF(lines)); + + // draw curve line: + if (mLineStyle != lsNone) + { + painter->setPen(finalCurvePen); + painter->setBrush(Qt::NoBrush); + drawCurveLine(painter, lines); + } + + // draw scatters: + QCPScatterStyle finalScatterStyle = mScatterStyle; + if (isSelectedSegment && mSelectionDecorator) + finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle); + if (!finalScatterStyle.isNone()) + { + getScatters(&scatters, allSegments.at(i), finalScatterStyle.size()); + drawScatterPlot(painter, scatters, finalScatterStyle); + } + } + + // draw other selection decoration that isn't just line/scatter pens and brushes: + if (mSelectionDecorator) + mSelectionDecorator->drawDecoration(painter, selection()); +} + +/* inherits documentation from base class */ +void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + // draw fill: + if (mBrush.style() != Qt::NoBrush) + { + applyFillAntialiasingHint(painter); + painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush); + } + // draw line vertically centered: + if (mLineStyle != lsNone) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens + } + // draw scatter symbol: + if (!mScatterStyle.isNone()) + { + applyScattersAntialiasingHint(painter); + // scale scatter pixmap if it's too large to fit in legend icon rect: + if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height())) + { + QCPScatterStyle scaledStyle(mScatterStyle); + scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + scaledStyle.applyTo(painter, mPen); + scaledStyle.drawShape(painter, QRectF(rect).center()); + } else + { + mScatterStyle.applyTo(painter, mPen); + mScatterStyle.drawShape(painter, QRectF(rect).center()); + } + } +} + +/*! \internal + + Draws lines between the points in \a lines, given in pixel coordinates. + + \see drawScatterPlot, getCurveLines +*/ +void QCPCurve::drawCurveLine(QCPPainter *painter, const QVector &lines) const +{ + if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + drawPolyline(painter, lines); + } +} + +/*! \internal + + Draws scatter symbols at every point passed in \a points, given in pixel coordinates. The + scatters will be drawn with \a painter and have the appearance as specified in \a style. + + \see drawCurveLine, getCurveLines +*/ +void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector &points, const QCPScatterStyle &style) const +{ + // draw scatter point symbols: + applyScattersAntialiasingHint(painter); + style.applyTo(painter, mPen); + foreach (const QPointF &point, points) + if (!qIsNaN(point.x()) && !qIsNaN(point.y())) + style.drawShape(painter, point); +} + +/*! \internal + + Called by \ref draw to generate points in pixel coordinates which represent the line of the + curve. + + Line segments that aren't visible in the current axis rect are handled in an optimized way. They + are projected onto a rectangle slightly larger than the visible axis rect and simplified + regarding point count. The algorithm makes sure to preserve appearance of lines and fills inside + the visible axis rect by generating new temporary points on the outer rect if necessary. + + \a lines will be filled with points in pixel coordinates, that can be drawn with \ref + drawCurveLine. + + \a dataRange specifies the beginning and ending data indices that will be taken into account for + conversion. In this function, the specified range may exceed the total data bounds without harm: + a correspondingly trimmed data range will be used. This takes the burden off the user of this + function to check for valid indices in \a dataRange, e.g. when extending ranges coming from \ref + getDataSegments. + + \a penWidth specifies the pen width that will be used to later draw the lines generated by this + function. This is needed here to calculate an accordingly wider margin around the axis rect when + performing the line optimization. + + Methods that are also involved in the algorithm are: \ref getRegion, \ref getOptimizedPoint, \ref + getOptimizedCornerPoints \ref mayTraverse, \ref getTraverse, \ref getTraverseCornerPoints. + + \see drawCurveLine, drawScatterPlot +*/ +void QCPCurve::getCurveLines(QVector *lines, const QCPDataRange &dataRange, double penWidth) const +{ + if (!lines) return; + lines->clear(); + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + // add margins to rect to compensate for stroke width + const double strokeMargin = qMax(qreal(1.0), qreal(penWidth*0.75)); // stroke radius + 50% safety + const double keyMin = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*keyAxis->pixelOrientation()); + const double keyMax = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*keyAxis->pixelOrientation()); + const double valueMin = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)-strokeMargin*valueAxis->pixelOrientation()); + const double valueMax = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)+strokeMargin*valueAxis->pixelOrientation()); + QCPCurveDataContainer::const_iterator itBegin = mDataContainer->constBegin(); + QCPCurveDataContainer::const_iterator itEnd = mDataContainer->constEnd(); + mDataContainer->limitIteratorsToDataRange(itBegin, itEnd, dataRange); + if (itBegin == itEnd) + return; + QCPCurveDataContainer::const_iterator it = itBegin; + QCPCurveDataContainer::const_iterator prevIt = itEnd-1; + int prevRegion = getRegion(prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin); + QVector trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right) + while (it != itEnd) + { + const int currentRegion = getRegion(it->key, it->value, keyMin, valueMax, keyMax, valueMin); + if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R + { + if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal + { + QPointF crossA, crossB; + if (prevRegion == 5) // we're coming from R, so add this point optimized + { + lines->append(getOptimizedPoint(currentRegion, it->key, it->value, prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin)); + // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point + *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin); + } else if (mayTraverse(prevRegion, currentRegion) && + getTraverse(prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin, crossA, crossB)) + { + // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point: + QVector beforeTraverseCornerPoints, afterTraverseCornerPoints; + getTraverseCornerPoints(prevRegion, currentRegion, keyMin, valueMax, keyMax, valueMin, beforeTraverseCornerPoints, afterTraverseCornerPoints); + if (it != itBegin) + { + *lines << beforeTraverseCornerPoints; + lines->append(crossA); + lines->append(crossB); + *lines << afterTraverseCornerPoints; + } else + { + lines->append(crossB); + *lines << afterTraverseCornerPoints; + trailingPoints << beforeTraverseCornerPoints << crossA ; + } + } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s) + { + *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin); + } + } else // segment does end in R, so we add previous point optimized and this point at original position + { + if (it == itBegin) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end + trailingPoints << getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin); + else + lines->append(getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin)); + lines->append(coordsToPixels(it->key, it->value)); + } + } else // region didn't change + { + if (currentRegion == 5) // still in R, keep adding original points + { + lines->append(coordsToPixels(it->key, it->value)); + } else // still outside R, no need to add anything + { + // see how this is not doing anything? That's the main optimization... + } + } + prevIt = it; + prevRegion = currentRegion; + ++it; + } + *lines << trailingPoints; +} + +/*! \internal + + Called by \ref draw to generate points in pixel coordinates which represent the scatters of the + curve. If a scatter skip is configured (\ref setScatterSkip), the returned points are accordingly + sparser. + + Scatters that aren't visible in the current axis rect are optimized away. + + \a scatters will be filled with points in pixel coordinates, that can be drawn with \ref + drawScatterPlot. + + \a dataRange specifies the beginning and ending data indices that will be taken into account for + conversion. + + \a scatterWidth specifies the scatter width that will be used to later draw the scatters at pixel + coordinates generated by this function. This is needed here to calculate an accordingly wider + margin around the axis rect when performing the data point reduction. + + \see draw, drawScatterPlot +*/ +void QCPCurve::getScatters(QVector *scatters, const QCPDataRange &dataRange, double scatterWidth) const +{ + if (!scatters) return; + scatters->clear(); + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin(); + QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd(); + mDataContainer->limitIteratorsToDataRange(begin, end, dataRange); + if (begin == end) + return; + const int scatterModulo = mScatterSkip+1; + const bool doScatterSkip = mScatterSkip > 0; + int endIndex = int( end-mDataContainer->constBegin() ); + + QCPRange keyRange = keyAxis->range(); + QCPRange valueRange = valueAxis->range(); + // extend range to include width of scatter symbols: + keyRange.lower = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.lower)-scatterWidth*keyAxis->pixelOrientation()); + keyRange.upper = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.upper)+scatterWidth*keyAxis->pixelOrientation()); + valueRange.lower = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.lower)-scatterWidth*valueAxis->pixelOrientation()); + valueRange.upper = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.upper)+scatterWidth*valueAxis->pixelOrientation()); + + QCPCurveDataContainer::const_iterator it = begin; + int itIndex = int( begin-mDataContainer->constBegin() ); + while (doScatterSkip && it != end && itIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter + { + ++itIndex; + ++it; + } + if (keyAxis->orientation() == Qt::Vertical) + { + while (it != end) + { + if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value)) + scatters->append(QPointF(valueAxis->coordToPixel(it->value), keyAxis->coordToPixel(it->key))); + + // advance iterator to next (non-skipped) data point: + if (!doScatterSkip) + ++it; + else + { + itIndex += scatterModulo; + if (itIndex < endIndex) // make sure we didn't jump over end + it += scatterModulo; + else + { + it = end; + itIndex = endIndex; + } + } + } + } else + { + while (it != end) + { + if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value)) + scatters->append(QPointF(keyAxis->coordToPixel(it->key), valueAxis->coordToPixel(it->value))); + + // advance iterator to next (non-skipped) data point: + if (!doScatterSkip) + ++it; + else + { + itIndex += scatterModulo; + if (itIndex < endIndex) // make sure we didn't jump over end + it += scatterModulo; + else + { + it = end; + itIndex = endIndex; + } + } + } + } +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveLines. + + It returns the region of the given point (\a key, \a value) with respect to a rectangle defined + by \a keyMin, \a keyMax, \a valueMin, and \a valueMax. + + The regions are enumerated from top to bottom (\a valueMin to \a valueMax) and left to right (\a + keyMin to \a keyMax): + + + + + +
147
258
369
+ + With the rectangle being region 5, and the outer regions extending infinitely outwards. In the + curve optimization algorithm, region 5 is considered to be the visible portion of the plot. +*/ +int QCPCurve::getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const +{ + if (key < keyMin) // region 123 + { + if (value > valueMax) + return 1; + else if (value < valueMin) + return 3; + else + return 2; + } else if (key > keyMax) // region 789 + { + if (value > valueMax) + return 7; + else if (value < valueMin) + return 9; + else + return 8; + } else // region 456 + { + if (value > valueMax) + return 4; + else if (value < valueMin) + return 6; + else + return 5; + } +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveLines. + + This method is used in case the current segment passes from inside the visible rect (region 5, + see \ref getRegion) to any of the outer regions (\a otherRegion). The current segment is given by + the line connecting (\a key, \a value) with (\a otherKey, \a otherValue). + + It returns the intersection point of the segment with the border of region 5. + + For this function it doesn't matter whether (\a key, \a value) is the point inside region 5 or + whether it's (\a otherKey, \a otherValue), i.e. whether the segment is coming from region 5 or + leaving it. It is important though that \a otherRegion correctly identifies the other region not + equal to 5. +*/ +QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const +{ + // The intersection point interpolation here is done in pixel coordinates, so we don't need to + // differentiate between different axis scale types. Note that the nomenclature + // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be + // different in pixel coordinates (horz/vert key axes, reversed ranges) + + const double keyMinPx = mKeyAxis->coordToPixel(keyMin); + const double keyMaxPx = mKeyAxis->coordToPixel(keyMax); + const double valueMinPx = mValueAxis->coordToPixel(valueMin); + const double valueMaxPx = mValueAxis->coordToPixel(valueMax); + const double otherValuePx = mValueAxis->coordToPixel(otherValue); + const double valuePx = mValueAxis->coordToPixel(value); + const double otherKeyPx = mKeyAxis->coordToPixel(otherKey); + const double keyPx = mKeyAxis->coordToPixel(key); + double intersectKeyPx = keyMinPx; // initial key just a fail-safe + double intersectValuePx = valueMinPx; // initial value just a fail-safe + switch (otherRegion) + { + case 1: // top and left edge + { + intersectValuePx = valueMaxPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed) + { + intersectKeyPx = keyMinPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); + } + break; + } + case 2: // left edge + { + intersectKeyPx = keyMinPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); + break; + } + case 3: // bottom and left edge + { + intersectValuePx = valueMinPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed) + { + intersectKeyPx = keyMinPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); + } + break; + } + case 4: // top edge + { + intersectValuePx = valueMaxPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + break; + } + case 5: + { + break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table + } + case 6: // bottom edge + { + intersectValuePx = valueMinPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + break; + } + case 7: // top and right edge + { + intersectValuePx = valueMaxPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed) + { + intersectKeyPx = keyMaxPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); + } + break; + } + case 8: // right edge + { + intersectKeyPx = keyMaxPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); + break; + } + case 9: // bottom and right edge + { + intersectValuePx = valueMinPx; + intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx); + if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed) + { + intersectKeyPx = keyMaxPx; + intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx); + } + break; + } + } + if (mKeyAxis->orientation() == Qt::Horizontal) + return {intersectKeyPx, intersectValuePx}; + else + return {intersectValuePx, intersectKeyPx}; +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveLines. + + In situations where a single segment skips over multiple regions it might become necessary to add + extra points at the corners of region 5 (see \ref getRegion) such that the optimized segment + doesn't unintentionally cut through the visible area of the axis rect and create plot artifacts. + This method provides these points that must be added, assuming the original segment doesn't + start, end, or traverse region 5. (Corner points where region 5 is traversed are calculated by + \ref getTraverseCornerPoints.) + + For example, consider a segment which directly goes from region 4 to 2 but originally is far out + to the top left such that it doesn't cross region 5. Naively optimizing these points by + projecting them on the top and left borders of region 5 will create a segment that surely crosses + 5, creating a visual artifact in the plot. This method prevents this by providing extra points at + the top left corner, making the optimized curve correctly pass from region 4 to 1 to 2 without + traversing 5. +*/ +QVector QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const +{ + QVector result; + switch (prevRegion) + { + case 1: + { + switch (currentRegion) + { + case 2: { result << coordsToPixels(keyMin, valueMax); break; } + case 4: { result << coordsToPixels(keyMin, valueMax); break; } + case 3: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); break; } + case 7: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); break; } + case 6: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; } + case 8: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; } + case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points + if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R + { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); } + else + { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); } + break; + } + } + break; + } + case 2: + { + switch (currentRegion) + { + case 1: { result << coordsToPixels(keyMin, valueMax); break; } + case 3: { result << coordsToPixels(keyMin, valueMin); break; } + case 4: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; } + case 6: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; } + case 7: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; } + case 9: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; } + } + break; + } + case 3: + { + switch (currentRegion) + { + case 2: { result << coordsToPixels(keyMin, valueMin); break; } + case 6: { result << coordsToPixels(keyMin, valueMin); break; } + case 1: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); break; } + case 9: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); break; } + case 4: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; } + case 8: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; } + case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points + if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R + { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); } + else + { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); } + break; + } + } + break; + } + case 4: + { + switch (currentRegion) + { + case 1: { result << coordsToPixels(keyMin, valueMax); break; } + case 7: { result << coordsToPixels(keyMax, valueMax); break; } + case 2: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; } + case 8: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; } + case 3: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; } + case 9: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; } + } + break; + } + case 5: + { + switch (currentRegion) + { + case 1: { result << coordsToPixels(keyMin, valueMax); break; } + case 7: { result << coordsToPixels(keyMax, valueMax); break; } + case 9: { result << coordsToPixels(keyMax, valueMin); break; } + case 3: { result << coordsToPixels(keyMin, valueMin); break; } + } + break; + } + case 6: + { + switch (currentRegion) + { + case 3: { result << coordsToPixels(keyMin, valueMin); break; } + case 9: { result << coordsToPixels(keyMax, valueMin); break; } + case 2: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; } + case 8: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; } + case 1: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; } + case 7: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; } + } + break; + } + case 7: + { + switch (currentRegion) + { + case 4: { result << coordsToPixels(keyMax, valueMax); break; } + case 8: { result << coordsToPixels(keyMax, valueMax); break; } + case 1: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); break; } + case 9: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); break; } + case 2: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; } + case 6: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; } + case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points + if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R + { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); } + else + { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); } + break; + } + } + break; + } + case 8: + { + switch (currentRegion) + { + case 7: { result << coordsToPixels(keyMax, valueMax); break; } + case 9: { result << coordsToPixels(keyMax, valueMin); break; } + case 4: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; } + case 6: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; } + case 1: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; } + case 3: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; } + } + break; + } + case 9: + { + switch (currentRegion) + { + case 6: { result << coordsToPixels(keyMax, valueMin); break; } + case 8: { result << coordsToPixels(keyMax, valueMin); break; } + case 3: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); break; } + case 7: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); break; } + case 2: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; } + case 4: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; } + case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points + if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R + { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); } + else + { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); } + break; + } + } + break; + } + } + return result; +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveLines. + + This method returns whether a segment going from \a prevRegion to \a currentRegion (see \ref + getRegion) may traverse the visible region 5. This function assumes that neither \a prevRegion + nor \a currentRegion is 5 itself. + + If this method returns false, the segment for sure doesn't pass region 5. If it returns true, the + segment may or may not pass region 5 and a more fine-grained calculation must be used (\ref + getTraverse). +*/ +bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const +{ + switch (prevRegion) + { + case 1: + { + switch (currentRegion) + { + case 4: + case 7: + case 2: + case 3: return false; + default: return true; + } + } + case 2: + { + switch (currentRegion) + { + case 1: + case 3: return false; + default: return true; + } + } + case 3: + { + switch (currentRegion) + { + case 1: + case 2: + case 6: + case 9: return false; + default: return true; + } + } + case 4: + { + switch (currentRegion) + { + case 1: + case 7: return false; + default: return true; + } + } + case 5: return false; // should never occur + case 6: + { + switch (currentRegion) + { + case 3: + case 9: return false; + default: return true; + } + } + case 7: + { + switch (currentRegion) + { + case 1: + case 4: + case 8: + case 9: return false; + default: return true; + } + } + case 8: + { + switch (currentRegion) + { + case 7: + case 9: return false; + default: return true; + } + } + case 9: + { + switch (currentRegion) + { + case 3: + case 6: + case 8: + case 7: return false; + default: return true; + } + } + default: return true; + } +} + + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveLines. + + This method assumes that the \ref mayTraverse test has returned true, so there is a chance the + segment defined by (\a prevKey, \a prevValue) and (\a key, \a value) goes through the visible + region 5. + + The return value of this method indicates whether the segment actually traverses region 5 or not. + + If the segment traverses 5, the output parameters \a crossA and \a crossB indicate the entry and + exit points of region 5. They will become the optimized points for that segment. +*/ +bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const +{ + // The intersection point interpolation here is done in pixel coordinates, so we don't need to + // differentiate between different axis scale types. Note that the nomenclature + // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be + // different in pixel coordinates (horz/vert key axes, reversed ranges) + + QList intersections; + const double valueMinPx = mValueAxis->coordToPixel(valueMin); + const double valueMaxPx = mValueAxis->coordToPixel(valueMax); + const double keyMinPx = mKeyAxis->coordToPixel(keyMin); + const double keyMaxPx = mKeyAxis->coordToPixel(keyMax); + const double keyPx = mKeyAxis->coordToPixel(key); + const double valuePx = mValueAxis->coordToPixel(value); + const double prevKeyPx = mKeyAxis->coordToPixel(prevKey); + const double prevValuePx = mValueAxis->coordToPixel(prevValue); + if (qFuzzyIsNull(keyPx-prevKeyPx)) // line is parallel to value axis + { + // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMinPx) : QPointF(valueMinPx, keyPx)); // direction will be taken care of at end of method + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMaxPx) : QPointF(valueMaxPx, keyPx)); + } else if (qFuzzyIsNull(valuePx-prevValuePx)) // line is parallel to key axis + { + // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, valuePx) : QPointF(valuePx, keyMinPx)); // direction will be taken care of at end of method + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, valuePx) : QPointF(valuePx, keyMaxPx)); + } else // line is skewed + { + double gamma; + double keyPerValuePx = (keyPx-prevKeyPx)/(valuePx-prevValuePx); + // check top of rect: + gamma = prevKeyPx + (valueMaxPx-prevValuePx)*keyPerValuePx; + if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMaxPx) : QPointF(valueMaxPx, gamma)); + // check bottom of rect: + gamma = prevKeyPx + (valueMinPx-prevValuePx)*keyPerValuePx; + if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMinPx) : QPointF(valueMinPx, gamma)); + const double valuePerKeyPx = 1.0/keyPerValuePx; + // check left of rect: + gamma = prevValuePx + (keyMinPx-prevKeyPx)*valuePerKeyPx; + if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, gamma) : QPointF(gamma, keyMinPx)); + // check right of rect: + gamma = prevValuePx + (keyMaxPx-prevKeyPx)*valuePerKeyPx; + if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed + intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, gamma) : QPointF(gamma, keyMaxPx)); + } + + // handle cases where found points isn't exactly 2: + if (intersections.size() > 2) + { + // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between: + double distSqrMax = 0; + QPointF pv1, pv2; + for (int i=0; i distSqrMax) + { + pv1 = intersections.at(i); + pv2 = intersections.at(k); + distSqrMax = distSqr; + } + } + } + intersections = QList() << pv1 << pv2; + } else if (intersections.size() != 2) + { + // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment + return false; + } + + // possibly re-sort points so optimized point segment has same direction as original segment: + double xDelta = keyPx-prevKeyPx; + double yDelta = valuePx-prevValuePx; + if (mKeyAxis->orientation() != Qt::Horizontal) + qSwap(xDelta, yDelta); + if (xDelta*(intersections.at(1).x()-intersections.at(0).x()) + yDelta*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction + intersections.move(0, 1); + crossA = intersections.at(0); + crossB = intersections.at(1); + return true; +} + +/*! \internal + + This function is part of the curve optimization algorithm of \ref getCurveLines. + + This method assumes that the \ref getTraverse test has returned true, so the segment definitely + traverses the visible region 5 when going from \a prevRegion to \a currentRegion. + + In certain situations it is not sufficient to merely generate the entry and exit points of the + segment into/out of region 5, as \ref getTraverse provides. It may happen that a single segment, in + addition to traversing region 5, skips another region outside of region 5, which makes it + necessary to add an optimized corner point there (very similar to the job \ref + getOptimizedCornerPoints does for segments that are completely in outside regions and don't + traverse 5). + + As an example, consider a segment going from region 1 to region 6, traversing the lower left + corner of region 5. In this configuration, the segment additionally crosses the border between + region 1 and 2 before entering region 5. This makes it necessary to add an additional point in + the top left corner, before adding the optimized traverse points. So in this case, the output + parameter \a beforeTraverse will contain the top left corner point, and \a afterTraverse will be + empty. + + In some cases, such as when going from region 1 to 9, it may even be necessary to add additional + corner points before and after the traverse. Then both \a beforeTraverse and \a afterTraverse + return the respective corner points. +*/ +void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector &beforeTraverse, QVector &afterTraverse) const +{ + switch (prevRegion) + { + case 1: + { + switch (currentRegion) + { + case 6: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; } + case 9: { beforeTraverse << coordsToPixels(keyMin, valueMax); afterTraverse << coordsToPixels(keyMax, valueMin); break; } + case 8: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; } + } + break; + } + case 2: + { + switch (currentRegion) + { + case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; } + case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; } + } + break; + } + case 3: + { + switch (currentRegion) + { + case 4: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; } + case 7: { beforeTraverse << coordsToPixels(keyMin, valueMin); afterTraverse << coordsToPixels(keyMax, valueMax); break; } + case 8: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; } + } + break; + } + case 4: + { + switch (currentRegion) + { + case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; } + case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; } + } + break; + } + case 5: { break; } // shouldn't happen because this method only handles full traverses + case 6: + { + switch (currentRegion) + { + case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; } + case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; } + } + break; + } + case 7: + { + switch (currentRegion) + { + case 2: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; } + case 3: { beforeTraverse << coordsToPixels(keyMax, valueMax); afterTraverse << coordsToPixels(keyMin, valueMin); break; } + case 6: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; } + } + break; + } + case 8: + { + switch (currentRegion) + { + case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; } + case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; } + } + break; + } + case 9: + { + switch (currentRegion) + { + case 2: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; } + case 1: { beforeTraverse << coordsToPixels(keyMax, valueMin); afterTraverse << coordsToPixels(keyMin, valueMax); break; } + case 4: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; } + } + break; + } + } +} + +/*! \internal + + Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a + pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in + \ref selectTest. The closest data point to \a pixelPoint is returned in \a closestData. Note that + if the curve has a line representation, the returned distance may be smaller than the distance to + the \a closestData point, since the distance to the curve line is also taken into account. + + If either the curve has no data or if the line style is \ref lsNone and the scatter style's shape + is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the curve), returns + -1.0. +*/ +double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const +{ + closestData = mDataContainer->constEnd(); + if (mDataContainer->isEmpty()) + return -1.0; + if (mLineStyle == lsNone && mScatterStyle.isNone()) + return -1.0; + + if (mDataContainer->size() == 1) + { + QPointF dataPoint = coordsToPixels(mDataContainer->constBegin()->key, mDataContainer->constBegin()->value); + closestData = mDataContainer->constBegin(); + return QCPVector2D(dataPoint-pixelPoint).length(); + } + + // calculate minimum distances to curve data points and find closestData iterator: + double minDistSqr = (std::numeric_limits::max)(); + // iterate over found data points and then choose the one with the shortest distance to pos: + QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin(); + QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd(); + for (QCPCurveDataContainer::const_iterator it=begin; it!=end; ++it) + { + const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared(); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestData = it; + } + } + + // calculate distance to line if there is one (if so, will probably be smaller than distance to closest data point): + if (mLineStyle != lsNone) + { + QVector lines; + getCurveLines(&lines, QCPDataRange(0, dataCount()), mParentPlot->selectionTolerance()*1.2); // optimized lines outside axis rect shouldn't respond to clicks at the edge, so use 1.2*tolerance as pen width + for (int i=0; i QCPBarsGroup::bars() const + + Returns all bars currently in this group. + + \see bars(int index) +*/ + +/*! \fn int QCPBarsGroup::size() const + + Returns the number of QCPBars plottables that are part of this group. + +*/ + +/*! \fn bool QCPBarsGroup::isEmpty() const + + Returns whether this bars group is empty. + + \see size +*/ + +/*! \fn bool QCPBarsGroup::contains(QCPBars *bars) + + Returns whether the specified \a bars plottable is part of this group. + +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a new bars group for the specified QCustomPlot instance. +*/ +QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) : + QObject(parentPlot), + mParentPlot(parentPlot), + mSpacingType(stAbsolute), + mSpacing(4) +{ +} + +QCPBarsGroup::~QCPBarsGroup() +{ + clear(); +} + +/*! + Sets how the spacing between adjacent bars is interpreted. See \ref SpacingType. + + The actual spacing can then be specified with \ref setSpacing. + + \see setSpacing +*/ +void QCPBarsGroup::setSpacingType(SpacingType spacingType) +{ + mSpacingType = spacingType; +} + +/*! + Sets the spacing between adjacent bars. What the number passed as \a spacing actually means, is + defined by the current \ref SpacingType, which can be set with \ref setSpacingType. + + \see setSpacingType +*/ +void QCPBarsGroup::setSpacing(double spacing) +{ + mSpacing = spacing; +} + +/*! + Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars + exists, returns \c nullptr. + + \see bars(), size +*/ +QCPBars *QCPBarsGroup::bars(int index) const +{ + if (index >= 0 && index < mBars.size()) + { + return mBars.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "index out of bounds:" << index; + return nullptr; + } +} + +/*! + Removes all QCPBars plottables from this group. + + \see isEmpty +*/ +void QCPBarsGroup::clear() +{ + const QList oldBars = mBars; + foreach (QCPBars *bars, oldBars) + bars->setBarsGroup(nullptr); // removes itself from mBars via removeBars +} + +/*! + Adds the specified \a bars plottable to this group. Alternatively, you can also use \ref + QCPBars::setBarsGroup on the \a bars instance. + + \see insert, remove +*/ +void QCPBarsGroup::append(QCPBars *bars) +{ + if (!bars) + { + qDebug() << Q_FUNC_INFO << "bars is 0"; + return; + } + + if (!mBars.contains(bars)) + bars->setBarsGroup(this); + else + qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast(bars); +} + +/*! + Inserts the specified \a bars plottable into this group at the specified index position \a i. + This gives you full control over the ordering of the bars. + + \a bars may already be part of this group. In that case, \a bars is just moved to the new index + position. + + \see append, remove +*/ +void QCPBarsGroup::insert(int i, QCPBars *bars) +{ + if (!bars) + { + qDebug() << Q_FUNC_INFO << "bars is 0"; + return; + } + + // first append to bars list normally: + if (!mBars.contains(bars)) + bars->setBarsGroup(this); + // then move to according position: + mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1)); +} + +/*! + Removes the specified \a bars plottable from this group. + + \see contains, clear +*/ +void QCPBarsGroup::remove(QCPBars *bars) +{ + if (!bars) + { + qDebug() << Q_FUNC_INFO << "bars is 0"; + return; + } + + if (mBars.contains(bars)) + bars->setBarsGroup(nullptr); + else + qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast(bars); +} + +/*! \internal + + Adds the specified \a bars to the internal mBars list of bars. This method does not change the + barsGroup property on \a bars. + + \see unregisterBars +*/ +void QCPBarsGroup::registerBars(QCPBars *bars) +{ + if (!mBars.contains(bars)) + mBars.append(bars); +} + +/*! \internal + + Removes the specified \a bars from the internal mBars list of bars. This method does not change + the barsGroup property on \a bars. + + \see registerBars +*/ +void QCPBarsGroup::unregisterBars(QCPBars *bars) +{ + mBars.removeOne(bars); +} + +/*! \internal + + Returns the pixel offset in the key dimension the specified \a bars plottable should have at the + given key coordinate \a keyCoord. The offset is relative to the pixel position of the key + coordinate \a keyCoord. +*/ +double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord) +{ + // find list of all base bars in case some mBars are stacked: + QList baseBars; + foreach (const QCPBars *b, mBars) + { + while (b->barBelow()) + b = b->barBelow(); + if (!baseBars.contains(b)) + baseBars.append(b); + } + // find base bar this "bars" is stacked on: + const QCPBars *thisBase = bars; + while (thisBase->barBelow()) + thisBase = thisBase->barBelow(); + + // determine key pixel offset of this base bars considering all other base bars in this barsgroup: + double result = 0; + int index = static_cast(baseBars.indexOf(thisBase)); + if (index >= 0) + { + if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose) + { + return result; + } else + { + double lowerPixelWidth, upperPixelWidth; + int startIndex; + int dir = (index <= (baseBars.size()-1)/2) ? -1 : 1; // if bar is to lower keys of center, dir is negative + if (baseBars.size() % 2 == 0) // even number of bars + { + startIndex = static_cast(baseBars.size())/2 + (dir < 0 ? -1 : 0); + result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing + } else // uneven number of bars + { + startIndex = (static_cast(baseBars.size())-1)/2+dir; + baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar + result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing + } + for (int i = startIndex; i != index; i += dir) // add widths and spacings of bars in between center and our bars + { + baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result += qAbs(upperPixelWidth-lowerPixelWidth); + result += getPixelSpacing(baseBars.at(i), keyCoord); + } + // finally half of our bars width: + baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth); + result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; + // correct sign of result depending on orientation and direction of key axis: + result *= dir*thisBase->keyAxis()->pixelOrientation(); + } + } + return result; +} + +/*! \internal + + Returns the spacing in pixels which is between this \a bars and the following one, both at the + key coordinate \a keyCoord. + + \note Typically the returned value doesn't depend on \a bars or \a keyCoord. \a bars is only + needed to get access to the key axis transformation and axis rect for the modes \ref + stAxisRectRatio and \ref stPlotCoords. The \a keyCoord is only relevant for spacings given in + \ref stPlotCoords on a logarithmic axis. +*/ +double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord) +{ + switch (mSpacingType) + { + case stAbsolute: + { + return mSpacing; + } + case stAxisRectRatio: + { + if (bars->keyAxis()->orientation() == Qt::Horizontal) + return bars->keyAxis()->axisRect()->width()*mSpacing; + else + return bars->keyAxis()->axisRect()->height()*mSpacing; + } + case stPlotCoords: + { + double keyPixel = bars->keyAxis()->coordToPixel(keyCoord); + return qAbs(bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel); + } + } + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPBarsData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPBarsData + \brief Holds the data of one single data point (one bar) for QCPBars. + + The stored data is: + \li \a key: coordinate on the key axis of this bar (this is the \a mainKey and the \a sortKey) + \li \a value: height coordinate on the value axis of this bar (this is the \a mainValue) + + The container for storing multiple data points is \ref QCPBarsDataContainer. It is a typedef for + \ref QCPDataContainer with \ref QCPBarsData as the DataType template parameter. See the + documentation there for an explanation regarding the data type's generic methods. + + \see QCPBarsDataContainer +*/ + +/* start documentation of inline functions */ + +/*! \fn double QCPBarsData::sortKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static QCPBarsData QCPBarsData::fromSortKey(double sortKey) + + Returns a data point with the specified \a sortKey. All other members are set to zero. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static static bool QCPBarsData::sortKeyIsMainKey() + + Since the member \a key is both the data point key coordinate and the data ordering parameter, + this method returns true. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPBarsData::mainKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPBarsData::mainValue() const + + Returns the \a value member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn QCPRange QCPBarsData::valueRange() const + + Returns a QCPRange with both lower and upper boundary set to \a value of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/* end documentation of inline functions */ + +/*! + Constructs a bar data point with key and value set to zero. +*/ +QCPBarsData::QCPBarsData() : + key(0), + value(0) +{ +} + +/*! + Constructs a bar data point with the specified \a key and \a value. +*/ +QCPBarsData::QCPBarsData(double key, double value) : + key(key), + value(value) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPBars +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPBars + \brief A plottable representing a bar chart in a plot. + + \image html QCPBars.png + + To plot data, assign it with the \ref setData or \ref addData functions. + + \section qcpbars-appearance Changing the appearance + + The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush). + The width of the individual bars can be controlled with \ref setWidthType and \ref setWidth. + + Bar charts are stackable. This means, two QCPBars plottables can be placed on top of each other + (see \ref QCPBars::moveAbove). So when two bars are at the same key position, they will appear + stacked. + + If you would like to group multiple QCPBars plottables together so they appear side by side as + shown below, use QCPBarsGroup. + + \image html QCPBarsGroup.png + + \section qcpbars-usage Usage + + Like all data representing objects in QCustomPlot, the QCPBars is a plottable + (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies + (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.) + + Usually, you first create an instance: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1 + which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot instance takes + ownership of the plottable, so do not delete it manually but use QCustomPlot::removePlottable() instead. + The newly created plottable can be modified, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2 +*/ + +/* start of documentation of inline functions */ + +/*! \fn QSharedPointer QCPBars::data() const + + Returns a shared pointer to the internal data storage of type \ref QCPBarsDataContainer. You may + use it to directly manipulate the data, which may be more convenient and faster than using the + regular \ref setData or \ref addData methods. +*/ + +/*! \fn QCPBars *QCPBars::barBelow() const + Returns the bars plottable that is directly below this bars plottable. + If there is no such plottable, returns \c nullptr. + + \see barAbove, moveBelow, moveAbove +*/ + +/*! \fn QCPBars *QCPBars::barAbove() const + Returns the bars plottable that is directly above this bars plottable. + If there is no such plottable, returns \c nullptr. + + \see barBelow, moveBelow, moveAbove +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + The created QCPBars is automatically registered with the QCustomPlot instance inferred from \a + keyAxis. This QCustomPlot instance takes ownership of the QCPBars, so do not delete it manually + but use QCustomPlot::removePlottable() instead. +*/ +QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable1D(keyAxis, valueAxis), + mWidth(0.75), + mWidthType(wtPlotCoords), + mBarsGroup(nullptr), + mBaseValue(0), + mStackingGap(1) +{ + // modify inherited properties from abstract plottable: + mPen.setColor(Qt::blue); + mPen.setStyle(Qt::SolidLine); + mBrush.setColor(QColor(40, 50, 255, 30)); + mBrush.setStyle(Qt::SolidPattern); + mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255))); +} + +QCPBars::~QCPBars() +{ + setBarsGroup(nullptr); + if (mBarBelow || mBarAbove) + connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking +} + +/*! \overload + + Replaces the current data container with the provided \a data container. + + Since a QSharedPointer is used, multiple QCPBars may share the same data container safely. + Modifying the data in the container will then affect all bars that share the container. Sharing + can be achieved by simply exchanging the data containers wrapped in shared pointers: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-datasharing-1 + + If you do not wish to share containers, but create a copy from an existing container, rather use + the \ref QCPDataContainer::set method on the bar's data container directly: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-datasharing-2 + + \see addData +*/ +void QCPBars::setData(QSharedPointer data) +{ + mDataContainer = data; +} + +/*! \overload + + Replaces the current data with the provided points in \a keys and \a values. The provided + vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + \see addData +*/ +void QCPBars::setData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + mDataContainer->clear(); + addData(keys, values, alreadySorted); +} + +/*! + Sets the width of the bars. + + How the number passed as \a width is interpreted (e.g. screen pixels, plot coordinates,...), + depends on the currently set width type, see \ref setWidthType and \ref WidthType. +*/ +void QCPBars::setWidth(double width) +{ + mWidth = width; +} + +/*! + Sets how the width of the bars is defined. See the documentation of \ref WidthType for an + explanation of the possible values for \a widthType. + + The default value is \ref wtPlotCoords. + + \see setWidth +*/ +void QCPBars::setWidthType(QCPBars::WidthType widthType) +{ + mWidthType = widthType; +} + +/*! + Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref + QCPBarsGroup::append. + + To remove this QCPBars from any group, set \a barsGroup to \c nullptr. +*/ +void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup) +{ + // deregister at old group: + if (mBarsGroup) + mBarsGroup->unregisterBars(this); + mBarsGroup = barsGroup; + // register at new group: + if (mBarsGroup) + mBarsGroup->registerBars(this); +} + +/*! + Sets the base value of this bars plottable. + + The base value defines where on the value coordinate the bars start. How far the bars extend from + the base value is given by their individual value data. For example, if the base value is set to + 1, a bar with data value 2 will have its lowest point at value coordinate 1 and highest point at + 3. + + For stacked bars, only the base value of the bottom-most QCPBars has meaning. + + The default base value is 0. +*/ +void QCPBars::setBaseValue(double baseValue) +{ + mBaseValue = baseValue; +} + +/*! + If this bars plottable is stacked on top of another bars plottable (\ref moveAbove), this method + allows specifying a distance in \a pixels, by which the drawn bar rectangles will be separated by + the bars below it. +*/ +void QCPBars::setStackingGap(double pixels) +{ + mStackingGap = pixels; +} + +/*! \overload + + Adds the provided points in \a keys and \a values to the current data. The provided vectors + should have equal length. Else, the number of added points will be the size of the smallest + vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPBars::addData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + if (keys.size() != values.size()) + qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size(); + const int n = static_cast(qMin(keys.size(), values.size())); + QVector tempData(n); + QVector::iterator it = tempData.begin(); + const QVector::iterator itEnd = tempData.end(); + int i = 0; + while (it != itEnd) + { + it->key = keys[i]; + it->value = values[i]; + ++it; + ++i; + } + mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write +} + +/*! \overload + Adds the provided data point as \a key and \a value to the current data. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPBars::addData(double key, double value) +{ + mDataContainer->add(QCPBarsData(key, value)); +} + +/*! + Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear + below the bars of \a bars. The move target \a bars must use the same key and value axis as this + plottable. + + Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already + has a bars object below itself, this bars object is inserted between the two. If this bars object + is already between two other bars, the two other bars will be stacked on top of each other after + the operation. + + To remove this bars plottable from any stacking, set \a bars to \c nullptr. + + \see moveBelow, barAbove, barBelow +*/ +void QCPBars::moveBelow(QCPBars *bars) +{ + if (bars == this) return; + if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data())) + { + qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars"; + return; + } + // remove from stacking: + connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0 + // if new bar given, insert this bar below it: + if (bars) + { + if (bars->mBarBelow) + connectBars(bars->mBarBelow.data(), this); + connectBars(this, bars); + } +} + +/*! + Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear + above the bars of \a bars. The move target \a bars must use the same key and value axis as this + plottable. + + Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already + has a bars object above itself, this bars object is inserted between the two. If this bars object + is already between two other bars, the two other bars will be stacked on top of each other after + the operation. + + To remove this bars plottable from any stacking, set \a bars to \c nullptr. + + \see moveBelow, barBelow, barAbove +*/ +void QCPBars::moveAbove(QCPBars *bars) +{ + if (bars == this) return; + if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data())) + { + qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars"; + return; + } + // remove from stacking: + connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0 + // if new bar given, insert this bar above it: + if (bars) + { + if (bars->mBarAbove) + connectBars(this, bars->mBarAbove.data()); + connectBars(bars, this); + } +} + +/*! + \copydoc QCPPlottableInterface1D::selectTestRect +*/ +QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable) const +{ + QCPDataSelection result; + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return result; + if (!mKeyAxis || !mValueAxis) + return result; + + QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd; + getVisibleDataBounds(visibleBegin, visibleEnd); + + for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) + { + if (rect.intersects(getBarRect(it->key, it->value))) + result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false); + } + result.simplify(); + return result; +} + +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ +double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) + { + // get visible data range: + QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd; + getVisibleDataBounds(visibleBegin, visibleEnd); + for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) + { + if (getBarRect(it->key, it->value).contains(pos)) + { + if (details) + { + int pointIndex = int(it-mDataContainer->constBegin()); + details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); + } + return mParentPlot->selectionTolerance()*0.99; + } + } + } + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + /* Note: If this QCPBars uses absolute pixels as width (or is in a QCPBarsGroup with spacing in + absolute pixels), using this method to adapt the key axis range to fit the bars into the + currently visible axis range will not work perfectly. Because in the moment the axis range is + changed to the new range, the fixed pixel widths/spacings will represent different coordinate + spans than before, which in turn would require a different key range to perfectly fit, and so on. + The only solution would be to iteratively approach the perfect fitting axis range, but the + mismatch isn't large enough in most applications, to warrant this here. If a user does need a + better fit, he should call the corresponding axis rescale multiple times in a row. + */ + QCPRange range; + range = mDataContainer->keyRange(foundRange, inSignDomain); + + // determine exact range of bars by including bar width and barsgroup offset: + if (foundRange && mKeyAxis) + { + double lowerPixelWidth, upperPixelWidth, keyPixel; + // lower range bound: + getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth); + keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth; + if (mBarsGroup) + keyPixel += mBarsGroup->keyPixelOffset(this, range.lower); + const double lowerCorrected = mKeyAxis.data()->pixelToCoord(keyPixel); + if (!qIsNaN(lowerCorrected) && qIsFinite(lowerCorrected) && range.lower > lowerCorrected) + range.lower = lowerCorrected; + // upper range bound: + getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth); + keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth; + if (mBarsGroup) + keyPixel += mBarsGroup->keyPixelOffset(this, range.upper); + const double upperCorrected = mKeyAxis.data()->pixelToCoord(keyPixel); + if (!qIsNaN(upperCorrected) && qIsFinite(upperCorrected) && range.upper < upperCorrected) + range.upper = upperCorrected; + } + return range; +} + +/* inherits documentation from base class */ +QCPRange QCPBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + // Note: can't simply use mDataContainer->valueRange here because we need to + // take into account bar base value and possible stacking of multiple bars + QCPRange range; + range.lower = mBaseValue; + range.upper = mBaseValue; + bool haveLower = true; // set to true, because baseValue should always be visible in bar charts + bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts + QCPBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin(); + QCPBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd(); + if (inKeyRange != QCPRange()) + { + itBegin = mDataContainer->findBegin(inKeyRange.lower, false); + itEnd = mDataContainer->findEnd(inKeyRange.upper, false); + } + for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it) + { + const double current = it->value + getStackedBaseValue(it->key, it->value >= 0); + if (qIsNaN(current)) continue; + if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + } + + foundRange = true; // return true because bar charts always have the 0-line visible + return range; +} + +/* inherits documentation from base class */ +QPointF QCPBars::dataPixelPosition(int index) const +{ + if (index >= 0 && index < mDataContainer->size()) + { + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; } + + const QCPDataContainer::const_iterator it = mDataContainer->constBegin()+index; + const double valuePixel = valueAxis->coordToPixel(getStackedBaseValue(it->key, it->value >= 0) + it->value); + const double keyPixel = keyAxis->coordToPixel(it->key) + (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0); + if (keyAxis->orientation() == Qt::Horizontal) + return {keyPixel, valuePixel}; + else + return {valuePixel, keyPixel}; + } else + { + qDebug() << Q_FUNC_INFO << "Index out of bounds" << index; + return {}; + } +} + +/* inherits documentation from base class */ +void QCPBars::draw(QCPPainter *painter) +{ + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (mDataContainer->isEmpty()) return; + + QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd; + getVisibleDataBounds(visibleBegin, visibleEnd); + + // loop over and draw segments of unselected/selected data: + QList selectedSegments, unselectedSegments, allSegments; + getDataSegments(selectedSegments, unselectedSegments); + allSegments << unselectedSegments << selectedSegments; + for (int i=0; i= unselectedSegments.size(); + QCPBarsDataContainer::const_iterator begin = visibleBegin; + QCPBarsDataContainer::const_iterator end = visibleEnd; + mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i)); + if (begin == end) + continue; + + for (QCPBarsDataContainer::const_iterator it=begin; it!=end; ++it) + { + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + if (QCP::isInvalidData(it->key, it->value)) + qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range invalid." << "Plottable name:" << name(); +#endif + // draw bar: + if (isSelectedSegment && mSelectionDecorator) + { + mSelectionDecorator->applyBrush(painter); + mSelectionDecorator->applyPen(painter); + } else + { + painter->setBrush(mBrush); + painter->setPen(mPen); + } + applyDefaultAntialiasingHint(painter); + painter->drawPolygon(getBarRect(it->key, it->value)); + } + } + + // draw other selection decoration that isn't just line/scatter pens and brushes: + if (mSelectionDecorator) + mSelectionDecorator->drawDecoration(painter, selection()); +} + +/* inherits documentation from base class */ +void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + // draw filled rect: + applyDefaultAntialiasingHint(painter); + painter->setBrush(mBrush); + painter->setPen(mPen); + QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67); + r.moveCenter(rect.center()); + painter->drawRect(r); +} + +/*! \internal + + called by \ref draw to determine which data (key) range is visible at the current key axis range + setting, so only that needs to be processed. It also takes into account the bar width. + + \a begin returns an iterator to the lowest data point that needs to be taken into account when + plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a + lower may still be just outside the visible range. + + \a end returns an iterator one higher than the highest visible data point. Same as before, \a end + may also lie just outside of the visible range. + + if the plottable contains no data, both \a begin and \a end point to constEnd. +*/ +void QCPBars::getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const +{ + if (!mKeyAxis) + { + qDebug() << Q_FUNC_INFO << "invalid key axis"; + begin = mDataContainer->constEnd(); + end = mDataContainer->constEnd(); + return; + } + if (mDataContainer->isEmpty()) + { + begin = mDataContainer->constEnd(); + end = mDataContainer->constEnd(); + return; + } + + // get visible data range as QMap iterators + begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower); + end = mDataContainer->findEnd(mKeyAxis.data()->range().upper); + double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower); + double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper); + bool isVisible = false; + // walk left from begin to find lower bar that actually is completely outside visible pixel range: + QCPBarsDataContainer::const_iterator it = begin; + while (it != mDataContainer->constBegin()) + { + --it; + const QRectF barRect = getBarRect(it->key, it->value); + if (mKeyAxis.data()->orientation() == Qt::Horizontal) + isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.left() <= lowerPixelBound)); + else // keyaxis is vertical + isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.bottom() >= lowerPixelBound)); + if (isVisible) + begin = it; + else + break; + } + // walk right from ubound to find upper bar that actually is completely outside visible pixel range: + it = end; + while (it != mDataContainer->constEnd()) + { + const QRectF barRect = getBarRect(it->key, it->value); + if (mKeyAxis.data()->orientation() == Qt::Horizontal) + isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.right() >= upperPixelBound)); + else // keyaxis is vertical + isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.top() <= upperPixelBound)); + if (isVisible) + end = it+1; + else + break; + ++it; + } +} + +/*! \internal + + Returns the rect in pixel coordinates of a single bar with the specified \a key and \a value. The + rect is shifted according to the bar stacking (see \ref moveAbove) and base value (see \ref + setBaseValue), and to have non-overlapping border lines with the bars stacked below. +*/ +QRectF QCPBars::getBarRect(double key, double value) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; } + + double lowerPixelWidth, upperPixelWidth; + getPixelWidth(key, lowerPixelWidth, upperPixelWidth); + double base = getStackedBaseValue(key, value >= 0); + double basePixel = valueAxis->coordToPixel(base); + double valuePixel = valueAxis->coordToPixel(base+value); + double keyPixel = keyAxis->coordToPixel(key); + if (mBarsGroup) + keyPixel += mBarsGroup->keyPixelOffset(this, key); + double bottomOffset = (mBarBelow && mPen != Qt::NoPen ? 1 : 0)*(mPen.isCosmetic() ? 1 : mPen.widthF()); + bottomOffset += mBarBelow ? mStackingGap : 0; + bottomOffset *= (value<0 ? -1 : 1)*valueAxis->pixelOrientation(); + if (qAbs(valuePixel-basePixel) <= qAbs(bottomOffset)) + bottomOffset = valuePixel-basePixel; + if (keyAxis->orientation() == Qt::Horizontal) + { + return QRectF(QPointF(keyPixel+lowerPixelWidth, valuePixel), QPointF(keyPixel+upperPixelWidth, basePixel+bottomOffset)).normalized(); + } else + { + return QRectF(QPointF(basePixel+bottomOffset, keyPixel+lowerPixelWidth), QPointF(valuePixel, keyPixel+upperPixelWidth)).normalized(); + } +} + +/*! \internal + + This function is used to determine the width of the bar at coordinate \a key, according to the + specified width (\ref setWidth) and width type (\ref setWidthType). + + The output parameters \a lower and \a upper return the number of pixels the bar extends to lower + and higher keys, relative to the \a key coordinate (so with a non-reversed horizontal axis, \a + lower is negative and \a upper positive). +*/ +void QCPBars::getPixelWidth(double key, double &lower, double &upper) const +{ + lower = 0; + upper = 0; + switch (mWidthType) + { + case wtAbsolute: + { + upper = mWidth*0.5*mKeyAxis.data()->pixelOrientation(); + lower = -upper; + break; + } + case wtAxisRectRatio: + { + if (mKeyAxis && mKeyAxis.data()->axisRect()) + { + if (mKeyAxis.data()->orientation() == Qt::Horizontal) + upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation(); + else + upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation(); + lower = -upper; + } else + qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined"; + break; + } + case wtPlotCoords: + { + if (mKeyAxis) + { + double keyPixel = mKeyAxis.data()->coordToPixel(key); + upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel; + lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel; + // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by + // coordinate transform which includes range direction + } else + qDebug() << Q_FUNC_INFO << "No key axis defined"; + break; + } + } +} + +/*! \internal + + This function is called to find at which value to start drawing the base of a bar at \a key, when + it is stacked on top of another QCPBars (e.g. with \ref moveAbove). + + positive and negative bars are separated per stack (positive are stacked above baseValue upwards, + negative are stacked below baseValue downwards). This can be indicated with \a positive. So if the + bar for which we need the base value is negative, set \a positive to false. +*/ +double QCPBars::getStackedBaseValue(double key, bool positive) const +{ + if (mBarBelow) + { + double max = 0; // don't initialize with mBaseValue here because only base value of bottom-most bar has meaning in a bar stack + // find bars of mBarBelow that are approximately at key and find largest one: + double epsilon = qAbs(key)*(sizeof(key)==4 ? 1e-6 : 1e-14); // should be safe even when changed to use float at some point + if (key == 0) + epsilon = (sizeof(key)==4 ? 1e-6 : 1e-14); + QCPBarsDataContainer::const_iterator it = mBarBelow.data()->mDataContainer->findBegin(key-epsilon); + QCPBarsDataContainer::const_iterator itEnd = mBarBelow.data()->mDataContainer->findEnd(key+epsilon); + while (it != itEnd) + { + if (it->key > key-epsilon && it->key < key+epsilon) + { + if ((positive && it->value > max) || + (!positive && it->value < max)) + max = it->value; + } + ++it; + } + // recurse down the bar-stack to find the total height: + return max + mBarBelow.data()->getStackedBaseValue(key, positive); + } else + return mBaseValue; +} + +/*! \internal + + Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties. The bar(s) + currently above lower and below upper will become disconnected to lower/upper. + + If lower is zero, upper will be disconnected at the bottom. + If upper is zero, lower will be disconnected at the top. +*/ +void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) +{ + if (!lower && !upper) return; + + if (!lower) // disconnect upper at bottom + { + // disconnect old bar below upper: + if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper) + upper->mBarBelow.data()->mBarAbove = nullptr; + upper->mBarBelow = nullptr; + } else if (!upper) // disconnect lower at top + { + // disconnect old bar above lower: + if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower) + lower->mBarAbove.data()->mBarBelow = nullptr; + lower->mBarAbove = nullptr; + } else // connect lower and upper + { + // disconnect old bar above lower: + if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower) + lower->mBarAbove.data()->mBarBelow = nullptr; + // disconnect old bar below upper: + if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper) + upper->mBarBelow.data()->mBarAbove = nullptr; + lower->mBarAbove = upper; + upper->mBarBelow = lower; + } +} +/* end of 'src/plottables/plottable-bars.cpp' */ + + +/* including file 'src/plottables/plottable-statisticalbox.cpp' */ +/* modified 2022-11-06T12:45:57, size 28951 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPStatisticalBoxData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPStatisticalBoxData + \brief Holds the data of one single data point for QCPStatisticalBox. + + The stored data is: + + \li \a key: coordinate on the key axis of this data point (this is the \a mainKey and the \a sortKey) + + \li \a minimum: the position of the lower whisker, typically the minimum measurement of the + sample that's not considered an outlier. + + \li \a lowerQuartile: the lower end of the box. The lower and the upper quartiles are the two + statistical quartiles around the median of the sample, they should contain 50% of the sample + data. + + \li \a median: the value of the median mark inside the quartile box. The median separates the + sample data in half (50% of the sample data is below/above the median). (This is the \a mainValue) + + \li \a upperQuartile: the upper end of the box. The lower and the upper quartiles are the two + statistical quartiles around the median of the sample, they should contain 50% of the sample + data. + + \li \a maximum: the position of the upper whisker, typically the maximum measurement of the + sample that's not considered an outlier. + + \li \a outliers: a QVector of outlier values that will be drawn as scatter points at the \a key + coordinate of this data point (see \ref QCPStatisticalBox::setOutlierStyle) + + The container for storing multiple data points is \ref QCPStatisticalBoxDataContainer. It is a + typedef for \ref QCPDataContainer with \ref QCPStatisticalBoxData as the DataType template + parameter. See the documentation there for an explanation regarding the data type's generic + methods. + + \see QCPStatisticalBoxDataContainer +*/ + +/* start documentation of inline functions */ + +/*! \fn double QCPStatisticalBoxData::sortKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static QCPStatisticalBoxData QCPStatisticalBoxData::fromSortKey(double sortKey) + + Returns a data point with the specified \a sortKey. All other members are set to zero. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static static bool QCPStatisticalBoxData::sortKeyIsMainKey() + + Since the member \a key is both the data point key coordinate and the data ordering parameter, + this method returns true. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPStatisticalBoxData::mainKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPStatisticalBoxData::mainValue() const + + Returns the \a median member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn QCPRange QCPStatisticalBoxData::valueRange() const + + Returns a QCPRange spanning from the \a minimum to the \a maximum member of this statistical box + data point, possibly further expanded by outliers. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/* end documentation of inline functions */ + +/*! + Constructs a data point with key and all values set to zero. +*/ +QCPStatisticalBoxData::QCPStatisticalBoxData() : + key(0), + minimum(0), + lowerQuartile(0), + median(0), + upperQuartile(0), + maximum(0) +{ +} + +/*! + Constructs a data point with the specified \a key, \a minimum, \a lowerQuartile, \a median, \a + upperQuartile, \a maximum and optionally a number of \a outliers. +*/ +QCPStatisticalBoxData::QCPStatisticalBoxData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector &outliers) : + key(key), + minimum(minimum), + lowerQuartile(lowerQuartile), + median(median), + upperQuartile(upperQuartile), + maximum(maximum), + outliers(outliers) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPStatisticalBox +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPStatisticalBox + \brief A plottable representing a single statistical box in a plot. + + \image html QCPStatisticalBox.png + + To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can + also access and modify the data via the \ref data method, which returns a pointer to the internal + \ref QCPStatisticalBoxDataContainer. + + Additionally each data point can itself have a list of outliers, drawn as scatter points at the + key coordinate of the respective statistical box data point. They can either be set by using the + respective \ref addData(double,double,double,double,double,double,const QVector&) + "addData" method or accessing the individual data points through \ref data, and setting the + QVector outliers of the data points directly. + + \section qcpstatisticalbox-appearance Changing the appearance + + The appearance of each data point box, ranging from the lower to the upper quartile, is + controlled via \ref setPen and \ref setBrush. You may change the width of the boxes with \ref + setWidth in plot coordinates. + + Each data point's visual representation also consists of two whiskers. Whiskers are the lines + which reach from the upper quartile to the maximum, and from the lower quartile to the minimum. + The appearance of the whiskers can be modified with: \ref setWhiskerPen, \ref setWhiskerBarPen, + \ref setWhiskerWidth. The whisker width is the width of the bar perpendicular to the whisker at + the top (for maximum) and bottom (for minimum). If the whisker pen is changed, make sure to set + the \c capStyle to \c Qt::FlatCap. Otherwise the backbone line might exceed the whisker bars by a + few pixels due to the pen cap being not perfectly flat. + + The median indicator line inside the box has its own pen, \ref setMedianPen. + + The outlier data points are drawn as normal scatter points. Their look can be controlled with + \ref setOutlierStyle + + \section qcpstatisticalbox-usage Usage + + Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable + (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies + (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.) + + Usually, you first create an instance: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-1 + which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot instance takes + ownership of the plottable, so do not delete it manually but use QCustomPlot::removePlottable() instead. + The newly created plottable can be modified, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2 +*/ + +/* start documentation of inline functions */ + +/*! \fn QSharedPointer QCPStatisticalBox::data() const + + Returns a shared pointer to the internal data storage of type \ref + QCPStatisticalBoxDataContainer. You may use it to directly manipulate the data, which may be more + convenient and faster than using the regular \ref setData or \ref addData methods. +*/ + +/* end documentation of inline functions */ + +/*! + Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its + value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and + not have the same orientation. If either of these restrictions is violated, a corresponding + message is printed to the debug output (qDebug), the construction is not aborted, though. + + The created QCPStatisticalBox is automatically registered with the QCustomPlot instance inferred + from \a keyAxis. This QCustomPlot instance takes ownership of the QCPStatisticalBox, so do not + delete it manually but use QCustomPlot::removePlottable() instead. +*/ +QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable1D(keyAxis, valueAxis), + mWidth(0.5), + mWhiskerWidth(0.2), + mWhiskerPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap), + mWhiskerBarPen(Qt::black), + mWhiskerAntialiased(false), + mMedianPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap), + mOutlierStyle(QCPScatterStyle::ssCircle, Qt::blue, 6) +{ + setPen(QPen(Qt::black)); + setBrush(Qt::NoBrush); +} + +/*! \overload + + Replaces the current data container with the provided \a data container. + + Since a QSharedPointer is used, multiple QCPStatisticalBoxes may share the same data container + safely. Modifying the data in the container will then affect all statistical boxes that share the + container. Sharing can be achieved by simply exchanging the data containers wrapped in shared + pointers: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-datasharing-1 + + If you do not wish to share containers, but create a copy from an existing container, rather use + the \ref QCPDataContainer::set method on the statistical box data container directly: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-datasharing-2 + + \see addData +*/ +void QCPStatisticalBox::setData(QSharedPointer data) +{ + mDataContainer = data; +} +/*! \overload + + Replaces the current data with the provided points in \a keys, \a minimum, \a lowerQuartile, \a + median, \a upperQuartile and \a maximum. The provided vectors should have equal length. Else, the + number of added points will be the size of the smallest vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + \see addData +*/ +void QCPStatisticalBox::setData(const QVector &keys, const QVector &minimum, const QVector &lowerQuartile, const QVector &median, const QVector &upperQuartile, const QVector &maximum, bool alreadySorted) +{ + mDataContainer->clear(); + addData(keys, minimum, lowerQuartile, median, upperQuartile, maximum, alreadySorted); +} + +/*! + Sets the width of the boxes in key coordinates. + + \see setWhiskerWidth +*/ +void QCPStatisticalBox::setWidth(double width) +{ + mWidth = width; +} + +/*! + Sets the width of the whiskers in key coordinates. + + Whiskers are the lines which reach from the upper quartile to the maximum, and from the lower + quartile to the minimum. + + \see setWidth +*/ +void QCPStatisticalBox::setWhiskerWidth(double width) +{ + mWhiskerWidth = width; +} + +/*! + Sets the pen used for drawing the whisker backbone. + + Whiskers are the lines which reach from the upper quartile to the maximum, and from the lower + quartile to the minimum. + + Make sure to set the \c capStyle of the passed \a pen to \c Qt::FlatCap. Otherwise the backbone + line might exceed the whisker bars by a few pixels due to the pen cap being not perfectly flat. + + \see setWhiskerBarPen +*/ +void QCPStatisticalBox::setWhiskerPen(const QPen &pen) +{ + mWhiskerPen = pen; +} + +/*! + Sets the pen used for drawing the whisker bars. Those are the lines parallel to the key axis at + each end of the whisker backbone. + + Whiskers are the lines which reach from the upper quartile to the maximum, and from the lower + quartile to the minimum. + + \see setWhiskerPen +*/ +void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen) +{ + mWhiskerBarPen = pen; +} + +/*! + Sets whether the statistical boxes whiskers are drawn with antialiasing or not. + + Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPStatisticalBox::setWhiskerAntialiased(bool enabled) +{ + mWhiskerAntialiased = enabled; +} + +/*! + Sets the pen used for drawing the median indicator line inside the statistical boxes. +*/ +void QCPStatisticalBox::setMedianPen(const QPen &pen) +{ + mMedianPen = pen; +} + +/*! + Sets the appearance of the outlier data points. + + Outliers can be specified with the method + \ref addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector &outliers) +*/ +void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style) +{ + mOutlierStyle = style; +} + +/*! \overload + + Adds the provided points in \a keys, \a minimum, \a lowerQuartile, \a median, \a upperQuartile and + \a maximum to the current data. The provided vectors should have equal length. Else, the number + of added points will be the size of the smallest vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPStatisticalBox::addData(const QVector &keys, const QVector &minimum, const QVector &lowerQuartile, const QVector &median, const QVector &upperQuartile, const QVector &maximum, bool alreadySorted) +{ + if (keys.size() != minimum.size() || minimum.size() != lowerQuartile.size() || lowerQuartile.size() != median.size() || + median.size() != upperQuartile.size() || upperQuartile.size() != maximum.size() || maximum.size() != keys.size()) + qDebug() << Q_FUNC_INFO << "keys, minimum, lowerQuartile, median, upperQuartile, maximum have different sizes:" + << keys.size() << minimum.size() << lowerQuartile.size() << median.size() << upperQuartile.size() << maximum.size(); + const int n = static_cast(qMin(keys.size(), qMin(minimum.size(), qMin(lowerQuartile.size(), qMin(median.size(), qMin(upperQuartile.size(), maximum.size())))))); + QVector tempData(n); + QVector::iterator it = tempData.begin(); + const QVector::iterator itEnd = tempData.end(); + int i = 0; + while (it != itEnd) + { + it->key = keys[i]; + it->minimum = minimum[i]; + it->lowerQuartile = lowerQuartile[i]; + it->median = median[i]; + it->upperQuartile = upperQuartile[i]; + it->maximum = maximum[i]; + ++it; + ++i; + } + mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write +} + +/*! \overload + + Adds the provided data point as \a key, \a minimum, \a lowerQuartile, \a median, \a upperQuartile + and \a maximum to the current data. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. +*/ +void QCPStatisticalBox::addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector &outliers) +{ + mDataContainer->add(QCPStatisticalBoxData(key, minimum, lowerQuartile, median, upperQuartile, maximum, outliers)); +} + +/*! + \copydoc QCPPlottableInterface1D::selectTestRect +*/ +QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool onlySelectable) const +{ + QCPDataSelection result; + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return result; + if (!mKeyAxis || !mValueAxis) + return result; + + QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd; + getVisibleDataBounds(visibleBegin, visibleEnd); + + for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) + { + if (rect.intersects(getQuartileBox(it))) + result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false); + } + result.simplify(); + return result; +} + +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ +double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) + { + // get visible data range: + QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd; + QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); + getVisibleDataBounds(visibleBegin, visibleEnd); + double minDistSqr = (std::numeric_limits::max)(); + for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) + { + if (getQuartileBox(it).contains(pos)) // quartile box + { + double currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99; + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestDataPoint = it; + } + } else // whiskers + { + const QVector whiskerBackbones = getWhiskerBackboneLines(it); + const QCPVector2D posVec(pos); + foreach (const QLineF &backbone, whiskerBackbones) + { + double currentDistSqr = posVec.distanceSquaredToLine(backbone); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestDataPoint = it; + } + } + } + } + if (details) + { + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); + details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); + } + return qSqrt(minDistSqr); + } + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain); + // determine exact range by including width of bars/flags: + if (foundRange) + { + if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0) + range.lower -= mWidth*0.5; + if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0) + range.upper += mWidth*0.5; + } + return range; +} + +/* inherits documentation from base class */ +QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange); +} + +/* inherits documentation from base class */ +void QCPStatisticalBox::draw(QCPPainter *painter) +{ + if (mDataContainer->isEmpty()) return; + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd; + getVisibleDataBounds(visibleBegin, visibleEnd); + + // loop over and draw segments of unselected/selected data: + QList selectedSegments, unselectedSegments, allSegments; + getDataSegments(selectedSegments, unselectedSegments); + allSegments << unselectedSegments << selectedSegments; + for (int i=0; i= unselectedSegments.size(); + QCPStatisticalBoxDataContainer::const_iterator begin = visibleBegin; + QCPStatisticalBoxDataContainer::const_iterator end = visibleEnd; + mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i)); + if (begin == end) + continue; + + for (QCPStatisticalBoxDataContainer::const_iterator it=begin; it!=end; ++it) + { + // check data validity if flag set: +# ifdef QCUSTOMPLOT_CHECK_DATA + if (QCP::isInvalidData(it->key, it->minimum) || + QCP::isInvalidData(it->lowerQuartile, it->median) || + QCP::isInvalidData(it->upperQuartile, it->maximum)) + qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range has invalid data." << "Plottable name:" << name(); + for (int i=0; ioutliers.size(); ++i) + if (QCP::isInvalidData(it->outliers.at(i))) + qDebug() << Q_FUNC_INFO << "Data point outlier at" << it->key << "of drawn range invalid." << "Plottable name:" << name(); +# endif + + if (isSelectedSegment && mSelectionDecorator) + { + mSelectionDecorator->applyPen(painter); + mSelectionDecorator->applyBrush(painter); + } else + { + painter->setPen(mPen); + painter->setBrush(mBrush); + } + QCPScatterStyle finalOutlierStyle = mOutlierStyle; + if (isSelectedSegment && mSelectionDecorator) + finalOutlierStyle = mSelectionDecorator->getFinalScatterStyle(mOutlierStyle); + drawStatisticalBox(painter, it, finalOutlierStyle); + } + } + + // draw other selection decoration that isn't just line/scatter pens and brushes: + if (mSelectionDecorator) + mSelectionDecorator->drawDecoration(painter, selection()); +} + +/* inherits documentation from base class */ +void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + // draw filled rect: + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + painter->setBrush(mBrush); + QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67); + r.moveCenter(rect.center()); + painter->drawRect(r); +} + +/*! + Draws the graphical representation of a single statistical box with the data given by the + iterator \a it with the provided \a painter. + + If the statistical box has a set of outlier data points, they are drawn with \a outlierStyle. + + \see getQuartileBox, getWhiskerBackboneLines, getWhiskerBarLines +*/ +void QCPStatisticalBox::drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) const +{ + // draw quartile box: + applyDefaultAntialiasingHint(painter); + const QRectF quartileBox = getQuartileBox(it); + painter->drawRect(quartileBox); + // draw median line with cliprect set to quartile box: + painter->save(); + painter->setClipRect(quartileBox, Qt::IntersectClip); + painter->setPen(mMedianPen); + painter->drawLine(QLineF(coordsToPixels(it->key-mWidth*0.5, it->median), coordsToPixels(it->key+mWidth*0.5, it->median))); + painter->restore(); + // draw whisker lines: + applyAntialiasingHint(painter, mWhiskerAntialiased, QCP::aePlottables); + painter->setPen(mWhiskerPen); + painter->drawLines(getWhiskerBackboneLines(it)); + painter->setPen(mWhiskerBarPen); + painter->drawLines(getWhiskerBarLines(it)); + // draw outliers: + applyScattersAntialiasingHint(painter); + outlierStyle.applyTo(painter, mPen); + for (int i=0; ioutliers.size(); ++i) + outlierStyle.drawShape(painter, coordsToPixels(it->key, it->outliers.at(i))); +} + +/*! \internal + + called by \ref draw to determine which data (key) range is visible at the current key axis range + setting, so only that needs to be processed. It also takes into account the bar width. + + \a begin returns an iterator to the lowest data point that needs to be taken into account when + plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a + lower may still be just outside the visible range. + + \a end returns an iterator one higher than the highest visible data point. Same as before, \a end + may also lie just outside of the visible range. + + if the plottable contains no data, both \a begin and \a end point to constEnd. +*/ +void QCPStatisticalBox::getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const +{ + if (!mKeyAxis) + { + qDebug() << Q_FUNC_INFO << "invalid key axis"; + begin = mDataContainer->constEnd(); + end = mDataContainer->constEnd(); + return; + } + begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of box to include partially visible data points + end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of box to include partially visible data points +} + +/*! \internal + + Returns the box in plot coordinates (keys in x, values in y of the returned rect) that covers the + value range from the lower to the upper quartile, of the data given by \a it. + + \see drawStatisticalBox, getWhiskerBackboneLines, getWhiskerBarLines +*/ +QRectF QCPStatisticalBox::getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const +{ + QRectF result; + result.setTopLeft(coordsToPixels(it->key-mWidth*0.5, it->upperQuartile)); + result.setBottomRight(coordsToPixels(it->key+mWidth*0.5, it->lowerQuartile)); + return result; +} + +/*! \internal + + Returns the whisker backbones (keys in x, values in y of the returned lines) that cover the value + range from the minimum to the lower quartile, and from the upper quartile to the maximum of the + data given by \a it. + + \see drawStatisticalBox, getQuartileBox, getWhiskerBarLines +*/ +QVector QCPStatisticalBox::getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const +{ + QVector result(2); + result[0].setPoints(coordsToPixels(it->key, it->lowerQuartile), coordsToPixels(it->key, it->minimum)); // min backbone + result[1].setPoints(coordsToPixels(it->key, it->upperQuartile), coordsToPixels(it->key, it->maximum)); // max backbone + return result; +} + +/*! \internal + + Returns the whisker bars (keys in x, values in y of the returned lines) that are placed at the + end of the whisker backbones, at the minimum and maximum of the data given by \a it. + + \see drawStatisticalBox, getQuartileBox, getWhiskerBackboneLines +*/ +QVector QCPStatisticalBox::getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const +{ + QVector result(2); + result[0].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->minimum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->minimum)); // min bar + result[1].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->maximum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->maximum)); // max bar + return result; +} +/* end of 'src/plottables/plottable-statisticalbox.cpp' */ + + +/* including file 'src/plottables/plottable-colormap.cpp' */ +/* modified 2022-11-06T12:45:56, size 48189 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorMapData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorMapData + \brief Holds the two-dimensional data of a QCPColorMap plottable. + + This class is a data storage for \ref QCPColorMap. It holds a two-dimensional array, which \ref + QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a + color, depending on the value. + + The size of the array can be controlled via \ref setSize (or \ref setKeySize, \ref setValueSize). + Which plot coordinates these cells correspond to can be configured with \ref setRange (or \ref + setKeyRange, \ref setValueRange). + + The data cells can be accessed in two ways: They can be directly addressed by an integer index + with \ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot + coordinate with \ref setData. plot coordinate to cell index transformations and vice versa are + provided by the functions \ref coordToCell and \ref cellToCoord. + + A \ref QCPColorMapData also holds an on-demand two-dimensional array of alpha values which (if + allocated) has the same size as the data map. It can be accessed via \ref setAlpha, \ref + fillAlpha and \ref clearAlpha. The memory for the alpha map is only allocated if needed, i.e. on + the first call of \ref setAlpha. \ref clearAlpha restores full opacity and frees the alpha map. + + This class also buffers the minimum and maximum values that are in the data set, to provide + QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value + that is greater than the current maximum increases this maximum to the new value. However, + setting the cell that currently holds the maximum value to a smaller value doesn't decrease the + maximum again, because finding the true new maximum would require going through the entire data + array, which might be time consuming. The same holds for the data minimum. This functionality is + given by \ref recalculateDataBounds, such that you can decide when it is sensible to find the + true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience + parameter \a recalculateDataBounds which may be set to true to automatically call \ref + recalculateDataBounds internally. +*/ + +/* start of documentation of inline functions */ + +/*! \fn bool QCPColorMapData::isEmpty() const + + Returns whether this instance carries no data. This is equivalent to having a size where at least + one of the dimensions is 0 (see \ref setSize). +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a new QCPColorMapData instance. The instance has \a keySize cells in the key direction + and \a valueSize cells in the value direction. These cells will be displayed by the \ref QCPColorMap + at the coordinates \a keyRange and \a valueRange. + + \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange +*/ +QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) : + mKeySize(0), + mValueSize(0), + mKeyRange(keyRange), + mValueRange(valueRange), + mIsEmpty(true), + mData(nullptr), + mAlpha(nullptr), + mDataModified(true) +{ + setSize(keySize, valueSize); + fill(0); +} + +QCPColorMapData::~QCPColorMapData() +{ + delete[] mData; + delete[] mAlpha; +} + +/*! + Constructs a new QCPColorMapData instance copying the data and range of \a other. +*/ +QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) : + mKeySize(0), + mValueSize(0), + mIsEmpty(true), + mData(nullptr), + mAlpha(nullptr), + mDataModified(true) +{ + *this = other; +} + +/*! + Overwrites this color map data instance with the data stored in \a other. The alpha map state is + transferred, too. +*/ +QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other) +{ + if (&other != this) + { + const int keySize = other.keySize(); + const int valueSize = other.valueSize(); + if (!other.mAlpha && mAlpha) + clearAlpha(); + setSize(keySize, valueSize); + if (other.mAlpha && !mAlpha) + createAlpha(false); + setRange(other.keyRange(), other.valueRange()); + if (!isEmpty()) + { + memcpy(mData, other.mData, sizeof(mData[0])*size_t(keySize*valueSize)); + if (mAlpha && other.mAlpha) + memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*size_t(keySize*valueSize)); + } + mDataBounds = other.mDataBounds; + mDataModified = true; + } + return *this; +} + +/* undocumented getter */ +double QCPColorMapData::data(double key, double value) +{ + int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 ); + int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 ); + if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize) + return mData[valueCell*mKeySize + keyCell]; + else + return 0; +} + +/* undocumented getter */ +double QCPColorMapData::cell(int keyIndex, int valueIndex) +{ + if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize) + return mData[valueIndex*mKeySize + keyIndex]; + else + return 0; +} + +/*! + Returns the alpha map value of the cell with the indices \a keyIndex and \a valueIndex. + + If this color map data doesn't have an alpha map (because \ref setAlpha was never called after + creation or after a call to \ref clearAlpha), returns 255, which corresponds to full opacity. + + \see setAlpha +*/ +unsigned char QCPColorMapData::alpha(int keyIndex, int valueIndex) +{ + if (mAlpha && keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize) + return mAlpha[valueIndex*mKeySize + keyIndex]; + else + return 255; +} + +/*! + Resizes the data array to have \a keySize cells in the key dimension and \a valueSize cells in + the value dimension. + + The current data is discarded and the map cells are set to 0, unless the map had already the + requested size. + + Setting at least one of \a keySize or \a valueSize to zero frees the internal data array and \ref + isEmpty returns true. + + \see setRange, setKeySize, setValueSize +*/ +void QCPColorMapData::setSize(int keySize, int valueSize) +{ + if (keySize != mKeySize || valueSize != mValueSize) + { + mKeySize = keySize; + mValueSize = valueSize; + delete[] mData; + mIsEmpty = mKeySize == 0 || mValueSize == 0; + if (!mIsEmpty) + { +#ifdef __EXCEPTIONS + try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message +#endif + mData = new double[size_t(mKeySize*mValueSize)]; +#ifdef __EXCEPTIONS + } catch (...) { mData = nullptr; } +#endif + if (mData) + fill(0); + else + qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize; + } else + mData = nullptr; + + if (mAlpha) // if we had an alpha map, recreate it with new size + createAlpha(); + + mDataModified = true; + } +} + +/*! + Resizes the data array to have \a keySize cells in the key dimension. + + The current data is discarded and the map cells are set to 0, unless the map had already the + requested size. + + Setting \a keySize to zero frees the internal data array and \ref isEmpty returns true. + + \see setKeyRange, setSize, setValueSize +*/ +void QCPColorMapData::setKeySize(int keySize) +{ + setSize(keySize, mValueSize); +} + +/*! + Resizes the data array to have \a valueSize cells in the value dimension. + + The current data is discarded and the map cells are set to 0, unless the map had already the + requested size. + + Setting \a valueSize to zero frees the internal data array and \ref isEmpty returns true. + + \see setValueRange, setSize, setKeySize +*/ +void QCPColorMapData::setValueSize(int valueSize) +{ + setSize(mKeySize, valueSize); +} + +/*! + Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area + covered by the color map in plot coordinates. + + The outer cells will be centered on the range boundaries given to this function. For example, if + the key size (\ref setKeySize) is 3 and \a keyRange is set to QCPRange(2, 3) there will + be cells centered on the key coordinates 2, 2.5 and 3. + + \see setSize +*/ +void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange) +{ + setKeyRange(keyRange); + setValueRange(valueRange); +} + +/*! + Sets the coordinate range the data shall be distributed over in the key dimension. Together with + the value range, This defines the rectangular area covered by the color map in plot coordinates. + + The outer cells will be centered on the range boundaries given to this function. For example, if + the key size (\ref setKeySize) is 3 and \a keyRange is set to QCPRange(2, 3) there will + be cells centered on the key coordinates 2, 2.5 and 3. + + \see setRange, setValueRange, setSize +*/ +void QCPColorMapData::setKeyRange(const QCPRange &keyRange) +{ + mKeyRange = keyRange; +} + +/*! + Sets the coordinate range the data shall be distributed over in the value dimension. Together with + the key range, This defines the rectangular area covered by the color map in plot coordinates. + + The outer cells will be centered on the range boundaries given to this function. For example, if + the value size (\ref setValueSize) is 3 and \a valueRange is set to QCPRange(2, 3) there + will be cells centered on the value coordinates 2, 2.5 and 3. + + \see setRange, setKeyRange, setSize +*/ +void QCPColorMapData::setValueRange(const QCPRange &valueRange) +{ + mValueRange = valueRange; +} + +/*! + Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a + z. + + \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or + value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes, + you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to + determine the cell index. Rather directly access the cell index with \ref + QCPColorMapData::setCell. + + \see setCell, setRange +*/ +void QCPColorMapData::setData(double key, double value, double z) +{ + int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 ); + int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 ); + if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize) + { + mData[valueCell*mKeySize + keyCell] = z; + if (z < mDataBounds.lower) + mDataBounds.lower = z; + if (z > mDataBounds.upper) + mDataBounds.upper = z; + mDataModified = true; + } +} + +/*! + Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z. The indices + enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see + \ref setSize). + + In the standard plot configuration (horizontal key axis and vertical value axis, both not + range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with + indices (keySize-1, valueSize-1) is in the top right corner of the color map. + + \see setData, setSize +*/ +void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z) +{ + if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize) + { + mData[valueIndex*mKeySize + keyIndex] = z; + if (z < mDataBounds.lower) + mDataBounds.lower = z; + if (z > mDataBounds.upper) + mDataBounds.upper = z; + mDataModified = true; + } else + qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex; +} + +/*! + Sets the alpha of the color map cell given by \a keyIndex and \a valueIndex to \a alpha. A value + of 0 for \a alpha results in a fully transparent cell, and a value of 255 results in a fully + opaque cell. + + If an alpha map doesn't exist yet for this color map data, it will be created here. If you wish + to restore full opacity and free any allocated memory of the alpha map, call \ref clearAlpha. + + Note that the cell-wise alpha which can be configured here is independent of any alpha configured + in the color map's gradient (\ref QCPColorGradient). If a cell is affected both by the cell-wise + and gradient alpha, the alpha values will be blended accordingly during rendering of the color + map. + + \see fillAlpha, clearAlpha +*/ +void QCPColorMapData::setAlpha(int keyIndex, int valueIndex, unsigned char alpha) +{ + if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize) + { + if (mAlpha || createAlpha()) + { + mAlpha[valueIndex*mKeySize + keyIndex] = alpha; + mDataModified = true; + } + } else + qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex; +} + +/*! + Goes through the data and updates the buffered minimum and maximum data values. + + Calling this method is only advised if you are about to call \ref QCPColorMap::rescaleDataRange + and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten + with a smaller or larger value respectively, since the buffered maximum/minimum values have been + updated the last time. Why this is the case is explained in the class description (\ref + QCPColorMapData). + + Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter \a + recalculateDataBounds for convenience. Setting this to true will call this method for you, before + doing the rescale. +*/ +void QCPColorMapData::recalculateDataBounds() +{ + if (mKeySize > 0 && mValueSize > 0) + { + double minHeight = std::numeric_limits::max(); + double maxHeight = -std::numeric_limits::max(); + const int dataCount = mValueSize*mKeySize; + for (int i=0; i maxHeight) + maxHeight = mData[i]; + if (mData[i] < minHeight) + minHeight = mData[i]; + } + mDataBounds.lower = minHeight; + mDataBounds.upper = maxHeight; + } +} + +/*! + Frees the internal data memory. + + This is equivalent to calling \ref setSize "setSize(0, 0)". +*/ +void QCPColorMapData::clear() +{ + setSize(0, 0); +} + +/*! + Frees the internal alpha map. The color map will have full opacity again. +*/ +void QCPColorMapData::clearAlpha() +{ + if (mAlpha) + { + delete[] mAlpha; + mAlpha = nullptr; + mDataModified = true; + } +} + +/*! + Sets all cells to the value \a z. +*/ +void QCPColorMapData::fill(double z) +{ + const int dataCount = mValueSize*mKeySize; + memset(mData, z, dataCount*sizeof(*mData)); + mDataBounds = QCPRange(z, z); + mDataModified = true; +} + +/*! + Sets the opacity of all color map cells to \a alpha. A value of 0 for \a alpha results in a fully + transparent color map, and a value of 255 results in a fully opaque color map. + + If you wish to restore opacity to 100% and free any used memory for the alpha map, rather use + \ref clearAlpha. + + \see setAlpha +*/ +void QCPColorMapData::fillAlpha(unsigned char alpha) +{ + if (mAlpha || createAlpha(false)) + { + const int dataCount = mValueSize*mKeySize; + memset(mAlpha, alpha, dataCount*sizeof(*mAlpha)); + mDataModified = true; + } +} + +/*! + Transforms plot coordinates given by \a key and \a value to cell indices of this QCPColorMapData + instance. The resulting cell indices are returned via the output parameters \a keyIndex and \a + valueIndex. + + The retrieved key/value cell indices can then be used for example with \ref setCell. + + If you are only interested in a key or value index, you may pass \c nullptr as \a valueIndex or + \a keyIndex. + + \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or + value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes, + you shouldn't use the \ref QCPColorMapData::coordToCell method as it uses a linear transformation to + determine the cell index. + + \see cellToCoord, QCPAxis::coordToPixel +*/ +void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const +{ + if (keyIndex) + *keyIndex = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 ); + if (valueIndex) + *valueIndex = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 ); +} + +/*! + Transforms cell indices given by \a keyIndex and \a valueIndex to cell indices of this QCPColorMapData + instance. The resulting coordinates are returned via the output parameters \a key and \a + value. + + If you are only interested in a key or value coordinate, you may pass \c nullptr as \a key or \a + value. + + \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or + value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes, + you shouldn't use the \ref QCPColorMapData::cellToCoord method as it uses a linear transformation to + determine the cell index. + + \see coordToCell, QCPAxis::pixelToCoord +*/ +void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const +{ + if (key) + *key = keyIndex/double(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower; + if (value) + *value = valueIndex/double(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower; +} + +/*! \internal + + Allocates the internal alpha map with the current data map key/value size and, if \a + initializeOpaque is true, initializes all values to 255. If \a initializeOpaque is false, the + values are not initialized at all. In this case, the alpha map should be initialized manually, + e.g. with \ref fillAlpha. + + If an alpha map exists already, it is deleted first. If this color map is empty (has either key + or value size zero, see \ref isEmpty), the alpha map is cleared. + + The return value indicates the existence of the alpha map after the call. So this method returns + true if the data map isn't empty and an alpha map was successfully allocated. +*/ +bool QCPColorMapData::createAlpha(bool initializeOpaque) +{ + clearAlpha(); + if (isEmpty()) + return false; + +#ifdef __EXCEPTIONS + try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message +#endif + mAlpha = new unsigned char[size_t(mKeySize*mValueSize)]; +#ifdef __EXCEPTIONS + } catch (...) { mAlpha = nullptr; } +#endif + if (mAlpha) + { + if (initializeOpaque) + fillAlpha(255); + return true; + } else + { + qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize; + return false; + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPColorMap +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPColorMap + \brief A plottable representing a two-dimensional color map in a plot. + + \image html QCPColorMap.png + + The data is stored in the class \ref QCPColorMapData, which can be accessed via the data() + method. + + A color map has three dimensions to represent a data point: The \a key dimension, the \a value + dimension and the \a data dimension. As with other plottables such as graphs, \a key and \a value + correspond to two orthogonal axes on the QCustomPlot surface that you specify in the QCPColorMap + constructor. The \a data dimension however is encoded as the color of the point at (\a key, \a + value). + + Set the number of points (or \a cells) in the key/value dimension via \ref + QCPColorMapData::setSize. The plot coordinate range over which these points will be displayed is + specified via \ref QCPColorMapData::setRange. The first cell will be centered on the lower range + boundary and the last cell will be centered on the upper range boundary. The data can be set by + either accessing the cells directly with QCPColorMapData::setCell or by addressing the cells via + their plot coordinates with \ref QCPColorMapData::setData. If possible, you should prefer + setCell, since it doesn't need to do any coordinate transformation and thus performs a bit + better. + + The cell with index (0, 0) is at the bottom left, if the color map uses normal (i.e. not reversed) + key and value axes. + + To show the user which colors correspond to which \a data values, a \ref QCPColorScale is + typically placed to the right of the axis rect. See the documentation there for details on how to + add and use a color scale. + + \section qcpcolormap-appearance Changing the appearance + + Most important to the appearance is the color gradient, which can be specified via \ref + setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color + gradient. + + The \a data range that is mapped to the colors of the gradient can be specified with \ref + setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref + rescaleDataRange. If your data may contain NaN values, use \ref QCPColorGradient::setNanHandling + to define how they are displayed. + + \section qcpcolormap-transparency Transparency + + Transparency in color maps can be achieved by two mechanisms. On one hand, you can specify alpha + values for color stops of the \ref QCPColorGradient, via the regular QColor interface. This will + cause the color map data which gets mapped to colors around those color stops to appear with the + accordingly interpolated transparency. + + On the other hand you can also directly apply an alpha value to each cell independent of its + data, by using the alpha map feature of \ref QCPColorMapData. The relevant methods are \ref + QCPColorMapData::setAlpha, QCPColorMapData::fillAlpha and \ref QCPColorMapData::clearAlpha(). + + The two transparencies will be joined together in the plot and otherwise not interfere with each + other. They are mixed in a multiplicative matter, so an alpha of e.g. 50% (128/255) in both modes + simultaneously, will result in a total transparency of 25% (64/255). + + \section qcpcolormap-usage Usage + + Like all data representing objects in QCustomPlot, the QCPColorMap is a plottable + (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies + (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.) + + Usually, you first create an instance: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-1 + which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot instance takes + ownership of the plottable, so do not delete it manually but use QCustomPlot::removePlottable() instead. + The newly created plottable can be modified, e.g.: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-2 + + \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or + value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes, + you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to + determine the cell index. Rather directly access the cell index with \ref + QCPColorMapData::setCell. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPColorMapData *QCPColorMap::data() const + + Returns a pointer to the internal data storage of type \ref QCPColorMapData. Access this to + modify data points (cells) and the color map key/value range. + + \see setData +*/ + +/* end documentation of inline functions */ + +/* start documentation of signals */ + +/*! \fn void QCPColorMap::dataRangeChanged(const QCPRange &newRange); + + This signal is emitted when the data range changes. + + \see setDataRange +*/ + +/*! \fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType); + + This signal is emitted when the data scale type changes. + + \see setDataScaleType +*/ + +/*! \fn void QCPColorMap::gradientChanged(const QCPColorGradient &newGradient); + + This signal is emitted when the gradient changes. + + \see setGradient +*/ + +/* end documentation of signals */ + +/*! + Constructs a color map with the specified \a keyAxis and \a valueAxis. + + The created QCPColorMap is automatically registered with the QCustomPlot instance inferred from + \a keyAxis. This QCustomPlot instance takes ownership of the QCPColorMap, so do not delete it + manually but use QCustomPlot::removePlottable() instead. +*/ +QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable(keyAxis, valueAxis), + mDataScaleType(QCPAxis::stLinear), + mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))), + mGradient(QCPColorGradient::gpCold), + mInterpolate(true), + mTightBoundary(false), + mMapImageInvalidated(true) +{ +} + +QCPColorMap::~QCPColorMap() +{ + delete mMapData; +} + +/*! + Replaces the current \ref data with the provided \a data. + + If \a copy is set to true, the \a data object will only be copied. if false, the color map + takes ownership of the passed data and replaces the internal data pointer with it. This is + significantly faster than copying for large datasets. +*/ +void QCPColorMap::setData(QCPColorMapData *data, bool copy) +{ + if (mMapData == data) + { + qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast(data); + return; + } + if (copy) + { + *mMapData = *data; + } else + { + delete mMapData; + mMapData = data; + } + mMapImageInvalidated = true; +} + +/*! + Sets the data range of this color map to \a dataRange. The data range defines which data values + are mapped to the color gradient. + + To make the data range span the full range of the data set, use \ref rescaleDataRange. + + \see QCPColorScale::setDataRange +*/ +void QCPColorMap::setDataRange(const QCPRange &dataRange) +{ + if (!QCPRange::validRange(dataRange)) return; + if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper) + { + if (mDataScaleType == QCPAxis::stLogarithmic) + mDataRange = dataRange.sanitizedForLogScale(); + else + mDataRange = dataRange.sanitizedForLinScale(); + mMapImageInvalidated = true; + emit dataRangeChanged(mDataRange); + } +} + +/*! + Sets whether the data is correlated with the color gradient linearly or logarithmically. + + \see QCPColorScale::setDataScaleType +*/ +void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType) +{ + if (mDataScaleType != scaleType) + { + mDataScaleType = scaleType; + mMapImageInvalidated = true; + emit dataScaleTypeChanged(mDataScaleType); + if (mDataScaleType == QCPAxis::stLogarithmic) + setDataRange(mDataRange.sanitizedForLogScale()); + } +} + +/*! + Sets the color gradient that is used to represent the data. For more details on how to create an + own gradient or use one of the preset gradients, see \ref QCPColorGradient. + + The colors defined by the gradient will be used to represent data values in the currently set + data range, see \ref setDataRange. Data points that are outside this data range will either be + colored uniformly with the respective gradient boundary color, or the gradient will repeat, + depending on \ref QCPColorGradient::setPeriodic. + + \see QCPColorScale::setGradient +*/ +void QCPColorMap::setGradient(const QCPColorGradient &gradient) +{ + if (mGradient != gradient) + { + mGradient = gradient; + mMapImageInvalidated = true; + emit gradientChanged(mGradient); + } +} + +/*! + Sets whether the color map image shall use bicubic interpolation when displaying the color map + shrinked or expanded, and not at a 1:1 pixel-to-data scale. + + \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation and without interpolation enabled" +*/ +void QCPColorMap::setInterpolate(bool enabled) +{ + mInterpolate = enabled; + mMapImageInvalidated = true; // because oversampling factors might need to change +} + +/*! + Sets whether the outer most data rows and columns are clipped to the specified key and value + range (see \ref QCPColorMapData::setKeyRange, \ref QCPColorMapData::setValueRange). + + if \a enabled is set to false, the data points at the border of the color map are drawn with the + same width and height as all other data points. Since the data points are represented by + rectangles of one color centered on the data coordinate, this means that the shown color map + extends by half a data point over the specified key/value range in each direction. + + \image html QCPColorMap-tightboundary.png "A color map, with tight boundary enabled and disabled" +*/ +void QCPColorMap::setTightBoundary(bool enabled) +{ + mTightBoundary = enabled; +} + +/*! + Associates the color scale \a colorScale with this color map. + + This means that both the color scale and the color map synchronize their gradient, data range and + data scale type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple color maps + can be associated with one single color scale. This causes the color maps to also synchronize + those properties, via the mutual color scale. + + This function causes the color map to adopt the current color gradient, data range and data scale + type of \a colorScale. After this call, you may change these properties at either the color map + or the color scale, and the setting will be applied to both. + + Pass \c nullptr as \a colorScale to disconnect the color scale from this color map again. +*/ +void QCPColorMap::setColorScale(QCPColorScale *colorScale) +{ + if (mColorScale) // unconnect signals from old color scale + { + disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange))); + disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType))); + disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient))); + disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); + disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient))); + disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); + } + mColorScale = colorScale; + if (mColorScale) // connect signals to new color scale + { + setGradient(mColorScale.data()->gradient()); + setDataRange(mColorScale.data()->dataRange()); + setDataScaleType(mColorScale.data()->dataScaleType()); + connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange))); + connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType))); + connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient))); + connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); + connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient))); + connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); + } +} + +/*! + Sets the data range (\ref setDataRange) to span the minimum and maximum values that occur in the + current data set. This corresponds to the \ref rescaleKeyAxis or \ref rescaleValueAxis methods, + only for the third data dimension of the color map. + + The minimum and maximum values of the data set are buffered in the internal QCPColorMapData + instance (\ref data). As data is updated via its \ref QCPColorMapData::setCell or \ref + QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For + performance reasons, however, they are only updated in an expanding fashion. So the buffered + maximum can only increase and the buffered minimum can only decrease. In consequence, changes to + the data that actually lower the maximum of the data set (by overwriting the cell holding the + current maximum with a smaller value), aren't recognized and the buffered maximum overestimates + the true maximum of the data set. The same happens for the buffered minimum. To recalculate the + true minimum and maximum by explicitly looking at each cell, the method + QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \a + recalculateDataBounds calls this method before setting the data range to the buffered minimum and + maximum. + + \see setDataRange +*/ +void QCPColorMap::rescaleDataRange(bool recalculateDataBounds) +{ + if (recalculateDataBounds) + mMapData->recalculateDataBounds(); + setDataRange(mMapData->dataBounds()); +} + +/*! + Takes the current appearance of the color map and updates the legend icon, which is used to + represent this color map in the legend (see \ref QCPLegend). + + The \a transformMode specifies whether the rescaling is done by a faster, low quality image + scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm + (Qt::SmoothTransformation). + + The current color map appearance is scaled down to \a thumbSize. Ideally, this should be equal to + the size of the legend icon (see \ref QCPLegend::setIconSize). If it isn't exactly the configured + legend icon size, the thumb will be rescaled during drawing of the legend item. + + \see setDataRange +*/ +void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize) +{ + if (mMapImage.isNull() && !data()->isEmpty()) + updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet) + + if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again + { + bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed(); + bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed(); + mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode); + } +} + +/* inherits documentation from base class */ +double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if ((onlySelectable && mSelectable == QCP::stNone) || mMapData->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) + { + double posKey = 0.0, posValue = 0.0; + pixelsToCoords(pos, posKey, posValue); + if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue)) + { + if (details) + details->setValue(QCPDataSelection(QCPDataRange(0, 1))); // temporary solution, to facilitate whole-plottable selection. Replace in future version with segmented 2D selection. + return mParentPlot->selectionTolerance()*0.99; + } + } + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPColorMap::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + foundRange = true; + QCPRange result = mMapData->keyRange(); + result.normalize(); + if (inSignDomain == QCP::sdPositive) + { + if (result.lower <= 0 && result.upper > 0) + result.lower = result.upper*1e-3; + else if (result.lower <= 0 && result.upper <= 0) + foundRange = false; + } else if (inSignDomain == QCP::sdNegative) + { + if (result.upper >= 0 && result.lower < 0) + result.upper = result.lower*1e-3; + else if (result.upper >= 0 && result.lower >= 0) + foundRange = false; + } + return result; +} + +/* inherits documentation from base class */ +QCPRange QCPColorMap::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + if (inKeyRange != QCPRange()) + { + if (mMapData->keyRange().upper < inKeyRange.lower || mMapData->keyRange().lower > inKeyRange.upper) + { + foundRange = false; + return {}; + } + } + + foundRange = true; + QCPRange result = mMapData->valueRange(); + result.normalize(); + if (inSignDomain == QCP::sdPositive) + { + if (result.lower <= 0 && result.upper > 0) + result.lower = result.upper*1e-3; + else if (result.lower <= 0 && result.upper <= 0) + foundRange = false; + } else if (inSignDomain == QCP::sdNegative) + { + if (result.upper >= 0 && result.lower < 0) + result.upper = result.lower*1e-3; + else if (result.upper >= 0 && result.lower >= 0) + foundRange = false; + } + return result; +} + +/*! \internal + + Updates the internal map image buffer by going through the internal \ref QCPColorMapData and + turning the data values into color pixels with \ref QCPColorGradient::colorize. + + This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image + has been invalidated for a different reason (e.g. a change of the data range with \ref + setDataRange). + + If the map cell count is low, the image created will be oversampled in order to avoid a + QPainter::drawImage bug which makes inner pixel boundaries jitter when stretch-drawing images + without smooth transform enabled. Accordingly, oversampling isn't performed if \ref + setInterpolate is true. +*/ +void QCPColorMap::updateMapImage() +{ + QCPAxis *keyAxis = mKeyAxis.data(); + if (!keyAxis) return; + if (mMapData->isEmpty()) return; + + const QImage::Format format = QImage::Format_ARGB32_Premultiplied; + const int keySize = mMapData->keySize(); + const int valueSize = mMapData->valueSize(); + int keyOversamplingFactor = mInterpolate ? 1 : int(1.0+100.0/double(keySize)); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on + int valueOversamplingFactor = mInterpolate ? 1 : int(1.0+100.0/double(valueSize)); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on + + // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation: + if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor)) + mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), format); + else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor)) + mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), format); + + if (mMapImage.isNull()) + { + qDebug() << Q_FUNC_INFO << "Couldn't create map image (possibly too large for memory)"; + mMapImage = QImage(QSize(10, 10), format); + mMapImage.fill(Qt::black); + } else + { + QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage + if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) + { + // resize undersampled map image to actual key/value cell sizes: + if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize)) + mUndersampledMapImage = QImage(QSize(keySize, valueSize), format); + else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize)) + mUndersampledMapImage = QImage(QSize(valueSize, keySize), format); + localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image + } else if (!mUndersampledMapImage.isNull()) + mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it + + const double *rawData = mMapData->mData; + const unsigned char *rawAlpha = mMapData->mAlpha; + if (keyAxis->orientation() == Qt::Horizontal) + { + const int lineCount = valueSize; + const int rowCount = keySize; + for (int line=0; line(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) + if (rawAlpha) + mGradient.colorize(rawData+line*rowCount, rawAlpha+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic); + else + mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic); + } + } else // keyAxis->orientation() == Qt::Vertical + { + const int lineCount = keySize; + const int rowCount = valueSize; + for (int line=0; line(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) + if (rawAlpha) + mGradient.colorize(rawData+line, rawAlpha+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic); + else + mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic); + } + } + + if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) + { + if (keyAxis->orientation() == Qt::Horizontal) + mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + else + mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + } + } + mMapData->mDataModified = false; + mMapImageInvalidated = false; +} + +/* inherits documentation from base class */ +void QCPColorMap::draw(QCPPainter *painter) +{ + if (mMapData->isEmpty()) return; + if (!mKeyAxis || !mValueAxis) return; + applyDefaultAntialiasingHint(painter); + + if (mMapData->mDataModified || mMapImageInvalidated) + updateMapImage(); + + // use buffer if painting vectorized (PDF): + const bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized); + QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized + QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in + QPixmap mapBuffer; + if (useBuffer) + { + const double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps + mapBufferTarget = painter->clipRegion().boundingRect(); + mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize()); + mapBuffer.fill(Qt::transparent); + localPainter = new QCPPainter(&mapBuffer); + localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio); + localPainter->translate(-mapBufferTarget.topLeft()); + } + + QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower), + coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(); + // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary): + double halfCellWidth = 0; // in pixels + double halfCellHeight = 0; // in pixels + if (keyAxis()->orientation() == Qt::Horizontal) + { + if (mMapData->keySize() > 1) + halfCellWidth = 0.5*imageRect.width()/double(mMapData->keySize()-1); + if (mMapData->valueSize() > 1) + halfCellHeight = 0.5*imageRect.height()/double(mMapData->valueSize()-1); + } else // keyAxis orientation is Qt::Vertical + { + if (mMapData->keySize() > 1) + halfCellHeight = 0.5*imageRect.height()/double(mMapData->keySize()-1); + if (mMapData->valueSize() > 1) + halfCellWidth = 0.5*imageRect.width()/double(mMapData->valueSize()-1); + } + imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight); + const bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed(); + const bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed(); + const bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform); + localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate); + QRegion clipBackup; + if (mTightBoundary) + { + clipBackup = localPainter->clipRegion(); + QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower), + coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(); + localPainter->setClipRect(tightClipRect, Qt::IntersectClip); + } + localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY)); + if (mTightBoundary) + localPainter->setClipRegion(clipBackup); + localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup); + + if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter + { + delete localPainter; + painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer); + } +} + +/* inherits documentation from base class */ +void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + applyDefaultAntialiasingHint(painter); + // draw map thumbnail: + if (!mLegendIcon.isNull()) + { + QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation); + QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height()); + iconRect.moveCenter(rect.center()); + painter->drawPixmap(iconRect.topLeft(), scaledIcon); + } + /* + // draw frame: + painter->setBrush(Qt::NoBrush); + painter->setPen(Qt::black); + painter->drawRect(rect.adjusted(1, 1, 0, 0)); + */ +} +/* end of 'src/plottables/plottable-colormap.cpp' */ + + +/* including file 'src/plottables/plottable-financial.cpp' */ +/* modified 2022-11-06T12:45:57, size 42914 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPFinancialData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPFinancialData + \brief Holds the data of one single data point for QCPFinancial. + + The stored data is: + \li \a key: coordinate on the key axis of this data point (this is the \a mainKey and the \a sortKey) + \li \a open: The opening value at the data point (this is the \a mainValue) + \li \a high: The high/maximum value at the data point + \li \a low: The low/minimum value at the data point + \li \a close: The closing value at the data point + + The container for storing multiple data points is \ref QCPFinancialDataContainer. It is a typedef + for \ref QCPDataContainer with \ref QCPFinancialData as the DataType template parameter. See the + documentation there for an explanation regarding the data type's generic methods. + + \see QCPFinancialDataContainer +*/ + +/* start documentation of inline functions */ + +/*! \fn double QCPFinancialData::sortKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static QCPFinancialData QCPFinancialData::fromSortKey(double sortKey) + + Returns a data point with the specified \a sortKey. All other members are set to zero. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn static static bool QCPFinancialData::sortKeyIsMainKey() + + Since the member \a key is both the data point key coordinate and the data ordering parameter, + this method returns true. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPFinancialData::mainKey() const + + Returns the \a key member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn double QCPFinancialData::mainValue() const + + Returns the \a open member of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/*! \fn QCPRange QCPFinancialData::valueRange() const + + Returns a QCPRange spanning from the \a low to the \a high value of this data point. + + For a general explanation of what this method is good for in the context of the data container, + see the documentation of \ref QCPDataContainer. +*/ + +/* end documentation of inline functions */ + +/*! + Constructs a data point with key and all values set to zero. +*/ +QCPFinancialData::QCPFinancialData() : + key(0), + open(0), + high(0), + low(0), + close(0) +{ +} + +/*! + Constructs a data point with the specified \a key and OHLC values. +*/ +QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) : + key(key), + open(open), + high(high), + low(low), + close(close) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPFinancial +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPFinancial + \brief A plottable representing a financial stock chart + + \image html QCPFinancial.png + + This plottable represents time series data binned to certain intervals, mainly used for stock + charts. The two common representations OHLC (Open-High-Low-Close) bars and Candlesticks can be + set via \ref setChartStyle. + + The data is passed via \ref setData as a set of open/high/low/close values at certain keys + (typically times). This means the data must be already binned appropriately. If data is only + available as a series of values (e.g. \a price against \a time), you can use the static + convenience function \ref timeSeriesToOhlc to generate binned OHLC-data which can then be passed + to \ref setData. + + The width of the OHLC bars/candlesticks can be controlled with \ref setWidth and \ref + setWidthType. A typical choice is to set the width type to \ref wtPlotCoords (the default) and + the width to (or slightly less than) one time bin interval width. + + \section qcpfinancial-appearance Changing the appearance + + Charts can be either single- or two-colored (\ref setTwoColored). If set to be single-colored, + lines are drawn with the plottable's pen (\ref setPen) and fills with the brush (\ref setBrush). + + If set to two-colored, positive changes of the value during an interval (\a close >= \a open) are + represented with a different pen and brush than negative changes (\a close < \a open). These can + be configured with \ref setPenPositive, \ref setPenNegative, \ref setBrushPositive, and \ref + setBrushNegative. In two-colored mode, the normal plottable pen/brush is ignored. Upon selection + however, the normal selected pen/brush (provided by the \ref selectionDecorator) is used, + irrespective of whether the chart is single- or two-colored. + + \section qcpfinancial-usage Usage + + Like all data representing objects in QCustomPlot, the QCPFinancial is a plottable + (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies + (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.) + + Usually, you first create an instance: + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpfinancial-creation-1 + which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot + instance takes ownership of the plottable, so do not delete it manually but use + QCustomPlot::removePlottable() instead. The newly created plottable can be modified, e.g.: + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpfinancial-creation-2 + Here we have used the static helper method \ref timeSeriesToOhlc, to turn a time-price data + series into a 24-hour binned open-high-low-close data series as QCPFinancial uses. +*/ + +/* start of documentation of inline functions */ + +/*! \fn QCPFinancialDataContainer *QCPFinancial::data() const + + Returns a pointer to the internal data storage of type \ref QCPFinancialDataContainer. You may + use it to directly manipulate the data, which may be more convenient and faster than using the + regular \ref setData or \ref addData methods, in certain situations. +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs a financial chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + The created QCPFinancial is automatically registered with the QCustomPlot instance inferred from \a + keyAxis. This QCustomPlot instance takes ownership of the QCPFinancial, so do not delete it manually + but use QCustomPlot::removePlottable() instead. +*/ +QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable1D(keyAxis, valueAxis), + mChartStyle(csCandlestick), + mWidth(0.5), + mWidthType(wtPlotCoords), + mTwoColored(true), + mBrushPositive(QBrush(QColor(50, 160, 0))), + mBrushNegative(QBrush(QColor(180, 0, 15))), + mPenPositive(QPen(QColor(40, 150, 0))), + mPenNegative(QPen(QColor(170, 5, 5))) +{ + mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255))); +} + +QCPFinancial::~QCPFinancial() +{ +} + +/*! \overload + + Replaces the current data container with the provided \a data container. + + Since a QSharedPointer is used, multiple QCPFinancials may share the same data container safely. + Modifying the data in the container will then affect all financials that share the container. + Sharing can be achieved by simply exchanging the data containers wrapped in shared pointers: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpfinancial-datasharing-1 + + If you do not wish to share containers, but create a copy from an existing container, rather use + the \ref QCPDataContainer::set method on the financial's data container directly: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpfinancial-datasharing-2 + + \see addData, timeSeriesToOhlc +*/ +void QCPFinancial::setData(QSharedPointer data) +{ + mDataContainer = data; +} + +/*! \overload + + Replaces the current data with the provided points in \a keys, \a open, \a high, \a low and \a + close. The provided vectors should have equal length. Else, the number of added points will be + the size of the smallest vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + \see addData, timeSeriesToOhlc +*/ +void QCPFinancial::setData(const QVector &keys, const QVector &open, const QVector &high, const QVector &low, const QVector &close, bool alreadySorted) +{ + mDataContainer->clear(); + addData(keys, open, high, low, close, alreadySorted); +} + +/*! + Sets which representation style shall be used to display the OHLC data. +*/ +void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style) +{ + mChartStyle = style; +} + +/*! + Sets the width of the individual bars/candlesticks to \a width in plot key coordinates. + + A typical choice is to set it to (or slightly less than) one bin interval width. +*/ +void QCPFinancial::setWidth(double width) +{ + mWidth = width; +} + +/*! + Sets how the width of the financial bars is defined. See the documentation of \ref WidthType for + an explanation of the possible values for \a widthType. + + The default value is \ref wtPlotCoords. + + \see setWidth +*/ +void QCPFinancial::setWidthType(QCPFinancial::WidthType widthType) +{ + mWidthType = widthType; +} + +/*! + Sets whether this chart shall contrast positive from negative trends per data point by using two + separate colors to draw the respective bars/candlesticks. + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative +*/ +void QCPFinancial::setTwoColored(bool twoColored) +{ + mTwoColored = twoColored; +} + +/*! + If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills + of data points with a positive trend (i.e. bars/candlesticks with close >= open). + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setBrushNegative, setPenPositive, setPenNegative +*/ +void QCPFinancial::setBrushPositive(const QBrush &brush) +{ + mBrushPositive = brush; +} + +/*! + If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills + of data points with a negative trend (i.e. bars/candlesticks with close < open). + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setBrushPositive, setPenNegative, setPenPositive +*/ +void QCPFinancial::setBrushNegative(const QBrush &brush) +{ + mBrushNegative = brush; +} + +/*! + If \ref setTwoColored is set to true, this function controls the pen that is used to draw + outlines of data points with a positive trend (i.e. bars/candlesticks with close >= open). + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setPenNegative, setBrushPositive, setBrushNegative +*/ +void QCPFinancial::setPenPositive(const QPen &pen) +{ + mPenPositive = pen; +} + +/*! + If \ref setTwoColored is set to true, this function controls the pen that is used to draw + outlines of data points with a negative trend (i.e. bars/candlesticks with close < open). + + If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref + setBrush). + + \see setPenPositive, setBrushNegative, setBrushPositive +*/ +void QCPFinancial::setPenNegative(const QPen &pen) +{ + mPenNegative = pen; +} + +/*! \overload + + Adds the provided points in \a keys, \a open, \a high, \a low and \a close to the current data. + The provided vectors should have equal length. Else, the number of added points will be the size + of the smallest vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. + + \see timeSeriesToOhlc +*/ +void QCPFinancial::addData(const QVector &keys, const QVector &open, const QVector &high, const QVector &low, const QVector &close, bool alreadySorted) +{ + if (keys.size() != open.size() || open.size() != high.size() || high.size() != low.size() || low.size() != close.size() || close.size() != keys.size()) + qDebug() << Q_FUNC_INFO << "keys, open, high, low, close have different sizes:" << keys.size() << open.size() << high.size() << low.size() << close.size(); + const int n = static_cast(qMin(keys.size(), qMin(open.size(), qMin(high.size(), qMin(low.size(), close.size()))))); + QVector tempData(n); + QVector::iterator it = tempData.begin(); + const QVector::iterator itEnd = tempData.end(); + int i = 0; + while (it != itEnd) + { + it->key = keys[i]; + it->open = open[i]; + it->high = high[i]; + it->low = low[i]; + it->close = close[i]; + ++it; + ++i; + } + mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write +} + +/*! \overload + + Adds the provided data point as \a key, \a open, \a high, \a low and \a close to the current + data. + + Alternatively, you can also access and modify the data directly via the \ref data method, which + returns a pointer to the internal data container. + + \see timeSeriesToOhlc +*/ +void QCPFinancial::addData(double key, double open, double high, double low, double close) +{ + mDataContainer->add(QCPFinancialData(key, open, high, low, close)); +} + +/*! + \copydoc QCPPlottableInterface1D::selectTestRect +*/ +QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelectable) const +{ + QCPDataSelection result; + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return result; + if (!mKeyAxis || !mValueAxis) + return result; + + QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd; + getVisibleDataBounds(visibleBegin, visibleEnd); + + for (QCPFinancialDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) + { + if (rect.intersects(selectionHitBox(it))) + result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false); + } + result.simplify(); + return result; +} + +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ +double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) + { + // get visible data range: + QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd; + QCPFinancialDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); + getVisibleDataBounds(visibleBegin, visibleEnd); + // perform select test according to configured style: + double result = -1; + switch (mChartStyle) + { + case QCPFinancial::csOhlc: + result = ohlcSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break; + case QCPFinancial::csCandlestick: + result = candlestickSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break; + } + if (details) + { + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); + details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); + } + return result; + } + + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain); + // determine exact range by including width of bars/flags: + if (foundRange) + { + if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0) + range.lower -= mWidth*0.5; + if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0) + range.upper += mWidth*0.5; + } + return range; +} + +/* inherits documentation from base class */ +QCPRange QCPFinancial::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange); +} + +/*! + A convenience function that converts time series data (\a value against \a time) to OHLC binned + data points. The return value can then be passed on to \ref QCPFinancialDataContainer::set(const + QCPFinancialDataContainer&). + + The size of the bins can be controlled with \a timeBinSize in the same units as \a time is given. + For example, if the unit of \a time is seconds and single OHLC/Candlesticks should span an hour + each, set \a timeBinSize to 3600. + + \a timeBinOffset allows to control precisely at what \a time coordinate a bin should start. The + value passed as \a timeBinOffset doesn't need to be in the range encompassed by the \a time keys. + It merely defines the mathematical offset/phase of the bins that will be used to process the + data. +*/ +QCPFinancialDataContainer QCPFinancial::timeSeriesToOhlc(const QVector &time, const QVector &value, double timeBinSize, double timeBinOffset) +{ + QCPFinancialDataContainer data; + int count = static_cast(qMin(time.size(), value.size())); + if (count == 0) + return QCPFinancialDataContainer(); + + QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first()); + int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5); + for (int i=0; i currentBinData.high) currentBinData.high = value.at(i); + if (i == count-1) // last data point is in current bin, finalize bin: + { + currentBinData.close = value.at(i); + currentBinData.key = timeBinOffset+(index)*timeBinSize; + data.add(currentBinData); + } + } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map: + { + // finalize current bin: + currentBinData.close = value.at(i-1); + currentBinData.key = timeBinOffset+(index-1)*timeBinSize; + data.add(currentBinData); + // start next bin: + currentBinIndex = index; + currentBinData.open = value.at(i); + currentBinData.high = value.at(i); + currentBinData.low = value.at(i); + } + } + + return data; +} + +/* inherits documentation from base class */ +void QCPFinancial::draw(QCPPainter *painter) +{ + // get visible data range: + QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd; + getVisibleDataBounds(visibleBegin, visibleEnd); + + // loop over and draw segments of unselected/selected data: + QList selectedSegments, unselectedSegments, allSegments; + getDataSegments(selectedSegments, unselectedSegments); + allSegments << unselectedSegments << selectedSegments; + for (int i=0; i= unselectedSegments.size(); + QCPFinancialDataContainer::const_iterator begin = visibleBegin; + QCPFinancialDataContainer::const_iterator end = visibleEnd; + mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i)); + if (begin == end) + continue; + + // draw data segment according to configured style: + switch (mChartStyle) + { + case QCPFinancial::csOhlc: + drawOhlcPlot(painter, begin, end, isSelectedSegment); break; + case QCPFinancial::csCandlestick: + drawCandlestickPlot(painter, begin, end, isSelectedSegment); break; + } + } + + // draw other selection decoration that isn't just line/scatter pens and brushes: + if (mSelectionDecorator) + mSelectionDecorator->drawDecoration(painter, selection()); +} + +/* inherits documentation from base class */ +void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing + if (mChartStyle == csOhlc) + { + if (mTwoColored) + { + // draw upper left half icon with positive color: + painter->setBrush(mBrushPositive); + painter->setPen(mPenPositive); + painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint())); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft())); + // draw bottom right half icon with negative color: + painter->setBrush(mBrushNegative); + painter->setPen(mPenNegative); + painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint())); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft())); + } else + { + painter->setBrush(mBrush); + painter->setPen(mPen); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft())); + } + } else if (mChartStyle == csCandlestick) + { + if (mTwoColored) + { + // draw upper left half icon with positive color: + painter->setBrush(mBrushPositive); + painter->setPen(mPenPositive); + painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint())); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft())); + // draw bottom right half icon with negative color: + painter->setBrush(mBrushNegative); + painter->setPen(mPenNegative); + painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint())); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft())); + } else + { + painter->setBrush(mBrush); + painter->setPen(mPen); + painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft())); + painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft())); + painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft())); + } + } +} + +/*! \internal + + Draws the data from \a begin to \a end-1 as OHLC bars with the provided \a painter. + + This method is a helper function for \ref draw. It is used when the chart style is \ref csOhlc. +*/ +void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected) +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + if (keyAxis->orientation() == Qt::Horizontal) + { + for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it) + { + if (isSelected && mSelectionDecorator) + mSelectionDecorator->applyPen(painter); + else if (mTwoColored) + painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative); + else + painter->setPen(mPen); + double keyPixel = keyAxis->coordToPixel(it->key); + double openPixel = valueAxis->coordToPixel(it->open); + double closePixel = valueAxis->coordToPixel(it->close); + // draw backbone: + painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(it->low))); + // draw open: + double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides + painter->drawLine(QPointF(keyPixel-pixelWidth, openPixel), QPointF(keyPixel, openPixel)); + // draw close: + painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+pixelWidth, closePixel)); + } + } else + { + for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it) + { + if (isSelected && mSelectionDecorator) + mSelectionDecorator->applyPen(painter); + else if (mTwoColored) + painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative); + else + painter->setPen(mPen); + double keyPixel = keyAxis->coordToPixel(it->key); + double openPixel = valueAxis->coordToPixel(it->open); + double closePixel = valueAxis->coordToPixel(it->close); + // draw backbone: + painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(it->low), keyPixel)); + // draw open: + double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides + painter->drawLine(QPointF(openPixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel)); + // draw close: + painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+pixelWidth)); + } + } +} + +/*! \internal + + Draws the data from \a begin to \a end-1 as Candlesticks with the provided \a painter. + + This method is a helper function for \ref draw. It is used when the chart style is \ref csCandlestick. +*/ +void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected) +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + if (keyAxis->orientation() == Qt::Horizontal) + { + for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it) + { + if (isSelected && mSelectionDecorator) + { + mSelectionDecorator->applyPen(painter); + mSelectionDecorator->applyBrush(painter); + } else if (mTwoColored) + { + painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative); + painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative); + } else + { + painter->setPen(mPen); + painter->setBrush(mBrush); + } + double keyPixel = keyAxis->coordToPixel(it->key); + double openPixel = valueAxis->coordToPixel(it->open); + double closePixel = valueAxis->coordToPixel(it->close); + // draw high: + painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close)))); + // draw low: + painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close)))); + // draw open-close box: + double pixelWidth = getPixelWidth(it->key, keyPixel); + painter->drawRect(QRectF(QPointF(keyPixel-pixelWidth, closePixel), QPointF(keyPixel+pixelWidth, openPixel))); + } + } else // keyAxis->orientation() == Qt::Vertical + { + for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it) + { + if (isSelected && mSelectionDecorator) + { + mSelectionDecorator->applyPen(painter); + mSelectionDecorator->applyBrush(painter); + } else if (mTwoColored) + { + painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative); + painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative); + } else + { + painter->setPen(mPen); + painter->setBrush(mBrush); + } + double keyPixel = keyAxis->coordToPixel(it->key); + double openPixel = valueAxis->coordToPixel(it->open); + double closePixel = valueAxis->coordToPixel(it->close); + // draw high: + painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel)); + // draw low: + painter->drawLine(QPointF(valueAxis->coordToPixel(it->low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel)); + // draw open-close box: + double pixelWidth = getPixelWidth(it->key, keyPixel); + painter->drawRect(QRectF(QPointF(closePixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel+pixelWidth))); + } + } +} + +/*! \internal + + This function is used to determine the width of the bar at coordinate \a key, according to the + specified width (\ref setWidth) and width type (\ref setWidthType). Provide the pixel position of + \a key in \a keyPixel (because usually this was already calculated via \ref QCPAxis::coordToPixel + when this function is called). + + It returns the number of pixels the bar extends to higher keys, relative to the \a key + coordinate. So with a non-reversed horizontal axis, the return value is positive. With a reversed + horizontal axis, the return value is negative. This is important so the open/close flags on the + \ref csOhlc bar are drawn to the correct side. +*/ +double QCPFinancial::getPixelWidth(double key, double keyPixel) const +{ + double result = 0; + switch (mWidthType) + { + case wtAbsolute: + { + if (mKeyAxis) + result = mWidth*0.5*mKeyAxis.data()->pixelOrientation(); + break; + } + case wtAxisRectRatio: + { + if (mKeyAxis && mKeyAxis.data()->axisRect()) + { + if (mKeyAxis.data()->orientation() == Qt::Horizontal) + result = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation(); + else + result = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation(); + } else + qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined"; + break; + } + case wtPlotCoords: + { + if (mKeyAxis) + result = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel; + else + qDebug() << Q_FUNC_INFO << "No key axis defined"; + break; + } + } + return result; +} + +/*! \internal + + This method is a helper function for \ref selectTest. It is used to test for selection when the + chart style is \ref csOhlc. It only tests against the data points between \a begin and \a end. + + Like \ref selectTest, this method returns the shortest distance of \a pos to the graphical + representation of the plottable, and \a closestDataPoint will point to the respective data point. +*/ +double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const +{ + closestDataPoint = mDataContainer->constEnd(); + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + double minDistSqr = (std::numeric_limits::max)(); + if (keyAxis->orientation() == Qt::Horizontal) + { + for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it) + { + double keyPixel = keyAxis->coordToPixel(it->key); + // calculate distance to backbone: + double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low))); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestDataPoint = it; + } + } + } else // keyAxis->orientation() == Qt::Vertical + { + for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it) + { + double keyPixel = keyAxis->coordToPixel(it->key); + // calculate distance to backbone: + double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel)); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestDataPoint = it; + } + } + } + return qSqrt(minDistSqr); +} + +/*! \internal + + This method is a helper function for \ref selectTest. It is used to test for selection when the + chart style is \ref csCandlestick. It only tests against the data points between \a begin and \a + end. + + Like \ref selectTest, this method returns the shortest distance of \a pos to the graphical + representation of the plottable, and \a closestDataPoint will point to the respective data point. +*/ +double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const +{ + closestDataPoint = mDataContainer->constEnd(); + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } + + double minDistSqr = (std::numeric_limits::max)(); + if (keyAxis->orientation() == Qt::Horizontal) + { + for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it) + { + double currentDistSqr; + // determine whether pos is in open-close-box: + QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5); + QCPRange boxValueRange(it->close, it->open); + double posKey = 0.0, posValue = 0.0; + pixelsToCoords(pos, posKey, posValue); + if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box + { + currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99; + } else + { + // calculate distance to high/low lines: + double keyPixel = keyAxis->coordToPixel(it->key); + double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close)))); + double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close)))); + currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr); + } + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestDataPoint = it; + } + } + } else // keyAxis->orientation() == Qt::Vertical + { + for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it) + { + double currentDistSqr; + // determine whether pos is in open-close-box: + QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5); + QCPRange boxValueRange(it->close, it->open); + double posKey = 0.0, posValue = 0.0; + pixelsToCoords(pos, posKey, posValue); + if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box + { + currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99; + } else + { + // calculate distance to high/low lines: + double keyPixel = keyAxis->coordToPixel(it->key); + double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel)); + double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel)); + currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr); + } + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestDataPoint = it; + } + } + } + return qSqrt(minDistSqr); +} + +/*! \internal + + called by the drawing methods to determine which data (key) range is visible at the current key + axis range setting, so only that needs to be processed. + + \a begin returns an iterator to the lowest data point that needs to be taken into account when + plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a + begin may still be just outside the visible range. + + \a end returns the iterator just above the highest data point that needs to be taken into + account. Same as before, \a end may also lie just outside of the visible range + + if the plottable contains no data, both \a begin and \a end point to \c constEnd. +*/ +void QCPFinancial::getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const +{ + if (!mKeyAxis) + { + qDebug() << Q_FUNC_INFO << "invalid key axis"; + begin = mDataContainer->constEnd(); + end = mDataContainer->constEnd(); + return; + } + begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of ohlc/candlestick to include partially visible data points + end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of ohlc/candlestick to include partially visible data points +} + +/*! \internal + + Returns the hit box in pixel coordinates that will be used for data selection with the selection + rect (\ref selectTestRect), of the data point given by \a it. +*/ +QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator it) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return {}; } + + double keyPixel = keyAxis->coordToPixel(it->key); + double highPixel = valueAxis->coordToPixel(it->high); + double lowPixel = valueAxis->coordToPixel(it->low); + double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it->key-mWidth*0.5); + if (keyAxis->orientation() == Qt::Horizontal) + return QRectF(keyPixel-keyWidthPixels, highPixel, keyWidthPixels*2, lowPixel-highPixel).normalized(); + else + return QRectF(highPixel, keyPixel-keyWidthPixels, lowPixel-highPixel, keyWidthPixels*2).normalized(); +} +/* end of 'src/plottables/plottable-financial.cpp' */ + + +/* including file 'src/plottables/plottable-errorbar.cpp' */ +/* modified 2022-11-06T12:45:56, size 37679 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPErrorBarsData +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPErrorBarsData + \brief Holds the data of one single error bar for QCPErrorBars. + + The stored data is: + \li \a errorMinus: how much the error bar extends towards negative coordinates from the data + point position + \li \a errorPlus: how much the error bar extends towards positive coordinates from the data point + position + + The container for storing the error bar information is \ref QCPErrorBarsDataContainer. It is a + typedef for QVector. + + \see QCPErrorBarsDataContainer +*/ + +/*! + Constructs an error bar with errors set to zero. +*/ +QCPErrorBarsData::QCPErrorBarsData() : + errorMinus(0), + errorPlus(0) +{ +} + +/*! + Constructs an error bar with equal \a error in both negative and positive direction. +*/ +QCPErrorBarsData::QCPErrorBarsData(double error) : + errorMinus(error), + errorPlus(error) +{ +} + +/*! + Constructs an error bar with negative and positive errors set to \a errorMinus and \a errorPlus, + respectively. +*/ +QCPErrorBarsData::QCPErrorBarsData(double errorMinus, double errorPlus) : + errorMinus(errorMinus), + errorPlus(errorPlus) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPErrorBars +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPErrorBars + \brief A plottable that adds a set of error bars to other plottables. + + \image html QCPErrorBars.png + + The \ref QCPErrorBars plottable can be attached to other one-dimensional plottables (e.g. \ref + QCPGraph, \ref QCPCurve, \ref QCPBars, etc.) and equips them with error bars. + + Use \ref setDataPlottable to define for which plottable the \ref QCPErrorBars shall display the + error bars. The orientation of the error bars can be controlled with \ref setErrorType. + + By using \ref setData, you can supply the actual error data, either as symmetric error or + plus/minus asymmetric errors. \ref QCPErrorBars only stores the error data. The absolute + key/value position of each error bar will be adopted from the configured data plottable. The + error data of the \ref QCPErrorBars are associated one-to-one via their index to the data points + of the data plottable. You can directly access and manipulate the error bar data via \ref data. + + Set either of the plus/minus errors to NaN (qQNaN() or + std::numeric_limits::quiet_NaN()) to not show the respective error bar on the data point at + that index. + + \section qcperrorbars-appearance Changing the appearance + + The appearance of the error bars is defined by the pen (\ref setPen), and the width of the + whiskers (\ref setWhiskerWidth). Further, the error bar backbones may leave a gap around the data + point center to prevent that error bars are drawn too close to or even through scatter points. + This gap size can be controlled via \ref setSymbolGap. +*/ + +/* start of documentation of inline functions */ + +/*! \fn QSharedPointer QCPErrorBars::data() const + + Returns a shared pointer to the internal data storage of type \ref QCPErrorBarsDataContainer. You + may use it to directly manipulate the error values, which may be more convenient and faster than + using the regular \ref setData methods. +*/ + +/* end of documentation of inline functions */ + +/*! + Constructs an error bars plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value + axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have + the same orientation. If either of these restrictions is violated, a corresponding message is + printed to the debug output (qDebug), the construction is not aborted, though. + + It is also important that the \a keyAxis and \a valueAxis are the same for the error bars + plottable and the data plottable that the error bars shall be drawn on (\ref setDataPlottable). + + The created \ref QCPErrorBars is automatically registered with the QCustomPlot instance inferred + from \a keyAxis. This QCustomPlot instance takes ownership of the \ref QCPErrorBars, so do not + delete it manually but use \ref QCustomPlot::removePlottable() instead. +*/ +QCPErrorBars::QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable(keyAxis, valueAxis), + mDataContainer(new QVector), + mErrorType(etValueError), + mWhiskerWidth(9), + mSymbolGap(10) +{ + setPen(QPen(Qt::black, 0)); + setBrush(Qt::NoBrush); +} + +QCPErrorBars::~QCPErrorBars() +{ +} + +/*! \overload + + Replaces the current data container with the provided \a data container. + + Since a QSharedPointer is used, multiple \ref QCPErrorBars instances may share the same data + container safely. Modifying the data in the container will then affect all \ref QCPErrorBars + instances that share the container. Sharing can be achieved by simply exchanging the data + containers wrapped in shared pointers: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcperrorbars-datasharing-1 + + If you do not wish to share containers, but create a copy from an existing container, assign the + data containers directly: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcperrorbars-datasharing-2 + (This uses different notation compared with other plottables, because the \ref QCPErrorBars + uses a \c QVector as its data container, instead of a \ref QCPDataContainer.) + + \see addData +*/ +void QCPErrorBars::setData(QSharedPointer data) +{ + mDataContainer = data; +} + +/*! \overload + + Sets symmetrical error values as specified in \a error. The errors will be associated one-to-one + by the data point index to the associated data plottable (\ref setDataPlottable). + + You can directly access and manipulate the error bar data via \ref data. + + \see addData +*/ +void QCPErrorBars::setData(const QVector &error) +{ + mDataContainer->clear(); + addData(error); +} + +/*! \overload + + Sets asymmetrical errors as specified in \a errorMinus and \a errorPlus. The errors will be + associated one-to-one by the data point index to the associated data plottable (\ref + setDataPlottable). + + You can directly access and manipulate the error bar data via \ref data. + + \see addData +*/ +void QCPErrorBars::setData(const QVector &errorMinus, const QVector &errorPlus) +{ + mDataContainer->clear(); + addData(errorMinus, errorPlus); +} + +/*! + Sets the data plottable to which the error bars will be applied. The error values specified e.g. + via \ref setData will be associated one-to-one by the data point index to the data points of \a + plottable. This means that the error bars will adopt the key/value coordinates of the data point + with the same index. + + The passed \a plottable must be a one-dimensional plottable, i.e. it must implement the \ref + QCPPlottableInterface1D. Further, it must not be a \ref QCPErrorBars instance itself. If either + of these restrictions is violated, a corresponding qDebug output is generated, and the data + plottable of this \ref QCPErrorBars instance is set to zero. + + For proper display, care must also be taken that the key and value axes of the \a plottable match + those configured for this \ref QCPErrorBars instance. +*/ +void QCPErrorBars::setDataPlottable(QCPAbstractPlottable *plottable) +{ + if (plottable && qobject_cast(plottable)) + { + mDataPlottable = nullptr; + qDebug() << Q_FUNC_INFO << "can't set another QCPErrorBars instance as data plottable"; + return; + } + if (plottable && !plottable->interface1D()) + { + mDataPlottable = nullptr; + qDebug() << Q_FUNC_INFO << "passed plottable doesn't implement 1d interface, can't associate with QCPErrorBars"; + return; + } + + mDataPlottable = plottable; +} + +/*! + Sets in which orientation the error bars shall appear on the data points. If your data needs both + error dimensions, create two \ref QCPErrorBars with different \a type. +*/ +void QCPErrorBars::setErrorType(ErrorType type) +{ + mErrorType = type; +} + +/*! + Sets the width of the whiskers (the short bars at the end of the actual error bar backbones) to + \a pixels. +*/ +void QCPErrorBars::setWhiskerWidth(double pixels) +{ + mWhiskerWidth = pixels; +} + +/*! + Sets the gap diameter around the data points that will be left out when drawing the error bar + backbones. This gap prevents that error bars are drawn too close to or even through scatter + points. +*/ +void QCPErrorBars::setSymbolGap(double pixels) +{ + mSymbolGap = pixels; +} + +/*! \overload + + Adds symmetrical error values as specified in \a error. The errors will be associated one-to-one + by the data point index to the associated data plottable (\ref setDataPlottable). + + You can directly access and manipulate the error bar data via \ref data. + + \see setData +*/ +void QCPErrorBars::addData(const QVector &error) +{ + addData(error, error); +} + +/*! \overload + + Adds asymmetrical errors as specified in \a errorMinus and \a errorPlus. The errors will be + associated one-to-one by the data point index to the associated data plottable (\ref + setDataPlottable). + + You can directly access and manipulate the error bar data via \ref data. + + \see setData +*/ +void QCPErrorBars::addData(const QVector &errorMinus, const QVector &errorPlus) +{ + if (errorMinus.size() != errorPlus.size()) + qDebug() << Q_FUNC_INFO << "minus and plus error vectors have different sizes:" << errorMinus.size() << errorPlus.size(); + const int n = static_cast(qMin(errorMinus.size(), errorPlus.size())); + mDataContainer->reserve(n); + for (int i=0; iappend(QCPErrorBarsData(errorMinus.at(i), errorPlus.at(i))); +} + +/*! \overload + + Adds a single symmetrical error bar as specified in \a error. The errors will be associated + one-to-one by the data point index to the associated data plottable (\ref setDataPlottable). + + You can directly access and manipulate the error bar data via \ref data. + + \see setData +*/ +void QCPErrorBars::addData(double error) +{ + mDataContainer->append(QCPErrorBarsData(error)); +} + +/*! \overload + + Adds a single asymmetrical error bar as specified in \a errorMinus and \a errorPlus. The errors + will be associated one-to-one by the data point index to the associated data plottable (\ref + setDataPlottable). + + You can directly access and manipulate the error bar data via \ref data. + + \see setData +*/ +void QCPErrorBars::addData(double errorMinus, double errorPlus) +{ + mDataContainer->append(QCPErrorBarsData(errorMinus, errorPlus)); +} + +/* inherits documentation from base class */ +int QCPErrorBars::dataCount() const +{ + return static_cast(mDataContainer->size()); +} + +/* inherits documentation from base class */ +double QCPErrorBars::dataMainKey(int index) const +{ + if (mDataPlottable) + return mDataPlottable->interface1D()->dataMainKey(index); + else + qDebug() << Q_FUNC_INFO << "no data plottable set"; + return 0; +} + +/* inherits documentation from base class */ +double QCPErrorBars::dataSortKey(int index) const +{ + if (mDataPlottable) + return mDataPlottable->interface1D()->dataSortKey(index); + else + qDebug() << Q_FUNC_INFO << "no data plottable set"; + return 0; +} + +/* inherits documentation from base class */ +double QCPErrorBars::dataMainValue(int index) const +{ + if (mDataPlottable) + return mDataPlottable->interface1D()->dataMainValue(index); + else + qDebug() << Q_FUNC_INFO << "no data plottable set"; + return 0; +} + +/* inherits documentation from base class */ +QCPRange QCPErrorBars::dataValueRange(int index) const +{ + if (mDataPlottable) + { + const double value = mDataPlottable->interface1D()->dataMainValue(index); + if (index >= 0 && index < mDataContainer->size() && mErrorType == etValueError) + return {value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus}; + else + return {value, value}; + } else + { + qDebug() << Q_FUNC_INFO << "no data plottable set"; + return {}; + } +} + +/* inherits documentation from base class */ +QPointF QCPErrorBars::dataPixelPosition(int index) const +{ + if (mDataPlottable) + return mDataPlottable->interface1D()->dataPixelPosition(index); + else + qDebug() << Q_FUNC_INFO << "no data plottable set"; + return {}; +} + +/* inherits documentation from base class */ +bool QCPErrorBars::sortKeyIsMainKey() const +{ + if (mDataPlottable) + { + return mDataPlottable->interface1D()->sortKeyIsMainKey(); + } else + { + qDebug() << Q_FUNC_INFO << "no data plottable set"; + return true; + } +} + +/*! + \copydoc QCPPlottableInterface1D::selectTestRect +*/ +QCPDataSelection QCPErrorBars::selectTestRect(const QRectF &rect, bool onlySelectable) const +{ + QCPDataSelection result; + if (!mDataPlottable) + return result; + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return result; + if (!mKeyAxis || !mValueAxis) + return result; + + QCPErrorBarsDataContainer::const_iterator visibleBegin, visibleEnd; + getVisibleDataBounds(visibleBegin, visibleEnd, QCPDataRange(0, dataCount())); + + QVector backbones, whiskers; + for (QCPErrorBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) + { + backbones.clear(); + whiskers.clear(); + getErrorBarLines(it, backbones, whiskers); + foreach (const QLineF &backbone, backbones) + { + if (rectIntersectsLine(rect, backbone)) + { + result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false); + break; + } + } + } + result.simplify(); + return result; +} + +/* inherits documentation from base class */ +int QCPErrorBars::findBegin(double sortKey, bool expandedRange) const +{ + if (mDataPlottable) + { + if (mDataContainer->isEmpty()) + return 0; + int beginIndex = mDataPlottable->interface1D()->findBegin(sortKey, expandedRange); + if (beginIndex >= mDataContainer->size()) + beginIndex = static_cast(mDataContainer->size())-1; + return beginIndex; + } else + qDebug() << Q_FUNC_INFO << "no data plottable set"; + return 0; +} + +/* inherits documentation from base class */ +int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const +{ + if (mDataPlottable) + { + if (mDataContainer->isEmpty()) + return 0; + int endIndex = mDataPlottable->interface1D()->findEnd(sortKey, expandedRange); + if (endIndex > mDataContainer->size()) + endIndex = static_cast(mDataContainer->size()); + return endIndex; + } else + qDebug() << Q_FUNC_INFO << "no data plottable set"; + return 0; +} + +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ +double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if (!mDataPlottable) return -1; + + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect)) + { + QCPErrorBarsDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); + double result = pointDistance(pos, closestDataPoint); + if (details) + { + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); + details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); + } + return result; + } else + return -1; +} + +/* inherits documentation from base class */ +void QCPErrorBars::draw(QCPPainter *painter) +{ + if (!mDataPlottable) return; + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return; + + // if the sort key isn't the main key, we must check the visibility for each data point/error bar individually + // (getVisibleDataBounds applies range restriction, but otherwise can only return full data range): + bool checkPointVisibility = !mDataPlottable->interface1D()->sortKeyIsMainKey(); + + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + QCPErrorBarsDataContainer::const_iterator it; + for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it) + { + if (QCP::isInvalidData(it->errorMinus, it->errorPlus)) + qDebug() << Q_FUNC_INFO << "Data point at index" << it-mDataContainer->constBegin() << "invalid." << "Plottable name:" << name(); + } +#endif + + applyDefaultAntialiasingHint(painter); + painter->setBrush(Qt::NoBrush); + // loop over and draw segments of unselected/selected data: + QList selectedSegments, unselectedSegments, allSegments; + getDataSegments(selectedSegments, unselectedSegments); + allSegments << unselectedSegments << selectedSegments; + QVector backbones, whiskers; + for (int i=0; i= unselectedSegments.size(); + if (isSelectedSegment && mSelectionDecorator) + mSelectionDecorator->applyPen(painter); + else + painter->setPen(mPen); + if (painter->pen().capStyle() == Qt::SquareCap) + { + QPen capFixPen(painter->pen()); + capFixPen.setCapStyle(Qt::FlatCap); + painter->setPen(capFixPen); + } + backbones.clear(); + whiskers.clear(); + for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it) + { + if (!checkPointVisibility || errorBarVisible(int(it-mDataContainer->constBegin()))) + getErrorBarLines(it, backbones, whiskers); + } + painter->drawLines(backbones); + painter->drawLines(whiskers); + } + + // draw other selection decoration that isn't just line/scatter pens and brushes: + if (mSelectionDecorator) + mSelectionDecorator->drawDecoration(painter, selection()); +} + +/* inherits documentation from base class */ +void QCPErrorBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const +{ + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + if (mErrorType == etValueError && mValueAxis && mValueAxis->orientation() == Qt::Vertical) + { + painter->drawLine(QLineF(rect.center().x(), rect.top()+2, rect.center().x(), rect.bottom()-1)); + painter->drawLine(QLineF(rect.center().x()-4, rect.top()+2, rect.center().x()+4, rect.top()+2)); + painter->drawLine(QLineF(rect.center().x()-4, rect.bottom()-1, rect.center().x()+4, rect.bottom()-1)); + } else + { + painter->drawLine(QLineF(rect.left()+2, rect.center().y(), rect.right()-2, rect.center().y())); + painter->drawLine(QLineF(rect.left()+2, rect.center().y()-4, rect.left()+2, rect.center().y()+4)); + painter->drawLine(QLineF(rect.right()-2, rect.center().y()-4, rect.right()-2, rect.center().y()+4)); + } +} + +/* inherits documentation from base class */ +QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + if (!mDataPlottable) + { + foundRange = false; + return {}; + } + + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + QCPErrorBarsDataContainer::const_iterator it; + for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it) + { + if (mErrorType == etValueError) + { + // error bar doesn't extend in key dimension (except whisker but we ignore that here), so only use data point center + const double current = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin())); + if (qIsNaN(current)) continue; + if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + } else // mErrorType == etKeyError + { + const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin())); + if (qIsNaN(dataKey)) continue; + // plus error: + double current = dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus); + if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) + { + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + // minus error: + current = dataKey - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus); + if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + } + } + } + + if (haveUpper && !haveLower) + { + range.lower = range.upper; + haveLower = true; + } else if (haveLower && !haveUpper) + { + range.upper = range.lower; + haveUpper = true; + } + + foundRange = haveLower && haveUpper; + return range; +} + +/* inherits documentation from base class */ +QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + if (!mDataPlottable) + { + foundRange = false; + return {}; + } + + QCPRange range; + const bool restrictKeyRange = inKeyRange != QCPRange(); + bool haveLower = false; + bool haveUpper = false; + QCPErrorBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin(); + QCPErrorBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd(); + if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange) + { + itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower, false); + itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper, false); + } + for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it) + { + if (restrictKeyRange) + { + const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin())); + if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper) + continue; + } + if (mErrorType == etValueError) + { + const double dataValue = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin())); + if (qIsNaN(dataValue)) continue; + // plus error: + double current = dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus); + if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) + { + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + // minus error: + current = dataValue - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus); + if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + } + } else // mErrorType == etKeyError + { + // error bar doesn't extend in value dimension (except whisker but we ignore that here), so only use data point center + const double current = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin())); + if (qIsNaN(current)) continue; + if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0)) + { + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + } + } + + if (haveUpper && !haveLower) + { + range.lower = range.upper; + haveLower = true; + } else if (haveLower && !haveUpper) + { + range.upper = range.lower; + haveUpper = true; + } + + foundRange = haveLower && haveUpper; + return range; +} + +/*! \internal + + Calculates the lines that make up the error bar belonging to the data point \a it. + + The resulting lines are added to \a backbones and \a whiskers. The vectors are not cleared, so + calling this method with different \a it but the same \a backbones and \a whiskers allows to + accumulate lines for multiple data points. + + This method assumes that \a it is a valid iterator within the bounds of this \ref QCPErrorBars + instance and within the bounds of the associated data plottable. +*/ +void QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector &backbones, QVector &whiskers) const +{ + if (!mDataPlottable) return; + + int index = int(it-mDataContainer->constBegin()); + QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index); + if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y())) + return; + QCPAxis *errorAxis = mErrorType == etValueError ? mValueAxis.data() : mKeyAxis.data(); + QCPAxis *orthoAxis = mErrorType == etValueError ? mKeyAxis.data() : mValueAxis.data(); + const double centerErrorAxisPixel = errorAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y(); + const double centerOrthoAxisPixel = orthoAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y(); + const double centerErrorAxisCoord = errorAxis->pixelToCoord(centerErrorAxisPixel); // depending on plottable, this might be different from just mDataPlottable->interface1D()->dataMainKey/Value + const double symbolGap = mSymbolGap*0.5*errorAxis->pixelOrientation(); + // plus error: + double errorStart, errorEnd; + if (!qIsNaN(it->errorPlus)) + { + errorStart = centerErrorAxisPixel+symbolGap; + errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord+it->errorPlus); + if (errorAxis->orientation() == Qt::Vertical) + { + if ((errorStart > errorEnd) != errorAxis->rangeReversed()) + backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd)); + whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd)); + } else + { + if ((errorStart < errorEnd) != errorAxis->rangeReversed()) + backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel)); + whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5)); + } + } + // minus error: + if (!qIsNaN(it->errorMinus)) + { + errorStart = centerErrorAxisPixel-symbolGap; + errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord-it->errorMinus); + if (errorAxis->orientation() == Qt::Vertical) + { + if ((errorStart < errorEnd) != errorAxis->rangeReversed()) + backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd)); + whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd)); + } else + { + if ((errorStart > errorEnd) != errorAxis->rangeReversed()) + backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel)); + whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5)); + } + } +} + +/*! \internal + + This method outputs the currently visible data range via \a begin and \a end. The returned range + will also never exceed \a rangeRestriction. + + Since error bars with type \ref etKeyError may extend to arbitrarily positive and negative key + coordinates relative to their data point key, this method checks all outer error bars whether + they truly don't reach into the visible portion of the axis rect, by calling \ref + errorBarVisible. On the other hand error bars with type \ref etValueError that are associated + with data plottables whose sort key is equal to the main key (see \ref qcpdatacontainer-datatype + "QCPDataContainer DataType") can be handled very efficiently by finding the visible range of + error bars through binary search (\ref QCPPlottableInterface1D::findBegin and \ref + QCPPlottableInterface1D::findEnd). + + If the plottable's sort key is not equal to the main key, this method returns the full data + range, only restricted by \a rangeRestriction. Drawing optimization then has to be done on a + point-by-point basis in the \ref draw method. +*/ +void QCPErrorBars::getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const +{ + QCPAxis *keyAxis = mKeyAxis.data(); + QCPAxis *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + end = mDataContainer->constEnd(); + begin = end; + return; + } + if (!mDataPlottable || rangeRestriction.isEmpty()) + { + end = mDataContainer->constEnd(); + begin = end; + return; + } + if (!mDataPlottable->interface1D()->sortKeyIsMainKey()) + { + // if the sort key isn't the main key, it's not possible to find a contiguous range of visible + // data points, so this method then only applies the range restriction and otherwise returns + // the full data range. Visibility checks must be done on a per-datapoin-basis during drawing + QCPDataRange dataRange(0, static_cast(mDataContainer->size())); + dataRange = dataRange.bounded(rangeRestriction); + begin = mDataContainer->constBegin()+dataRange.begin(); + end = mDataContainer->constBegin()+dataRange.end(); + return; + } + + // get visible data range via interface from data plottable, and then restrict to available error data points: + const int n = static_cast(qMin(mDataContainer->size(), mDataPlottable->interface1D()->dataCount())); + int beginIndex = mDataPlottable->interface1D()->findBegin(keyAxis->range().lower); + int endIndex = mDataPlottable->interface1D()->findEnd(keyAxis->range().upper); + int i = beginIndex; + while (i > 0 && i < n && i > rangeRestriction.begin()) + { + if (errorBarVisible(i)) + beginIndex = i; + --i; + } + i = endIndex; + while (i >= 0 && i < n && i < rangeRestriction.end()) + { + if (errorBarVisible(i)) + endIndex = i+1; + ++i; + } + QCPDataRange dataRange(beginIndex, endIndex); + dataRange = dataRange.bounded(rangeRestriction.bounded(QCPDataRange(0, static_cast(mDataContainer->size())))); + begin = mDataContainer->constBegin()+dataRange.begin(); + end = mDataContainer->constBegin()+dataRange.end(); +} + +/*! \internal + + Calculates the minimum distance in pixels the error bars' representation has from the given \a + pixelPoint. This is used to determine whether the error bar was clicked or not, e.g. in \ref + selectTest. The closest data point to \a pixelPoint is returned in \a closestData. +*/ +double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const +{ + closestData = mDataContainer->constEnd(); + if (!mDataPlottable || mDataContainer->isEmpty()) + return -1.0; + if (!mKeyAxis || !mValueAxis) + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + return -1.0; + } + + QCPErrorBarsDataContainer::const_iterator begin, end; + getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount())); + + // calculate minimum distances to error backbones (whiskers are ignored for speed) and find closestData iterator: + double minDistSqr = (std::numeric_limits::max)(); + QVector backbones, whiskers; + for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it) + { + getErrorBarLines(it, backbones, whiskers); + foreach (const QLineF &backbone, backbones) + { + const double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(backbone); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestData = it; + } + } + } + return qSqrt(minDistSqr); +} + +/*! \internal + + \note This method is identical to \ref QCPAbstractPlottable1D::getDataSegments but needs to be + reproduced here since the \ref QCPErrorBars plottable, as a special case that doesn't have its + own key/value data coordinates, doesn't derive from \ref QCPAbstractPlottable1D. See the + documentation there for details. +*/ +void QCPErrorBars::getDataSegments(QList &selectedSegments, QList &unselectedSegments) const +{ + selectedSegments.clear(); + unselectedSegments.clear(); + if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty + { + if (selected()) + selectedSegments << QCPDataRange(0, dataCount()); + else + unselectedSegments << QCPDataRange(0, dataCount()); + } else + { + QCPDataSelection sel(selection()); + sel.simplify(); + selectedSegments = sel.dataRanges(); + unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges(); + } +} + +/*! \internal + + Returns whether the error bar at the specified \a index is visible within the current key axis + range. + + This method assumes for performance reasons without checking that the key axis, the value axis, + and the data plottable (\ref setDataPlottable) are not \c nullptr and that \a index is within + valid bounds of this \ref QCPErrorBars instance and the bounds of the data plottable. +*/ +bool QCPErrorBars::errorBarVisible(int index) const +{ + QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index); + const double centerKeyPixel = mKeyAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y(); + if (qIsNaN(centerKeyPixel)) + return false; + + double keyMin, keyMax; + if (mErrorType == etKeyError) + { + const double centerKey = mKeyAxis->pixelToCoord(centerKeyPixel); + const double errorPlus = mDataContainer->at(index).errorPlus; + const double errorMinus = mDataContainer->at(index).errorMinus; + keyMax = centerKey+(qIsNaN(errorPlus) ? 0 : errorPlus); + keyMin = centerKey-(qIsNaN(errorMinus) ? 0 : errorMinus); + } else // mErrorType == etValueError + { + keyMax = mKeyAxis->pixelToCoord(centerKeyPixel+mWhiskerWidth*0.5*mKeyAxis->pixelOrientation()); + keyMin = mKeyAxis->pixelToCoord(centerKeyPixel-mWhiskerWidth*0.5*mKeyAxis->pixelOrientation()); + } + return ((keyMax > mKeyAxis->range().lower) && (keyMin < mKeyAxis->range().upper)); +} + +/*! \internal + + Returns whether \a line intersects (or is contained in) \a pixelRect. + + \a line is assumed to be either perfectly horizontal or perfectly vertical, as is the case for + error bar lines. +*/ +bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const +{ + if (pixelRect.left() > line.x1() && pixelRect.left() > line.x2()) + return false; + else if (pixelRect.right() < line.x1() && pixelRect.right() < line.x2()) + return false; + else if (pixelRect.top() > line.y1() && pixelRect.top() > line.y2()) + return false; + else if (pixelRect.bottom() < line.y1() && pixelRect.bottom() < line.y2()) + return false; + else + return true; +} +/* end of 'src/plottables/plottable-errorbar.cpp' */ + + +/* including file 'src/items/item-straightline.cpp' */ +/* modified 2022-11-06T12:45:56, size 7596 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemStraightLine +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemStraightLine + \brief A straight line that spans infinitely in both directions + + \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a point1 and \a point2, which define the straight line. +*/ + +/*! + Creates a straight line item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + point1(createPosition(QLatin1String("point1"))), + point2(createPosition(QLatin1String("point2"))) +{ + point1->setCoords(0, 0); + point2->setCoords(1, 1); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue,2)); +} + +QCPItemStraightLine::~QCPItemStraightLine() +{ +} + +/*! + Sets the pen that will be used to draw the line + + \see setSelectedPen +*/ +void QCPItemStraightLine::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line when selected + + \see setPen, setSelected +*/ +void QCPItemStraightLine::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/* inherits documentation from base class */ +double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + return QCPVector2D(pos).distanceToStraightLine(point1->pixelPosition(), point2->pixelPosition()-point1->pixelPosition()); +} + +/* inherits documentation from base class */ +void QCPItemStraightLine::draw(QCPPainter *painter) +{ + QCPVector2D start(point1->pixelPosition()); + QCPVector2D end(point2->pixelPosition()); + // get visible segment of straight line inside clipRect: + int clipPad = qCeil(mainPen().widthF()); + QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad)); + // paint visible segment, if existent: + if (!line.isNull()) + { + painter->setPen(mainPen()); + painter->drawLine(line); + } +} + +/*! \internal + + Returns the section of the straight line defined by \a base and direction vector \a + vec, that is visible in the specified \a rect. + + This is a helper function for \ref draw. +*/ +QLineF QCPItemStraightLine::getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const +{ + double bx, by; + double gamma; + QLineF result; + if (vec.x() == 0 && vec.y() == 0) + return result; + if (qFuzzyIsNull(vec.x())) // line is vertical + { + // check top of rect: + bx = rect.left(); + by = rect.top(); + gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y(); + if (gamma >= 0 && gamma <= rect.width()) + result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical + } else if (qFuzzyIsNull(vec.y())) // line is horizontal + { + // check left of rect: + bx = rect.left(); + by = rect.top(); + gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x(); + if (gamma >= 0 && gamma <= rect.height()) + result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal + } else // line is skewed + { + QList pointVectors; + // check top of rect: + bx = rect.left(); + by = rect.top(); + gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y(); + if (gamma >= 0 && gamma <= rect.width()) + pointVectors.append(QCPVector2D(bx+gamma, by)); + // check bottom of rect: + bx = rect.left(); + by = rect.bottom(); + gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y(); + if (gamma >= 0 && gamma <= rect.width()) + pointVectors.append(QCPVector2D(bx+gamma, by)); + // check left of rect: + bx = rect.left(); + by = rect.top(); + gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x(); + if (gamma >= 0 && gamma <= rect.height()) + pointVectors.append(QCPVector2D(bx, by+gamma)); + // check right of rect: + bx = rect.right(); + by = rect.top(); + gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x(); + if (gamma >= 0 && gamma <= rect.height()) + pointVectors.append(QCPVector2D(bx, by+gamma)); + + // evaluate points: + if (pointVectors.size() == 2) + { + result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF()); + } else if (pointVectors.size() > 2) + { + // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance: + double distSqrMax = 0; + QCPVector2D pv1, pv2; + for (int i=0; i distSqrMax) + { + pv1 = pointVectors.at(i); + pv2 = pointVectors.at(k); + distSqrMax = distSqr; + } + } + } + result.setPoints(pv1.toPointF(), pv2.toPointF()); + } + } + return result; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemStraightLine::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} +/* end of 'src/items/item-straightline.cpp' */ + + +/* including file 'src/items/item-line.cpp' */ +/* modified 2022-11-06T12:45:56, size 8525 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemLine +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemLine + \brief A line from one point to another + + \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a start and \a end, which define the end points of the line. + + With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow. +*/ + +/*! + Creates a line item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + start(createPosition(QLatin1String("start"))), + end(createPosition(QLatin1String("end"))) +{ + start->setCoords(0, 0); + end->setCoords(1, 1); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue,2)); +} + +QCPItemLine::~QCPItemLine() +{ +} + +/*! + Sets the pen that will be used to draw the line + + \see setSelectedPen +*/ +void QCPItemLine::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line when selected + + \see setPen, setSelected +*/ +void QCPItemLine::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the line ending style of the head. The head corresponds to the \a end position. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify + a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode + + \see setTail +*/ +void QCPItemLine::setHead(const QCPLineEnding &head) +{ + mHead = head; +} + +/*! + Sets the line ending style of the tail. The tail corresponds to the \a start position. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify + a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode + + \see setHead +*/ +void QCPItemLine::setTail(const QCPLineEnding &tail) +{ + mTail = tail; +} + +/* inherits documentation from base class */ +double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + return qSqrt(QCPVector2D(pos).distanceSquaredToLine(start->pixelPosition(), end->pixelPosition())); +} + +/* inherits documentation from base class */ +void QCPItemLine::draw(QCPPainter *painter) +{ + QCPVector2D startVec(start->pixelPosition()); + QCPVector2D endVec(end->pixelPosition()); + if (qFuzzyIsNull((startVec-endVec).lengthSquared())) + return; + // get visible segment of straight line inside clipRect: + int clipPad = int(qMax(mHead.boundingDistance(), mTail.boundingDistance())); + clipPad = qMax(clipPad, qCeil(mainPen().widthF())); + QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad)); + // paint visible segment, if existent: + if (!line.isNull()) + { + painter->setPen(mainPen()); + painter->drawLine(line); + painter->setBrush(Qt::SolidPattern); + if (mTail.style() != QCPLineEnding::esNone) + mTail.draw(painter, startVec, startVec-endVec); + if (mHead.style() != QCPLineEnding::esNone) + mHead.draw(painter, endVec, endVec-startVec); + } +} + +/*! \internal + + Returns the section of the line defined by \a start and \a end, that is visible in the specified + \a rect. + + This is a helper function for \ref draw. +*/ +QLineF QCPItemLine::getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const +{ + bool containsStart = rect.contains(qRound(start.x()), qRound(start.y())); + bool containsEnd = rect.contains(qRound(end.x()), qRound(end.y())); + if (containsStart && containsEnd) + return {start.toPointF(), end.toPointF()}; + + QCPVector2D base = start; + QCPVector2D vec = end-start; + double bx, by; + double gamma, mu; + QLineF result; + QList pointVectors; + + if (!qFuzzyIsNull(vec.y())) // line is not horizontal + { + // check top of rect: + bx = rect.left(); + by = rect.top(); + mu = (by-base.y())/vec.y(); + if (mu >= 0 && mu <= 1) + { + gamma = base.x()-bx + mu*vec.x(); + if (gamma >= 0 && gamma <= rect.width()) + pointVectors.append(QCPVector2D(bx+gamma, by)); + } + // check bottom of rect: + bx = rect.left(); + by = rect.bottom(); + mu = (by-base.y())/vec.y(); + if (mu >= 0 && mu <= 1) + { + gamma = base.x()-bx + mu*vec.x(); + if (gamma >= 0 && gamma <= rect.width()) + pointVectors.append(QCPVector2D(bx+gamma, by)); + } + } + if (!qFuzzyIsNull(vec.x())) // line is not vertical + { + // check left of rect: + bx = rect.left(); + by = rect.top(); + mu = (bx-base.x())/vec.x(); + if (mu >= 0 && mu <= 1) + { + gamma = base.y()-by + mu*vec.y(); + if (gamma >= 0 && gamma <= rect.height()) + pointVectors.append(QCPVector2D(bx, by+gamma)); + } + // check right of rect: + bx = rect.right(); + by = rect.top(); + mu = (bx-base.x())/vec.x(); + if (mu >= 0 && mu <= 1) + { + gamma = base.y()-by + mu*vec.y(); + if (gamma >= 0 && gamma <= rect.height()) + pointVectors.append(QCPVector2D(bx, by+gamma)); + } + } + + if (containsStart) + pointVectors.append(start); + if (containsEnd) + pointVectors.append(end); + + // evaluate points: + if (pointVectors.size() == 2) + { + result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF()); + } else if (pointVectors.size() > 2) + { + // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance: + double distSqrMax = 0; + QCPVector2D pv1, pv2; + for (int i=0; i distSqrMax) + { + pv1 = pointVectors.at(i); + pv2 = pointVectors.at(k); + distSqrMax = distSqr; + } + } + } + result.setPoints(pv1.toPointF(), pv2.toPointF()); + } + return result; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemLine::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} +/* end of 'src/items/item-line.cpp' */ + + +/* including file 'src/items/item-curve.cpp' */ +/* modified 2022-11-06T12:45:56, size 7273 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemCurve +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemCurve + \brief A curved line from one point to another + + \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions." + + It has four positions, \a start and \a end, which define the end points of the line, and two + control points which define the direction the line exits from the start and the direction from + which it approaches the end: \a startDir and \a endDir. + + With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an + arrow. + + Often it is desirable for the control points to stay at fixed relative positions to the start/end + point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start, + and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir. +*/ + +/*! + Creates a curve item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + start(createPosition(QLatin1String("start"))), + startDir(createPosition(QLatin1String("startDir"))), + endDir(createPosition(QLatin1String("endDir"))), + end(createPosition(QLatin1String("end"))) +{ + start->setCoords(0, 0); + startDir->setCoords(0.5, 0); + endDir->setCoords(0, 0.5); + end->setCoords(1, 1); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue,2)); +} + +QCPItemCurve::~QCPItemCurve() +{ +} + +/*! + Sets the pen that will be used to draw the line + + \see setSelectedPen +*/ +void QCPItemCurve::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line when selected + + \see setPen, setSelected +*/ +void QCPItemCurve::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the line ending style of the head. The head corresponds to the \a end position. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify + a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode + + \see setTail +*/ +void QCPItemCurve::setHead(const QCPLineEnding &head) +{ + mHead = head; +} + +/*! + Sets the line ending style of the tail. The tail corresponds to the \a start position. + + Note that due to the overloaded QCPLineEnding constructor, you may directly specify + a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode + + \see setHead +*/ +void QCPItemCurve::setTail(const QCPLineEnding &tail) +{ + mTail = tail; +} + +/* inherits documentation from base class */ +double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QPointF startVec(start->pixelPosition()); + QPointF startDirVec(startDir->pixelPosition()); + QPointF endDirVec(endDir->pixelPosition()); + QPointF endVec(end->pixelPosition()); + + QPainterPath cubicPath(startVec); + cubicPath.cubicTo(startDirVec, endDirVec, endVec); + + QList polygons = cubicPath.toSubpathPolygons(); + if (polygons.isEmpty()) + return -1; + const QPolygonF polygon = polygons.first(); + QCPVector2D p(pos); + double minDistSqr = (std::numeric_limits::max)(); + for (int i=1; ipixelPosition()); + QCPVector2D startDirVec(startDir->pixelPosition()); + QCPVector2D endDirVec(endDir->pixelPosition()); + QCPVector2D endVec(end->pixelPosition()); + if ((endVec-startVec).length() > 1e10) // too large curves cause crash + return; + + QPainterPath cubicPath(startVec.toPointF()); + cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(), endVec.toPointF()); + + // paint visible segment, if existent: + const int clipEnlarge = qCeil(mainPen().widthF()); + QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge); + QRect cubicRect = cubicPath.controlPointRect().toRect(); + if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position + cubicRect.adjust(0, 0, 1, 1); + if (clip.intersects(cubicRect)) + { + painter->setPen(mainPen()); + painter->drawPath(cubicPath); + painter->setBrush(Qt::SolidPattern); + if (mTail.style() != QCPLineEnding::esNone) + mTail.draw(painter, startVec, M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI); + if (mHead.style() != QCPLineEnding::esNone) + mHead.draw(painter, endVec, -cubicPath.angleAtPercent(1)/180.0*M_PI); + } +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemCurve::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} +/* end of 'src/items/item-curve.cpp' */ + + +/* including file 'src/items/item-rect.cpp' */ +/* modified 2022-11-06T12:45:56, size 6472 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemRect +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemRect + \brief A rectangle + + \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a topLeft and \a bottomRight, which define the rectangle. +*/ + +/*! + Creates a rectangle item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + topLeft(createPosition(QLatin1String("topLeft"))), + bottomRight(createPosition(QLatin1String("bottomRight"))), + top(createAnchor(QLatin1String("top"), aiTop)), + topRight(createAnchor(QLatin1String("topRight"), aiTopRight)), + right(createAnchor(QLatin1String("right"), aiRight)), + bottom(createAnchor(QLatin1String("bottom"), aiBottom)), + bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)), + left(createAnchor(QLatin1String("left"), aiLeft)) +{ + topLeft->setCoords(0, 1); + bottomRight->setCoords(1, 0); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue,2)); + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); +} + +QCPItemRect::~QCPItemRect() +{ +} + +/*! + Sets the pen that will be used to draw the line of the rectangle + + \see setSelectedPen, setBrush +*/ +void QCPItemRect::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line of the rectangle when selected + + \see setPen, setSelected +*/ +void QCPItemRect::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to + Qt::NoBrush. + + \see setSelectedBrush, setPen +*/ +void QCPItemRect::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a + brush to Qt::NoBrush. + + \see setBrush +*/ +void QCPItemRect::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/* inherits documentation from base class */ +double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition()).normalized(); + bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0; + return rectDistance(rect, pos, filledRect); +} + +/* inherits documentation from base class */ +void QCPItemRect::draw(QCPPainter *painter) +{ + QPointF p1 = topLeft->pixelPosition(); + QPointF p2 = bottomRight->pixelPosition(); + if (p1.toPoint() == p2.toPoint()) + return; + QRectF rect = QRectF(p1, p2).normalized(); + double clipPad = mainPen().widthF(); + QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad); + if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect + { + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); + painter->drawRect(rect); + } +} + +/* inherits documentation from base class */ +QPointF QCPItemRect::anchorPixelPosition(int anchorId) const +{ + QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition()); + switch (anchorId) + { + case aiTop: return (rect.topLeft()+rect.topRight())*0.5; + case aiTopRight: return rect.topRight(); + case aiRight: return (rect.topRight()+rect.bottomRight())*0.5; + case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5; + case aiBottomLeft: return rect.bottomLeft(); + case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5; + } + + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return {}; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemRect::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item + is not selected and mSelectedBrush when it is. +*/ +QBrush QCPItemRect::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} +/* end of 'src/items/item-rect.cpp' */ + + +/* including file 'src/items/item-text.cpp' */ +/* modified 2022-11-06T12:45:56, size 13335 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemText +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemText + \brief A text label + + \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions." + + Its position is defined by the member \a position and the setting of \ref setPositionAlignment. + The latter controls which part of the text rect shall be aligned with \a position. + + The text alignment itself (i.e. left, center, right) can be controlled with \ref + setTextAlignment. + + The text may be rotated around the \a position point with \ref setRotation. +*/ + +/*! + Creates a text item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemText::QCPItemText(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + position(createPosition(QLatin1String("position"))), + topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)), + top(createAnchor(QLatin1String("top"), aiTop)), + topRight(createAnchor(QLatin1String("topRight"), aiTopRight)), + right(createAnchor(QLatin1String("right"), aiRight)), + bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)), + bottom(createAnchor(QLatin1String("bottom"), aiBottom)), + bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)), + left(createAnchor(QLatin1String("left"), aiLeft)), + mText(QLatin1String("text")), + mPositionAlignment(Qt::AlignCenter), + mTextAlignment(Qt::AlignTop|Qt::AlignHCenter), + mRotation(0) +{ + position->setCoords(0, 0); + + setPen(Qt::NoPen); + setSelectedPen(Qt::NoPen); + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); + setColor(Qt::black); + setSelectedColor(Qt::blue); +} + +QCPItemText::~QCPItemText() +{ +} + +/*! + Sets the color of the text. +*/ +void QCPItemText::setColor(const QColor &color) +{ + mColor = color; +} + +/*! + Sets the color of the text that will be used when the item is selected. +*/ +void QCPItemText::setSelectedColor(const QColor &color) +{ + mSelectedColor = color; +} + +/*! + Sets the pen that will be used do draw a rectangular border around the text. To disable the + border, set \a pen to Qt::NoPen. + + \see setSelectedPen, setBrush, setPadding +*/ +void QCPItemText::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used do draw a rectangular border around the text, when the item is + selected. To disable the border, set \a pen to Qt::NoPen. + + \see setPen +*/ +void QCPItemText::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the brush that will be used do fill the background of the text. To disable the + background, set \a brush to Qt::NoBrush. + + \see setSelectedBrush, setPen, setPadding +*/ +void QCPItemText::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the + background, set \a brush to Qt::NoBrush. + + \see setBrush +*/ +void QCPItemText::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/*! + Sets the font of the text. + + \see setSelectedFont, setColor +*/ +void QCPItemText::setFont(const QFont &font) +{ + mFont = font; +} + +/*! + Sets the font of the text that will be used when the item is selected. + + \see setFont +*/ +void QCPItemText::setSelectedFont(const QFont &font) +{ + mSelectedFont = font; +} + +/*! + Sets the text that will be displayed. Multi-line texts are supported by inserting a line break + character, e.g. '\n'. + + \see setFont, setColor, setTextAlignment +*/ +void QCPItemText::setText(const QString &text) +{ + mText = text; +} + +/*! + Sets which point of the text rect shall be aligned with \a position. + + Examples: + \li If \a alignment is Qt::AlignHCenter | Qt::AlignTop, the text will be positioned such + that the top of the text rect will be horizontally centered on \a position. + \li If \a alignment is Qt::AlignLeft | Qt::AlignBottom, \a position will indicate the + bottom left corner of the text rect. + + If you want to control the alignment of (multi-lined) text within the text rect, use \ref + setTextAlignment. +*/ +void QCPItemText::setPositionAlignment(Qt::Alignment alignment) +{ + mPositionAlignment = alignment; +} + +/*! + Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight). +*/ +void QCPItemText::setTextAlignment(Qt::Alignment alignment) +{ + mTextAlignment = alignment; +} + +/*! + Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated + around \a position. +*/ +void QCPItemText::setRotation(double degrees) +{ + mRotation = degrees; +} + +/*! + Sets the distance between the border of the text rectangle and the text. The appearance (and + visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush. +*/ +void QCPItemText::setPadding(const QMargins &padding) +{ + mPadding = padding; +} + +/* inherits documentation from base class */ +double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + // The rect may be rotated, so we transform the actual clicked pos to the rotated + // coordinate system, so we can use the normal rectDistance function for non-rotated rects: + QPointF positionPixels(position->pixelPosition()); + QTransform inputTransform; + inputTransform.translate(positionPixels.x(), positionPixels.y()); + inputTransform.rotate(-mRotation); + inputTransform.translate(-positionPixels.x(), -positionPixels.y()); + QPointF rotatedPos = inputTransform.map(pos); + QFontMetrics fontMetrics(mFont); + QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText); + QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom()); + QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment); + textBoxRect.moveTopLeft(textPos.toPoint()); + + return rectDistance(textBoxRect, rotatedPos, true); +} + +/* inherits documentation from base class */ +void QCPItemText::draw(QCPPainter *painter) +{ + QPointF pos(position->pixelPosition()); + QTransform transform = painter->transform(); + transform.translate(pos.x(), pos.y()); + if (!qFuzzyIsNull(mRotation)) + transform.rotate(mRotation); + painter->setFont(mainFont()); + QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText); + QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom()); + QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation + textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top())); + textBoxRect.moveTopLeft(textPos.toPoint()); + int clipPad = qCeil(mainPen().widthF()); + QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad); + if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect()))) + { + painter->setTransform(transform); + if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) || + (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)) + { + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); + painter->drawRect(textBoxRect); + } + painter->setBrush(Qt::NoBrush); + painter->setPen(QPen(mainColor())); + painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText); + } +} + +/* inherits documentation from base class */ +QPointF QCPItemText::anchorPixelPosition(int anchorId) const +{ + // get actual rect points (pretty much copied from draw function): + QPointF pos(position->pixelPosition()); + QTransform transform; + transform.translate(pos.x(), pos.y()); + if (!qFuzzyIsNull(mRotation)) + transform.rotate(mRotation); + QFontMetrics fontMetrics(mainFont()); + QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText); + QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom()); + QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation + textBoxRect.moveTopLeft(textPos.toPoint()); + QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect)); + + switch (anchorId) + { + case aiTopLeft: return rectPoly.at(0); + case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5; + case aiTopRight: return rectPoly.at(1); + case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5; + case aiBottomRight: return rectPoly.at(2); + case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5; + case aiBottomLeft: return rectPoly.at(3); + case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5; + } + + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return {}; +} + +/*! \internal + + Returns the point that must be given to the QPainter::drawText function (which expects the top + left point of the text rect), according to the position \a pos, the text bounding box \a rect and + the requested \a positionAlignment. + + For example, if \a positionAlignment is Qt::AlignLeft | Qt::AlignBottom the returned point + will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally + drawn at that point, the lower left corner of the resulting text rect is at \a pos. +*/ +QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const +{ + if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop)) + return pos; + + QPointF result = pos; // start at top left + if (positionAlignment.testFlag(Qt::AlignHCenter)) + result.rx() -= rect.width()/2.0; + else if (positionAlignment.testFlag(Qt::AlignRight)) + result.rx() -= rect.width(); + if (positionAlignment.testFlag(Qt::AlignVCenter)) + result.ry() -= rect.height()/2.0; + else if (positionAlignment.testFlag(Qt::AlignBottom)) + result.ry() -= rect.height(); + return result; +} + +/*! \internal + + Returns the font that should be used for drawing text. Returns mFont when the item is not selected + and mSelectedFont when it is. +*/ +QFont QCPItemText::mainFont() const +{ + return mSelected ? mSelectedFont : mFont; +} + +/*! \internal + + Returns the color that should be used for drawing text. Returns mColor when the item is not + selected and mSelectedColor when it is. +*/ +QColor QCPItemText::mainColor() const +{ + return mSelected ? mSelectedColor : mColor; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemText::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item + is not selected and mSelectedBrush when it is. +*/ +QBrush QCPItemText::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} +/* end of 'src/items/item-text.cpp' */ + + +/* including file 'src/items/item-ellipse.cpp' */ +/* modified 2022-11-06T12:45:56, size 7881 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemEllipse +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemEllipse + \brief An ellipse + + \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in. +*/ + +/*! + Creates an ellipse item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + topLeft(createPosition(QLatin1String("topLeft"))), + bottomRight(createPosition(QLatin1String("bottomRight"))), + topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)), + top(createAnchor(QLatin1String("top"), aiTop)), + topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)), + right(createAnchor(QLatin1String("right"), aiRight)), + bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)), + bottom(createAnchor(QLatin1String("bottom"), aiBottom)), + bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)), + left(createAnchor(QLatin1String("left"), aiLeft)), + center(createAnchor(QLatin1String("center"), aiCenter)) +{ + topLeft->setCoords(0, 1); + bottomRight->setCoords(1, 0); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue, 2)); + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); +} + +QCPItemEllipse::~QCPItemEllipse() +{ +} + +/*! + Sets the pen that will be used to draw the line of the ellipse + + \see setSelectedPen, setBrush +*/ +void QCPItemEllipse::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line of the ellipse when selected + + \see setPen, setSelected +*/ +void QCPItemEllipse::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to + Qt::NoBrush. + + \see setSelectedBrush, setPen +*/ +void QCPItemEllipse::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a + brush to Qt::NoBrush. + + \see setBrush +*/ +void QCPItemEllipse::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/* inherits documentation from base class */ +double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QPointF p1 = topLeft->pixelPosition(); + QPointF p2 = bottomRight->pixelPosition(); + QPointF center((p1+p2)/2.0); + double a = qAbs(p1.x()-p2.x())/2.0; + double b = qAbs(p1.y()-p2.y())/2.0; + double x = pos.x()-center.x(); + double y = pos.y()-center.y(); + + // distance to border: + double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b)); + double result = qAbs(c-1)*qSqrt(x*x+y*y); + // filled ellipse, allow click inside to count as hit: + if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0) + { + if (x*x/(a*a) + y*y/(b*b) <= 1) + result = mParentPlot->selectionTolerance()*0.99; + } + return result; +} + +/* inherits documentation from base class */ +void QCPItemEllipse::draw(QCPPainter *painter) +{ + QPointF p1 = topLeft->pixelPosition(); + QPointF p2 = bottomRight->pixelPosition(); + if (p1.toPoint() == p2.toPoint()) + return; + QRectF ellipseRect = QRectF(p1, p2).normalized(); + const int clipEnlarge = qCeil(mainPen().widthF()); + QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge); + if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect + { + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); +#ifdef __EXCEPTIONS + try // drawEllipse sometimes throws exceptions if ellipse is too big + { +#endif + painter->drawEllipse(ellipseRect); +#ifdef __EXCEPTIONS + } catch (...) + { + qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible"; + setVisible(false); + } +#endif + } +} + +/* inherits documentation from base class */ +QPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const +{ + QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition()); + switch (anchorId) + { + case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2); + case aiTop: return (rect.topLeft()+rect.topRight())*0.5; + case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2); + case aiRight: return (rect.topRight()+rect.bottomRight())*0.5; + case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2); + case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5; + case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2); + case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5; + case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5; + } + + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return {}; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemEllipse::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item + is not selected and mSelectedBrush when it is. +*/ +QBrush QCPItemEllipse::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} +/* end of 'src/items/item-ellipse.cpp' */ + + +/* including file 'src/items/item-pixmap.cpp' */ +/* modified 2022-11-06T12:45:56, size 10622 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemPixmap +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemPixmap + \brief An arbitrary pixmap + + \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will + be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to + fit the rectangle or be drawn aligned to the topLeft position. + + If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown + on the right side of the example image), the pixmap will be flipped in the respective + orientations. +*/ + +/*! + Creates a rectangle item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + topLeft(createPosition(QLatin1String("topLeft"))), + bottomRight(createPosition(QLatin1String("bottomRight"))), + top(createAnchor(QLatin1String("top"), aiTop)), + topRight(createAnchor(QLatin1String("topRight"), aiTopRight)), + right(createAnchor(QLatin1String("right"), aiRight)), + bottom(createAnchor(QLatin1String("bottom"), aiBottom)), + bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)), + left(createAnchor(QLatin1String("left"), aiLeft)), + mScaled(false), + mScaledPixmapInvalidated(true), + mAspectRatioMode(Qt::KeepAspectRatio), + mTransformationMode(Qt::SmoothTransformation) +{ + topLeft->setCoords(0, 1); + bottomRight->setCoords(1, 0); + + setPen(Qt::NoPen); + setSelectedPen(QPen(Qt::blue)); +} + +QCPItemPixmap::~QCPItemPixmap() +{ +} + +/*! + Sets the pixmap that will be displayed. +*/ +void QCPItemPixmap::setPixmap(const QPixmap &pixmap) +{ + mPixmap = pixmap; + mScaledPixmapInvalidated = true; + if (mPixmap.isNull()) + qDebug() << Q_FUNC_INFO << "pixmap is null"; +} + +/*! + Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a + bottomRight positions. +*/ +void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode) +{ + mScaled = scaled; + mAspectRatioMode = aspectRatioMode; + mTransformationMode = transformationMode; + mScaledPixmapInvalidated = true; +} + +/*! + Sets the pen that will be used to draw a border around the pixmap. + + \see setSelectedPen, setBrush +*/ +void QCPItemPixmap::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw a border around the pixmap when selected + + \see setPen, setSelected +*/ +void QCPItemPixmap::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/* inherits documentation from base class */ +double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + return rectDistance(getFinalRect(), pos, true); +} + +/* inherits documentation from base class */ +void QCPItemPixmap::draw(QCPPainter *painter) +{ + bool flipHorz = false; + bool flipVert = false; + QRect rect = getFinalRect(&flipHorz, &flipVert); + int clipPad = mainPen().style() == Qt::NoPen ? 0 : qCeil(mainPen().widthF()); + QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad); + if (boundingRect.intersects(clipRect())) + { + updateScaledPixmap(rect, flipHorz, flipVert); + painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap); + QPen pen = mainPen(); + if (pen.style() != Qt::NoPen) + { + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->drawRect(rect); + } + } +} + +/* inherits documentation from base class */ +QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const +{ + bool flipHorz = false; + bool flipVert = false; + QRect rect = getFinalRect(&flipHorz, &flipVert); + // we actually want denormal rects (negative width/height) here, so restore + // the flipped state: + if (flipHorz) + rect.adjust(rect.width(), 0, -rect.width(), 0); + if (flipVert) + rect.adjust(0, rect.height(), 0, -rect.height()); + + switch (anchorId) + { + case aiTop: return (rect.topLeft()+rect.topRight())*0.5; + case aiTopRight: return rect.topRight(); + case aiRight: return (rect.topRight()+rect.bottomRight())*0.5; + case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5; + case aiBottomLeft: return rect.bottomLeft(); + case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5; + } + + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return {}; +} + +/*! \internal + + Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The + parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped + horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a + bottomRight.) + + This function only creates the scaled pixmap when the buffered pixmap has a different size than + the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does + not cause expensive rescaling every time. + + If scaling is disabled, sets mScaledPixmap to a null QPixmap. +*/ +void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert) +{ + if (mPixmap.isNull()) + return; + + if (mScaled) + { +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED + double devicePixelRatio = mPixmap.devicePixelRatio(); +#else + double devicePixelRatio = 1.0; +#endif + if (finalRect.isNull()) + finalRect = getFinalRect(&flipHorz, &flipVert); + if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size()/devicePixelRatio) + { + mScaledPixmap = mPixmap.scaled(finalRect.size()*devicePixelRatio, mAspectRatioMode, mTransformationMode); + if (flipHorz || flipVert) + mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert)); +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED + mScaledPixmap.setDevicePixelRatio(devicePixelRatio); +#endif + } + } else if (!mScaledPixmap.isNull()) + mScaledPixmap = QPixmap(); + mScaledPixmapInvalidated = false; +} + +/*! \internal + + Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions + and scaling settings. + + The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn + flipped horizontally or vertically in the returned rect. (The returned rect itself is always + normalized, i.e. the top left corner of the rect is actually further to the top/left than the + bottom right corner). This is the case when the item position \a topLeft is further to the + bottom/right than \a bottomRight. + + If scaling is disabled, returns a rect with size of the original pixmap and the top left corner + aligned with the item position \a topLeft. The position \a bottomRight is ignored. +*/ +QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const +{ + QRect result; + bool flipHorz = false; + bool flipVert = false; + QPoint p1 = topLeft->pixelPosition().toPoint(); + QPoint p2 = bottomRight->pixelPosition().toPoint(); + if (p1 == p2) + return {p1, QSize(0, 0)}; + if (mScaled) + { + QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y()); + QPoint topLeft = p1; + if (newSize.width() < 0) + { + flipHorz = true; + newSize.rwidth() *= -1; + topLeft.setX(p2.x()); + } + if (newSize.height() < 0) + { + flipVert = true; + newSize.rheight() *= -1; + topLeft.setY(p2.y()); + } + QSize scaledSize = mPixmap.size(); +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED + scaledSize /= mPixmap.devicePixelRatio(); + scaledSize.scale(newSize*mPixmap.devicePixelRatio(), mAspectRatioMode); +#else + scaledSize.scale(newSize, mAspectRatioMode); +#endif + result = QRect(topLeft, scaledSize); + } else + { +#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED + result = QRect(p1, mPixmap.size()/mPixmap.devicePixelRatio()); +#else + result = QRect(p1, mPixmap.size()); +#endif + } + if (flippedHorz) + *flippedHorz = flipHorz; + if (flippedVert) + *flippedVert = flipVert; + return result; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemPixmap::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} +/* end of 'src/items/item-pixmap.cpp' */ + + +/* including file 'src/items/item-tracer.cpp' */ +/* modified 2022-11-06T12:45:56, size 14645 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemTracer +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemTracer + \brief Item that sticks to QCPGraph data points + + \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions." + + The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt + the coordinate axes of the graph and update its \a position to be on the graph's data. This means + the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a + QCPGraph is connected, note that setting the coordinates of the tracer item directly via \a + position will have no effect because they will be overriden in the next redraw (this is when the + coordinate update happens). + + If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will + stay at the corresponding end of the graph. + + With \ref setInterpolating you may specify whether the tracer may only stay exactly on data + points or whether it interpolates data points linearly, if given a key that lies between two data + points of the graph. + + The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer + have no own visual appearance (set the style to \ref tsNone), and just connect other item + positions to the tracer \a position (used as an anchor) via \ref + QCPItemPosition::setParentAnchor. + + \note The tracer position is only automatically updated upon redraws. So when the data of the + graph changes and immediately afterwards (without a redraw) the position coordinates of the + tracer are retrieved, they will not reflect the updated data of the graph. In this case \ref + updatePosition must be called manually, prior to reading the tracer coordinates. +*/ + +/*! + Creates a tracer item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + position(createPosition(QLatin1String("position"))), + mSize(6), + mStyle(tsCrosshair), + mGraph(nullptr), + mGraphKey(0), + mInterpolating(false) +{ + position->setCoords(0, 0); + + setBrush(Qt::NoBrush); + setSelectedBrush(Qt::NoBrush); + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue, 2)); +} + +QCPItemTracer::~QCPItemTracer() +{ +} + +/*! + Sets the pen that will be used to draw the line of the tracer + + \see setSelectedPen, setBrush +*/ +void QCPItemTracer::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the line of the tracer when selected + + \see setPen, setSelected +*/ +void QCPItemTracer::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the brush that will be used to draw any fills of the tracer + + \see setSelectedBrush, setPen +*/ +void QCPItemTracer::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +/*! + Sets the brush that will be used to draw any fills of the tracer, when selected. + + \see setBrush, setSelected +*/ +void QCPItemTracer::setSelectedBrush(const QBrush &brush) +{ + mSelectedBrush = brush; +} + +/*! + Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare + does, \ref tsCrosshair does not). +*/ +void QCPItemTracer::setSize(double size) +{ + mSize = size; +} + +/*! + Sets the style/visual appearance of the tracer. + + If you only want to use the tracer \a position as an anchor for other items, set \a style to + \ref tsNone. +*/ +void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style) +{ + mStyle = style; +} + +/*! + Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type + QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph. + + To free the tracer from any graph, set \a graph to \c nullptr. The tracer \a position can then be + placed freely like any other item position. This is the state the tracer will assume when its + graph gets deleted while still attached to it. + + \see setGraphKey +*/ +void QCPItemTracer::setGraph(QCPGraph *graph) +{ + if (graph) + { + if (graph->parentPlot() == mParentPlot) + { + position->setType(QCPItemPosition::ptPlotCoords); + position->setAxes(graph->keyAxis(), graph->valueAxis()); + mGraph = graph; + updatePosition(); + } else + qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item"; + } else + { + mGraph = nullptr; + } +} + +/*! + Sets the key of the graph's data point the tracer will be positioned at. This is the only free + coordinate of a tracer when attached to a graph. + + Depending on \ref setInterpolating, the tracer will be either positioned on the data point + closest to \a key, or will stay exactly at \a key and interpolate the value linearly. + + \see setGraph, setInterpolating +*/ +void QCPItemTracer::setGraphKey(double key) +{ + mGraphKey = key; +} + +/*! + Sets whether the value of the graph's data points shall be interpolated, when positioning the + tracer. + + If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on + the data point of the graph which is closest to the key, but which is not necessarily exactly + there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and + the appropriate value will be interpolated from the graph's data points linearly. + + \see setGraph, setGraphKey +*/ +void QCPItemTracer::setInterpolating(bool enabled) +{ + mInterpolating = enabled; +} + +/* inherits documentation from base class */ +double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QPointF center(position->pixelPosition()); + double w = mSize/2.0; + QRect clip = clipRect(); + switch (mStyle) + { + case tsNone: return -1; + case tsPlus: + { + if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(center+QPointF(-w, 0), center+QPointF(w, 0)), + QCPVector2D(pos).distanceSquaredToLine(center+QPointF(0, -w), center+QPointF(0, w)))); + break; + } + case tsCrosshair: + { + return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(clip.left(), center.y()), QCPVector2D(clip.right(), center.y())), + QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(center.x(), clip.top()), QCPVector2D(center.x(), clip.bottom())))); + } + case tsCircle: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + { + // distance to border: + double centerDist = QCPVector2D(center-pos).length(); + double circleLine = w; + double result = qAbs(centerDist-circleLine); + // filled ellipse, allow click inside to count as hit: + if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0) + { + if (centerDist <= circleLine) + result = mParentPlot->selectionTolerance()*0.99; + } + return result; + } + break; + } + case tsSquare: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + { + QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w)); + bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0; + return rectDistance(rect, pos, filledRect); + } + break; + } + } + return -1; +} + +/* inherits documentation from base class */ +void QCPItemTracer::draw(QCPPainter *painter) +{ + updatePosition(); + if (mStyle == tsNone) + return; + + painter->setPen(mainPen()); + painter->setBrush(mainBrush()); + QPointF center(position->pixelPosition()); + double w = mSize/2.0; + QRect clip = clipRect(); + switch (mStyle) + { + case tsNone: return; + case tsPlus: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + { + painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0))); + painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w))); + } + break; + } + case tsCrosshair: + { + if (center.y() > clip.top() && center.y() < clip.bottom()) + painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y())); + if (center.x() > clip.left() && center.x() < clip.right()) + painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom())); + break; + } + case tsCircle: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + painter->drawEllipse(center, w, w); + break; + } + case tsSquare: + { + if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect())) + painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w))); + break; + } + } +} + +/*! + If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a + position to reside on the graph data, depending on the configured key (\ref setGraphKey). + + It is called automatically on every redraw and normally doesn't need to be called manually. One + exception is when you want to read the tracer coordinates via \a position and are not sure that + the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw. + In that situation, call this function before accessing \a position, to make sure you don't get + out-of-date coordinates. + + If there is no graph set on this tracer, this function does nothing. +*/ +void QCPItemTracer::updatePosition() +{ + if (mGraph) + { + if (mParentPlot->hasPlottable(mGraph)) + { + if (mGraph->data()->size() > 1) + { + QCPGraphDataContainer::const_iterator first = mGraph->data()->constBegin(); + QCPGraphDataContainer::const_iterator last = mGraph->data()->constEnd()-1; + if (mGraphKey <= first->key) + position->setCoords(first->key, first->value); + else if (mGraphKey >= last->key) + position->setCoords(last->key, last->value); + else + { + QCPGraphDataContainer::const_iterator it = mGraph->data()->findBegin(mGraphKey); + if (it != mGraph->data()->constEnd()) // mGraphKey is not exactly on last iterator, but somewhere between iterators + { + QCPGraphDataContainer::const_iterator prevIt = it; + ++it; // won't advance to constEnd because we handled that case (mGraphKey >= last->key) before + if (mInterpolating) + { + // interpolate between iterators around mGraphKey: + double slope = 0; + if (!qFuzzyCompare(double(it->key), double(prevIt->key))) + slope = (it->value-prevIt->value)/(it->key-prevIt->key); + position->setCoords(mGraphKey, (mGraphKey-prevIt->key)*slope+prevIt->value); + } else + { + // find iterator with key closest to mGraphKey: + if (mGraphKey < (prevIt->key+it->key)*0.5) + position->setCoords(prevIt->key, prevIt->value); + else + position->setCoords(it->key, it->value); + } + } else // mGraphKey is exactly on last iterator (should actually be caught when comparing first/last keys, but this is a failsafe for fp uncertainty) + position->setCoords(it->key, it->value); + } + } else if (mGraph->data()->size() == 1) + { + QCPGraphDataContainer::const_iterator it = mGraph->data()->constBegin(); + position->setCoords(it->key, it->value); + } else + qDebug() << Q_FUNC_INFO << "graph has no data"; + } else + qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)"; + } +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected + and mSelectedPen when it is. +*/ +QPen QCPItemTracer::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} + +/*! \internal + + Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item + is not selected and mSelectedBrush when it is. +*/ +QBrush QCPItemTracer::mainBrush() const +{ + return mSelected ? mSelectedBrush : mBrush; +} +/* end of 'src/items/item-tracer.cpp' */ + + +/* including file 'src/items/item-bracket.cpp' */ +/* modified 2022-11-06T12:45:56, size 10705 */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPItemBracket +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPItemBracket + \brief A bracket for referencing/highlighting certain parts in the plot. + + \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions." + + It has two positions, \a left and \a right, which define the span of the bracket. If \a left is + actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the + example image. + + The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket + stretches away from the embraced span, can be controlled with \ref setLength. + + \image html QCPItemBracket-length.png +
Demonstrating the effect of different values for \ref setLength, for styles \ref + bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.
+ + It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine + or QCPItemCurve) or a text label (QCPItemText), to the bracket. +*/ + +/*! + Creates a bracket item and sets default values. + + The created item is automatically registered with \a parentPlot. This QCustomPlot instance takes + ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead. +*/ +QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) : + QCPAbstractItem(parentPlot), + left(createPosition(QLatin1String("left"))), + right(createPosition(QLatin1String("right"))), + center(createAnchor(QLatin1String("center"), aiCenter)), + mLength(8), + mStyle(bsCalligraphic) +{ + left->setCoords(0, 0); + right->setCoords(1, 1); + + setPen(QPen(Qt::black)); + setSelectedPen(QPen(Qt::blue, 2)); +} + +QCPItemBracket::~QCPItemBracket() +{ +} + +/*! + Sets the pen that will be used to draw the bracket. + + Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the + stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use + \ref setLength, which has a similar effect. + + \see setSelectedPen +*/ +void QCPItemBracket::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + Sets the pen that will be used to draw the bracket when selected + + \see setPen, setSelected +*/ +void QCPItemBracket::setSelectedPen(const QPen &pen) +{ + mSelectedPen = pen; +} + +/*! + Sets the \a length in pixels how far the bracket extends in the direction towards the embraced + span of the bracket (i.e. perpendicular to the left-right-direction) + + \image html QCPItemBracket-length.png +
Demonstrating the effect of different values for \ref setLength, for styles \ref + bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.
+*/ +void QCPItemBracket::setLength(double length) +{ + mLength = length; +} + +/*! + Sets the style of the bracket, i.e. the shape/visual appearance. + + \see setPen +*/ +void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style) +{ + mStyle = style; +} + +/* inherits documentation from base class */ +double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + Q_UNUSED(details) + if (onlySelectable && !mSelectable) + return -1; + + QCPVector2D p(pos); + QCPVector2D leftVec(left->pixelPosition()); + QCPVector2D rightVec(right->pixelPosition()); + if (leftVec.toPoint() == rightVec.toPoint()) + return -1; + + QCPVector2D widthVec = (rightVec-leftVec)*0.5; + QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; + QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; + + switch (mStyle) + { + case QCPItemBracket::bsSquare: + case QCPItemBracket::bsRound: + { + double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec); + double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec); + double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec); + return qSqrt(qMin(qMin(a, b), c)); + } + case QCPItemBracket::bsCurly: + case QCPItemBracket::bsCalligraphic: + { + double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3); + double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15); + double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3); + double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15); + return qSqrt(qMin(qMin(a, b), qMin(c, d))); + } + } + return -1; +} + +/* inherits documentation from base class */ +void QCPItemBracket::draw(QCPPainter *painter) +{ + QCPVector2D leftVec(left->pixelPosition()); + QCPVector2D rightVec(right->pixelPosition()); + if (leftVec.toPoint() == rightVec.toPoint()) + return; + + QCPVector2D widthVec = (rightVec-leftVec)*0.5; + QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; + QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; + + QPolygon boundingPoly; + boundingPoly << leftVec.toPoint() << rightVec.toPoint() + << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint(); + const int clipEnlarge = qCeil(mainPen().widthF()); + QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge); + if (clip.intersects(boundingPoly.boundingRect())) + { + painter->setPen(mainPen()); + switch (mStyle) + { + case bsSquare: + { + painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF()); + painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF()); + painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + break; + } + case bsRound: + { + painter->setBrush(Qt::NoBrush); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + painter->drawPath(path); + break; + } + case bsCurly: + { + painter->setBrush(Qt::NoBrush); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + painter->drawPath(path); + break; + } + case bsCalligraphic: + { + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(mainPen().color())); + QPainterPath path; + path.moveTo((centerVec+widthVec+lengthVec).toPointF()); + + path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF()); + path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF()); + + path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF()); + path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF()); + + painter->drawPath(path); + break; + } + } + } +} + +/* inherits documentation from base class */ +QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const +{ + QCPVector2D leftVec(left->pixelPosition()); + QCPVector2D rightVec(right->pixelPosition()); + if (leftVec.toPoint() == rightVec.toPoint()) + return leftVec.toPointF(); + + QCPVector2D widthVec = (rightVec-leftVec)*0.5; + QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength; + QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec; + + switch (anchorId) + { + case aiCenter: + return centerVec.toPointF(); + } + qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId; + return {}; +} + +/*! \internal + + Returns the pen that should be used for drawing lines. Returns mPen when the + item is not selected and mSelectedPen when it is. +*/ +QPen QCPItemBracket::mainPen() const +{ + return mSelected ? mSelectedPen : mPen; +} +/* end of 'src/items/item-bracket.cpp' */ + + +/* including file 'src/polar/radialaxis.cpp' */ +/* modified 2022-11-06T12:45:57, size 49415 */ + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarAxisRadial +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarAxisRadial + \brief The radial axis inside a radial plot + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. + + Each axis holds an instance of QCPAxisTicker which is used to generate the tick coordinates and + tick labels. You can access the currently installed \ref ticker or set a new one (possibly one of + the specialized subclasses, or your own subclass) via \ref setTicker. For details, see the + documentation of QCPAxisTicker. +*/ + +/* start of documentation of inline functions */ + +/*! \fn QSharedPointer QCPPolarAxisRadial::ticker() const + + Returns a modifiable shared pointer to the currently installed axis ticker. The axis ticker is + responsible for generating the tick positions and tick labels of this axis. You can access the + \ref QCPAxisTicker with this method and modify basic properties such as the approximate tick count + (\ref QCPAxisTicker::setTickCount). + + You can gain more control over the axis ticks by setting a different \ref QCPAxisTicker subclass, see + the documentation there. A new axis ticker can be set with \ref setTicker. + + Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis + ticker simply by passing the same shared pointer to multiple axes. + + \see setTicker +*/ + +/* end of documentation of inline functions */ +/* start of documentation of signals */ + +/*! \fn void QCPPolarAxisRadial::rangeChanged(const QCPRange &newRange) + + This signal is emitted when the range of this axis has changed. You can connect it to the \ref + setRange slot of another axis to communicate the new range to the other axis, in order for it to + be synchronized. + + You may also manipulate/correct the range with \ref setRange in a slot connected to this signal. + This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper + range shouldn't go beyond certain values (see \ref QCPRange::bounded). For example, the following + slot would limit the x axis to ranges between 0 and 10: + \code + customPlot->xAxis->setRange(newRange.bounded(0, 10)) + \endcode +*/ + +/*! \fn void QCPPolarAxisRadial::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange) + \overload + + Additionally to the new range, this signal also provides the previous range held by the axis as + \a oldRange. +*/ + +/*! \fn void QCPPolarAxisRadial::scaleTypeChanged(QCPPolarAxisRadial::ScaleType scaleType); + + This signal is emitted when the scale type changes, by calls to \ref setScaleType +*/ + +/*! \fn void QCPPolarAxisRadial::selectionChanged(QCPPolarAxisRadial::SelectableParts selection) + + This signal is emitted when the selection state of this axis has changed, either by user interaction + or by a direct call to \ref setSelectedParts. +*/ + +/*! \fn void QCPPolarAxisRadial::selectableChanged(const QCPPolarAxisRadial::SelectableParts &parts); + + This signal is emitted when the selectability changes, by calls to \ref setSelectableParts +*/ + +/* end of documentation of signals */ + +/*! + Constructs an Axis instance of Type \a type for the axis rect \a parent. + + Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create + them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however, + create them manually and then inject them also via \ref QCPAxisRect::addAxis. +*/ +QCPPolarAxisRadial::QCPPolarAxisRadial(QCPPolarAxisAngular *parent) : + QCPLayerable(parent->parentPlot(), QString(), parent), + mRangeDrag(true), + mRangeZoom(true), + mRangeZoomFactor(0.85), + // axis base: + mAngularAxis(parent), + mAngle(45), + mAngleReference(arAngularAxis), + mSelectableParts(spAxis | spTickLabels | spAxisLabel), + mSelectedParts(spNone), + mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedBasePen(QPen(Qt::blue, 2)), + // axis label: + mLabelPadding(0), + mLabel(), + mLabelFont(mParentPlot->font()), + mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)), + mLabelColor(Qt::black), + mSelectedLabelColor(Qt::blue), + // tick labels: + // mTickLabelPadding(0), in label painter + mTickLabels(true), + // mTickLabelRotation(0), in label painter + mTickLabelFont(mParentPlot->font()), + mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)), + mTickLabelColor(Qt::black), + mSelectedTickLabelColor(Qt::blue), + mNumberPrecision(6), + mNumberFormatChar('g'), + mNumberBeautifulPowers(true), + mNumberMultiplyCross(false), + // ticks and subticks: + mTicks(true), + mSubTicks(true), + mTickLengthIn(5), + mTickLengthOut(0), + mSubTickLengthIn(2), + mSubTickLengthOut(0), + mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedTickPen(QPen(Qt::blue, 2)), + mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedSubTickPen(QPen(Qt::blue, 2)), + // scale and range: + mRange(0, 5), + mRangeReversed(false), + mScaleType(stLinear), + // internal members: + mRadius(1), // non-zero initial value, will be overwritten in ::update() according to inner rect + mTicker(new QCPAxisTicker), + mLabelPainter(mParentPlot) +{ + setParent(parent); + setAntialiased(true); + + setTickLabelPadding(5); + setTickLabelRotation(0); + setTickLabelMode(lmUpright); + mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artTangent); + mLabelPainter.setAbbreviateDecimalPowers(false); +} + +QCPPolarAxisRadial::~QCPPolarAxisRadial() +{ +} + +QCPPolarAxisRadial::LabelMode QCPPolarAxisRadial::tickLabelMode() const +{ + switch (mLabelPainter.anchorMode()) + { + case QCPLabelPainterPrivate::amSkewedUpright: return lmUpright; + case QCPLabelPainterPrivate::amSkewedRotated: return lmRotated; + default: qDebug() << Q_FUNC_INFO << "invalid mode for polar axis"; break; + } + return lmUpright; +} + +/* No documentation as it is a property getter */ +QString QCPPolarAxisRadial::numberFormat() const +{ + QString result; + result.append(mNumberFormatChar); + if (mNumberBeautifulPowers) + { + result.append(QLatin1Char('b')); + if (mNumberMultiplyCross) + result.append(QLatin1Char('c')); + } + return result; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::tickLengthIn() const +{ + return mTickLengthIn; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::tickLengthOut() const +{ + return mTickLengthOut; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::subTickLengthIn() const +{ + return mSubTickLengthIn; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::subTickLengthOut() const +{ + return mSubTickLengthOut; +} + +/* No documentation as it is a property getter */ +int QCPPolarAxisRadial::labelPadding() const +{ + return mLabelPadding; +} + +void QCPPolarAxisRadial::setRangeDrag(bool enabled) +{ + mRangeDrag = enabled; +} + +void QCPPolarAxisRadial::setRangeZoom(bool enabled) +{ + mRangeZoom = enabled; +} + +void QCPPolarAxisRadial::setRangeZoomFactor(double factor) +{ + mRangeZoomFactor = factor; +} + +/*! + Sets whether the axis uses a linear scale or a logarithmic scale. + + Note that this method controls the coordinate transformation. For logarithmic scales, you will + likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting + the axis ticker to an instance of \ref QCPAxisTickerLog : + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation + + See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick + creation. + + \ref setNumberPrecision +*/ +void QCPPolarAxisRadial::setScaleType(QCPPolarAxisRadial::ScaleType type) +{ + if (mScaleType != type) + { + mScaleType = type; + if (mScaleType == stLogarithmic) + setRange(mRange.sanitizedForLogScale()); + //mCachedMarginValid = false; + emit scaleTypeChanged(mScaleType); + } +} + +/*! + Sets the range of the axis. + + This slot may be connected with the \ref rangeChanged signal of another axis so this axis + is always synchronized with the other axis range, when it changes. + + To invert the direction of an axis, use \ref setRangeReversed. +*/ +void QCPPolarAxisRadial::setRange(const QCPRange &range) +{ + if (range.lower == mRange.lower && range.upper == mRange.upper) + return; + + if (!QCPRange::validRange(range)) return; + QCPRange oldRange = mRange; + if (mScaleType == stLogarithmic) + { + mRange = range.sanitizedForLogScale(); + } else + { + mRange = range.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains iSelectAxes.) + + However, even when \a selectable is set to a value not allowing the selection of a specific part, + it is still possible to set the selection of this part manually, by calling \ref setSelectedParts + directly. + + \see SelectablePart, setSelectedParts +*/ +void QCPPolarAxisRadial::setSelectableParts(const SelectableParts &selectable) +{ + if (mSelectableParts != selectable) + { + mSelectableParts = selectable; + emit selectableChanged(mSelectableParts); + } +} + +/*! + Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part + is selected, it uses a different pen/font. + + The entire selection mechanism for axes is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you + wish to change the selection state manually. + + This function can change the selection state of a part, independent of the \ref setSelectableParts setting. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen, + setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor +*/ +void QCPPolarAxisRadial::setSelectedParts(const SelectableParts &selected) +{ + if (mSelectedParts != selected) + { + mSelectedParts = selected; + emit selectionChanged(mSelectedParts); + } +} + +/*! + \overload + + Sets the lower and upper bound of the axis range. + + To invert the direction of an axis, use \ref setRangeReversed. + + There is also a slot to set a range, see \ref setRange(const QCPRange &range). +*/ +void QCPPolarAxisRadial::setRange(double lower, double upper) +{ + if (lower == mRange.lower && upper == mRange.upper) + return; + + if (!QCPRange::validRange(lower, upper)) return; + QCPRange oldRange = mRange; + mRange.lower = lower; + mRange.upper = upper; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + \overload + + Sets the range of the axis. + + The \a position coordinate indicates together with the \a alignment parameter, where the new + range will be positioned. \a size defines the size of the new axis range. \a alignment may be + Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border, + or center of the range to be aligned with \a position. Any other values of \a alignment will + default to Qt::AlignCenter. +*/ +void QCPPolarAxisRadial::setRange(double position, double size, Qt::AlignmentFlag alignment) +{ + if (alignment == Qt::AlignLeft) + setRange(position, position+size); + else if (alignment == Qt::AlignRight) + setRange(position-size, position); + else // alignment == Qt::AlignCenter + setRange(position-size/2.0, position+size/2.0); +} + +/*! + Sets the lower bound of the axis range. The upper bound is not changed. + \see setRange +*/ +void QCPPolarAxisRadial::setRangeLower(double lower) +{ + if (mRange.lower == lower) + return; + + QCPRange oldRange = mRange; + mRange.lower = lower; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets the upper bound of the axis range. The lower bound is not changed. + \see setRange +*/ +void QCPPolarAxisRadial::setRangeUpper(double upper) +{ + if (mRange.upper == upper) + return; + + QCPRange oldRange = mRange; + mRange.upper = upper; + if (mScaleType == stLogarithmic) + { + mRange = mRange.sanitizedForLogScale(); + } else + { + mRange = mRange.sanitizedForLinScale(); + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal + axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the + direction of increasing values is inverted. + + Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part + of the \ref setRange interface will still reference the mathematically smaller number than the \a + upper part. +*/ +void QCPPolarAxisRadial::setRangeReversed(bool reversed) +{ + mRangeReversed = reversed; +} + +void QCPPolarAxisRadial::setAngle(double degrees) +{ + mAngle = degrees; +} + +void QCPPolarAxisRadial::setAngleReference(AngleReference reference) +{ + mAngleReference = reference; +} + +/*! + The axis ticker is responsible for generating the tick positions and tick labels. See the + documentation of QCPAxisTicker for details on how to work with axis tickers. + + You can change the tick positioning/labeling behaviour of this axis by setting a different + QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis + ticker, access it via \ref ticker. + + Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis + ticker simply by passing the same shared pointer to multiple axes. + + \see ticker +*/ +void QCPPolarAxisRadial::setTicker(QSharedPointer ticker) +{ + if (ticker) + mTicker = ticker; + else + qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker"; + // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector +} + +/*! + Sets whether tick marks are displayed. + + Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve + that, see \ref setTickLabels. + + \see setSubTicks +*/ +void QCPPolarAxisRadial::setTicks(bool show) +{ + if (mTicks != show) + { + mTicks = show; + //mCachedMarginValid = false; + } +} + +/*! + Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks. +*/ +void QCPPolarAxisRadial::setTickLabels(bool show) +{ + if (mTickLabels != show) + { + mTickLabels = show; + //mCachedMarginValid = false; + if (!mTickLabels) + mTickVectorLabels.clear(); + } +} + +/*! + Sets the distance between the axis base line (including any outward ticks) and the tick labels. + \see setLabelPadding, setPadding +*/ +void QCPPolarAxisRadial::setTickLabelPadding(int padding) +{ + mLabelPainter.setPadding(padding); +} + +/*! + Sets the font of the tick labels. + + \see setTickLabels, setTickLabelColor +*/ +void QCPPolarAxisRadial::setTickLabelFont(const QFont &font) +{ + if (font != mTickLabelFont) + { + mTickLabelFont = font; + //mCachedMarginValid = false; + } +} + +/*! + Sets the color of the tick labels. + + \see setTickLabels, setTickLabelFont +*/ +void QCPPolarAxisRadial::setTickLabelColor(const QColor &color) +{ + mTickLabelColor = color; +} + +/*! + Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else, + the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values + from -90 to 90 degrees. + + If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For + other angles, the label is drawn with an offset such that it seems to point toward or away from + the tick mark. +*/ +void QCPPolarAxisRadial::setTickLabelRotation(double degrees) +{ + mLabelPainter.setRotation(degrees); +} + +void QCPPolarAxisRadial::setTickLabelMode(LabelMode mode) +{ + switch (mode) + { + case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break; + case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break; + } +} + +/*! + Sets the number format for the numbers in tick labels. This \a formatCode is an extended version + of the format code used e.g. by QString::number() and QLocale::toString(). For reference about + that, see the "Argument Formats" section in the detailed description of the QString class. + + \a formatCode is a string of one, two or three characters. The first character is identical to + the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed + format, 'g'/'G' scientific or fixed, whichever is shorter. + + The second and third characters are optional and specific to QCustomPlot:\n + If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g. + "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for + "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5 + [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot. + If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can + be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the + cross and 183 (0xB7) for the dot. + + Examples for \a formatCode: + \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large, + normal scientific format is used + \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with + beautifully typeset decimal powers and a dot as multiplication sign + \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as + multiplication sign + \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal + powers. Format code will be reduced to 'f'. + \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format + code will not be changed. +*/ +void QCPPolarAxisRadial::setNumberFormat(const QString &formatCode) +{ + if (formatCode.isEmpty()) + { + qDebug() << Q_FUNC_INFO << "Passed formatCode is empty"; + return; + } + //mCachedMarginValid = false; + + // interpret first char as number format char: + QString allowedFormatChars(QLatin1String("eEfgG")); + if (allowedFormatChars.contains(formatCode.at(0))) + { + mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1()); + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode; + return; + } + + if (formatCode.length() < 2) + { + mNumberBeautifulPowers = false; + mNumberMultiplyCross = false; + } else + { + // interpret second char as indicator for beautiful decimal powers: + if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g'))) + mNumberBeautifulPowers = true; + else + qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode; + + if (formatCode.length() < 3) + { + mNumberMultiplyCross = false; + } else + { + // interpret third char as indicator for dot or cross multiplication symbol: + if (formatCode.at(2) == QLatin1Char('c')) + mNumberMultiplyCross = true; + else if (formatCode.at(2) == QLatin1Char('d')) + mNumberMultiplyCross = false; + else + qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode; + } + } + mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers); + mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot); +} + +/*! + Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec) + for details. The effect of precisions are most notably for number Formats starting with 'e', see + \ref setNumberFormat +*/ +void QCPPolarAxisRadial::setNumberPrecision(int precision) +{ + if (mNumberPrecision != precision) + { + mNumberPrecision = precision; + //mCachedMarginValid = false; + } +} + +/*! + Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the + plot and \a outside is the length they will reach outside the plot. If \a outside is greater than + zero, the tick labels and axis label will increase their distance to the axis accordingly, so + they won't collide with the ticks. + + \see setSubTickLength, setTickLengthIn, setTickLengthOut +*/ +void QCPPolarAxisRadial::setTickLength(int inside, int outside) +{ + setTickLengthIn(inside); + setTickLengthOut(outside); +} + +/*! + Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach + inside the plot. + + \see setTickLengthOut, setTickLength, setSubTickLength +*/ +void QCPPolarAxisRadial::setTickLengthIn(int inside) +{ + if (mTickLengthIn != inside) + { + mTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach + outside the plot. If \a outside is greater than zero, the tick labels and axis label will + increase their distance to the axis accordingly, so they won't collide with the ticks. + + \see setTickLengthIn, setTickLength, setSubTickLength +*/ +void QCPPolarAxisRadial::setTickLengthOut(int outside) +{ + if (mTickLengthOut != outside) + { + mTickLengthOut = outside; + //mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets whether sub tick marks are displayed. + + Sub ticks are only potentially visible if (major) ticks are also visible (see \ref setTicks) + + \see setTicks +*/ +void QCPPolarAxisRadial::setSubTicks(bool show) +{ + if (mSubTicks != show) + { + mSubTicks = show; + //mCachedMarginValid = false; + } +} + +/*! + Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside + the plot and \a outside is the length they will reach outside the plot. If \a outside is greater + than zero, the tick labels and axis label will increase their distance to the axis accordingly, + so they won't collide with the ticks. + + \see setTickLength, setSubTickLengthIn, setSubTickLengthOut +*/ +void QCPPolarAxisRadial::setSubTickLength(int inside, int outside) +{ + setSubTickLengthIn(inside); + setSubTickLengthOut(outside); +} + +/*! + Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside + the plot. + + \see setSubTickLengthOut, setSubTickLength, setTickLength +*/ +void QCPPolarAxisRadial::setSubTickLengthIn(int inside) +{ + if (mSubTickLengthIn != inside) + { + mSubTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach + outside the plot. If \a outside is greater than zero, the tick labels will increase their + distance to the axis accordingly, so they won't collide with the ticks. + + \see setSubTickLengthIn, setSubTickLength, setTickLength +*/ +void QCPPolarAxisRadial::setSubTickLengthOut(int outside) +{ + if (mSubTickLengthOut != outside) + { + mSubTickLengthOut = outside; + //mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets the pen, the axis base line is drawn with. + + \see setTickPen, setSubTickPen +*/ +void QCPPolarAxisRadial::setBasePen(const QPen &pen) +{ + mBasePen = pen; +} + +/*! + Sets the pen, tick marks will be drawn with. + + \see setTickLength, setBasePen +*/ +void QCPPolarAxisRadial::setTickPen(const QPen &pen) +{ + mTickPen = pen; +} + +/*! + Sets the pen, subtick marks will be drawn with. + + \see setSubTickCount, setSubTickLength, setBasePen +*/ +void QCPPolarAxisRadial::setSubTickPen(const QPen &pen) +{ + mSubTickPen = pen; +} + +/*! + Sets the font of the axis label. + + \see setLabelColor +*/ +void QCPPolarAxisRadial::setLabelFont(const QFont &font) +{ + if (mLabelFont != font) + { + mLabelFont = font; + //mCachedMarginValid = false; + } +} + +/*! + Sets the color of the axis label. + + \see setLabelFont +*/ +void QCPPolarAxisRadial::setLabelColor(const QColor &color) +{ + mLabelColor = color; +} + +/*! + Sets the text of the axis label that will be shown below/above or next to the axis, depending on + its orientation. To disable axis labels, pass an empty string as \a str. +*/ +void QCPPolarAxisRadial::setLabel(const QString &str) +{ + if (mLabel != str) + { + mLabel = str; + //mCachedMarginValid = false; + } +} + +/*! + Sets the distance between the tick labels and the axis label. + + \see setTickLabelPadding, setPadding +*/ +void QCPPolarAxisRadial::setLabelPadding(int padding) +{ + if (mLabelPadding != padding) + { + mLabelPadding = padding; + //mCachedMarginValid = false; + } +} + +/*! + Sets the font that is used for tick labels when they are selected. + + \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedTickLabelFont(const QFont &font) +{ + if (font != mSelectedTickLabelFont) + { + mSelectedTickLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts + } +} + +/*! + Sets the font that is used for the axis label when it is selected. + + \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedLabelFont(const QFont &font) +{ + mSelectedLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts +} + +/*! + Sets the color that is used for tick labels when they are selected. + + \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedTickLabelColor(const QColor &color) +{ + if (color != mSelectedTickLabelColor) + { + mSelectedTickLabelColor = color; + } +} + +/*! + Sets the color that is used for the axis label when it is selected. + + \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedLabelColor(const QColor &color) +{ + mSelectedLabelColor = color; +} + +/*! + Sets the pen that is used to draw the axis base line when selected. + + \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedBasePen(const QPen &pen) +{ + mSelectedBasePen = pen; +} + +/*! + Sets the pen that is used to draw the (major) ticks when selected. + + \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedTickPen(const QPen &pen) +{ + mSelectedTickPen = pen; +} + +/*! + Sets the pen that is used to draw the subticks when selected. + + \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisRadial::setSelectedSubTickPen(const QPen &pen) +{ + mSelectedSubTickPen = pen; +} + +/*! + If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper + bounds of the range. The range is simply moved by \a diff. + + If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This + corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff). +*/ +void QCPPolarAxisRadial::moveRange(double diff) +{ + QCPRange oldRange = mRange; + if (mScaleType == stLinear) + { + mRange.lower += diff; + mRange.upper += diff; + } else // mScaleType == stLogarithmic + { + mRange.lower *= diff; + mRange.upper *= diff; + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Scales the range of this axis by \a factor around the center of the current axis range. For + example, if \a factor is 2.0, then the axis range will double its size, and the point at the axis + range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around + the center will have moved symmetrically closer). + + If you wish to scale around a different coordinate than the current axis range center, use the + overload \ref scaleRange(double factor, double center). +*/ +void QCPPolarAxisRadial::scaleRange(double factor) +{ + scaleRange(factor, range().center()); +} + +/*! \overload + + Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a + factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at + coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates + around 1.0 will have moved symmetrically closer to 1.0). + + \see scaleRange(double factor) +*/ +void QCPPolarAxisRadial::scaleRange(double factor, double center) +{ + QCPRange oldRange = mRange; + if (mScaleType == stLinear) + { + QCPRange newRange; + newRange.lower = (mRange.lower-center)*factor + center; + newRange.upper = (mRange.upper-center)*factor + center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLinScale(); + } else // mScaleType == stLogarithmic + { + if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range + { + QCPRange newRange; + newRange.lower = qPow(mRange.lower/center, factor)*center; + newRange.upper = qPow(mRange.upper/center, factor)*center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLogScale(); + } else + qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center; + } + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Changes the axis range such that all plottables associated with this axis are fully visible in + that dimension. + + \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes +*/ +void QCPPolarAxisRadial::rescale(bool onlyVisiblePlottables) +{ + Q_UNUSED(onlyVisiblePlottables) + /* TODO + QList p = plottables(); + QCPRange newRange; + bool haveRange = false; + for (int i=0; irealVisibility() && onlyVisiblePlottables) + continue; + QCPRange plottableRange; + bool currentFoundRange; + QCP::SignDomain signDomain = QCP::sdBoth; + if (mScaleType == stLogarithmic) + signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive); + if (p.at(i)->keyAxis() == this) + plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain); + else + plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain); + if (currentFoundRange) + { + if (!haveRange) + newRange = plottableRange; + else + newRange.expand(plottableRange); + haveRange = true; + } + } + if (haveRange) + { + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (mScaleType == stLinear) + { + newRange.lower = center-mRange.size()/2.0; + newRange.upper = center+mRange.size()/2.0; + } else // mScaleType == stLogarithmic + { + newRange.lower = center/qSqrt(mRange.upper/mRange.lower); + newRange.upper = center*qSqrt(mRange.upper/mRange.lower); + } + } + setRange(newRange); + } + */ +} + +/*! + Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates. +*/ +void QCPPolarAxisRadial::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const +{ + QCPVector2D posVector(pixelPos-mCenter); + radiusCoord = radiusToCoord(posVector.length()); + angleCoord = mAngularAxis->angleRadToCoord(posVector.angle()); +} + +/*! + Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget. +*/ +QPointF QCPPolarAxisRadial::coordToPixel(double angleCoord, double radiusCoord) const +{ + const double radiusPixel = coordToRadius(radiusCoord); + const double angleRad = mAngularAxis->coordToAngleRad(angleCoord); + return QPointF(mCenter.x()+qCos(angleRad)*radiusPixel, mCenter.y()+qSin(angleRad)*radiusPixel); +} + +double QCPPolarAxisRadial::coordToRadius(double coord) const +{ + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (coord-mRange.lower)/mRange.size()*mRadius; + else + return (mRange.upper-coord)/mRange.size()*mRadius; + } else // mScaleType == stLogarithmic + { + if (coord >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just return outside visible range + return !mRangeReversed ? mRadius+200 : mRadius-200; + else if (coord <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just return outside visible range + return !mRangeReversed ? mRadius-200 :mRadius+200; + else + { + if (!mRangeReversed) + return qLn(coord/mRange.lower)/qLn(mRange.upper/mRange.lower)*mRadius; + else + return qLn(mRange.upper/coord)/qLn(mRange.upper/mRange.lower)*mRadius; + } + } +} + +double QCPPolarAxisRadial::radiusToCoord(double radius) const +{ + if (mScaleType == stLinear) + { + if (!mRangeReversed) + return (radius)/mRadius*mRange.size()+mRange.lower; + else + return -(radius)/mRadius*mRange.size()+mRange.upper; + } else // mScaleType == stLogarithmic + { + if (!mRangeReversed) + return qPow(mRange.upper/mRange.lower, (radius)/mRadius)*mRange.lower; + else + return qPow(mRange.upper/mRange.lower, (-radius)/mRadius)*mRange.upper; + } +} + + +/*! + Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function + is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this + function does not change the current selection state of the axis. + + If the axis is not visible (\ref setVisible), this function always returns \ref spNone. + + \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions +*/ +QCPPolarAxisRadial::SelectablePart QCPPolarAxisRadial::getPartAt(const QPointF &pos) const +{ + Q_UNUSED(pos) // TODO remove later + if (!mVisible) + return spNone; + + /* + TODO: + if (mAxisPainter->axisSelectionBox().contains(pos.toPoint())) + return spAxis; + else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint())) + return spTickLabels; + else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint())) + return spAxisLabel; + else + return spNone; + */ + return spNone; +} + +/* inherits documentation from base class */ +double QCPPolarAxisRadial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if (!mParentPlot) return -1; + SelectablePart part = getPartAt(pos); + if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone) + return -1; + + if (details) + details->setValue(part); + return mParentPlot->selectionTolerance()*0.99; +} + +/* inherits documentation from base class */ +void QCPPolarAxisRadial::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + SelectablePart part = details.value(); + if (mSelectableParts.testFlag(part)) + { + SelectableParts selBefore = mSelectedParts; + setSelectedParts(additive ? mSelectedParts^part : part); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; + } +} + +/* inherits documentation from base class */ +void QCPPolarAxisRadial::deselectEvent(bool *selectionStateChanged) +{ + SelectableParts selBefore = mSelectedParts; + setSelectedParts(mSelectedParts & ~mSelectableParts); + if (selectionStateChanged) + *selectionStateChanged = mSelectedParts != selBefore; +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + For the axis to accept this event and perform the single axis drag, the parent \ref QCPAxisRect + must be configured accordingly, i.e. it must allow range dragging in the orientation of this axis + (\ref QCPAxisRect::setRangeDrag) and this axis must be a draggable axis (\ref + QCPAxisRect::setRangeDragAxes) + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. +*/ +void QCPPolarAxisRadial::mousePressEvent(QMouseEvent *event, const QVariant &details) +{ + Q_UNUSED(details) + if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + event->ignore(); + return; + } + + if (event->buttons() & Qt::LeftButton) + { + mDragging = true; + // initialize antialiasing backup in case we start dragging: + if (mParentPlot->noAntialiasingOnDrag()) + { + mAADragBackup = mParentPlot->antialiasedElements(); + mNotAADragBackup = mParentPlot->notAntialiasedElements(); + } + // Mouse range dragging interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + mDragStartRange = mRange; + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. + + \see QCPAxis::mousePressEvent +*/ +void QCPPolarAxisRadial::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(event) // TODO remove later + Q_UNUSED(startPos) // TODO remove later + if (mDragging) + { + /* TODO + const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y(); + const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y(); + if (mScaleType == QCPPolarAxisRadial::stLinear) + { + const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel); + setRange(mDragStartRange.lower+diff, mDragStartRange.upper+diff); + } else if (mScaleType == QCPPolarAxisRadial::stLogarithmic) + { + const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel); + setRange(mDragStartRange.lower*diff, mDragStartRange.upper*diff); + } + */ + + if (mParentPlot->noAntialiasingOnDrag()) + mParentPlot->setNotAntialiasedElements(QCP::aeAll); + mParentPlot->replot(QCustomPlot::rpQueuedReplot); + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user drag individual axes + exclusively, by startig the drag on top of the axis. + + \seebaseclassmethod + + \note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis + rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::mousePressEvent. + + \see QCPAxis::mousePressEvent +*/ +void QCPPolarAxisRadial::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(event) + Q_UNUSED(startPos) + mDragging = false; + if (mParentPlot->noAntialiasingOnDrag()) + { + mParentPlot->setAntialiasedElements(mAADragBackup); + mParentPlot->setNotAntialiasedElements(mNotAADragBackup); + } +} + +/*! \internal + + This mouse event reimplementation provides the functionality to let the user zoom individual axes + exclusively, by performing the wheel event on top of the axis. + + For the axis to accept this event and perform the single axis zoom, the parent \ref QCPAxisRect + must be configured accordingly, i.e. it must allow range zooming in the orientation of this axis + (\ref QCPAxisRect::setRangeZoom) and this axis must be a zoomable axis (\ref + QCPAxisRect::setRangeZoomAxes) + + \seebaseclassmethod + + \note The zooming of possibly multiple axes at once by performing the wheel event anywhere in the + axis rect is handled by the axis rect's mouse event, e.g. \ref QCPAxisRect::wheelEvent. +*/ +void QCPPolarAxisRadial::wheelEvent(QWheelEvent *event) +{ + // Mouse range zooming interaction: + if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom)) + { + event->ignore(); + return; + } + + // TODO: + //const double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually + //const double factor = qPow(mRangeZoomFactor, wheelSteps); + //scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y())); + mParentPlot->replot(); +} + +void QCPPolarAxisRadial::updateGeometry(const QPointF ¢er, double radius) +{ + mCenter = center; + mRadius = radius; + if (mRadius < 1) mRadius = 1; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing axis lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \seebaseclassmethod + + \see setAntialiased +*/ +void QCPPolarAxisRadial::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes); +} + +/*! \internal + + Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance. + + \seebaseclassmethod +*/ +void QCPPolarAxisRadial::draw(QCPPainter *painter) +{ + const double axisAngleRad = (mAngle+(mAngleReference==arAngularAxis ? mAngularAxis->angle() : 0))/180.0*M_PI; + const QPointF axisVector(qCos(axisAngleRad), qSin(axisAngleRad)); // semantically should be QCPVector2D, but we save time in loops when we keep it as QPointF + const QPointF tickNormal = QCPVector2D(axisVector).perpendicular().toPointF(); // semantically should be QCPVector2D, but we save time in loops when we keep it as QPointF + + // draw baseline: + painter->setPen(getBasePen()); + painter->drawLine(QLineF(mCenter, mCenter+axisVector*(mRadius-0.5))); + + // draw subticks: + if (!mSubTickVector.isEmpty()) + { + painter->setPen(getSubTickPen()); + for (int i=0; idrawLine(QLineF(tickPosition-tickNormal*mSubTickLengthIn, tickPosition+tickNormal*mSubTickLengthOut)); + } + } + + // draw ticks and labels: + if (!mTickVector.isEmpty()) + { + mLabelPainter.setAnchorReference(mCenter-axisVector); // subtract (normalized) axisVector, just to prevent degenerate tangents for tick label at exact lower axis range + mLabelPainter.setFont(getTickLabelFont()); + mLabelPainter.setColor(getTickLabelColor()); + const QPen ticksPen = getTickPen(); + painter->setPen(ticksPen); + for (int i=0; idrawLine(QLineF(tickPosition-tickNormal*mTickLengthIn, tickPosition+tickNormal*mTickLengthOut)); + // possibly draw tick labels: + if (!mTickVectorLabels.isEmpty()) + { + if ((!mRangeReversed && (i < mTickVectorLabels.count()-1 || mRadius-r > 10)) || + (mRangeReversed && (i > 0 || mRadius-r > 10))) // skip last label if it's closer than 10 pixels to angular axis + mLabelPainter.drawTickLabel(painter, tickPosition+tickNormal*mSubTickLengthOut, mTickVectorLabels.at(i)); + } + } + } +} + +/*! \internal + + Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling + QCPAxisTicker::generate on the currently installed ticker. + + If a change in the label text/count is detected, the cached axis margin is invalidated to make + sure the next margin calculation recalculates the label sizes and returns an up-to-date value. +*/ +void QCPPolarAxisRadial::setupTickVectors() +{ + if (!mParentPlot) return; + if ((!mTicks && !mTickLabels) || mRange.size() <= 0) return; + + mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0); +} + +/*! \internal + + Returns the pen that is used to draw the axis base line. Depending on the selection state, this + is either mSelectedBasePen or mBasePen. +*/ +QPen QCPPolarAxisRadial::getBasePen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen; +} + +/*! \internal + + Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this + is either mSelectedTickPen or mTickPen. +*/ +QPen QCPPolarAxisRadial::getTickPen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen; +} + +/*! \internal + + Returns the pen that is used to draw the subticks. Depending on the selection state, this + is either mSelectedSubTickPen or mSubTickPen. +*/ +QPen QCPPolarAxisRadial::getSubTickPen() const +{ + return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen; +} + +/*! \internal + + Returns the font that is used to draw the tick labels. Depending on the selection state, this + is either mSelectedTickLabelFont or mTickLabelFont. +*/ +QFont QCPPolarAxisRadial::getTickLabelFont() const +{ + return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont; +} + +/*! \internal + + Returns the font that is used to draw the axis label. Depending on the selection state, this + is either mSelectedLabelFont or mLabelFont. +*/ +QFont QCPPolarAxisRadial::getLabelFont() const +{ + return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont; +} + +/*! \internal + + Returns the color that is used to draw the tick labels. Depending on the selection state, this + is either mSelectedTickLabelColor or mTickLabelColor. +*/ +QColor QCPPolarAxisRadial::getTickLabelColor() const +{ + return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor; +} + +/*! \internal + + Returns the color that is used to draw the axis label. Depending on the selection state, this + is either mSelectedLabelColor or mLabelColor. +*/ +QColor QCPPolarAxisRadial::getLabelColor() const +{ + return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor; +} + + +/* inherits documentation from base class */ +QCP::Interaction QCPPolarAxisRadial::selectionCategory() const +{ + return QCP::iSelectAxes; +} +/* end of 'src/polar/radialaxis.cpp' */ + + +/* including file 'src/polar/layoutelement-angularaxis.cpp' */ +/* modified 2022-11-06T12:45:57, size 57266 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarAxisAngular +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarAxisAngular + \brief The main container for polar plots, representing the angular axis as a circle + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPLayoutInset *QCPPolarAxisAngular::insetLayout() const + + Returns the inset layout of this axis rect. It can be used to place other layout elements (or + even layouts with multiple other elements) inside/on top of an axis rect. + + \see QCPLayoutInset +*/ + +/*! \fn int QCPPolarAxisAngular::left() const + + Returns the pixel position of the left border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::right() const + + Returns the pixel position of the right border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::top() const + + Returns the pixel position of the top border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::bottom() const + + Returns the pixel position of the bottom border of this axis rect. Margins are not taken into + account here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::width() const + + Returns the pixel width of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn int QCPPolarAxisAngular::height() const + + Returns the pixel height of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QSize QCPPolarAxisAngular::size() const + + Returns the pixel size of this axis rect. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::topLeft() const + + Returns the top left corner of this axis rect in pixels. Margins are not taken into account here, + so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::topRight() const + + Returns the top right corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::bottomLeft() const + + Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::bottomRight() const + + Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account + here, so the returned value is with respect to the inner \ref rect. +*/ + +/*! \fn QPoint QCPPolarAxisAngular::center() const + + Returns the center of this axis rect in pixels. Margins are not taken into account here, so the + returned value is with respect to the inner \ref rect. +*/ + +/* end documentation of inline functions */ + +/*! + Creates a QCPPolarAxis instance and sets default values. An axis is added for each of the four + sides, the top and right axes are set invisible initially. +*/ +QCPPolarAxisAngular::QCPPolarAxisAngular(QCustomPlot *parentPlot) : + QCPLayoutElement(parentPlot), + mBackgroundBrush(Qt::NoBrush), + mBackgroundScaled(true), + mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding), + mInsetLayout(new QCPLayoutInset), + mRangeDrag(false), + mRangeZoom(false), + mRangeZoomFactor(0.85), + // axis base: + mAngle(-90), + mAngleRad(mAngle/180.0*M_PI), + mSelectableParts(spAxis | spTickLabels | spAxisLabel), + mSelectedParts(spNone), + mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedBasePen(QPen(Qt::blue, 2)), + // axis label: + mLabelPadding(0), + mLabel(), + mLabelFont(mParentPlot->font()), + mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)), + mLabelColor(Qt::black), + mSelectedLabelColor(Qt::blue), + // tick labels: + //mTickLabelPadding(0), in label painter + mTickLabels(true), + //mTickLabelRotation(0), in label painter + mTickLabelFont(mParentPlot->font()), + mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)), + mTickLabelColor(Qt::black), + mSelectedTickLabelColor(Qt::blue), + mNumberPrecision(6), + mNumberFormatChar('g'), + mNumberBeautifulPowers(true), + mNumberMultiplyCross(false), + // ticks and subticks: + mTicks(true), + mSubTicks(true), + mTickLengthIn(5), + mTickLengthOut(0), + mSubTickLengthIn(2), + mSubTickLengthOut(0), + mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedTickPen(QPen(Qt::blue, 2)), + mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)), + mSelectedSubTickPen(QPen(Qt::blue, 2)), + // scale and range: + mRange(0, 360), + mRangeReversed(false), + // internal members: + mRadius(1), // non-zero initial value, will be overwritten in ::update() according to inner rect + mGrid(new QCPPolarGrid(this)), + mTicker(new QCPAxisTickerFixed), + mDragging(false), + mLabelPainter(parentPlot) +{ + // TODO: + //mInsetLayout->initializeParentPlot(mParentPlot); + //mInsetLayout->setParentLayerable(this); + //mInsetLayout->setParent(this); + + if (QCPAxisTickerFixed *fixedTicker = mTicker.dynamicCast().data()) + { + fixedTicker->setTickStep(30); + } + setAntialiased(true); + setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again + + setTickLabelPadding(5); + setTickLabelRotation(0); + setTickLabelMode(lmUpright); + mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artNormal); + mLabelPainter.setAbbreviateDecimalPowers(false); + mLabelPainter.setCacheSize(24); // so we can cache up to 15-degree intervals, polar angular axis uses a bit larger cache than normal axes + + setMinimumSize(50, 50); + setMinimumMargins(QMargins(30, 30, 30, 30)); + + addRadialAxis(); + mGrid->setRadialAxis(radialAxis()); +} + +QCPPolarAxisAngular::~QCPPolarAxisAngular() +{ + delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order + mGrid = 0; + + delete mInsetLayout; + mInsetLayout = 0; + + QList radialAxesList = radialAxes(); + for (int i=0; i(mRadialAxes.size()); +} + +/*! + Returns the axis with the given \a index on the axis rect side specified with \a type. + + \see axisCount, axes +*/ +QCPPolarAxisRadial *QCPPolarAxisAngular::radialAxis(int index) const +{ + if (index >= 0 && index < mRadialAxes.size()) + { + return mRadialAxes.at(index); + } else + { + qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index; + return 0; + } +} + +/*! + Returns all axes on the axis rect sides specified with \a types. + + \a types may be a single \ref QCPAxis::AxisType or an or-combination, to get the axes of + multiple sides. + + \see axis +*/ +QList QCPPolarAxisAngular::radialAxes() const +{ + return mRadialAxes; +} + + +/*! + Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a + new QCPAxis instance is created internally. QCustomPlot owns the returned axis, so if you want to + remove an axis, use \ref removeAxis instead of deleting it manually. + + You may inject QCPAxis instances (or subclasses of QCPAxis) by setting \a axis to an axis that was + previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership + of the axis, so you may not delete it afterwards. Further, the \a axis must have been created + with this axis rect as parent and with the same axis type as specified in \a type. If this is not + the case, a debug output is generated, the axis is not added, and the method returns 0. + + This method can not be used to move \a axis between axis rects. The same \a axis instance must + not be added multiple times to the same or different axis rects. + + If an axis rect side already contains one or more axes, the lower and upper endings of the new + axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref + QCPLineEnding::esHalfBar. + + \see addAxes, setupFullAxesBox +*/ +QCPPolarAxisRadial *QCPPolarAxisAngular::addRadialAxis(QCPPolarAxisRadial *axis) +{ + QCPPolarAxisRadial *newAxis = axis; + if (!newAxis) + { + newAxis = new QCPPolarAxisRadial(this); + } else // user provided existing axis instance, do some sanity checks + { + if (newAxis->angularAxis() != this) + { + qDebug() << Q_FUNC_INFO << "passed radial axis doesn't have this angular axis as parent angular axis"; + return 0; + } + if (radialAxes().contains(newAxis)) + { + qDebug() << Q_FUNC_INFO << "passed axis is already owned by this angular axis"; + return 0; + } + } + mRadialAxes.append(newAxis); + return newAxis; +} + +/*! + Removes the specified \a axis from the axis rect and deletes it. + + Returns true on success, i.e. if \a axis was a valid axis in this axis rect. + + \see addAxis +*/ +bool QCPPolarAxisAngular::removeRadialAxis(QCPPolarAxisRadial *radialAxis) +{ + if (mRadialAxes.contains(radialAxis)) + { + mRadialAxes.removeOne(radialAxis); + delete radialAxis; + return true; + } else + { + qDebug() << Q_FUNC_INFO << "Radial axis isn't associated with this angular axis:" << reinterpret_cast(radialAxis); + return false; + } +} + +QRegion QCPPolarAxisAngular::exactClipRegion() const +{ + return QRegion(mCenter.x()-mRadius, mCenter.y()-mRadius, qRound(2*mRadius), qRound(2*mRadius), QRegion::Ellipse); +} + +/*! + If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper + bounds of the range. The range is simply moved by \a diff. + + If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This + corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff). +*/ +void QCPPolarAxisAngular::moveRange(double diff) +{ + QCPRange oldRange = mRange; + mRange.lower += diff; + mRange.upper += diff; + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Scales the range of this axis by \a factor around the center of the current axis range. For + example, if \a factor is 2.0, then the axis range will double its size, and the point at the axis + range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around + the center will have moved symmetrically closer). + + If you wish to scale around a different coordinate than the current axis range center, use the + overload \ref scaleRange(double factor, double center). +*/ +void QCPPolarAxisAngular::scaleRange(double factor) +{ + scaleRange(factor, range().center()); +} + +/*! \overload + + Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a + factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at + coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates + around 1.0 will have moved symmetrically closer to 1.0). + + \see scaleRange(double factor) +*/ +void QCPPolarAxisAngular::scaleRange(double factor, double center) +{ + QCPRange oldRange = mRange; + QCPRange newRange; + newRange.lower = (mRange.lower-center)*factor + center; + newRange.upper = (mRange.upper-center)*factor + center; + if (QCPRange::validRange(newRange)) + mRange = newRange.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Changes the axis range such that all plottables associated with this axis are fully visible in + that dimension. + + \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes +*/ +void QCPPolarAxisAngular::rescale(bool onlyVisiblePlottables) +{ + QCPRange newRange; + bool haveRange = false; + for (int i=0; irealVisibility() && onlyVisiblePlottables) + continue; + QCPRange range; + bool currentFoundRange; + if (mGraphs.at(i)->keyAxis() == this) + range = mGraphs.at(i)->getKeyRange(currentFoundRange, QCP::sdBoth); + else + range = mGraphs.at(i)->getValueRange(currentFoundRange, QCP::sdBoth); + if (currentFoundRange) + { + if (!haveRange) + newRange = range; + else + newRange.expand(range); + haveRange = true; + } + } + if (haveRange) + { + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + newRange.lower = center-mRange.size()/2.0; + newRange.upper = center+mRange.size()/2.0; + } + setRange(newRange); + } +} + +/*! + Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates. +*/ +void QCPPolarAxisAngular::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const +{ + if (!mRadialAxes.isEmpty()) + mRadialAxes.first()->pixelToCoord(pixelPos, angleCoord, radiusCoord); + else + qDebug() << Q_FUNC_INFO << "no radial axis configured"; +} + +/*! + Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget. +*/ +QPointF QCPPolarAxisAngular::coordToPixel(double angleCoord, double radiusCoord) const +{ + if (!mRadialAxes.isEmpty()) + { + return mRadialAxes.first()->coordToPixel(angleCoord, radiusCoord); + } else + { + qDebug() << Q_FUNC_INFO << "no radial axis configured"; + return QPointF(); + } +} + +/*! + Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function + is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this + function does not change the current selection state of the axis. + + If the axis is not visible (\ref setVisible), this function always returns \ref spNone. + + \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions +*/ +QCPPolarAxisAngular::SelectablePart QCPPolarAxisAngular::getPartAt(const QPointF &pos) const +{ + Q_UNUSED(pos) // TODO remove later + + if (!mVisible) + return spNone; + + /* + TODO: + if (mAxisPainter->axisSelectionBox().contains(pos.toPoint())) + return spAxis; + else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint())) + return spTickLabels; + else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint())) + return spAxisLabel; + return spNone; + */ + return spNone; +} + +/* inherits documentation from base class */ +double QCPPolarAxisAngular::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + /* + if (!mParentPlot) return -1; + SelectablePart part = getPartAt(pos); + if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone) + return -1; + + if (details) + details->setValue(part); + return mParentPlot->selectionTolerance()*0.99; + */ + + Q_UNUSED(details) + + if (onlySelectable) + return -1; + + if (QRectF(mOuterRect).contains(pos)) + { + if (mParentPlot) + return mParentPlot->selectionTolerance()*0.99; + else + { + qDebug() << Q_FUNC_INFO << "parent plot not defined"; + return -1; + } + } else + return -1; +} + +/*! + This method is called automatically upon replot and doesn't need to be called by users of + QCPPolarAxisAngular. + + Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update), + and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its + QCPInsetLayout::update function. + + \seebaseclassmethod +*/ +void QCPPolarAxisAngular::update(UpdatePhase phase) +{ + QCPLayoutElement::update(phase); + + switch (phase) + { + case upPreparation: + { + setupTickVectors(); + for (int i=0; isetupTickVectors(); + break; + } + case upLayout: + { + mCenter = mRect.center(); + mRadius = 0.5*qMin(qAbs(mRect.width()), qAbs(mRect.height())); + if (mRadius < 1) mRadius = 1; // prevent cases where radius might become 0 which causes trouble + for (int i=0; iupdateGeometry(mCenter, mRadius); + + mInsetLayout->setOuterRect(rect()); + break; + } + default: break; + } + + // pass update call on to inset layout (doesn't happen automatically, because QCPPolarAxis doesn't derive from QCPLayout): + mInsetLayout->update(phase); +} + +/* inherits documentation from base class */ +QList QCPPolarAxisAngular::elements(bool recursive) const +{ + QList result; + if (mInsetLayout) + { + result << mInsetLayout; + if (recursive) + result << mInsetLayout->elements(recursive); + } + return result; +} + +bool QCPPolarAxisAngular::removeGraph(QCPPolarGraph *graph) +{ + if (!mGraphs.contains(graph)) + { + qDebug() << Q_FUNC_INFO << "graph not in list:" << reinterpret_cast(graph); + return false; + } + + // remove plottable from legend: + graph->removeFromLegend(); + // remove plottable: + delete graph; + mGraphs.removeOne(graph); + return true; +} + +/* inherits documentation from base class */ +void QCPPolarAxisAngular::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes); +} + +/* inherits documentation from base class */ +void QCPPolarAxisAngular::draw(QCPPainter *painter) +{ + drawBackground(painter, mCenter, mRadius); + + // draw baseline circle: + painter->setPen(getBasePen()); + painter->drawEllipse(mCenter, mRadius, mRadius); + + // draw subticks: + if (!mSubTickVector.isEmpty()) + { + painter->setPen(getSubTickPen()); + for (int i=0; idrawLine(mCenter+mSubTickVectorCosSin.at(i)*(mRadius-mSubTickLengthIn), + mCenter+mSubTickVectorCosSin.at(i)*(mRadius+mSubTickLengthOut)); + } + } + + // draw ticks and labels: + if (!mTickVector.isEmpty()) + { + mLabelPainter.setAnchorReference(mCenter); + mLabelPainter.setFont(getTickLabelFont()); + mLabelPainter.setColor(getTickLabelColor()); + const QPen ticksPen = getTickPen(); + painter->setPen(ticksPen); + for (int i=0; idrawLine(mCenter+mTickVectorCosSin.at(i)*(mRadius-mTickLengthIn), outerTick); + // draw tick labels: + if (!mTickVectorLabels.isEmpty()) + { + if (i < mTickVectorLabels.count()-1 || (mTickVectorCosSin.at(i)-mTickVectorCosSin.first()).manhattanLength() > 5/180.0*M_PI) // skip last label if it's closer than approx 5 degrees to first + mLabelPainter.drawTickLabel(painter, outerTick, mTickVectorLabels.at(i)); + } + } + } +} + +/* inherits documentation from base class */ +QCP::Interaction QCPPolarAxisAngular::selectionCategory() const +{ + return QCP::iSelectAxes; +} + + +/*! + Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the + axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect + backgrounds are usually drawn below everything else. + + For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be + enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio + is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call, + consider using the overloaded version of this function. + + Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref + setBackground(const QBrush &brush). + + \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush) +*/ +void QCPPolarAxisAngular::setBackground(const QPixmap &pm) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); +} + +/*! \overload + + Sets \a brush as the background brush. The axis rect background will be filled with this brush. + Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds + are usually drawn below everything else. + + The brush will be drawn before (under) any background pixmap, which may be specified with \ref + setBackground(const QPixmap &pm). + + To disable drawing of a background brush, set \a brush to Qt::NoBrush. + + \see setBackground(const QPixmap &pm) +*/ +void QCPPolarAxisAngular::setBackground(const QBrush &brush) +{ + mBackgroundBrush = brush; +} + +/*! \overload + + Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it + shall be scaled in one call. + + \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode +*/ +void QCPPolarAxisAngular::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode) +{ + mBackgroundPixmap = pm; + mScaledBackgroundPixmap = QPixmap(); + mBackgroundScaled = scaled; + mBackgroundScaledMode = mode; +} + +/*! + Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled + is set to true, you may control whether and how the aspect ratio of the original pixmap is + preserved with \ref setBackgroundScaledMode. + + Note that the scaled version of the original pixmap is buffered, so there is no performance + penalty on replots. (Except when the axis rect dimensions are changed continuously.) + + \see setBackground, setBackgroundScaledMode +*/ +void QCPPolarAxisAngular::setBackgroundScaled(bool scaled) +{ + mBackgroundScaled = scaled; +} + +/*! + If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to + define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved. + \see setBackground, setBackgroundScaled +*/ +void QCPPolarAxisAngular::setBackgroundScaledMode(Qt::AspectRatioMode mode) +{ + mBackgroundScaledMode = mode; +} + +void QCPPolarAxisAngular::setRangeDrag(bool enabled) +{ + mRangeDrag = enabled; +} + +void QCPPolarAxisAngular::setRangeZoom(bool enabled) +{ + mRangeZoom = enabled; +} + +void QCPPolarAxisAngular::setRangeZoomFactor(double factor) +{ + mRangeZoomFactor = factor; +} + + + + + + + +/*! + Sets the range of the axis. + + This slot may be connected with the \ref rangeChanged signal of another axis so this axis + is always synchronized with the other axis range, when it changes. + + To invert the direction of an axis, use \ref setRangeReversed. +*/ +void QCPPolarAxisAngular::setRange(const QCPRange &range) +{ + if (range.lower == mRange.lower && range.upper == mRange.upper) + return; + + if (!QCPRange::validRange(range)) return; + QCPRange oldRange = mRange; + mRange = range.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface. + (When \ref QCustomPlot::setInteractions contains iSelectAxes.) + + However, even when \a selectable is set to a value not allowing the selection of a specific part, + it is still possible to set the selection of this part manually, by calling \ref setSelectedParts + directly. + + \see SelectablePart, setSelectedParts +*/ +void QCPPolarAxisAngular::setSelectableParts(const SelectableParts &selectable) +{ + if (mSelectableParts != selectable) + { + mSelectableParts = selectable; + emit selectableChanged(mSelectableParts); + } +} + +/*! + Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part + is selected, it uses a different pen/font. + + The entire selection mechanism for axes is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you + wish to change the selection state manually. + + This function can change the selection state of a part, independent of the \ref setSelectableParts setting. + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen, + setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor +*/ +void QCPPolarAxisAngular::setSelectedParts(const SelectableParts &selected) +{ + if (mSelectedParts != selected) + { + mSelectedParts = selected; + emit selectionChanged(mSelectedParts); + } +} + +/*! + \overload + + Sets the lower and upper bound of the axis range. + + To invert the direction of an axis, use \ref setRangeReversed. + + There is also a slot to set a range, see \ref setRange(const QCPRange &range). +*/ +void QCPPolarAxisAngular::setRange(double lower, double upper) +{ + if (lower == mRange.lower && upper == mRange.upper) + return; + + if (!QCPRange::validRange(lower, upper)) return; + QCPRange oldRange = mRange; + mRange.lower = lower; + mRange.upper = upper; + mRange = mRange.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + \overload + + Sets the range of the axis. + + The \a position coordinate indicates together with the \a alignment parameter, where the new + range will be positioned. \a size defines the size of the new axis range. \a alignment may be + Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border, + or center of the range to be aligned with \a position. Any other values of \a alignment will + default to Qt::AlignCenter. +*/ +void QCPPolarAxisAngular::setRange(double position, double size, Qt::AlignmentFlag alignment) +{ + if (alignment == Qt::AlignLeft) + setRange(position, position+size); + else if (alignment == Qt::AlignRight) + setRange(position-size, position); + else // alignment == Qt::AlignCenter + setRange(position-size/2.0, position+size/2.0); +} + +/*! + Sets the lower bound of the axis range. The upper bound is not changed. + \see setRange +*/ +void QCPPolarAxisAngular::setRangeLower(double lower) +{ + if (mRange.lower == lower) + return; + + QCPRange oldRange = mRange; + mRange.lower = lower; + mRange = mRange.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets the upper bound of the axis range. The lower bound is not changed. + \see setRange +*/ +void QCPPolarAxisAngular::setRangeUpper(double upper) +{ + if (mRange.upper == upper) + return; + + QCPRange oldRange = mRange; + mRange.upper = upper; + mRange = mRange.sanitizedForLinScale(); + emit rangeChanged(mRange); + emit rangeChanged(mRange, oldRange); +} + +/*! + Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal + axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the + direction of increasing values is inverted. + + Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part + of the \ref setRange interface will still reference the mathematically smaller number than the \a + upper part. +*/ +void QCPPolarAxisAngular::setRangeReversed(bool reversed) +{ + mRangeReversed = reversed; +} + +void QCPPolarAxisAngular::setAngle(double degrees) +{ + mAngle = degrees; + mAngleRad = mAngle/180.0*M_PI; +} + +/*! + The axis ticker is responsible for generating the tick positions and tick labels. See the + documentation of QCPAxisTicker for details on how to work with axis tickers. + + You can change the tick positioning/labeling behaviour of this axis by setting a different + QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis + ticker, access it via \ref ticker. + + Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis + ticker simply by passing the same shared pointer to multiple axes. + + \see ticker +*/ +void QCPPolarAxisAngular::setTicker(QSharedPointer ticker) +{ + if (ticker) + mTicker = ticker; + else + qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker"; + // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector +} + +/*! + Sets whether tick marks are displayed. + + Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve + that, see \ref setTickLabels. + + \see setSubTicks +*/ +void QCPPolarAxisAngular::setTicks(bool show) +{ + if (mTicks != show) + { + mTicks = show; + //mCachedMarginValid = false; + } +} + +/*! + Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks. +*/ +void QCPPolarAxisAngular::setTickLabels(bool show) +{ + if (mTickLabels != show) + { + mTickLabels = show; + //mCachedMarginValid = false; + if (!mTickLabels) + mTickVectorLabels.clear(); + } +} + +/*! + Sets the distance between the axis base line (including any outward ticks) and the tick labels. + \see setLabelPadding, setPadding +*/ +void QCPPolarAxisAngular::setTickLabelPadding(int padding) +{ + mLabelPainter.setPadding(padding); +} + +/*! + Sets the font of the tick labels. + + \see setTickLabels, setTickLabelColor +*/ +void QCPPolarAxisAngular::setTickLabelFont(const QFont &font) +{ + mTickLabelFont = font; +} + +/*! + Sets the color of the tick labels. + + \see setTickLabels, setTickLabelFont +*/ +void QCPPolarAxisAngular::setTickLabelColor(const QColor &color) +{ + mTickLabelColor = color; +} + +/*! + Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else, + the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values + from -90 to 90 degrees. + + If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For + other angles, the label is drawn with an offset such that it seems to point toward or away from + the tick mark. +*/ +void QCPPolarAxisAngular::setTickLabelRotation(double degrees) +{ + mLabelPainter.setRotation(degrees); +} + +void QCPPolarAxisAngular::setTickLabelMode(LabelMode mode) +{ + switch (mode) + { + case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break; + case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break; + } +} + +/*! + Sets the number format for the numbers in tick labels. This \a formatCode is an extended version + of the format code used e.g. by QString::number() and QLocale::toString(). For reference about + that, see the "Argument Formats" section in the detailed description of the QString class. + + \a formatCode is a string of one, two or three characters. The first character is identical to + the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed + format, 'g'/'G' scientific or fixed, whichever is shorter. + + The second and third characters are optional and specific to QCustomPlot:\n If the first char was + 'e' or 'g', numbers are/might be displayed in the scientific format, e.g. "5.5e9", which might be + visually unappealing in a plot. So when the second char of \a formatCode is set to 'b' (for + "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5 + [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot. + If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can + be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the + cross and 183 (0xB7) for the dot. + + Examples for \a formatCode: + \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large, + normal scientific format is used + \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with + beautifully typeset decimal powers and a dot as multiplication sign + \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as + multiplication sign + \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal + powers. Format code will be reduced to 'f'. + \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format + code will not be changed. +*/ +void QCPPolarAxisAngular::setNumberFormat(const QString &formatCode) +{ + if (formatCode.isEmpty()) + { + qDebug() << Q_FUNC_INFO << "Passed formatCode is empty"; + return; + } + //mCachedMarginValid = false; + + // interpret first char as number format char: + QString allowedFormatChars(QLatin1String("eEfgG")); + if (allowedFormatChars.contains(formatCode.at(0))) + { + mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1()); + } else + { + qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode; + return; + } + + if (formatCode.length() < 2) + { + mNumberBeautifulPowers = false; + mNumberMultiplyCross = false; + } else + { + // interpret second char as indicator for beautiful decimal powers: + if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g'))) + mNumberBeautifulPowers = true; + else + qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode; + + if (formatCode.length() < 3) + { + mNumberMultiplyCross = false; + } else + { + // interpret third char as indicator for dot or cross multiplication symbol: + if (formatCode.at(2) == QLatin1Char('c')) + mNumberMultiplyCross = true; + else if (formatCode.at(2) == QLatin1Char('d')) + mNumberMultiplyCross = false; + else + qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode; + } + } + mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers); + mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot); +} + +/*! + Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec) + for details. The effect of precisions are most notably for number Formats starting with 'e', see + \ref setNumberFormat +*/ +void QCPPolarAxisAngular::setNumberPrecision(int precision) +{ + if (mNumberPrecision != precision) + { + mNumberPrecision = precision; + //mCachedMarginValid = false; + } +} + +/*! + Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the + plot and \a outside is the length they will reach outside the plot. If \a outside is greater than + zero, the tick labels and axis label will increase their distance to the axis accordingly, so + they won't collide with the ticks. + + \see setSubTickLength, setTickLengthIn, setTickLengthOut +*/ +void QCPPolarAxisAngular::setTickLength(int inside, int outside) +{ + setTickLengthIn(inside); + setTickLengthOut(outside); +} + +/*! + Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach + inside the plot. + + \see setTickLengthOut, setTickLength, setSubTickLength +*/ +void QCPPolarAxisAngular::setTickLengthIn(int inside) +{ + if (mTickLengthIn != inside) + { + mTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach + outside the plot. If \a outside is greater than zero, the tick labels and axis label will + increase their distance to the axis accordingly, so they won't collide with the ticks. + + \see setTickLengthIn, setTickLength, setSubTickLength +*/ +void QCPPolarAxisAngular::setTickLengthOut(int outside) +{ + if (mTickLengthOut != outside) + { + mTickLengthOut = outside; + //mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets whether sub tick marks are displayed. + + Sub ticks are only potentially visible if (major) ticks are also visible (see \ref setTicks) + + \see setTicks +*/ +void QCPPolarAxisAngular::setSubTicks(bool show) +{ + if (mSubTicks != show) + { + mSubTicks = show; + //mCachedMarginValid = false; + } +} + +/*! + Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside + the plot and \a outside is the length they will reach outside the plot. If \a outside is greater + than zero, the tick labels and axis label will increase their distance to the axis accordingly, + so they won't collide with the ticks. + + \see setTickLength, setSubTickLengthIn, setSubTickLengthOut +*/ +void QCPPolarAxisAngular::setSubTickLength(int inside, int outside) +{ + setSubTickLengthIn(inside); + setSubTickLengthOut(outside); +} + +/*! + Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside + the plot. + + \see setSubTickLengthOut, setSubTickLength, setTickLength +*/ +void QCPPolarAxisAngular::setSubTickLengthIn(int inside) +{ + if (mSubTickLengthIn != inside) + { + mSubTickLengthIn = inside; + } +} + +/*! + Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach + outside the plot. If \a outside is greater than zero, the tick labels will increase their + distance to the axis accordingly, so they won't collide with the ticks. + + \see setSubTickLengthIn, setSubTickLength, setTickLength +*/ +void QCPPolarAxisAngular::setSubTickLengthOut(int outside) +{ + if (mSubTickLengthOut != outside) + { + mSubTickLengthOut = outside; + //mCachedMarginValid = false; // only outside tick length can change margin + } +} + +/*! + Sets the pen, the axis base line is drawn with. + + \see setTickPen, setSubTickPen +*/ +void QCPPolarAxisAngular::setBasePen(const QPen &pen) +{ + mBasePen = pen; +} + +/*! + Sets the pen, tick marks will be drawn with. + + \see setTickLength, setBasePen +*/ +void QCPPolarAxisAngular::setTickPen(const QPen &pen) +{ + mTickPen = pen; +} + +/*! + Sets the pen, subtick marks will be drawn with. + + \see setSubTickCount, setSubTickLength, setBasePen +*/ +void QCPPolarAxisAngular::setSubTickPen(const QPen &pen) +{ + mSubTickPen = pen; +} + +/*! + Sets the font of the axis label. + + \see setLabelColor +*/ +void QCPPolarAxisAngular::setLabelFont(const QFont &font) +{ + if (mLabelFont != font) + { + mLabelFont = font; + //mCachedMarginValid = false; + } +} + +/*! + Sets the color of the axis label. + + \see setLabelFont +*/ +void QCPPolarAxisAngular::setLabelColor(const QColor &color) +{ + mLabelColor = color; +} + +/*! + Sets the text of the axis label that will be shown below/above or next to the axis, depending on + its orientation. To disable axis labels, pass an empty string as \a str. +*/ +void QCPPolarAxisAngular::setLabel(const QString &str) +{ + if (mLabel != str) + { + mLabel = str; + //mCachedMarginValid = false; + } +} + +/*! + Sets the distance between the tick labels and the axis label. + + \see setTickLabelPadding, setPadding +*/ +void QCPPolarAxisAngular::setLabelPadding(int padding) +{ + if (mLabelPadding != padding) + { + mLabelPadding = padding; + //mCachedMarginValid = false; + } +} + +/*! + Sets the font that is used for tick labels when they are selected. + + \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedTickLabelFont(const QFont &font) +{ + if (font != mSelectedTickLabelFont) + { + mSelectedTickLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts + } +} + +/*! + Sets the font that is used for the axis label when it is selected. + + \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedLabelFont(const QFont &font) +{ + mSelectedLabelFont = font; + // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts +} + +/*! + Sets the color that is used for tick labels when they are selected. + + \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedTickLabelColor(const QColor &color) +{ + if (color != mSelectedTickLabelColor) + { + mSelectedTickLabelColor = color; + } +} + +/*! + Sets the color that is used for the axis label when it is selected. + + \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedLabelColor(const QColor &color) +{ + mSelectedLabelColor = color; +} + +/*! + Sets the pen that is used to draw the axis base line when selected. + + \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedBasePen(const QPen &pen) +{ + mSelectedBasePen = pen; +} + +/*! + Sets the pen that is used to draw the (major) ticks when selected. + + \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedTickPen(const QPen &pen) +{ + mSelectedTickPen = pen; +} + +/*! + Sets the pen that is used to draw the subticks when selected. + + \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions +*/ +void QCPPolarAxisAngular::setSelectedSubTickPen(const QPen &pen) +{ + mSelectedSubTickPen = pen; +} + +/*! \internal + + Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a + pixmap. + + If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an + according filling inside the axis rect with the provided \a painter. + + Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version + depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside + the axis rect with the provided \a painter. The scaled version is buffered in + mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when + the axis rect has changed in a way that requires a rescale of the background pixmap (this is + dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was + set. + + \see setBackground, setBackgroundScaled, setBackgroundScaledMode +*/ +void QCPPolarAxisAngular::drawBackground(QCPPainter *painter, const QPointF ¢er, double radius) +{ + // draw background fill (don't use circular clip, looks bad): + if (mBackgroundBrush != Qt::NoBrush) + { + QPainterPath ellipsePath; + ellipsePath.addEllipse(center, radius, radius); + painter->fillPath(ellipsePath, mBackgroundBrush); + } + + // draw background pixmap (on top of fill, if brush specified): + if (!mBackgroundPixmap.isNull()) + { + QRegion clipCircle(center.x()-radius, center.y()-radius, qRound(2*radius), qRound(2*radius), QRegion::Ellipse); + QRegion originalClip = painter->clipRegion(); + painter->setClipRegion(clipCircle); + if (mBackgroundScaled) + { + // check whether mScaledBackground needs to be updated: + QSize scaledSize(mBackgroundPixmap.size()); + scaledSize.scale(mRect.size(), mBackgroundScaledMode); + if (mScaledBackgroundPixmap.size() != scaledSize) + mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation); + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect()); + } else + { + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height())); + } + painter->setClipRegion(originalClip); + } +} + +/*! \internal + + Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling + QCPAxisTicker::generate on the currently installed ticker. + + If a change in the label text/count is detected, the cached axis margin is invalidated to make + sure the next margin calculation recalculates the label sizes and returns an up-to-date value. +*/ +void QCPPolarAxisAngular::setupTickVectors() +{ + if (!mParentPlot) return; + if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return; + + mSubTickVector.clear(); // since we might not pass it to mTicker->generate(), and we don't want old data in there + mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0); + + // fill cos/sin buffers which will be used by draw() and QCPPolarGrid::draw(), so we don't have to calculate it twice: + mTickVectorCosSin.resize(mTickVector.size()); + for (int i=0; ibuttons() & Qt::LeftButton) + { + mDragging = true; + // initialize antialiasing backup in case we start dragging: + if (mParentPlot->noAntialiasingOnDrag()) + { + mAADragBackup = mParentPlot->antialiasedElements(); + mNotAADragBackup = mParentPlot->notAntialiasedElements(); + } + // Mouse range dragging interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + mDragAngularStart = range(); + mDragRadialStart.clear(); + for (int i=0; irange()); + } + } +} + +/*! \internal + + Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a + preceding \ref mousePressEvent, the range is moved accordingly. + + \see mousePressEvent, mouseReleaseEvent +*/ +void QCPPolarAxisAngular::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(startPos) + bool doReplot = false; + // Mouse range dragging interaction: + if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag)) + { + if (mRangeDrag) + { + doReplot = true; + double angleCoordStart, radiusCoordStart; + double angleCoord = 0.0, radiusCoord = 0.0; + pixelToCoord(startPos, angleCoordStart, radiusCoordStart); + pixelToCoord(event->pos(), angleCoord, radiusCoord); + double diff = angleCoordStart - angleCoord; + setRange(mDragAngularStart.lower+diff, mDragAngularStart.upper+diff); + } + + for (int i=0; irangeDrag()) + continue; + doReplot = true; + double angleCoordStart, radiusCoordStart; + double angleCoord, radiusCoord; + ax->pixelToCoord(startPos, angleCoordStart, radiusCoordStart); + ax->pixelToCoord(event->pos(), angleCoord, radiusCoord); + if (ax->scaleType() == QCPPolarAxisRadial::stLinear) + { + double diff = radiusCoordStart - radiusCoord; + ax->setRange(mDragRadialStart.at(i).lower+diff, mDragRadialStart.at(i).upper+diff); + } else if (ax->scaleType() == QCPPolarAxisRadial::stLogarithmic) + { + if (radiusCoord != 0) + { + double diff = radiusCoordStart/radiusCoord; + ax->setRange(mDragRadialStart.at(i).lower*diff, mDragRadialStart.at(i).upper*diff); + } + } + } + + if (doReplot) // if either vertical or horizontal drag was enabled, do a replot + { + if (mParentPlot->noAntialiasingOnDrag()) + mParentPlot->setNotAntialiasedElements(QCP::aeAll); + mParentPlot->replot(QCustomPlot::rpQueuedReplot); + } + } +} + +/* inherits documentation from base class */ +void QCPPolarAxisAngular::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) +{ + Q_UNUSED(event) + Q_UNUSED(startPos) + mDragging = false; + if (mParentPlot->noAntialiasingOnDrag()) + { + mParentPlot->setAntialiasedElements(mAADragBackup); + mParentPlot->setNotAntialiasedElements(mNotAADragBackup); + } +} + +/*! \internal + + Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the + ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of + the scaling operation is the current cursor position inside the axis rect. The scaling factor is + dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural + zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor. + + Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse + wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be + multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as + exponent of the range zoom factor. This takes care of the wheel direction automatically, by + inverting the factor, when the wheel step is negative (f^-1 = 1/f). +*/ +void QCPPolarAxisAngular::wheelEvent(QWheelEvent *event) +{ + bool doReplot = false; + // Mouse range zooming interaction: + if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) + { +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + const double delta = event->delta(); +#else + const double delta = event->angleDelta().y(); +#endif + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPointF pos = event->pos(); +#else + const QPointF pos = event->position(); +#endif + const double wheelSteps = delta/120.0; // a single step delta is +/-120 usually + if (mRangeZoom) + { + double angleCoord = 0.0, radiusCoord = 0.0; + pixelToCoord(pos, angleCoord, radiusCoord); + scaleRange(qPow(mRangeZoomFactor, wheelSteps), angleCoord); + } + + for (int i=0; irangeZoom()) + continue; + doReplot = true; + double angleCoord, radiusCoord; + ax->pixelToCoord(pos, angleCoord, radiusCoord); + ax->scaleRange(qPow(ax->rangeZoomFactor(), wheelSteps), radiusCoord); + } + } + if (doReplot) + mParentPlot->replot(); +} + +bool QCPPolarAxisAngular::registerPolarGraph(QCPPolarGraph *graph) +{ + if (mGraphs.contains(graph)) + { + qDebug() << Q_FUNC_INFO << "plottable already added:" << reinterpret_cast(graph); + return false; + } + if (graph->keyAxis() != this) + { + qDebug() << Q_FUNC_INFO << "plottable not created with this as axis:" << reinterpret_cast(graph); + return false; + } + + mGraphs.append(graph); + // possibly add plottable to legend: + if (mParentPlot->autoAddPlottableToLegend()) + graph->addToLegend(); + if (!graph->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor) + graph->setLayer(mParentPlot->currentLayer()); + return true; +} +/* end of 'src/polar/layoutelement-angularaxis.cpp' */ + + +/* including file 'src/polar/polargrid.cpp' */ +/* modified 2022-11-06T12:45:57, size 7493 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarGrid +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarGrid + \brief The grid in both angular and radial dimensions for polar plots + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. +*/ + +/*! + Creates a QCPPolarGrid instance and sets default values. + + You shouldn't instantiate grids on their own, since every axis brings its own grid. +*/ +QCPPolarGrid::QCPPolarGrid(QCPPolarAxisAngular *parentAxis) : + QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis), + mType(gtNone), + mSubGridType(gtNone), + mAntialiasedSubGrid(true), + mAntialiasedZeroLine(true), + mParentAxis(parentAxis) +{ + // warning: this is called in QCPPolarAxisAngular constructor, so parentAxis members should not be accessed/called + setParent(parentAxis); + setType(gtAll); + setSubGridType(gtNone); + + setAngularPen(QPen(QColor(200,200,200), 0, Qt::DotLine)); + setAngularSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine)); + + setRadialPen(QPen(QColor(200,200,200), 0, Qt::DotLine)); + setRadialSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine)); + setRadialZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine)); + + setAntialiased(true); +} + +void QCPPolarGrid::setRadialAxis(QCPPolarAxisRadial *axis) +{ + mRadialAxis = axis; +} + +void QCPPolarGrid::setType(GridTypes type) +{ + mType = type; +} + +void QCPPolarGrid::setSubGridType(GridTypes type) +{ + mSubGridType = type; +} + +/*! + Sets whether sub grid lines are drawn antialiased. +*/ +void QCPPolarGrid::setAntialiasedSubGrid(bool enabled) +{ + mAntialiasedSubGrid = enabled; +} + +/*! + Sets whether zero lines are drawn antialiased. +*/ +void QCPPolarGrid::setAntialiasedZeroLine(bool enabled) +{ + mAntialiasedZeroLine = enabled; +} + +/*! + Sets the pen with which (major) grid lines are drawn. +*/ +void QCPPolarGrid::setAngularPen(const QPen &pen) +{ + mAngularPen = pen; +} + +/*! + Sets the pen with which sub grid lines are drawn. +*/ +void QCPPolarGrid::setAngularSubGridPen(const QPen &pen) +{ + mAngularSubGridPen = pen; +} + +void QCPPolarGrid::setRadialPen(const QPen &pen) +{ + mRadialPen = pen; +} + +void QCPPolarGrid::setRadialSubGridPen(const QPen &pen) +{ + mRadialSubGridPen = pen; +} + +void QCPPolarGrid::setRadialZeroLinePen(const QPen &pen) +{ + mRadialZeroLinePen = pen; +} + +/*! \internal + + A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter + before drawing the major grid lines. + + This is the antialiasing state the painter passed to the \ref draw method is in by default. + + This function takes into account the local setting of the antialiasing flag as well as the + overrides set with \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. + + \see setAntialiased +*/ +void QCPPolarGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid); +} + +/*! \internal + + Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning + over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen). +*/ +void QCPPolarGrid::draw(QCPPainter *painter) +{ + if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; } + + const QPointF center = mParentAxis->mCenter; + const double radius = mParentAxis->mRadius; + + painter->setBrush(Qt::NoBrush); + // draw main angular grid: + if (mType.testFlag(gtAngular)) + drawAngularGrid(painter, center, radius, mParentAxis->mTickVectorCosSin, mAngularPen); + // draw main radial grid: + if (mType.testFlag(gtRadial) && mRadialAxis) + drawRadialGrid(painter, center, mRadialAxis->tickVector(), mRadialPen, mRadialZeroLinePen); + + applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeGrid); + // draw sub angular grid: + if (mSubGridType.testFlag(gtAngular)) + drawAngularGrid(painter, center, radius, mParentAxis->mSubTickVectorCosSin, mAngularSubGridPen); + // draw sub radial grid: + if (mSubGridType.testFlag(gtRadial) && mRadialAxis) + drawRadialGrid(painter, center, mRadialAxis->subTickVector(), mRadialSubGridPen); +} + +void QCPPolarGrid::drawRadialGrid(QCPPainter *painter, const QPointF ¢er, const QVector &coords, const QPen &pen, const QPen &zeroPen) +{ + if (!mRadialAxis) return; + if (coords.isEmpty()) return; + const bool drawZeroLine = zeroPen != Qt::NoPen; + const double zeroLineEpsilon = qAbs(coords.last()-coords.first())*1e-6; + + painter->setPen(pen); + for (int i=0; icoordToRadius(coords.at(i)); + if (drawZeroLine && qAbs(coords.at(i)) < zeroLineEpsilon) + { + applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine); + painter->setPen(zeroPen); + painter->drawEllipse(center, r, r); + painter->setPen(pen); + applyDefaultAntialiasingHint(painter); + } else + { + painter->drawEllipse(center, r, r); + } + } +} + +void QCPPolarGrid::drawAngularGrid(QCPPainter *painter, const QPointF ¢er, double radius, const QVector &ticksCosSin, const QPen &pen) +{ + if (ticksCosSin.isEmpty()) return; + + painter->setPen(pen); + for (int i=0; idrawLine(center, center+ticksCosSin.at(i)*radius); +} +/* end of 'src/polar/polargrid.cpp' */ + + +/* including file 'src/polar/polargraph.cpp' */ +/* modified 2022-11-06T12:45:57, size 44035 */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarLegendItem +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! QCPPolarLegendItem + \brief A legend item for polar plots + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. +*/ +QCPPolarLegendItem::QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph) : + QCPAbstractLegendItem(parent), + mPolarGraph(graph) +{ + setAntialiased(false); +} + +void QCPPolarLegendItem::draw(QCPPainter *painter) +{ + if (!mPolarGraph) return; + painter->setFont(getFont()); + painter->setPen(QPen(getTextColor())); + QSizeF iconSize = mParentLegend->iconSize(); + QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name()); + QRectF iconRect(mRect.topLeft(), iconSize); + int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops + painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPolarGraph->name()); + // draw icon: + painter->save(); + painter->setClipRect(iconRect, Qt::IntersectClip); + mPolarGraph->drawLegendIcon(painter, iconRect); + painter->restore(); + // draw icon border: + if (getIconBorderPen().style() != Qt::NoPen) + { + painter->setPen(getIconBorderPen()); + painter->setBrush(Qt::NoBrush); + int halfPen = qCeil(painter->pen().widthF()*0.5)+1; + painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped + painter->drawRect(iconRect); + } +} + +QSize QCPPolarLegendItem::minimumOuterSizeHint() const +{ + if (!mPolarGraph) return QSize(); + QSize result(0, 0); + QRect textRect; + QFontMetrics fontMetrics(getFont()); + QSize iconSize = mParentLegend->iconSize(); + textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name()); + result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width()); + result.setHeight(qMax(textRect.height(), iconSize.height())); + result.rwidth() += mMargins.left()+mMargins.right(); + result.rheight() += mMargins.top()+mMargins.bottom(); + return result; +} + +QPen QCPPolarLegendItem::getIconBorderPen() const +{ + return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen(); +} + +QColor QCPPolarLegendItem::getTextColor() const +{ + return mSelected ? mSelectedTextColor : mTextColor; +} + +QFont QCPPolarLegendItem::getFont() const +{ + return mSelected ? mSelectedFont : mFont; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPolarGraph +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPolarGraph + \brief A radial graph used to display data in polar plots + + \warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and + functionality to be incomplete, as well as changing public interfaces in the future. +*/ + +/* start of documentation of inline functions */ + +// TODO + +/* end of documentation of inline functions */ + +/*! + Constructs a graph which uses \a keyAxis as its angular and \a valueAxis as its radial axis. \a + keyAxis and \a valueAxis must reside in the same QCustomPlot, and the radial axis must be + associated with the angular axis. If either of these restrictions is violated, a corresponding + message is printed to the debug output (qDebug), the construction is not aborted, though. + + The created QCPPolarGraph is automatically registered with the QCustomPlot instance inferred from + \a keyAxis. This QCustomPlot instance takes ownership of the QCPPolarGraph, so do not delete it + manually but use QCPPolarAxisAngular::removeGraph() instead. + + To directly create a QCPPolarGraph inside a plot, you shoud use the QCPPolarAxisAngular::addGraph + method. +*/ +QCPPolarGraph::QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis) : + QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis), + mDataContainer(new QCPGraphDataContainer), + mName(), + mAntialiasedFill(true), + mAntialiasedScatters(true), + mPen(Qt::black), + mBrush(Qt::NoBrush), + mPeriodic(true), + mKeyAxis(keyAxis), + mValueAxis(valueAxis), + mSelectable(QCP::stWhole) + //mSelectionDecorator(0) // TODO +{ + if (keyAxis->parentPlot() != valueAxis->parentPlot()) + qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis."; + + mKeyAxis->registerPolarGraph(this); + + //setSelectionDecorator(new QCPSelectionDecorator); // TODO + + setPen(QPen(Qt::blue, 0)); + setBrush(Qt::NoBrush); + setLineStyle(lsLine); +} + +QCPPolarGraph::~QCPPolarGraph() +{ + /* TODO + if (mSelectionDecorator) + { + delete mSelectionDecorator; + mSelectionDecorator = 0; + } + */ +} + +/*! + The name is the textual representation of this plottable as it is displayed in the legend + (\ref QCPLegend). It may contain any UTF-8 characters, including newlines. +*/ +void QCPPolarGraph::setName(const QString &name) +{ + mName = name; +} + +/*! + Sets whether fills of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPPolarGraph::setAntialiasedFill(bool enabled) +{ + mAntialiasedFill = enabled; +} + +/*! + Sets whether the scatter symbols of this plottable are drawn antialiased or not. + + Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref + QCustomPlot::setNotAntialiasedElements. +*/ +void QCPPolarGraph::setAntialiasedScatters(bool enabled) +{ + mAntialiasedScatters = enabled; +} + +/*! + The pen is used to draw basic lines that make up the plottable representation in the + plot. + + For example, the \ref QCPGraph subclass draws its graph lines with this pen. + + \see setBrush +*/ +void QCPPolarGraph::setPen(const QPen &pen) +{ + mPen = pen; +} + +/*! + The brush is used to draw basic fills of the plottable representation in the + plot. The Fill can be a color, gradient or texture, see the usage of QBrush. + + For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when + it's not set to Qt::NoBrush. + + \see setPen +*/ +void QCPPolarGraph::setBrush(const QBrush &brush) +{ + mBrush = brush; +} + +void QCPPolarGraph::setPeriodic(bool enabled) +{ + mPeriodic = enabled; +} + +/*! + The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal + to the plottable's value axis. This function performs no checks to make sure this is the case. + The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the + y-axis (QCustomPlot::yAxis) as value axis. + + Normally, the key and value axes are set in the constructor of the plottable (or \ref + QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface). + + \see setValueAxis +*/ +void QCPPolarGraph::setKeyAxis(QCPPolarAxisAngular *axis) +{ + mKeyAxis = axis; +} + +/*! + The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is + orthogonal to the plottable's key axis. This function performs no checks to make sure this is the + case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and + the y-axis (QCustomPlot::yAxis) as value axis. + + Normally, the key and value axes are set in the constructor of the plottable (or \ref + QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface). + + \see setKeyAxis +*/ +void QCPPolarGraph::setValueAxis(QCPPolarAxisRadial *axis) +{ + mValueAxis = axis; +} + +/*! + Sets whether and to which granularity this plottable can be selected. + + A selection can happen by clicking on the QCustomPlot surface (When \ref + QCustomPlot::setInteractions contains \ref QCP::iSelectPlottables), by dragging a selection rect + (When \ref QCustomPlot::setSelectionRectMode is \ref QCP::srmSelect), or programmatically by + calling \ref setSelection. + + \see setSelection, QCP::SelectionType +*/ +void QCPPolarGraph::setSelectable(QCP::SelectionType selectable) +{ + if (mSelectable != selectable) + { + mSelectable = selectable; + QCPDataSelection oldSelection = mSelection; + mSelection.enforceType(mSelectable); + emit selectableChanged(mSelectable); + if (mSelection != oldSelection) + { + emit selectionChanged(selected()); + emit selectionChanged(mSelection); + } + } +} + +/*! + Sets which data ranges of this plottable are selected. Selected data ranges are drawn differently + (e.g. color) in the plot. This can be controlled via the selection decorator (see \ref + selectionDecorator). + + The entire selection mechanism for plottables is handled automatically when \ref + QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when + you wish to change the selection state programmatically. + + Using \ref setSelectable you can further specify for each plottable whether and to which + granularity it is selectable. If \a selection is not compatible with the current \ref + QCP::SelectionType set via \ref setSelectable, the resulting selection will be adjusted + accordingly (see \ref QCPDataSelection::enforceType). + + emits the \ref selectionChanged signal when \a selected is different from the previous selection state. + + \see setSelectable, selectTest +*/ +void QCPPolarGraph::setSelection(QCPDataSelection selection) +{ + selection.enforceType(mSelectable); + if (mSelection != selection) + { + mSelection = selection; + emit selectionChanged(selected()); + emit selectionChanged(mSelection); + } +} + +/*! \overload + + Replaces the current data container with the provided \a data container. + + Since a QSharedPointer is used, multiple QCPPolarGraphs may share the same data container safely. + Modifying the data in the container will then affect all graphs that share the container. Sharing + can be achieved by simply exchanging the data containers wrapped in shared pointers: + \snippet documentation/doc-code-snippets/mainwindow.cpp QCPPolarGraph-datasharing-1 + + If you do not wish to share containers, but create a copy from an existing container, rather use + the \ref QCPDataContainer::set method on the graph's data container directly: + \snippet documentation/doc-code-snippets/mainwindow.cpp QCPPolarGraph-datasharing-2 + + \see addData +*/ +void QCPPolarGraph::setData(QSharedPointer data) +{ + mDataContainer = data; +} + +/*! \overload + + Replaces the current data with the provided points in \a keys and \a values. The provided + vectors should have equal length. Else, the number of added points will be the size of the + smallest vector. + + If you can guarantee that the passed data points are sorted by \a keys in ascending order, you + can set \a alreadySorted to true, to improve performance by saving a sorting run. + + \see addData +*/ +void QCPPolarGraph::setData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + mDataContainer->clear(); + addData(keys, values, alreadySorted); +} + +/*! + Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to + \ref lsNone and \ref setScatterStyle to the desired scatter style. + + \see setScatterStyle +*/ +void QCPPolarGraph::setLineStyle(LineStyle ls) +{ + mLineStyle = ls; +} + +/*! + Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points + are drawn (e.g. for line-only-plots with appropriate line style). + + \see QCPScatterStyle, setLineStyle +*/ +void QCPPolarGraph::setScatterStyle(const QCPScatterStyle &style) +{ + mScatterStyle = style; +} + +void QCPPolarGraph::addData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + if (keys.size() != values.size()) + qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size(); + const int n = static_cast(qMin(keys.size(), values.size())); + QVector tempData(n); + QVector::iterator it = tempData.begin(); + const QVector::iterator itEnd = tempData.end(); + int i = 0; + while (it != itEnd) + { + it->key = keys[i]; + it->value = values[i]; + ++it; + ++i; + } + mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write +} + +void QCPPolarGraph::addData(double key, double value) +{ + mDataContainer->add(QCPGraphData(key, value)); +} + +/*! + Use this method to set an own QCPSelectionDecorator (subclass) instance. This allows you to + customize the visual representation of selected data ranges further than by using the default + QCPSelectionDecorator. + + The plottable takes ownership of the \a decorator. + + The currently set decorator can be accessed via \ref selectionDecorator. +*/ +/* +void QCPPolarGraph::setSelectionDecorator(QCPSelectionDecorator *decorator) +{ + if (decorator) + { + if (decorator->registerWithPlottable(this)) + { + if (mSelectionDecorator) // delete old decorator if necessary + delete mSelectionDecorator; + mSelectionDecorator = decorator; + } + } else if (mSelectionDecorator) // just clear decorator + { + delete mSelectionDecorator; + mSelectionDecorator = 0; + } +} +*/ + +void QCPPolarGraph::coordsToPixels(double key, double value, double &x, double &y) const +{ + if (mValueAxis) + { + const QPointF point = mValueAxis->coordToPixel(key, value); + x = point.x(); + y = point.y(); + } else + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + } +} + +const QPointF QCPPolarGraph::coordsToPixels(double key, double value) const +{ + if (mValueAxis) + { + return mValueAxis->coordToPixel(key, value); + } else + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + return QPointF(); + } +} + +void QCPPolarGraph::pixelsToCoords(double x, double y, double &key, double &value) const +{ + if (mValueAxis) + { + mValueAxis->pixelToCoord(QPointF(x, y), key, value); + } else + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + } +} + +void QCPPolarGraph::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const +{ + if (mValueAxis) + { + mValueAxis->pixelToCoord(pixelPos, key, value); + } else + { + qDebug() << Q_FUNC_INFO << "invalid key or value axis"; + } +} + +void QCPPolarGraph::rescaleAxes(bool onlyEnlarge) const +{ + rescaleKeyAxis(onlyEnlarge); + rescaleValueAxis(onlyEnlarge); +} + +void QCPPolarGraph::rescaleKeyAxis(bool onlyEnlarge) const +{ + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; } + + bool foundRange; + QCPRange newRange = getKeyRange(foundRange, QCP::sdBoth); + if (foundRange) + { + if (onlyEnlarge) + newRange.expand(keyAxis->range()); + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + newRange.lower = center-keyAxis->range().size()/2.0; + newRange.upper = center+keyAxis->range().size()/2.0; + } + keyAxis->setRange(newRange); + } +} + +void QCPPolarGraph::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const +{ + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + QCPPolarAxisRadial *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + QCP::SignDomain signDomain = QCP::sdBoth; + if (valueAxis->scaleType() == QCPPolarAxisRadial::stLogarithmic) + signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive); + + bool foundRange; + QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange()); + if (foundRange) + { + if (onlyEnlarge) + newRange.expand(valueAxis->range()); + if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable + { + double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason + if (valueAxis->scaleType() == QCPPolarAxisRadial::stLinear) + { + newRange.lower = center-valueAxis->range().size()/2.0; + newRange.upper = center+valueAxis->range().size()/2.0; + } else // scaleType() == stLogarithmic + { + newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower); + newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower); + } + } + valueAxis->setRange(newRange); + } +} + +bool QCPPolarGraph::addToLegend(QCPLegend *legend) +{ + if (!legend) + { + qDebug() << Q_FUNC_INFO << "passed legend is null"; + return false; + } + if (legend->parentPlot() != mParentPlot) + { + qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable"; + return false; + } + + //if (!legend->hasItemWithPlottable(this)) // TODO + //{ + legend->addItem(new QCPPolarLegendItem(legend, this)); + return true; + //} else + // return false; +} + +bool QCPPolarGraph::addToLegend() +{ + if (!mParentPlot || !mParentPlot->legend) + return false; + else + return addToLegend(mParentPlot->legend); +} + +bool QCPPolarGraph::removeFromLegend(QCPLegend *legend) const +{ + if (!legend) + { + qDebug() << Q_FUNC_INFO << "passed legend is null"; + return false; + } + + + QCPPolarLegendItem *removableItem = 0; + for (int i=0; iitemCount(); ++i) // TODO: reduce this to code in QCPAbstractPlottable::removeFromLegend once unified + { + if (QCPPolarLegendItem *pli = qobject_cast(legend->item(i))) + { + if (pli->polarGraph() == this) + { + removableItem = pli; + break; + } + } + } + + if (removableItem) + return legend->removeItem(removableItem); + else + return false; +} + +bool QCPPolarGraph::removeFromLegend() const +{ + if (!mParentPlot || !mParentPlot->legend) + return false; + else + return removeFromLegend(mParentPlot->legend); +} + +double QCPPolarGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + if (mKeyAxis->rect().contains(pos.toPoint())) + { + QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); + double result = pointDistance(pos, closestDataPoint); + if (details) + { + int pointIndex = int(closestDataPoint-mDataContainer->constBegin()); + details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1))); + } + return result; + } else + return -1; +} + +/* inherits documentation from base class */ +QCPRange QCPPolarGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const +{ + return mDataContainer->keyRange(foundRange, inSignDomain); +} + +/* inherits documentation from base class */ +QCPRange QCPPolarGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const +{ + return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange); +} + +/* inherits documentation from base class */ +QRect QCPPolarGraph::clipRect() const +{ + if (mKeyAxis) + return mKeyAxis.data()->rect(); + else + return QRect(); +} + +void QCPPolarGraph::draw(QCPPainter *painter) +{ + if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return; + if (mLineStyle == lsNone && mScatterStyle.isNone()) return; + + painter->setClipRegion(mKeyAxis->exactClipRegion()); + + QVector lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments + + // loop over and draw segments of unselected/selected data: + QList selectedSegments, unselectedSegments, allSegments; + getDataSegments(selectedSegments, unselectedSegments); + allSegments << unselectedSegments << selectedSegments; + for (int i=0; i= unselectedSegments.size(); + // get line pixel points appropriate to line style: + QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getLines takes care) + getLines(&lines, lineDataRange); + + // check data validity if flag set: +#ifdef QCUSTOMPLOT_CHECK_DATA + QCPGraphDataContainer::const_iterator it; + for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it) + { + if (QCP::isInvalidData(it->key, it->value)) + qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name(); + } +#endif + + // draw fill of graph: + //if (isSelectedSegment && mSelectionDecorator) + // mSelectionDecorator->applyBrush(painter); + //else + painter->setBrush(mBrush); + painter->setPen(Qt::NoPen); + drawFill(painter, &lines); + + + // draw line: + if (mLineStyle != lsNone) + { + //if (isSelectedSegment && mSelectionDecorator) + // mSelectionDecorator->applyPen(painter); + //else + painter->setPen(mPen); + painter->setBrush(Qt::NoBrush); + drawLinePlot(painter, lines); + } + + // draw scatters: + + QCPScatterStyle finalScatterStyle = mScatterStyle; + //if (isSelectedSegment && mSelectionDecorator) + // finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle); + if (!finalScatterStyle.isNone()) + { + getScatters(&scatters, allSegments.at(i)); + drawScatterPlot(painter, scatters, finalScatterStyle); + } + } + + // draw other selection decoration that isn't just line/scatter pens and brushes: + //if (mSelectionDecorator) + // mSelectionDecorator->drawDecoration(painter, selection()); +} + +QCP::Interaction QCPPolarGraph::selectionCategory() const +{ + return QCP::iSelectPlottables; +} + +void QCPPolarGraph::applyDefaultAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables); +} + +/* inherits documentation from base class */ +void QCPPolarGraph::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) +{ + Q_UNUSED(event) + + if (mSelectable != QCP::stNone) + { + QCPDataSelection newSelection = details.value(); + QCPDataSelection selectionBefore = mSelection; + if (additive) + { + if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit + { + if (selected()) + setSelection(QCPDataSelection()); + else + setSelection(newSelection); + } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments + { + if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection + setSelection(mSelection-newSelection); + else + setSelection(mSelection+newSelection); + } + } else + setSelection(newSelection); + if (selectionStateChanged) + *selectionStateChanged = mSelection != selectionBefore; + } +} + +/* inherits documentation from base class */ +void QCPPolarGraph::deselectEvent(bool *selectionStateChanged) +{ + if (mSelectable != QCP::stNone) + { + QCPDataSelection selectionBefore = mSelection; + setSelection(QCPDataSelection()); + if (selectionStateChanged) + *selectionStateChanged = mSelection != selectionBefore; + } +} + +/*! \internal + + Draws lines between the points in \a lines, given in pixel coordinates. + + \see drawScatterPlot, drawImpulsePlot, QCPAbstractPlottable1D::drawPolyline +*/ +void QCPPolarGraph::drawLinePlot(QCPPainter *painter, const QVector &lines) const +{ + if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0) + { + applyDefaultAntialiasingHint(painter); + drawPolyline(painter, lines); + } +} + +/*! \internal + + Draws the fill of the graph using the specified \a painter, with the currently set brush. + + Depending on whether a normal fill or a channel fill (\ref setChannelFillGraph) is needed, \ref + getFillPolygon or \ref getChannelFillPolygon are used to find the according fill polygons. + + In order to handle NaN Data points correctly (the fill needs to be split into disjoint areas), + this method first determines a list of non-NaN segments with \ref getNonNanSegments, on which to + operate. In the channel fill case, \ref getOverlappingSegments is used to consolidate the non-NaN + segments of the two involved graphs, before passing the overlapping pairs to \ref + getChannelFillPolygon. + + Pass the points of this graph's line as \a lines, in pixel coordinates. + + \see drawLinePlot, drawImpulsePlot, drawScatterPlot +*/ +void QCPPolarGraph::drawFill(QCPPainter *painter, QVector *lines) const +{ + applyFillAntialiasingHint(painter); + if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0) + painter->drawPolygon(QPolygonF(*lines)); +} + +/*! \internal + + Draws scatter symbols at every point passed in \a scatters, given in pixel coordinates. The + scatters will be drawn with \a painter and have the appearance as specified in \a style. + + \see drawLinePlot, drawImpulsePlot +*/ +void QCPPolarGraph::drawScatterPlot(QCPPainter *painter, const QVector &scatters, const QCPScatterStyle &style) const +{ + applyScattersAntialiasingHint(painter); + style.applyTo(painter, mPen); + for (int i=0; ifillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush); + } + // draw line vertically centered: + if (mLineStyle != lsNone) + { + applyDefaultAntialiasingHint(painter); + painter->setPen(mPen); + painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens + } + // draw scatter symbol: + if (!mScatterStyle.isNone()) + { + applyScattersAntialiasingHint(painter); + // scale scatter pixmap if it's too large to fit in legend icon rect: + if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height())) + { + QCPScatterStyle scaledStyle(mScatterStyle); + scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + scaledStyle.applyTo(painter, mPen); + scaledStyle.drawShape(painter, QRectF(rect).center()); + } else + { + mScatterStyle.applyTo(painter, mPen); + mScatterStyle.drawShape(painter, QRectF(rect).center()); + } + } +} + +void QCPPolarGraph::applyFillAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills); +} + +void QCPPolarGraph::applyScattersAntialiasingHint(QCPPainter *painter) const +{ + applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters); +} + +double QCPPolarGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const +{ + closestData = mDataContainer->constEnd(); + if (mDataContainer->isEmpty()) + return -1.0; + if (mLineStyle == lsNone && mScatterStyle.isNone()) + return -1.0; + + // calculate minimum distances to graph data points and find closestData iterator: + double minDistSqr = (std::numeric_limits::max)(); + // determine which key range comes into question, taking selection tolerance around pos into account: + double posKeyMin = 0.0, posKeyMax = 0.0, dummy; + pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy); + pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy); + if (posKeyMin > posKeyMax) + qSwap(posKeyMin, posKeyMax); + // iterate over found data points and then choose the one with the shortest distance to pos: + QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true); + QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true); + for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it) + { + const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared(); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + closestData = it; + } + } + + // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point): + if (mLineStyle != lsNone) + { + // line displayed, calculate distance to line segments: + QVector lineData; + getLines(&lineData, QCPDataRange(0, dataCount())); + QCPVector2D p(pixelPoint); + for (int i=0; isize(); +} + +void QCPPolarGraph::getDataSegments(QList &selectedSegments, QList &unselectedSegments) const +{ + selectedSegments.clear(); + unselectedSegments.clear(); + if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty + { + if (selected()) + selectedSegments << QCPDataRange(0, dataCount()); + else + unselectedSegments << QCPDataRange(0, dataCount()); + } else + { + QCPDataSelection sel(selection()); + sel.simplify(); + selectedSegments = sel.dataRanges(); + unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges(); + } +} + +void QCPPolarGraph::drawPolyline(QCPPainter *painter, const QVector &lineData) const +{ + // if drawing solid line and not in PDF, use much faster line drawing instead of polyline: + if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) && + painter->pen().style() == Qt::SolidLine && + !painter->modes().testFlag(QCPPainter::pmVectorized) && + !painter->modes().testFlag(QCPPainter::pmNoCaching)) + { + int i = 0; + bool lastIsNan = false; + const int lineDataSize = static_cast(lineData.size()); + while (i < lineDataSize && (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()))) // make sure first point is not NaN + ++i; + ++i; // because drawing works in 1 point retrospect + while (i < lineDataSize) + { + if (!qIsNaN(lineData.at(i).y()) && !qIsNaN(lineData.at(i).x())) // NaNs create a gap in the line + { + if (!lastIsNan) + painter->drawLine(lineData.at(i-1), lineData.at(i)); + else + lastIsNan = false; + } else + lastIsNan = true; + ++i; + } + } else + { + int segmentStart = 0; + int i = 0; + const int lineDataSize = static_cast(lineData.size()); + while (i < lineDataSize) + { + if (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()) || qIsInf(lineData.at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block + { + painter->drawPolyline(lineData.constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point + segmentStart = i+1; + } + ++i; + } + // draw last segment: + painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart); + } +} + +void QCPPolarGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const +{ + if (rangeRestriction.isEmpty()) + { + end = mDataContainer->constEnd(); + begin = end; + } else + { + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + QCPPolarAxisRadial *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + // get visible data range: + if (mPeriodic) + { + begin = mDataContainer->constBegin(); + end = mDataContainer->constEnd(); + } else + { + begin = mDataContainer->findBegin(keyAxis->range().lower); + end = mDataContainer->findEnd(keyAxis->range().upper); + } + // limit lower/upperEnd to rangeRestriction: + mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything + } +} + +/*! \internal + + This method retrieves an optimized set of data points via \ref getOptimizedLineData, an branches + out to the line style specific functions such as \ref dataToLines, \ref dataToStepLeftLines, etc. + according to the line style of the graph. + + \a lines will be filled with points in pixel coordinates, that can be drawn with the according + draw functions like \ref drawLinePlot and \ref drawImpulsePlot. The points returned in \a lines + aren't necessarily the original data points. For example, step line styles require additional + points to form the steps when drawn. If the line style of the graph is \ref lsNone, the \a + lines vector will be empty. + + \a dataRange specifies the beginning and ending data indices that will be taken into account for + conversion. In this function, the specified range may exceed the total data bounds without harm: + a correspondingly trimmed data range will be used. This takes the burden off the user of this + function to check for valid indices in \a dataRange, e.g. when extending ranges coming from \ref + getDataSegments. + + \see getScatters +*/ +void QCPPolarGraph::getLines(QVector *lines, const QCPDataRange &dataRange) const +{ + if (!lines) return; + QCPGraphDataContainer::const_iterator begin, end; + getVisibleDataBounds(begin, end, dataRange); + if (begin == end) + { + lines->clear(); + return; + } + + QVector lineData; + if (mLineStyle != lsNone) + getOptimizedLineData(&lineData, begin, end); + + switch (mLineStyle) + { + case lsNone: lines->clear(); break; + case lsLine: *lines = dataToLines(lineData); break; + } +} + +void QCPPolarGraph::getScatters(QVector *scatters, const QCPDataRange &dataRange) const +{ + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + QCPPolarAxisRadial *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; } + + if (!scatters) return; + QCPGraphDataContainer::const_iterator begin, end; + getVisibleDataBounds(begin, end, dataRange); + if (begin == end) + { + scatters->clear(); + return; + } + + QVector data; + getOptimizedScatterData(&data, begin, end); + + scatters->resize(data.size()); + for (int i=0; icoordToPixel(data.at(i).key, data.at(i).value); + } +} + +void QCPPolarGraph::getOptimizedLineData(QVector *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const +{ + lineData->clear(); + + // TODO: fix for log axes and thick line style + + const QCPRange range = mValueAxis->range(); + bool reversed = mValueAxis->rangeReversed(); + const double clipMargin = range.size()*0.05; // extra distance from visible circle, so optimized outside lines can cover more angle before having to place a dummy point to prevent tangents + const double upperClipValue = range.upper + (reversed ? 0 : range.size()*0.05+clipMargin); // clip slightly outside of actual range to avoid line thicknesses to peek into visible circle + const double lowerClipValue = range.lower - (reversed ? range.size()*0.05+clipMargin : 0); // clip slightly outside of actual range to avoid line thicknesses to peek into visible circle + const double maxKeySkip = qAsin(qSqrt(clipMargin*(clipMargin+2*range.size()))/(range.size()+clipMargin))/M_PI*mKeyAxis->range().size(); // the maximum angle between two points on outer circle (r=clipValue+clipMargin) before connecting line becomes tangent to inner circle (r=clipValue) + double skipBegin = 0; + bool belowRange = false; + bool aboveRange = false; + QCPGraphDataContainer::const_iterator it = begin; + while (it != end) + { + if (it->value < lowerClipValue) + { + if (aboveRange) // jumped directly from above to below visible range, draw previous point so entry angle is correct + { + aboveRange = false; + if (!reversed) // TODO: with inner radius, we'll need else case here with projected border point + lineData->append(*(it-1)); + } + if (!belowRange) + { + skipBegin = it->key; + lineData->append(QCPGraphData(it->key, lowerClipValue)); + belowRange = true; + } + if (it->key-skipBegin > maxKeySkip) // add dummy point if we're exceeding the maximum skippable angle (to prevent unintentional intersections with visible circle) + { + skipBegin += maxKeySkip; + lineData->append(QCPGraphData(skipBegin, lowerClipValue)); + } + } else if (it->value > upperClipValue) + { + if (belowRange) // jumped directly from below to above visible range, draw previous point so entry angle is correct (if lower means outer, so if reversed axis) + { + belowRange = false; + if (reversed) + lineData->append(*(it-1)); + } + if (!aboveRange) + { + skipBegin = it->key; + lineData->append(QCPGraphData(it->key, upperClipValue)); + aboveRange = true; + } + if (it->key-skipBegin > maxKeySkip) // add dummy point if we're exceeding the maximum skippable angle (to prevent unintentional intersections with visible circle) + { + skipBegin += maxKeySkip; + lineData->append(QCPGraphData(skipBegin, upperClipValue)); + } + } else // value within bounds where we don't optimize away points + { + if (aboveRange) + { + aboveRange = false; + if (!reversed) + lineData->append(*(it-1)); // just entered from above, draw previous point so entry angle is correct (if above means outer, so if not reversed axis) + } + if (belowRange) + { + belowRange = false; + if (reversed) + lineData->append(*(it-1)); // just entered from below, draw previous point so entry angle is correct (if below means outer, so if reversed axis) + } + lineData->append(*it); // inside visible circle, add point normally + } + ++it; + } + // to make fill not erratic, add last point normally if it was outside visible circle: + if (aboveRange) + { + // aboveRange = false; // Dead store + if (!reversed) + lineData->append(*(it-1)); // just entered from above, draw previous point so entry angle is correct (if above means outer, so if not reversed axis) + } + if (belowRange) + { + // belowRange = false; // Dead store + if (reversed) + lineData->append(*(it-1)); // just entered from below, draw previous point so entry angle is correct (if below means outer, so if reversed axis) + } +} + +void QCPPolarGraph::getOptimizedScatterData(QVector *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const +{ + scatterData->clear(); + + const QCPRange range = mValueAxis->range(); + bool reversed = mValueAxis->rangeReversed(); + const double clipMargin = range.size()*0.05; + const double upperClipValue = range.upper + (reversed ? 0 : clipMargin); // clip slightly outside of actual range to avoid scatter size to peek into visible circle + const double lowerClipValue = range.lower - (reversed ? clipMargin : 0); // clip slightly outside of actual range to avoid scatter size to peek into visible circle + QCPGraphDataContainer::const_iterator it = begin; + while (it != end) + { + if (it->value > lowerClipValue && it->value < upperClipValue) + scatterData->append(*it); + ++it; + } +} + +/*! \internal + + Takes raw data points in plot coordinates as \a data, and returns a vector containing pixel + coordinate points which are suitable for drawing the line style \ref lsLine. + + The source of \a data is usually \ref getOptimizedLineData, and this method is called in \a + getLines if the line style is set accordingly. + + \see dataToStepLeftLines, dataToStepRightLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot +*/ +QVector QCPPolarGraph::dataToLines(const QVector &data) const +{ + QVector result; + QCPPolarAxisAngular *keyAxis = mKeyAxis.data(); + QCPPolarAxisRadial *valueAxis = mValueAxis.data(); + if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; } + + // transform data points to pixels: + result.resize(data.size()); + for (int i=0; icoordToPixel(data.at(i).key, data.at(i).value); + return result; +} +/* end of 'src/polar/polargraph.cpp' */ + + diff --git a/ui/qt/widgets/qcustomplot.h b/ui/qt/widgets/qcustomplot.h new file mode 100644 index 00000000..a7d80c39 --- /dev/null +++ b/ui/qt/widgets/qcustomplot.h @@ -0,0 +1,7770 @@ +/** @file +** +**************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011-2022 Emanuel Eichhammer ** +** ** +**************************************************************************** +** Author: Emanuel Eichhammer ** +** Website/Contact: https://www.qcustomplot.com/ ** +** Date: 06.11.22 ** +** Version: 2.1.1 ** +** ** +** Emanuel Eichhammer has granted Wireshark permission to use QCustomPlot ** +** under the terms of the GNU General Public License version 2. ** +** Date: 22.12.15 (V1.3.2) ** +** 13.09.19 (V2.0.1) ** +** ** +** SPDX-License-Identifier: GPL-2.0-or-later ** +****************************************************************************/ + +#ifndef QCUSTOMPLOT_H +#define QCUSTOMPLOT_H + +#include + +// some Qt version/configuration dependent macros to include or exclude certain code paths: +#ifdef QCUSTOMPLOT_USE_OPENGL +# if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +# define QCP_OPENGL_PBUFFER +# else +# define QCP_OPENGL_FBO +# endif +# if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) +# define QCP_OPENGL_OFFSCREENSURFACE +# endif +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) +# define QCP_DEVICEPIXELRATIO_SUPPORTED +# if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +# define QCP_DEVICEPIXELRATIO_FLOAT +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef QCP_OPENGL_FBO +# include +# if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +# include +# else +# include +# include +# endif +# ifdef QCP_OPENGL_OFFSCREENSURFACE +# include +# else +# include +# endif +#endif +#ifdef QCP_OPENGL_PBUFFER +# include +#endif +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +# include +# include +# include +# include +#else +# include +# include +# include +#endif +#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) +# include +#endif +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) +# include +#endif + +class QCPPainter; +class QCustomPlot; +class QCPLayerable; +class QCPLayoutElement; +class QCPLayout; +class QCPAxis; +class QCPAxisRect; +class QCPAxisPainterPrivate; +class QCPAbstractPlottable; +class QCPGraph; +class QCPAbstractItem; +class QCPPlottableInterface1D; +class QCPLegend; +class QCPItemPosition; +class QCPLayer; +class QCPAbstractLegendItem; +class QCPSelectionRect; +class QCPColorMap; +class QCPColorScale; +class QCPBars; +class QCPPolarAxisRadial; +class QCPPolarAxisAngular; +class QCPPolarGrid; +class QCPPolarGraph; + +/* including file 'src/global.h' */ +/* modified 2022-11-06T12:45:57, size 18102 */ + +#define QCUSTOMPLOT_VERSION_STR "2.1.1" +#define QCUSTOMPLOT_VERSION 0x020101 + +// decl definitions for shared library compilation/usage: +#if defined(QT_STATIC_BUILD) +# define QCP_LIB_DECL +#elif defined(QCUSTOMPLOT_COMPILE_LIBRARY) +# define QCP_LIB_DECL Q_DECL_EXPORT +#elif defined(QCUSTOMPLOT_USE_LIBRARY) +# define QCP_LIB_DECL Q_DECL_IMPORT +#else +# define QCP_LIB_DECL +#endif + +// define empty macro for Q_DECL_OVERRIDE if it doesn't exist (Qt < 5) +#ifndef Q_DECL_OVERRIDE +# define Q_DECL_OVERRIDE +#endif + +/*! + The QCP Namespace contains general enums, QFlags and functions used throughout the QCustomPlot + library. + + It provides QMetaObject-based reflection of its enums and flags via \a QCP::staticMetaObject. +*/ + +// Qt version < 6.2.0: to get metatypes Q_GADGET/Q_ENUMS/Q_FLAGS in namespace we have to make it look like a class during moc-run +#if QT_VERSION >= 0x060200 // don't use QT_VERSION_CHECK here, some moc versions don't understand it +namespace QCP { + Q_NAMESPACE // this is how to add the staticMetaObject to namespaces in newer Qt versions +#else // Qt version older than 6.2.0 +# ifndef Q_MOC_RUN +namespace QCP { +# else // not in moc run +class QCP { + Q_GADGET + Q_ENUMS(ExportPen) + Q_ENUMS(ResolutionUnit) + Q_ENUMS(SignDomain) + Q_ENUMS(MarginSide) + Q_ENUMS(AntialiasedElement) + Q_ENUMS(PlottingHint) + Q_ENUMS(Interaction) + Q_ENUMS(SelectionRectMode) + Q_ENUMS(SelectionType) + + Q_FLAGS(AntialiasedElements) + Q_FLAGS(PlottingHints) + Q_FLAGS(MarginSides) + Q_FLAGS(Interactions) +public: +# endif +#endif + + +/*! + Defines the different units in which the image resolution can be specified in the export + functions. + + \see QCustomPlot::savePng, QCustomPlot::saveJpg, QCustomPlot::saveBmp, QCustomPlot::saveRastered +*/ +enum ResolutionUnit { ruDotsPerMeter ///< Resolution is given in dots per meter (dpm) + ,ruDotsPerCentimeter ///< Resolution is given in dots per centimeter (dpcm) + ,ruDotsPerInch ///< Resolution is given in dots per inch (DPI/PPI) + }; + +/*! + Defines how cosmetic pens (pens with numerical width 0) are handled during export. + + \see QCustomPlot::savePdf +*/ +enum ExportPen { epNoCosmetic ///< Cosmetic pens are converted to pens with pixel width 1 when exporting + ,epAllowCosmetic ///< Cosmetic pens are exported normally (e.g. in PDF exports, cosmetic pens always appear as 1 pixel on screen, independent of viewer zoom level) + }; + +/*! + Represents negative and positive sign domain, e.g. for passing to \ref + QCPAbstractPlottable::getKeyRange and \ref QCPAbstractPlottable::getValueRange. + + This is primarily needed when working with logarithmic axis scales, since only one of the sign + domains can be visible at a time. +*/ +enum SignDomain { sdNegative ///< The negative sign domain, i.e. numbers smaller than zero + ,sdBoth ///< Both sign domains, including zero, i.e. all numbers + ,sdPositive ///< The positive sign domain, i.e. numbers greater than zero + }; + +/*! + Defines the sides of a rectangular entity to which margins can be applied. + + \see QCPLayoutElement::setAutoMargins, QCPAxisRect::setAutoMargins +*/ +enum MarginSide { msLeft = 0x01 ///< 0x01 left margin + ,msRight = 0x02 ///< 0x02 right margin + ,msTop = 0x04 ///< 0x04 top margin + ,msBottom = 0x08 ///< 0x08 bottom margin + ,msAll = 0xFF ///< 0xFF all margins + ,msNone = 0x00 ///< 0x00 no margin + }; +Q_DECLARE_FLAGS(MarginSides, MarginSide) + +/*! + Defines what objects of a plot can be forcibly drawn antialiased/not antialiased. If an object is + neither forcibly drawn antialiased nor forcibly drawn not antialiased, it is up to the respective + element how it is drawn. Typically it provides a \a setAntialiased function for this. + + \c AntialiasedElements is a flag of or-combined elements of this enum type. + + \see QCustomPlot::setAntialiasedElements, QCustomPlot::setNotAntialiasedElements +*/ +enum AntialiasedElement { aeAxes = 0x0001 ///< 0x0001 Axis base line and tick marks + ,aeGrid = 0x0002 ///< 0x0002 Grid lines + ,aeSubGrid = 0x0004 ///< 0x0004 Sub grid lines + ,aeLegend = 0x0008 ///< 0x0008 Legend box + ,aeLegendItems = 0x0010 ///< 0x0010 Legend items + ,aePlottables = 0x0020 ///< 0x0020 Main lines of plottables + ,aeItems = 0x0040 ///< 0x0040 Main lines of items + ,aeScatters = 0x0080 ///< 0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap) + ,aeFills = 0x0100 ///< 0x0100 Borders of fills (e.g. under or between graphs) + ,aeZeroLine = 0x0200 ///< 0x0200 Zero-lines, see \ref QCPGrid::setZeroLinePen + ,aeOther = 0x8000 ///< 0x8000 Other elements that don't fit into any of the existing categories + ,aeAll = 0xFFFF ///< 0xFFFF All elements + ,aeNone = 0x0000 ///< 0x0000 No elements + }; +Q_DECLARE_FLAGS(AntialiasedElements, AntialiasedElement) + +/*! + Defines plotting hints that control various aspects of the quality and speed of plotting. + + \see QCustomPlot::setPlottingHints +*/ +enum PlottingHint { phNone = 0x000 ///< 0x000 No hints are set + ,phFastPolylines = 0x001 ///< 0x001 Graph/Curve lines are drawn with a faster method. This reduces the quality especially of the line segment + ///< joins, thus is most effective for pen sizes larger than 1. It is only used for solid line pens. + ,phImmediateRefresh = 0x002 ///< 0x002 causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called with parameter \ref QCustomPlot::rpRefreshHint. + ///< This is set by default to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse). + ,phCacheLabels = 0x004 ///< 0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance. + }; +Q_DECLARE_FLAGS(PlottingHints, PlottingHint) + +/*! + Defines the mouse interactions possible with QCustomPlot. + + \c Interactions is a flag of or-combined elements of this enum type. + + \see QCustomPlot::setInteractions +*/ +enum Interaction { iNone = 0x000 ///< 0x000 None of the interactions are possible + ,iRangeDrag = 0x001 ///< 0x001 Axis ranges are draggable (see \ref QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeDragAxes) + ,iRangeZoom = 0x002 ///< 0x002 Axis ranges are zoomable with the mouse wheel (see \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeZoomAxes) + ,iMultiSelect = 0x004 ///< 0x004 The user can select multiple objects by holding the modifier set by \ref QCustomPlot::setMultiSelectModifier while clicking + ,iSelectPlottables = 0x008 ///< 0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) + ,iSelectAxes = 0x010 ///< 0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts) + ,iSelectLegend = 0x020 ///< 0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) + ,iSelectItems = 0x040 ///< 0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref QCPAbstractItem) + ,iSelectOther = 0x080 ///< 0x080 All other objects are selectable (e.g. your own derived layerables, other layout elements,...) + ,iSelectPlottablesBeyondAxisRect = 0x100 ///< 0x100 When performing plottable selection/hit tests, this flag extends the sensitive area beyond the axis rect + }; +Q_DECLARE_FLAGS(Interactions, Interaction) + +/*! + Defines the behaviour of the selection rect. + + \see QCustomPlot::setSelectionRectMode, QCustomPlot::selectionRect, QCPSelectionRect +*/ +enum SelectionRectMode { srmNone ///< The selection rect is disabled, and all mouse events are forwarded to the underlying objects, e.g. for axis range dragging + ,srmZoom ///< When dragging the mouse, a selection rect becomes active. Upon releasing, the axes that are currently set as range zoom axes (\ref QCPAxisRect::setRangeZoomAxes) will have their ranges zoomed accordingly. + ,srmSelect ///< When dragging the mouse, a selection rect becomes active. Upon releasing, plottable data points that were within the selection rect are selected, if the plottable's selectability setting permits. (See \ref dataselection "data selection mechanism" for details.) + ,srmCustom ///< When dragging the mouse, a selection rect becomes active. It is the programmer's responsibility to connect according slots to the selection rect's signals (e.g. \ref QCPSelectionRect::accepted) in order to process the user interaction. + }; + +/*! + Defines the different ways a plottable can be selected. These images show the effect of the + different selection types, when the indicated selection rect was dragged: + +
+ + + + + + + + +
\image html selectiontype-none.png stNone\image html selectiontype-whole.png stWhole\image html selectiontype-singledata.png stSingleData\image html selectiontype-datarange.png stDataRange\image html selectiontype-multipledataranges.png stMultipleDataRanges
+
+ + \see QCPAbstractPlottable::setSelectable, QCPDataSelection::enforceType +*/ +enum SelectionType { stNone ///< The plottable is not selectable + ,stWhole ///< Selection behaves like \ref stMultipleDataRanges, but if there are any data points selected, the entire plottable is drawn as selected. + ,stSingleData ///< One individual data point can be selected at a time + ,stDataRange ///< Multiple contiguous data points (a data range) can be selected + ,stMultipleDataRanges ///< Any combination of data points/ranges can be selected + }; + +/*! \internal + + Returns whether the specified \a value is considered an invalid data value for plottables (i.e. + is \e nan or \e +/-inf). This function is used to check data validity upon replots, when the + compiler flag \c QCUSTOMPLOT_CHECK_DATA is set. +*/ +inline bool isInvalidData(double value) +{ + return qIsNaN(value) || qIsInf(value); +} + +/*! \internal + \overload + + Checks two arguments instead of one. +*/ +inline bool isInvalidData(double value1, double value2) +{ + return isInvalidData(value1) || isInvalidData(value2); +} + +/*! \internal + + Sets the specified \a side of \a margins to \a value + + \see getMarginValue +*/ +inline void setMarginValue(QMargins &margins, QCP::MarginSide side, int value) +{ + switch (side) + { + case QCP::msLeft: margins.setLeft(value); break; + case QCP::msRight: margins.setRight(value); break; + case QCP::msTop: margins.setTop(value); break; + case QCP::msBottom: margins.setBottom(value); break; + case QCP::msAll: margins = QMargins(value, value, value, value); break; + default: break; + } +} + +/*! \internal + + Returns the value of the specified \a side of \a margins. If \a side is \ref QCP::msNone or + \ref QCP::msAll, returns 0. + + \see setMarginValue +*/ +inline int getMarginValue(const QMargins &margins, QCP::MarginSide side) +{ + switch (side) + { + case QCP::msLeft: return margins.left(); + case QCP::msRight: return margins.right(); + case QCP::msTop: return margins.top(); + case QCP::msBottom: return margins.bottom(); + default: break; + } + return 0; +} + +// for newer Qt versions we have to declare the enums/flags as metatypes inside the namespace using Q_ENUM_NS/Q_FLAG_NS: +// if you change anything here, don't forget to change it for older Qt versions below, too, +// and at the start of the namespace in the fake moc-run class +#if QT_VERSION >= 0x060200 +Q_ENUM_NS(ExportPen) +Q_ENUM_NS(ResolutionUnit) +Q_ENUM_NS(SignDomain) +Q_ENUM_NS(MarginSide) +Q_ENUM_NS(AntialiasedElement) +Q_ENUM_NS(PlottingHint) +Q_ENUM_NS(Interaction) +Q_ENUM_NS(SelectionRectMode) +Q_ENUM_NS(SelectionType) + +Q_FLAG_NS(AntialiasedElements) +Q_FLAG_NS(PlottingHints) +Q_FLAG_NS(MarginSides) +Q_FLAG_NS(Interactions) +#else +extern const QMetaObject staticMetaObject; +#endif + +} // end of namespace QCP + +Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::AntialiasedElements) +Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::PlottingHints) +Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::MarginSides) +Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::Interactions) + +// for older Qt versions we have to declare the enums/flags as metatypes outside the namespace using Q_DECLARE_METATYPE: +// if you change anything here, don't forget to change it for newer Qt versions above, too, +// and at the start of the namespace in the fake moc-run class +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0) +Q_DECLARE_METATYPE(QCP::ExportPen) +Q_DECLARE_METATYPE(QCP::ResolutionUnit) +Q_DECLARE_METATYPE(QCP::SignDomain) +Q_DECLARE_METATYPE(QCP::MarginSide) +Q_DECLARE_METATYPE(QCP::AntialiasedElement) +Q_DECLARE_METATYPE(QCP::PlottingHint) +Q_DECLARE_METATYPE(QCP::Interaction) +Q_DECLARE_METATYPE(QCP::SelectionRectMode) +Q_DECLARE_METATYPE(QCP::SelectionType) +#endif + +/* end of 'src/global.h' */ + + +/* including file 'src/vector2d.h' */ +/* modified 2022-11-06T12:45:56, size 4988 */ + +class QCP_LIB_DECL QCPVector2D +{ +public: + QCPVector2D(); + QCPVector2D(double x, double y); + QCPVector2D(const QPoint &point); + QCPVector2D(const QPointF &point); + + // getters: + double x() const { return mX; } + double y() const { return mY; } + double &rx() { return mX; } + double &ry() { return mY; } + + // setters: + void setX(double x) { mX = x; } + void setY(double y) { mY = y; } + + // non-virtual methods: + double length() const { return qSqrt(mX*mX+mY*mY); } + double lengthSquared() const { return mX*mX+mY*mY; } + double angle() const { return qAtan2(mY, mX); } + QPoint toPoint() const { return QPoint(int(mX), int(mY)); } + QPointF toPointF() const { return QPointF(mX, mY); } + + bool isNull() const { return qIsNull(mX) && qIsNull(mY); } + void normalize(); + QCPVector2D normalized() const; + QCPVector2D perpendicular() const { return QCPVector2D(-mY, mX); } + double dot(const QCPVector2D &vec) const { return mX*vec.mX+mY*vec.mY; } + double distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const; + double distanceSquaredToLine(const QLineF &line) const; + double distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const; + + QCPVector2D &operator*=(double factor); + QCPVector2D &operator/=(double divisor); + QCPVector2D &operator+=(const QCPVector2D &vector); + QCPVector2D &operator-=(const QCPVector2D &vector); + +private: + // property members: + double mX, mY; + + friend inline const QCPVector2D operator*(double factor, const QCPVector2D &vec); + friend inline const QCPVector2D operator*(const QCPVector2D &vec, double factor); + friend inline const QCPVector2D operator/(const QCPVector2D &vec, double divisor); + friend inline const QCPVector2D operator+(const QCPVector2D &vec1, const QCPVector2D &vec2); + friend inline const QCPVector2D operator-(const QCPVector2D &vec1, const QCPVector2D &vec2); + friend inline const QCPVector2D operator-(const QCPVector2D &vec); +}; +Q_DECLARE_TYPEINFO(QCPVector2D, Q_MOVABLE_TYPE); + +inline const QCPVector2D operator*(double factor, const QCPVector2D &vec) { return QCPVector2D(vec.mX*factor, vec.mY*factor); } +inline const QCPVector2D operator*(const QCPVector2D &vec, double factor) { return QCPVector2D(vec.mX*factor, vec.mY*factor); } +inline const QCPVector2D operator/(const QCPVector2D &vec, double divisor) { return QCPVector2D(vec.mX/divisor, vec.mY/divisor); } +inline const QCPVector2D operator+(const QCPVector2D &vec1, const QCPVector2D &vec2) { return QCPVector2D(vec1.mX+vec2.mX, vec1.mY+vec2.mY); } +inline const QCPVector2D operator-(const QCPVector2D &vec1, const QCPVector2D &vec2) { return QCPVector2D(vec1.mX-vec2.mX, vec1.mY-vec2.mY); } +inline const QCPVector2D operator-(const QCPVector2D &vec) { return QCPVector2D(-vec.mX, -vec.mY); } + +/*! \relates QCPVector2D + + Prints \a vec in a human readable format to the qDebug output. +*/ +inline QDebug operator<< (QDebug d, const QCPVector2D &vec) +{ + d.nospace() << "QCPVector2D(" << vec.x() << ", " << vec.y() << ")"; + return d.space(); +} + +/* end of 'src/vector2d.h' */ + + +/* including file 'src/painter.h' */ +/* modified 2022-11-06T12:45:56, size 4035 */ + +class QCP_LIB_DECL QCPPainter : public QPainter +{ + Q_GADGET +public: + /*! + Defines special modes the painter can operate in. They disable or enable certain subsets of features/fixes/workarounds, + depending on whether they are wanted on the respective output device. + */ + enum PainterMode { pmDefault = 0x00 ///< 0x00 Default mode for painting on screen devices + ,pmVectorized = 0x01 ///< 0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fixes. + ,pmNoCaching = 0x02 ///< 0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixmap labels + ,pmNonCosmetic = 0x04 ///< 0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width 1 pixel in the vector image/pdf viewer, independent of zoom.) + }; + Q_ENUMS(PainterMode) + Q_FLAGS(PainterModes) + Q_DECLARE_FLAGS(PainterModes, PainterMode) + + QCPPainter(); + explicit QCPPainter(QPaintDevice *device); + + // getters: + bool antialiasing() const { return testRenderHint(QPainter::Antialiasing); } + PainterModes modes() const { return mModes; } + + // setters: + void setAntialiasing(bool enabled); + void setMode(PainterMode mode, bool enabled=true); + void setModes(PainterModes modes); + + // methods hiding non-virtual base class functions (QPainter bug workarounds): + bool begin(QPaintDevice *device); + void setPen(const QPen &pen); + void setPen(const QColor &color); + void setPen(Qt::PenStyle penStyle); + void drawLine(const QLineF &line); + void drawLine(const QPointF &p1, const QPointF &p2) {drawLine(QLineF(p1, p2));} + void save(); + void restore(); + + // non-virtual methods: + void makeNonCosmetic(); + +protected: + // property members: + PainterModes mModes; + bool mIsAntialiasing; + + // non-property members: + QStack mAntialiasingStack; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPainter::PainterModes) +Q_DECLARE_METATYPE(QCPPainter::PainterMode) + +/* end of 'src/painter.h' */ + + +/* including file 'src/paintbuffer.h' */ +/* modified 2022-11-06T12:45:56, size 5006 */ + +class QCP_LIB_DECL QCPAbstractPaintBuffer +{ +public: + explicit QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio); + virtual ~QCPAbstractPaintBuffer(); + + // getters: + QSize size() const { return mSize; } + bool invalidated() const { return mInvalidated; } + double devicePixelRatio() const { return mDevicePixelRatio; } + + // setters: + void setSize(const QSize &size); + void setInvalidated(bool invalidated=true); + void setDevicePixelRatio(double ratio); + + // introduced virtual methods: + virtual QCPPainter *startPainting() = 0; + virtual void donePainting() {} + virtual void draw(QCPPainter *painter) const = 0; + virtual void clear(const QColor &color) = 0; + +protected: + // property members: + QSize mSize; + double mDevicePixelRatio; + + // non-property members: + bool mInvalidated; + + // introduced virtual methods: + virtual void reallocateBuffer() = 0; +}; + + +class QCP_LIB_DECL QCPPaintBufferPixmap : public QCPAbstractPaintBuffer +{ +public: + explicit QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio); + virtual ~QCPPaintBufferPixmap() Q_DECL_OVERRIDE; + + // reimplemented virtual methods: + virtual QCPPainter *startPainting() Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE; + void clear(const QColor &color) Q_DECL_OVERRIDE; + +protected: + // non-property members: + QPixmap mBuffer; + + // reimplemented virtual methods: + virtual void reallocateBuffer() Q_DECL_OVERRIDE; +}; + + +#ifdef QCP_OPENGL_PBUFFER +class QCP_LIB_DECL QCPPaintBufferGlPbuffer : public QCPAbstractPaintBuffer +{ +public: + explicit QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples); + virtual ~QCPPaintBufferGlPbuffer() Q_DECL_OVERRIDE; + + // reimplemented virtual methods: + virtual QCPPainter *startPainting() Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE; + void clear(const QColor &color) Q_DECL_OVERRIDE; + +protected: + // non-property members: + QGLPixelBuffer *mGlPBuffer; + int mMultisamples; + + // reimplemented virtual methods: + virtual void reallocateBuffer() Q_DECL_OVERRIDE; +}; +#endif // QCP_OPENGL_PBUFFER + + +#ifdef QCP_OPENGL_FBO +class QCP_LIB_DECL QCPPaintBufferGlFbo : public QCPAbstractPaintBuffer +{ +public: + explicit QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer glContext, QWeakPointer glPaintDevice); + virtual ~QCPPaintBufferGlFbo() Q_DECL_OVERRIDE; + + // reimplemented virtual methods: + virtual QCPPainter *startPainting() Q_DECL_OVERRIDE; + virtual void donePainting() Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE; + void clear(const QColor &color) Q_DECL_OVERRIDE; + +protected: + // non-property members: + QWeakPointer mGlContext; + QWeakPointer mGlPaintDevice; + QOpenGLFramebufferObject *mGlFrameBuffer; + + // reimplemented virtual methods: + virtual void reallocateBuffer() Q_DECL_OVERRIDE; +}; +#endif // QCP_OPENGL_FBO + +/* end of 'src/paintbuffer.h' */ + + +/* including file 'src/layer.h' */ +/* modified 2022-11-06T12:45:56, size 7038 */ + +class QCP_LIB_DECL QCPLayer : public QObject +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot) + Q_PROPERTY(QString name READ name) + Q_PROPERTY(int index READ index) + Q_PROPERTY(QList children READ children) + Q_PROPERTY(bool visible READ visible WRITE setVisible) + Q_PROPERTY(LayerMode mode READ mode WRITE setMode) + /// \endcond +public: + + /*! + Defines the different rendering modes of a layer. Depending on the mode, certain layers can be + replotted individually, without the need to replot (possibly complex) layerables on other + layers. + + \see setMode + */ + enum LayerMode { lmLogical ///< Layer is used only for rendering order, and shares paint buffer with all other adjacent logical layers. + ,lmBuffered ///< Layer has its own paint buffer and may be replotted individually (see \ref replot). + }; + Q_ENUMS(LayerMode) + + QCPLayer(QCustomPlot* parentPlot, const QString &layerName); + virtual ~QCPLayer(); + + // getters: + QCustomPlot *parentPlot() const { return mParentPlot; } + QString name() const { return mName; } + int index() const { return mIndex; } + QList children() const { return mChildren; } + bool visible() const { return mVisible; } + LayerMode mode() const { return mMode; } + + // setters: + void setVisible(bool visible); + void setMode(LayerMode mode); + + // non-virtual methods: + void replot(); + +protected: + // property members: + QCustomPlot *mParentPlot; + QString mName; + int mIndex; + QList mChildren; + bool mVisible; + LayerMode mMode; + + // non-property members: + QWeakPointer mPaintBuffer; + + // non-virtual methods: + void draw(QCPPainter *painter); + void drawToPaintBuffer(); + void addChild(QCPLayerable *layerable, bool prepend); + void removeChild(QCPLayerable *layerable); + +private: + Q_DISABLE_COPY(QCPLayer) + + friend class QCustomPlot; + friend class QCPLayerable; +}; +Q_DECLARE_METATYPE(QCPLayer::LayerMode) + +class QCP_LIB_DECL QCPLayerable : public QObject +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(bool visible READ visible WRITE setVisible) + Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot) + Q_PROPERTY(QCPLayerable* parentLayerable READ parentLayerable) + Q_PROPERTY(QCPLayer* layer READ layer WRITE setLayer NOTIFY layerChanged) + Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased) + /// \endcond +public: + QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=nullptr); + virtual ~QCPLayerable(); + + // getters: + bool visible() const { return mVisible; } + QCustomPlot *parentPlot() const { return mParentPlot; } + QCPLayerable *parentLayerable() const { return mParentLayerable.data(); } + QCPLayer *layer() const { return mLayer; } + bool antialiased() const { return mAntialiased; } + + // setters: + void setVisible(bool on); + Q_SLOT bool setLayer(QCPLayer *layer); + bool setLayer(const QString &layerName); + void setAntialiased(bool enabled); + + // introduced virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const; + + // non-property methods: + bool realVisibility() const; + +signals: + void layerChanged(QCPLayer *newLayer); + +protected: + // property members: + bool mVisible; + QCustomPlot *mParentPlot; + QPointer mParentLayerable; + QCPLayer *mLayer; + bool mAntialiased; + + // introduced virtual methods: + virtual void parentPlotInitialized(QCustomPlot *parentPlot); + virtual QCP::Interaction selectionCategory() const; + virtual QRect clipRect() const; + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const = 0; + virtual void draw(QCPPainter *painter) = 0; + // selection events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + // low-level mouse events: + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details); + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos); + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos); + virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details); + virtual void wheelEvent(QWheelEvent *event); + + // non-property methods: + void initializeParentPlot(QCustomPlot *parentPlot); + void setParentLayerable(QCPLayerable* parentLayerable); + bool moveToLayer(QCPLayer *layer, bool prepend); + void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const; + +private: + Q_DISABLE_COPY(QCPLayerable) + + friend class QCustomPlot; + friend class QCPLayer; + friend class QCPAxisRect; +}; + +/* end of 'src/layer.h' */ + + +/* including file 'src/axis/range.h' */ +/* modified 2022-11-06T12:45:56, size 5280 */ + +class QCP_LIB_DECL QCPRange +{ +public: + double lower, upper; + + QCPRange(); + QCPRange(double lower, double upper); + + bool operator==(const QCPRange& other) const { return lower == other.lower && upper == other.upper; } + bool operator!=(const QCPRange& other) const { return !(*this == other); } + + QCPRange &operator+=(const double& value) { lower+=value; upper+=value; return *this; } + QCPRange &operator-=(const double& value) { lower-=value; upper-=value; return *this; } + QCPRange &operator*=(const double& value) { lower*=value; upper*=value; return *this; } + QCPRange &operator/=(const double& value) { lower/=value; upper/=value; return *this; } + friend inline const QCPRange operator+(const QCPRange&, double); + friend inline const QCPRange operator+(double, const QCPRange&); + friend inline const QCPRange operator-(const QCPRange& range, double value); + friend inline const QCPRange operator*(const QCPRange& range, double value); + friend inline const QCPRange operator*(double value, const QCPRange& range); + friend inline const QCPRange operator/(const QCPRange& range, double value); + + double size() const { return upper-lower; } + double center() const { return (upper+lower)*0.5; } + void normalize() { if (lower > upper) qSwap(lower, upper); } + void expand(const QCPRange &otherRange); + void expand(double includeCoord); + QCPRange expanded(const QCPRange &otherRange) const; + QCPRange expanded(double includeCoord) const; + QCPRange bounded(double lowerBound, double upperBound) const; + QCPRange sanitizedForLogScale() const; + QCPRange sanitizedForLinScale() const; + bool contains(double value) const { return value >= lower && value <= upper; } + + static bool validRange(double lower, double upper); + static bool validRange(const QCPRange &range); + static const double minRange; + static const double maxRange; + +}; +Q_DECLARE_TYPEINFO(QCPRange, Q_MOVABLE_TYPE); + +/*! \relates QCPRange + + Prints \a range in a human readable format to the qDebug output. +*/ +inline QDebug operator<< (QDebug d, const QCPRange &range) +{ + d.nospace() << "QCPRange(" << range.lower << ", " << range.upper << ")"; + return d.space(); +} + +/*! + Adds \a value to both boundaries of the range. +*/ +inline const QCPRange operator+(const QCPRange& range, double value) +{ + QCPRange result(range); + result += value; + return result; +} + +/*! + Adds \a value to both boundaries of the range. +*/ +inline const QCPRange operator+(double value, const QCPRange& range) +{ + QCPRange result(range); + result += value; + return result; +} + +/*! + Subtracts \a value from both boundaries of the range. +*/ +inline const QCPRange operator-(const QCPRange& range, double value) +{ + QCPRange result(range); + result -= value; + return result; +} + +/*! + Multiplies both boundaries of the range by \a value. +*/ +inline const QCPRange operator*(const QCPRange& range, double value) +{ + QCPRange result(range); + result *= value; + return result; +} + +/*! + Multiplies both boundaries of the range by \a value. +*/ +inline const QCPRange operator*(double value, const QCPRange& range) +{ + QCPRange result(range); + result *= value; + return result; +} + +/*! + Divides both boundaries of the range by \a value. +*/ +inline const QCPRange operator/(const QCPRange& range, double value) +{ + QCPRange result(range); + result /= value; + return result; +} + +/* end of 'src/axis/range.h' */ + + +/* including file 'src/selection.h' */ +/* modified 2022-11-06T12:45:56, size 8569 */ + +class QCP_LIB_DECL QCPDataRange +{ +public: + QCPDataRange(); + QCPDataRange(int begin, int end); + + bool operator==(const QCPDataRange& other) const { return mBegin == other.mBegin && mEnd == other.mEnd; } + bool operator!=(const QCPDataRange& other) const { return !(*this == other); } + + // getters: + int begin() const { return mBegin; } + int end() const { return mEnd; } + int size() const { return mEnd-mBegin; } + int length() const { return size(); } + + // setters: + void setBegin(int begin) { mBegin = begin; } + void setEnd(int end) { mEnd = end; } + + // non-property methods: + bool isValid() const { return (mEnd >= mBegin) && (mBegin >= 0); } + bool isEmpty() const { return length() == 0; } + QCPDataRange bounded(const QCPDataRange &other) const; + QCPDataRange expanded(const QCPDataRange &other) const; + QCPDataRange intersection(const QCPDataRange &other) const; + QCPDataRange adjusted(int changeBegin, int changeEnd) const { return QCPDataRange(mBegin+changeBegin, mEnd+changeEnd); } + bool intersects(const QCPDataRange &other) const; + bool contains(const QCPDataRange &other) const; + +private: + // property members: + int mBegin, mEnd; + +}; +Q_DECLARE_TYPEINFO(QCPDataRange, Q_MOVABLE_TYPE); + + +class QCP_LIB_DECL QCPDataSelection +{ +public: + explicit QCPDataSelection(); + explicit QCPDataSelection(const QCPDataRange &range); + + bool operator==(const QCPDataSelection& other) const; + bool operator!=(const QCPDataSelection& other) const { return !(*this == other); } + QCPDataSelection &operator+=(const QCPDataSelection& other); + QCPDataSelection &operator+=(const QCPDataRange& other); + QCPDataSelection &operator-=(const QCPDataSelection& other); + QCPDataSelection &operator-=(const QCPDataRange& other); + friend inline const QCPDataSelection operator+(const QCPDataSelection& a, const QCPDataSelection& b); + friend inline const QCPDataSelection operator+(const QCPDataRange& a, const QCPDataSelection& b); + friend inline const QCPDataSelection operator+(const QCPDataSelection& a, const QCPDataRange& b); + friend inline const QCPDataSelection operator+(const QCPDataRange& a, const QCPDataRange& b); + friend inline const QCPDataSelection operator-(const QCPDataSelection& a, const QCPDataSelection& b); + friend inline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataSelection& b); + friend inline const QCPDataSelection operator-(const QCPDataSelection& a, const QCPDataRange& b); + friend inline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataRange& b); + + // getters: + int dataRangeCount() const { return static_cast(mDataRanges.size()); } + int dataPointCount() const; + QCPDataRange dataRange(int index=0) const; + QList dataRanges() const { return mDataRanges; } + QCPDataRange span() const; + + // non-property methods: + void addDataRange(const QCPDataRange &dataRange, bool simplify=true); + void clear(); + bool isEmpty() const { return mDataRanges.isEmpty(); } + void simplify(); + void enforceType(QCP::SelectionType type); + bool contains(const QCPDataSelection &other) const; + QCPDataSelection intersection(const QCPDataRange &other) const; + QCPDataSelection intersection(const QCPDataSelection &other) const; + QCPDataSelection inverse(const QCPDataRange &outerRange) const; + +private: + // property members: + QList mDataRanges; + + inline static bool lessThanDataRangeBegin(const QCPDataRange &a, const QCPDataRange &b) { return a.begin() < b.begin(); } +}; +Q_DECLARE_METATYPE(QCPDataSelection) + + +/*! + Return a \ref QCPDataSelection with the data points in \a a joined with the data points in \a b. + The resulting data selection is already simplified (see \ref QCPDataSelection::simplify). +*/ +inline const QCPDataSelection operator+(const QCPDataSelection& a, const QCPDataSelection& b) +{ + QCPDataSelection result(a); + result += b; + return result; +} + +/*! + Return a \ref QCPDataSelection with the data points in \a a joined with the data points in \a b. + The resulting data selection is already simplified (see \ref QCPDataSelection::simplify). +*/ +inline const QCPDataSelection operator+(const QCPDataRange& a, const QCPDataSelection& b) +{ + QCPDataSelection result(a); + result += b; + return result; +} + +/*! + Return a \ref QCPDataSelection with the data points in \a a joined with the data points in \a b. + The resulting data selection is already simplified (see \ref QCPDataSelection::simplify). +*/ +inline const QCPDataSelection operator+(const QCPDataSelection& a, const QCPDataRange& b) +{ + QCPDataSelection result(a); + result += b; + return result; +} + +/*! + Return a \ref QCPDataSelection with the data points in \a a joined with the data points in \a b. + The resulting data selection is already simplified (see \ref QCPDataSelection::simplify). +*/ +inline const QCPDataSelection operator+(const QCPDataRange& a, const QCPDataRange& b) +{ + QCPDataSelection result(a); + result += b; + return result; +} + +/*! + Return a \ref QCPDataSelection with the data points which are in \a a but not in \a b. +*/ +inline const QCPDataSelection operator-(const QCPDataSelection& a, const QCPDataSelection& b) +{ + QCPDataSelection result(a); + result -= b; + return result; +} + +/*! + Return a \ref QCPDataSelection with the data points which are in \a a but not in \a b. +*/ +inline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataSelection& b) +{ + QCPDataSelection result(a); + result -= b; + return result; +} + +/*! + Return a \ref QCPDataSelection with the data points which are in \a a but not in \a b. +*/ +inline const QCPDataSelection operator-(const QCPDataSelection& a, const QCPDataRange& b) +{ + QCPDataSelection result(a); + result -= b; + return result; +} + +/*! + Return a \ref QCPDataSelection with the data points which are in \a a but not in \a b. +*/ +inline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataRange& b) +{ + QCPDataSelection result(a); + result -= b; + return result; +} + +/*! \relates QCPDataRange + + Prints \a dataRange in a human readable format to the qDebug output. +*/ +inline QDebug operator<< (QDebug d, const QCPDataRange &dataRange) +{ + d.nospace() << "QCPDataRange(" << dataRange.begin() << ", " << dataRange.end() << ")"; + return d; +} + +/*! \relates QCPDataSelection + + Prints \a selection in a human readable format to the qDebug output. +*/ +inline QDebug operator<< (QDebug d, const QCPDataSelection &selection) +{ + d.nospace() << "QCPDataSelection("; + for (int i=0; i elements(QCP::MarginSide side) const { return mChildren.value(side); } + bool isEmpty() const; + void clear(); + +protected: + // non-property members: + QCustomPlot *mParentPlot; + QHash > mChildren; + + // introduced virtual methods: + virtual int commonMargin(QCP::MarginSide side) const; + + // non-virtual methods: + void addChild(QCP::MarginSide side, QCPLayoutElement *element); + void removeChild(QCP::MarginSide side, QCPLayoutElement *element); + +private: + Q_DISABLE_COPY(QCPMarginGroup) + + friend class QCPLayoutElement; +}; + + +class QCP_LIB_DECL QCPLayoutElement : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPLayout* layout READ layout) + Q_PROPERTY(QRect rect READ rect) + Q_PROPERTY(QRect outerRect READ outerRect WRITE setOuterRect) + Q_PROPERTY(QMargins margins READ margins WRITE setMargins) + Q_PROPERTY(QMargins minimumMargins READ minimumMargins WRITE setMinimumMargins) + Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize) + Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize) + Q_PROPERTY(SizeConstraintRect sizeConstraintRect READ sizeConstraintRect WRITE setSizeConstraintRect) + /// \endcond +public: + /*! + Defines the phases of the update process, that happens just before a replot. At each phase, + \ref update is called with the according UpdatePhase value. + */ + enum UpdatePhase { upPreparation ///< Phase used for any type of preparation that needs to be done before margin calculation and layout + ,upMargins ///< Phase in which the margins are calculated and set + ,upLayout ///< Final phase in which the layout system places the rects of the elements + }; + Q_ENUMS(UpdatePhase) + + /*! + Defines to which rect of a layout element the size constraints that can be set via \ref + setMinimumSize and \ref setMaximumSize apply. The outer rect (\ref outerRect) includes the + margins (e.g. in the case of a QCPAxisRect the axis labels), whereas the inner rect (\ref rect) + does not. + + \see setSizeConstraintRect + */ + enum SizeConstraintRect { scrInnerRect ///< Minimum/Maximum size constraints apply to inner rect + , scrOuterRect ///< Minimum/Maximum size constraints apply to outer rect, thus include layout element margins + }; + Q_ENUMS(SizeConstraintRect) + + explicit QCPLayoutElement(QCustomPlot *parentPlot=nullptr); + virtual ~QCPLayoutElement() Q_DECL_OVERRIDE; + + // getters: + QCPLayout *layout() const { return mParentLayout; } + QRect rect() const { return mRect; } + QRect outerRect() const { return mOuterRect; } + QMargins margins() const { return mMargins; } + QMargins minimumMargins() const { return mMinimumMargins; } + QCP::MarginSides autoMargins() const { return mAutoMargins; } + QSize minimumSize() const { return mMinimumSize; } + QSize maximumSize() const { return mMaximumSize; } + SizeConstraintRect sizeConstraintRect() const { return mSizeConstraintRect; } + QCPMarginGroup *marginGroup(QCP::MarginSide side) const { return mMarginGroups.value(side, nullptr); } + QHash marginGroups() const { return mMarginGroups; } + + // setters: + void setOuterRect(const QRect &rect); + void setMargins(const QMargins &margins); + void setMinimumMargins(const QMargins &margins); + void setAutoMargins(QCP::MarginSides sides); + void setMinimumSize(const QSize &size); + void setMinimumSize(int width, int height); + void setMaximumSize(const QSize &size); + void setMaximumSize(int width, int height); + void setSizeConstraintRect(SizeConstraintRect constraintRect); + void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group); + + // introduced virtual methods: + virtual void update(UpdatePhase phase); + virtual QSize minimumOuterSizeHint() const; + virtual QSize maximumOuterSizeHint() const; + virtual QList elements(bool recursive) const; + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + +protected: + // property members: + QCPLayout *mParentLayout; + QSize mMinimumSize, mMaximumSize; + SizeConstraintRect mSizeConstraintRect; + QRect mRect, mOuterRect; + QMargins mMargins, mMinimumMargins; + QCP::MarginSides mAutoMargins; + QHash mMarginGroups; + + // introduced virtual methods: + virtual int calculateAutoMargin(QCP::MarginSide side); + virtual void layoutChanged(); + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE { Q_UNUSED(painter) } + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE { Q_UNUSED(painter) } + virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE; + +private: + Q_DISABLE_COPY(QCPLayoutElement) + + friend class QCustomPlot; + friend class QCPLayout; + friend class QCPMarginGroup; +}; +Q_DECLARE_METATYPE(QCPLayoutElement::UpdatePhase) + + +class QCP_LIB_DECL QCPLayout : public QCPLayoutElement +{ + Q_OBJECT +public: + explicit QCPLayout(); + + // reimplemented virtual methods: + virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE; + virtual QList elements(bool recursive) const Q_DECL_OVERRIDE; + + // introduced virtual methods: + virtual int elementCount() const = 0; + virtual QCPLayoutElement* elementAt(int index) const = 0; + virtual QCPLayoutElement* takeAt(int index) = 0; + virtual bool take(QCPLayoutElement* element) = 0; + virtual void simplify(); + + // non-virtual methods: + bool removeAt(int index); + bool remove(QCPLayoutElement* element); + void clear(); + +protected: + // introduced virtual methods: + virtual void updateLayout(); + + // non-virtual methods: + void sizeConstraintsChanged() const; + void adoptElement(QCPLayoutElement *el); + void releaseElement(QCPLayoutElement *el); + QVector getSectionSizes(QVector maxSizes, QVector minSizes, QVector stretchFactors, int totalSize) const; + static QSize getFinalMinimumOuterSize(const QCPLayoutElement *el); + static QSize getFinalMaximumOuterSize(const QCPLayoutElement *el); + +private: + Q_DISABLE_COPY(QCPLayout) + friend class QCPLayoutElement; +}; + + +class QCP_LIB_DECL QCPLayoutGrid : public QCPLayout +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(int rowCount READ rowCount) + Q_PROPERTY(int columnCount READ columnCount) + Q_PROPERTY(QList columnStretchFactors READ columnStretchFactors WRITE setColumnStretchFactors) + Q_PROPERTY(QList rowStretchFactors READ rowStretchFactors WRITE setRowStretchFactors) + Q_PROPERTY(int columnSpacing READ columnSpacing WRITE setColumnSpacing) + Q_PROPERTY(int rowSpacing READ rowSpacing WRITE setRowSpacing) + Q_PROPERTY(FillOrder fillOrder READ fillOrder WRITE setFillOrder) + Q_PROPERTY(int wrap READ wrap WRITE setWrap) + /// \endcond +public: + + /*! + Defines in which direction the grid is filled when using \ref addElement(QCPLayoutElement*). + The column/row at which wrapping into the next row/column occurs can be specified with \ref + setWrap. + + \see setFillOrder + */ + enum FillOrder { foRowsFirst ///< Rows are filled first, and a new element is wrapped to the next column if the row count would exceed \ref setWrap. + ,foColumnsFirst ///< Columns are filled first, and a new element is wrapped to the next row if the column count would exceed \ref setWrap. + }; + Q_ENUMS(FillOrder) + + explicit QCPLayoutGrid(); + virtual ~QCPLayoutGrid() Q_DECL_OVERRIDE; + + // getters: + int rowCount() const { return static_cast(mElements.size()); } + int columnCount() const { return mElements.size() > 0 ? static_cast(mElements.first().size()) : 0; } + QList columnStretchFactors() const { return mColumnStretchFactors; } + QList rowStretchFactors() const { return mRowStretchFactors; } + int columnSpacing() const { return mColumnSpacing; } + int rowSpacing() const { return mRowSpacing; } + int wrap() const { return mWrap; } + FillOrder fillOrder() const { return mFillOrder; } + + // setters: + void setColumnStretchFactor(int column, double factor); + void setColumnStretchFactors(const QList &factors); + void setRowStretchFactor(int row, double factor); + void setRowStretchFactors(const QList &factors); + void setColumnSpacing(int pixels); + void setRowSpacing(int pixels); + void setWrap(int count); + void setFillOrder(FillOrder order, bool rearrange=true); + + // reimplemented virtual methods: + virtual void updateLayout() Q_DECL_OVERRIDE; + virtual int elementCount() const Q_DECL_OVERRIDE { return rowCount()*columnCount(); } + virtual QCPLayoutElement* elementAt(int index) const Q_DECL_OVERRIDE; + virtual QCPLayoutElement* takeAt(int index) Q_DECL_OVERRIDE; + virtual bool take(QCPLayoutElement* element) Q_DECL_OVERRIDE; + virtual QList elements(bool recursive) const Q_DECL_OVERRIDE; + virtual void simplify() Q_DECL_OVERRIDE; + virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE; + virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE; + + // non-virtual methods: + QCPLayoutElement *element(int row, int column) const; + bool addElement(int row, int column, QCPLayoutElement *element); + bool addElement(QCPLayoutElement *element); + bool hasElement(int row, int column); + void expandTo(int newRowCount, int newColumnCount); + void insertRow(int newIndex); + void insertColumn(int newIndex); + int rowColToIndex(int row, int column) const; + void indexToRowCol(int index, int &row, int &column) const; + +protected: + // property members: + QList > mElements; + QList mColumnStretchFactors; + QList mRowStretchFactors; + int mColumnSpacing, mRowSpacing; + int mWrap; + FillOrder mFillOrder; + + // non-virtual methods: + void getMinimumRowColSizes(QVector *minColWidths, QVector *minRowHeights) const; + void getMaximumRowColSizes(QVector *maxColWidths, QVector *maxRowHeights) const; + +private: + Q_DISABLE_COPY(QCPLayoutGrid) +}; +Q_DECLARE_METATYPE(QCPLayoutGrid::FillOrder) + + +class QCP_LIB_DECL QCPLayoutInset : public QCPLayout +{ + Q_OBJECT +public: + /*! + Defines how the placement and sizing is handled for a certain element in a QCPLayoutInset. + */ + enum InsetPlacement { ipFree ///< The element may be positioned/sized arbitrarily, see \ref setInsetRect + ,ipBorderAligned ///< The element is aligned to one of the layout sides, see \ref setInsetAlignment + }; + Q_ENUMS(InsetPlacement) + + explicit QCPLayoutInset(); + virtual ~QCPLayoutInset() Q_DECL_OVERRIDE; + + // getters: + InsetPlacement insetPlacement(int index) const; + Qt::Alignment insetAlignment(int index) const; + QRectF insetRect(int index) const; + + // setters: + void setInsetPlacement(int index, InsetPlacement placement); + void setInsetAlignment(int index, Qt::Alignment alignment); + void setInsetRect(int index, const QRectF &rect); + + // reimplemented virtual methods: + virtual void updateLayout() Q_DECL_OVERRIDE; + virtual int elementCount() const Q_DECL_OVERRIDE; + virtual QCPLayoutElement* elementAt(int index) const Q_DECL_OVERRIDE; + virtual QCPLayoutElement* takeAt(int index) Q_DECL_OVERRIDE; + virtual bool take(QCPLayoutElement* element) Q_DECL_OVERRIDE; + virtual void simplify() Q_DECL_OVERRIDE {} + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + // non-virtual methods: + void addElement(QCPLayoutElement *element, Qt::Alignment alignment); + void addElement(QCPLayoutElement *element, const QRectF &rect); + +protected: + // property members: + QList mElements; + QList mInsetPlacement; + QList mInsetAlignment; + QList mInsetRect; + +private: + Q_DISABLE_COPY(QCPLayoutInset) +}; +Q_DECLARE_METATYPE(QCPLayoutInset::InsetPlacement) + +/* end of 'src/layout.h' */ + + +/* including file 'src/lineending.h' */ +/* modified 2022-11-06T12:45:56, size 4426 */ + +class QCP_LIB_DECL QCPLineEnding +{ + Q_GADGET +public: + /*! + Defines the type of ending decoration for line-like items, e.g. an arrow. + + \image html QCPLineEnding.png + + The width and length of these decorations can be controlled with the functions \ref setWidth + and \ref setLength. Some decorations like \ref esDisc, \ref esSquare, \ref esDiamond and \ref esBar only + support a width, the length property is ignored. + + \see QCPItemLine::setHead, QCPItemLine::setTail, QCPItemCurve::setHead, QCPItemCurve::setTail, QCPAxis::setLowerEnding, QCPAxis::setUpperEnding + */ + enum EndingStyle { esNone ///< No ending decoration + ,esFlatArrow ///< A filled arrow head with a straight/flat back (a triangle) + ,esSpikeArrow ///< A filled arrow head with an indented back + ,esLineArrow ///< A non-filled arrow head with open back + ,esDisc ///< A filled circle + ,esSquare ///< A filled square + ,esDiamond ///< A filled diamond (45 degrees rotated square) + ,esBar ///< A bar perpendicular to the line + ,esHalfBar ///< A bar perpendicular to the line, pointing out to only one side (to which side can be changed with \ref setInverted) + ,esSkewedBar ///< A bar that is skewed (skew controllable via \ref setLength) + }; + Q_ENUMS(EndingStyle) + + QCPLineEnding(); + QCPLineEnding(EndingStyle style, double width=8, double length=10, bool inverted=false); + + // getters: + EndingStyle style() const { return mStyle; } + double width() const { return mWidth; } + double length() const { return mLength; } + bool inverted() const { return mInverted; } + + // setters: + void setStyle(EndingStyle style); + void setWidth(double width); + void setLength(double length); + void setInverted(bool inverted); + + // non-property methods: + double boundingDistance() const; + double realLength() const; + void draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const; + void draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const; + +protected: + // property members: + EndingStyle mStyle; + double mWidth, mLength; + bool mInverted; +}; +Q_DECLARE_TYPEINFO(QCPLineEnding, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(QCPLineEnding::EndingStyle) + +/* end of 'src/lineending.h' */ + + +/* including file 'src/axis/labelpainter.h' */ +/* modified 2022-11-06T12:45:56, size 7086 */ + +class QCPLabelPainterPrivate +{ + Q_GADGET +public: + /*! + TODO + */ + enum AnchorMode { amRectangular ///< + ,amSkewedUpright ///< + ,amSkewedRotated ///< + }; + Q_ENUMS(AnchorMode) + + /*! + TODO + */ + enum AnchorReferenceType { artNormal ///< + ,artTangent ///< + }; + Q_ENUMS(AnchorReferenceType) + + /*! + TODO + */ + enum AnchorSide { asLeft ///< + ,asRight ///< + ,asTop ///< + ,asBottom ///< + ,asTopLeft + ,asTopRight + ,asBottomRight + ,asBottomLeft + }; + Q_ENUMS(AnchorSide) + + explicit QCPLabelPainterPrivate(QCustomPlot *parentPlot); + virtual ~QCPLabelPainterPrivate(); + + // setters: + void setAnchorSide(AnchorSide side); + void setAnchorMode(AnchorMode mode); + void setAnchorReference(const QPointF &pixelPoint); + void setAnchorReferenceType(AnchorReferenceType type); + void setFont(const QFont &font); + void setColor(const QColor &color); + void setPadding(int padding); + void setRotation(double rotation); + void setSubstituteExponent(bool enabled); + void setMultiplicationSymbol(QChar symbol); + void setAbbreviateDecimalPowers(bool enabled); + void setCacheSize(int labelCount); + + // getters: + AnchorMode anchorMode() const { return mAnchorMode; } + AnchorSide anchorSide() const { return mAnchorSide; } + QPointF anchorReference() const { return mAnchorReference; } + AnchorReferenceType anchorReferenceType() const { return mAnchorReferenceType; } + QFont font() const { return mFont; } + QColor color() const { return mColor; } + int padding() const { return mPadding; } + double rotation() const { return mRotation; } + bool substituteExponent() const { return mSubstituteExponent; } + QChar multiplicationSymbol() const { return mMultiplicationSymbol; } + bool abbreviateDecimalPowers() const { return mAbbreviateDecimalPowers; } + int cacheSize() const; + + //virtual int size() const; + + // non-property methods: + void drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text); + void clearCache(); + + // constants that may be used with setMultiplicationSymbol: + static const QChar SymbolDot; + static const QChar SymbolCross; + +protected: + struct CachedLabel + { + QPoint offset; + QPixmap pixmap; + }; + struct LabelData + { + AnchorSide side; + double rotation; // angle in degrees + QTransform transform; // the transform about the label anchor which is at (0, 0). Does not contain final absolute x/y positioning on the plot/axis + QString basePart, expPart, suffixPart; + QRect baseBounds, expBounds, suffixBounds; + QRect totalBounds; // is in a coordinate system where label top left is at (0, 0) + QRect rotatedTotalBounds; // is in a coordinate system where the label anchor is at (0, 0) + QFont baseFont, expFont; + QColor color; + }; + + // property members: + AnchorMode mAnchorMode; + AnchorSide mAnchorSide; + QPointF mAnchorReference; + AnchorReferenceType mAnchorReferenceType; + QFont mFont; + QColor mColor; + int mPadding; + double mRotation; // this is the rotation applied uniformly to all labels, not the heterogeneous rotation in amCircularRotated mode + bool mSubstituteExponent; + QChar mMultiplicationSymbol; + bool mAbbreviateDecimalPowers; + // non-property members: + QCustomPlot *mParentPlot; + QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters + QCache mLabelCache; + QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox; + int mLetterCapHeight, mLetterDescent; + + // introduced virtual methods: + virtual void drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text); + virtual QByteArray generateLabelParameterHash() const; // TODO: get rid of this in favor of invalidation flag upon setters? + + // non-virtual methods: + QPointF getAnchorPos(const QPointF &tickPos); + void drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const; + LabelData getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const; + void applyAnchorTransform(LabelData &labelData) const; + //void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const; + CachedLabel *createCachedLabel(const LabelData &labelData) const; + QByteArray cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const; + AnchorSide skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const; + AnchorSide rotationCorrectedSide(AnchorSide side, double rotation) const; + void analyzeFontMetrics(); +}; +Q_DECLARE_METATYPE(QCPLabelPainterPrivate::AnchorMode) +Q_DECLARE_METATYPE(QCPLabelPainterPrivate::AnchorSide) + + +/* end of 'src/axis/labelpainter.h' */ + + +/* including file 'src/axis/axisticker.h' */ +/* modified 2022-11-06T12:45:56, size 4230 */ + +class QCP_LIB_DECL QCPAxisTicker +{ + Q_GADGET +public: + /*! + Defines the strategies that the axis ticker may follow when choosing the size of the tick step. + + \see setTickStepStrategy + */ + enum TickStepStrategy + { + tssReadability ///< A nicely readable tick step is prioritized over matching the requested number of ticks (see \ref setTickCount) + ,tssMeetTickCount ///< Less readable tick steps are allowed which in turn facilitates getting closer to the requested tick count + }; + Q_ENUMS(TickStepStrategy) + + QCPAxisTicker(); + virtual ~QCPAxisTicker(); + + // getters: + TickStepStrategy tickStepStrategy() const { return mTickStepStrategy; } + int tickCount() const { return mTickCount; } + double tickOrigin() const { return mTickOrigin; } + + // setters: + void setTickStepStrategy(TickStepStrategy strategy); + void setTickCount(int count); + void setTickOrigin(double origin); + + // introduced virtual methods: + virtual void generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector &ticks, QVector *subTicks, QVector *tickLabels); + +protected: + // property members: + TickStepStrategy mTickStepStrategy; + int mTickCount; + double mTickOrigin; + + // introduced virtual methods: + virtual double getTickStep(const QCPRange &range); + virtual int getSubTickCount(double tickStep); + virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision); + virtual QVector createTickVector(double tickStep, const QCPRange &range); + virtual QVector createSubTickVector(int subTickCount, const QVector &ticks); + virtual QVector createLabelVector(const QVector &ticks, const QLocale &locale, QChar formatChar, int precision); + + // non-virtual methods: + void trimTicks(const QCPRange &range, QVector &ticks, bool keepOneOutlier) const; + double pickClosest(double target, const QVector &candidates) const; + double getMantissa(double input, double *magnitude=nullptr) const; + double cleanMantissa(double input) const; + +private: + Q_DISABLE_COPY(QCPAxisTicker) + +}; +Q_DECLARE_METATYPE(QCPAxisTicker::TickStepStrategy) +Q_DECLARE_METATYPE(QSharedPointer) + +/* end of 'src/axis/axisticker.h' */ + + +/* including file 'src/axis/axistickerdatetime.h' */ +/* modified 2022-11-06T12:45:56, size 3600 */ + +class QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker +{ +public: + QCPAxisTickerDateTime(); + + // getters: + QString dateTimeFormat() const { return mDateTimeFormat; } + Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; } +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + QTimeZone timeZone() const { return mTimeZone; } +#endif + + // setters: + void setDateTimeFormat(const QString &format); + void setDateTimeSpec(Qt::TimeSpec spec); +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + void setTimeZone(const QTimeZone &zone); +# endif + void setTickOrigin(double origin); // hides base class method but calls baseclass implementation ("using" throws off IDEs and doxygen) + void setTickOrigin(const QDateTime &origin); + + // static methods: + static QDateTime keyToDateTime(double key); + static double dateTimeToKey(const QDateTime &dateTime); + static double dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec=Qt::LocalTime); + +protected: + // property members: + QString mDateTimeFormat; + Qt::TimeSpec mDateTimeSpec; +# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + QTimeZone mTimeZone; +# endif + // non-property members: + enum DateStrategy {dsNone, dsUniformTimeInDay, dsUniformDayInMonth} mDateStrategy; + + // reimplemented virtual methods: + virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE; + virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE; + virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE; + virtual QVector createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE; +}; + +/* end of 'src/axis/axistickerdatetime.h' */ + + +/* including file 'src/axis/axistickertime.h' */ +/* modified 2022-11-06T12:45:56, size 3542 */ + +class QCP_LIB_DECL QCPAxisTickerTime : public QCPAxisTicker +{ + Q_GADGET +public: + /*! + Defines the logical units in which fractions of time spans can be expressed. + + \see setFieldWidth, setTimeFormat + */ + enum TimeUnit { tuMilliseconds ///< Milliseconds, one thousandth of a second (%%z in \ref setTimeFormat) + ,tuSeconds ///< Seconds (%%s in \ref setTimeFormat) + ,tuMinutes ///< Minutes (%%m in \ref setTimeFormat) + ,tuHours ///< Hours (%%h in \ref setTimeFormat) + ,tuDays ///< Days (%%d in \ref setTimeFormat) + }; + Q_ENUMS(TimeUnit) + + QCPAxisTickerTime(); + + // getters: + QString timeFormat() const { return mTimeFormat; } + int fieldWidth(TimeUnit unit) const { return mFieldWidth.value(unit); } + + // setters: + void setTimeFormat(const QString &format); + void setFieldWidth(TimeUnit unit, int width); + +protected: + // property members: + QString mTimeFormat; + QHash mFieldWidth; + + // non-property members: + TimeUnit mSmallestUnit, mBiggestUnit; + QHash mFormatPattern; + + // reimplemented virtual methods: + virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE; + virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE; + virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE; + + // non-virtual methods: + void replaceUnit(QString &text, TimeUnit unit, int value) const; +}; +Q_DECLARE_METATYPE(QCPAxisTickerTime::TimeUnit) + +/* end of 'src/axis/axistickertime.h' */ + + +/* including file 'src/axis/axistickerfixed.h' */ +/* modified 2022-11-06T12:45:56, size 3308 */ + +class QCP_LIB_DECL QCPAxisTickerFixed : public QCPAxisTicker +{ + Q_GADGET +public: + /*! + Defines how the axis ticker may modify the specified tick step (\ref setTickStep) in order to + control the number of ticks in the axis range. + + \see setScaleStrategy + */ + enum ScaleStrategy { ssNone ///< Modifications are not allowed, the specified tick step is absolutely fixed. This might cause a high tick density and overlapping labels if the axis range is zoomed out. + ,ssMultiples ///< An integer multiple of the specified tick step is allowed. The used factor follows the base class properties of \ref setTickStepStrategy and \ref setTickCount. + ,ssPowers ///< An integer power of the specified tick step is allowed. + }; + Q_ENUMS(ScaleStrategy) + + QCPAxisTickerFixed(); + + // getters: + double tickStep() const { return mTickStep; } + ScaleStrategy scaleStrategy() const { return mScaleStrategy; } + + // setters: + void setTickStep(double step); + void setScaleStrategy(ScaleStrategy strategy); + +protected: + // property members: + double mTickStep; + ScaleStrategy mScaleStrategy; + + // reimplemented virtual methods: + virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE; +}; +Q_DECLARE_METATYPE(QCPAxisTickerFixed::ScaleStrategy) + +/* end of 'src/axis/axistickerfixed.h' */ + + +/* including file 'src/axis/axistickertext.h' */ +/* modified 2022-11-06T12:45:56, size 3090 */ + +class QCP_LIB_DECL QCPAxisTickerText : public QCPAxisTicker +{ +public: + QCPAxisTickerText(); + + // getters: + QMap &ticks() { return mTicks; } + int subTickCount() const { return mSubTickCount; } + + // setters: + void setTicks(const QMap &ticks); + void setTicks(const QVector &positions, const QVector &labels); + void setSubTickCount(int subTicks); + + // non-virtual methods: + void clear(); + void addTick(double position, const QString &label); + void addTicks(const QMap &ticks); + void addTicks(const QVector &positions, const QVector &labels); + +protected: + // property members: + QMap mTicks; + int mSubTickCount; + + // reimplemented virtual methods: + virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE; + virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE; + virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE; + virtual QVector createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE; +}; + +/* end of 'src/axis/axistickertext.h' */ + + +/* including file 'src/axis/axistickerpi.h' */ +/* modified 2022-11-06T12:45:56, size 3911 */ + +class QCP_LIB_DECL QCPAxisTickerPi : public QCPAxisTicker +{ + Q_GADGET +public: + /*! + Defines how fractions should be displayed in tick labels. + + \see setFractionStyle + */ + enum FractionStyle { fsFloatingPoint ///< Fractions are displayed as regular decimal floating point numbers, e.g. "0.25" or "0.125". + ,fsAsciiFractions ///< Fractions are written as rationals using ASCII characters only, e.g. "1/4" or "1/8" + ,fsUnicodeFractions ///< Fractions are written using sub- and superscript UTF-8 digits and the fraction symbol. + }; + Q_ENUMS(FractionStyle) + + QCPAxisTickerPi(); + + // getters: + QString piSymbol() const { return mPiSymbol; } + double piValue() const { return mPiValue; } + bool periodicity() const { return mPeriodicity; } + FractionStyle fractionStyle() const { return mFractionStyle; } + + // setters: + void setPiSymbol(QString symbol); + void setPiValue(double pi); + void setPeriodicity(int multiplesOfPi); + void setFractionStyle(FractionStyle style); + +protected: + // property members: + QString mPiSymbol; + double mPiValue; + int mPeriodicity; + FractionStyle mFractionStyle; + + // non-property members: + double mPiTickStep; // size of one tick step in units of mPiValue + + // reimplemented virtual methods: + virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE; + virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE; + virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE; + + // non-virtual methods: + void simplifyFraction(int &numerator, int &denominator) const; + QString fractionToString(int numerator, int denominator) const; + QString unicodeFraction(int numerator, int denominator) const; + QString unicodeSuperscript(int number) const; + QString unicodeSubscript(int number) const; +}; +Q_DECLARE_METATYPE(QCPAxisTickerPi::FractionStyle) + +/* end of 'src/axis/axistickerpi.h' */ + + +/* including file 'src/axis/axistickerlog.h' */ +/* modified 2022-11-06T12:45:56, size 2594 */ + +class QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker +{ +public: + QCPAxisTickerLog(); + + // getters: + double logBase() const { return mLogBase; } + int subTickCount() const { return mSubTickCount; } + + // setters: + void setLogBase(double base); + void setSubTickCount(int subTicks); + +protected: + // property members: + double mLogBase; + int mSubTickCount; + + // non-property members: + double mLogBaseLnInv; + + // reimplemented virtual methods: + virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE; + virtual QVector createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE; +}; + +/* end of 'src/axis/axistickerlog.h' */ + + +/* including file 'src/axis/axis.h' */ +/* modified 2022-11-06T12:45:56, size 20913 */ + +class QCP_LIB_DECL QCPGrid :public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(bool subGridVisible READ subGridVisible WRITE setSubGridVisible) + Q_PROPERTY(bool antialiasedSubGrid READ antialiasedSubGrid WRITE setAntialiasedSubGrid) + Q_PROPERTY(bool antialiasedZeroLine READ antialiasedZeroLine WRITE setAntialiasedZeroLine) + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen subGridPen READ subGridPen WRITE setSubGridPen) + Q_PROPERTY(QPen zeroLinePen READ zeroLinePen WRITE setZeroLinePen) + /// \endcond +public: + explicit QCPGrid(QCPAxis *parentAxis); + + // getters: + bool subGridVisible() const { return mSubGridVisible; } + bool antialiasedSubGrid() const { return mAntialiasedSubGrid; } + bool antialiasedZeroLine() const { return mAntialiasedZeroLine; } + QPen pen() const { return mPen; } + QPen subGridPen() const { return mSubGridPen; } + QPen zeroLinePen() const { return mZeroLinePen; } + + // setters: + void setSubGridVisible(bool visible); + void setAntialiasedSubGrid(bool enabled); + void setAntialiasedZeroLine(bool enabled); + void setPen(const QPen &pen); + void setSubGridPen(const QPen &pen); + void setZeroLinePen(const QPen &pen); + +protected: + // property members: + bool mSubGridVisible; + bool mAntialiasedSubGrid, mAntialiasedZeroLine; + QPen mPen, mSubGridPen, mZeroLinePen; + + // non-property members: + QCPAxis *mParentAxis; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + + // non-virtual methods: + void drawGridLines(QCPPainter *painter) const; + void drawSubGridLines(QCPPainter *painter) const; + + friend class QCPAxis; +}; + + +class QCP_LIB_DECL QCPAxis : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(AxisType axisType READ axisType) + Q_PROPERTY(QCPAxisRect* axisRect READ axisRect) + Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType NOTIFY scaleTypeChanged) + Q_PROPERTY(QCPRange range READ range WRITE setRange NOTIFY rangeChanged) + Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed) + Q_PROPERTY(QSharedPointer ticker READ ticker WRITE setTicker) + Q_PROPERTY(bool ticks READ ticks WRITE setTicks) + Q_PROPERTY(bool tickLabels READ tickLabels WRITE setTickLabels) + Q_PROPERTY(int tickLabelPadding READ tickLabelPadding WRITE setTickLabelPadding) + Q_PROPERTY(QFont tickLabelFont READ tickLabelFont WRITE setTickLabelFont) + Q_PROPERTY(QColor tickLabelColor READ tickLabelColor WRITE setTickLabelColor) + Q_PROPERTY(double tickLabelRotation READ tickLabelRotation WRITE setTickLabelRotation) + Q_PROPERTY(LabelSide tickLabelSide READ tickLabelSide WRITE setTickLabelSide) + Q_PROPERTY(QString numberFormat READ numberFormat WRITE setNumberFormat) + Q_PROPERTY(int numberPrecision READ numberPrecision WRITE setNumberPrecision) + Q_PROPERTY(QVector tickVector READ tickVector) + Q_PROPERTY(QVector tickVectorLabels READ tickVectorLabels) + Q_PROPERTY(int tickLengthIn READ tickLengthIn WRITE setTickLengthIn) + Q_PROPERTY(int tickLengthOut READ tickLengthOut WRITE setTickLengthOut) + Q_PROPERTY(bool subTicks READ subTicks WRITE setSubTicks) + Q_PROPERTY(int subTickLengthIn READ subTickLengthIn WRITE setSubTickLengthIn) + Q_PROPERTY(int subTickLengthOut READ subTickLengthOut WRITE setSubTickLengthOut) + Q_PROPERTY(QPen basePen READ basePen WRITE setBasePen) + Q_PROPERTY(QPen tickPen READ tickPen WRITE setTickPen) + Q_PROPERTY(QPen subTickPen READ subTickPen WRITE setSubTickPen) + Q_PROPERTY(QFont labelFont READ labelFont WRITE setLabelFont) + Q_PROPERTY(QColor labelColor READ labelColor WRITE setLabelColor) + Q_PROPERTY(QString label READ label WRITE setLabel) + Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding) + Q_PROPERTY(int padding READ padding WRITE setPadding) + Q_PROPERTY(int offset READ offset WRITE setOffset) + Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectionChanged) + Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectableChanged) + Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE setSelectedTickLabelFont) + Q_PROPERTY(QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont) + Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE setSelectedTickLabelColor) + Q_PROPERTY(QColor selectedLabelColor READ selectedLabelColor WRITE setSelectedLabelColor) + Q_PROPERTY(QPen selectedBasePen READ selectedBasePen WRITE setSelectedBasePen) + Q_PROPERTY(QPen selectedTickPen READ selectedTickPen WRITE setSelectedTickPen) + Q_PROPERTY(QPen selectedSubTickPen READ selectedSubTickPen WRITE setSelectedSubTickPen) + Q_PROPERTY(QCPLineEnding lowerEnding READ lowerEnding WRITE setLowerEnding) + Q_PROPERTY(QCPLineEnding upperEnding READ upperEnding WRITE setUpperEnding) + Q_PROPERTY(QCPGrid* grid READ grid) + /// \endcond +public: + /*! + Defines at which side of the axis rect the axis will appear. This also affects how the tick + marks are drawn, on which side the labels are placed etc. + */ + enum AxisType { atLeft = 0x01 ///< 0x01 Axis is vertical and on the left side of the axis rect + ,atRight = 0x02 ///< 0x02 Axis is vertical and on the right side of the axis rect + ,atTop = 0x04 ///< 0x04 Axis is horizontal and on the top side of the axis rect + ,atBottom = 0x08 ///< 0x08 Axis is horizontal and on the bottom side of the axis rect + }; + Q_ENUMS(AxisType) + Q_FLAGS(AxisTypes) + Q_DECLARE_FLAGS(AxisTypes, AxisType) + /*! + Defines on which side of the axis the tick labels (numbers) shall appear. + + \see setTickLabelSide + */ + enum LabelSide { lsInside ///< Tick labels will be displayed inside the axis rect and clipped to the inner axis rect + ,lsOutside ///< Tick labels will be displayed outside the axis rect + }; + Q_ENUMS(LabelSide) + /*! + Defines the scale of an axis. + \see setScaleType + */ + enum ScaleType { stLinear ///< Linear scaling + ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed axis coordinates (possibly also \ref setTicker to a \ref QCPAxisTickerLog instance). + }; + Q_ENUMS(ScaleType) + /*! + Defines the selectable parts of an axis. + \see setSelectableParts, setSelectedParts + */ + enum SelectablePart { spNone = 0 ///< None of the selectable parts + ,spAxis = 0x001 ///< The axis backbone and tick marks + ,spTickLabels = 0x002 ///< Tick labels (numbers) of this axis (as a whole, not individually) + ,spAxisLabel = 0x004 ///< The axis label + }; + Q_ENUMS(SelectablePart) + Q_FLAGS(SelectableParts) + Q_DECLARE_FLAGS(SelectableParts, SelectablePart) + + explicit QCPAxis(QCPAxisRect *parent, AxisType type); + virtual ~QCPAxis() Q_DECL_OVERRIDE; + + // getters: + AxisType axisType() const { return mAxisType; } + QCPAxisRect *axisRect() const { return mAxisRect; } + ScaleType scaleType() const { return mScaleType; } + const QCPRange range() const { return mRange; } + bool rangeReversed() const { return mRangeReversed; } + QSharedPointer ticker() const { return mTicker; } + bool ticks() const { return mTicks; } + bool tickLabels() const { return mTickLabels; } + int tickLabelPadding() const; + QFont tickLabelFont() const { return mTickLabelFont; } + QColor tickLabelColor() const { return mTickLabelColor; } + double tickLabelRotation() const; + LabelSide tickLabelSide() const; + QString numberFormat() const; + int numberPrecision() const { return mNumberPrecision; } + QVector tickVector() const { return mTickVector; } + QVector tickVectorLabels() const { return mTickVectorLabels; } + int tickLengthIn() const; + int tickLengthOut() const; + bool subTicks() const { return mSubTicks; } + int subTickLengthIn() const; + int subTickLengthOut() const; + QPen basePen() const { return mBasePen; } + QPen tickPen() const { return mTickPen; } + QPen subTickPen() const { return mSubTickPen; } + QFont labelFont() const { return mLabelFont; } + QColor labelColor() const { return mLabelColor; } + QString label() const { return mLabel; } + int labelPadding() const; + int padding() const { return mPadding; } + int offset() const; + SelectableParts selectedParts() const { return mSelectedParts; } + SelectableParts selectableParts() const { return mSelectableParts; } + QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; } + QFont selectedLabelFont() const { return mSelectedLabelFont; } + QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; } + QColor selectedLabelColor() const { return mSelectedLabelColor; } + QPen selectedBasePen() const { return mSelectedBasePen; } + QPen selectedTickPen() const { return mSelectedTickPen; } + QPen selectedSubTickPen() const { return mSelectedSubTickPen; } + QCPLineEnding lowerEnding() const; + QCPLineEnding upperEnding() const; + QCPGrid *grid() const { return mGrid; } + + // setters: + Q_SLOT void setScaleType(QCPAxis::ScaleType type); + Q_SLOT void setRange(const QCPRange &range); + void setRange(double lower, double upper); + void setRange(double position, double size, Qt::AlignmentFlag alignment); + void setRangeLower(double lower); + void setRangeUpper(double upper); + void setRangeReversed(bool reversed); + void setTicker(QSharedPointer ticker); + void setTicks(bool show); + void setTickLabels(bool show); + void setTickLabelPadding(int padding); + void setTickLabelFont(const QFont &font); + void setTickLabelColor(const QColor &color); + void setTickLabelRotation(double degrees); + void setTickLabelSide(LabelSide side); + void setNumberFormat(const QString &formatCode); + void setNumberPrecision(int precision); + void setTickLength(int inside, int outside=0); + void setTickLengthIn(int inside); + void setTickLengthOut(int outside); + void setSubTicks(bool show); + void setSubTickLength(int inside, int outside=0); + void setSubTickLengthIn(int inside); + void setSubTickLengthOut(int outside); + void setBasePen(const QPen &pen); + void setTickPen(const QPen &pen); + void setSubTickPen(const QPen &pen); + void setLabelFont(const QFont &font); + void setLabelColor(const QColor &color); + void setLabel(const QString &str); + void setLabelPadding(int padding); + void setPadding(int padding); + void setOffset(int offset); + void setSelectedTickLabelFont(const QFont &font); + void setSelectedLabelFont(const QFont &font); + void setSelectedTickLabelColor(const QColor &color); + void setSelectedLabelColor(const QColor &color); + void setSelectedBasePen(const QPen &pen); + void setSelectedTickPen(const QPen &pen); + void setSelectedSubTickPen(const QPen &pen); + Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts); + Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts); + void setLowerEnding(const QCPLineEnding &ending); + void setUpperEnding(const QCPLineEnding &ending); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + // non-property methods: + Qt::Orientation orientation() const { return mOrientation; } + int pixelOrientation() const { return rangeReversed() != (orientation()==Qt::Vertical) ? -1 : 1; } + void moveRange(double diff); + void scaleRange(double factor); + void scaleRange(double factor, double center); + void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0); + void rescale(bool onlyVisiblePlottables=false); + double pixelToCoord(double value) const; + double coordToPixel(double value) const; + SelectablePart getPartAt(const QPointF &pos) const; + QList plottables() const; + QList graphs() const; + QList items() const; + + static AxisType marginSideToAxisType(QCP::MarginSide side); + static Qt::Orientation orientation(AxisType type) { return type==atBottom || type==atTop ? Qt::Horizontal : Qt::Vertical; } + static AxisType opposite(AxisType type); + +signals: + void rangeChanged(const QCPRange &newRange); + void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange); + void scaleTypeChanged(QCPAxis::ScaleType scaleType); + void selectionChanged(const QCPAxis::SelectableParts &parts); + void selectableChanged(const QCPAxis::SelectableParts &parts); + +protected: + // property members: + // axis base: + AxisType mAxisType; + QCPAxisRect *mAxisRect; + //int mOffset; // in QCPAxisPainter + int mPadding; + Qt::Orientation mOrientation; + SelectableParts mSelectableParts, mSelectedParts; + QPen mBasePen, mSelectedBasePen; + //QCPLineEnding mLowerEnding, mUpperEnding; // in QCPAxisPainter + // axis label: + //int mLabelPadding; // in QCPAxisPainter + QString mLabel; + QFont mLabelFont, mSelectedLabelFont; + QColor mLabelColor, mSelectedLabelColor; + // tick labels: + //int mTickLabelPadding; // in QCPAxisPainter + bool mTickLabels; + //double mTickLabelRotation; // in QCPAxisPainter + QFont mTickLabelFont, mSelectedTickLabelFont; + QColor mTickLabelColor, mSelectedTickLabelColor; + int mNumberPrecision; + QLatin1Char mNumberFormatChar; + bool mNumberBeautifulPowers; + //bool mNumberMultiplyCross; // QCPAxisPainter + // ticks and subticks: + bool mTicks; + bool mSubTicks; + //int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; // QCPAxisPainter + QPen mTickPen, mSelectedTickPen; + QPen mSubTickPen, mSelectedSubTickPen; + // scale and range: + QCPRange mRange; + bool mRangeReversed; + ScaleType mScaleType; + + // non-property members: + QCPGrid *mGrid; + QCPAxisPainterPrivate *mAxisPainter; + QSharedPointer mTicker; + QVector mTickVector; + QVector mTickVectorLabels; + QVector mSubTickVector; + bool mCachedMarginValid; + int mCachedMargin; + bool mDragging; + QCPRange mDragStartRange; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + + // introduced virtual methods: + virtual int calculateMargin(); + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; + virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; + // mouse events: + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + // non-virtual methods: + void setupTickVectors(); + QPen getBasePen() const; + QPen getTickPen() const; + QPen getSubTickPen() const; + QFont getTickLabelFont() const; + QFont getLabelFont() const; + QColor getTickLabelColor() const; + QColor getLabelColor() const; + +private: + Q_DISABLE_COPY(QCPAxis) + + friend class QCustomPlot; + friend class QCPGrid; + friend class QCPAxisRect; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::SelectableParts) +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::AxisTypes) +Q_DECLARE_METATYPE(QCPAxis::AxisType) +Q_DECLARE_METATYPE(QCPAxis::LabelSide) +Q_DECLARE_METATYPE(QCPAxis::ScaleType) +Q_DECLARE_METATYPE(QCPAxis::SelectablePart) + + +class QCPAxisPainterPrivate +{ +public: + explicit QCPAxisPainterPrivate(QCustomPlot *parentPlot); + virtual ~QCPAxisPainterPrivate(); + + virtual void draw(QCPPainter *painter); + virtual int size(); + void clearCache(); + + QRect axisSelectionBox() const { return mAxisSelectionBox; } + QRect tickLabelsSelectionBox() const { return mTickLabelsSelectionBox; } + QRect labelSelectionBox() const { return mLabelSelectionBox; } + + // public property members: + QCPAxis::AxisType type; + QPen basePen; + QCPLineEnding lowerEnding, upperEnding; // directly accessed by QCPAxis setters/getters + int labelPadding; // directly accessed by QCPAxis setters/getters + QFont labelFont; + QColor labelColor; + QString label; + int tickLabelPadding; // directly accessed by QCPAxis setters/getters + double tickLabelRotation; // directly accessed by QCPAxis setters/getters + QCPAxis::LabelSide tickLabelSide; // directly accessed by QCPAxis setters/getters + bool substituteExponent; + bool numberMultiplyCross; // directly accessed by QCPAxis setters/getters + int tickLengthIn, tickLengthOut, subTickLengthIn, subTickLengthOut; // directly accessed by QCPAxis setters/getters + QPen tickPen, subTickPen; + QFont tickLabelFont; + QColor tickLabelColor; + QRect axisRect, viewportRect; + int offset; // directly accessed by QCPAxis setters/getters + bool abbreviateDecimalPowers; + bool reversedEndings; + + QVector subTickPositions; + QVector tickPositions; + QVector tickLabels; + +protected: + struct CachedLabel + { + QPointF offset; + QPixmap pixmap; + }; + struct TickLabelData + { + QString basePart, expPart, suffixPart; + QRect baseBounds, expBounds, suffixBounds, totalBounds, rotatedTotalBounds; + QFont baseFont, expFont; + }; + QCustomPlot *mParentPlot; + QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters + QCache mLabelCache; + QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox; + + virtual QByteArray generateLabelParameterHash() const; + + virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize); + virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const; + virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const; + virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const; + virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const; +}; + +/* end of 'src/axis/axis.h' */ + + +/* including file 'src/scatterstyle.h' */ +/* modified 2022-11-06T12:45:56, size 7275 */ + +class QCP_LIB_DECL QCPScatterStyle +{ + Q_GADGET +public: + /*! + Represents the various properties of a scatter style instance. For example, this enum is used + to specify which properties of \ref QCPSelectionDecorator::setScatterStyle will be used when + highlighting selected data points. + + Specific scatter properties can be transferred between \ref QCPScatterStyle instances via \ref + setFromOther. + */ + enum ScatterProperty { spNone = 0x00 ///< 0x00 None + ,spPen = 0x01 ///< 0x01 The pen property, see \ref setPen + ,spBrush = 0x02 ///< 0x02 The brush property, see \ref setBrush + ,spSize = 0x04 ///< 0x04 The size property, see \ref setSize + ,spShape = 0x08 ///< 0x08 The shape property, see \ref setShape + ,spAll = 0xFF ///< 0xFF All properties + }; + Q_ENUMS(ScatterProperty) + Q_FLAGS(ScatterProperties) + Q_DECLARE_FLAGS(ScatterProperties, ScatterProperty) + + /*! + Defines the shape used for scatter points. + + On plottables/items that draw scatters, the sizes of these visualizations (with exception of + \ref ssDot and \ref ssPixmap) can be controlled with the \ref setSize function. Scatters are + drawn with the pen and brush specified with \ref setPen and \ref setBrush. + */ + enum ScatterShape { ssNone ///< no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines) + ,ssDot ///< \enumimage{ssDot.png} a single pixel (use \ref ssDisc or \ref ssCircle if you want a round shape with a certain radius) + ,ssCross ///< \enumimage{ssCross.png} a cross + ,ssPlus ///< \enumimage{ssPlus.png} a plus + ,ssCircle ///< \enumimage{ssCircle.png} a circle + ,ssDisc ///< \enumimage{ssDisc.png} a circle which is filled with the pen's color (not the brush as with ssCircle) + ,ssSquare ///< \enumimage{ssSquare.png} a square + ,ssDiamond ///< \enumimage{ssDiamond.png} a diamond + ,ssStar ///< \enumimage{ssStar.png} a star with eight arms, i.e. a combination of cross and plus + ,ssTriangle ///< \enumimage{ssTriangle.png} an equilateral triangle, standing on baseline + ,ssTriangleInverted ///< \enumimage{ssTriangleInverted.png} an equilateral triangle, standing on corner + ,ssCrossSquare ///< \enumimage{ssCrossSquare.png} a square with a cross inside + ,ssPlusSquare ///< \enumimage{ssPlusSquare.png} a square with a plus inside + ,ssCrossCircle ///< \enumimage{ssCrossCircle.png} a circle with a cross inside + ,ssPlusCircle ///< \enumimage{ssPlusCircle.png} a circle with a plus inside + ,ssPeace ///< \enumimage{ssPeace.png} a circle, with one vertical and two downward diagonal lines + ,ssPixmap ///< a custom pixmap specified by \ref setPixmap, centered on the data point coordinates + ,ssCustom ///< custom painter operations are performed per scatter (As QPainterPath, see \ref setCustomPath) + }; + Q_ENUMS(ScatterShape) + + QCPScatterStyle(); + QCPScatterStyle(ScatterShape shape, double size=6); + QCPScatterStyle(ScatterShape shape, const QColor &color, double size); + QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size); + QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size); + QCPScatterStyle(const QPixmap &pixmap); + QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush=Qt::NoBrush, double size=6); + + // getters: + double size() const { return mSize; } + ScatterShape shape() const { return mShape; } + QPen pen() const { return mPen; } + QBrush brush() const { return mBrush; } + QPixmap pixmap() const { return mPixmap; } + QPainterPath customPath() const { return mCustomPath; } + + // setters: + void setFromOther(const QCPScatterStyle &other, ScatterProperties properties); + void setSize(double size); + void setShape(ScatterShape shape); + void setPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setPixmap(const QPixmap &pixmap); + void setCustomPath(const QPainterPath &customPath); + + // non-property methods: + bool isNone() const { return mShape == ssNone; } + bool isPenDefined() const { return mPenDefined; } + void undefinePen(); + void applyTo(QCPPainter *painter, const QPen &defaultPen) const; + void drawShape(QCPPainter *painter, const QPointF &pos) const; + void drawShape(QCPPainter *painter, double x, double y) const; + +protected: + // property members: + double mSize; + ScatterShape mShape; + QPen mPen; + QBrush mBrush; + QPixmap mPixmap; + QPainterPath mCustomPath; + + // non-property members: + bool mPenDefined; +}; +Q_DECLARE_TYPEINFO(QCPScatterStyle, Q_MOVABLE_TYPE); +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPScatterStyle::ScatterProperties) +Q_DECLARE_METATYPE(QCPScatterStyle::ScatterProperty) +Q_DECLARE_METATYPE(QCPScatterStyle::ScatterShape) + +/* end of 'src/scatterstyle.h' */ + + +/* including file 'src/datacontainer.h' */ +/* modified 2022-11-06T12:45:56, size 34305 */ + +/*! \relates QCPDataContainer + Returns whether the sort key of \a a is less than the sort key of \a b. + + \see QCPDataContainer::sort +*/ +template +inline bool qcpLessThanSortKey(const DataType &a, const DataType &b) { return a.sortKey() < b.sortKey(); } + +template +class QCPDataContainer // no QCP_LIB_DECL, template class ends up in header (cpp included below) +{ +public: + typedef typename QVector::const_iterator const_iterator; + typedef typename QVector::iterator iterator; + + QCPDataContainer(); + + // getters: + int size() const { return static_cast(mData.size()-mPreallocSize); } + bool isEmpty() const { return size() == 0; } + bool autoSqueeze() const { return mAutoSqueeze; } + + // setters: + void setAutoSqueeze(bool enabled); + + // non-virtual methods: + void set(const QCPDataContainer &data); + void set(const QVector &data, bool alreadySorted=false); + void add(const QCPDataContainer &data); + void add(const QVector &data, bool alreadySorted=false); + void add(const DataType &data); + void removeBefore(double sortKey); + void removeAfter(double sortKey); + void remove(double sortKeyFrom, double sortKeyTo); + void remove(double sortKey); + void clear(); + void sort(); + void squeeze(bool preAllocation=true, bool postAllocation=true); + + const_iterator constBegin() const { return mData.constBegin()+mPreallocSize; } + const_iterator constEnd() const { return mData.constEnd(); } + iterator begin() { return mData.begin()+mPreallocSize; } + iterator end() { return mData.end(); } + const_iterator findBegin(double sortKey, bool expandedRange=true) const; + const_iterator findEnd(double sortKey, bool expandedRange=true) const; + const_iterator at(int index) const { return constBegin()+qBound(0, index, size()); } + QCPRange keyRange(bool &foundRange, QCP::SignDomain signDomain=QCP::sdBoth); + QCPRange valueRange(bool &foundRange, QCP::SignDomain signDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()); + QCPDataRange dataRange() const { return QCPDataRange(0, size()); } + void limitIteratorsToDataRange(const_iterator &begin, const_iterator &end, const QCPDataRange &dataRange) const; + +protected: + // property members: + bool mAutoSqueeze; + + // non-property memebers: + QVector mData; + int mPreallocSize; + int mPreallocIteration; + + // non-virtual methods: + void preallocateGrow(int minimumPreallocSize); + void performAutoSqueeze(); +}; + + + +// include implementation in header since it is a class template: +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPDataContainer +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPDataContainer + \brief The generic data container for one-dimensional plottables + + This class template provides a fast container for data storage of one-dimensional data. The data + type is specified as template parameter (called \a DataType in the following) and must provide + some methods as described in the \ref qcpdatacontainer-datatype "next section". + + The data is stored in a sorted fashion, which allows very quick lookups by the sorted key as well + as retrieval of ranges (see \ref findBegin, \ref findEnd, \ref keyRange) using binary search. The + container uses a preallocation and a postallocation scheme, such that appending and prepending + data (with respect to the sort key) is very fast and minimizes reallocations. If data is added + which needs to be inserted between existing keys, the merge usually can be done quickly too, + using the fact that existing data is always sorted. The user can further improve performance by + specifying that added data is already itself sorted by key, if he can guarantee that this is the + case (see for example \ref add(const QVector &data, bool alreadySorted)). + + The data can be accessed with the provided const iterators (\ref constBegin, \ref constEnd). If + it is necessary to alter existing data in-place, the non-const iterators can be used (\ref begin, + \ref end). Changing data members that are not the sort key (for most data types called \a key) is + safe from the container's perspective. + + Great care must be taken however if the sort key is modified through the non-const iterators. For + performance reasons, the iterators don't automatically cause a re-sorting upon their + manipulation. It is thus the responsibility of the user to leave the container in a sorted state + when finished with the data manipulation, before calling any other methods on the container. A + complete re-sort (e.g. after finishing all sort key manipulation) can be done by calling \ref + sort. Failing to do so can not be detected by the container efficiently and will cause both + rendering artifacts and potential data loss. + + Implementing one-dimensional plottables that make use of a \ref QCPDataContainer is usually + done by subclassing from \ref QCPAbstractPlottable1D "QCPAbstractPlottable1D", which + introduces an according \a mDataContainer member and some convenience methods. + + \section qcpdatacontainer-datatype Requirements for the DataType template parameter + + The template parameter DataType is the type of the stored data points. It must be + trivially copyable and have the following public methods, preferably inline: + + \li double sortKey() const\n Returns the member variable of this data point that is the + sort key, defining the ordering in the container. Often this variable is simply called \a key. + + \li static DataType fromSortKey(double sortKey)\n Returns a new instance of the data + type initialized with its sort key set to \a sortKey. + + \li static bool sortKeyIsMainKey()\n Returns true if the sort key is equal to the main + key (see method \c mainKey below). For most plottables this is the case. It is not the case for + example for \ref QCPCurve, which uses \a t as sort key and \a key as main key. This is the reason + why QCPCurve unlike QCPGraph can display parametric curves with loops. + + \li double mainKey() const\n Returns the variable of this data point considered the main + key. This is commonly the variable that is used as the coordinate of this data point on the key + axis of the plottable. This method is used for example when determining the automatic axis + rescaling of key axes (\ref QCPAxis::rescale). + + \li double mainValue() const\n Returns the variable of this data point considered the + main value. This is commonly the variable that is used as the coordinate of this data point on + the value axis of the plottable. + + \li QCPRange valueRange() const\n Returns the range this data point spans in the value + axis coordinate. If the data is single-valued (e.g. QCPGraphData), this is simply a range with + both lower and upper set to the main data point value. However if the data points can represent + multiple values at once (e.g QCPFinancialData with its \a high, \a low, \a open and \a close + values at each \a key) this method should return the range those values span. This method is used + for example when determining the automatic axis rescaling of value axes (\ref + QCPAxis::rescale). +*/ + +/* start documentation of inline functions */ + +/*! \fn int QCPDataContainer::size() const + + Returns the number of data points in the container. +*/ + +/*! \fn bool QCPDataContainer::isEmpty() const + + Returns whether this container holds no data points. +*/ + +/*! \fn QCPDataContainer::const_iterator QCPDataContainer::constBegin() const + + Returns a const iterator to the first data point in this container. +*/ + +/*! \fn QCPDataContainer::const_iterator QCPDataContainer::constEnd() const + + Returns a const iterator to the element past the last data point in this container. +*/ + +/*! \fn QCPDataContainer::iterator QCPDataContainer::begin() const + + Returns a non-const iterator to the first data point in this container. + + You can manipulate the data points in-place through the non-const iterators, but great care must + be taken when manipulating the sort key of a data point, see \ref sort, or the detailed + description of this class. +*/ + +/*! \fn QCPDataContainer::iterator QCPDataContainer::end() const + + Returns a non-const iterator to the element past the last data point in this container. + + You can manipulate the data points in-place through the non-const iterators, but great care must + be taken when manipulating the sort key of a data point, see \ref sort, or the detailed + description of this class. +*/ + +/*! \fn QCPDataContainer::const_iterator QCPDataContainer::at(int index) const + + Returns a const iterator to the element with the specified \a index. If \a index points beyond + the available elements in this container, returns \ref constEnd, i.e. an iterator past the last + valid element. + + You can use this method to easily obtain iterators from a \ref QCPDataRange, see the \ref + dataselection-accessing "data selection page" for an example. +*/ + +/*! \fn QCPDataRange QCPDataContainer::dataRange() const + + Returns a \ref QCPDataRange encompassing the entire data set of this container. This means the + begin index of the returned range is 0, and the end index is \ref size. +*/ + +/* end documentation of inline functions */ + +/*! + Constructs a QCPDataContainer used for plottable classes that represent a series of key-sorted + data +*/ +template +QCPDataContainer::QCPDataContainer() : + mAutoSqueeze(true), + mPreallocSize(0), + mPreallocIteration(0) +{ +} + +/*! + Sets whether the container automatically decides when to release memory from its post- and + preallocation pools when data points are removed. By default this is enabled and for typical + applications shouldn't be changed. + + If auto squeeze is disabled, you can manually decide when to release pre-/postallocation with + \ref squeeze. +*/ +template +void QCPDataContainer::setAutoSqueeze(bool enabled) +{ + if (mAutoSqueeze != enabled) + { + mAutoSqueeze = enabled; + if (mAutoSqueeze) + performAutoSqueeze(); + } +} + +/*! \overload + + Replaces the current data in this container with the provided \a data. + + \see add, remove +*/ +template +void QCPDataContainer::set(const QCPDataContainer &data) +{ + clear(); + add(data); +} + +/*! \overload + + Replaces the current data in this container with the provided \a data + + If you can guarantee that the data points in \a data have ascending order with respect to the + DataType's sort key, set \a alreadySorted to true to avoid an unnecessary sorting run. + + \see add, remove +*/ +template +void QCPDataContainer::set(const QVector &data, bool alreadySorted) +{ + mData = data; + mPreallocSize = 0; + mPreallocIteration = 0; + if (!alreadySorted) + sort(); +} + +/*! \overload + + Adds the provided \a data to the current data in this container. + + \see set, remove +*/ +template +void QCPDataContainer::add(const QCPDataContainer &data) +{ + if (data.isEmpty()) + return; + + const int n = data.size(); + const int oldSize = size(); + + if (oldSize > 0 && !qcpLessThanSortKey(*constBegin(), *(data.constEnd()-1))) // prepend if new data keys are all smaller than or equal to existing ones + { + if (mPreallocSize < n) + preallocateGrow(n); + mPreallocSize -= n; + std::copy(data.constBegin(), data.constEnd(), begin()); + } else // don't need to prepend, so append and merge if necessary + { + mData.resize(mData.size()+n); + std::copy(data.constBegin(), data.constEnd(), end()-n); + if (oldSize > 0 && !qcpLessThanSortKey(*(constEnd()-n-1), *(constEnd()-n))) // if appended range keys aren't all greater than existing ones, merge the two partitions + std::inplace_merge(begin(), end()-n, end(), qcpLessThanSortKey); + } +} + +/*! + Adds the provided data points in \a data to the current data. + + If you can guarantee that the data points in \a data have ascending order with respect to the + DataType's sort key, set \a alreadySorted to true to avoid an unnecessary sorting run. + + \see set, remove +*/ +template +void QCPDataContainer::add(const QVector &data, bool alreadySorted) +{ + if (data.isEmpty()) + return; + if (isEmpty()) + { + set(data, alreadySorted); + return; + } + + const int n = static_cast(data.size()); + const int oldSize = size(); + + if (alreadySorted && oldSize > 0 && !qcpLessThanSortKey(*constBegin(), *(data.constEnd()-1))) // prepend if new data is sorted and keys are all smaller than or equal to existing ones + { + if (mPreallocSize < n) + preallocateGrow(n); + mPreallocSize -= n; + std::copy(data.constBegin(), data.constEnd(), begin()); + } else // don't need to prepend, so append and then sort and merge if necessary + { + mData.resize(mData.size()+n); + std::copy(data.constBegin(), data.constEnd(), end()-n); + if (!alreadySorted) // sort appended subrange if it wasn't already sorted + std::sort(end()-n, end(), qcpLessThanSortKey); + if (oldSize > 0 && !qcpLessThanSortKey(*(constEnd()-n-1), *(constEnd()-n))) // if appended range keys aren't all greater than existing ones, merge the two partitions + std::inplace_merge(begin(), end()-n, end(), qcpLessThanSortKey); + } +} + +/*! \overload + + Adds the provided single data point to the current data. + + \see remove +*/ +template +void QCPDataContainer::add(const DataType &data) +{ + if (isEmpty() || !qcpLessThanSortKey(data, *(constEnd()-1))) // quickly handle appends if new data key is greater or equal to existing ones + { + mData.append(data); + } else if (qcpLessThanSortKey(data, *constBegin())) // quickly handle prepends using preallocated space + { + if (mPreallocSize < 1) + preallocateGrow(1); + --mPreallocSize; + *begin() = data; + } else // handle inserts, maintaining sorted keys + { + QCPDataContainer::iterator insertionPoint = std::lower_bound(begin(), end(), data, qcpLessThanSortKey); + mData.insert(insertionPoint, data); + } +} + +/*! + Removes all data points with (sort-)keys smaller than or equal to \a sortKey. + + \see removeAfter, remove, clear +*/ +template +void QCPDataContainer::removeBefore(double sortKey) +{ + QCPDataContainer::iterator it = begin(); + QCPDataContainer::iterator itEnd = std::lower_bound(begin(), end(), DataType::fromSortKey(sortKey), qcpLessThanSortKey); + mPreallocSize += int(itEnd-it); // don't actually delete, just add it to the preallocated block (if it gets too large, squeeze will take care of it) + if (mAutoSqueeze) + performAutoSqueeze(); +} + +/*! + Removes all data points with (sort-)keys greater than or equal to \a sortKey. + + \see removeBefore, remove, clear +*/ +template +void QCPDataContainer::removeAfter(double sortKey) +{ + QCPDataContainer::iterator it = std::upper_bound(begin(), end(), DataType::fromSortKey(sortKey), qcpLessThanSortKey); + QCPDataContainer::iterator itEnd = end(); + mData.erase(it, itEnd); // typically adds it to the postallocated block + if (mAutoSqueeze) + performAutoSqueeze(); +} + +/*! + Removes all data points with (sort-)keys between \a sortKeyFrom and \a sortKeyTo. if \a + sortKeyFrom is greater or equal to \a sortKeyTo, the function does nothing. To remove a single + data point with known (sort-)key, use \ref remove(double sortKey). + + \see removeBefore, removeAfter, clear +*/ +template +void QCPDataContainer::remove(double sortKeyFrom, double sortKeyTo) +{ + if (sortKeyFrom >= sortKeyTo || isEmpty()) + return; + + QCPDataContainer::iterator it = std::lower_bound(begin(), end(), DataType::fromSortKey(sortKeyFrom), qcpLessThanSortKey); + QCPDataContainer::iterator itEnd = std::upper_bound(it, end(), DataType::fromSortKey(sortKeyTo), qcpLessThanSortKey); + mData.erase(it, itEnd); + if (mAutoSqueeze) + performAutoSqueeze(); +} + +/*! \overload + + Removes a single data point at \a sortKey. If the position is not known with absolute (binary) + precision, consider using \ref remove(double sortKeyFrom, double sortKeyTo) with a small + fuzziness interval around the suspected position, depeding on the precision with which the + (sort-)key is known. + + \see removeBefore, removeAfter, clear +*/ +template +void QCPDataContainer::remove(double sortKey) +{ + QCPDataContainer::iterator it = std::lower_bound(begin(), end(), DataType::fromSortKey(sortKey), qcpLessThanSortKey); + if (it != end() && it->sortKey() == sortKey) + { + if (it == begin()) + ++mPreallocSize; // don't actually delete, just add it to the preallocated block (if it gets too large, squeeze will take care of it) + else + mData.erase(it); + } + if (mAutoSqueeze) + performAutoSqueeze(); +} + +/*! + Removes all data points. + + \see remove, removeAfter, removeBefore +*/ +template +void QCPDataContainer::clear() +{ + mData.clear(); + mPreallocIteration = 0; + mPreallocSize = 0; +} + +/*! + Re-sorts all data points in the container by their sort key. + + When setting, adding or removing points using the QCPDataContainer interface (\ref set, \ref add, + \ref remove, etc.), the container makes sure to always stay in a sorted state such that a full + resort is never necessary. However, if you choose to directly manipulate the sort key on data + points by accessing and modifying it through the non-const iterators (\ref begin, \ref end), it + is your responsibility to bring the container back into a sorted state before any other methods + are called on it. This can be achieved by calling this method immediately after finishing the + sort key manipulation. +*/ +template +void QCPDataContainer::sort() +{ + std::sort(begin(), end(), qcpLessThanSortKey); +} + +/*! + Frees all unused memory that is currently in the preallocation and postallocation pools. + + Note that QCPDataContainer automatically decides whether squeezing is necessary, if \ref + setAutoSqueeze is left enabled. It should thus not be necessary to use this method for typical + applications. + + The parameters \a preAllocation and \a postAllocation control whether pre- and/or post allocation + should be freed, respectively. +*/ +template +void QCPDataContainer::squeeze(bool preAllocation, bool postAllocation) +{ + if (preAllocation) + { + if (mPreallocSize > 0) + { + std::copy(begin(), end(), mData.begin()); + mData.resize(size()); + mPreallocSize = 0; + } + mPreallocIteration = 0; + } + if (postAllocation) + mData.squeeze(); +} + +/*! + Returns an iterator to the data point with a (sort-)key that is equal to, just below, or just + above \a sortKey. If \a expandedRange is true, the data point just below \a sortKey will be + considered, otherwise the one just above. + + This can be used in conjunction with \ref findEnd to iterate over data points within a given key + range, including or excluding the bounding data points that are just beyond the specified range. + + If \a expandedRange is true but there are no data points below \a sortKey, \ref constBegin is + returned. + + If the container is empty, returns \ref constEnd. + + \see findEnd, QCPPlottableInterface1D::findBegin +*/ +template +typename QCPDataContainer::const_iterator QCPDataContainer::findBegin(double sortKey, bool expandedRange) const +{ + if (isEmpty()) + return constEnd(); + + QCPDataContainer::const_iterator it = std::lower_bound(constBegin(), constEnd(), DataType::fromSortKey(sortKey), qcpLessThanSortKey); + if (expandedRange && it != constBegin()) // also covers it == constEnd case, and we know --constEnd is valid because mData isn't empty + --it; + return it; +} + +/*! + Returns an iterator to the element after the data point with a (sort-)key that is equal to, just + above or just below \a sortKey. If \a expandedRange is true, the data point just above \a sortKey + will be considered, otherwise the one just below. + + This can be used in conjunction with \ref findBegin to iterate over data points within a given + key range, including the bounding data points that are just below and above the specified range. + + If \a expandedRange is true but there are no data points above \a sortKey, \ref constEnd is + returned. + + If the container is empty, \ref constEnd is returned. + + \see findBegin, QCPPlottableInterface1D::findEnd +*/ +template +typename QCPDataContainer::const_iterator QCPDataContainer::findEnd(double sortKey, bool expandedRange) const +{ + if (isEmpty()) + return constEnd(); + + QCPDataContainer::const_iterator it = std::upper_bound(constBegin(), constEnd(), DataType::fromSortKey(sortKey), qcpLessThanSortKey); + if (expandedRange && it != constEnd()) + ++it; + return it; +} + +/*! + Returns the range encompassed by the (main-)key coordinate of all data points. The output + parameter \a foundRange indicates whether a sensible range was found. If this is false, you + should not use the returned QCPRange (e.g. the data container is empty or all points have the + same key). + + Use \a signDomain to control which sign of the key coordinates should be considered. This is + relevant e.g. for logarithmic plots which can mathematically only display one sign domain at a + time. + + If the DataType reports that its main key is equal to the sort key (\a sortKeyIsMainKey), as is + the case for most plottables, this method uses this fact and finds the range very quickly. + + \see valueRange +*/ +template +QCPRange QCPDataContainer::keyRange(bool &foundRange, QCP::SignDomain signDomain) +{ + if (isEmpty()) + { + foundRange = false; + return QCPRange(); + } + QCPRange range; + bool haveLower = false; + bool haveUpper = false; + double current; + + QCPDataContainer::const_iterator it = constBegin(); + QCPDataContainer::const_iterator itEnd = constEnd(); + if (signDomain == QCP::sdBoth) // range may be anywhere + { + if (DataType::sortKeyIsMainKey()) // if DataType is sorted by main key (e.g. QCPGraph, but not QCPCurve), use faster algorithm by finding just first and last key with non-NaN value + { + while (it != itEnd) // find first non-nan going up from left + { + if (!qIsNaN(it->mainValue())) + { + range.lower = it->mainKey(); + haveLower = true; + break; + } + ++it; + } + it = itEnd; + while (it != constBegin()) // find first non-nan going down from right + { + --it; + if (!qIsNaN(it->mainValue())) + { + range.upper = it->mainKey(); + haveUpper = true; + break; + } + } + } else // DataType is not sorted by main key, go through all data points and accordingly expand range + { + while (it != itEnd) + { + if (!qIsNaN(it->mainValue())) + { + current = it->mainKey(); + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + } + } else if (signDomain == QCP::sdNegative) // range may only be in the negative sign domain + { + while (it != itEnd) + { + if (!qIsNaN(it->mainValue())) + { + current = it->mainKey(); + if ((current < range.lower || !haveLower) && current < 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current < 0) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + } else if (signDomain == QCP::sdPositive) // range may only be in the positive sign domain + { + while (it != itEnd) + { + if (!qIsNaN(it->mainValue())) + { + current = it->mainKey(); + if ((current < range.lower || !haveLower) && current > 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current > 0) + { + range.upper = current; + haveUpper = true; + } + } + ++it; + } + } + + foundRange = haveLower && haveUpper; + return range; +} + +/*! + Returns the range encompassed by the value coordinates of the data points in the specified key + range (\a inKeyRange), using the full \a DataType::valueRange reported by the data points. The + output parameter \a foundRange indicates whether a sensible range was found. If this is false, + you should not use the returned QCPRange (e.g. the data container is empty or all points have the + same value). + + Inf and -Inf data values are ignored. + + If \a inKeyRange has both lower and upper bound set to zero (is equal to QCPRange()), + all data points are considered, without any restriction on the keys. + + Use \a signDomain to control which sign of the value coordinates should be considered. This is + relevant e.g. for logarithmic plots which can mathematically only display one sign domain at a + time. + + \see keyRange +*/ +template +QCPRange QCPDataContainer::valueRange(bool &foundRange, QCP::SignDomain signDomain, const QCPRange &inKeyRange) +{ + if (isEmpty()) + { + foundRange = false; + return QCPRange(); + } + QCPRange range; + const bool restrictKeyRange = inKeyRange != QCPRange(); + bool haveLower = false; + bool haveUpper = false; + QCPRange current; + QCPDataContainer::const_iterator itBegin = constBegin(); + QCPDataContainer::const_iterator itEnd = constEnd(); + if (DataType::sortKeyIsMainKey() && restrictKeyRange) + { + itBegin = findBegin(inKeyRange.lower, false); + itEnd = findEnd(inKeyRange.upper, false); + } + if (signDomain == QCP::sdBoth) // range may be anywhere + { + for (QCPDataContainer::const_iterator it = itBegin; it != itEnd; ++it) + { + if (restrictKeyRange && (it->mainKey() < inKeyRange.lower || it->mainKey() > inKeyRange.upper)) + continue; + current = it->valueRange(); + if ((current.lower < range.lower || !haveLower) && !qIsNaN(current.lower) && std::isfinite(current.lower)) + { + range.lower = current.lower; + haveLower = true; + } + if ((current.upper > range.upper || !haveUpper) && !qIsNaN(current.upper) && std::isfinite(current.upper)) + { + range.upper = current.upper; + haveUpper = true; + } + } + } else if (signDomain == QCP::sdNegative) // range may only be in the negative sign domain + { + for (QCPDataContainer::const_iterator it = itBegin; it != itEnd; ++it) + { + if (restrictKeyRange && (it->mainKey() < inKeyRange.lower || it->mainKey() > inKeyRange.upper)) + continue; + current = it->valueRange(); + if ((current.lower < range.lower || !haveLower) && current.lower < 0 && !qIsNaN(current.lower) && std::isfinite(current.lower)) + { + range.lower = current.lower; + haveLower = true; + } + if ((current.upper > range.upper || !haveUpper) && current.upper < 0 && !qIsNaN(current.upper) && std::isfinite(current.upper)) + { + range.upper = current.upper; + haveUpper = true; + } + } + } else if (signDomain == QCP::sdPositive) // range may only be in the positive sign domain + { + for (QCPDataContainer::const_iterator it = itBegin; it != itEnd; ++it) + { + if (restrictKeyRange && (it->mainKey() < inKeyRange.lower || it->mainKey() > inKeyRange.upper)) + continue; + current = it->valueRange(); + if ((current.lower < range.lower || !haveLower) && current.lower > 0 && !qIsNaN(current.lower) && std::isfinite(current.lower)) + { + range.lower = current.lower; + haveLower = true; + } + if ((current.upper > range.upper || !haveUpper) && current.upper > 0 && !qIsNaN(current.upper) && std::isfinite(current.upper)) + { + range.upper = current.upper; + haveUpper = true; + } + } + } + + foundRange = haveLower && haveUpper; + return range; +} + +/*! + Makes sure \a begin and \a end mark a data range that is both within the bounds of this data + container's data, as well as within the specified \a dataRange. The initial range described by + the passed iterators \a begin and \a end is never expanded, only contracted if necessary. + + This function doesn't require for \a dataRange to be within the bounds of this data container's + valid range. +*/ +template +void QCPDataContainer::limitIteratorsToDataRange(const_iterator &begin, const_iterator &end, const QCPDataRange &dataRange) const +{ + QCPDataRange iteratorRange(int(begin-constBegin()), int(end-constBegin())); + iteratorRange = iteratorRange.bounded(dataRange.bounded(this->dataRange())); + begin = constBegin()+iteratorRange.begin(); + end = constBegin()+iteratorRange.end(); +} + +/*! \internal + + Increases the preallocation pool to have a size of at least \a minimumPreallocSize. Depending on + the preallocation history, the container will grow by more than requested, to speed up future + consecutive size increases. + + if \a minimumPreallocSize is smaller than or equal to the current preallocation pool size, this + method does nothing. +*/ +template +void QCPDataContainer::preallocateGrow(int minimumPreallocSize) +{ + if (minimumPreallocSize <= mPreallocSize) + return; + + int newPreallocSize = minimumPreallocSize; + newPreallocSize += (1u< +void QCPDataContainer::performAutoSqueeze() +{ + const int totalAlloc = mData.capacity(); + const int postAllocSize = totalAlloc-mData.size(); + const int usedSize = size(); + bool shrinkPostAllocation = false; + bool shrinkPreAllocation = false; + if (totalAlloc > 650000) // if allocation is larger, shrink earlier with respect to total used size + { + shrinkPostAllocation = postAllocSize > usedSize*1.5; // QVector grow strategy is 2^n for static data. Watch out not to oscillate! + shrinkPreAllocation = mPreallocSize*10 > usedSize; + } else if (totalAlloc > 1000) // below 10 MiB raw data be generous with preallocated memory, below 1k points don't even bother + { + shrinkPostAllocation = postAllocSize > usedSize*5; + shrinkPreAllocation = mPreallocSize > usedSize*1.5; // preallocation can grow into postallocation, so can be smaller + } + + if (shrinkPreAllocation || shrinkPostAllocation) + squeeze(shrinkPreAllocation, shrinkPostAllocation); +} + + +/* end of 'src/datacontainer.h' */ + + +/* including file 'src/plottable.h' */ +/* modified 2022-11-06T12:45:56, size 8461 */ + +class QCP_LIB_DECL QCPSelectionDecorator +{ + Q_GADGET +public: + QCPSelectionDecorator(); + virtual ~QCPSelectionDecorator(); + + // getters: + QPen pen() const { return mPen; } + QBrush brush() const { return mBrush; } + QCPScatterStyle scatterStyle() const { return mScatterStyle; } + QCPScatterStyle::ScatterProperties usedScatterProperties() const { return mUsedScatterProperties; } + + // setters: + void setPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties=QCPScatterStyle::spPen); + void setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties); + + // non-virtual methods: + void applyPen(QCPPainter *painter) const; + void applyBrush(QCPPainter *painter) const; + QCPScatterStyle getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const; + + // introduced virtual methods: + virtual void copyFrom(const QCPSelectionDecorator *other); + virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection); + +protected: + // property members: + QPen mPen; + QBrush mBrush; + QCPScatterStyle mScatterStyle; + QCPScatterStyle::ScatterProperties mUsedScatterProperties; + // non-property members: + QCPAbstractPlottable *mPlottable; + + // introduced virtual methods: + virtual bool registerWithPlottable(QCPAbstractPlottable *plottable); + +private: + Q_DISABLE_COPY(QCPSelectionDecorator) + friend class QCPAbstractPlottable; +}; +Q_DECLARE_METATYPE(QCPSelectionDecorator*) + + +class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(bool antialiasedFill READ antialiasedFill WRITE setAntialiasedFill) + Q_PROPERTY(bool antialiasedScatters READ antialiasedScatters WRITE setAntialiasedScatters) + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QCPAxis* keyAxis READ keyAxis WRITE setKeyAxis) + Q_PROPERTY(QCPAxis* valueAxis READ valueAxis WRITE setValueAxis) + Q_PROPERTY(QCP::SelectionType selectable READ selectable WRITE setSelectable NOTIFY selectableChanged) + Q_PROPERTY(QCPDataSelection selection READ selection WRITE setSelection NOTIFY selectionChanged) + Q_PROPERTY(QCPSelectionDecorator* selectionDecorator READ selectionDecorator WRITE setSelectionDecorator) + /// \endcond +public: + QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPAbstractPlottable() Q_DECL_OVERRIDE; + + // getters: + QString name() const { return mName; } + bool antialiasedFill() const { return mAntialiasedFill; } + bool antialiasedScatters() const { return mAntialiasedScatters; } + QPen pen() const { return mPen; } + QBrush brush() const { return mBrush; } + QCPAxis *keyAxis() const { return mKeyAxis.data(); } + QCPAxis *valueAxis() const { return mValueAxis.data(); } + QCP::SelectionType selectable() const { return mSelectable; } + bool selected() const { return !mSelection.isEmpty(); } + QCPDataSelection selection() const { return mSelection; } + QCPSelectionDecorator *selectionDecorator() const { return mSelectionDecorator; } + + // setters: + void setName(const QString &name); + void setAntialiasedFill(bool enabled); + void setAntialiasedScatters(bool enabled); + void setPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setKeyAxis(QCPAxis *axis); + void setValueAxis(QCPAxis *axis); + Q_SLOT void setSelectable(QCP::SelectionType selectable); + Q_SLOT void setSelection(QCPDataSelection selection); + void setSelectionDecorator(QCPSelectionDecorator *decorator); + + // introduced virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE = 0; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables + virtual QCPPlottableInterface1D *interface1D() { return nullptr; } + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const = 0; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const = 0; + + // non-property methods: + void coordsToPixels(double key, double value, double &x, double &y) const; + const QPointF coordsToPixels(double key, double value) const; + void pixelsToCoords(double x, double y, double &key, double &value) const; + void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const; + void rescaleAxes(bool onlyEnlarge=false) const; + void rescaleKeyAxis(bool onlyEnlarge=false) const; + void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const; + bool addToLegend(QCPLegend *legend); + bool addToLegend(); + bool removeFromLegend(QCPLegend *legend) const; + bool removeFromLegend() const; + +signals: + void selectionChanged(bool selected); + void selectionChanged(const QCPDataSelection &selection); + void selectableChanged(QCP::SelectionType selectable); + +protected: + // property members: + QString mName; + bool mAntialiasedFill, mAntialiasedScatters; + QPen mPen; + QBrush mBrush; + QPointer mKeyAxis, mValueAxis; + QCP::SelectionType mSelectable; + QCPDataSelection mSelection; + QCPSelectionDecorator *mSelectionDecorator; + + // reimplemented virtual methods: + virtual QRect clipRect() const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE = 0; + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; + virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; + + // introduced virtual methods: + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const = 0; + + // non-virtual methods: + void applyFillAntialiasingHint(QCPPainter *painter) const; + void applyScattersAntialiasingHint(QCPPainter *painter) const; + +private: + Q_DISABLE_COPY(QCPAbstractPlottable) + + friend class QCustomPlot; + friend class QCPAxis; + friend class QCPPlottableLegendItem; +}; + + +/* end of 'src/plottable.h' */ + + +/* including file 'src/item.h' */ +/* modified 2022-11-06T12:45:56, size 9425 */ + +class QCP_LIB_DECL QCPItemAnchor +{ + Q_GADGET +public: + QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId=-1); + virtual ~QCPItemAnchor(); + + // getters: + QString name() const { return mName; } + virtual QPointF pixelPosition() const; + +protected: + // property members: + QString mName; + + // non-property members: + QCustomPlot *mParentPlot; + QCPAbstractItem *mParentItem; + int mAnchorId; + QSet mChildrenX, mChildrenY; + + // introduced virtual methods: + virtual QCPItemPosition *toQCPItemPosition() { return nullptr; } + + // non-virtual methods: + void addChildX(QCPItemPosition* pos); // called from pos when this anchor is set as parent + void removeChildX(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted + void addChildY(QCPItemPosition* pos); // called from pos when this anchor is set as parent + void removeChildY(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted + +private: + Q_DISABLE_COPY(QCPItemAnchor) + + friend class QCPItemPosition; +}; + + + +class QCP_LIB_DECL QCPItemPosition : public QCPItemAnchor +{ + Q_GADGET +public: + /*! + Defines the ways an item position can be specified. Thus it defines what the numbers passed to + \ref setCoords actually mean. + + \see setType + */ + enum PositionType { ptAbsolute ///< Static positioning in pixels, starting from the top left corner of the viewport/widget. + ,ptViewportRatio ///< Static positioning given by a fraction of the viewport size. For example, if you call setCoords(0, 0), the position will be at the top + ///< left corner of the viewport/widget. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and + ///< vertically at the top of the viewport/widget, etc. + ,ptAxisRectRatio ///< Static positioning given by a fraction of the axis rect size (see \ref setAxisRect). For example, if you call setCoords(0, 0), the position will be at the top + ///< left corner of the axis rect. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and + ///< vertically at the top of the axis rect, etc. You can also go beyond the axis rect by providing negative coordinates or coordinates larger than 1. + ,ptPlotCoords ///< Dynamic positioning at a plot coordinate defined by two axes (see \ref setAxes). + }; + Q_ENUMS(PositionType) + + QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name); + virtual ~QCPItemPosition() Q_DECL_OVERRIDE; + + // getters: + PositionType type() const { return typeX(); } + PositionType typeX() const { return mPositionTypeX; } + PositionType typeY() const { return mPositionTypeY; } + QCPItemAnchor *parentAnchor() const { return parentAnchorX(); } + QCPItemAnchor *parentAnchorX() const { return mParentAnchorX; } + QCPItemAnchor *parentAnchorY() const { return mParentAnchorY; } + double key() const { return mKey; } + double value() const { return mValue; } + QPointF coords() const { return QPointF(mKey, mValue); } + QCPAxis *keyAxis() const { return mKeyAxis.data(); } + QCPAxis *valueAxis() const { return mValueAxis.data(); } + QCPAxisRect *axisRect() const; + virtual QPointF pixelPosition() const Q_DECL_OVERRIDE; + + // setters: + void setType(PositionType type); + void setTypeX(PositionType type); + void setTypeY(PositionType type); + bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false); + bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false); + bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false); + void setCoords(double key, double value); + void setCoords(const QPointF &pos); + void setAxes(QCPAxis* keyAxis, QCPAxis* valueAxis); + void setAxisRect(QCPAxisRect *axisRect); + void setPixelPosition(const QPointF &pixelPosition); + +protected: + // property members: + PositionType mPositionTypeX, mPositionTypeY; + QPointer mKeyAxis, mValueAxis; + QPointer mAxisRect; + double mKey, mValue; + QCPItemAnchor *mParentAnchorX, *mParentAnchorY; + + // reimplemented virtual methods: + virtual QCPItemPosition *toQCPItemPosition() Q_DECL_OVERRIDE { return this; } + +private: + Q_DISABLE_COPY(QCPItemPosition) + +}; +Q_DECLARE_METATYPE(QCPItemPosition::PositionType) + + +class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect) + Q_PROPERTY(QCPAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect) + Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged) + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged) + /// \endcond +public: + explicit QCPAbstractItem(QCustomPlot *parentPlot); + virtual ~QCPAbstractItem() Q_DECL_OVERRIDE; + + // getters: + bool clipToAxisRect() const { return mClipToAxisRect; } + QCPAxisRect *clipAxisRect() const; + bool selectable() const { return mSelectable; } + bool selected() const { return mSelected; } + + // setters: + void setClipToAxisRect(bool clip); + void setClipAxisRect(QCPAxisRect *rect); + Q_SLOT void setSelectable(bool selectable); + Q_SLOT void setSelected(bool selected); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE = 0; + + // non-virtual methods: + QList positions() const { return mPositions; } + QList anchors() const { return mAnchors; } + QCPItemPosition *position(const QString &name) const; + QCPItemAnchor *anchor(const QString &name) const; + bool hasAnchor(const QString &name) const; + +signals: + void selectionChanged(bool selected); + void selectableChanged(bool selectable); + +protected: + // property members: + bool mClipToAxisRect; + QPointer mClipAxisRect; + QList mPositions; + QList mAnchors; + bool mSelectable, mSelected; + + // reimplemented virtual methods: + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + virtual QRect clipRect() const Q_DECL_OVERRIDE; + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE = 0; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; + virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; + + // introduced virtual methods: + virtual QPointF anchorPixelPosition(int anchorId) const; + + // non-virtual methods: + double rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const; + QCPItemPosition *createPosition(const QString &name); + QCPItemAnchor *createAnchor(const QString &name, int anchorId); + +private: + Q_DISABLE_COPY(QCPAbstractItem) + + friend class QCustomPlot; + friend class QCPItemAnchor; +}; + +/* end of 'src/item.h' */ + + +/* including file 'src/core.h' */ +/* modified 2022-11-06T12:45:56, size 19304 */ + +class QCP_LIB_DECL QCustomPlot : public QWidget +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QRect viewport READ viewport WRITE setViewport) + Q_PROPERTY(QPixmap background READ background WRITE setBackground) + Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled) + Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode) + Q_PROPERTY(QCPLayoutGrid* plotLayout READ plotLayout) + Q_PROPERTY(bool autoAddPlottableToLegend READ autoAddPlottableToLegend WRITE setAutoAddPlottableToLegend) + Q_PROPERTY(int selectionTolerance READ selectionTolerance WRITE setSelectionTolerance) + Q_PROPERTY(bool noAntialiasingOnDrag READ noAntialiasingOnDrag WRITE setNoAntialiasingOnDrag) + Q_PROPERTY(Qt::KeyboardModifier multiSelectModifier READ multiSelectModifier WRITE setMultiSelectModifier) + Q_PROPERTY(bool openGl READ openGl WRITE setOpenGl) + /// \endcond +public: + /*! + Defines how a layer should be inserted relative to an other layer. + + \see addLayer, moveLayer + */ + enum LayerInsertMode { limBelow ///< Layer is inserted below other layer + ,limAbove ///< Layer is inserted above other layer + }; + Q_ENUMS(LayerInsertMode) + + /*! + Defines with what timing the QCustomPlot surface is refreshed after a replot. + + \see replot + */ + enum RefreshPriority { rpImmediateRefresh ///< Replots immediately and repaints the widget immediately by calling QWidget::repaint() after the replot + ,rpQueuedRefresh ///< Replots immediately, but queues the widget repaint, by calling QWidget::update() after the replot. This way multiple redundant widget repaints can be avoided. + ,rpRefreshHint ///< Whether to use immediate or queued refresh depends on whether the plotting hint \ref QCP::phImmediateRefresh is set, see \ref setPlottingHints. + ,rpQueuedReplot ///< Queues the entire replot for the next event loop iteration. This way multiple redundant replots can be avoided. The actual replot is then done with \ref rpRefreshHint priority. + }; + Q_ENUMS(RefreshPriority) + + explicit QCustomPlot(QWidget *parent = nullptr); + virtual ~QCustomPlot() Q_DECL_OVERRIDE; + + // getters: + QRect viewport() const { return mViewport; } + double bufferDevicePixelRatio() const { return mBufferDevicePixelRatio; } + QPixmap background() const { return mBackgroundPixmap; } + bool backgroundScaled() const { return mBackgroundScaled; } + Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; } + QCPLayoutGrid *plotLayout() const { return mPlotLayout; } + QCP::AntialiasedElements antialiasedElements() const { return mAntialiasedElements; } + QCP::AntialiasedElements notAntialiasedElements() const { return mNotAntialiasedElements; } + bool autoAddPlottableToLegend() const { return mAutoAddPlottableToLegend; } + const QCP::Interactions interactions() const { return mInteractions; } + int selectionTolerance() const { return mSelectionTolerance; } + bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; } + QCP::PlottingHints plottingHints() const { return mPlottingHints; } + Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; } + QCP::SelectionRectMode selectionRectMode() const { return mSelectionRectMode; } + QCPSelectionRect *selectionRect() const { return mSelectionRect; } + bool openGl() const { return mOpenGl; } + + // setters: + void setViewport(const QRect &rect); + void setBufferDevicePixelRatio(double ratio); + void setBackground(const QPixmap &pm); + void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding); + void setBackground(const QBrush &brush); + void setBackgroundScaled(bool scaled); + void setBackgroundScaledMode(Qt::AspectRatioMode mode); + void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements); + void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true); + void setNotAntialiasedElements(const QCP::AntialiasedElements ¬AntialiasedElements); + void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true); + void setAutoAddPlottableToLegend(bool on); + void setInteractions(const QCP::Interactions &interactions); + void setInteraction(const QCP::Interaction &interaction, bool enabled=true); + void setSelectionTolerance(int pixels); + void setNoAntialiasingOnDrag(bool enabled); + void setPlottingHints(const QCP::PlottingHints &hints); + void setPlottingHint(QCP::PlottingHint hint, bool enabled=true); + void setMultiSelectModifier(Qt::KeyboardModifier modifier); + void setSelectionRectMode(QCP::SelectionRectMode mode); + void setSelectionRect(QCPSelectionRect *selectionRect); + void setOpenGl(bool enabled, int multisampling=16); + + // non-property methods: + // plottable interface: + QCPAbstractPlottable *plottable(int index); + QCPAbstractPlottable *plottable(); + bool removePlottable(QCPAbstractPlottable *plottable); + bool removePlottable(int index); + int clearPlottables(); + int plottableCount() const; + QList selectedPlottables() const; + template + PlottableType *plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const; + QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const; + bool hasPlottable(QCPAbstractPlottable *plottable) const; + + // specialized interface for QCPGraph: + QCPGraph *graph(int index) const; + QCPGraph *graph() const; + QCPGraph *addGraph(QCPAxis *keyAxis=nullptr, QCPAxis *valueAxis=nullptr); + bool removeGraph(QCPGraph *graph); + bool removeGraph(int index); + int clearGraphs(); + int graphCount() const; + QList selectedGraphs() const; + + // item interface: + QCPAbstractItem *item(int index) const; + QCPAbstractItem *item() const; + bool removeItem(QCPAbstractItem *item); + bool removeItem(int index); + int clearItems(); + int itemCount() const; + QList selectedItems() const; + template + ItemType *itemAt(const QPointF &pos, bool onlySelectable=false) const; + QCPAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const; + bool hasItem(QCPAbstractItem *item) const; + + // layer interface: + QCPLayer *layer(const QString &name) const; + QCPLayer *layer(int index) const; + QCPLayer *currentLayer() const; + bool setCurrentLayer(const QString &name); + bool setCurrentLayer(QCPLayer *layer); + int layerCount() const; + bool addLayer(const QString &name, QCPLayer *otherLayer=nullptr, LayerInsertMode insertMode=limAbove); + bool removeLayer(QCPLayer *layer); + bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove); + + // axis rect/layout interface: + int axisRectCount() const; + QCPAxisRect* axisRect(int index=0) const; + QList axisRects() const; + QCPLayoutElement* layoutElementAt(const QPointF &pos) const; + QCPAxisRect* axisRectAt(const QPointF &pos) const; + Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false); + + QList selectedAxes() const; + QList selectedLegends() const; + Q_SLOT void deselectAll(); + + bool savePdf(const QString &fileName, int width=0, int height=0, QCP::ExportPen exportPen=QCP::epAllowCosmetic, const QString &pdfCreator=QString(), const QString &pdfTitle=QString()); + bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch); + bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch); + bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch); + bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch); + QPixmap toPixmap(int width=0, int height=0, double scale=1.0); + void toPainter(QCPPainter *painter, int width=0, int height=0); + Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint); + double replotTime(bool average=false) const; + + QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2; + QCPLegend *legend; + +signals: + void mouseDoubleClick(QMouseEvent *event); + void mousePress(QMouseEvent *event); + void mouseMove(QMouseEvent *event); + void mouseRelease(QMouseEvent *event); + void mouseWheel(QWheelEvent *event); + + void plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event); + void plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event); + void itemClick(QCPAbstractItem *item, QMouseEvent *event); + void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event); + void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event); + void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event); + void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event); + void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event); + + void selectionChangedByUser(); + void beforeReplot(); + void afterLayout(); + void afterReplot(); + +protected: + // property members: + QRect mViewport; + double mBufferDevicePixelRatio; + QCPLayoutGrid *mPlotLayout; + bool mAutoAddPlottableToLegend; + QList mPlottables; + QList mGraphs; // extra list of plottables also in mPlottables that are of type QCPGraph + QList mItems; + QList mLayers; + QCP::AntialiasedElements mAntialiasedElements, mNotAntialiasedElements; + QCP::Interactions mInteractions; + int mSelectionTolerance; + bool mNoAntialiasingOnDrag; + QBrush mBackgroundBrush; + QPixmap mBackgroundPixmap; + QPixmap mScaledBackgroundPixmap; + bool mBackgroundScaled; + Qt::AspectRatioMode mBackgroundScaledMode; + QCPLayer *mCurrentLayer; + QCP::PlottingHints mPlottingHints; + Qt::KeyboardModifier mMultiSelectModifier; + QCP::SelectionRectMode mSelectionRectMode; + QCPSelectionRect *mSelectionRect; + bool mOpenGl; + + // non-property members: + QList > mPaintBuffers; + QPoint mMousePressPos; + bool mMouseHasMoved; + QPointer mMouseEventLayerable; + QPointer mMouseSignalLayerable; + QVariant mMouseEventLayerableDetails; + QVariant mMouseSignalLayerableDetails; + bool mReplotting; + bool mReplotQueued; + double mReplotTime, mReplotTimeAverage; + int mOpenGlMultisamples; + QCP::AntialiasedElements mOpenGlAntialiasedElementsBackup; + bool mOpenGlCacheLabelsBackup; +#ifdef QCP_OPENGL_FBO + QSharedPointer mGlContext; + QSharedPointer mGlSurface; + QSharedPointer mGlPaintDevice; +#endif + + // reimplemented virtual methods: + virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; + virtual void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + // introduced virtual methods: + virtual void draw(QCPPainter *painter); + virtual void updateLayout(); + virtual void axisRemoved(QCPAxis *axis); + virtual void legendRemoved(QCPLegend *legend); + Q_SLOT virtual void processRectSelection(QRect rect, QMouseEvent *event); + Q_SLOT virtual void processRectZoom(QRect rect, QMouseEvent *event); + Q_SLOT virtual void processPointSelection(QMouseEvent *event); + + // non-virtual methods: + bool registerPlottable(QCPAbstractPlottable *plottable); + bool registerGraph(QCPGraph *graph); + bool registerItem(QCPAbstractItem* item); + void updateLayerIndices() const; + QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=nullptr) const; + QList layerableListAt(const QPointF &pos, bool onlySelectable, QList *selectionDetails=nullptr) const; + void drawBackground(QCPPainter *painter); + void setupPaintBuffers(); + QCPAbstractPaintBuffer *createPaintBuffer(); + bool hasInvalidatedPaintBuffers(); + bool setupOpenGl(); + void freeOpenGl(); + + friend class QCPLegend; + friend class QCPAxis; + friend class QCPLayer; + friend class QCPAxisRect; + friend class QCPAbstractPlottable; + friend class QCPGraph; + friend class QCPAbstractItem; +}; +Q_DECLARE_METATYPE(QCustomPlot::LayerInsertMode) +Q_DECLARE_METATYPE(QCustomPlot::RefreshPriority) + + +// implementation of template functions: + +/*! + Returns the plottable at the pixel position \a pos. The plottable type (a QCPAbstractPlottable + subclass) that shall be taken into consideration can be specified via the template parameter. + + Plottables that only consist of single lines (like graphs) have a tolerance band around them, see + \ref setSelectionTolerance. If multiple plottables come into consideration, the one closest to \a + pos is returned. + + If \a onlySelectable is true, only plottables that are selectable + (QCPAbstractPlottable::setSelectable) are considered. + + if \a dataIndex is non-null, it is set to the index of the plottable's data point that is closest + to \a pos. + + If there is no plottable of the specified type at \a pos, returns \c nullptr. + + \see itemAt, layoutElementAt +*/ +template +PlottableType *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const +{ + PlottableType *resultPlottable = 0; + QVariant resultDetails; + double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value + + foreach (QCPAbstractPlottable *plottable, mPlottables) + { + PlottableType *currentPlottable = qobject_cast(plottable); + if (!currentPlottable || (onlySelectable && !currentPlottable->selectable())) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractPlottable::selectable + continue; + if (currentPlottable->clipRect().contains(pos.toPoint())) // only consider clicks where the plottable is actually visible + { + QVariant details; + double currentDistance = currentPlottable->selectTest(pos, false, dataIndex ? &details : nullptr); + if (currentDistance >= 0 && currentDistance < resultDistance) + { + resultPlottable = currentPlottable; + resultDetails = details; + resultDistance = currentDistance; + } + } + } + + if (resultPlottable && dataIndex) + { + QCPDataSelection sel = resultDetails.value(); + if (!sel.isEmpty()) + *dataIndex = sel.dataRange(0).begin(); + } + return resultPlottable; +} + +/*! + Returns the item at the pixel position \a pos. The item type (a QCPAbstractItem subclass) that shall be + taken into consideration can be specified via the template parameter. Items that only consist of single + lines (e.g. \ref QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref + setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is returned. + + If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are + considered. + + If there is no item at \a pos, returns \c nullptr. + + \see plottableAt, layoutElementAt +*/ +template +ItemType *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const +{ + ItemType *resultItem = 0; + double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value + + foreach (QCPAbstractItem *item, mItems) + { + ItemType *currentItem = qobject_cast(item); + if (!currentItem || (onlySelectable && !currentItem->selectable())) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable + continue; + if (!currentItem->clipToAxisRect() || currentItem->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it + { + double currentDistance = currentItem->selectTest(pos, false); + if (currentDistance >= 0 && currentDistance < resultDistance) + { + resultItem = currentItem; + resultDistance = currentDistance; + } + } + } + + return resultItem; +} + + + +/* end of 'src/core.h' */ + + +/* including file 'src/plottable1d.h' */ +/* modified 2022-11-06T12:45:56, size 25638 */ + +class QCPPlottableInterface1D +{ +public: + virtual ~QCPPlottableInterface1D() = default; + // introduced pure virtual methods: + virtual int dataCount() const = 0; + virtual double dataMainKey(int index) const = 0; + virtual double dataSortKey(int index) const = 0; + virtual double dataMainValue(int index) const = 0; + virtual QCPRange dataValueRange(int index) const = 0; + virtual QPointF dataPixelPosition(int index) const = 0; + virtual bool sortKeyIsMainKey() const = 0; + virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const = 0; + virtual int findBegin(double sortKey, bool expandedRange=true) const = 0; + virtual int findEnd(double sortKey, bool expandedRange=true) const = 0; +}; + +template +class QCPAbstractPlottable1D : public QCPAbstractPlottable, public QCPPlottableInterface1D // no QCP_LIB_DECL, template class ends up in header (cpp included below) +{ + // No Q_OBJECT macro due to template class + +public: + QCPAbstractPlottable1D(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPAbstractPlottable1D() Q_DECL_OVERRIDE; + + // virtual methods of 1d plottable interface: + virtual int dataCount() const Q_DECL_OVERRIDE; + virtual double dataMainKey(int index) const Q_DECL_OVERRIDE; + virtual double dataSortKey(int index) const Q_DECL_OVERRIDE; + virtual double dataMainValue(int index) const Q_DECL_OVERRIDE; + virtual QCPRange dataValueRange(int index) const Q_DECL_OVERRIDE; + virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE; + virtual bool sortKeyIsMainKey() const Q_DECL_OVERRIDE; + virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE; + virtual int findBegin(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; + virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; } + +protected: + // property members: + QSharedPointer > mDataContainer; + + // helpers for subclasses: + void getDataSegments(QList &selectedSegments, QList &unselectedSegments) const; + void drawPolyline(QCPPainter *painter, const QVector &lineData) const; + +private: + Q_DISABLE_COPY(QCPAbstractPlottable1D) + +}; + + + +// include implementation in header since it is a class template: +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPPlottableInterface1D +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPPlottableInterface1D + \brief Defines an abstract interface for one-dimensional plottables + + This class contains only pure virtual methods which define a common interface to the data + of one-dimensional plottables. + + For example, it is implemented by the template class \ref QCPAbstractPlottable1D (the preferred + base class for one-dimensional plottables). So if you use that template class as base class of + your one-dimensional plottable, you won't have to care about implementing the 1d interface + yourself. + + If your plottable doesn't derive from \ref QCPAbstractPlottable1D but still wants to provide a 1d + interface (e.g. like \ref QCPErrorBars does), you should inherit from both \ref + QCPAbstractPlottable and \ref QCPPlottableInterface1D and accordingly reimplement the pure + virtual methods of the 1d interface, matching your data container. Also, reimplement \ref + QCPAbstractPlottable::interface1D to return the \c this pointer. + + If you have a \ref QCPAbstractPlottable pointer, you can check whether it implements this + interface by calling \ref QCPAbstractPlottable::interface1D and testing it for a non-zero return + value. If it indeed implements this interface, you may use it to access the plottable's data + without needing to know the exact type of the plottable or its data point type. +*/ + +/* start documentation of pure virtual functions */ + +/*! \fn virtual int QCPPlottableInterface1D::dataCount() const = 0; + + Returns the number of data points of the plottable. +*/ + +/*! \fn virtual QCPDataSelection QCPPlottableInterface1D::selectTestRect(const QRectF &rect, bool onlySelectable) const = 0; + + Returns a data selection containing all the data points of this plottable which are contained (or + hit by) \a rect. This is used mainly in the selection rect interaction for data selection (\ref + dataselection "data selection mechanism"). + + If \a onlySelectable is true, an empty QCPDataSelection is returned if this plottable is not + selectable (i.e. if \ref QCPAbstractPlottable::setSelectable is \ref QCP::stNone). + + \note \a rect must be a normalized rect (positive or zero width and height). This is especially + important when using the rect of \ref QCPSelectionRect::accepted, which is not necessarily + normalized. Use QRect::normalized() when passing a rect which might not be normalized. +*/ + +/*! \fn virtual double QCPPlottableInterface1D::dataMainKey(int index) const = 0 + + Returns the main key of the data point at the given \a index. + + What the main key is, is defined by the plottable's data type. See the \ref + qcpdatacontainer-datatype "QCPDataContainer DataType" documentation for details about this naming + convention. +*/ + +/*! \fn virtual double QCPPlottableInterface1D::dataSortKey(int index) const = 0 + + Returns the sort key of the data point at the given \a index. + + What the sort key is, is defined by the plottable's data type. See the \ref + qcpdatacontainer-datatype "QCPDataContainer DataType" documentation for details about this naming + convention. +*/ + +/*! \fn virtual double QCPPlottableInterface1D::dataMainValue(int index) const = 0 + + Returns the main value of the data point at the given \a index. + + What the main value is, is defined by the plottable's data type. See the \ref + qcpdatacontainer-datatype "QCPDataContainer DataType" documentation for details about this naming + convention. +*/ + +/*! \fn virtual QCPRange QCPPlottableInterface1D::dataValueRange(int index) const = 0 + + Returns the value range of the data point at the given \a index. + + What the value range is, is defined by the plottable's data type. See the \ref + qcpdatacontainer-datatype "QCPDataContainer DataType" documentation for details about this naming + convention. +*/ + +/*! \fn virtual QPointF QCPPlottableInterface1D::dataPixelPosition(int index) const = 0 + + Returns the pixel position on the widget surface at which the data point at the given \a index + appears. + + Usually this corresponds to the point of \ref dataMainKey/\ref dataMainValue, in pixel + coordinates. However, depending on the plottable, this might be a different apparent position + than just a coord-to-pixel transform of those values. For example, \ref QCPBars apparent data + values can be shifted depending on their stacking, bar grouping or configured base value. +*/ + +/*! \fn virtual bool QCPPlottableInterface1D::sortKeyIsMainKey() const = 0 + + Returns whether the sort key (\ref dataSortKey) is identical to the main key (\ref dataMainKey). + + What the sort and main keys are, is defined by the plottable's data type. See the \ref + qcpdatacontainer-datatype "QCPDataContainer DataType" documentation for details about this naming + convention. +*/ + +/*! \fn virtual int QCPPlottableInterface1D::findBegin(double sortKey, bool expandedRange) const = 0 + + Returns the index of the data point with a (sort-)key that is equal to, just below, or just above + \a sortKey. If \a expandedRange is true, the data point just below \a sortKey will be considered, + otherwise the one just above. + + This can be used in conjunction with \ref findEnd to iterate over data points within a given key + range, including or excluding the bounding data points that are just beyond the specified range. + + If \a expandedRange is true but there are no data points below \a sortKey, 0 is returned. + + If the container is empty, returns 0 (in that case, \ref findEnd will also return 0, so a loop + using these methods will not iterate over the index 0). + + \see findEnd, QCPDataContainer::findBegin +*/ + +/*! \fn virtual int QCPPlottableInterface1D::findEnd(double sortKey, bool expandedRange) const = 0 + + Returns the index one after the data point with a (sort-)key that is equal to, just above, or + just below \a sortKey. If \a expandedRange is true, the data point just above \a sortKey will be + considered, otherwise the one just below. + + This can be used in conjunction with \ref findBegin to iterate over data points within a given + key range, including the bounding data points that are just below and above the specified range. + + If \a expandedRange is true but there are no data points above \a sortKey, the index just above the + highest data point is returned. + + If the container is empty, returns 0. + + \see findBegin, QCPDataContainer::findEnd +*/ + +/* end documentation of pure virtual functions */ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractPlottable1D +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractPlottable1D + \brief A template base class for plottables with one-dimensional data + + This template class derives from \ref QCPAbstractPlottable and from the abstract interface \ref + QCPPlottableInterface1D. It serves as a base class for all one-dimensional data (i.e. data with + one key dimension), such as \ref QCPGraph and QCPCurve. + + The template parameter \a DataType is the type of the data points of this plottable (e.g. \ref + QCPGraphData or \ref QCPCurveData). The main purpose of this base class is to provide the member + \a mDataContainer (a shared pointer to a \ref QCPDataContainer "QCPDataContainer") and + implement the according virtual methods of the \ref QCPPlottableInterface1D, such that most + subclassed plottables don't need to worry about this anymore. + + Further, it provides a convenience method for retrieving selected/unselected data segments via + \ref getDataSegments. This is useful when subclasses implement their \ref draw method and need to + draw selected segments with a different pen/brush than unselected segments (also see \ref + QCPSelectionDecorator). + + This class implements basic functionality of \ref QCPAbstractPlottable::selectTest and \ref + QCPPlottableInterface1D::selectTestRect, assuming point-like data points, based on the 1D data + interface. In spite of that, most plottable subclasses will want to reimplement those methods + again, to provide a more accurate hit test based on their specific data visualization geometry. +*/ + +/* start documentation of inline functions */ + +/*! \fn QCPPlottableInterface1D *QCPAbstractPlottable1D::interface1D() + + Returns a \ref QCPPlottableInterface1D pointer to this plottable, providing access to its 1D + interface. + + \seebaseclassmethod +*/ + +/* end documentation of inline functions */ + +/*! + Forwards \a keyAxis and \a valueAxis to the \ref QCPAbstractPlottable::QCPAbstractPlottable + "QCPAbstractPlottable" constructor and allocates the \a mDataContainer. +*/ +template +QCPAbstractPlottable1D::QCPAbstractPlottable1D(QCPAxis *keyAxis, QCPAxis *valueAxis) : + QCPAbstractPlottable(keyAxis, valueAxis), + mDataContainer(new QCPDataContainer) +{ +} + +template +QCPAbstractPlottable1D::~QCPAbstractPlottable1D() +{ +} + +/*! + \copydoc QCPPlottableInterface1D::dataCount +*/ +template +int QCPAbstractPlottable1D::dataCount() const +{ + return mDataContainer->size(); +} + +/*! + \copydoc QCPPlottableInterface1D::dataMainKey +*/ +template +double QCPAbstractPlottable1D::dataMainKey(int index) const +{ + if (index >= 0 && index < mDataContainer->size()) + { + return (mDataContainer->constBegin()+index)->mainKey(); + } else + { + qDebug() << Q_FUNC_INFO << "Index out of bounds" << index; + return 0; + } +} + +/*! + \copydoc QCPPlottableInterface1D::dataSortKey +*/ +template +double QCPAbstractPlottable1D::dataSortKey(int index) const +{ + if (index >= 0 && index < mDataContainer->size()) + { + return (mDataContainer->constBegin()+index)->sortKey(); + } else + { + qDebug() << Q_FUNC_INFO << "Index out of bounds" << index; + return 0; + } +} + +/*! + \copydoc QCPPlottableInterface1D::dataMainValue +*/ +template +double QCPAbstractPlottable1D::dataMainValue(int index) const +{ + if (index >= 0 && index < mDataContainer->size()) + { + return (mDataContainer->constBegin()+index)->mainValue(); + } else + { + qDebug() << Q_FUNC_INFO << "Index out of bounds" << index; + return 0; + } +} + +/*! + \copydoc QCPPlottableInterface1D::dataValueRange +*/ +template +QCPRange QCPAbstractPlottable1D::dataValueRange(int index) const +{ + if (index >= 0 && index < mDataContainer->size()) + { + return (mDataContainer->constBegin()+index)->valueRange(); + } else + { + qDebug() << Q_FUNC_INFO << "Index out of bounds" << index; + return QCPRange(0, 0); + } +} + +/*! + \copydoc QCPPlottableInterface1D::dataPixelPosition +*/ +template +QPointF QCPAbstractPlottable1D::dataPixelPosition(int index) const +{ + if (index >= 0 && index < mDataContainer->size()) + { + const typename QCPDataContainer::const_iterator it = mDataContainer->constBegin()+index; + return coordsToPixels(it->mainKey(), it->mainValue()); + } else + { + qDebug() << Q_FUNC_INFO << "Index out of bounds" << index; + return QPointF(); + } +} + +/*! + \copydoc QCPPlottableInterface1D::sortKeyIsMainKey +*/ +template +bool QCPAbstractPlottable1D::sortKeyIsMainKey() const +{ + return DataType::sortKeyIsMainKey(); +} + +/*! + Implements a rect-selection algorithm assuming the data (accessed via the 1D data interface) is + point-like. Most subclasses will want to reimplement this method again, to provide a more + accurate hit test based on the true data visualization geometry. + + \seebaseclassmethod +*/ +template +QCPDataSelection QCPAbstractPlottable1D::selectTestRect(const QRectF &rect, bool onlySelectable) const +{ + QCPDataSelection result; + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return result; + if (!mKeyAxis || !mValueAxis) + return result; + + // convert rect given in pixels to ranges given in plot coordinates: + double key1, value1, key2, value2; + pixelsToCoords(rect.topLeft(), key1, value1); + pixelsToCoords(rect.bottomRight(), key2, value2); + QCPRange keyRange(key1, key2); // QCPRange normalizes internally so we don't have to care about whether key1 < key2 + QCPRange valueRange(value1, value2); + typename QCPDataContainer::const_iterator begin = mDataContainer->constBegin(); + typename QCPDataContainer::const_iterator end = mDataContainer->constEnd(); + if (DataType::sortKeyIsMainKey()) // we can assume that data is sorted by main key, so can reduce the searched key interval: + { + begin = mDataContainer->findBegin(keyRange.lower, false); + end = mDataContainer->findEnd(keyRange.upper, false); + } + if (begin == end) + return result; + + int currentSegmentBegin = -1; // -1 means we're currently not in a segment that's contained in rect + for (typename QCPDataContainer::const_iterator it=begin; it!=end; ++it) + { + if (currentSegmentBegin == -1) + { + if (valueRange.contains(it->mainValue()) && keyRange.contains(it->mainKey())) // start segment + currentSegmentBegin = int(it-mDataContainer->constBegin()); + } else if (!valueRange.contains(it->mainValue()) || !keyRange.contains(it->mainKey())) // segment just ended + { + result.addDataRange(QCPDataRange(currentSegmentBegin, int(it-mDataContainer->constBegin())), false); + currentSegmentBegin = -1; + } + } + // process potential last segment: + if (currentSegmentBegin != -1) + result.addDataRange(QCPDataRange(currentSegmentBegin, int(end-mDataContainer->constBegin())), false); + + result.simplify(); + return result; +} + +/*! + \copydoc QCPPlottableInterface1D::findBegin +*/ +template +int QCPAbstractPlottable1D::findBegin(double sortKey, bool expandedRange) const +{ + return int(mDataContainer->findBegin(sortKey, expandedRange)-mDataContainer->constBegin()); +} + +/*! + \copydoc QCPPlottableInterface1D::findEnd +*/ +template +int QCPAbstractPlottable1D::findEnd(double sortKey, bool expandedRange) const +{ + return int(mDataContainer->findEnd(sortKey, expandedRange)-mDataContainer->constBegin()); +} + +/*! + Implements a point-selection algorithm assuming the data (accessed via the 1D data interface) is + point-like. Most subclasses will want to reimplement this method again, to provide a more + accurate hit test based on the true data visualization geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data point + to \a pos. + + \seebaseclassmethod +*/ +template +double QCPAbstractPlottable1D::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const +{ + if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) + return -1; + if (!mKeyAxis || !mValueAxis) + return -1; + + QCPDataSelection selectionResult; + double minDistSqr = (std::numeric_limits::max)(); + int minDistIndex = mDataContainer->size(); + + typename QCPDataContainer::const_iterator begin = mDataContainer->constBegin(); + typename QCPDataContainer::const_iterator end = mDataContainer->constEnd(); + if (DataType::sortKeyIsMainKey()) // we can assume that data is sorted by main key, so can reduce the searched key interval: + { + // determine which key range comes into question, taking selection tolerance around pos into account: + double posKeyMin, posKeyMax, dummy; + pixelsToCoords(pos-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy); + pixelsToCoords(pos+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy); + if (posKeyMin > posKeyMax) + qSwap(posKeyMin, posKeyMax); + begin = mDataContainer->findBegin(posKeyMin, true); + end = mDataContainer->findEnd(posKeyMax, true); + } + if (begin == end) + return -1; + QCPRange keyRange(mKeyAxis->range()); + QCPRange valueRange(mValueAxis->range()); + for (typename QCPDataContainer::const_iterator it=begin; it!=end; ++it) + { + const double mainKey = it->mainKey(); + const double mainValue = it->mainValue(); + if (keyRange.contains(mainKey) && valueRange.contains(mainValue)) // make sure data point is inside visible range, for speedup in cases where sort key isn't main key and we iterate over all points + { + const double currentDistSqr = QCPVector2D(coordsToPixels(mainKey, mainValue)-pos).lengthSquared(); + if (currentDistSqr < minDistSqr) + { + minDistSqr = currentDistSqr; + minDistIndex = int(it-mDataContainer->constBegin()); + } + } + } + if (minDistIndex != mDataContainer->size()) + selectionResult.addDataRange(QCPDataRange(minDistIndex, minDistIndex+1), false); + + selectionResult.simplify(); + if (details) + details->setValue(selectionResult); + return qSqrt(minDistSqr); +} + +/*! + Splits all data into selected and unselected segments and outputs them via \a selectedSegments + and \a unselectedSegments, respectively. + + This is useful when subclasses implement their \ref draw method and need to draw selected + segments with a different pen/brush than unselected segments (also see \ref + QCPSelectionDecorator). + + \see setSelection +*/ +template +void QCPAbstractPlottable1D::getDataSegments(QList &selectedSegments, QList &unselectedSegments) const +{ + selectedSegments.clear(); + unselectedSegments.clear(); + if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty + { + if (selected()) + selectedSegments << QCPDataRange(0, dataCount()); + else + unselectedSegments << QCPDataRange(0, dataCount()); + } else + { + QCPDataSelection sel(selection()); + sel.simplify(); + selectedSegments = sel.dataRanges(); + unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges(); + } +} + +/*! + A helper method which draws a line with the passed \a painter, according to the pixel data in \a + lineData. NaN points create gaps in the line, as expected from QCustomPlot's plottables (this is + the main difference to QPainter's regular drawPolyline, which handles NaNs by lagging or + crashing). + + Further it uses a faster line drawing technique based on \ref QCPPainter::drawLine rather than \c + QPainter::drawPolyline if the configured \ref QCustomPlot::setPlottingHints() and \a painter + style allows. +*/ +template +void QCPAbstractPlottable1D::drawPolyline(QCPPainter *painter, const QVector &lineData) const +{ + // if drawing lines in plot (instead of PDF), reduce 1px lines to cosmetic, because at least in + // Qt6 drawing of "1px" width lines is much slower even though it has same appearance apart from + // High-DPI. In High-DPI cases people must set a pen width slightly larger than 1.0 to get + // correct DPI scaling of width, but of course with performance penalty. + if (!painter->modes().testFlag(QCPPainter::pmVectorized) && + qFuzzyCompare(painter->pen().widthF(), 1.0)) + { + QPen newPen = painter->pen(); + newPen.setWidth(0); + painter->setPen(newPen); + } + + // if drawing solid line and not in PDF, use much faster line drawing instead of polyline: + if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) && + painter->pen().style() == Qt::SolidLine && + !painter->modes().testFlag(QCPPainter::pmVectorized) && + !painter->modes().testFlag(QCPPainter::pmNoCaching)) + { + int i = 0; + bool lastIsNan = false; + const int lineDataSize = static_cast(lineData.size()); + while (i < lineDataSize && (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()))) // make sure first point is not NaN + ++i; + ++i; // because drawing works in 1 point retrospect + while (i < lineDataSize) + { + if (!qIsNaN(lineData.at(i).y()) && !qIsNaN(lineData.at(i).x())) // NaNs create a gap in the line + { + if (!lastIsNan) + painter->drawLine(lineData.at(i-1), lineData.at(i)); + else + lastIsNan = false; + } else + lastIsNan = true; + ++i; + } + } else + { + int segmentStart = 0; + int i = 0; + const int lineDataSize = static_cast(lineData.size()); + while (i < lineDataSize) + { + if (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()) || qIsInf(lineData.at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block + { + painter->drawPolyline(lineData.constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point + segmentStart = i+1; + } + ++i; + } + // draw last segment: + painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart); + } +} + + +/* end of 'src/plottable1d.h' */ + + +/* including file 'src/colorgradient.h' */ +/* modified 2022-11-06T12:45:56, size 7262 */ + +class QCP_LIB_DECL QCPColorGradient +{ + Q_GADGET +public: + /*! + Defines the color spaces in which color interpolation between gradient stops can be performed. + + \see setColorInterpolation + */ + enum ColorInterpolation { ciRGB ///< Color channels red, green and blue are linearly interpolated + ,ciHSV ///< Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the shortest angle distance) + }; + Q_ENUMS(ColorInterpolation) + + /*! + Defines how NaN data points shall appear in the plot. + + \see setNanHandling, setNanColor + */ + enum NanHandling { nhNone ///< NaN data points are not explicitly handled and shouldn't occur in the data (this gives slight performance improvement) + ,nhLowestColor ///< NaN data points appear as the lowest color defined in this QCPColorGradient + ,nhHighestColor ///< NaN data points appear as the highest color defined in this QCPColorGradient + ,nhTransparent ///< NaN data points appear transparent + ,nhNanColor ///< NaN data points appear as the color defined with \ref setNanColor + }; + Q_ENUMS(NanHandling) + + /*! + Defines the available presets that can be loaded with \ref loadPreset. See the documentation + there for an image of the presets. + */ + enum GradientPreset { gpGrayscale ///< Continuous lightness from black to white (suited for non-biased data representation) + ,gpHot ///< Continuous lightness from black over firey colors to white (suited for non-biased data representation) + ,gpCold ///< Continuous lightness from black over icey colors to white (suited for non-biased data representation) + ,gpNight ///< Continuous lightness from black over weak blueish colors to white (suited for non-biased data representation) + ,gpCandy ///< Blue over pink to white + ,gpGeography ///< Colors suitable to represent different elevations on geographical maps + ,gpIon ///< Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allows more precise magnitude estimates) + ,gpThermal ///< Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white + ,gpPolar ///< Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle and red for positive values + ,gpSpectrum ///< An approximation of the visible light spectrum (creates banding illusion but allows more precise magnitude estimates) + ,gpJet ///< Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion but allows more precise magnitude estimates) + ,gpHues ///< Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and phases, see \ref setPeriodic) + }; + Q_ENUMS(GradientPreset) + + QCPColorGradient(); + QCPColorGradient(GradientPreset preset); + bool operator==(const QCPColorGradient &other) const; + bool operator!=(const QCPColorGradient &other) const { return !(*this == other); } + + // getters: + int levelCount() const { return mLevelCount; } + QMap colorStops() const { return mColorStops; } + ColorInterpolation colorInterpolation() const { return mColorInterpolation; } + NanHandling nanHandling() const { return mNanHandling; } + QColor nanColor() const { return mNanColor; } + bool periodic() const { return mPeriodic; } + + // setters: + void setLevelCount(int n); + void setColorStops(const QMap &colorStops); + void setColorStopAt(double position, const QColor &color); + void setColorInterpolation(ColorInterpolation interpolation); + void setNanHandling(NanHandling handling); + void setNanColor(const QColor &color); + void setPeriodic(bool enabled); + + // non-property methods: + void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false); + void colorize(const double *data, const unsigned char *alpha, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false); + QRgb color(double position, const QCPRange &range, bool logarithmic=false); + void loadPreset(GradientPreset preset); + void clearColorStops(); + QCPColorGradient inverted() const; + +protected: + // property members: + int mLevelCount; + QMap mColorStops; + ColorInterpolation mColorInterpolation; + NanHandling mNanHandling; + QColor mNanColor; + bool mPeriodic; + + // non-property members: + QVector mColorBuffer; // have colors premultiplied with alpha (for usage with QImage::Format_ARGB32_Premultiplied) + bool mColorBufferInvalidated; + + // non-virtual methods: + bool stopsUseAlpha() const; + void updateColorBuffer(); +}; +Q_DECLARE_METATYPE(QCPColorGradient::ColorInterpolation) +Q_DECLARE_METATYPE(QCPColorGradient::NanHandling) +Q_DECLARE_METATYPE(QCPColorGradient::GradientPreset) + +/* end of 'src/colorgradient.h' */ + + +/* including file 'src/selectiondecorator-bracket.h' */ +/* modified 2022-11-06T12:45:56, size 4458 */ + +class QCP_LIB_DECL QCPSelectionDecoratorBracket : public QCPSelectionDecorator +{ + Q_GADGET +public: + + /*! + Defines which shape is drawn at the boundaries of selected data ranges. + + Some of the bracket styles further allow specifying a height and/or width, see \ref + setBracketHeight and \ref setBracketWidth. + */ + enum BracketStyle { bsSquareBracket ///< A square bracket is drawn. + ,bsHalfEllipse ///< A half ellipse is drawn. The size of the ellipse is given by the bracket width/height properties. + ,bsEllipse ///< An ellipse is drawn. The size of the ellipse is given by the bracket width/height properties. + ,bsPlus ///< A plus is drawn. + ,bsUserStyle ///< Start custom bracket styles at this index when subclassing and reimplementing \ref drawBracket. + }; + Q_ENUMS(BracketStyle) + + QCPSelectionDecoratorBracket(); + virtual ~QCPSelectionDecoratorBracket() Q_DECL_OVERRIDE; + + // getters: + QPen bracketPen() const { return mBracketPen; } + QBrush bracketBrush() const { return mBracketBrush; } + int bracketWidth() const { return mBracketWidth; } + int bracketHeight() const { return mBracketHeight; } + BracketStyle bracketStyle() const { return mBracketStyle; } + bool tangentToData() const { return mTangentToData; } + int tangentAverage() const { return mTangentAverage; } + + // setters: + void setBracketPen(const QPen &pen); + void setBracketBrush(const QBrush &brush); + void setBracketWidth(int width); + void setBracketHeight(int height); + void setBracketStyle(BracketStyle style); + void setTangentToData(bool enabled); + void setTangentAverage(int pointCount); + + // introduced virtual methods: + virtual void drawBracket(QCPPainter *painter, int direction) const; + + // virtual methods: + virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection) Q_DECL_OVERRIDE; + +protected: + // property members: + QPen mBracketPen; + QBrush mBracketBrush; + int mBracketWidth; + int mBracketHeight; + BracketStyle mBracketStyle; + bool mTangentToData; + int mTangentAverage; + + // non-virtual methods: + double getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const; + QPointF getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const; + +}; +Q_DECLARE_METATYPE(QCPSelectionDecoratorBracket::BracketStyle) + +/* end of 'src/selectiondecorator-bracket.h' */ + + +/* including file 'src/layoutelements/layoutelement-axisrect.h' */ +/* modified 2022-11-06T12:45:56, size 7529 */ + +class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPixmap background READ background WRITE setBackground) + Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled) + Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode) + Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag) + Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom) + /// \endcond +public: + explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true); + virtual ~QCPAxisRect() Q_DECL_OVERRIDE; + + // getters: + QPixmap background() const { return mBackgroundPixmap; } + QBrush backgroundBrush() const { return mBackgroundBrush; } + bool backgroundScaled() const { return mBackgroundScaled; } + Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; } + Qt::Orientations rangeDrag() const { return mRangeDrag; } + Qt::Orientations rangeZoom() const { return mRangeZoom; } + QCPAxis *rangeDragAxis(Qt::Orientation orientation); + QCPAxis *rangeZoomAxis(Qt::Orientation orientation); + QList rangeDragAxes(Qt::Orientation orientation); + QList rangeZoomAxes(Qt::Orientation orientation); + double rangeZoomFactor(Qt::Orientation orientation); + + // setters: + void setBackground(const QPixmap &pm); + void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding); + void setBackground(const QBrush &brush); + void setBackgroundScaled(bool scaled); + void setBackgroundScaledMode(Qt::AspectRatioMode mode); + void setRangeDrag(Qt::Orientations orientations); + void setRangeZoom(Qt::Orientations orientations); + void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical); + void setRangeDragAxes(QList axes); + void setRangeDragAxes(QList horizontal, QList vertical); + void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical); + void setRangeZoomAxes(QList axes); + void setRangeZoomAxes(QList horizontal, QList vertical); + void setRangeZoomFactor(double horizontalFactor, double verticalFactor); + void setRangeZoomFactor(double factor); + + // non-property methods: + int axisCount(QCPAxis::AxisType type) const; + QCPAxis *axis(QCPAxis::AxisType type, int index=0) const; + QList axes(QCPAxis::AxisTypes types) const; + QList axes() const; + QCPAxis *addAxis(QCPAxis::AxisType type, QCPAxis *axis=nullptr); + QList addAxes(QCPAxis::AxisTypes types); + bool removeAxis(QCPAxis *axis); + QCPLayoutInset *insetLayout() const { return mInsetLayout; } + + void zoom(const QRectF &pixelRect); + void zoom(const QRectF &pixelRect, const QList &affectedAxes); + void setupFullAxesBox(bool connectRanges=false); + QList plottables() const; + QList graphs() const; + QList items() const; + + // read-only interface imitating a QRect: + int left() const { return mRect.left(); } + int right() const { return mRect.right(); } + int top() const { return mRect.top(); } + int bottom() const { return mRect.bottom(); } + int width() const { return mRect.width(); } + int height() const { return mRect.height(); } + QSize size() const { return mRect.size(); } + QPoint topLeft() const { return mRect.topLeft(); } + QPoint topRight() const { return mRect.topRight(); } + QPoint bottomLeft() const { return mRect.bottomLeft(); } + QPoint bottomRight() const { return mRect.bottomRight(); } + QPoint center() const { return mRect.center(); } + + // reimplemented virtual methods: + virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE; + virtual QList elements(bool recursive) const Q_DECL_OVERRIDE; + +protected: + // property members: + QBrush mBackgroundBrush; + QPixmap mBackgroundPixmap; + QPixmap mScaledBackgroundPixmap; + bool mBackgroundScaled; + Qt::AspectRatioMode mBackgroundScaledMode; + QCPLayoutInset *mInsetLayout; + Qt::Orientations mRangeDrag, mRangeZoom; + QList > mRangeDragHorzAxis, mRangeDragVertAxis; + QList > mRangeZoomHorzAxis, mRangeZoomVertAxis; + double mRangeZoomFactorHorz, mRangeZoomFactorVert; + + // non-property members: + QList mDragStartHorzRange, mDragStartVertRange; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + bool mDragging; + QHash > mAxes; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual int calculateAutoMargin(QCP::MarginSide side) Q_DECL_OVERRIDE; + virtual void layoutChanged() Q_DECL_OVERRIDE; + // events: + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + // non-property methods: + void drawBackground(QCPPainter *painter); + void updateAxesOffset(QCPAxis::AxisType type); + +private: + Q_DISABLE_COPY(QCPAxisRect) + + friend class QCustomPlot; +}; + + +/* end of 'src/layoutelements/layoutelement-axisrect.h' */ + + +/* including file 'src/layoutelements/layoutelement-legend.h' */ +/* modified 2022-11-06T12:45:56, size 10425 */ + +class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPLegend* parentLegend READ parentLegend) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor) + Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectionChanged) + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectableChanged) + /// \endcond +public: + explicit QCPAbstractLegendItem(QCPLegend *parent); + + // getters: + QCPLegend *parentLegend() const { return mParentLegend; } + QFont font() const { return mFont; } + QColor textColor() const { return mTextColor; } + QFont selectedFont() const { return mSelectedFont; } + QColor selectedTextColor() const { return mSelectedTextColor; } + bool selectable() const { return mSelectable; } + bool selected() const { return mSelected; } + + // setters: + void setFont(const QFont &font); + void setTextColor(const QColor &color); + void setSelectedFont(const QFont &font); + void setSelectedTextColor(const QColor &color); + Q_SLOT void setSelectable(bool selectable); + Q_SLOT void setSelected(bool selected); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + +signals: + void selectionChanged(bool selected); + void selectableChanged(bool selectable); + +protected: + // property members: + QCPLegend *mParentLegend; + QFont mFont; + QColor mTextColor; + QFont mSelectedFont; + QColor mSelectedTextColor; + bool mSelectable, mSelected; + + // reimplemented virtual methods: + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual QRect clipRect() const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE = 0; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; + virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; + +private: + Q_DISABLE_COPY(QCPAbstractLegendItem) + + friend class QCPLegend; +}; + + +class QCP_LIB_DECL QCPPlottableLegendItem : public QCPAbstractLegendItem +{ + Q_OBJECT +public: + QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable); + + // getters: + QCPAbstractPlottable *plottable() { return mPlottable; } + +protected: + // property members: + QCPAbstractPlottable *mPlottable; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen getIconBorderPen() const; + QColor getTextColor() const; + QFont getFont() const; +}; + + +class QCP_LIB_DECL QCPLegend : public QCPLayoutGrid +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen borderPen READ borderPen WRITE setBorderPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize) + Q_PROPERTY(int iconTextPadding READ iconTextPadding WRITE setIconTextPadding) + Q_PROPERTY(QPen iconBorderPen READ iconBorderPen WRITE setIconBorderPen) + Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectionChanged) + Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectableChanged) + Q_PROPERTY(QPen selectedBorderPen READ selectedBorderPen WRITE setSelectedBorderPen) + Q_PROPERTY(QPen selectedIconBorderPen READ selectedIconBorderPen WRITE setSelectedIconBorderPen) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor) + /// \endcond +public: + /*! + Defines the selectable parts of a legend + + \see setSelectedParts, setSelectableParts + */ + enum SelectablePart { spNone = 0x000 ///< 0x000 None + ,spLegendBox = 0x001 ///< 0x001 The legend box (frame) + ,spItems = 0x002 ///< 0x002 Legend items individually (see \ref selectedItems) + }; + Q_ENUMS(SelectablePart) + Q_FLAGS(SelectableParts) + Q_DECLARE_FLAGS(SelectableParts, SelectablePart) + + explicit QCPLegend(); + virtual ~QCPLegend() Q_DECL_OVERRIDE; + + // getters: + QPen borderPen() const { return mBorderPen; } + QBrush brush() const { return mBrush; } + QFont font() const { return mFont; } + QColor textColor() const { return mTextColor; } + QSize iconSize() const { return mIconSize; } + int iconTextPadding() const { return mIconTextPadding; } + QPen iconBorderPen() const { return mIconBorderPen; } + SelectableParts selectableParts() const { return mSelectableParts; } + SelectableParts selectedParts() const; + QPen selectedBorderPen() const { return mSelectedBorderPen; } + QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; } + QBrush selectedBrush() const { return mSelectedBrush; } + QFont selectedFont() const { return mSelectedFont; } + QColor selectedTextColor() const { return mSelectedTextColor; } + + // setters: + void setBorderPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setFont(const QFont &font); + void setTextColor(const QColor &color); + void setIconSize(const QSize &size); + void setIconSize(int width, int height); + void setIconTextPadding(int padding); + void setIconBorderPen(const QPen &pen); + Q_SLOT void setSelectableParts(const SelectableParts &selectableParts); + Q_SLOT void setSelectedParts(const SelectableParts &selectedParts); + void setSelectedBorderPen(const QPen &pen); + void setSelectedIconBorderPen(const QPen &pen); + void setSelectedBrush(const QBrush &brush); + void setSelectedFont(const QFont &font); + void setSelectedTextColor(const QColor &color); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + // non-virtual methods: + QCPAbstractLegendItem *item(int index) const; + QCPPlottableLegendItem *itemWithPlottable(const QCPAbstractPlottable *plottable) const; + int itemCount() const; + bool hasItem(QCPAbstractLegendItem *item) const; + bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const; + bool addItem(QCPAbstractLegendItem *item); + bool removeItem(int index); + bool removeItem(QCPAbstractLegendItem *item); + void clearItems(); + QList selectedItems() const; + +signals: + void selectionChanged(QCPLegend::SelectableParts parts); + void selectableChanged(QCPLegend::SelectableParts parts); + +protected: + // property members: + QPen mBorderPen, mIconBorderPen; + QBrush mBrush; + QFont mFont; + QColor mTextColor; + QSize mIconSize; + int mIconTextPadding; + SelectableParts mSelectedParts, mSelectableParts; + QPen mSelectedBorderPen, mSelectedIconBorderPen; + QBrush mSelectedBrush; + QFont mSelectedFont; + QColor mSelectedTextColor; + + // reimplemented virtual methods: + virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE; + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; + virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen getBorderPen() const; + QBrush getBrush() const; + +private: + Q_DISABLE_COPY(QCPLegend) + + friend class QCustomPlot; + friend class QCPAbstractLegendItem; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPLegend::SelectableParts) +Q_DECLARE_METATYPE(QCPLegend::SelectablePart) + +/* end of 'src/layoutelements/layoutelement-legend.h' */ + + +/* including file 'src/layoutelements/layoutelement-textelement.h' */ +/* modified 2022-11-06T12:45:56, size 5359 */ + +class QCP_LIB_DECL QCPTextElement : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor) + Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged) + Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged) + /// \endcond +public: + explicit QCPTextElement(QCustomPlot *parentPlot); + QCPTextElement(QCustomPlot *parentPlot, const QString &text); + QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize); + QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize); + QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QFont &font); + + // getters: + QString text() const { return mText; } + int textFlags() const { return mTextFlags; } + QFont font() const { return mFont; } + QColor textColor() const { return mTextColor; } + QFont selectedFont() const { return mSelectedFont; } + QColor selectedTextColor() const { return mSelectedTextColor; } + bool selectable() const { return mSelectable; } + bool selected() const { return mSelected; } + + // setters: + void setText(const QString &text); + void setTextFlags(int flags); + void setFont(const QFont &font); + void setTextColor(const QColor &color); + void setSelectedFont(const QFont &font); + void setSelectedTextColor(const QColor &color); + Q_SLOT void setSelectable(bool selectable); + Q_SLOT void setSelected(bool selected); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + +signals: + void selectionChanged(bool selected); + void selectableChanged(bool selectable); + void clicked(QMouseEvent *event); + void doubleClicked(QMouseEvent *event); + +protected: + // property members: + QString mText; + int mTextFlags; + QFont mFont; + QColor mTextColor; + QFont mSelectedFont; + QColor mSelectedTextColor; + QRect mTextBoundingRect; + bool mSelectable, mSelected; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE; + virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; + virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; + + // non-virtual methods: + QFont mainFont() const; + QColor mainTextColor() const; + +private: + Q_DISABLE_COPY(QCPTextElement) +}; + + + +/* end of 'src/layoutelements/layoutelement-textelement.h' */ + + +/* including file 'src/layoutelements/layoutelement-colorscale.h' */ +/* modified 2022-11-06T12:45:56, size 5939 */ + + +class QCPColorScaleAxisRectPrivate : public QCPAxisRect +{ + Q_OBJECT +public: + explicit QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale); +protected: + QCPColorScale *mParentColorScale; + QImage mGradientImage; + bool mGradientImageInvalidated; + // re-using some methods of QCPAxisRect to make them available to friend class QCPColorScale + using QCPAxisRect::calculateAutoMargin; + using QCPAxisRect::mousePressEvent; + using QCPAxisRect::mouseMoveEvent; + using QCPAxisRect::mouseReleaseEvent; + using QCPAxisRect::wheelEvent; + using QCPAxisRect::update; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + void updateGradientImage(); + Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts); + Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts); + friend class QCPColorScale; +}; + + +class QCP_LIB_DECL QCPColorScale : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPAxis::AxisType type READ type WRITE setType) + Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged) + Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged) + Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged) + Q_PROPERTY(QString label READ label WRITE setLabel) + Q_PROPERTY(int barWidth READ barWidth WRITE setBarWidth) + Q_PROPERTY(bool rangeDrag READ rangeDrag WRITE setRangeDrag) + Q_PROPERTY(bool rangeZoom READ rangeZoom WRITE setRangeZoom) + /// \endcond +public: + explicit QCPColorScale(QCustomPlot *parentPlot); + virtual ~QCPColorScale() Q_DECL_OVERRIDE; + + // getters: + QCPAxis *axis() const { return mColorAxis.data(); } + QCPAxis::AxisType type() const { return mType; } + QCPRange dataRange() const { return mDataRange; } + QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; } + QCPColorGradient gradient() const { return mGradient; } + QString label() const; + int barWidth () const { return mBarWidth; } + bool rangeDrag() const; + bool rangeZoom() const; + + // setters: + void setType(QCPAxis::AxisType type); + Q_SLOT void setDataRange(const QCPRange &dataRange); + Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType); + Q_SLOT void setGradient(const QCPColorGradient &gradient); + void setLabel(const QString &str); + void setBarWidth(int width); + void setRangeDrag(bool enabled); + void setRangeZoom(bool enabled); + + // non-property methods: + QList colorMaps() const; + void rescaleDataRange(bool onlyVisibleMaps); + + // reimplemented virtual methods: + virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE; + +signals: + void dataRangeChanged(const QCPRange &newRange); + void dataScaleTypeChanged(QCPAxis::ScaleType scaleType); + void gradientChanged(const QCPColorGradient &newGradient); + +protected: + // property members: + QCPAxis::AxisType mType; + QCPRange mDataRange; + QCPAxis::ScaleType mDataScaleType; + QCPColorGradient mGradient; + int mBarWidth; + + // non-property members: + QPointer mAxisRect; + QPointer mColorAxis; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + // events: + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + +private: + Q_DISABLE_COPY(QCPColorScale) + + friend class QCPColorScaleAxisRectPrivate; +}; + + +/* end of 'src/layoutelements/layoutelement-colorscale.h' */ + + +/* including file 'src/plottables/plottable-graph.h' */ +/* modified 2022-11-06T12:45:56, size 9316 */ + +class QCP_LIB_DECL QCPGraphData +{ +public: + QCPGraphData(); + QCPGraphData(double key, double value); + + inline double sortKey() const { return key; } + inline static QCPGraphData fromSortKey(double sortKey) { return QCPGraphData(sortKey, 0); } + inline static bool sortKeyIsMainKey() { return true; } + + inline double mainKey() const { return key; } + inline double mainValue() const { return value; } + + inline QCPRange valueRange() const { return QCPRange(value, value); } + + double key, value; +}; +Q_DECLARE_TYPEINFO(QCPGraphData, Q_PRIMITIVE_TYPE); + + +/*! \typedef QCPGraphDataContainer + + Container for storing \ref QCPGraphData points. The data is stored sorted by \a key. + + This template instantiation is the container in which QCPGraph holds its data. For details about + the generic container, see the documentation of the class template \ref QCPDataContainer. + + \see QCPGraphData, QCPGraph::setData +*/ +typedef QCPDataContainer QCPGraphDataContainer; + +class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable1D +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle) + Q_PROPERTY(QCPScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle) + Q_PROPERTY(int scatterSkip READ scatterSkip WRITE setScatterSkip) + Q_PROPERTY(QCPGraph* channelFillGraph READ channelFillGraph WRITE setChannelFillGraph) + Q_PROPERTY(bool adaptiveSampling READ adaptiveSampling WRITE setAdaptiveSampling) + /// \endcond +public: + /*! + Defines how the graph's line is represented visually in the plot. The line is drawn with the + current pen of the graph (\ref setPen). + \see setLineStyle + */ + enum LineStyle { lsNone ///< data points are not connected with any lines (e.g. data only represented + ///< with symbols according to the scatter style, see \ref setScatterStyle) + ,lsLine ///< data points are connected by a straight line + ,lsStepLeft ///< line is drawn as steps where the step height is the value of the left data point + ,lsStepRight ///< line is drawn as steps where the step height is the value of the right data point + ,lsStepCenter ///< line is drawn as steps where the step is in between two data points + ,lsImpulse ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line + }; + Q_ENUMS(LineStyle) + + explicit QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPGraph() Q_DECL_OVERRIDE; + + // getters: + QSharedPointer data() const { return mDataContainer; } + LineStyle lineStyle() const { return mLineStyle; } + QCPScatterStyle scatterStyle() const { return mScatterStyle; } + int scatterSkip() const { return mScatterSkip; } + QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); } + bool adaptiveSampling() const { return mAdaptiveSampling; } + + // setters: + void setData(QSharedPointer data); + void setData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void setLineStyle(LineStyle ls); + void setScatterStyle(const QCPScatterStyle &style); + void setScatterSkip(int skip); + void setChannelFillGraph(QCPGraph *targetGraph); + void setAdaptiveSampling(bool enabled); + + // non-property methods: + void addData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void addData(double key, double value); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; + +protected: + // property members: + LineStyle mLineStyle; + QCPScatterStyle mScatterStyle; + int mScatterSkip; + QPointer mChannelFillGraph; + bool mAdaptiveSampling; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE; + + // introduced virtual methods: + virtual void drawFill(QCPPainter *painter, QVector *lines) const; + virtual void drawScatterPlot(QCPPainter *painter, const QVector &scatters, const QCPScatterStyle &style) const; + virtual void drawLinePlot(QCPPainter *painter, const QVector &lines) const; + virtual void drawImpulsePlot(QCPPainter *painter, const QVector &lines) const; + + virtual void getOptimizedLineData(QVector *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const; + virtual void getOptimizedScatterData(QVector *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const; + + // non-virtual methods: + void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const; + void getLines(QVector *lines, const QCPDataRange &dataRange) const; + void getScatters(QVector *scatters, const QCPDataRange &dataRange) const; + QVector dataToLines(const QVector &data) const; + QVector dataToStepLeftLines(const QVector &data) const; + QVector dataToStepRightLines(const QVector &data) const; + QVector dataToStepCenterLines(const QVector &data) const; + QVector dataToImpulseLines(const QVector &data) const; + QVector getNonNanSegments(const QVector *lineData, Qt::Orientation keyOrientation) const; + QVector > getOverlappingSegments(QVector thisSegments, const QVector *thisData, QVector otherSegments, const QVector *otherData) const; + bool segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const; + QPointF getFillBasePoint(QPointF matchingDataPoint) const; + const QPolygonF getFillPolygon(const QVector *lineData, QCPDataRange segment) const; + const QPolygonF getChannelFillPolygon(const QVector *thisData, QCPDataRange thisSegment, const QVector *otherData, QCPDataRange otherSegment) const; + int findIndexBelowX(const QVector *data, double x) const; + int findIndexAboveX(const QVector *data, double x) const; + int findIndexBelowY(const QVector *data, double y) const; + int findIndexAboveY(const QVector *data, double y) const; + double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; +Q_DECLARE_METATYPE(QCPGraph::LineStyle) + +/* end of 'src/plottables/plottable-graph.h' */ + + +/* including file 'src/plottables/plottable-curve.h' */ +/* modified 2022-11-06T12:45:56, size 7434 */ + +class QCP_LIB_DECL QCPCurveData +{ +public: + QCPCurveData(); + QCPCurveData(double t, double key, double value); + + inline double sortKey() const { return t; } + inline static QCPCurveData fromSortKey(double sortKey) { return QCPCurveData(sortKey, 0, 0); } + inline static bool sortKeyIsMainKey() { return false; } + + inline double mainKey() const { return key; } + inline double mainValue() const { return value; } + + inline QCPRange valueRange() const { return QCPRange(value, value); } + + double t, key, value; +}; +Q_DECLARE_TYPEINFO(QCPCurveData, Q_PRIMITIVE_TYPE); + + +/*! \typedef QCPCurveDataContainer + + Container for storing \ref QCPCurveData points. The data is stored sorted by \a t, so the \a + sortKey() (returning \a t) is different from \a mainKey() (returning \a key). + + This template instantiation is the container in which QCPCurve holds its data. For details about + the generic container, see the documentation of the class template \ref QCPDataContainer. + + \see QCPCurveData, QCPCurve::setData +*/ +typedef QCPDataContainer QCPCurveDataContainer; + +class QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable1D +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle) + Q_PROPERTY(int scatterSkip READ scatterSkip WRITE setScatterSkip) + Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle) + /// \endcond +public: + /*! + Defines how the curve's line is represented visually in the plot. The line is drawn with the + current pen of the curve (\ref setPen). + \see setLineStyle + */ + enum LineStyle { lsNone ///< No line is drawn between data points (e.g. only scatters) + ,lsLine ///< Data points are connected with a straight line + }; + Q_ENUMS(LineStyle) + + explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPCurve() Q_DECL_OVERRIDE; + + // getters: + QSharedPointer data() const { return mDataContainer; } + QCPScatterStyle scatterStyle() const { return mScatterStyle; } + int scatterSkip() const { return mScatterSkip; } + LineStyle lineStyle() const { return mLineStyle; } + + // setters: + void setData(QSharedPointer data); + void setData(const QVector &t, const QVector &keys, const QVector &values, bool alreadySorted=false); + void setData(const QVector &keys, const QVector &values); + void setScatterStyle(const QCPScatterStyle &style); + void setScatterSkip(int skip); + void setLineStyle(LineStyle style); + + // non-property methods: + void addData(const QVector &t, const QVector &keys, const QVector &values, bool alreadySorted=false); + void addData(const QVector &keys, const QVector &values); + void addData(double t, double key, double value); + void addData(double key, double value); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; + +protected: + // property members: + QCPScatterStyle mScatterStyle; + int mScatterSkip; + LineStyle mLineStyle; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE; + + // introduced virtual methods: + virtual void drawCurveLine(QCPPainter *painter, const QVector &lines) const; + virtual void drawScatterPlot(QCPPainter *painter, const QVector &points, const QCPScatterStyle &style) const; + + // non-virtual methods: + void getCurveLines(QVector *lines, const QCPDataRange &dataRange, double penWidth) const; + void getScatters(QVector *scatters, const QCPDataRange &dataRange, double scatterWidth) const; + int getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const; + QPointF getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const; + QVector getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const; + bool mayTraverse(int prevRegion, int currentRegion) const; + bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const; + void getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector &beforeTraverse, QVector &afterTraverse) const; + double pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; +Q_DECLARE_METATYPE(QCPCurve::LineStyle) + +/* end of 'src/plottables/plottable-curve.h' */ + + +/* including file 'src/plottables/plottable-bars.h' */ +/* modified 2022-11-06T12:45:56, size 8955 */ + +class QCP_LIB_DECL QCPBarsGroup : public QObject +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(SpacingType spacingType READ spacingType WRITE setSpacingType) + Q_PROPERTY(double spacing READ spacing WRITE setSpacing) + /// \endcond +public: + /*! + Defines the ways the spacing between bars in the group can be specified. Thus it defines what + the number passed to \ref setSpacing actually means. + + \see setSpacingType, setSpacing + */ + enum SpacingType { stAbsolute ///< Bar spacing is in absolute pixels + ,stAxisRectRatio ///< Bar spacing is given by a fraction of the axis rect size + ,stPlotCoords ///< Bar spacing is in key coordinates and thus scales with the key axis range + }; + Q_ENUMS(SpacingType) + + explicit QCPBarsGroup(QCustomPlot *parentPlot); + virtual ~QCPBarsGroup(); + + // getters: + SpacingType spacingType() const { return mSpacingType; } + double spacing() const { return mSpacing; } + + // setters: + void setSpacingType(SpacingType spacingType); + void setSpacing(double spacing); + + // non-virtual methods: + QList bars() const { return mBars; } + QCPBars* bars(int index) const; + int size() const { return static_cast(mBars.size()); } + bool isEmpty() const { return mBars.isEmpty(); } + void clear(); + bool contains(QCPBars *bars) const { return mBars.contains(bars); } + void append(QCPBars *bars); + void insert(int i, QCPBars *bars); + void remove(QCPBars *bars); + +protected: + // non-property members: + QCustomPlot *mParentPlot; + SpacingType mSpacingType; + double mSpacing; + QList mBars; + + // non-virtual methods: + void registerBars(QCPBars *bars); + void unregisterBars(QCPBars *bars); + + // virtual methods: + double keyPixelOffset(const QCPBars *bars, double keyCoord); + double getPixelSpacing(const QCPBars *bars, double keyCoord); + +private: + Q_DISABLE_COPY(QCPBarsGroup) + + friend class QCPBars; +}; +Q_DECLARE_METATYPE(QCPBarsGroup::SpacingType) + + +class QCP_LIB_DECL QCPBarsData +{ +public: + QCPBarsData(); + QCPBarsData(double key, double value); + + inline double sortKey() const { return key; } + inline static QCPBarsData fromSortKey(double sortKey) { return QCPBarsData(sortKey, 0); } + inline static bool sortKeyIsMainKey() { return true; } + + inline double mainKey() const { return key; } + inline double mainValue() const { return value; } + + inline QCPRange valueRange() const { return QCPRange(value, value); } // note that bar base value isn't held in each QCPBarsData and thus can't/shouldn't be returned here + + double key, value; +}; +Q_DECLARE_TYPEINFO(QCPBarsData, Q_PRIMITIVE_TYPE); + + +/*! \typedef QCPBarsDataContainer + + Container for storing \ref QCPBarsData points. The data is stored sorted by \a key. + + This template instantiation is the container in which QCPBars holds its data. For details about + the generic container, see the documentation of the class template \ref QCPDataContainer. + + \see QCPBarsData, QCPBars::setData +*/ +typedef QCPDataContainer QCPBarsDataContainer; + +class QCP_LIB_DECL QCPBars : public QCPAbstractPlottable1D +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(double width READ width WRITE setWidth) + Q_PROPERTY(WidthType widthType READ widthType WRITE setWidthType) + Q_PROPERTY(QCPBarsGroup* barsGroup READ barsGroup WRITE setBarsGroup) + Q_PROPERTY(double baseValue READ baseValue WRITE setBaseValue) + Q_PROPERTY(double stackingGap READ stackingGap WRITE setStackingGap) + Q_PROPERTY(QCPBars* barBelow READ barBelow) + Q_PROPERTY(QCPBars* barAbove READ barAbove) + /// \endcond +public: + /*! + Defines the ways the width of the bar can be specified. Thus it defines what the number passed + to \ref setWidth actually means. + + \see setWidthType, setWidth + */ + enum WidthType { wtAbsolute ///< Bar width is in absolute pixels + ,wtAxisRectRatio ///< Bar width is given by a fraction of the axis rect size + ,wtPlotCoords ///< Bar width is in key coordinates and thus scales with the key axis range + }; + Q_ENUMS(WidthType) + + explicit QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPBars() Q_DECL_OVERRIDE; + + // getters: + double width() const { return mWidth; } + WidthType widthType() const { return mWidthType; } + QCPBarsGroup *barsGroup() const { return mBarsGroup; } + double baseValue() const { return mBaseValue; } + double stackingGap() const { return mStackingGap; } + QCPBars *barBelow() const { return mBarBelow.data(); } + QCPBars *barAbove() const { return mBarAbove.data(); } + QSharedPointer data() const { return mDataContainer; } + + // setters: + void setData(QSharedPointer data); + void setData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void setWidth(double width); + void setWidthType(WidthType widthType); + void setBarsGroup(QCPBarsGroup *barsGroup); + void setBaseValue(double baseValue); + void setStackingGap(double pixels); + + // non-property methods: + void addData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void addData(double key, double value); + void moveBelow(QCPBars *bars); + void moveAbove(QCPBars *bars); + + // reimplemented virtual methods: + virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; + virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE; + +protected: + // property members: + double mWidth; + WidthType mWidthType; + QCPBarsGroup *mBarsGroup; + double mBaseValue; + double mStackingGap; + QPointer mBarBelow, mBarAbove; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE; + + // non-virtual methods: + void getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const; + QRectF getBarRect(double key, double value) const; + void getPixelWidth(double key, double &lower, double &upper) const; + double getStackedBaseValue(double key, bool positive) const; + static void connectBars(QCPBars* lower, QCPBars* upper); + + friend class QCustomPlot; + friend class QCPLegend; + friend class QCPBarsGroup; +}; +Q_DECLARE_METATYPE(QCPBars::WidthType) + +/* end of 'src/plottables/plottable-bars.h' */ + + +/* including file 'src/plottables/plottable-statisticalbox.h' */ +/* modified 2022-11-06T12:45:56, size 7522 */ + +class QCP_LIB_DECL QCPStatisticalBoxData +{ +public: + QCPStatisticalBoxData(); + QCPStatisticalBoxData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector& outliers=QVector()); + + inline double sortKey() const { return key; } + inline static QCPStatisticalBoxData fromSortKey(double sortKey) { return QCPStatisticalBoxData(sortKey, 0, 0, 0, 0, 0); } + inline static bool sortKeyIsMainKey() { return true; } + + inline double mainKey() const { return key; } + inline double mainValue() const { return median; } + + inline QCPRange valueRange() const + { + QCPRange result(minimum, maximum); + for (QVector::const_iterator it = outliers.constBegin(); it != outliers.constEnd(); ++it) + result.expand(*it); + return result; + } + + double key, minimum, lowerQuartile, median, upperQuartile, maximum; + QVector outliers; +}; +Q_DECLARE_TYPEINFO(QCPStatisticalBoxData, Q_MOVABLE_TYPE); + + +/*! \typedef QCPStatisticalBoxDataContainer + + Container for storing \ref QCPStatisticalBoxData points. The data is stored sorted by \a key. + + This template instantiation is the container in which QCPStatisticalBox holds its data. For + details about the generic container, see the documentation of the class template \ref + QCPDataContainer. + + \see QCPStatisticalBoxData, QCPStatisticalBox::setData +*/ +typedef QCPDataContainer QCPStatisticalBoxDataContainer; + +class QCP_LIB_DECL QCPStatisticalBox : public QCPAbstractPlottable1D +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(double width READ width WRITE setWidth) + Q_PROPERTY(double whiskerWidth READ whiskerWidth WRITE setWhiskerWidth) + Q_PROPERTY(QPen whiskerPen READ whiskerPen WRITE setWhiskerPen) + Q_PROPERTY(QPen whiskerBarPen READ whiskerBarPen WRITE setWhiskerBarPen) + Q_PROPERTY(bool whiskerAntialiased READ whiskerAntialiased WRITE setWhiskerAntialiased) + Q_PROPERTY(QPen medianPen READ medianPen WRITE setMedianPen) + Q_PROPERTY(QCPScatterStyle outlierStyle READ outlierStyle WRITE setOutlierStyle) + /// \endcond +public: + explicit QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis); + + // getters: + QSharedPointer data() const { return mDataContainer; } + double width() const { return mWidth; } + double whiskerWidth() const { return mWhiskerWidth; } + QPen whiskerPen() const { return mWhiskerPen; } + QPen whiskerBarPen() const { return mWhiskerBarPen; } + bool whiskerAntialiased() const { return mWhiskerAntialiased; } + QPen medianPen() const { return mMedianPen; } + QCPScatterStyle outlierStyle() const { return mOutlierStyle; } + + // setters: + void setData(QSharedPointer data); + void setData(const QVector &keys, const QVector &minimum, const QVector &lowerQuartile, const QVector &median, const QVector &upperQuartile, const QVector &maximum, bool alreadySorted=false); + void setWidth(double width); + void setWhiskerWidth(double width); + void setWhiskerPen(const QPen &pen); + void setWhiskerBarPen(const QPen &pen); + void setWhiskerAntialiased(bool enabled); + void setMedianPen(const QPen &pen); + void setOutlierStyle(const QCPScatterStyle &style); + + // non-property methods: + void addData(const QVector &keys, const QVector &minimum, const QVector &lowerQuartile, const QVector &median, const QVector &upperQuartile, const QVector &maximum, bool alreadySorted=false); + void addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector &outliers=QVector()); + + // reimplemented virtual methods: + virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; + +protected: + // property members: + double mWidth; + double mWhiskerWidth; + QPen mWhiskerPen, mWhiskerBarPen; + bool mWhiskerAntialiased; + QPen mMedianPen; + QCPScatterStyle mOutlierStyle; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE; + + // introduced virtual methods: + virtual void drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) const; + + // non-virtual methods: + void getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const; + QRectF getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const; + QVector getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const; + QVector getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; + +/* end of 'src/plottables/plottable-statisticalbox.h' */ + + +/* including file 'src/plottables/plottable-colormap.h' */ +/* modified 2022-11-06T12:45:56, size 7092 */ + +class QCP_LIB_DECL QCPColorMapData +{ +public: + QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange); + ~QCPColorMapData(); + QCPColorMapData(const QCPColorMapData &other); + QCPColorMapData &operator=(const QCPColorMapData &other); + + // getters: + int keySize() const { return mKeySize; } + int valueSize() const { return mValueSize; } + QCPRange keyRange() const { return mKeyRange; } + QCPRange valueRange() const { return mValueRange; } + QCPRange dataBounds() const { return mDataBounds; } + double data(double key, double value); + double cell(int keyIndex, int valueIndex); + unsigned char alpha(int keyIndex, int valueIndex); + + // setters: + void setSize(int keySize, int valueSize); + void setKeySize(int keySize); + void setValueSize(int valueSize); + void setRange(const QCPRange &keyRange, const QCPRange &valueRange); + void setKeyRange(const QCPRange &keyRange); + void setValueRange(const QCPRange &valueRange); + void setData(double key, double value, double z); + void setCell(int keyIndex, int valueIndex, double z); + void setAlpha(int keyIndex, int valueIndex, unsigned char alpha); + + // non-property methods: + void recalculateDataBounds(); + void clear(); + void clearAlpha(); + void fill(double z); + void fillAlpha(unsigned char alpha); + bool isEmpty() const { return mIsEmpty; } + void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const; + void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const; + +protected: + // property members: + int mKeySize, mValueSize; + QCPRange mKeyRange, mValueRange; + bool mIsEmpty; + + // non-property members: + double *mData; + unsigned char *mAlpha; + QCPRange mDataBounds; + bool mDataModified; + + bool createAlpha(bool initializeOpaque=true); + + friend class QCPColorMap; +}; + + +class QCP_LIB_DECL QCPColorMap : public QCPAbstractPlottable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged) + Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged) + Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged) + Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate) + Q_PROPERTY(bool tightBoundary READ tightBoundary WRITE setTightBoundary) + Q_PROPERTY(QCPColorScale* colorScale READ colorScale WRITE setColorScale) + /// \endcond +public: + explicit QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPColorMap() Q_DECL_OVERRIDE; + + // getters: + QCPColorMapData *data() const { return mMapData; } + QCPRange dataRange() const { return mDataRange; } + QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; } + bool interpolate() const { return mInterpolate; } + bool tightBoundary() const { return mTightBoundary; } + QCPColorGradient gradient() const { return mGradient; } + QCPColorScale *colorScale() const { return mColorScale.data(); } + + // setters: + void setData(QCPColorMapData *data, bool copy=false); + Q_SLOT void setDataRange(const QCPRange &dataRange); + Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType); + Q_SLOT void setGradient(const QCPColorGradient &gradient); + void setInterpolate(bool enabled); + void setTightBoundary(bool enabled); + void setColorScale(QCPColorScale *colorScale); + + // non-property methods: + void rescaleDataRange(bool recalculateDataBounds=false); + Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18)); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; + +signals: + void dataRangeChanged(const QCPRange &newRange); + void dataScaleTypeChanged(QCPAxis::ScaleType scaleType); + void gradientChanged(const QCPColorGradient &newGradient); + +protected: + // property members: + QCPRange mDataRange; + QCPAxis::ScaleType mDataScaleType; + QCPColorMapData *mMapData; + QCPColorGradient mGradient; + bool mInterpolate; + bool mTightBoundary; + QPointer mColorScale; + + // non-property members: + QImage mMapImage, mUndersampledMapImage; + QPixmap mLegendIcon; + bool mMapImageInvalidated; + + // introduced virtual methods: + virtual void updateMapImage(); + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE; + + friend class QCustomPlot; + friend class QCPLegend; +}; + +/* end of 'src/plottables/plottable-colormap.h' */ + + +/* including file 'src/plottables/plottable-financial.h' */ +/* modified 2022-11-06T12:45:56, size 8644 */ + +class QCP_LIB_DECL QCPFinancialData +{ +public: + QCPFinancialData(); + QCPFinancialData(double key, double open, double high, double low, double close); + + inline double sortKey() const { return key; } + inline static QCPFinancialData fromSortKey(double sortKey) { return QCPFinancialData(sortKey, 0, 0, 0, 0); } + inline static bool sortKeyIsMainKey() { return true; } + + inline double mainKey() const { return key; } + inline double mainValue() const { return open; } + + inline QCPRange valueRange() const { return QCPRange(low, high); } // open and close must lie between low and high, so we don't need to check them + + double key, open, high, low, close; +}; +Q_DECLARE_TYPEINFO(QCPFinancialData, Q_PRIMITIVE_TYPE); + + +/*! \typedef QCPFinancialDataContainer + + Container for storing \ref QCPFinancialData points. The data is stored sorted by \a key. + + This template instantiation is the container in which QCPFinancial holds its data. For details + about the generic container, see the documentation of the class template \ref QCPDataContainer. + + \see QCPFinancialData, QCPFinancial::setData +*/ +typedef QCPDataContainer QCPFinancialDataContainer; + +class QCP_LIB_DECL QCPFinancial : public QCPAbstractPlottable1D +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(ChartStyle chartStyle READ chartStyle WRITE setChartStyle) + Q_PROPERTY(double width READ width WRITE setWidth) + Q_PROPERTY(WidthType widthType READ widthType WRITE setWidthType) + Q_PROPERTY(bool twoColored READ twoColored WRITE setTwoColored) + Q_PROPERTY(QBrush brushPositive READ brushPositive WRITE setBrushPositive) + Q_PROPERTY(QBrush brushNegative READ brushNegative WRITE setBrushNegative) + Q_PROPERTY(QPen penPositive READ penPositive WRITE setPenPositive) + Q_PROPERTY(QPen penNegative READ penNegative WRITE setPenNegative) + /// \endcond +public: + /*! + Defines the ways the width of the financial bar can be specified. Thus it defines what the + number passed to \ref setWidth actually means. + + \see setWidthType, setWidth + */ + enum WidthType { wtAbsolute ///< width is in absolute pixels + ,wtAxisRectRatio ///< width is given by a fraction of the axis rect size + ,wtPlotCoords ///< width is in key coordinates and thus scales with the key axis range + }; + Q_ENUMS(WidthType) + + /*! + Defines the possible representations of OHLC data in the plot. + + \see setChartStyle + */ + enum ChartStyle { csOhlc ///< Open-High-Low-Close bar representation + ,csCandlestick ///< Candlestick representation + }; + Q_ENUMS(ChartStyle) + + explicit QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPFinancial() Q_DECL_OVERRIDE; + + // getters: + QSharedPointer data() const { return mDataContainer; } + ChartStyle chartStyle() const { return mChartStyle; } + double width() const { return mWidth; } + WidthType widthType() const { return mWidthType; } + bool twoColored() const { return mTwoColored; } + QBrush brushPositive() const { return mBrushPositive; } + QBrush brushNegative() const { return mBrushNegative; } + QPen penPositive() const { return mPenPositive; } + QPen penNegative() const { return mPenNegative; } + + // setters: + void setData(QSharedPointer data); + void setData(const QVector &keys, const QVector &open, const QVector &high, const QVector &low, const QVector &close, bool alreadySorted=false); + void setChartStyle(ChartStyle style); + void setWidth(double width); + void setWidthType(WidthType widthType); + void setTwoColored(bool twoColored); + void setBrushPositive(const QBrush &brush); + void setBrushNegative(const QBrush &brush); + void setPenPositive(const QPen &pen); + void setPenNegative(const QPen &pen); + + // non-property methods: + void addData(const QVector &keys, const QVector &open, const QVector &high, const QVector &low, const QVector &close, bool alreadySorted=false); + void addData(double key, double open, double high, double low, double close); + + // reimplemented virtual methods: + virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; + + // static methods: + static QCPFinancialDataContainer timeSeriesToOhlc(const QVector &time, const QVector &value, double timeBinSize, double timeBinOffset = 0); + +protected: + // property members: + ChartStyle mChartStyle; + double mWidth; + WidthType mWidthType; + bool mTwoColored; + QBrush mBrushPositive, mBrushNegative; + QPen mPenPositive, mPenNegative; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE; + + // non-virtual methods: + void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected); + void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected); + double getPixelWidth(double key, double keyPixel) const; + double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const; + double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const; + void getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const; + QRectF selectionHitBox(QCPFinancialDataContainer::const_iterator it) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; +Q_DECLARE_METATYPE(QCPFinancial::ChartStyle) + +/* end of 'src/plottables/plottable-financial.h' */ + + +/* including file 'src/plottables/plottable-errorbar.h' */ +/* modified 2022-11-06T12:45:56, size 7749 */ + +class QCP_LIB_DECL QCPErrorBarsData +{ +public: + QCPErrorBarsData(); + explicit QCPErrorBarsData(double error); + QCPErrorBarsData(double errorMinus, double errorPlus); + + double errorMinus, errorPlus; +}; +Q_DECLARE_TYPEINFO(QCPErrorBarsData, Q_PRIMITIVE_TYPE); + + +/*! \typedef QCPErrorBarsDataContainer + + Container for storing \ref QCPErrorBarsData points. It is a typedef for + QVector. + + This is the container in which \ref QCPErrorBars holds its data. Unlike most other data + containers for plottables, it is not based on \ref QCPDataContainer. This is because the error + bars plottable is special in that it doesn't store its own key and value coordinate per error + bar. It adopts the key and value from the plottable to which the error bars shall be applied + (\ref QCPErrorBars::setDataPlottable). So the stored \ref QCPErrorBarsData doesn't need a + sortable key, but merely an index (as \c QVector provides), which maps one-to-one to the indices + of the other plottable's data. + + \see QCPErrorBarsData, QCPErrorBars::setData +*/ +typedef QVector QCPErrorBarsDataContainer; + +class QCP_LIB_DECL QCPErrorBars : public QCPAbstractPlottable, public QCPPlottableInterface1D +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QSharedPointer data READ data WRITE setData) + Q_PROPERTY(QCPAbstractPlottable* dataPlottable READ dataPlottable WRITE setDataPlottable) + Q_PROPERTY(ErrorType errorType READ errorType WRITE setErrorType) + Q_PROPERTY(double whiskerWidth READ whiskerWidth WRITE setWhiskerWidth) + Q_PROPERTY(double symbolGap READ symbolGap WRITE setSymbolGap) + /// \endcond +public: + + /*! + Defines in which orientation the error bars shall appear. If your data needs both error + dimensions, create two \ref QCPErrorBars with different \ref ErrorType. + + \see setErrorType + */ + enum ErrorType { etKeyError ///< The errors are for the key dimension (bars appear parallel to the key axis) + ,etValueError ///< The errors are for the value dimension (bars appear parallel to the value axis) + }; + Q_ENUMS(ErrorType) + + explicit QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis); + virtual ~QCPErrorBars() Q_DECL_OVERRIDE; + // getters: + QSharedPointer data() const { return mDataContainer; } + QCPAbstractPlottable *dataPlottable() const { return mDataPlottable.data(); } + ErrorType errorType() const { return mErrorType; } + double whiskerWidth() const { return mWhiskerWidth; } + double symbolGap() const { return mSymbolGap; } + + // setters: + void setData(QSharedPointer data); + void setData(const QVector &error); + void setData(const QVector &errorMinus, const QVector &errorPlus); + void setDataPlottable(QCPAbstractPlottable* plottable); + void setErrorType(ErrorType type); + void setWhiskerWidth(double pixels); + void setSymbolGap(double pixels); + + // non-property methods: + void addData(const QVector &error); + void addData(const QVector &errorMinus, const QVector &errorPlus); + void addData(double error); + void addData(double errorMinus, double errorPlus); + + // virtual methods of 1d plottable interface: + virtual int dataCount() const Q_DECL_OVERRIDE; + virtual double dataMainKey(int index) const Q_DECL_OVERRIDE; + virtual double dataSortKey(int index) const Q_DECL_OVERRIDE; + virtual double dataMainValue(int index) const Q_DECL_OVERRIDE; + virtual QCPRange dataValueRange(int index) const Q_DECL_OVERRIDE; + virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE; + virtual bool sortKeyIsMainKey() const Q_DECL_OVERRIDE; + virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE; + virtual int findBegin(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; + virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE; + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; } + +protected: + // property members: + QSharedPointer mDataContainer; + QPointer mDataPlottable; + ErrorType mErrorType; + double mWhiskerWidth; + double mSymbolGap; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE; + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE; + + // non-virtual methods: + void getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector &backbones, QVector &whiskers) const; + void getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const; + double pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const; + // helpers: + void getDataSegments(QList &selectedSegments, QList &unselectedSegments) const; + bool errorBarVisible(int index) const; + bool rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const; + + friend class QCustomPlot; + friend class QCPLegend; +}; + +/* end of 'src/plottables/plottable-errorbar.h' */ + + +/* including file 'src/items/item-straightline.h' */ +/* modified 2022-11-06T12:45:56, size 3137 */ + +class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + /// \endcond +public: + explicit QCPItemStraightLine(QCustomPlot *parentPlot); + virtual ~QCPItemStraightLine() Q_DECL_OVERRIDE; + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + QCPItemPosition * const point1; + QCPItemPosition * const point2; + +protected: + // property members: + QPen mPen, mSelectedPen; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + + // non-virtual methods: + QLineF getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const; + QPen mainPen() const; +}; + +/* end of 'src/items/item-straightline.h' */ + + +/* including file 'src/items/item-line.h' */ +/* modified 2022-11-06T12:45:56, size 3429 */ + +class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QCPLineEnding head READ head WRITE setHead) + Q_PROPERTY(QCPLineEnding tail READ tail WRITE setTail) + /// \endcond +public: + explicit QCPItemLine(QCustomPlot *parentPlot); + virtual ~QCPItemLine() Q_DECL_OVERRIDE; + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QCPLineEnding head() const { return mHead; } + QCPLineEnding tail() const { return mTail; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setHead(const QCPLineEnding &head); + void setTail(const QCPLineEnding &tail); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + QCPItemPosition * const start; + QCPItemPosition * const end; + +protected: + // property members: + QPen mPen, mSelectedPen; + QCPLineEnding mHead, mTail; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + + // non-virtual methods: + QLineF getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const; + QPen mainPen() const; +}; + +/* end of 'src/items/item-line.h' */ + + +/* including file 'src/items/item-curve.h' */ +/* modified 2022-11-06T12:45:56, size 3401 */ + +class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QCPLineEnding head READ head WRITE setHead) + Q_PROPERTY(QCPLineEnding tail READ tail WRITE setTail) + /// \endcond +public: + explicit QCPItemCurve(QCustomPlot *parentPlot); + virtual ~QCPItemCurve() Q_DECL_OVERRIDE; + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QCPLineEnding head() const { return mHead; } + QCPLineEnding tail() const { return mTail; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setHead(const QCPLineEnding &head); + void setTail(const QCPLineEnding &tail); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + QCPItemPosition * const start; + QCPItemPosition * const startDir; + QCPItemPosition * const endDir; + QCPItemPosition * const end; + +protected: + // property members: + QPen mPen, mSelectedPen; + QCPLineEnding mHead, mTail; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen mainPen() const; +}; + +/* end of 'src/items/item-curve.h' */ + + +/* including file 'src/items/item-rect.h' */ +/* modified 2022-11-06T12:45:56, size 3710 */ + +class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + /// \endcond +public: + explicit QCPItemRect(QCustomPlot *parentPlot); + virtual ~QCPItemRect() Q_DECL_OVERRIDE; + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + QCPItemPosition * const topLeft; + QCPItemPosition * const bottomRight; + QCPItemAnchor * const top; + QCPItemAnchor * const topRight; + QCPItemAnchor * const right; + QCPItemAnchor * const bottom; + QCPItemAnchor * const bottomLeft; + QCPItemAnchor * const left; + +protected: + enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft}; + + // property members: + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen mainPen() const; + QBrush mainBrush() const; +}; + +/* end of 'src/items/item-rect.h' */ + + +/* including file 'src/items/item-text.h' */ +/* modified 2022-11-06T12:45:56, size 5576 */ + +class QCP_LIB_DECL QCPItemText : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor) + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont) + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(Qt::Alignment positionAlignment READ positionAlignment WRITE setPositionAlignment) + Q_PROPERTY(Qt::Alignment textAlignment READ textAlignment WRITE setTextAlignment) + Q_PROPERTY(double rotation READ rotation WRITE setRotation) + Q_PROPERTY(QMargins padding READ padding WRITE setPadding) + /// \endcond +public: + explicit QCPItemText(QCustomPlot *parentPlot); + virtual ~QCPItemText() Q_DECL_OVERRIDE; + + // getters: + QColor color() const { return mColor; } + QColor selectedColor() const { return mSelectedColor; } + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + QFont font() const { return mFont; } + QFont selectedFont() const { return mSelectedFont; } + QString text() const { return mText; } + Qt::Alignment positionAlignment() const { return mPositionAlignment; } + Qt::Alignment textAlignment() const { return mTextAlignment; } + double rotation() const { return mRotation; } + QMargins padding() const { return mPadding; } + + // setters; + void setColor(const QColor &color); + void setSelectedColor(const QColor &color); + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + void setFont(const QFont &font); + void setSelectedFont(const QFont &font); + void setText(const QString &text); + void setPositionAlignment(Qt::Alignment alignment); + void setTextAlignment(Qt::Alignment alignment); + void setRotation(double degrees); + void setPadding(const QMargins &padding); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + QCPItemPosition * const position; + QCPItemAnchor * const topLeft; + QCPItemAnchor * const top; + QCPItemAnchor * const topRight; + QCPItemAnchor * const right; + QCPItemAnchor * const bottomRight; + QCPItemAnchor * const bottom; + QCPItemAnchor * const bottomLeft; + QCPItemAnchor * const left; + +protected: + enum AnchorIndex {aiTopLeft, aiTop, aiTopRight, aiRight, aiBottomRight, aiBottom, aiBottomLeft, aiLeft}; + + // property members: + QColor mColor, mSelectedColor; + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + QFont mFont, mSelectedFont; + QString mText; + Qt::Alignment mPositionAlignment; + Qt::Alignment mTextAlignment; + double mRotation; + QMargins mPadding; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE; + + // non-virtual methods: + QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const; + QFont mainFont() const; + QColor mainColor() const; + QPen mainPen() const; + QBrush mainBrush() const; +}; + +/* end of 'src/items/item-text.h' */ + + +/* including file 'src/items/item-ellipse.h' */ +/* modified 2022-11-06T12:45:56, size 3890 */ + +class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + /// \endcond +public: + explicit QCPItemEllipse(QCustomPlot *parentPlot); + virtual ~QCPItemEllipse() Q_DECL_OVERRIDE; + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + QCPItemPosition * const topLeft; + QCPItemPosition * const bottomRight; + QCPItemAnchor * const topLeftRim; + QCPItemAnchor * const top; + QCPItemAnchor * const topRightRim; + QCPItemAnchor * const right; + QCPItemAnchor * const bottomRightRim; + QCPItemAnchor * const bottom; + QCPItemAnchor * const bottomLeftRim; + QCPItemAnchor * const left; + QCPItemAnchor * const center; + +protected: + enum AnchorIndex {aiTopLeftRim, aiTop, aiTopRightRim, aiRight, aiBottomRightRim, aiBottom, aiBottomLeftRim, aiLeft, aiCenter}; + + // property members: + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen mainPen() const; + QBrush mainBrush() const; +}; + +/* end of 'src/items/item-ellipse.h' */ + + +/* including file 'src/items/item-pixmap.h' */ +/* modified 2022-11-06T12:45:56, size 4407 */ + +class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) + Q_PROPERTY(bool scaled READ scaled WRITE setScaled) + Q_PROPERTY(Qt::AspectRatioMode aspectRatioMode READ aspectRatioMode) + Q_PROPERTY(Qt::TransformationMode transformationMode READ transformationMode) + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + /// \endcond +public: + explicit QCPItemPixmap(QCustomPlot *parentPlot); + virtual ~QCPItemPixmap() Q_DECL_OVERRIDE; + + // getters: + QPixmap pixmap() const { return mPixmap; } + bool scaled() const { return mScaled; } + Qt::AspectRatioMode aspectRatioMode() const { return mAspectRatioMode; } + Qt::TransformationMode transformationMode() const { return mTransformationMode; } + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + + // setters; + void setPixmap(const QPixmap &pixmap); + void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation); + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + QCPItemPosition * const topLeft; + QCPItemPosition * const bottomRight; + QCPItemAnchor * const top; + QCPItemAnchor * const topRight; + QCPItemAnchor * const right; + QCPItemAnchor * const bottom; + QCPItemAnchor * const bottomLeft; + QCPItemAnchor * const left; + +protected: + enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft}; + + // property members: + QPixmap mPixmap; + QPixmap mScaledPixmap; + bool mScaled; + bool mScaledPixmapInvalidated; + Qt::AspectRatioMode mAspectRatioMode; + Qt::TransformationMode mTransformationMode; + QPen mPen, mSelectedPen; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE; + + // non-virtual methods: + void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false); + QRect getFinalRect(bool *flippedHorz=nullptr, bool *flippedVert=nullptr) const; + QPen mainPen() const; +}; + +/* end of 'src/items/item-pixmap.h' */ + + +/* including file 'src/items/item-tracer.h' */ +/* modified 2022-11-06T12:45:56, size 4811 */ + +class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush) + Q_PROPERTY(double size READ size WRITE setSize) + Q_PROPERTY(TracerStyle style READ style WRITE setStyle) + Q_PROPERTY(QCPGraph* graph READ graph WRITE setGraph) + Q_PROPERTY(double graphKey READ graphKey WRITE setGraphKey) + Q_PROPERTY(bool interpolating READ interpolating WRITE setInterpolating) + /// \endcond +public: + /*! + The different visual appearances a tracer item can have. Some styles size may be controlled with \ref setSize. + + \see setStyle + */ + enum TracerStyle { tsNone ///< The tracer is not visible + ,tsPlus ///< A plus shaped crosshair with limited size + ,tsCrosshair ///< A plus shaped crosshair which spans the complete axis rect + ,tsCircle ///< A circle + ,tsSquare ///< A square + }; + Q_ENUMS(TracerStyle) + + explicit QCPItemTracer(QCustomPlot *parentPlot); + virtual ~QCPItemTracer() Q_DECL_OVERRIDE; + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + QBrush brush() const { return mBrush; } + QBrush selectedBrush() const { return mSelectedBrush; } + double size() const { return mSize; } + TracerStyle style() const { return mStyle; } + QCPGraph *graph() const { return mGraph; } + double graphKey() const { return mGraphKey; } + bool interpolating() const { return mInterpolating; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setSelectedBrush(const QBrush &brush); + void setSize(double size); + void setStyle(TracerStyle style); + void setGraph(QCPGraph *graph); + void setGraphKey(double key); + void setInterpolating(bool enabled); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + // non-virtual methods: + void updatePosition(); + + QCPItemPosition * const position; + +protected: + // property members: + QPen mPen, mSelectedPen; + QBrush mBrush, mSelectedBrush; + double mSize; + TracerStyle mStyle; + QCPGraph *mGraph; + double mGraphKey; + bool mInterpolating; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen mainPen() const; + QBrush mainBrush() const; +}; +Q_DECLARE_METATYPE(QCPItemTracer::TracerStyle) + +/* end of 'src/items/item-tracer.h' */ + + +/* including file 'src/items/item-bracket.h' */ +/* modified 2022-11-06T12:45:56, size 3991 */ + +class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QPen pen READ pen WRITE setPen) + Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen) + Q_PROPERTY(double length READ length WRITE setLength) + Q_PROPERTY(BracketStyle style READ style WRITE setStyle) + /// \endcond +public: + /*! + Defines the various visual shapes of the bracket item. The appearance can be further modified + by \ref setLength and \ref setPen. + + \see setStyle + */ + enum BracketStyle { bsSquare ///< A brace with angled edges + ,bsRound ///< A brace with round edges + ,bsCurly ///< A curly brace + ,bsCalligraphic ///< A curly brace with varying stroke width giving a calligraphic impression + }; + Q_ENUMS(BracketStyle) + + explicit QCPItemBracket(QCustomPlot *parentPlot); + virtual ~QCPItemBracket() Q_DECL_OVERRIDE; + + // getters: + QPen pen() const { return mPen; } + QPen selectedPen() const { return mSelectedPen; } + double length() const { return mLength; } + BracketStyle style() const { return mStyle; } + + // setters; + void setPen(const QPen &pen); + void setSelectedPen(const QPen &pen); + void setLength(double length); + void setStyle(BracketStyle style); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE; + + QCPItemPosition * const left; + QCPItemPosition * const right; + QCPItemAnchor * const center; + +protected: + // property members: + enum AnchorIndex {aiCenter}; + QPen mPen, mSelectedPen; + double mLength; + BracketStyle mStyle; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen mainPen() const; +}; +Q_DECLARE_METATYPE(QCPItemBracket::BracketStyle) + +/* end of 'src/items/item-bracket.h' */ + + +/* including file 'src/polar/radialaxis.h' */ +/* modified 2022-11-06T12:45:56, size 12227 */ + + +class QCP_LIB_DECL QCPPolarAxisRadial : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + + /// \endcond +public: + /*! + Defines the reference of the angle at which a radial axis is tilted (\ref setAngle). + */ + enum AngleReference { arAbsolute ///< The axis tilt is given in absolute degrees. The zero is to the right and positive angles are measured counter-clockwise. + ,arAngularAxis ///< The axis tilt is measured in the angular coordinate system given by the parent angular axis. + }; + Q_ENUMS(AngleReference) + /*! + Defines the scale of an axis. + \see setScaleType + */ + enum ScaleType { stLinear ///< Linear scaling + ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed axis coordinates (possibly also \ref setTicker to a \ref QCPAxisTickerLog instance). + }; + Q_ENUMS(ScaleType) + /*! + Defines the selectable parts of an axis. + \see setSelectableParts, setSelectedParts + */ + enum SelectablePart { spNone = 0 ///< None of the selectable parts + ,spAxis = 0x001 ///< The axis backbone and tick marks + ,spTickLabels = 0x002 ///< Tick labels (numbers) of this axis (as a whole, not individually) + ,spAxisLabel = 0x004 ///< The axis label + }; + Q_ENUMS(SelectablePart) + Q_FLAGS(SelectableParts) + Q_DECLARE_FLAGS(SelectableParts, SelectablePart) + + enum LabelMode { lmUpright ///< + ,lmRotated ///< + }; + Q_ENUMS(LabelMode) + + explicit QCPPolarAxisRadial(QCPPolarAxisAngular *parent); + virtual ~QCPPolarAxisRadial(); + + // getters: + bool rangeDrag() const { return mRangeDrag; } + bool rangeZoom() const { return mRangeZoom; } + double rangeZoomFactor() const { return mRangeZoomFactor; } + + QCPPolarAxisAngular *angularAxis() const { return mAngularAxis; } + ScaleType scaleType() const { return mScaleType; } + const QCPRange range() const { return mRange; } + bool rangeReversed() const { return mRangeReversed; } + double angle() const { return mAngle; } + AngleReference angleReference() const { return mAngleReference; } + QSharedPointer ticker() const { return mTicker; } + bool ticks() const { return mTicks; } + bool tickLabels() const { return mTickLabels; } + int tickLabelPadding() const { return mLabelPainter.padding(); } + QFont tickLabelFont() const { return mTickLabelFont; } + QColor tickLabelColor() const { return mTickLabelColor; } + double tickLabelRotation() const { return mLabelPainter.rotation(); } + LabelMode tickLabelMode() const; + QString numberFormat() const; + int numberPrecision() const { return mNumberPrecision; } + QVector tickVector() const { return mTickVector; } + QVector subTickVector() const { return mSubTickVector; } + QVector tickVectorLabels() const { return mTickVectorLabels; } + int tickLengthIn() const; + int tickLengthOut() const; + bool subTicks() const { return mSubTicks; } + int subTickLengthIn() const; + int subTickLengthOut() const; + QPen basePen() const { return mBasePen; } + QPen tickPen() const { return mTickPen; } + QPen subTickPen() const { return mSubTickPen; } + QFont labelFont() const { return mLabelFont; } + QColor labelColor() const { return mLabelColor; } + QString label() const { return mLabel; } + int labelPadding() const; + SelectableParts selectedParts() const { return mSelectedParts; } + SelectableParts selectableParts() const { return mSelectableParts; } + QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; } + QFont selectedLabelFont() const { return mSelectedLabelFont; } + QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; } + QColor selectedLabelColor() const { return mSelectedLabelColor; } + QPen selectedBasePen() const { return mSelectedBasePen; } + QPen selectedTickPen() const { return mSelectedTickPen; } + QPen selectedSubTickPen() const { return mSelectedSubTickPen; } + + // setters: + void setRangeDrag(bool enabled); + void setRangeZoom(bool enabled); + void setRangeZoomFactor(double factor); + + Q_SLOT void setScaleType(QCPPolarAxisRadial::ScaleType type); + Q_SLOT void setRange(const QCPRange &range); + void setRange(double lower, double upper); + void setRange(double position, double size, Qt::AlignmentFlag alignment); + void setRangeLower(double lower); + void setRangeUpper(double upper); + void setRangeReversed(bool reversed); + void setAngle(double degrees); + void setAngleReference(AngleReference reference); + void setTicker(QSharedPointer ticker); + void setTicks(bool show); + void setTickLabels(bool show); + void setTickLabelPadding(int padding); + void setTickLabelFont(const QFont &font); + void setTickLabelColor(const QColor &color); + void setTickLabelRotation(double degrees); + void setTickLabelMode(LabelMode mode); + void setNumberFormat(const QString &formatCode); + void setNumberPrecision(int precision); + void setTickLength(int inside, int outside=0); + void setTickLengthIn(int inside); + void setTickLengthOut(int outside); + void setSubTicks(bool show); + void setSubTickLength(int inside, int outside=0); + void setSubTickLengthIn(int inside); + void setSubTickLengthOut(int outside); + void setBasePen(const QPen &pen); + void setTickPen(const QPen &pen); + void setSubTickPen(const QPen &pen); + void setLabelFont(const QFont &font); + void setLabelColor(const QColor &color); + void setLabel(const QString &str); + void setLabelPadding(int padding); + void setSelectedTickLabelFont(const QFont &font); + void setSelectedLabelFont(const QFont &font); + void setSelectedTickLabelColor(const QColor &color); + void setSelectedLabelColor(const QColor &color); + void setSelectedBasePen(const QPen &pen); + void setSelectedTickPen(const QPen &pen); + void setSelectedSubTickPen(const QPen &pen); + Q_SLOT void setSelectableParts(const QCPPolarAxisRadial::SelectableParts &selectableParts); + Q_SLOT void setSelectedParts(const QCPPolarAxisRadial::SelectableParts &selectedParts); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + + // non-property methods: + void moveRange(double diff); + void scaleRange(double factor); + void scaleRange(double factor, double center); + void rescale(bool onlyVisiblePlottables=false); + void pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const; + QPointF coordToPixel(double angleCoord, double radiusCoord) const; + double coordToRadius(double coord) const; + double radiusToCoord(double radius) const; + SelectablePart getPartAt(const QPointF &pos) const; + +signals: + void rangeChanged(const QCPRange &newRange); + void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange); + void scaleTypeChanged(QCPPolarAxisRadial::ScaleType scaleType); + void selectionChanged(const QCPPolarAxisRadial::SelectableParts &parts); + void selectableChanged(const QCPPolarAxisRadial::SelectableParts &parts); + +protected: + // property members: + bool mRangeDrag; + bool mRangeZoom; + double mRangeZoomFactor; + + // axis base: + QCPPolarAxisAngular *mAngularAxis; + double mAngle; + AngleReference mAngleReference; + SelectableParts mSelectableParts, mSelectedParts; + QPen mBasePen, mSelectedBasePen; + // axis label: + int mLabelPadding; + QString mLabel; + QFont mLabelFont, mSelectedLabelFont; + QColor mLabelColor, mSelectedLabelColor; + // tick labels: + //int mTickLabelPadding; in label painter + bool mTickLabels; + //double mTickLabelRotation; in label painter + QFont mTickLabelFont, mSelectedTickLabelFont; + QColor mTickLabelColor, mSelectedTickLabelColor; + int mNumberPrecision; + QLatin1Char mNumberFormatChar; + bool mNumberBeautifulPowers; + bool mNumberMultiplyCross; + // ticks and subticks: + bool mTicks; + bool mSubTicks; + int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; + QPen mTickPen, mSelectedTickPen; + QPen mSubTickPen, mSelectedSubTickPen; + // scale and range: + QCPRange mRange; + bool mRangeReversed; + ScaleType mScaleType; + + // non-property members: + QPointF mCenter; + double mRadius; + QSharedPointer mTicker; + QVector mTickVector; + QVector mTickVectorLabels; + QVector mSubTickVector; + bool mDragging; + QCPRange mDragStartRange; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + QCPLabelPainterPrivate mLabelPainter; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; + virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; + // mouse events: + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + // non-virtual methods: + void updateGeometry(const QPointF ¢er, double radius); + void setupTickVectors(); + QPen getBasePen() const; + QPen getTickPen() const; + QPen getSubTickPen() const; + QFont getTickLabelFont() const; + QFont getLabelFont() const; + QColor getTickLabelColor() const; + QColor getLabelColor() const; + +private: + Q_DISABLE_COPY(QCPPolarAxisRadial) + + friend class QCustomPlot; + friend class QCPPolarAxisAngular; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarAxisRadial::SelectableParts) +Q_DECLARE_METATYPE(QCPPolarAxisRadial::AngleReference) +Q_DECLARE_METATYPE(QCPPolarAxisRadial::ScaleType) +Q_DECLARE_METATYPE(QCPPolarAxisRadial::SelectablePart) + + + +/* end of 'src/polar/radialaxis.h' */ + + +/* including file 'src/polar/layoutelement-angularaxis.h' */ +/* modified 2022-11-06T12:45:56, size 13461 */ + +class QCP_LIB_DECL QCPPolarAxisAngular : public QCPLayoutElement +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + + /// \endcond +public: + /*! + Defines the selectable parts of an axis. + \see setSelectableParts, setSelectedParts + */ + enum SelectablePart { spNone = 0 ///< None of the selectable parts + ,spAxis = 0x001 ///< The axis backbone and tick marks + ,spTickLabels = 0x002 ///< Tick labels (numbers) of this axis (as a whole, not individually) + ,spAxisLabel = 0x004 ///< The axis label + }; + Q_ENUMS(SelectablePart) + Q_FLAGS(SelectableParts) + Q_DECLARE_FLAGS(SelectableParts, SelectablePart) + + /*! + TODO + */ + enum LabelMode { lmUpright ///< + ,lmRotated ///< + }; + Q_ENUMS(LabelMode) + + explicit QCPPolarAxisAngular(QCustomPlot *parentPlot); + virtual ~QCPPolarAxisAngular(); + + // getters: + QPixmap background() const { return mBackgroundPixmap; } + QBrush backgroundBrush() const { return mBackgroundBrush; } + bool backgroundScaled() const { return mBackgroundScaled; } + Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; } + bool rangeDrag() const { return mRangeDrag; } + bool rangeZoom() const { return mRangeZoom; } + double rangeZoomFactor() const { return mRangeZoomFactor; } + + const QCPRange range() const { return mRange; } + bool rangeReversed() const { return mRangeReversed; } + double angle() const { return mAngle; } + QSharedPointer ticker() const { return mTicker; } + bool ticks() const { return mTicks; } + bool tickLabels() const { return mTickLabels; } + int tickLabelPadding() const { return mLabelPainter.padding(); } + QFont tickLabelFont() const { return mTickLabelFont; } + QColor tickLabelColor() const { return mTickLabelColor; } + double tickLabelRotation() const { return mLabelPainter.rotation(); } + LabelMode tickLabelMode() const; + QString numberFormat() const; + int numberPrecision() const { return mNumberPrecision; } + QVector tickVector() const { return mTickVector; } + QVector tickVectorLabels() const { return mTickVectorLabels; } + int tickLengthIn() const { return mTickLengthIn; } + int tickLengthOut() const { return mTickLengthOut; } + bool subTicks() const { return mSubTicks; } + int subTickLengthIn() const { return mSubTickLengthIn; } + int subTickLengthOut() const { return mSubTickLengthOut; } + QPen basePen() const { return mBasePen; } + QPen tickPen() const { return mTickPen; } + QPen subTickPen() const { return mSubTickPen; } + QFont labelFont() const { return mLabelFont; } + QColor labelColor() const { return mLabelColor; } + QString label() const { return mLabel; } + int labelPadding() const { return mLabelPadding; } + SelectableParts selectedParts() const { return mSelectedParts; } + SelectableParts selectableParts() const { return mSelectableParts; } + QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; } + QFont selectedLabelFont() const { return mSelectedLabelFont; } + QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; } + QColor selectedLabelColor() const { return mSelectedLabelColor; } + QPen selectedBasePen() const { return mSelectedBasePen; } + QPen selectedTickPen() const { return mSelectedTickPen; } + QPen selectedSubTickPen() const { return mSelectedSubTickPen; } + QCPPolarGrid *grid() const { return mGrid; } + + // setters: + void setBackground(const QPixmap &pm); + void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding); + void setBackground(const QBrush &brush); + void setBackgroundScaled(bool scaled); + void setBackgroundScaledMode(Qt::AspectRatioMode mode); + void setRangeDrag(bool enabled); + void setRangeZoom(bool enabled); + void setRangeZoomFactor(double factor); + + Q_SLOT void setRange(const QCPRange &range); + void setRange(double lower, double upper); + void setRange(double position, double size, Qt::AlignmentFlag alignment); + void setRangeLower(double lower); + void setRangeUpper(double upper); + void setRangeReversed(bool reversed); + void setAngle(double degrees); + void setTicker(QSharedPointer ticker); + void setTicks(bool show); + void setTickLabels(bool show); + void setTickLabelPadding(int padding); + void setTickLabelFont(const QFont &font); + void setTickLabelColor(const QColor &color); + void setTickLabelRotation(double degrees); + void setTickLabelMode(LabelMode mode); + void setNumberFormat(const QString &formatCode); + void setNumberPrecision(int precision); + void setTickLength(int inside, int outside=0); + void setTickLengthIn(int inside); + void setTickLengthOut(int outside); + void setSubTicks(bool show); + void setSubTickLength(int inside, int outside=0); + void setSubTickLengthIn(int inside); + void setSubTickLengthOut(int outside); + void setBasePen(const QPen &pen); + void setTickPen(const QPen &pen); + void setSubTickPen(const QPen &pen); + void setLabelFont(const QFont &font); + void setLabelColor(const QColor &color); + void setLabel(const QString &str); + void setLabelPadding(int padding); + void setLabelPosition(Qt::AlignmentFlag position); + void setSelectedTickLabelFont(const QFont &font); + void setSelectedLabelFont(const QFont &font); + void setSelectedTickLabelColor(const QColor &color); + void setSelectedLabelColor(const QColor &color); + void setSelectedBasePen(const QPen &pen); + void setSelectedTickPen(const QPen &pen); + void setSelectedSubTickPen(const QPen &pen); + Q_SLOT void setSelectableParts(const QCPPolarAxisAngular::SelectableParts &selectableParts); + Q_SLOT void setSelectedParts(const QCPPolarAxisAngular::SelectableParts &selectedParts); + + // reimplemented virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE; + virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE; + virtual QList elements(bool recursive) const Q_DECL_OVERRIDE; + + // non-property methods: + bool removeGraph(QCPPolarGraph *graph); + int radialAxisCount() const; + QCPPolarAxisRadial *radialAxis(int index=0) const; + QList radialAxes() const; + QCPPolarAxisRadial *addRadialAxis(QCPPolarAxisRadial *axis=0); + bool removeRadialAxis(QCPPolarAxisRadial *axis); + QCPLayoutInset *insetLayout() const { return mInsetLayout; } + QRegion exactClipRegion() const; + + void moveRange(double diff); + void scaleRange(double factor); + void scaleRange(double factor, double center); + void rescale(bool onlyVisiblePlottables=false); + double coordToAngleRad(double coord) const { return mAngleRad+(coord-mRange.lower)/mRange.size()*(mRangeReversed ? -2.0*M_PI : 2.0*M_PI); } // mention in doc that return doesn't wrap + double angleRadToCoord(double angleRad) const { return mRange.lower+(angleRad-mAngleRad)/(mRangeReversed ? -2.0*M_PI : 2.0*M_PI)*mRange.size(); } + void pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const; + QPointF coordToPixel(double angleCoord, double radiusCoord) const; + SelectablePart getPartAt(const QPointF &pos) const; + + // read-only interface imitating a QRect: + int left() const { return mRect.left(); } + int right() const { return mRect.right(); } + int top() const { return mRect.top(); } + int bottom() const { return mRect.bottom(); } + int width() const { return mRect.width(); } + int height() const { return mRect.height(); } + QSize size() const { return mRect.size(); } + QPoint topLeft() const { return mRect.topLeft(); } + QPoint topRight() const { return mRect.topRight(); } + QPoint bottomLeft() const { return mRect.bottomLeft(); } + QPoint bottomRight() const { return mRect.bottomRight(); } + QPointF center() const { return mCenter; } + double radius() const { return mRadius; } + +signals: + void rangeChanged(const QCPRange &newRange); + void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange); + void selectionChanged(const QCPPolarAxisAngular::SelectableParts &parts); + void selectableChanged(const QCPPolarAxisAngular::SelectableParts &parts); + +protected: + // property members: + QBrush mBackgroundBrush; + QPixmap mBackgroundPixmap; + QPixmap mScaledBackgroundPixmap; + bool mBackgroundScaled; + Qt::AspectRatioMode mBackgroundScaledMode; + QCPLayoutInset *mInsetLayout; + bool mRangeDrag; + bool mRangeZoom; + double mRangeZoomFactor; + + // axis base: + double mAngle, mAngleRad; + SelectableParts mSelectableParts, mSelectedParts; + QPen mBasePen, mSelectedBasePen; + // axis label: + int mLabelPadding; + QString mLabel; + QFont mLabelFont, mSelectedLabelFont; + QColor mLabelColor, mSelectedLabelColor; + // tick labels: + //int mTickLabelPadding; in label painter + bool mTickLabels; + //double mTickLabelRotation; in label painter + QFont mTickLabelFont, mSelectedTickLabelFont; + QColor mTickLabelColor, mSelectedTickLabelColor; + int mNumberPrecision; + QLatin1Char mNumberFormatChar; + bool mNumberBeautifulPowers; + bool mNumberMultiplyCross; + // ticks and subticks: + bool mTicks; + bool mSubTicks; + int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; + QPen mTickPen, mSelectedTickPen; + QPen mSubTickPen, mSelectedSubTickPen; + // scale and range: + QCPRange mRange; + bool mRangeReversed; + + // non-property members: + QPointF mCenter; + double mRadius; + QList mRadialAxes; + QCPPolarGrid *mGrid; + QList mGraphs; + QSharedPointer mTicker; + QVector mTickVector; + QVector mTickVectorLabels; + QVector mTickVectorCosSin; + QVector mSubTickVector; + QVector mSubTickVectorCosSin; + bool mDragging; + QCPRange mDragAngularStart; + QList mDragRadialStart; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + QCPLabelPainterPrivate mLabelPainter; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE; + // events: + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + // non-virtual methods: + bool registerPolarGraph(QCPPolarGraph *graph); + void drawBackground(QCPPainter *painter, const QPointF ¢er, double radius); + void setupTickVectors(); + QPen getBasePen() const; + QPen getTickPen() const; + QPen getSubTickPen() const; + QFont getTickLabelFont() const; + QFont getLabelFont() const; + QColor getTickLabelColor() const; + QColor getLabelColor() const; + +private: + Q_DISABLE_COPY(QCPPolarAxisAngular) + + friend class QCustomPlot; + friend class QCPPolarGrid; + friend class QCPPolarGraph; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarAxisAngular::SelectableParts) +Q_DECLARE_METATYPE(QCPPolarAxisAngular::SelectablePart) + +/* end of 'src/polar/layoutelement-angularaxis.h' */ + + +/* including file 'src/polar/polargrid.h' */ +/* modified 2022-11-06T12:45:56, size 4506 */ + +class QCP_LIB_DECL QCPPolarGrid :public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + + /// \endcond +public: + /*! + TODO + */ + enum GridType { gtAngular = 0x01 ///< + ,gtRadial = 0x02 ///< + ,gtAll = 0xFF ///< + ,gtNone = 0x00 ///< + }; + Q_ENUMS(GridType) + Q_FLAGS(GridTypes) + Q_DECLARE_FLAGS(GridTypes, GridType) + + explicit QCPPolarGrid(QCPPolarAxisAngular *parentAxis); + + // getters: + QCPPolarAxisRadial *radialAxis() const { return mRadialAxis.data(); } + GridTypes type() const { return mType; } + GridTypes subGridType() const { return mSubGridType; } + bool antialiasedSubGrid() const { return mAntialiasedSubGrid; } + bool antialiasedZeroLine() const { return mAntialiasedZeroLine; } + QPen angularPen() const { return mAngularPen; } + QPen angularSubGridPen() const { return mAngularSubGridPen; } + QPen radialPen() const { return mRadialPen; } + QPen radialSubGridPen() const { return mRadialSubGridPen; } + QPen radialZeroLinePen() const { return mRadialZeroLinePen; } + + // setters: + void setRadialAxis(QCPPolarAxisRadial *axis); + void setType(GridTypes type); + void setSubGridType(GridTypes type); + void setAntialiasedSubGrid(bool enabled); + void setAntialiasedZeroLine(bool enabled); + void setAngularPen(const QPen &pen); + void setAngularSubGridPen(const QPen &pen); + void setRadialPen(const QPen &pen); + void setRadialSubGridPen(const QPen &pen); + void setRadialZeroLinePen(const QPen &pen); + +protected: + // property members: + GridTypes mType; + GridTypes mSubGridType; + bool mAntialiasedSubGrid, mAntialiasedZeroLine; + QPen mAngularPen, mAngularSubGridPen; + QPen mRadialPen, mRadialSubGridPen, mRadialZeroLinePen; + + // non-property members: + QCPPolarAxisAngular *mParentAxis; + QPointer mRadialAxis; + + // reimplemented virtual methods: + virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE; + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + + // non-virtual methods: + void drawRadialGrid(QCPPainter *painter, const QPointF ¢er, const QVector &coords, const QPen &pen, const QPen &zeroPen=Qt::NoPen); + void drawAngularGrid(QCPPainter *painter, const QPointF ¢er, double radius, const QVector &ticksCosSin, const QPen &pen); + +private: + Q_DISABLE_COPY(QCPPolarGrid) + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarGrid::GridTypes) +Q_DECLARE_METATYPE(QCPPolarGrid::GridType) + + +/* end of 'src/polar/polargrid.h' */ + + +/* including file 'src/polar/polargraph.h' */ +/* modified 2022-11-06T12:45:56, size 9606 */ + + +class QCP_LIB_DECL QCPPolarLegendItem : public QCPAbstractLegendItem +{ + Q_OBJECT +public: + QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph); + + // getters: + QCPPolarGraph *polarGraph() { return mPolarGraph; } + +protected: + // property members: + QCPPolarGraph *mPolarGraph; + + // reimplemented virtual methods: + virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE; + virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE; + + // non-virtual methods: + QPen getIconBorderPen() const; + QColor getTextColor() const; + QFont getFont() const; +}; + + +class QCP_LIB_DECL QCPPolarGraph : public QCPLayerable +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + + /// \endcond +public: + /*! + Defines how the graph's line is represented visually in the plot. The line is drawn with the + current pen of the graph (\ref setPen). + \see setLineStyle + */ + enum LineStyle { lsNone ///< data points are not connected with any lines (e.g. data only represented + ///< with symbols according to the scatter style, see \ref setScatterStyle) + ,lsLine ///< data points are connected by a straight line + }; + Q_ENUMS(LineStyle) + + QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis); + virtual ~QCPPolarGraph(); + + // getters: + QString name() const { return mName; } + bool antialiasedFill() const { return mAntialiasedFill; } + bool antialiasedScatters() const { return mAntialiasedScatters; } + QPen pen() const { return mPen; } + QBrush brush() const { return mBrush; } + bool periodic() const { return mPeriodic; } + QCPPolarAxisAngular *keyAxis() const { return mKeyAxis.data(); } + QCPPolarAxisRadial *valueAxis() const { return mValueAxis.data(); } + QCP::SelectionType selectable() const { return mSelectable; } + bool selected() const { return !mSelection.isEmpty(); } + QCPDataSelection selection() const { return mSelection; } + //QCPSelectionDecorator *selectionDecorator() const { return mSelectionDecorator; } + QSharedPointer data() const { return mDataContainer; } + LineStyle lineStyle() const { return mLineStyle; } + QCPScatterStyle scatterStyle() const { return mScatterStyle; } + + // setters: + void setName(const QString &name); + void setAntialiasedFill(bool enabled); + void setAntialiasedScatters(bool enabled); + void setPen(const QPen &pen); + void setBrush(const QBrush &brush); + void setPeriodic(bool enabled); + void setKeyAxis(QCPPolarAxisAngular *axis); + void setValueAxis(QCPPolarAxisRadial *axis); + Q_SLOT void setSelectable(QCP::SelectionType selectable); + Q_SLOT void setSelection(QCPDataSelection selection); + //void setSelectionDecorator(QCPSelectionDecorator *decorator); + void setData(QSharedPointer data); + void setData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void setLineStyle(LineStyle ls); + void setScatterStyle(const QCPScatterStyle &style); + + // non-property methods: + void addData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void addData(double key, double value); + void coordsToPixels(double key, double value, double &x, double &y) const; + const QPointF coordsToPixels(double key, double value) const; + void pixelsToCoords(double x, double y, double &key, double &value) const; + void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const; + void rescaleAxes(bool onlyEnlarge=false) const; + void rescaleKeyAxis(bool onlyEnlarge=false) const; + void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const; + bool addToLegend(QCPLegend *legend); + bool addToLegend(); + bool removeFromLegend(QCPLegend *legend) const; + bool removeFromLegend() const; + + // introduced virtual methods: + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables + virtual QCPPlottableInterface1D *interface1D() { return 0; } // TODO: return this later, when QCPAbstractPolarPlottable is created + virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const; + virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const; + +signals: + void selectionChanged(bool selected); + void selectionChanged(const QCPDataSelection &selection); + void selectableChanged(QCP::SelectionType selectable); + +protected: + // property members: + QSharedPointer mDataContainer; + LineStyle mLineStyle; + QCPScatterStyle mScatterStyle; + QString mName; + bool mAntialiasedFill, mAntialiasedScatters; + QPen mPen; + QBrush mBrush; + bool mPeriodic; + QPointer mKeyAxis; + QPointer mValueAxis; + QCP::SelectionType mSelectable; + QCPDataSelection mSelection; + //QCPSelectionDecorator *mSelectionDecorator; + + // introduced virtual methods (later reimplemented TODO from QCPAbstractPolarPlottable): + virtual QRect clipRect() const; + virtual void draw(QCPPainter *painter); + virtual QCP::Interaction selectionCategory() const; + void applyDefaultAntialiasingHint(QCPPainter *painter) const; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); + // virtual drawing helpers: + virtual void drawLinePlot(QCPPainter *painter, const QVector &lines) const; + virtual void drawFill(QCPPainter *painter, QVector *lines) const; + virtual void drawScatterPlot(QCPPainter *painter, const QVector &scatters, const QCPScatterStyle &style) const; + + // introduced virtual methods: + virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const; + + // non-virtual methods: + void applyFillAntialiasingHint(QCPPainter *painter) const; + void applyScattersAntialiasingHint(QCPPainter *painter) const; + double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const; + // drawing helpers: + virtual int dataCount() const; + void getDataSegments(QList &selectedSegments, QList &unselectedSegments) const; + void drawPolyline(QCPPainter *painter, const QVector &lineData) const; + void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const; + void getLines(QVector *lines, const QCPDataRange &dataRange) const; + void getScatters(QVector *scatters, const QCPDataRange &dataRange) const; + void getOptimizedLineData(QVector *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const; + void getOptimizedScatterData(QVector *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const; + QVector dataToLines(const QVector &data) const; + +private: + Q_DISABLE_COPY(QCPPolarGraph) + + friend class QCPPolarLegendItem; +}; + +/* end of 'src/polar/polargraph.h' */ + + +#endif // QCUSTOMPLOT_H + diff --git a/ui/qt/widgets/range_syntax_lineedit.cpp b/ui/qt/widgets/range_syntax_lineedit.cpp new file mode 100644 index 00000000..84f1603e --- /dev/null +++ b/ui/qt/widgets/range_syntax_lineedit.cpp @@ -0,0 +1,43 @@ +/* range_syntax_lineedit.cpp + * Delegates for editing prefereneces. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +RangeSyntaxLineEdit::RangeSyntaxLineEdit(QWidget *parent) + : SyntaxLineEdit(parent), + maxRange_(0xFFFFFFFF) +{ + connect(this, &RangeSyntaxLineEdit::textChanged, this, &RangeSyntaxLineEdit::checkRange); +} + +void RangeSyntaxLineEdit::setMaxRange(unsigned int max) +{ + maxRange_ = max; +} + +void RangeSyntaxLineEdit::checkRange(QString range) +{ + if (range.isEmpty()) { + setSyntaxState(SyntaxLineEdit::Empty); + return; + } + + range_t *newrange; + convert_ret_t ret = range_convert_str(NULL, &newrange, range.toUtf8().constData(), maxRange_); + + if (ret == CVT_NO_ERROR) { + setSyntaxState(SyntaxLineEdit::Valid); + wmem_free(NULL, newrange); + } else { + setSyntaxState(SyntaxLineEdit::Invalid); + } +} diff --git a/ui/qt/widgets/range_syntax_lineedit.h b/ui/qt/widgets/range_syntax_lineedit.h new file mode 100644 index 00000000..6b5fe82b --- /dev/null +++ b/ui/qt/widgets/range_syntax_lineedit.h @@ -0,0 +1,33 @@ +/** @file + * + * Delegates for editing prefereneces. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RANGE_SYNTAX_LINEEDIT_H +#define RANGE_SYNTAX_LINEEDIT_H + +#include + +#include + +class RangeSyntaxLineEdit : public SyntaxLineEdit +{ + Q_OBJECT +public: + explicit RangeSyntaxLineEdit(QWidget *parent = 0); + void setMaxRange(unsigned int max); + +public slots: + void checkRange(QString range); + +private: + unsigned int maxRange_; +}; + +#endif // RANGE_SYNTAX_LINEEDIT_H diff --git a/ui/qt/widgets/rtp_audio_graph.cpp b/ui/qt/widgets/rtp_audio_graph.cpp new file mode 100644 index 00000000..1340658b --- /dev/null +++ b/ui/qt/widgets/rtp_audio_graph.cpp @@ -0,0 +1,88 @@ +/* rtp_audio_graph.c + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "rtp_audio_graph.h" + +#include + +#include +#include + +static const double wf_graph_normal_width_ = 0.5; + +RtpAudioGraph::RtpAudioGraph(QCustomPlot *audio_plot, QRgb color) : QObject(audio_plot) +{ + QPen p; + QPalette sel_pal; + + color_ = color; + wave_ = audio_plot->addGraph(); + p = QPen(wave_->pen()); + p.setColor(color_); + p.setWidthF(wf_graph_normal_width_); + wave_->setPen(p); + wave_->setSelectable(QCP::stNone); + wave_->removeFromLegend(); + selection_color_ = sel_pal.color(QPalette::Highlight); +} + +// Indicate that audio will not be hearable +void RtpAudioGraph::setMuted(bool isMuted) +{ + QPen p = wave_->pen(); + if (isMuted) { + p.setStyle(Qt::DotLine); + } else { + p.setStyle(Qt::SolidLine); + } + wave_->setPen(p); +} + +void RtpAudioGraph::setHighlight(bool isHighlighted) +{ + wave_->setSelection(isHighlighted ? QCPDataSelection(QCPDataRange()) : QCPDataSelection()); + QPen p = wave_->pen(); + if (isHighlighted) { + p.setWidthF(wf_graph_normal_width_*2); + } else { + p.setWidthF(wf_graph_normal_width_); + } + wave_->setPen(p); +} + +void RtpAudioGraph::setSelected(bool isSelected) +{ + wave_->setSelection(isSelected ? QCPDataSelection(QCPDataRange()) : QCPDataSelection()); + QPen p = wave_->pen(); + if (isSelected) { + p.setColor(selection_color_); + } else { + p.setColor(color_); + } + wave_->setPen(p); +} + +void RtpAudioGraph::setData(const QVector &keys, const QVector &values, bool alreadySorted) +{ + wave_->setData(keys, values, alreadySorted); +} + +void RtpAudioGraph::remove(QCustomPlot *audioPlot) +{ + audioPlot->removeGraph(wave_); +} + +bool RtpAudioGraph::isMyPlottable(QCPAbstractPlottable *plottable) +{ + if (plottable == wave_) { + return true; + } else { + return false; + } +} diff --git a/ui/qt/widgets/rtp_audio_graph.h b/ui/qt/widgets/rtp_audio_graph.h new file mode 100644 index 00000000..1951960e --- /dev/null +++ b/ui/qt/widgets/rtp_audio_graph.h @@ -0,0 +1,40 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef RTP_AUDIO_GRAPH_H +#define RTP_AUDIO_GRAPH_H + +#include "config.h" + +#include + +//class QCPItemStraightLine; +//class QCPAxisTicker; +//class QCPAxisTickerDateTime; + +class RtpAudioGraph : public QObject +{ + Q_OBJECT +public: + explicit RtpAudioGraph(QCustomPlot *audioPlot, QRgb color); + void setMuted(bool isMuted); + void setHighlight(bool isHighlighted); + void setSelected(bool isSelected); + void setData(const QVector &keys, const QVector &values, bool alreadySorted=false); + void remove(QCustomPlot *audioPlot); + bool isMyPlottable(QCPAbstractPlottable *plottable); + + +private: + QCPGraph *wave_; + QRgb color_; + QColor selection_color_; +}; + +#endif // RTP_AUDIO_GRAPH_H diff --git a/ui/qt/widgets/splash_overlay.cpp b/ui/qt/widgets/splash_overlay.cpp new file mode 100644 index 00000000..1b9e1f41 --- /dev/null +++ b/ui/qt/widgets/splash_overlay.cpp @@ -0,0 +1,179 @@ +/* splash_overlay.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "splash_overlay.h" +#include +#include "main_application.h" + +#include + +#include "ui/util.h" +#include +#include + +#ifdef HAVE_LUA +#include "epan/wslua/init_wslua.h" +#endif + +#include "extcap.h" + +// Uncomment to slow the update progress +//#define THROTTLE_STARTUP 1 + +/* + * Update frequency for the splash screen, given in milliseconds. + */ +const int info_update_freq_ = 65; // ~15 fps + +void splash_update(register_action_e action, const char *message, void *) { + emit mainApp->registerUpdate(action, message); +} + +SplashOverlay::SplashOverlay(QWidget *parent) : + QWidget(parent), + so_ui_(new Ui::SplashOverlay), + last_action_(RA_NONE), + register_cur_(0) +{ + so_ui_->setupUi(this); + + int register_max = RA_BASE_COUNT; +#ifdef HAVE_LUA + register_max++; +#endif + register_max++; + + so_ui_->progressBar->setMaximum(register_max); + elapsed_timer_.start(); + + QColor bg = QColor(tango_aluminium_6); + bg.setAlphaF(0.2f); + QPalette pal; + pal.setColor(QPalette::Window, bg); + setPalette(pal); + setAutoFillBackground(true); + + setStyleSheet(QString( + "QFrame#progressBand {" + " background: %1;" + "}" + "QLabel {" + " color: white;" + " background: transparent;" + "}" + "QProgressBar {" + " height: 1em;" + " width: 20em;" + " border: 0.1em solid white;" + " border-radius: 0.2em;" + " color: white;" + " background: transparent;" + "}" + "QProgressBar::chunk {" + " width: 0.1em;" + " background: rgba(255, 255, 255, 50%);" + "}" + ) + .arg(QColor(tango_aluminium_4).name())); + + connect(mainApp, &MainApplication::splashUpdate, this, &SplashOverlay::splashUpdate); +} + +SplashOverlay::~SplashOverlay() +{ + delete so_ui_; +} + +// Useful for debugging on fast machines. +#ifdef THROTTLE_STARTUP +#include +class ThrottleThread : public QThread +{ +public: + static void msleep(unsigned long msecs) + { + QThread::msleep(msecs); + } +}; +#endif + +void SplashOverlay::splashUpdate(register_action_e action, const char *message) +{ + QString action_msg = UTF8_HORIZONTAL_ELLIPSIS; + +#ifdef THROTTLE_STARTUP + ThrottleThread::msleep(10); +#endif + + if (last_action_ == action && (elapsed_timer_.elapsed() < info_update_freq_)) { + // Nothing to update yet + return; + } + + if (last_action_ != action) { + register_cur_++; + } + last_action_ = action; + + switch(action) { + case RA_DISSECTORS: + action_msg = tr("Initializing dissectors"); + break; + case RA_LISTENERS: + action_msg = tr("Initializing tap listeners"); + break; + case RA_EXTCAP: + action_msg = tr("Initializing external capture plugins"); + break; + case RA_REGISTER: + action_msg = tr("Registering dissectors"); + break; + case RA_PLUGIN_REGISTER: + action_msg = tr("Registering plugins"); + break; + case RA_HANDOFF: + action_msg = tr("Handing off dissectors"); + break; + case RA_PLUGIN_HANDOFF: + action_msg = tr("Handing off plugins"); + break; + case RA_LUA_PLUGINS: + action_msg = tr("Loading Lua plugins"); + break; + case RA_LUA_DEREGISTER: + action_msg = tr("Removing Lua plugins"); + break; + case RA_PREFERENCES: + action_msg = tr("Loading module preferences"); + break; + case RA_INTERFACES: + action_msg = tr("Finding local interfaces"); + break; + case RA_PREFERENCES_APPLY: + action_msg = tr("Applying changed preferences"); + break; + default: + action_msg = tr("(Unknown action)"); + break; + } + + if (message) { + if (!strncmp(message, "proto_register_", 15)) + message += 15; + else if (!strncmp(message, "proto_reg_handoff_", 18)) + message += 18; + action_msg.append(" ").append(message); + } + so_ui_->actionLabel->setText(action_msg); + + so_ui_->progressBar->setValue(register_cur_); + + mainApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers, 1); + elapsed_timer_.restart(); +} diff --git a/ui/qt/widgets/splash_overlay.h b/ui/qt/widgets/splash_overlay.h new file mode 100644 index 00000000..b15d98c4 --- /dev/null +++ b/ui/qt/widgets/splash_overlay.h @@ -0,0 +1,46 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SPLASH_OVERLAY_H +#define SPLASH_OVERLAY_H + +#include + +#include + +#include "epan/register.h" + +#include +#include + +void splash_update(register_action_e action, const char *message, void *dummy); + +namespace Ui { +class SplashOverlay; +} + +class SplashOverlay : public QWidget +{ + Q_OBJECT + +public: + explicit SplashOverlay(QWidget *parent = 0); + ~SplashOverlay(); + +private: + Ui::SplashOverlay *so_ui_; + register_action_e last_action_; + int register_cur_; + QElapsedTimer elapsed_timer_; + +private slots: + void splashUpdate(register_action_e action, const char *message); +}; + +#endif // SPLASH_OVERLAY_H diff --git a/ui/qt/widgets/splash_overlay.ui b/ui/qt/widgets/splash_overlay.ui new file mode 100644 index 00000000..657daefd --- /dev/null +++ b/ui/qt/widgets/splash_overlay.ui @@ -0,0 +1,115 @@ + + + SplashOverlay + + + true + + + + 0 + 0 + 400 + 300 + + + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 53 + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + Qt::Horizontal + + + + 116 + 50 + + + + + + + + + + + + + + + + + 24 + + + false + + + + + + + + + Qt::Horizontal + + + + 116 + 50 + + + + + + + + + + + Qt::Vertical + + + + 20 + 108 + + + + + + + + + diff --git a/ui/qt/widgets/stock_icon_tool_button.cpp b/ui/qt/widgets/stock_icon_tool_button.cpp new file mode 100644 index 00000000..06aabae2 --- /dev/null +++ b/ui/qt/widgets/stock_icon_tool_button.cpp @@ -0,0 +1,87 @@ +/* stock_icon_tool_button.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include + +#include +#include +#include +#include + +// We want nice icons that render correctly, and that are responsive +// when the user hovers and clicks them. +// Using setIcon renders correctly on normal and retina displays. It is +// not completely responsive, particularly on macOS. +// Calling setStyleSheet is responsive, but does not render correctly on +// retina displays: https://bugreports.qt.io/browse/QTBUG-36825 +// Subclass QToolButton, which lets us catch events and set icons as needed. + +StockIconToolButton::StockIconToolButton(QWidget * parent, QString stock_icon_name) : + QToolButton(parent) +{ + setCursor(Qt::ArrowCursor); + setStockIcon(stock_icon_name); +} + +void StockIconToolButton::setIconMode(QIcon::Mode mode) +{ + QIcon mode_icon; + QList states = QList() << QIcon::Off << QIcon::On; + foreach (QIcon::State state, states) { + foreach (QSize size, base_icon_.availableSizes(mode, state)) { + mode_icon.addPixmap(base_icon_.pixmap(size, mode, state), mode, state); + } + } + setIcon(mode_icon); +} + +void StockIconToolButton::setStockIcon(QString icon_name) +{ + if (!icon_name.isEmpty()) { + icon_name_ = icon_name; + } + if (icon_name_.isEmpty()) { + return; + } + base_icon_ = StockIcon(icon_name_); + setIconMode(); +} + +bool StockIconToolButton::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::Enter: + if (isEnabled()) { + setIconMode(QIcon::Active); + } + break; + case QEvent::Leave: + if (isEnabled()) { + setIconMode(); + } + break; + case QEvent::MouseButtonPress: + if (isEnabled()) { + setIconMode(QIcon::Selected); + } + break; + case QEvent::MouseButtonRelease: + setIconMode(); + break; + case QEvent::ApplicationPaletteChange: + setStockIcon(); + break; + default: + break; + } + + return QToolButton::event(event); +} diff --git a/ui/qt/widgets/stock_icon_tool_button.h b/ui/qt/widgets/stock_icon_tool_button.h new file mode 100644 index 00000000..26db61f4 --- /dev/null +++ b/ui/qt/widgets/stock_icon_tool_button.h @@ -0,0 +1,31 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef STOCKICONTOOLBUTTON_H +#define STOCKICONTOOLBUTTON_H + +#include + +class StockIconToolButton : public QToolButton +{ +public: + explicit StockIconToolButton(QWidget * parent = 0, QString stock_icon_name = QString()); + + void setIconMode(QIcon::Mode mode = QIcon::Normal); + void setStockIcon(QString icon_name = QString()); + +protected: + virtual bool event(QEvent *event); + +private: + QIcon base_icon_; + QString icon_name_; +}; + +#endif // STOCKICONTOOLBUTTON_H diff --git a/ui/qt/widgets/syntax_line_edit.cpp b/ui/qt/widgets/syntax_line_edit.cpp new file mode 100644 index 00000000..99ccf7bc --- /dev/null +++ b/ui/qt/widgets/syntax_line_edit.cpp @@ -0,0 +1,555 @@ +/* syntax_line_edit.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const int max_completion_items_ = 20; + +SyntaxLineEdit::SyntaxLineEdit(QWidget *parent) : + QLineEdit(parent), + completer_(NULL), + completion_model_(NULL), + completion_enabled_(false) +{ + setSyntaxState(); + setMaxLength(std::numeric_limits::max()); +} + +// Override setCompleter so that we don't clobber the filter text on activate. +void SyntaxLineEdit::setCompleter(QCompleter *c) +{ + if (completer_) + QObject::disconnect(completer_, 0, this, 0); + + completer_ = c; + + if (!completer_) + return; + + completer_->setWidget(this); + completer_->setCompletionMode(QCompleter::PopupCompletion); + completer_->setCaseSensitivity(Qt::CaseInsensitive); + // Completion items are not guaranteed to be sorted (recent filters + + // fields), so no setModelSorting. + completer_->setMaxVisibleItems(max_completion_items_); + QObject::connect(completer_, static_cast(&QCompleter::activated), + this, &SyntaxLineEdit::insertFieldCompletion); + + // Auto-completion is turned on. + completion_enabled_ = true; +} + +void SyntaxLineEdit::allowCompletion(bool enabled) +{ + completion_enabled_ = enabled; +} + +void SyntaxLineEdit::setSyntaxState(SyntaxState state) { + syntax_state_ = state; + + // XXX Should we drop the background colors here in favor of ::paintEvent below? + QColor valid_bg = ColorUtils::fromColorT(&prefs.gui_text_valid); + QColor valid_fg = ColorUtils::contrastingTextColor(valid_bg); + QColor invalid_bg = ColorUtils::fromColorT(&prefs.gui_text_invalid); + QColor invalid_fg = ColorUtils::contrastingTextColor(invalid_bg); + QColor deprecated_bg = ColorUtils::fromColorT(&prefs.gui_text_deprecated); + QColor deprecated_fg = ColorUtils::contrastingTextColor(deprecated_bg); + + // Try to matche QLineEdit's placeholder text color (which sets the + // alpha channel to 50%, which doesn't work in style sheets). + // Setting the foreground color lets us avoid yet another background + // color preference and should hopefully make things easier to + // distinguish for color blind folk. + QColor busy_fg = ColorUtils::alphaBlend(QApplication::palette().text(), QApplication::palette().base(), 0.5); + + state_style_sheet_ = QString( + "SyntaxLineEdit[syntaxState=\"%1\"] {" + " color: %2;" + " background-color: %3;" + "}" + + "SyntaxLineEdit[syntaxState=\"%4\"] {" + " color: %5;" + " background-color: %6;" + "}" + + "SyntaxLineEdit[syntaxState=\"%7\"] {" + " color: %8;" + " background-color: %9;" + "}" + + "SyntaxLineEdit[syntaxState=\"%10\"] {" + " color: %11;" + " background-color: %12;" + "}" + ) + + // CSS selector, foreground, background + .arg(Valid) + .arg(valid_fg.name()) + .arg(valid_bg.name()) + + .arg(Invalid) + .arg(invalid_fg.name()) + .arg(invalid_bg.name()) + + .arg(Deprecated) + .arg(deprecated_fg.name()) + .arg(deprecated_bg.name()) + + .arg(Busy) + .arg(busy_fg.name()) + .arg(palette().base().color().name()) + ; + setStyleSheet(style_sheet_); +} + +QString SyntaxLineEdit::syntaxErrorMessage() +{ + return syntax_error_message_; +} + +QString SyntaxLineEdit::syntaxErrorMessageFull() +{ + return syntax_error_message_full_; +} + +QString SyntaxLineEdit::createSyntaxErrorMessageFull( + const QString &filter, const QString &err_msg, + qsizetype loc_start, size_t loc_length) +{ + QString msg = tr("Invalid filter: %1").arg(err_msg); + + if (loc_start >= 0 && loc_length >= 1) { + // Add underlined location + msg = QString("

%1

  %2\n  %3^%4

") + .arg(msg) + .arg(filter) + .arg(QString(' ').repeated(static_cast(loc_start))) + .arg(QString('~').repeated(static_cast(loc_length) - 1)); + } + return msg; +} + +QString SyntaxLineEdit::styleSheet() const { + return style_sheet_; +} + +void SyntaxLineEdit::setStyleSheet(const QString &style_sheet) { + style_sheet_ = style_sheet; + QLineEdit::setStyleSheet(style_sheet_ + state_style_sheet_); +} + +void SyntaxLineEdit::insertFilter(const QString &filter) +{ + QString padded_filter = filter; + + if (hasSelectedText()) { + backspace(); + } + + int pos = cursorPosition(); + if (pos > 0 && !text().at(pos - 1).isSpace()) { + padded_filter.prepend(" "); + } + if (pos < text().length() - 1 && !text().at(pos + 1).isSpace()) { + padded_filter.append(" "); + } + insert(padded_filter); +} + +bool SyntaxLineEdit::checkDisplayFilter(QString filter) +{ + if (!completion_enabled_) { + return false; + } + + if (filter.isEmpty()) { + setSyntaxState(SyntaxLineEdit::Empty); + return true; + } + + dfilter_t *dfp = NULL; + df_error_t *df_err = NULL; + if (dfilter_compile(filter.toUtf8().constData(), &dfp, &df_err)) { + GSList *warn; + GPtrArray *depr = NULL; + if (dfp != NULL && (warn = dfilter_get_warnings(dfp)) != NULL) { + // FIXME Need to use a different state or rename ::Deprecated + setSyntaxState(SyntaxLineEdit::Deprecated); + /* + * We're being lazy and only printing the first warning. + * Would it be better to print all of them? + */ + syntax_error_message_ = QString(static_cast(warn->data)); + } else if (dfp != NULL && (depr = dfilter_deprecated_tokens(dfp)) != NULL) { + // You keep using that word. I do not think it means what you think it means. + // Possible alternatives: ::Troubled, or ::Problematic maybe? + setSyntaxState(SyntaxLineEdit::Deprecated); + /* + * We're being lazy and only printing the first "problem" token. + * Would it be better to print all of them? + */ + QString token((const char *)g_ptr_array_index(depr, 0)); + gchar *token_str = qstring_strdup(token.section('.', 0, 0)); + header_field_info *hfi = proto_registrar_get_byalias(token_str); + if (hfi) + syntax_error_message_ = tr("\"%1\" is deprecated in favour of \"%2\". " + "See Help section 6.4.8 for details.").arg(token_str).arg(hfi->abbrev); + else + // The token_str is the message. + syntax_error_message_ = tr("%1").arg(token_str); + g_free(token_str); + } else { + setSyntaxState(SyntaxLineEdit::Valid); + } + } else { + setSyntaxState(SyntaxLineEdit::Invalid); + syntax_error_message_ = QString::fromUtf8(df_err->msg); + syntax_error_message_full_ = createSyntaxErrorMessageFull(filter, syntax_error_message_, df_err->loc.col_start, df_err->loc.col_len); + df_error_free(&df_err); + } + dfilter_free(dfp); + + return true; +} + +void SyntaxLineEdit::checkFieldName(QString field) +{ + if (field.isEmpty()) { + setSyntaxState(SyntaxLineEdit::Empty); + return; + } + + char invalid_char = proto_check_field_name(field.toUtf8().constData()); + if (invalid_char) { + setSyntaxState(SyntaxLineEdit::Invalid); + } else { + checkDisplayFilter(field); + } +} + +void SyntaxLineEdit::checkCustomColumn(QString fields) +{ + if (fields.isEmpty()) { + setSyntaxState(SyntaxLineEdit::Empty); + return; + } + + gchar **splitted_fields = g_regex_split_simple(COL_CUSTOM_PRIME_REGEX, + fields.toUtf8().constData(), G_REGEX_ANCHORED, G_REGEX_MATCH_ANCHORED); + + for (guint i = 0; i < g_strv_length(splitted_fields); i++) { + if (splitted_fields[i] && *splitted_fields[i]) { + if (proto_check_field_name(splitted_fields[i]) != 0) { + setSyntaxState(SyntaxLineEdit::Invalid); + g_strfreev(splitted_fields); + return; + } + } + } + g_strfreev(splitted_fields); + + checkDisplayFilter(fields); +} + +void SyntaxLineEdit::checkInteger(QString number) +{ + if (number.isEmpty()) { + setSyntaxState(SyntaxLineEdit::Empty); + return; + } + + bool ok; + text().toInt(&ok); + if (ok) { + setSyntaxState(SyntaxLineEdit::Valid); + } else { + setSyntaxState(SyntaxLineEdit::Invalid); + } +} + +bool SyntaxLineEdit::isComplexFilter(const QString &filter) +{ + bool is_complex = false; + for (int i = 0; i < filter.length(); i++) { + if (!token_chars_.contains(filter.at(i))) { + is_complex = true; + break; + } + } + // Don't complete the current filter. + if (is_complex && filter.startsWith(text()) && filter.compare(text())) { + return true; + } + return false; +} + +bool SyntaxLineEdit::event(QEvent *event) +{ + if (event->type() == QEvent::ShortcutOverride) { + // You can't set time display formats while the display filter edit + // has focus. + + // Keep shortcuts in the main window from stealing keyPressEvents + // with Ctrl+Alt modifiers from us. This is a problem for many AltGr + // combinations since they are delivered with Ctrl+Alt modifiers + // instead of Qt::Key_AltGr and they tend to match the time display + // format shortcuts. + + // Uncommenting the qDebug line below prints the following here: + // + // US Keyboard: + // Ctrl+o: 79 QFlags(ControlModifier) "\u000F" + // Ctrl+Alt+2: 50 QFlags(ControlModifier|AltModifier) "2" + // + // Swedish (Sweden) Keyboard: + // Ctrl+o: 79 QFlags(ControlModifier) "\u000F" + // Ctrl+Alt+2: 64 QFlags(ControlModifier|AltModifier) "@" + // AltGr+{: 123 QFlags(ControlModifier|AltModifier) "{" + + QKeyEvent* key_event = static_cast(event); + // qDebug() << "=so" << key_event->key() << key_event->modifiers() << key_event->text(); + + if (key_event->modifiers() == Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier)) { + event->accept(); + return true; + } + } + return QLineEdit::event(event); +} + +void SyntaxLineEdit::completionKeyPressEvent(QKeyEvent *event) +{ + // Forward to the completer if needed... + if (completer_ && completer_->popup()->isVisible()) { + switch (event->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + break; + case Qt::Key_Tab: + focusNextChild(); + break; + case Qt::Key_Escape: + case Qt::Key_Backtab: + event->ignore(); + return; + default: + break; + } + } + + // ...otherwise process the key ourselves. + SyntaxLineEdit::keyPressEvent(event); + + if (!completion_enabled_ || !completer_ || !completion_model_ || !prefs.gui_autocomplete_filter) return; + + // Do nothing on bare shift. + if ((event->modifiers() & Qt::ShiftModifier) && event->text().isEmpty()) return; + + if (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) { + completer_->popup()->hide(); + return; + } + + QStringList sentence(splitLineUnderCursor()); + Q_ASSERT(sentence.size() == 2); // (preamble, token) + buildCompletionList(sentence[1] /* token */, sentence[0] /* preamble */); + + if (completion_model_->stringList().length() < 1) { + completer_->popup()->hide(); + return; + } + + QRect cr = cursorRect(); + cr.setWidth(completer_->popup()->sizeHintForColumn(0) + + completer_->popup()->verticalScrollBar()->sizeHint().width()); + completer_->complete(cr); +} + +void SyntaxLineEdit::completionFocusInEvent(QFocusEvent *event) +{ + if (completer_) + completer_->setWidget(this); + SyntaxLineEdit::focusInEvent(event); +} + +void SyntaxLineEdit::focusOutEvent(QFocusEvent *event) +{ + if (completer_ && completer_->popup()->isVisible() && event->reason() == Qt::PopupFocusReason) { + // Pretend we still have focus so that we'll draw our cursor. + // If cursorRect() were more precise we could just draw the cursor + // during a paintEvent. + return; + } + QLineEdit::focusOutEvent(event); +} + +// Add indicator icons for syntax states in order to make things more clear for +// color blind people. +void SyntaxLineEdit::paintEvent(QPaintEvent *event) +{ + QStyleOptionFrame opt; + initStyleOption(&opt); + QRect cr = style()->subElementRect(QStyle::SE_LineEditContents, &opt, this); + QPainter painter(this); + + // In my (gcc) testing here, if I add "background: yellow;" to the DisplayFilterCombo + // stylesheet, when building with Qt 5.15.2 the combobox background is yellow and the + // text entry area (between the bookmark and apply button) is drawn in the correct + // base color (white for light mode and black for dark mode), and the correct syntax + // color otherwise. When building with Qt 6.2.4 and 6.3.1, the combobox background is + // yellow and the text entry area is always yellow, i.e. QLineEdit isn't painting its + // background for some reason. + // + // It's not clear if this is a bug or just how things work under Qt6. Either way, it's + // easy to work around. +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // Must match CaptureFilterEdit and DisplayFilterEdit stylesheets. + int pad = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) + 1; + QRect full_cr = cr.adjusted(-pad, 0, -1, 0); + QBrush bg; + + switch (syntax_state_) { + case Valid: + bg = ColorUtils::fromColorT(&prefs.gui_text_valid); + break; + case Invalid: + bg = ColorUtils::fromColorT(&prefs.gui_text_invalid); + break; + case Deprecated: + bg = ColorUtils::fromColorT(&prefs.gui_text_deprecated); + break; + default: + bg = palette().base(); + break; + } + + painter.fillRect(full_cr, bg); +#endif + + QLineEdit::paintEvent(event); + + QString si_name; + + switch (syntax_state_) { + case Invalid: + si_name = "x-filter-invalid"; + break; + case Deprecated: + si_name = "x-filter-deprecated"; + break; + default: + return; + } + + QRect sir = QRect(0, 0, 14, 14); // QIcon::paint scales, which is not what we want. + int textWidth = fontMetrics().boundingRect(text()).width(); + // Qt always adds a margin of 6px between the border and text, see + // QLineEditPrivate::effectiveLeftTextMargin and + // QLineEditPrivate::sideWidgetParameters. + int margin = 2 * 6 + 1; + + if (cr.width() - margin - textWidth < sir.width() || cr.height() < sir.height()) { + // No space to draw + return; + } + + QIcon state_icon = StockIcon(si_name); + if (state_icon.isNull()) { + return; + } + + int si_off = (cr.height() - sir.height()) / 2; + sir.moveTop(cr.top() + si_off); + sir.moveRight(cr.right() - si_off); + painter.save(); + painter.setOpacity(0.25); + state_icon.paint(&painter, sir); + painter.restore(); +} + +void SyntaxLineEdit::insertFieldCompletion(const QString &completion_text) +{ + if (!completer_) return; + + QPoint field_coords(getTokenUnderCursor()); + + // Insert only if we have a matching field or if the entry is empty + if (field_coords.y() < 1 && !text().isEmpty()) { + completer_->popup()->hide(); + return; + } + + QString new_text = text().replace(field_coords.x(), field_coords.y(), completion_text); + setText(new_text); + setCursorPosition(field_coords.x() + static_cast(completion_text.length())); + emit textEdited(new_text); +} + +QPoint SyntaxLineEdit::getTokenUnderCursor() +{ + if (selectionStart() >= 0) return (QPoint(0,0)); + + int pos = cursorPosition(); + int start = pos; + int len = 0; + + while (start > 0 && token_chars_.contains(text().at(start -1))) { + start--; + len++; + } + while (pos < text().length() && token_chars_.contains(text().at(pos))) { + pos++; + len++; + } + + return QPoint(start, len); +} + +QStringList SyntaxLineEdit::splitLineUnderCursor() +{ + QPoint token_coords(getTokenUnderCursor()); + + // Split line into preamble and word under cursor. +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QString preamble = text().first(token_coords.x()).trimmed(); +#else + QString preamble = text().mid(0, token_coords.x()).trimmed(); +#endif + // This should be trimmed already + QString token_word = text().mid(token_coords.x(), token_coords.y()); + + return QStringList{ preamble, token_word }; +} diff --git a/ui/qt/widgets/syntax_line_edit.h b/ui/qt/widgets/syntax_line_edit.h new file mode 100644 index 00000000..d5b4d7bf --- /dev/null +++ b/ui/qt/widgets/syntax_line_edit.h @@ -0,0 +1,91 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SYNTAX_LINE_EDIT_H +#define SYNTAX_LINE_EDIT_H + +#include + +class QCompleter; +class QStringListModel; + +// Autocompletion is partially implemented. Subclasses must: +// - Provide buildCompletionList +// - Call setCompletionTokenChars + +class SyntaxLineEdit : public QLineEdit +{ + Q_OBJECT + Q_PROPERTY(SyntaxState syntaxState READ syntaxState) + Q_ENUMS(SyntaxState) +public: + explicit SyntaxLineEdit(QWidget *parent = 0); + enum SyntaxState { Empty, Busy, Invalid, Deprecated, Valid }; + + SyntaxState syntaxState() const { return syntax_state_; } + void setSyntaxState(SyntaxState state = Empty); + QString syntaxErrorMessage(); + // Error message with filter expression and location error. + QString syntaxErrorMessageFull(); + QString styleSheet() const; + QString deprecatedToken(); + + void setCompleter(QCompleter *c); + QCompleter *completer() const { return completer_; } + void allowCompletion(bool enabled); + + static QString createSyntaxErrorMessageFull(const QString &filter, + const QString &err_msg, + qsizetype loc_start, size_t loc_length); + +public slots: + void setStyleSheet(const QString &style_sheet); + // Insert filter text at the current position, adding spaces where needed. + void insertFilter(const QString &filter); + + // Built-in syntax checks. Connect textChanged to these as needed. + bool checkDisplayFilter(QString filter); + void checkFieldName(QString field); + void checkCustomColumn(QString fields); + void checkInteger(QString number); + +protected: + QCompleter *completer_; + QStringListModel *completion_model_; + void setCompletionTokenChars(const QString &token_chars) { token_chars_ = token_chars; } + bool isComplexFilter(const QString &filter); + virtual void buildCompletionList(const QString &field_word, const QString &preamble) { Q_UNUSED(field_word); Q_UNUSED(preamble); } + // x = Start position, y = length + QPoint getTokenUnderCursor(); + // Returns (preamble, token) + QStringList splitLineUnderCursor(); + + virtual bool event(QEvent *event); + void completionKeyPressEvent(QKeyEvent *event); + void completionFocusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void paintEvent(QPaintEvent *event); + +private: + SyntaxState syntax_state_; + QString style_sheet_; + QString state_style_sheet_; + QString syntax_error_message_; + QString syntax_error_message_full_; + QString token_chars_; + bool completion_enabled_; + +private slots: + void insertFieldCompletion(const QString &completion_text); + +signals: + +}; + +#endif // SYNTAX_LINE_EDIT_H diff --git a/ui/qt/widgets/tabnav_tree_view.cpp b/ui/qt/widgets/tabnav_tree_view.cpp new file mode 100644 index 00000000..38b73469 --- /dev/null +++ b/ui/qt/widgets/tabnav_tree_view.cpp @@ -0,0 +1,57 @@ +/* tabnav_tree_view.cpp + * Tree view with saner tab navigation functionality. + * + * Copyright 2016 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tabnav_tree_view.h" + +TabnavTreeView::TabnavTreeView(QWidget *parent) : QTreeView(parent) +{ +} + +// Note: if a QTableView is used, then this is not needed anymore since Tab +// works as "expected" (move to next cell instead of row). +// Note 2: this does not help with fields with no widget (like filename). +QModelIndex TabnavTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) +{ + QModelIndex current = currentIndex(); + // If an item is currently selected, interpret Next/Previous. Otherwise, + // fallback to the default selection (e.g. first row for Next). + if (current.isValid()) { + if (cursorAction == MoveNext) { + if (current.column() < model()->columnCount()) { + return current.sibling(current.row(), current.column() + 1); + } + return current; + } else if (cursorAction == MovePrevious) { + if (current.column() > 0) { + return current.sibling(current.row(), current.column() - 1); + } + return current; + } + } + + return QTreeView::moveCursor(cursorAction, modifiers); +} + +/*! + \fn void TabnavTreeView::currentItemChanged(QModelIndex *current, QModelIndex *previous) + + This signal is emitted whenever the current item changes. + + \a previous is the item that previously had the focus; \a current is the + new current item. + */ + +void TabnavTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + QTreeView::currentChanged(current, previous); + emit currentItemChanged(current, previous); +} diff --git a/ui/qt/widgets/tabnav_tree_view.h b/ui/qt/widgets/tabnav_tree_view.h new file mode 100644 index 00000000..67cf4973 --- /dev/null +++ b/ui/qt/widgets/tabnav_tree_view.h @@ -0,0 +1,38 @@ +/** @file + * + * Tree view with saner tab navigation functionality. + * + * Copyright 2016 Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TABNAV_TREE_VIEW_H +#define TABNAV_TREE_VIEW_H + +#include +#include + +/** + * Like QTreeView, but instead of changing to the next row (same column) when + * pressing Tab while editing, change to the next column (same row). + */ +class TabnavTreeView : public QTreeView +{ + Q_OBJECT + +public: + TabnavTreeView(QWidget *parent = 0); + QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); + +protected slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +signals: + void currentItemChanged(const QModelIndex ¤t, const QModelIndex &previous); +}; +#endif // TABNAV_TREE_VIEW_H diff --git a/ui/qt/widgets/traffic_tab.cpp b/ui/qt/widgets/traffic_tab.cpp new file mode 100644 index 00000000..9913acaa --- /dev/null +++ b/ui/qt/widgets/traffic_tab.cpp @@ -0,0 +1,618 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TabData::TabData() : + _name(QString()), + _protoId(-1) +{} + +TabData::TabData(QString name, int protoId) : + _name(name), + _protoId(protoId) +{} + +QString TabData::name() const +{ + return _name; +} + +int TabData::protoId() const +{ + return _protoId; +} + + +TrafficTab::TrafficTab(QWidget * parent) : + DetachableTabWidget(parent) +{ + _createModel = nullptr; + _createDelegate = nullptr; + _disableTaps = false; + _nameResolution = false; + setTabBasename(QString()); +} + +TrafficTab::~TrafficTab() +{} + +void TrafficTab::setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentColumnList, ATapModelCallback createModel) +{ + setTabBasename(tableName); + + _allProtocols = trafficList->protocols(); + if (createModel) + _createModel = createModel; + + _recentColumnList = recentColumnList; + + setOpenTabs(trafficList->protocols(true)); +} + +void TrafficTab::setDelegate(ATapCreateDelegate createDelegate) +{ + if (! createDelegate) + return; + + _createDelegate = createDelegate; + + + for (int idx = 0; idx < count(); idx++) { + if (qobject_cast(widget(idx))) + { + QTreeView * tree = qobject_cast(widget(idx)); + tree->setItemDelegate(createDelegate(tree)); + } + } +} + +QTreeView * TrafficTab::createTree(int protoId) +{ + TrafficTree * tree = new TrafficTree(tabBasename(), _recentColumnList, this); + + if (_createModel) { + ATapDataModel * model = _createModel(protoId, ""); + model->setParent(tree); + connect(model, &ATapDataModel::tapListenerChanged, tree, &TrafficTree::tapListenerEnabled); + + model->enableTap(); + + if (_createDelegate) + { + tree->setItemDelegate(_createDelegate(tree)); + } + + TrafficDataFilterProxy * proxyModel = new TrafficDataFilterProxy(tree); + proxyModel->setSourceModel(model); + tree->setModel(proxyModel); + + QItemSelectionModel * ism = new QItemSelectionModel(proxyModel, tree); + tree->setSelectionModel(ism); + connect(ism, &QItemSelectionModel::currentChanged, this, &TrafficTab::doCurrentIndexChange); + + tree->applyRecentColumns(); + + tree->sortByColumn(0, Qt::AscendingOrder); + + connect(proxyModel, &TrafficDataFilterProxy::modelReset, this, [tree]() { + if (tree->model()->rowCount() > 0) { + for (int col = 0; col < tree->model()->columnCount(); col++) + tree->resizeColumnToContents(col); + } + }); + connect(proxyModel, &TrafficDataFilterProxy::modelReset, this, &TrafficTab::modelReset); + + /* If the columns for the tree have changed, contact the tab. By also having the tab + * columns changed signal connecting back to the tree, it will propagate to all trees + * registered with this tab. Attention, this heavily relies on the fact, that all + * tree data models are identical */ + connect(tree, &TrafficTree::columnsHaveChanged, this, &TrafficTab::columnsHaveChanged); + connect(this, &TrafficTab::columnsHaveChanged, tree, &TrafficTree::columnsChanged); + } + + return tree; +} + +void TrafficTab::useAbsoluteTime(bool absolute) +{ + for(int idx = 0; idx < count(); idx++) + { + ATapDataModel * atdm = modelForTabIndex(idx); + if (atdm) + atdm->useAbsoluteTime(absolute); + } +} + +void TrafficTab::useNanosecondTimestamps(bool nanoseconds) +{ + for(int idx = 0; idx < count(); idx++) + { + ATapDataModel * atdm = modelForTabIndex(idx); + if (atdm) + atdm->useNanosecondTimestamps(nanoseconds); + } +} + +void TrafficTab::disableTap() +{ + for(int idx = 0; idx < count(); idx++) + { + ATapDataModel * atdm = modelForTabIndex(idx); + if (atdm) + atdm->disableTap(); + } + + _disableTaps = true; + emit disablingTaps(); +} + +void TrafficTab::setOpenTabs(QList protocols) +{ + QList tabs = _tabs.keys(); + QList remove; + blockSignals(true); + + foreach(int protocol, protocols) + { + if (! tabs.contains(protocol)) { + insertProtoTab(protocol, false); + } + tabs.removeAll(protocol); + } + + foreach(int protocol, tabs) + removeProtoTab(protocol, false); + + blockSignals(false); + + emit tabsChanged(_tabs.keys()); + emit retapRequired(); +} + +void TrafficTab::insertProtoTab(int protoId, bool emitSignals) +{ + QList lUsed = _tabs.keys(); + + if (lUsed.contains(protoId) && lUsed.count() != count()) + { + _tabs.clear(); + for (int idx = 0; idx < count(); idx++) { + TabData tabData = qvariant_cast(tabBar()->tabData(idx)); + _tabs.insert(tabData.protoId(), idx); + } + lUsed = _tabs.keys(); + } + + if (protoId <= 0 || lUsed.contains(protoId)) + return; + + QList lFull = _allProtocols; + int idx = (int) lFull.indexOf(protoId); + if (idx < 0) + return; + + QList part = lFull.mid(0, idx); + int insertAt = 0; + if (part.count() > 0) { + for (int cnt = idx - 1; cnt >= 0; cnt--) { + if (lUsed.contains(part[cnt]) && part[cnt] != protoId) { + insertAt = (int) lUsed.indexOf(part[cnt]) + 1; + break; + } + } + } + + QTreeView * tree = createTree(protoId); + QString tableName = proto_get_protocol_short_name(find_protocol_by_id(protoId)); + TabData tabData(tableName, protoId); + QVariant storage; + storage.setValue(tabData); + if (tree->model()->rowCount() > 0) + tableName += QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(tree->model()->rowCount()); + + int tabId = -1; + if (insertAt > -1) + tabId = insertTab(insertAt, tree, tableName); + else + tabId = addTab(tree, tableName); + if (tabId >= 0) + tabBar()->setTabData(tabId, storage); + + + /* We reset the correct tab idxs. That operations is costly, but it is only + * called during this operation and ensures, that other operations do not + * need to iterate, but rather can lookup the indeces. */ + _tabs.clear(); + for (int idx = 0; idx < count(); idx++) { + TabData tabData = qvariant_cast(tabBar()->tabData(idx)); + _tabs.insert(tabData.protoId(), idx); + } + + if (emitSignals) { + emit tabsChanged(_tabs.keys()); + emit retapRequired(); + } +} + +void TrafficTab::removeProtoTab(int protoId, bool emitSignals) +{ + if (_tabs.keys().contains(protoId)) { + for(int idx = 0; idx < count(); idx++) { + TabData tabData = qvariant_cast(tabBar()->tabData(idx)); + if (protoId == tabData.protoId()) { + removeTab(idx); + break; + } + } + } + + /* We reset the correct tab idxs. That operations is costly, but it is only + * called during this operation and ensures, that other operations do not + * need to iterate, but rather can lookup the indeces. */ + _tabs.clear(); + for (int idx = 0; idx < count(); idx++) { + TabData tabData = qvariant_cast(tabBar()->tabData(idx)); + _tabs.insert(tabData.protoId(), idx); + } + + if (emitSignals) { + emit tabsChanged(_tabs.keys()); + emit retapRequired(); + } +} + +void TrafficTab::doCurrentIndexChange(const QModelIndex & cur, const QModelIndex &) +{ + if (! cur.isValid()) + return; + + const TrafficDataFilterProxy * proxy = qobject_cast(cur.model()); + if (! proxy) + return; + + ATapDataModel * model = qobject_cast(proxy->sourceModel()); + if (! model) + return; + + int tabId = _tabs[model->protoId()]; + emit tabDataChanged(tabId); +} + +QVariant TrafficTab::currentItemData(int role) +{ + QTreeView * tree = qobject_cast(currentWidget()); + if (tree) { + QModelIndex idx = tree->selectionModel()->currentIndex(); + /* In case no selection has been made yet, we select the topmostleft index, + * to ensure proper handling. Especially ConversationDialog depends on this + * method always returning data */ + if (!idx.isValid()) { + ATapDataModel * model = modelForTabIndex(currentIndex()); + idx = model->index(0, 0); + } + return idx.data(role); + } + + return QVariant(); +} + +void TrafficTab::modelReset() +{ + if (! qobject_cast(sender())) + return; + + TrafficDataFilterProxy * qsfpm = qobject_cast(sender()); + if (!qsfpm || ! qobject_cast(qsfpm->sourceModel())) + return; + + ATapDataModel * atdm = qobject_cast(qsfpm->sourceModel()); + int protoId = atdm->protoId(); + if (!_tabs.keys().contains(protoId)) + return; + + int tabIdx = _tabs[protoId]; + TabData tabData = qvariant_cast(tabBar()->tabData(tabIdx)); + + if (tabData.protoId() == protoId) { + if (qsfpm->rowCount() == 0) + setTabText(tabIdx, tabData.name()); + else + setTabText(tabIdx, tabData.name() + QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(qsfpm->rowCount())); + } + + emit tabDataChanged(tabIdx); +} + +ATapDataModel * TrafficTab::modelForTabIndex(int tabIdx) +{ + if (tabIdx == -1) + tabIdx = currentIndex(); + + return modelForWidget(widget(tabIdx)); +} + +ATapDataModel * TrafficTab::modelForWidget(QWidget * searchWidget) +{ + if (qobject_cast(searchWidget)) { + QTreeView * tree = qobject_cast(searchWidget); + if (qobject_cast(tree->model())) { + TrafficDataFilterProxy * qsfpm = qobject_cast(tree->model()); + if (qsfpm && qobject_cast(qsfpm->sourceModel())) { + return qobject_cast(qsfpm->sourceModel()); + } + } + } + + return nullptr; +} + +void TrafficTab::setFilter(QString filter) +{ + for (int idx = 0; idx < count(); idx++ ) + { + ATapDataModel * atdm = modelForTabIndex(idx); + if (! atdm) + continue; + atdm->setFilter(filter); + } +} + +void TrafficTab::setNameResolution(bool checked) +{ + if (checked == _nameResolution) + return; + + for (int idx = 0; idx < count(); idx++ ) + { + ATapDataModel * atdm = modelForTabIndex(idx); + if (! atdm) + continue; + atdm->setResolveNames(checked); + + } + + _nameResolution = checked; + + /* Send the signal, that all tabs have potentially changed */ + emit tabDataChanged(-1); +} + +bool TrafficTab::hasNameResolution(int tabIdx) +{ + int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx; + ATapDataModel * dataModel = modelForTabIndex(tab); + if (! dataModel) + return false; + + return dataModel->allowsNameResolution(); +} + +QMenu * TrafficTab::createCopyMenu(QWidget *parent) +{ + TrafficTree * tree = qobject_cast(currentWidget()); + if ( ! tree) + return nullptr; + + return tree->createCopyMenu(parent); +} + +#ifdef HAVE_MAXMINDDB +bool TrafficTab::hasGeoIPData(int tabIdx) +{ + int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx; + + ATapDataModel * dataModel = modelForTabIndex(tab); + return dataModel->hasGeoIPData(); +} + +bool +TrafficTab::writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataModel) +{ + QTextStream out(fp); + + if (!json_only) { + QFile ipmap(get_datafile_path("ipmap.html")); + + if (!ipmap.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, tr("Map file error"), tr("Could not open base file %1 for reading: %2") + .arg(get_datafile_path("ipmap.html")) + .arg(g_strerror(errno)) + ); + return false; + } + + /* Copy ipmap.html to map file. */ + QTextStream in(&ipmap); + QString line; + while (in.readLineInto(&line)) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + out << line << Qt::endl; +#else + out << line << endl; +#endif + } + + out << QString("\n"); + + out.flush(); + + return true; +} + +QUrl TrafficTab::createGeoIPMap(bool json_only, int tabIdx) +{ + int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx; + ATapDataModel * dataModel = modelForTabIndex(tab); + if (! (dataModel && dataModel->hasGeoIPData())) { + QMessageBox::warning(this, tr("Map file error"), tr("No endpoints available to map")); + return QUrl(); + } + + QString tempname = QString("%1/ipmapXXXXXX.html").arg(QDir::tempPath()); + QTemporaryFile tf(tempname); + if (!tf.open()) { + QMessageBox::warning(this, tr("Map file error"), tr("Unable to create temporary file")); + return QUrl(); + } + + if (!writeGeoIPMapFile(&tf, json_only, dataModel)) { + tf.close(); + return QUrl(); + } + + tf.setAutoRemove(false); + return QUrl::fromLocalFile(tf.fileName()); +} +#endif + +void TrafficTab::detachTab(int tabIdx, QPoint pos) { + ATapDataModel * model = modelForTabIndex(tabIdx); + if (!model) + return; + + TrafficTree * tree = qobject_cast(widget(tabIdx)); + if (!tree) + return; + + connect(this, &TrafficTab::disablingTaps ,tree , &TrafficTree::disableTap); + DetachableTabWidget::detachTab(tabIdx, pos); + + removeProtoTab(model->protoId()); +} + +void TrafficTab::attachTab(QWidget * content, QString name) +{ + ATapDataModel * model = modelForWidget(content); + if (!model) { + attachTab(content, name); + return; + } + + insertProtoTab(model->protoId()); +} diff --git a/ui/qt/widgets/traffic_tab.h b/ui/qt/widgets/traffic_tab.h new file mode 100644 index 00000000..71c48ae5 --- /dev/null +++ b/ui/qt/widgets/traffic_tab.h @@ -0,0 +1,246 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TRAFFIC_TAB_H +#define TRAFFIC_TAB_H + +#include "config.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * @brief Callback for creating an ATapDataModel + * + * @param protoId the protocol id for the callback to use + * @param filter setting the filter for the tap + * @return either null, if no model could be created, or an instance + * of the model itself. + */ +typedef ATapDataModel * (*ATapModelCallback)(int protoId, QString filter); + +/** + * @brief Callback for creating an item delegate + * + * @param parent the parent for the delegate to attach to + * @return either null if no delegate had been created, or an instance for + * the delegate + */ +typedef QAbstractItemDelegate * (*ATapCreateDelegate)(QWidget * parent); + +class TabData +{ +public: + TabData(); + TabData(const TabData &) = default; + TabData &operator=(const TabData &) = default; + + TabData(QString name, int proto); + + QString name() const; + int protoId() const; + +private: + QString _name; + int _protoId; +}; + +Q_DECLARE_METATYPE(TabData) + +/** + * @brief A QTabWidget class, providing tap information + * + * This class combines all required information, to display tapped data + * to the user. Specifically it handles all model data internally, therefore + * removing the need of the dialog to know how data is being stored or + * generated. + */ +class TrafficTab : public DetachableTabWidget +{ + Q_OBJECT + +public: + TrafficTab(QWidget *parent = nullptr); + virtual ~TrafficTab(); + + /** + * @brief Set the Protocol Info for the traffic tab + * + * This has to be called right after instantiating the class. The reason this is not + * done inside the constructor is such, that the object can be used with Qt Designer + * without having to removing the predefined object during setup of the UI. + * + * @param tableName The name for the table. Used for the protocol selection button + * @param trafficList an element of traffictypeslist, which handles all profile selections + * @param recentColumnList a list of columns to be displayed for this traffic type + * @param createModel A callback, which will create the correct model for the trees + * + * @see ATapModelCallback + */ + void setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentColumnList, ATapModelCallback createModel); + + /** + * @brief Set the Delegate object for the tab. It will apply for all + * models residing in this tab object + * + * @param createDelegate the callback for the delegate creation + * + * @see ATapCreateDelegate + */ + void setDelegate(ATapCreateDelegate createDelegate); + + /** + * @brief Set the filter or remove it by providing an empty filter + * + * This differs from filtering the model itself in such a way, that filtering is + * being done using the epan system. Therefore, once filtered, the only way to get + * all elements back is to set an empty string. + * + * @note Filtering will only work, as long as the capture file remains open. If + * taps have been disabled and capture has stopped, filtering will no longer work. + * + * @param filter the string to be filtered on + */ + void setFilter(QString filter = QString()); + + /** + * @brief Enable/Disable name resolution for the address column + * + * @param checked true to enable name resolution + */ + void setNameResolution(bool checked); + + /** + * @brief Disables the taps for this traffic tab. + * + * Disables all taps for models used by this traffic tab. They cannot be re-enabled on purpose, + * as in most cases, disabling them is being done during closing of the original capture file. + * This also disabled all filter actions, as well as the tap selection button. + */ + void disableTap(); + + /** + * @brief Create a menu containing clipboard copy entries for this tab + * + * It will create all entries, including copying the content of the currently selected tab + * to CSV, YAML and JSON + * + * @param parent the parent object or null + * @return QMenu* the resulting menu or null + */ + QMenu * createCopyMenu(QWidget * parent = nullptr); + + /** + * @brief Checks, wether the given tabpage support name resolution on the address column + * + * @param tabIdx the index of the page. If it is out of bounds or < 0, the current index is being used + * @return true if name resolution is being supported + * @return false if name resolution is not supported + */ + bool hasNameResolution(int tabIdx = -1); + +#ifdef HAVE_MAXMINDDB + /** + * @brief Checks, wether the given tabpage support GeoIP data + * + * @param tabIdx the index of the page. If it is out of bounds or < 0, the current index is being used + * @return true if geoIP data is being supported + * @return false if geoIP data is not supported + */ + bool hasGeoIPData(int tabIdx = -1); + + /** + * @brief Create a map of GeoIP data and write it to a temporary file + * + * @param onlyJSON only put the json content into the temporary file + * @param tabIdx the index of the page. If it is out of bounds or < 0, the current index is being used + * @return The path to the temporary file for the data + */ + QUrl createGeoIPMap(bool onlyJSON, int tabIdx = -1); +#endif + + /** + * @brief Return the itemData for the currently selected index in the currently + * displayed treeview. + * + * @param role the role to be used, defaults to Qt::DisplayRole + * @return QVariant the resulting value as QVariant type + */ + QVariant currentItemData(int role = Qt::DisplayRole); + + /** + * @brief Use nanosecond timestamps if requested + * + * @param useNSTime use nanosecond timestamps if required and requested + */ + void useNanosecondTimestamps(bool useNSTime); + +public slots: + + /** + * @brief Use absolute time for the time columns + * + * @param absolute true if absolute time should be used + */ + void useAbsoluteTime(bool absolute); + + void setOpenTabs(QList protocols); + +signals: + void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + void tabDataChanged(int idx); + void retapRequired(); + void disablingTaps(); + void tabsChanged(QList protocols); + void columnsHaveChanged(QList columns); + +protected slots: + + virtual void detachTab(int idx, QPoint pos) override; + virtual void attachTab(QWidget * content, QString name) override; + +private: + QList _allProtocols; + QMap _tabs; + ATapModelCallback _createModel; + ATapCreateDelegate _createDelegate; + GList ** _recentColumnList; + + bool _disableTaps; + bool _nameResolution; + + QTreeView * createTree(int protoId); + ATapDataModel * modelForTabIndex(int tabIdx = -1); + ATapDataModel * modelForWidget(QWidget * widget); + + void insertProtoTab(int protoId, bool emitSignals = true); + void removeProtoTab(int protoId, bool emitSignals = true); + +#ifdef HAVE_MAXMINDDB + bool writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataModel); +#endif + +private slots: + void modelReset(); + + void doCurrentIndexChange(const QModelIndex & cur, const QModelIndex & prev); +}; + +#endif // TRAFFIC_TAB_H diff --git a/ui/qt/widgets/traffic_tree.cpp b/ui/qt/widgets/traffic_tree.cpp new file mode 100644 index 00000000..be154d7d --- /dev/null +++ b/ui/qt/widgets/traffic_tree.cpp @@ -0,0 +1,879 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ui/recent.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MenuEditAction::MenuEditAction(QString text, QString hintText, QObject * parent) : + QWidgetAction(parent), + _hintText(hintText), + _text(text), + _lineEdit(nullptr) +{} + +QWidget * MenuEditAction::createWidget(QWidget *parent) { + _lineEdit = new QLineEdit(parent); + _lineEdit->setAlignment(Qt::AlignRight); + _lineEdit->setText(_text); + _lineEdit->setPlaceholderText(_hintText); + connect(_lineEdit, &QLineEdit::returnPressed, this, &MenuEditAction::triggerEntry); + return _lineEdit; +} + +void MenuEditAction::triggerEntry() { + if (_lineEdit) + _text = _lineEdit->text(); + + emit trigger(); +} + +QString MenuEditAction::text() const { + return _text; +} + + +TrafficTreeHeaderView::TrafficTreeHeaderView(GList ** recentColumnList, QWidget * parent): + QHeaderView(Qt::Horizontal, parent) +{ + _recentColumnList = recentColumnList; + + setContextMenuPolicy(Qt::CustomContextMenu); + + _actions = new QActionGroup(this); + + QAction * filterAction = _actions->addAction(tr("Less than")); + filterAction->setCheckable(true); + filterAction->setChecked(true); + filterAction->setProperty("filter_action", (int)TrafficDataFilterProxy::TRAFFIC_DATA_LESS); + filterAction = _actions->addAction(tr("Greater than")); + filterAction->setCheckable(true); + filterAction->setProperty("filter_action", (int)TrafficDataFilterProxy::TRAFFIC_DATA_GREATER); + filterAction = _actions->addAction(tr("Equal")); + filterAction->setCheckable(true); + filterAction->setProperty("filter_action", (int)TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL); + + connect(this, &QHeaderView::customContextMenuRequested, this, &TrafficTreeHeaderView::headerContextMenu); +} + +TrafficTreeHeaderView::~TrafficTreeHeaderView() +{} + +void TrafficTreeHeaderView::headerContextMenu(const QPoint &pos) +{ + TrafficTree * tree = qobject_cast(parent()); + if (!tree) + return; + + TrafficDataFilterProxy * proxy = qobject_cast(tree->model()); + if (sender() != this || ! proxy) + return; + + QMenu * ctxMenu = new QMenu(this); + ctxMenu->setAttribute(Qt::WA_DeleteOnClose); + + QAction * headerAction = ctxMenu->addAction(tr("Columns to display")); + headerAction->setEnabled(false); + + for (int col = 0; col < tree->dataModel()->columnCount(); col++) + { + QString name = tree->dataModel()->headerData(col).toString(); + QAction * action = new QAction(name); + action->setCheckable(true); + action->setChecked(proxy->columnVisible(col)); + action->setProperty("col_nr", col); + ctxMenu->addAction(action); + + connect(action, &QAction::triggered, this, &TrafficTreeHeaderView::columnTriggered); + } + + ctxMenu->addSeparator(); + + int column = logicalIndexAt(pos); + + bool is_address = false; + QModelIndex sourceIdx = proxy->mapToSource(proxy->index(0, column)); + if (qobject_cast(proxy->sourceModel()) && sourceIdx.column() == EndpointDataModel::ENDP_COLUMN_ADDR) { + is_address = true; + } else if (qobject_cast(proxy->sourceModel()) && (sourceIdx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR || + sourceIdx.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR)) { + is_address = true; + } + + if (! is_address) { + QString columnText = model()->headerData(column, Qt::Horizontal).toString(); + QAction * filterAction = ctxMenu->addAction(tr("Filter %1 by").arg(columnText)); + filterAction->setEnabled(false); + ctxMenu->addActions(_actions->actions()); + + MenuEditAction * editAction = new MenuEditAction(_filterText, tr("Enter filter value")); + editAction->setProperty("column", column); + ctxMenu->addAction(editAction); + connect(editAction, &MenuEditAction::triggered, this, &TrafficTreeHeaderView::filterColumn); + } + + connect(ctxMenu, &QMenu::triggered, this, &TrafficTreeHeaderView::menuActionTriggered); + + ctxMenu->popup(mapToGlobal(pos)); +} + +void TrafficTreeHeaderView::applyRecent() +{ + TrafficTree * tree = qobject_cast(parent()); + if (!tree) + return; + + QList columns; + for (GList * endTab = *_recentColumnList; endTab; endTab = endTab->next) { + QString colStr = QString((const char *)endTab->data); + bool ok = false; + int col = colStr.toInt(&ok); + if (ok) + columns << col; + } + + if (columns.count() > 0) { + TrafficDataFilterProxy * proxy = qobject_cast(tree->model()); + for (int col = 0; col < tree->dataModel()->columnCount(); col++) { + proxy->setColumnVisibility(col, columns.contains(col)); + } + } +} + +void TrafficTreeHeaderView::columnTriggered(bool checked) +{ + TrafficTree * tree = qobject_cast(parent()); + if (!tree) + return; + + TrafficDataFilterProxy * proxy = qobject_cast(tree->model()); + QAction * entry = qobject_cast(sender()); + if (! proxy || ! entry || ! entry->property("col_nr").isValid()) + return; + + int col = entry->property("col_nr").toInt(); + proxy->setColumnVisibility(col, checked); + + prefs_clear_string_list(*_recentColumnList); + *_recentColumnList = NULL; + + QList visible; + + for (int col = 0; col < tree->dataModel()->columnCount(); col++) { + if (proxy->columnVisible(col)) { + visible << col; + gchar *nr = qstring_strdup(QString::number(col)); + *_recentColumnList = g_list_append(*_recentColumnList, nr); + } + } + + emit columnsHaveChanged(visible); +} + +void TrafficTreeHeaderView::menuActionTriggered(QAction * act) +{ + if (_actions && _actions->actions().contains(act)) { + QMenu * menu = qobject_cast(sender()); + if (menu) { + MenuEditAction * menuAction = nullptr; + foreach(QAction * _act, menu->actions()) { + if (qobject_cast(_act)) { + menuAction = qobject_cast(_act); + break; + } + } + + int column = menuAction ? menuAction->property("column").toInt() : -1; + if (column >= 0) { + _filterText = menuAction->text().trimmed(); + if (_filterText.length() == 0) + column = -1; + int filterOn = act->property("filter_action").toInt(); + + emit filterOnColumn(column, filterOn, _filterText); + } + } + } +} + +void TrafficTreeHeaderView::filterColumn(bool) +{ + MenuEditAction * menuAction = qobject_cast(sender()); + if (!menuAction) + return; + + int filterOn = TrafficDataFilterProxy::TRAFFIC_DATA_LESS; + foreach(QAction * act, _actions->actions()) { + if (act->isChecked() && act->property("filter_action").isValid()) { + filterOn = act->property("filter_action").toInt(); + break; + } + } + + int column = menuAction->property("column").toInt(); + _filterText = menuAction->text().trimmed(); + if (_filterText.length() == 0) + column = -1; + + emit filterOnColumn(column, filterOn, _filterText); +} + + +TrafficDataFilterProxy::TrafficDataFilterProxy(QObject *parent) : + QSortFilterProxyModel(parent), + _filterColumn(-1), + _filterOn(-1), + _filterText(QString()) +{ + setSortRole(ATapDataModel::UNFORMATTED_DISPLAYDATA); +} + + +void TrafficDataFilterProxy::filterForColumn(int column, int filterOn, QString filterText) +{ + if (filterOn < 0 || filterOn > TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) + column = -1; + + _filterColumn = mapToSourceColumn(column); + _filterOn = filterOn; + _filterText = filterText; + invalidateFilter(); +} + +int TrafficDataFilterProxy::mapToSourceColumn(int proxyColumn) const +{ + ATapDataModel * model = qobject_cast(sourceModel()); + if (!model || proxyColumn == -1) { + return proxyColumn; + } + + if (rowCount() > 0) { + return mapToSource(index(0, proxyColumn)).column(); + } + + /* mapToSource() requires a valid QModelIndex, and thus does not work when + * all rows are filtered out by the current filter. (E.g., the user has + * accidentally entered an incorrect filter or operator and wants to fix + * it.) Since our filterAcceptsColumn doesn't depend on the row, we can + * determine the mapping between the currently displayed column number and + * the column number in the model this way, even if no rows are displayed. + * It is linear time in the number of columns, though. + */ + int currentProxyColumn = 0; + for (int column=0; column < model->columnCount(); ++column) { + if (filterAcceptsColumn(column, QModelIndex())) { + if (currentProxyColumn++ == proxyColumn) { + return column; + } + } + } + + return -1; +} + +bool TrafficDataFilterProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + ATapDataModel * dataModel = qobject_cast(sourceModel()); + if (dataModel) { + bool isFiltered = dataModel->data(dataModel->index(source_row, 0), ATapDataModel::ROW_IS_FILTERED).toBool(); + if (isFiltered && dataModel->filter().length() > 0) + return false; + /* XXX: What if the filter column is now hidden? Should the filter + * still apply or should it be cleared? Right now it is still applied. + */ + + QModelIndex srcIdx = dataModel->index(source_row, _filterColumn); + if (srcIdx.isValid()) { + QVariant data = srcIdx.data(ATapDataModel::UNFORMATTED_DISPLAYDATA); + + bool filtered = false; + /* QVariant comparisons coerce to the first parameter type, so + * putting data first and converting the string to it is important. + */ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + /* QVariant::compare coerces strings to numeric types, but does + * not try to automatically convert them to datetime related types. + */ + QVariant rhs = QVariant(_filterText); + if (data.userType() == QMetaType::QDateTime) { + /* Try to parse with a date included in the filter, and + * fallback to time only if that fails. + */ + QDateTime filter_dt = QDateTime::fromString(_filterText, Qt::ISODateWithMs); + if (filter_dt.isValid()) { + rhs.setValue(filter_dt); + } else { + QTime filterTime = QTime::fromString(_filterText, Qt::ISODateWithMs); + if (filterTime.isValid()) { + rhs.setValue(filterTime); + data.setValue(data.toTime()); + } else { + rhs = QVariant(); + } + } + } + QPartialOrdering result = QVariant::compare(data, rhs); + if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS) + filtered = result < 0; + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER) + filtered = result > 0; + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) + filtered = result == 0; +#else + /* The comparisons are deprecated in 5.15. This is most of the + * implementation of QAbstractItemModelPrivate::isVariantLessThan + * from the Qt source. + */ + if (_filterText.isEmpty()) + filtered = true; + else if (data.isNull()) + filtered = false; + else { + switch (data.userType()) { + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::LongLong: + if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS) + filtered = data.toLongLong() < _filterText.toLongLong(); + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER) + filtered = data.toLongLong() > _filterText.toLongLong(); + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) + filtered = data.toLongLong() == _filterText.toLongLong(); + break; + case QMetaType::Float: + case QMetaType::Double: + if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS) + filtered = data.toDouble() < _filterText.toDouble(); + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER) + filtered = data.toDouble() > _filterText.toDouble(); + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) + filtered = data.toDouble() == _filterText.toDouble(); + break; + case QMetaType::QDateTime: + { + /* Try to parse with a date included, and fall back to time + * only if that fails. + */ + QDateTime filter_dt = QDateTime::fromString(_filterText, Qt::ISODateWithMs); + if (filter_dt.isValid()) { + if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS) + filtered = data.toDateTime() < filter_dt; + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER) + filtered = data.toDateTime() > filter_dt; + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) + filtered = data.toDateTime() == filter_dt; + break; + } + } + /* FALLTHROUGH */ + case QMetaType::QTime: + { + QTime filter_t = QTime::fromString(_filterText, Qt::ISODateWithMs); + if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS) + filtered = data.toTime() < filter_t; + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER) + filtered = data.toTime() > filter_t; + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) + filtered = data.toTime() == filter_t; + break; + } + case QMetaType::QString: + default: + /* XXX: We don't do UTF-8 aware coallating in Packet List + * (because it's slow), but possibly could here. + */ + if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS) + filtered = data.toString() < _filterText; + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER) + filtered = data.toString() > _filterText; + else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) + filtered = data.toString() == _filterText; + break; + } + } +#endif + + if (!filtered) + return false; + } + } + + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} + +bool TrafficDataFilterProxy::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + if (! source_left.isValid() || ! qobject_cast(source_left.model())) + return false; + if (! source_right.isValid() || ! qobject_cast(source_right.model())) + return false; + + ATapDataModel * model = qobject_cast(sourceModel()); + + if (! model || source_left.model() != model || source_right.model() != model) + return false; + + QVariant datA = source_left.data(ATapDataModel::UNFORMATTED_DISPLAYDATA); + QVariant datB = source_right.data(ATapDataModel::UNFORMATTED_DISPLAYDATA); + + bool is_address = false; + if (qobject_cast(model) && source_left.column() == EndpointDataModel::ENDP_COLUMN_ADDR && + source_left.column() == source_right.column()) { + is_address = true; + } else if (qobject_cast(model) && (source_left.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR || + source_left.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) && source_left.column() == source_right.column()) { + is_address = true; + } + + if (is_address) { + bool result = false; + bool identical = false; + int addressTypeA = model->data(source_left, ATapDataModel::DATA_ADDRESS_TYPE).toInt(); + int addressTypeB = model->data(source_right, ATapDataModel::DATA_ADDRESS_TYPE).toInt(); + if (addressTypeA != 0 && addressTypeB != 0 && addressTypeA != addressTypeB) { + result = addressTypeA < addressTypeB; + } else if (addressTypeA != 0 && addressTypeA == addressTypeB) { + + if (addressTypeA == AT_IPv4) { + quint32 valA = model->data(source_left, ATapDataModel::DATA_IPV4_INTEGER).value(); + quint32 valB = model->data(source_right, ATapDataModel::DATA_IPV4_INTEGER).value(); + + result = valA < valB; + identical = valA == valB; + } else if (addressTypeA == AT_NUMERIC) { + quint32 valA = datA.toInt(); + quint32 valB = datB.toInt(); + result = valA < valB; + identical = valA == valB; + } else { + result = QString::compare(datA.toString(), datB.toString(), Qt::CaseInsensitive) < 0; + identical = QString::compare(datA.toString(), datB.toString(), Qt::CaseInsensitive) == 0; + } + + int portColumn = EndpointDataModel::ENDP_COLUMN_PORT; + if (identical && qobject_cast(model)) { + QModelIndex tstA, tstB; + if (source_left.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR) { + portColumn = ConversationDataModel::CONV_COLUMN_SRC_PORT; + int col = ConversationDataModel::CONV_COLUMN_DST_ADDR; + tstA = model->index(source_left.row(), col); + tstB = model->index(source_right.row(), col); + } else if (source_left.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) { + portColumn = ConversationDataModel::CONV_COLUMN_DST_PORT; + int col = ConversationDataModel::CONV_COLUMN_SRC_ADDR; + tstA = model->index(source_left.row(), col); + tstB = model->index(source_right.row(), col); + } + + if (addressTypeA == AT_IPv4) { + quint32 valX = model->data(tstA, ATapDataModel::DATA_IPV4_INTEGER).value(); + quint32 valY = model->data(tstB, ATapDataModel::DATA_IPV4_INTEGER).value(); + + result = valX < valY; + identical = valX == valY; + } else { + result = QString::compare(model->data(tstA).toString().toLower(), model->data(tstB).toString(), Qt::CaseInsensitive) < 0; + identical = QString::compare(model->data(tstA).toString().toLower(), model->data(tstB).toString(), Qt::CaseInsensitive) == 0; + } + } + + if (! result && identical && ! model->portsAreHidden()) { + int portA = model->data(model->index(source_left.row(), portColumn)).toInt(); + int portB = model->data(model->index(source_right.row(), portColumn)).toInt(); + return portA < portB; + } + } + + return result; + } + + return QSortFilterProxyModel::lessThan(source_left, source_right); +} + +bool TrafficDataFilterProxy::filterAcceptsColumn(int source_column, const QModelIndex &) const +{ + if (hideColumns_.contains(source_column)) + return false; + + ATapDataModel * model = qobject_cast(sourceModel()); + if (model) { + if (model->portsAreHidden()) { + if (qobject_cast(model) && source_column == EndpointDataModel::ENDP_COLUMN_PORT) + return false; + if (qobject_cast(model) && + (source_column == ConversationDataModel::CONV_COLUMN_SRC_PORT || source_column == ConversationDataModel::CONV_COLUMN_DST_PORT)) + return false; + } + if (! model->showTotalColumn()) { + if (qobject_cast(model) && + (source_column == EndpointDataModel::ENDP_COLUMN_PACKETS_TOTAL || source_column == EndpointDataModel::ENDP_COLUMN_BYTES_TOTAL)) + return false; + if (qobject_cast(model) && + (source_column == ConversationDataModel::CONV_COLUMN_PACKETS_TOTAL || source_column == ConversationDataModel::CONV_COLUMN_BYTES_TOTAL)) + return false; + } + if (qobject_cast(model)) { + ConversationDataModel * convModel = qobject_cast(model); + if (source_column == ConversationDataModel::CONV_COLUMN_CONV_ID && ! convModel->showConversationId()) + return false; + } + } + + return true; +} + +void TrafficDataFilterProxy::setColumnVisibility(int column, bool visible) +{ + hideColumns_.removeAll(column); + if (!visible) + hideColumns_.append(column); + invalidateFilter(); +} + +bool TrafficDataFilterProxy::columnVisible(int column) const +{ + return ! hideColumns_.contains(column); +} + + +TrafficTree::TrafficTree(QString baseName, GList ** recentColumnList, QWidget *parent) : + QTreeView(parent) +{ + _tapEnabled = true; + _saveRaw = true; + _baseName = baseName; + _exportRole = ATapDataModel::UNFORMATTED_DISPLAYDATA; + _header = nullptr; + + setAlternatingRowColors(true); + setRootIsDecorated(false); + setSortingEnabled(true); + setContextMenuPolicy(Qt::CustomContextMenu); + + _header = new TrafficTreeHeaderView(recentColumnList); + setHeader(_header); + + connect(_header, &TrafficTreeHeaderView::columnsHaveChanged, this, &TrafficTree::columnsHaveChanged); + connect(this, &QTreeView::customContextMenuRequested, this, &TrafficTree::customContextMenu); +} + +void TrafficTree::setModel(QAbstractItemModel * model) +{ + if (model) { + TrafficDataFilterProxy * proxy = qobject_cast(model); + if (proxy) { + connect(_header, &TrafficTreeHeaderView::filterOnColumn, proxy, &TrafficDataFilterProxy::filterForColumn); + } + } + + QTreeView::setModel(model); +} + +void TrafficTree::tapListenerEnabled(bool enable) +{ + _tapEnabled = enable; +} + +ATapDataModel * TrafficTree::dataModel() +{ + QSortFilterProxyModel * proxy = qobject_cast(model()); + if (proxy) + return qobject_cast(proxy->sourceModel()); + return nullptr; +} + +void TrafficTree::customContextMenu(const QPoint &pos) +{ + if (sender() != this) + return; + + QMenu * ctxMenu = new QMenu(this); + ctxMenu->setAttribute(Qt::WA_DeleteOnClose); + bool isConv = false; + + QModelIndex idx = indexAt(pos); + TrafficDataFilterProxy * proxy = qobject_cast(model()); + if (proxy) + idx = proxy->mapToSource(idx); + + ConversationDataModel * model = qobject_cast(dataModel()); + if (model) + isConv = true; + + ctxMenu->addMenu(createActionSubMenu(FilterAction::ActionApply, idx, isConv)); + ctxMenu->addMenu(createActionSubMenu(FilterAction::ActionPrepare, idx, isConv)); + ctxMenu->addMenu(createActionSubMenu(FilterAction::ActionFind, idx, isConv)); + ctxMenu->addMenu(createActionSubMenu(FilterAction::ActionColorize, idx, isConv)); + + ctxMenu->addSeparator(); + ctxMenu->addMenu(createCopyMenu()); + + ctxMenu->addSeparator(); + QAction * act = ctxMenu->addAction(tr("Resize all columns to content")); + connect(act, &QAction::triggered, this, &TrafficTree::resizeAction); + + ctxMenu->popup(mapToGlobal(pos)); +} + +static QMap fad_to_cd_; +static void initDirection() +{ + if (fad_to_cd_.count() == 0) { + fad_to_cd_[FilterAction::ActionDirectionAToFromB] = CONV_DIR_A_TO_FROM_B; + fad_to_cd_[FilterAction::ActionDirectionAToB] = CONV_DIR_A_TO_B; + fad_to_cd_[FilterAction::ActionDirectionAFromB] = CONV_DIR_A_FROM_B; + fad_to_cd_[FilterAction::ActionDirectionAToFromAny] = CONV_DIR_A_TO_FROM_ANY; + fad_to_cd_[FilterAction::ActionDirectionAToAny] = CONV_DIR_A_TO_ANY; + fad_to_cd_[FilterAction::ActionDirectionAFromAny] = CONV_DIR_A_FROM_ANY; + fad_to_cd_[FilterAction::ActionDirectionAnyToFromB] = CONV_DIR_ANY_TO_FROM_B; + fad_to_cd_[FilterAction::ActionDirectionAnyToB] = CONV_DIR_ANY_TO_B; + fad_to_cd_[FilterAction::ActionDirectionAnyFromB] = CONV_DIR_ANY_FROM_B; + } +} + +QMenu * TrafficTree::createActionSubMenu(FilterAction::Action cur_action, QModelIndex idx, bool isConversation) +{ + initDirection(); + + conv_item_t * conv_item = nullptr; + bool hasConvId = false; + if (isConversation) + { + ConversationDataModel * model = qobject_cast(dataModel()); + if (model) { + conv_item = model->itemForRow(idx.row()); + hasConvId = model->showConversationId(idx.row()); + } + } + + QMenu * subMenu = new QMenu(FilterAction::actionName(cur_action)); + subMenu->setEnabled(_tapEnabled); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + if (isConversation && conv_item) { + QMenu *subsubmenu = subMenu->addMenu(FilterAction::actionTypeName(at)); + if (hasConvId && (cur_action == FilterAction::ActionApply || cur_action == FilterAction::ActionPrepare)) { + QString filter = QString("%1.stream eq %2").arg(conv_item->ctype == CONVERSATION_TCP ? "tcp" : "udp").arg(conv_item->conv_id); + FilterAction * act = new FilterAction(subsubmenu, cur_action, at, tr("Filter on stream id")); + act->setProperty("filter", filter); + subsubmenu->addAction(act); + connect(act, &QAction::triggered, this, &TrafficTree::useFilterAction); + } + foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { + FilterAction *fa = new FilterAction(subsubmenu, cur_action, at, ad); + QString filter = get_conversation_filter(conv_item, (conv_direction_e) fad_to_cd_[fa->actionDirection()]); + fa->setProperty("filter", filter); + subsubmenu->addAction(fa); + connect(fa, &QAction::triggered, this, &TrafficTree::useFilterAction); + } + } else { + FilterAction *fa = new FilterAction(subMenu, cur_action, at); + fa->setProperty("filter", idx.data(ATapDataModel::DISPLAY_FILTER)); + subMenu->addAction(fa); + + connect(fa, &QAction::triggered, this, &TrafficTree::useFilterAction); + } + } + + return subMenu; +} + +QMenu * TrafficTree::createCopyMenu(QWidget *parent) +{ + QMenu *copy_menu = new QMenu(tr("Copy %1 table").arg(_baseName), parent); + QAction *ca; + ca = copy_menu->addAction(tr("as CSV")); + ca->setToolTip(tr("Copy all values of this page to the clipboard in CSV (Comma Separated Values) format.")); + ca->setProperty("copy_as", TrafficTree::CLIPBOARD_CSV); + connect(ca, &QAction::triggered, this, &TrafficTree::clipboardAction); + ca = copy_menu->addAction(tr("as YAML")); + ca->setToolTip(tr("Copy all values of this page to the clipboard in the YAML data serialization format.")); + ca->setProperty("copy_as", TrafficTree::CLIPBOARD_YAML); + connect(ca, &QAction::triggered, this, &TrafficTree::clipboardAction); + ca = copy_menu->addAction(tr("as JSON")); + ca->setToolTip(tr("Copy all values of this page to the clipboard in the JSON data serialization format.")); + ca->setProperty("copy_as", TrafficTree::CLIPBOARD_JSON); + connect(ca, &QAction::triggered, this, &TrafficTree::clipboardAction); + + copy_menu->addSeparator(); + ca = copy_menu->addAction(tr("Save data as raw")); + ca->setToolTip(tr("Disable data formatting for export/clipboard and save as raw data")); + ca->setCheckable(true); + ca->setChecked(_exportRole == ATapDataModel::UNFORMATTED_DISPLAYDATA); + connect(ca, &QAction::triggered, this, &TrafficTree::toggleSaveRawAction); + + return copy_menu; +} + +void TrafficTree::useFilterAction() +{ + FilterAction *fa = qobject_cast(sender()); + if (!fa || !_tapEnabled) + return; + + QString filter = fa->property("filter").toString(); + if (filter.length() > 0) + { + MainWindow * mainWin = (MainWindow *)(mainApp->mainWindow()); + mainWin->setDisplayFilter(filter, fa->action(), fa->actionType()); + } +} + +void TrafficTree::clipboardAction() +{ + QAction * ca = qobject_cast(sender()); + if (ca && ca->property("copy_as").isValid()) + copyToClipboard((eTrafficTreeClipboard)ca->property("copy_as").toInt()); +} + +void TrafficTree::resizeAction() +{ + for (int col = 0; col < model()->columnCount(); col++) + resizeColumnToContents(col); +} + +void TrafficTree::toggleSaveRawAction() +{ + if (_exportRole == ATapDataModel::UNFORMATTED_DISPLAYDATA) + _exportRole = Qt::DisplayRole; + else + _exportRole = ATapDataModel::UNFORMATTED_DISPLAYDATA; +} + +void TrafficTree::copyToClipboard(eTrafficTreeClipboard type) +{ + if (!model()) + return; + + QString clipText; + QTextStream stream(&clipText, QIODevice::Text); + + if (type == CLIPBOARD_CSV) { + QMap headers; + QStringList rdsl; + for (int cnt = 0; cnt < model()->columnCount(); cnt++) + { + rdsl << model()->headerData(cnt, Qt::Horizontal, Qt::DisplayRole).toString(); + } + stream << rdsl.join(",") << "\n"; + + for (int row = 0; row < model()->rowCount(); row++) { + rdsl.clear(); + for (int col = 0; col < model()->columnCount(); col++) { + QModelIndex idx = model()->index(row, col); + QVariant v = model()->data(idx, _exportRole); + if (!v.isValid()) { + rdsl << "\"\""; + } else if (v.userType() == QMetaType::QString) { + rdsl << QString("\"%1\"").arg(v.toString()); + } else { + rdsl << v.toString(); + } + } + stream << rdsl.join(",") << '\n'; + } + } else if (type == CLIPBOARD_YAML) { + stream << "---" << '\n'; + QMap headers; + for (int cnt = 0; cnt < model()->columnCount(); cnt++) + headers.insert(cnt, model()->headerData(cnt, Qt::Horizontal, Qt::DisplayRole).toString()); + + for (int row = 0; row < model()->rowCount(); row++) { + stream << "-" << '\n'; + for (int col = 0; col < model()->columnCount(); col++) { + QModelIndex idx = model()->index(row, col); + QVariant v = model()->data(idx, _exportRole); + stream << " - " << headers[col] << ": " << v.toString() << '\n'; + } + } + } else if (type == CLIPBOARD_JSON) { + QMap headers; + for (int cnt = 0; cnt < model()->columnCount(); cnt++) + headers.insert(cnt, model()->headerData(cnt, Qt::Horizontal, Qt::DisplayRole).toString()); + + QJsonArray records; + + for (int row = 0; row < model()->rowCount(); row++) { + QJsonObject rowData; + foreach(int col, headers.keys()) { + QModelIndex idx = model()->index(row, col); + rowData.insert(headers[col], model()->data(idx, _exportRole).toString()); + } + records.push_back(rowData); + } + + QJsonDocument json; + json.setArray(records); + stream << json.toJson(); + } + + mainApp->clipboard()->setText(stream.readAll()); +} + +void TrafficTree::disableTap() +{ + ATapDataModel * model = dataModel(); + if (!model) + return; + model->disableTap(); +} + +void TrafficTree::applyRecentColumns() +{ + if (_header) + _header->applyRecent(); +} + +void TrafficTree::columnsChanged(QList columns) +{ + TrafficDataFilterProxy * proxy = qobject_cast(model()); + if (!proxy) + return; + + for (int col = 0; col < dataModel()->columnCount(); col++) { + proxy->setColumnVisibility(col, columns.contains(col)); + } + + resizeAction(); +} diff --git a/ui/qt/widgets/traffic_tree.h b/ui/qt/widgets/traffic_tree.h new file mode 100644 index 00000000..5bc87e91 --- /dev/null +++ b/ui/qt/widgets/traffic_tree.h @@ -0,0 +1,177 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TRAFFIC_TREE_H +#define TRAFFIC_TREE_H + +#include "config.h" + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +class MenuEditAction : public QWidgetAction +{ + Q_OBJECT +public: + MenuEditAction(QString text, QString hintText, QObject * parent = nullptr); + + QString text() const; + +protected: + virtual QWidget * createWidget(QWidget *parent); +private: + QString _hintText; + QString _text; + QLineEdit * _lineEdit; + +private slots: + void triggerEntry(); +}; + + +class TrafficTreeHeaderView : public QHeaderView +{ + Q_OBJECT +public: + TrafficTreeHeaderView(GList ** recentColumnList, QWidget * parent = nullptr); + ~TrafficTreeHeaderView(); + + void applyRecent(); + +signals: + void columnsHaveChanged(QList visible); + void filterOnColumn(int column, int filterOn, QString filterText); +private: + GList ** _recentColumnList; + QActionGroup * _actions; + QString _filterText; + +private slots: + void headerContextMenu(const QPoint &pos); + void columnTriggered(bool checked = false); + void menuActionTriggered(QAction *); + void filterColumn(bool checked = false); + +}; + + +class TrafficDataFilterProxy : public QSortFilterProxyModel +{ + Q_OBJECT +public: + + enum { + TRAFFIC_DATA_LESS, + TRAFFIC_DATA_GREATER, + TRAFFIC_DATA_EQUAL, + }; + + TrafficDataFilterProxy(QObject *parent = nullptr); + + void setColumnVisibility(int column, bool visible); + bool columnVisible(int column) const; + +public slots: + void filterForColumn(int column, int filterOn, QString filterText); + +protected: + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const; + virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + +private: + QList hideColumns_; + + int _filterColumn; + int _filterOn; + QString _filterText; + + int mapToSourceColumn(int proxyColumn) const; + +}; + + +class TrafficTree : public QTreeView +{ + Q_OBJECT + +public: + /** + * @brief Type for the selection of export + * @see copyToClipboard + */ + typedef enum { + CLIPBOARD_CSV, /* export as CSV */ + CLIPBOARD_YAML, /* export as YAML */ + CLIPBOARD_JSON /* export as JSON */ + } eTrafficTreeClipboard; + + TrafficTree(QString baseName, GList ** recentColumnList, QWidget *parent = nullptr); + + /** + * @brief Create a menu containing clipboard copy entries for this tab + * + * It will create all entries, including copying the content of the currently selected tab + * to CSV, YAML and JSON + * + * @param parent the parent object or null + * @return QMenu* the resulting menu or null + */ + QMenu * createCopyMenu(QWidget * parent = nullptr); + + void applyRecentColumns(); + + virtual void setModel(QAbstractItemModel *model) override; + +signals: + void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + void columnsHaveChanged(QList columns); + +public slots: + void tapListenerEnabled(bool enable); + void disableTap(); + void columnsChanged(QList columns); + +private: + bool _tapEnabled; + int _exportRole; + bool _saveRaw; + QString _baseName; + + TrafficTreeHeaderView * _header; + + ATapDataModel * dataModel(); + + QMenu * createActionSubMenu(FilterAction::Action cur_action, QModelIndex idx, bool isConversation); + void copyToClipboard(eTrafficTreeClipboard type); + + friend class TrafficTreeHeaderView; + +private slots: + void customContextMenu(const QPoint &pos); + void useFilterAction(); + void clipboardAction(); + void resizeAction(); + void toggleSaveRawAction(); +}; + +#endif // TRAFFIC_TREE_H diff --git a/ui/qt/widgets/traffic_types_list.cpp b/ui/qt/widgets/traffic_types_list.cpp new file mode 100644 index 00000000..30126ef9 --- /dev/null +++ b/ui/qt/widgets/traffic_types_list.cpp @@ -0,0 +1,284 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include + +#include + +#include + +TrafficTypesRowData::TrafficTypesRowData(int protocol, QString name) : + _protocol(protocol), + _name(name), + _checked(false) +{} + +int TrafficTypesRowData::protocol() const +{ + return _protocol; +} + +QString TrafficTypesRowData::name() const +{ + return _name; +} + +bool TrafficTypesRowData::checked() const +{ + return _checked; +} + +void TrafficTypesRowData::setChecked(bool checked) +{ + _checked = checked; +} + +static bool iterateProtocols(const void *key, void *value, void *userdata) +{ + QList * protocols = (QList *)userdata; + + register_ct_t* ct = (register_ct_t*)value; + const QString title = (const gchar*)key; + int proto_id = get_conversation_proto_id(ct); + TrafficTypesRowData entry(proto_id, title); + protocols->append(entry); + + return FALSE; +} + +TrafficTypesModel::TrafficTypesModel(GList ** recentList, QObject *parent) : + QAbstractListModel(parent), + _recentList(recentList) +{ + conversation_table_iterate_tables(iterateProtocols, &_allTaps); + + std::sort(_allTaps.begin(), _allTaps.end(), [](TrafficTypesRowData a, TrafficTypesRowData b) { + return a.name().compare(b.name(), Qt::CaseInsensitive) < 0; + }); + + QList _protocols; + + for (GList * endTab = *_recentList; endTab; endTab = endTab->next) { + int protoId = proto_get_id_by_short_name((const char *)endTab->data); + if (protoId > -1 && ! _protocols.contains(protoId)) + _protocols.append(protoId); + } + + if (_protocols.isEmpty()) { + QStringList protoNames = QStringList() << "eth" << "ip" << "ipv6" << "tcp" << "udp"; + foreach(QString name, protoNames) + _protocols << proto_get_id_by_filter_name(name.toStdString().c_str()); + } + + for(int cnt = 0; cnt < _allTaps.count(); cnt++) + { + _allTaps[cnt].setChecked(false); + if (_protocols.contains(_allTaps[cnt].protocol())) + _allTaps[cnt].setChecked(true); + } + +} + +int TrafficTypesModel::rowCount(const QModelIndex &) const +{ + return (int) _allTaps.count(); +} + +int TrafficTypesModel::columnCount(const QModelIndex &) const +{ + return TrafficTypesModel::COL_NUM; +} + +QVariant TrafficTypesModel::data(const QModelIndex &idx, int role) const +{ + if (!idx.isValid()) + return QVariant(); + + TrafficTypesRowData data = _allTaps[idx.row()]; + if (role == Qt::DisplayRole) + { + switch(idx.column()) + { + case(TrafficTypesModel::COL_NAME): + return data.name(); + case(TrafficTypesModel::COL_PROTOCOL): + return data.protocol(); + } + } else if (role == Qt::CheckStateRole && idx.column() == TrafficTypesModel::COL_CHECKED) { + return data.checked() ? Qt::Checked : Qt::Unchecked; + } else if (role == TrafficTypesModel::TRAFFIC_PROTOCOL) { + return data.protocol(); + } else if (role == TrafficTypesModel::TRAFFIC_IS_CHECKED) { + return (bool)data.checked(); + } + + return QVariant(); +} + +QVariant TrafficTypesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (section < 0 || role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + + if (section == TrafficTypesModel::COL_NAME) + return tr("Protocol"); + return QVariant(); +} + +Qt::ItemFlags TrafficTypesModel::flags (const QModelIndex & idx) const +{ + Qt::ItemFlags defaultFlags = QAbstractListModel::flags(idx); + if (idx.isValid()) + return defaultFlags | Qt::ItemIsUserCheckable; + + return defaultFlags; +} + +bool TrafficTypesModel::setData(const QModelIndex &idx, const QVariant &value, int role) +{ + if(!idx.isValid() || role != Qt::CheckStateRole) + return false; + + if (_allTaps.count() <= idx.row()) + return false; + + _allTaps[idx.row()].setChecked(value.toInt() == Qt::Checked); + + QList selected; + prefs_clear_string_list(*_recentList); + *_recentList = NULL; + + for (int cnt = 0; cnt < _allTaps.count(); cnt++) { + if (_allTaps[cnt].checked()) { + int protoId = _allTaps[cnt].protocol(); + selected.append(protoId); + char *title = g_strdup(proto_get_protocol_short_name(find_protocol_by_id(protoId))); + *_recentList = g_list_append(*_recentList, title); + } + } + + emit protocolsChanged(selected); + + emit dataChanged(idx, idx); + return true; +} + +void TrafficTypesModel::selectProtocols(QList protocols) +{ + beginResetModel(); + for (int cnt = 0; cnt < _allTaps.count(); cnt++) { + _allTaps[cnt].setChecked(false); + if (protocols.contains(_allTaps[cnt].protocol())) + _allTaps[cnt].setChecked(true); + } + endResetModel(); +} + + +TrafficListSortModel::TrafficListSortModel(QObject * parent) : + QSortFilterProxyModel(parent) +{} + +bool TrafficListSortModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + if (source_left.isValid() && source_left.column() == TrafficTypesModel::COL_NAME) { + QString valA = source_left.data().toString(); + QString valB = source_right.data().toString(); + return valA.compare(valB, Qt::CaseInsensitive) <= 0; + } + return QSortFilterProxyModel::lessThan(source_left, source_right); +} + +void TrafficListSortModel::setFilter(QString filter) +{ + if ( filter.compare(_filter) != 0 ) { + _filter = filter; + invalidateFilter(); + } +} + +bool TrafficListSortModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + if (sourceModel() && _filter.length() > 0) { + QModelIndex idx = sourceModel()->index(source_row, TrafficTypesModel::COL_NAME); + + if (idx.isValid()) { + QString name = idx.data().toString(); + if (name.contains(_filter, Qt::CaseInsensitive)) + return true; + return false; + } + } + + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} + + +TrafficTypesList::TrafficTypesList(QWidget *parent) : + QTreeView(parent) +{ + _name = QString(); + _model = nullptr; + _sortModel = nullptr; + + setAlternatingRowColors(true); + setRootIsDecorated(false); +} + +void TrafficTypesList::setProtocolInfo(QString name, GList ** recentList) +{ + _name = name; + + _sortModel = new TrafficListSortModel(this); + + _model = new TrafficTypesModel(recentList, this); + _sortModel->setSourceModel(_model); + setModel(_sortModel); + + setSortingEnabled(true); + sortByColumn(TrafficTypesModel::COL_NAME, Qt::AscendingOrder); + + connect(_model, &TrafficTypesModel::protocolsChanged, this, &TrafficTypesList::protocolsChanged); + + resizeColumnToContents(0); + resizeColumnToContents(1); +} + +void TrafficTypesList::selectProtocols(QList protocols) +{ + if (_model) { + _model->selectProtocols(protocols); + emit clearFilterList(); + } +} + +QList TrafficTypesList::protocols(bool onlySelected) const +{ + QList entries; + for (int cnt = 0; cnt < _model->rowCount(); cnt++) { + QModelIndex idx = _model->index(cnt, TrafficTypesModel::COL_CHECKED); + int protoId = _model->data(idx, TrafficTypesModel::TRAFFIC_PROTOCOL).toInt(); + if (protoId > 0 && ! entries.contains(protoId)) { + if (!onlySelected || _model->data(idx, TrafficTypesModel::TRAFFIC_IS_CHECKED).toBool()) + entries.append(protoId); + } + } + + return entries; +} + +void TrafficTypesList::filterList(QString filter) +{ + _sortModel->setFilter(filter); +} + diff --git a/ui/qt/widgets/traffic_types_list.h b/ui/qt/widgets/traffic_types_list.h new file mode 100644 index 00000000..00798c50 --- /dev/null +++ b/ui/qt/widgets/traffic_types_list.h @@ -0,0 +1,125 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TRAFFIC_TYPES_LIST_H +#define TRAFFIC_TYPES_LIST_H + +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include + +class TrafficTypesRowData +{ + +public: + TrafficTypesRowData(int protocol, QString name); + + int protocol() const; + QString name() const; + bool checked() const; + void setChecked(bool checked); + +private: + int _protocol; + QString _name; + bool _checked; +}; + + +class TrafficTypesModel : public QAbstractListModel +{ + Q_OBJECT +public: + + enum { + TRAFFIC_PROTOCOL = Qt::UserRole, + TRAFFIC_IS_CHECKED, + } eTrafficUserData; + + enum { + COL_CHECKED, + COL_NAME, + COL_NUM, + COL_PROTOCOL, + } eTrafficColumnNames; + + TrafficTypesModel(GList ** recentList, QObject *parent = nullptr); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + virtual bool setData(const QModelIndex &idx, const QVariant &value, int role) override; + virtual Qt::ItemFlags flags (const QModelIndex & idx) const override; + + QList protocols() const; + +public slots: + void selectProtocols(QList protocols); + +signals: + void protocolsChanged(QList protocols); + +private: + QList _allTaps; + GList ** _recentList; + +}; + + +class TrafficListSortModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + TrafficListSortModel(QObject * parent = nullptr); + + void setFilter(QString filter = QString()); + +protected: + virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + +private: + QString _filter; +}; + + +class TrafficTypesList : public QTreeView +{ + Q_OBJECT +public: + + TrafficTypesList(QWidget *parent = nullptr); + + void setProtocolInfo(QString name, GList ** recentList); + QList protocols(bool onlySelected = false) const; + +public slots: + void selectProtocols(QList protocols); + void filterList(QString); + +signals: + void protocolsChanged(QList protocols); + void clearFilterList(); + +private: + QString _name; + TrafficTypesModel * _model; + TrafficListSortModel * _sortModel; +}; + +#endif // TRAFFIC_TYPES_LIST_H \ No newline at end of file diff --git a/ui/qt/widgets/wireless_timeline.cpp b/ui/qt/widgets/wireless_timeline.cpp new file mode 100644 index 00000000..dcdcd4f8 --- /dev/null +++ b/ui/qt/widgets/wireless_timeline.cpp @@ -0,0 +1,646 @@ +/* wireless_timeline.cpp + * GUI to show an 802.11 wireless timeline of packets + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copyright 2012 Parc Inc and Samsung Electronics + * Copyright 2015, 2016 & 2017 Cisco Inc + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "wireless_timeline.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "globals.h" +#include + +#include +#include "frame_tvbuff.h" + +#include +#include +#include "main_application.h" +#include +#include + +#ifdef Q_OS_WIN +#include "wsutil/file_util.h" +#include +#endif + +#include +#include +#include +#include + +#include +#include "packet_list.h" +#include + +/* we start rendering this number of microseconds left of the left edge - to ensure + * NAV lines are drawn correctly, and that small errors in time order don't prevent some + * frames from being rendered. + * These errors in time order can come from generators that record PHY rate incorrectly + * in some circumstances. + */ +#define RENDER_EARLY 40000 + + +const float fraction = 0.8F; +const float base = 0.1F; +class pcolor : public QColor +{ +public: + inline pcolor(float red, float green, float blue) : QColor( + (int) (255*(red * fraction + base)), + (int) (255*(green * fraction + base)), + (int) (255*(blue * fraction + base))) { } +}; + +static void reset_rgb(float rgb[TIMELINE_HEIGHT][3]) +{ + int i; + for (i = 0; i < TIMELINE_HEIGHT; i++) + rgb[i][0] = rgb[i][1] = rgb[i][2] = 1.0; +} + +static void render_pixels(QPainter &p, gint x, gint width, float rgb[TIMELINE_HEIGHT][3], float ratio) +{ + int previous = 0, i; + for (i = 1; i <= TIMELINE_HEIGHT; i++) { + if (i != TIMELINE_HEIGHT && + rgb[previous][0] == rgb[i][0] && + rgb[previous][1] == rgb[i][1] && + rgb[previous][2] == rgb[i][2]) + continue; + if (rgb[previous][0] != 1.0 || rgb[previous][1] != 1.0 || rgb[previous][2] != 1.0) { + p.fillRect(QRectF(x/ratio, previous, width/ratio, i-previous), pcolor(rgb[previous][0],rgb[previous][1],rgb[previous][2])); + } + previous = i; + } + reset_rgb(rgb); +} + +static void render_rectangle(QPainter &p, gint x, gint width, guint height, int dfilter, float r, float g, float b, float ratio) +{ + p.fillRect(QRectF(x/ratio, TIMELINE_HEIGHT/2-height, width/ratio, dfilter ? height * 2 : height), pcolor(r,g,b)); +} + +static void accumulate_rgb(float rgb[TIMELINE_HEIGHT][3], int height, int dfilter, float width, float red, float green, float blue) +{ + int i; + for (i = TIMELINE_HEIGHT/2-height; i < (TIMELINE_HEIGHT/2 + (dfilter ? height : 0)); i++) { + rgb[i][0] = rgb[i][0] - width + width * red; + rgb[i][1] = rgb[i][1] - width + width * green; + rgb[i][2] = rgb[i][2] - width + width * blue; + } +} + + +void WirelessTimeline::mousePressEvent(QMouseEvent *event) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + start_x = last_x = event->position().x(); +#else + start_x = last_x = event->localPos().x(); +#endif +} + + +void WirelessTimeline::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() == Qt::NoButton) + return; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + qreal offset = event->position().x() - last_x; + last_x = event->position().x(); +#else + qreal offset = event->localPos().x() - last_x; + last_x = event->localPos().x(); +#endif + + qreal shift = ((qreal) (end_tsf - start_tsf))/width() * offset; + start_tsf -= shift; + end_tsf -= shift; + clip_tsf(); + + // TODO: scroll by moving pixels and redraw only exposed area + // render(p, ...) + // then update full widget only on release. + update(); +} + + +void WirelessTimeline::mouseReleaseEvent(QMouseEvent *event) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0) + QPointF localPos = event->position(); +#else + QPointF localPos = event->localPos(); +#endif + qreal offset = localPos.x() - start_x; + + /* if this was a drag, ignore it */ + if (std::abs(offset) > 3) + return; + + /* this was a click */ + guint num = find_packet(localPos.x()); + if (num == 0) + return; + + frame_data *fdata = frame_data_sequence_find(cfile.provider.frames, num); + if (!fdata->passed_dfilter && fdata->prev_dis_num > 0) + num = fdata->prev_dis_num; + + cf_goto_frame(&cfile, num); +} + + +void WirelessTimeline::clip_tsf() +{ + // did we go past the start of the file? + if (((gint64) start_tsf) < ((gint64) first->start_tsf)) { + // align the start of the file at the left edge + guint64 shift = first->start_tsf - start_tsf; + start_tsf += shift; + end_tsf += shift; + } + if (end_tsf > last->end_tsf) { + guint64 shift = end_tsf - last->end_tsf; + start_tsf -= shift; + end_tsf -= shift; + } +} + + +void WirelessTimeline::selectedFrameChanged(QList) +{ + if (isHidden()) + return; + + if (cfile.current_frame) { + struct wlan_radio *wr = get_wlan_radio(cfile.current_frame->num); + + guint left_margin = 0.9 * start_tsf + 0.1 * end_tsf; + guint right_margin = 0.1 * start_tsf + 0.9 * end_tsf; + guint64 half_window = (end_tsf - start_tsf)/2; + + if (wr) { + // are we to the left of the left margin? + if (wr->start_tsf < left_margin) { + // scroll the left edge back to the left margin + guint64 offset = left_margin - wr->start_tsf; + if (offset < half_window) { + // small movement; keep packet to margin + start_tsf -= offset; + end_tsf -= offset; + } else { + // large movement; move packet to center of window + guint64 center = (wr->start_tsf + wr->end_tsf)/2; + start_tsf = center - half_window; + end_tsf = center + half_window; + } + } else if (wr->end_tsf > right_margin) { + guint64 offset = wr->end_tsf - right_margin; + if (offset < half_window) { + start_tsf += offset; + end_tsf += offset; + } else { + guint64 center = (wr->start_tsf + wr->end_tsf)/2; + start_tsf = center - half_window; + end_tsf = center + half_window; + } + } + clip_tsf(); + + update(); + } + } +} + + +/* given an x position find which packet that corresponds to. + * if it's inter frame space the subsequent packet is returned */ +guint +WirelessTimeline::find_packet(qreal x_position) +{ + guint64 x_time = start_tsf + (x_position/width() * (end_tsf - start_tsf)); + + return find_packet_tsf(x_time); +} + +void WirelessTimeline::captureFileReadStarted(capture_file *cf) +{ + capfile = cf; + hide(); + // TODO: hide or grey the toolbar controls +} + +void WirelessTimeline::captureFileReadFinished() +{ + /* All frames must be included in packet list */ + if (cfile.count == 0 || g_hash_table_size(radio_packet_list) != cfile.count) + return; + + /* check that all frames have start and end tsf time and are reasonable time order. + * packet timing reference seems to be off a little on some generators, which + * causes frequent IFS values in the range 0 to -30. Some generators emit excessive + * data when an FCS error happens, and this results in the duration calculation for + * the error frame being excessively long. This can cause larger negative IFS values + * (-30 to -1000) for the subsequent frame. Ignore these cases, as they don't seem + * to impact the GUI too badly. If the TSF reference point is set wrong (TSF at + * start of frame when it is at the end) then larger negative offsets are often + * seen. Don't display the timeline in these cases. + */ + /* TODO: update GUI to handle captures with occasional frames missing TSF data */ + /* TODO: indicate error message to the user */ + for (guint32 n = 1; n < cfile.count; n++) { + struct wlan_radio *w = get_wlan_radio(n); + if (w->start_tsf == 0 || w->end_tsf == 0) { + QString err = tr("Packet number %1 does not include TSF timestamp, not showing timeline.").arg(n); + mainApp->pushStatus(MainApplication::TemporaryStatus, err); + return; + } + if (w->ifs < -RENDER_EARLY) { + QString err = tr("Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong?").arg(n); + mainApp->pushStatus(MainApplication::TemporaryStatus, err); + return; + } + } + + first = get_wlan_radio(1); + last = get_wlan_radio(cfile.count); + + start_tsf = first->start_tsf; + end_tsf = last->end_tsf; + + /* TODO: only reset the zoom level if the file is changed, not on redissection */ + zoom_level = 0; + + show(); + selectedFrameChanged(QList()); + // TODO: show or ungrey the toolbar controls + update(); +} + +void WirelessTimeline::appInitialized() +{ + connect(qobject_cast(mainApp->mainWindow()), &MainWindow::framesSelected, this, &WirelessTimeline::selectedFrameChanged); + + GString *error_string; + error_string = register_tap_listener("wlan_radio_timeline", this, NULL, TL_REQUIRES_NOTHING, tap_timeline_reset, tap_timeline_packet, NULL/*tap_draw_cb tap_draw*/, NULL); + if (error_string) { + report_failure("Wireless Timeline - tap registration failed: %s", error_string->str); + g_string_free(error_string, TRUE); + } +} + +void WirelessTimeline::resizeEvent(QResizeEvent*) +{ + // TODO adjust scrollbar +} + + +// Calculate the x position on the GUI from the timestamp +int WirelessTimeline::position(guint64 tsf, float ratio) +{ + int position = -100; + + if (tsf != G_MAXUINT64) { + position = ((double) tsf - start_tsf)*width()*ratio/(end_tsf-start_tsf); + } + return position; +} + + +WirelessTimeline::WirelessTimeline(QWidget *parent) : QWidget(parent) +{ + setHidden(true); + zoom_level = 1.0; + setFixedHeight(TIMELINE_HEIGHT); + first_packet = 1; + setMouseTracking(true); + start_x = 0; + last_x = 0; + packet_list = NULL; + start_tsf = 0; + end_tsf = 0; + first = NULL; + last = NULL; + capfile = NULL; + + radio_packet_list = g_hash_table_new(g_direct_hash, g_direct_equal); + connect(mainApp, &MainApplication::appInitialized, this, &WirelessTimeline::appInitialized); +} + +WirelessTimeline::~WirelessTimeline() +{ + if (radio_packet_list != NULL) + { + g_hash_table_destroy(radio_packet_list); + } +} + +void WirelessTimeline::setPacketList(PacketList *packet_list) +{ + this->packet_list = packet_list; +} + +void WirelessTimeline::tap_timeline_reset(void* tapdata) +{ + WirelessTimeline* timeline = (WirelessTimeline*)tapdata; + + if (timeline->radio_packet_list != NULL) + { + g_hash_table_destroy(timeline->radio_packet_list); + } + timeline->hide(); + + timeline->radio_packet_list = g_hash_table_new(g_direct_hash, g_direct_equal); +} + +tap_packet_status WirelessTimeline::tap_timeline_packet(void *tapdata, packet_info* pinfo, epan_dissect_t* edt _U_, const void *data, tap_flags_t) +{ + WirelessTimeline* timeline = (WirelessTimeline*)tapdata; + const struct wlan_radio *wlan_radio_info = (const struct wlan_radio *)data; + + /* Save the radio information in our own (GUI) hashtable */ + g_hash_table_insert(timeline->radio_packet_list, GUINT_TO_POINTER(pinfo->num), (gpointer)wlan_radio_info); + return TAP_PACKET_DONT_REDRAW; +} + +struct wlan_radio* WirelessTimeline::get_wlan_radio(guint32 packet_num) +{ + return (struct wlan_radio*)g_hash_table_lookup(radio_packet_list, GUINT_TO_POINTER(packet_num)); +} + +void WirelessTimeline::doToolTip(struct wlan_radio *wr, QPoint pos, int x) +{ + if (x < position(wr->start_tsf, 1.0)) { + QToolTip::showText(pos, QString("Inter frame space %1 " UTF8_MICRO_SIGN "s").arg(wr->ifs)); + } else { + QToolTip::showText(pos, QString("Total duration %1 " UTF8_MICRO_SIGN "s\nNAV %2 " UTF8_MICRO_SIGN "s") + .arg(wr->end_tsf-wr->start_tsf).arg(wr->nav)); + } +} + + +bool WirelessTimeline::event(QEvent *event) +{ + if (event->type() == QEvent::ToolTip) { + QHelpEvent *helpEvent = static_cast(event); + guint packet = find_packet(helpEvent->pos().x()); + if (packet) { + doToolTip(get_wlan_radio(packet), helpEvent->globalPos(), helpEvent->x()); + } else { + QToolTip::hideText(); + event->ignore(); + } + return true; + } + return QWidget::event(event); +} + + +void WirelessTimeline::wheelEvent(QWheelEvent *event) +{ + // "Most mouse types work in steps of 15 degrees, in which case the delta + // value is a multiple of 120; i.e., 120 units * 1/8 = 15 degrees" + double steps = event->angleDelta().y() / 120.0; + if (steps != 0.0) { + zoom_level += steps; + if (zoom_level < 0) zoom_level = 0; + if (zoom_level > TIMELINE_MAX_ZOOM) zoom_level = TIMELINE_MAX_ZOOM; +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + zoom(event->position().x() / width()); +#else + zoom(event->posF().x() / width()); +#endif + } +} + + +void WirelessTimeline::bgColorizationProgress(int first, int last) +{ + if (isHidden()) return; + + struct wlan_radio *first_wr = get_wlan_radio(first); + + struct wlan_radio *last_wr = get_wlan_radio(last-1); + + int x = position(first_wr->start_tsf, 1); + int x_end = position(last_wr->end_tsf, 1); + + update(x, 0, x_end-x+1, height()); +} + + +// zoom at relative position 0.0 <= x_fraction <= 1.0. +void WirelessTimeline::zoom(double x_fraction) +{ + /* adjust the zoom around the selected packet */ + guint64 file_range = last->end_tsf - first->start_tsf; + guint64 center = start_tsf + x_fraction * (end_tsf - start_tsf); + guint64 span = pow(file_range, 1.0 - zoom_level / TIMELINE_MAX_ZOOM); + start_tsf = center - span * x_fraction; + end_tsf = center + span * (1.0 - x_fraction); + clip_tsf(); + update(); +} + +int WirelessTimeline::find_packet_tsf(guint64 tsf) +{ + if (cfile.count < 1) + return 0; + + if (cfile.count < 2) + return 1; + + guint32 min_count = 1; + guint32 max_count = cfile.count-1; + + guint64 min_tsf = get_wlan_radio(min_count)->end_tsf; + guint64 max_tsf = get_wlan_radio(max_count)->end_tsf; + + for (;;) { + if (tsf >= max_tsf) + return max_count+1; + + if (tsf < min_tsf) + return min_count; + + guint32 middle = (min_count + max_count)/2; + if (middle == min_count) + return middle+1; + + guint64 middle_tsf = get_wlan_radio(middle)->end_tsf; + + if (tsf >= middle_tsf) { + min_count = middle; + min_tsf = middle_tsf; + } else { + max_count = middle; + max_tsf = middle_tsf; + } + }; +} + +void +WirelessTimeline::paintEvent(QPaintEvent *qpe) +{ + QPainter p(this); + + // painting is done in device pixels in the x axis, get the ratio here + float ratio = p.device()->devicePixelRatio(); + + unsigned int packet; + double zoom; + int last_x=-1; + int left = qpe->rect().left()*ratio; + int right = qpe->rect().right()*ratio; + float rgb[TIMELINE_HEIGHT][3]; + reset_rgb(rgb); + + zoom = ((double) width())/(end_tsf - start_tsf) * ratio; + + /* background is light grey */ + p.fillRect(0, 0, width(), TIMELINE_HEIGHT, QColor(240,240,240)); + + /* background of packets visible in packet_list is white */ + int top = packet_list->indexAt(QPoint(0,0)).row(); + int bottom = packet_list->indexAt(QPoint(0,packet_list->viewport()->height())).row(); + + frame_data * topData = packet_list->getFDataForRow(top); + frame_data * botData = packet_list->getFDataForRow(bottom); + if (! topData || ! botData) + return; + + int x1 = top == -1 ? 0 : position(get_wlan_radio(topData->num)->start_tsf, ratio); + int x2 = bottom == -1 ? width() : position(get_wlan_radio(botData->num)->end_tsf, ratio); + p.fillRect(QRectF(x1/ratio, 0, (x2-x1+1)/ratio, TIMELINE_HEIGHT), Qt::white); + + /* background of current packet is blue */ + if (cfile.current_frame) { + struct wlan_radio *wr = get_wlan_radio(cfile.current_frame->num); + if (wr) { + x1 = position(wr->start_tsf, ratio); + x2 = position(wr->end_tsf, ratio); + p.fillRect(QRectF(x1/ratio, 0, (x2-x1+1)/ratio, TIMELINE_HEIGHT), Qt::blue); + } + } + + QGraphicsScene qs; + for (packet = find_packet_tsf(start_tsf + left/zoom - RENDER_EARLY); packet <= cfile.count; packet++) { + frame_data *fdata = frame_data_sequence_find(cfile.provider.frames, packet); + struct wlan_radio *ri = get_wlan_radio(fdata->num); + float x, width, red, green, blue; + + if (ri == NULL) continue; + + gint8 rssi = ri->aggregate ? ri->aggregate->rssi : ri->rssi; + guint height = (rssi+100)/2; + gint end_nav; + + /* leave a margin above the packets so the selected packet can be seen */ + if (height > TIMELINE_HEIGHT/2-6) + height = TIMELINE_HEIGHT/2-6; + + /* ensure shortest packets are clearly visible */ + if (height < 2) + height = 2; + + /* skip frames we don't have start and end data for */ + /* TODO: show something, so it's clear a frame is missing */ + if (ri->start_tsf == 0 || ri->end_tsf == 0) + continue; + + x = ((gint64) (ri->start_tsf - start_tsf))*zoom; + /* is there a previous anti-aliased pixel to output */ + if (last_x >= 0 && ((int) x) != last_x) { + /* write it out now */ + render_pixels(p, last_x, 1, rgb, ratio); + last_x = -1; + } + + /* does this packet start past the right edge of the window? */ + if (x >= right) { + break; + } + + width = (ri->end_tsf - ri->start_tsf)*zoom; + if (width < 0) { + continue; + } + + /* is this packet completely to the left of the displayed area? */ + // TODO clip NAV line properly if we are displaying it + if ((x + width) < left) + continue; + + /* remember the first displayed packet */ + if (first_packet < 0) + first_packet = packet; + + if (fdata->color_filter) { + const color_t *c = &((const color_filter_t *) fdata->color_filter)->fg_color; + red = c->red / 65535.0; + green = c->green / 65535.0; + blue = c->blue / 65535.0; + } else { + red = green = blue = 0.0; + } + + /* record NAV field at higher magnifications */ + end_nav = x + width + ri->nav*zoom; + if (zoom >= 0.01 && ri->nav && end_nav > 0) { + gint y = 2*(packet % (TIMELINE_HEIGHT/2)); + qs.addLine(QLineF((x+width)/ratio, y, end_nav/ratio, y), QPen(pcolor(red,green,blue))); + } + + /* does this rectangle fit within one pixel? */ + if (((int) x) == ((int) (x+width))) { + /* accumulate it for later rendering together + * with all other sub pixels that fall within this + * pixel */ + last_x = x; + accumulate_rgb(rgb, height, fdata->passed_dfilter, width, red, green, blue); + } else { + /* it spans more than 1 pixel. + * first accumulate the part that does fit */ + float partial = ((int) x) + 1 - x; + accumulate_rgb(rgb, height, fdata->passed_dfilter, partial, red, green, blue); + /* and render it */ + render_pixels(p, (int) x, 1, rgb, ratio); + last_x = -1; + x += partial; + width -= partial; + /* are there any whole pixels of width left to draw? */ + if (width > 1.0) { + render_rectangle(p, x, width, height, fdata->passed_dfilter, red, green, blue, ratio); + x += (int) width; + width -= (int) width; + } + /* is there a partial pixel left */ + if (width > 0.0) { + last_x = x; + accumulate_rgb(rgb, height, fdata->passed_dfilter, width, red, green, blue); + } + } + } + + // draw the NAV lines last, so they appear on top of the packets + qs.render(&p, rect(), rect()); +} diff --git a/ui/qt/widgets/wireless_timeline.h b/ui/qt/widgets/wireless_timeline.h new file mode 100644 index 00000000..de43d123 --- /dev/null +++ b/ui/qt/widgets/wireless_timeline.h @@ -0,0 +1,103 @@ +/** @file + * + * GUI to show an 802.11 wireless timeline of packets + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * Copyright 2012 Parc Inc and Samsung Electronics + * Copyright 2015, 2016 & 2017 Cisco Inc + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#ifndef WIRELESSTIMELINE_H +#define WIRELESSTIMELINE_H + +#include + +#include + +#include + +#include "file.h" + +#include "ui/ws_ui_util.h" + +#include +#include +#include +#include + +#include + +#include + +#include "cfile.h" + +/* pixels height for rendered timeline */ +#define TIMELINE_HEIGHT 64 + +/* Maximum zoom levels for the timeline */ +#define TIMELINE_MAX_ZOOM 25.0 + +class WirelessTimeline; +class PacketList; + +class WirelessTimeline : public QWidget +{ + Q_OBJECT + +public: + explicit WirelessTimeline(QWidget *parent); + ~WirelessTimeline(); + void setPacketList(PacketList *packet_list); + void captureFileReadStarted(capture_file *cf); + void captureFileReadFinished(); + +protected: + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); + void mousePressEvent (QMouseEvent *event); + void mouseMoveEvent (QMouseEvent *event); + void mouseReleaseEvent (QMouseEvent *event); + bool event(QEvent *event); + void wheelEvent(QWheelEvent *event); + +public slots: + void bgColorizationProgress(int first, int last); + void appInitialized(); + +protected: + static void tap_timeline_reset(void* tapdata); + static tap_packet_status tap_timeline_packet(void *tapdata, packet_info* pinfo, epan_dissect_t* edt, const void *data, tap_flags_t flags); + + struct wlan_radio* get_wlan_radio(guint32 packet_num); + + void clip_tsf(); + int position(guint64 tsf, float ratio); + int find_packet_tsf(guint64 tsf); + void doToolTip(struct wlan_radio *wr, QPoint pos, int x); + void zoom(double x_fraction); + double zoom_level; + qreal start_x, last_x; + PacketList *packet_list; + guint find_packet(qreal x); + float rgb[TIMELINE_HEIGHT][3]; + + guint64 start_tsf; + guint64 end_tsf; + int first_packet; /* first packet displayed */ + struct wlan_radio *first, *last; + capture_file *capfile; + + GHashTable* radio_packet_list; + +protected slots: + void selectedFrameChanged(QList); +}; + +#endif // WIRELESS_TIMELINE_H diff --git a/ui/qt/widgets/wireshark_file_dialog.cpp b/ui/qt/widgets/wireshark_file_dialog.cpp new file mode 100644 index 00000000..acf9c7db --- /dev/null +++ b/ui/qt/widgets/wireshark_file_dialog.cpp @@ -0,0 +1,80 @@ +/* wireshark_file_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "wireshark_file_dialog.h" + +#ifdef Q_OS_WIN +#include +#include "ui/packet_range.h" +#include "ui/win32/file_dlg_win32.h" +#endif // Q_OS_WIN + + +WiresharkFileDialog::WiresharkFileDialog(QWidget *parent, const QString &caption, const QString &directory, const QString &filter) : + QFileDialog(parent, caption, directory, filter) +{ +#ifdef Q_OS_MAC + // Add /Volumes to the sidebar. We might want to call + // setFilter(QDir::Hidden | QDir::AllEntries) in addition to or instead + // of this as recommended in QTBUG-6805 and QTBUG-6875, but you can + // access hidden files in the Qt file dialog by right-clicking on the + // file list or simply typing in the path in the "File name:" entry. + + QList sb_urls = sidebarUrls(); + bool have_volumes = false; + QString volumes = "/Volumes"; + foreach (QUrl sbu, sb_urls) { + if (sbu.toLocalFile() == volumes) { + have_volumes = true; + } + } + if (! have_volumes) { + sb_urls << QUrl::fromLocalFile(volumes); + setSidebarUrls(sb_urls); + } +#endif +} + +QString WiresharkFileDialog::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, Options options) +{ +#ifdef Q_OS_WIN + HANDLE da_ctx = set_thread_per_monitor_v2_awareness(); +#endif + QString ed = QFileDialog::getExistingDirectory(parent, caption, dir, options); +#ifdef Q_OS_WIN + revert_thread_per_monitor_v2_awareness(da_ctx); +#endif + return ed; +} + +QString WiresharkFileDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options) +{ +#ifdef Q_OS_WIN + HANDLE da_ctx = set_thread_per_monitor_v2_awareness(); +#endif + QString ofn = QFileDialog::getOpenFileName(parent, caption, dir, filter, selectedFilter, options); +#ifdef Q_OS_WIN + revert_thread_per_monitor_v2_awareness(da_ctx); +#endif + return ofn; +} + +QString WiresharkFileDialog::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options) +{ +#ifdef Q_OS_WIN + HANDLE da_ctx = set_thread_per_monitor_v2_awareness(); +#endif + QString sfn = QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options); +#ifdef Q_OS_WIN + revert_thread_per_monitor_v2_awareness(da_ctx); +#endif + return sfn; +} diff --git a/ui/qt/widgets/wireshark_file_dialog.h b/ui/qt/widgets/wireshark_file_dialog.h new file mode 100644 index 00000000..43ac67a6 --- /dev/null +++ b/ui/qt/widgets/wireshark_file_dialog.h @@ -0,0 +1,38 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WIRESHARK_FILE_DIALOG_H +#define WIRESHARK_FILE_DIALOG_H + +#include + +/** + * @brief The WiresharkFileDialog class + * + * Qt <= 5.9 supports setting old (Windows 8.1) per-monitor DPI awareness + * via Qt:AA_EnableHighDpiScaling. We do this in main.cpp. In order for + * native dialogs to be rendered correctly we need to set per-monitor + * *v2* awareness prior to creating the dialog. + * Qt doesn't render correctly when per-monitor v2 awareness is enabled, so + * we need to revert our thread context when we're done. + * The class functions below are simple wrappers around their QFileDialog + * equivalents that set PMv2 awareness before showing native dialogs on + * Windows and resets it afterward. + */ + +class WiresharkFileDialog : public QFileDialog +{ +public: + WiresharkFileDialog(QWidget *parent = nullptr, const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString()); + static QString getExistingDirectory(QWidget *parent = Q_NULLPTR, const QString &caption = QString(), const QString &dir = QString(), Options options = ShowDirsOnly); + static QString getOpenFileName(QWidget *parent = Q_NULLPTR, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = Q_NULLPTR, Options options = Options()); + static QString getSaveFileName(QWidget *parent = Q_NULLPTR, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = Q_NULLPTR, Options options = Options()); +}; + +#endif // WIRESHARK_FILE_DIALOG_H diff --git a/ui/qt/wireless_frame.cpp b/ui/qt/wireless_frame.cpp new file mode 100644 index 00000000..2247b113 --- /dev/null +++ b/ui/qt/wireless_frame.cpp @@ -0,0 +1,393 @@ +/* wireless_frame.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "wireless_frame.h" +#include + +#include "config.h" + +#include + +#include +#include + +#include + +#include "ui/ws_ui_util.h" +#include +#include +#include "main_application.h" + +#include +#include + +// To do: +// - Disable or hide invalid channel types. +// - Push more status messages ("switched to...") to the status bar. +// - Add a "Decrypt in the driver" checkbox? +// - Check for frequency and channel type changes. +// - Find something appropriate to run from the helperToolButton on Linux. + +// Questions: +// - From our perspective, what's the difference between "NOHT" and "HT20"? + +const int update_interval_ = 1500; // ms + +WirelessFrame::WirelessFrame(QWidget *parent) : + QFrame(parent), + ui(new Ui::WirelessFrame), + interfaces_(NULL), + capture_in_progress_(false), + iface_timer_id_(-1) +{ + ui->setupUi(this); + + ui->helperToolButton->hide(); + + if (ws80211_init() == WS80211_INIT_OK) { + ui->stackedWidget->setEnabled(true); + ui->stackedWidget->setCurrentWidget(ui->interfacePage); + +#ifdef HAVE_AIRPCAP + // We should arguably add ws80211_get_helper_name and ws80211_get_helper_tooltip. + // This works for now and is translatable. + ui->helperToolButton->setText(tr("AirPcap Control Panel")); + ui->helperToolButton->setToolTip(tr("Open the AirPcap Control Panel")); + ui->helperToolButton->show(); + ui->helperToolButton->setEnabled(ws80211_get_helper_path() != NULL); +#endif + + } else { + ui->stackedWidget->setEnabled(false); + ui->stackedWidget->setCurrentWidget(ui->noWirelessPage); + } + + ui->fcsFilterFrame->setVisible(ws80211_has_fcs_filter()); + + updateInterfaceList(); + connect(mainApp, &MainApplication::localInterfaceEvent, + this, &WirelessFrame::handleInterfaceEvent); +} + +WirelessFrame::~WirelessFrame() +{ + ws80211_free_interfaces(interfaces_); + delete ui; +} + +void WirelessFrame::setCaptureInProgress(bool capture_in_progress) +{ + capture_in_progress_ = capture_in_progress; + updateWidgets(); +} + + +int WirelessFrame::startTimer(int interval) +{ + if (iface_timer_id_ != -1) { + killTimer(iface_timer_id_); + iface_timer_id_ = -1; + } + iface_timer_id_ = QFrame::startTimer(interval); + return iface_timer_id_; +} + +void WirelessFrame::handleInterfaceEvent(const char *ifname _U_, int added, int up _U_) +{ + if (!added) { + // Unfortunately when an interface removed event is received the network + // interface is still present for a while in the system. + // To overcome this update the interface list after a while. + startTimer(update_interval_); + } else { + updateInterfaceList(); + } +} + +void WirelessFrame::timerEvent(QTimerEvent *event) +{ + if (event->timerId() != iface_timer_id_) { + QFrame::timerEvent(event); + return; + } + killTimer(iface_timer_id_); + iface_timer_id_ = -1; + updateInterfaceList(); +} + +// Check to see if the ws80211 interface list matches the one in our +// combobox. Rebuild ours if necessary and select the first interface if +// the current selection goes away. +void WirelessFrame::updateInterfaceList() +{ + ws80211_free_interfaces(interfaces_); + interfaces_ = ws80211_find_interfaces(); + const QString old_iface = ui->interfaceComboBox->currentText(); + guint iface_count = 0; + bool list_changed = false; + + // Don't interfere with user activity. + if (ui->interfaceComboBox->view()->isVisible() + || ui->channelComboBox->view()->isVisible() + || ui->channelTypeComboBox->view()->isVisible() + || ui->fcsComboBox->view()->isVisible()) { + startTimer(update_interval_); + return; + } + + if (interfaces_ && interfaces_->len > 0) { + iface_count = interfaces_->len; + } + + if ((int) iface_count != ui->interfaceComboBox->count()) { + list_changed = true; + } else { + for (guint i = 0; i < iface_count; i++) { + struct ws80211_interface *iface = g_array_index(interfaces_, struct ws80211_interface *, i); + if (ui->interfaceComboBox->itemText(i).compare(iface->ifname) != 0) { + list_changed = true; + break; + } + } + } + + if (list_changed) { + ui->interfaceComboBox->clear(); + for (guint i = 0; i < iface_count; i++) { + struct ws80211_interface *iface = g_array_index(interfaces_, struct ws80211_interface *, i); + ui->interfaceComboBox->addItem(iface->ifname); + if (old_iface.compare(iface->ifname) == 0) { + ui->interfaceComboBox->setCurrentIndex(ui->interfaceComboBox->count() - 1); + } + } + } + + if (ui->interfaceComboBox->currentText().compare(old_iface) != 0) { + getInterfaceInfo(); + } +} + +void WirelessFrame::updateWidgets() +{ + bool enable_interface = false; + bool enable_channel = false; + bool enable_offset = false; + bool enable_show_fcs = false; + + if (ui->interfaceComboBox->count() > 0) { + enable_interface = true; + enable_show_fcs = true; + } + + if (enable_interface && ui->channelComboBox->count() > 0) { + enable_channel = true; + } + + if (enable_channel && ui->channelTypeComboBox->count() > 1) { + enable_offset = true; + } + + ui->interfaceComboBox->setEnabled(enable_interface); + ui->channelComboBox->setEnabled(enable_channel); + ui->channelTypeComboBox->setEnabled(enable_offset); + ui->fcsComboBox->setEnabled(!capture_in_progress_ && enable_show_fcs); +} + +void WirelessFrame::on_helperToolButton_clicked() +{ + const QString helper_path = ws80211_get_helper_path(); + if (helper_path.isEmpty()) return; + + QString command = QString("\"%1\"").arg(helper_path); + QProcess::startDetached(command, QStringList()); +} + +void WirelessFrame::on_prefsToolButton_clicked() +{ + emit showWirelessPreferences("wlan"); +} + +void WirelessFrame::getInterfaceInfo() +{ + const QString cur_iface = ui->interfaceComboBox->currentText(); + + ui->channelComboBox->clear(); + ui->channelTypeComboBox->clear(); + ui->fcsComboBox->clear(); + + if (cur_iface.isEmpty()) { + updateWidgets(); + return; + } + + for (guint i = 0; i < interfaces_->len; i++) { + struct ws80211_interface *iface = g_array_index(interfaces_, struct ws80211_interface *, i); + if (cur_iface.compare(iface->ifname) == 0) { + struct ws80211_iface_info iface_info; + QString units = " GHz"; + + ws80211_get_iface_info(iface->ifname, &iface_info); + + for (guint j = 0; j < iface->frequencies->len; j++) { + guint32 frequency = g_array_index(iface->frequencies, guint32, j); + double ghz = frequency / 1000.0; + QString chan_str = QString("%1 " UTF8_MIDDLE_DOT " %2%3") + .arg(ieee80211_mhz_to_chan(frequency)) + .arg(ghz, 0, 'f', 3) + .arg(units); + ui->channelComboBox->addItem(chan_str, frequency); + if ((int)frequency == iface_info.current_freq) { + ui->channelComboBox->setCurrentIndex(ui->channelComboBox->count() - 1); + } + units = QString(); + } + // XXX - Do we need to make a distinction between WS80211_CHAN_NO_HT + // and WS80211_CHAN_HT20? E.g. is there a driver that won't capture + // HT frames if you use WS80211_CHAN_NO_HT? + ui->channelTypeComboBox->addItem("20 MHz", WS80211_CHAN_NO_HT); + if (iface_info.current_chan_type == WS80211_CHAN_NO_HT || iface_info.current_chan_type == WS80211_CHAN_HT20) { + ui->channelTypeComboBox->setCurrentIndex(0); + } + if (iface->channel_types & (1 << WS80211_CHAN_HT40MINUS)) { + ui->channelTypeComboBox->addItem("HT 40-", WS80211_CHAN_HT40MINUS); + if (iface_info.current_chan_type == WS80211_CHAN_HT40MINUS) { + ui->channelTypeComboBox->setCurrentIndex(ui->channelTypeComboBox->count() - 1); + } + } + if (iface->channel_types & (1 << WS80211_CHAN_HT40PLUS)) { + ui->channelTypeComboBox->addItem("HT 40+", WS80211_CHAN_HT40PLUS); + if (iface_info.current_chan_type == WS80211_CHAN_HT40PLUS) { + ui->channelTypeComboBox->setCurrentIndex(ui->channelTypeComboBox->count() - 1); + } + } + if (iface->channel_types & (1 << WS80211_CHAN_VHT80)) { + ui->channelTypeComboBox->addItem("VHT 80", WS80211_CHAN_VHT80); + if (iface_info.current_chan_type == WS80211_CHAN_VHT80) { + ui->channelTypeComboBox->setCurrentIndex(ui->channelTypeComboBox->count() - 1); + } + } + if (iface->channel_types & (1 << WS80211_CHAN_VHT160)) { + ui->channelTypeComboBox->addItem("VHT 160", WS80211_CHAN_VHT160); + if (iface_info.current_chan_type == WS80211_CHAN_VHT160) { + ui->channelTypeComboBox->setCurrentIndex(ui->channelTypeComboBox->count() - 1); + } + } + + if (ws80211_has_fcs_filter()) { + ui->fcsComboBox->setCurrentIndex(iface_info.current_fcs_validation); + } + } + } + + updateWidgets(); +} + +void WirelessFrame::setInterfaceInfo() +{ + QString cur_iface = ui->interfaceComboBox->currentText(); + int cur_chan_idx = ui->channelComboBox->currentIndex(); + int cur_type_idx = ui->channelTypeComboBox->currentIndex(); + int cur_fcs_idx = ui->fcsComboBox->currentIndex(); + + if (cur_iface.isEmpty() || cur_chan_idx < 0 || cur_type_idx < 0) return; + + QString err_str; + +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) && defined(HAVE_LIBPCAP) + int frequency = ui->channelComboBox->itemData(cur_chan_idx).toInt(); + int chan_type = ui->channelTypeComboBox->itemData(cur_type_idx).toInt(); + int bandwidth = getBandwidthFromChanType(chan_type); + int center_freq = getCenterFrequency(frequency, bandwidth); + const gchar *chan_type_s = ws80211_chan_type_to_str(chan_type); + gchar *center_freq_s = NULL; + gchar *data, *primary_msg, *secondary_msg; + int ret; + + if (frequency < 0 || chan_type < 0) return; + + if (center_freq != -1) { + center_freq_s = g_strdup(QString::number(center_freq).toUtf8().constData()); + } + + ret = sync_interface_set_80211_chan(cur_iface.toUtf8().constData(), + QString::number(frequency).toUtf8().constData(), chan_type_s, + center_freq_s, NULL, + &data, &primary_msg, &secondary_msg, main_window_update); + + g_free(center_freq_s); + g_free(data); + g_free(primary_msg); + g_free(secondary_msg); + + /* Parse the error msg */ + if (ret) { + err_str = tr("Unable to set channel or offset."); + } +#elif defined(HAVE_AIRPCAP) + int frequency = ui->channelComboBox->itemData(cur_chan_idx).toInt(); + int chan_type = ui->channelTypeComboBox->itemData(cur_type_idx).toInt(); + if (frequency < 0 || chan_type < 0) return; + + if (ws80211_set_freq(cur_iface.toUtf8().constData(), frequency, chan_type, -1, -1) != 0) { + err_str = tr("Unable to set channel or offset."); + } +#endif + + if (cur_fcs_idx >= 0) { + if (ws80211_set_fcs_validation(cur_iface.toUtf8().constData(), (enum ws80211_fcs_validation) cur_fcs_idx) != 0) { + err_str = tr("Unable to set FCS validation behavior."); + } + } + + if (!err_str.isEmpty()) { + mainApp->pushStatus(MainApplication::TemporaryStatus, err_str); + } + + getInterfaceInfo(); +} + +int WirelessFrame::getCenterFrequency(int control_frequency, int bandwidth) +{ + if (bandwidth < 80 || control_frequency < 5180) + return -1; + + return ((control_frequency - 5180) / bandwidth) * bandwidth + 5180 + (bandwidth / 2) - 10; +} + +int WirelessFrame::getBandwidthFromChanType(int chan_type) +{ + switch (chan_type) { + case WS80211_CHAN_VHT80: + return 80; + case WS80211_CHAN_VHT160: + return 160; + default: + return -1; + } +} + +void WirelessFrame::on_interfaceComboBox_activated(int) +{ + getInterfaceInfo(); +} + +void WirelessFrame::on_channelComboBox_activated(int) +{ + setInterfaceInfo(); +} + +void WirelessFrame::on_channelTypeComboBox_activated(int) +{ + setInterfaceInfo(); +} + +void WirelessFrame::on_fcsComboBox_activated(int) +{ + setInterfaceInfo(); +} diff --git a/ui/qt/wireless_frame.h b/ui/qt/wireless_frame.h new file mode 100644 index 00000000..0d20a234 --- /dev/null +++ b/ui/qt/wireless_frame.h @@ -0,0 +1,65 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WIRELESS_FRAME_H +#define WIRELESS_FRAME_H + +#include + +#include + +namespace Ui { +class WirelessFrame; +} + +class WirelessFrame : public QFrame +{ + Q_OBJECT + +public: + explicit WirelessFrame(QWidget *parent = 0); + ~WirelessFrame(); + + void setCaptureInProgress(bool capture_in_progress); + +signals: + void showWirelessPreferences(const QString wlan_module_name); + +protected: + void timerEvent(QTimerEvent *event); + +public slots: + void handleInterfaceEvent(const char *ifname, int added, int up); + +private: + int startTimer(int interval); + void getInterfaceInfo(); + void setInterfaceInfo(); + int getCenterFrequency(int control_frequency, int bandwidth); + int getBandwidthFromChanType(int chan_type); + void updateInterfaceList(); + +private slots: + void updateWidgets(); + + void on_helperToolButton_clicked(); + void on_prefsToolButton_clicked(); + void on_interfaceComboBox_activated(int); + void on_channelComboBox_activated(int); + void on_channelTypeComboBox_activated(int); + void on_fcsComboBox_activated(int); + +private: + Ui::WirelessFrame *ui; + GArray *interfaces_; + bool capture_in_progress_; + int iface_timer_id_; +}; + +#endif // WIRELESS_FRAME_H diff --git a/ui/qt/wireless_frame.ui b/ui/qt/wireless_frame.ui new file mode 100644 index 00000000..1aa41d71 --- /dev/null +++ b/ui/qt/wireless_frame.ui @@ -0,0 +1,208 @@ + + + WirelessFrame + + + + 0 + 0 + 955 + 20 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + + + Interface + + + + + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 12 + 5 + + + + + + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + + + Channel + + + + + + + QComboBox::AdjustToContents + + + + + + + QComboBox::AdjustToContents + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 37 + 5 + + + + + + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + + + FCS Filter + + + + + + + + All Frames + + + + + Valid Frames + + + + + Invalid Frames + + + + + + + + + + + + + 0 + + + 0 + + + + + Wireless controls are not supported in this version of Wireshark. + + + + + + + + + + + Qt::Horizontal + + + + 40 + 5 + + + + + + + + External Helper + + + + + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + + + 802.11 Preferences + + + + + + + + diff --git a/ui/qt/wireshark_application.cpp b/ui/qt/wireshark_application.cpp new file mode 100644 index 00000000..7b37dabc --- /dev/null +++ b/ui/qt/wireshark_application.cpp @@ -0,0 +1,39 @@ +/* wireshark_application.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "wireshark_application.h" + +WiresharkApplication *wsApp = NULL; + +WiresharkApplication::WiresharkApplication(int &argc, char **argv) : + MainApplication(argc, argv) +{ + wsApp = this; + Q_INIT_RESOURCE(wsicon); + setApplicationName("Wireshark"); + setDesktopFileName(QStringLiteral("org.wireshark.Wireshark")); +} + +WiresharkApplication::~WiresharkApplication() +{ + wsApp = NULL; +} + +void WiresharkApplication::initializeIcons() +{ + // Do this as late as possible in order to allow time for + // MimeDatabaseInitThread to do its work. + QList icon_sizes = QList() << 16 << 24 << 32 << 48 << 64 << 128 << 256 << 512 << 1024; + foreach (int icon_size, icon_sizes) { + QString icon_path = QString(":/wsicon/wsicon%1.png").arg(icon_size); + normal_icon_.addFile(icon_path); + icon_path = QString(":/wsicon/wsiconcap%1.png").arg(icon_size); + capture_icon_.addFile(icon_path); + } +} diff --git a/ui/qt/wireshark_application.h b/ui/qt/wireshark_application.h new file mode 100644 index 00000000..5e8e15fd --- /dev/null +++ b/ui/qt/wireshark_application.h @@ -0,0 +1,27 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WIRESHARK_APPLICATION_H +#define WIRESHARK_APPLICATION_H + +#include + +class WiresharkApplication : public MainApplication +{ +public: + explicit WiresharkApplication(int &argc, char **argv); + ~WiresharkApplication(); + +private: + void initializeIcons() override; +}; + +extern WiresharkApplication *wsApp; + +#endif // WIRESHARK_APPLICATION_H diff --git a/ui/qt/wireshark_de.ts b/ui/qt/wireshark_de.ts new file mode 100644 index 00000000..02a47f25 --- /dev/null +++ b/ui/qt/wireshark_de.ts @@ -0,0 +1,14844 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + Über Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Programm zur Netzwerkprotokollanalyse</span> + + + Copy the version information to the clipboard + Versionsinformationen in die Zwischenablage kopieren + + + Copy to Clipboard + In die Zwischenablage kopieren + + + Authors + Autoren + + + Search Authors + Suche nach Autoren + + + Folders + Ordner + + + Filter by path + Nach Pfad filtern + + + Plugins + Plugins + + + No plugins found. + Keine Plugins gefunden. + + + Search Plugins + Plugins suchen + + + Filter by type: + Nach Typ filtern: + + + Keyboard Shortcuts + Tastaturkürzel + + + Search Shortcuts + Tastaturkürzel suchen + + + Acknowledgments + Bestätigungen + + + License + Lizenzen + + + The directory does not exist + Das Verzeichnis existiert nicht + + + Should the directory %1 be created? + Soll das Verzeichnis %1 erstellt werden? + + + The directory could not be created + Das Verzeichnis konnte nicht erstellt werden + + + The directory %1 could not be created. + Das Verzeichnis %1 konnte nicht erstellt werden! + + + Show in Finder + Im Finder anzeigen + + + Show in Folder + Im Ordner anzeigen + + + Copy + Kopieren + + + Copy Row(s) + + Zeile kopieren + Zeilen kopieren + + + + + AddressEditorFrame + + Frame + Frame + + + Name Resolution Preferences… + Name Resolution Preferences... + Einstellungen Namensauflösung... + + + Address: + Adresse: + + + Name: + Name: + + + Can't assign %1 to %2. + Kann %1 nicht an %2 zuweisen + + + + AdvancedPrefsModel + + Name + Name + + + Status + Status + + + Type + Typ + + + Value + Wert + + + + ApplyLineEdit + + Apply changes + Änderungen anwenden + + + + AuthorListModel + + Name + Name + + + Email + E-Mail + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Bluetooth ATT Server Attribute + + + Handle + Handle + + + UUID + UUID + + + UUID Name + UUID-Name + + + All Interfaces + Alle Schnittstellen + + + All Devices + Alle Geräte + + + Remove duplicates + Duplikate entfernen + + + Copy Cell + Zelle kopieren + + + Copy Rows + Zeilen kopieren + + + Copy All + Alles kopieren + + + Save as image + Als Bild speichern + + + Mark/Unmark Row + Zeile markieren / Markierung aufheben + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Zelle markieren / Markierung aufheben + + + Save Table Image + Tabelle als Grafik speichern + + + PNG Image (*.png) + PNG-Bild (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + Bluetooth Gerät + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Name + + + Class of Device + Geräteklasse + + + LMP Version + LMP-Version + + + LMP Subversion + LMP-Subversion + + + Manufacturer + Hersteller + + + HCI Version + HCI-Version + + + HCI Revision + HCI-Revision + + + Scan + Scan + + + Authentication + Authentifizierung + + + Encryption + Verschlüsselung + + + ACL MTU + ACL MTU + + + ACL Total Packets + ACL-Pakete gesamt + + + SCO MTU + SCO MTU + + + SCO Total Packets + SCO-Pakete gesamt + + + LE ACL MTU + LE ACL MTU + + + LE ACL Total Packets + LE ACL Pakete gesamt + + + LE ISO MTU + LE ISO MTU + + + LE ISO Total Packets + LE ISO Pakete gesamt + + + Inquiry Mode + Abfragemodus + + + Page Timeout + Seitenzeitüberschreitung + + + Simple Pairing Mode + Einfacher Kopplungsmodus + + + Voice Setting + Spracheinstellungen + + + Value + Wert + + + Changes + Änderungen + + + %1 changes + %1 Änderungen + + + Copy Cell + Zelle kopieren + + + Copy Rows + Zeilen kopieren + + + Copy All + Alles kopieren + + + Save as image + Als Bild speichern + + + Mark/Unmark Row + Zeile markieren / Markierung aufheben + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Zelle markieren / Markierung aufheben + + + Unknown + Unbekannt + + + Bluetooth Device - %1%2 + Bluetooth-Gerät - %1%2 + + + enabled + aktiviert + + + disabled + deaktiviert + + + %1 ms (%2 slots) + %1 ms (%2 Zeitfenster) + + + Save Table Image + Tabelle als Grafik speichern + + + PNG Image (*.png) + PNG-Bild (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Bluetooth-Geräte + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Name + + + LMP Version + LMP-Version + + + LMP Subversion + LMP-Subversion + + + Manufacturer + Hersteller + + + HCI Version + HCI-Version + + + HCI Revision + HCI-Revision + + + Is Local Adapter + Ist lokaler Adapter + + + All Interfaces + Alle Schnittstellen + + + Show information steps + Einzelne Schritte anzeigen + + + %1 items; Right click for more option; Double click for device details + %1 Elemente; Rechtsklick für weitere Optionen; Doppelklick für Details zum Gerät + + + Copy Cell + Zelle kopieren + + + Copy Rows + Zeilen kopieren + + + Copy All + Alles kopieren + + + Save as image + Als Bild speichern + + + Mark/Unmark Row + Zeile markieren / Markierung aufheben + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Zelle markieren / Markierung aufheben + + + true + Wahr + + + Save Table Image + Tabelle als Grafik speichern + + + PNG Image (*.png) + PNG-Bild (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Bluetooth HCI Zusammenfassung + + + Name + Name + + + OGF + OGF + + + OCF + OCF + + + Opcode + Opcode + + + Event + Ereignis + + + Subevent + Unterereignis + + + Status + Status + + + Reason + Ursache + + + Hardware Error + Hardwarefehler + + + Occurrence + Auftrittsreihenfolge + + + Link Control Commands + Link Control Kommandos + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Link Policy Kommandos + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Controller- & Baseband-Befehle + + + 0x03 + 0x03 + + + Informational Parameters + Informationsparameter + + + 0x04 + 0x04 + + + Status Parameters + Statusparameter + + + 0x05 + 0x05 + + + Testing Commands + Testing Kommandos + + + 0x06 + 0x06 + + + LE Controller Commands + LE Controller Kommandos + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + Bluetooth Logo Testing Kommandos + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Herstellerspezifische Befehle + + + 0x3F + 0x3F + + + Unknown OGF + Unbekanntes OGF + + + Events + Ereignisse + + + Hardware Errors + Hardwarefehler + + + Results filter: + Ergebnisfilter: + + + Display filter: + Anzeigefilter: + + + All Interfaces + Alle Schnittstellen + + + All Adapters + Alle Adapter + + + Copy Cell + Zelle kopieren + + + Copy Rows + Zeilen kopieren + + + Copy All + Alles kopieren + + + Save as image + Als Bild speichern + + + Mark/Unmark Row + Zeile markieren / Markierung aufheben + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Zelle markieren / Markierung aufheben + + + Unknown + Unbekannt + + + Adapter %1 + Adapter %1 + + + Frame %1 + Frame %1 + + + Pending + Ausstehend + + + Save Table Image + Tabelle als Grafik speichern + + + PNG Image (*.png) + PNG-Bild (*.png) + + + + ByteViewTab + + Packet bytes + Paketbytes + + + + ByteViewText + + Allow hover highlighting + Hover-Auswahl erlauben + + + Show bytes as hexadecimal + Bytes in Hexadezimal anzeigen + + + …as decimal + ...als Dezimal + + + …as octal + ...als Oktal + + + …as bits + …als Bits + + + Show text based on packet + Text basierend auf den Paketen anzeigen + + + …as ASCII + …als ASCII + + + …as EBCDIC + …als EBCDIC + + + + CaptureFile + + [closing] + [schließe] + + + [closed] + [geschlossen] + + + + CaptureFileDialog + + This capture file contains comments. + Diese Mitschnittdatei enthält Kommentare. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + Das ausgewählte Dateiformat unterstützt keine Kommentare. Möchten Sie den Mitschnitt in einem Dateiformat abspeichern das Kommentare unterstützt, oder wollen Sie die Kommentare verwerfen und im ausgewählten Format abspeichern? + + + Discard comments and save + Kommentare verwerfen und speichern + + + Save in another format + In einem anderen Format speichern + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + Kein mögliches Dateiformat unterstützt Kommentare. Möchten Sie die Kommentare verwerfen und in dem ausgewählten Format speichern? + + + All Files ( + Alle Dateien ( + + + All Capture Files + Alle Mitschnittdateien + + + Format: + Format: + + + Size: + Größe: + + + Start / elapsed: + Start / vergangen: + + + Automatically detect file type + Dateityp automatisch erkennen + + + %1, error after %Ln packet(s) + %1, error after %2 packets + + %1, Fehler nach %Ln Paket + %1, Fehler nach %Ln Paketen + + + + %1, timed out at %Ln packet(s) + %1, timed out at %2 packets + + %1, Zeitüberschreitung nach %Ln Paket + %1, Zeitüberschreitung nach %Ln Paketen + + + + %1, %Ln packet(s) + + %1, %Ln Paket + %1, %Ln Pakete + + + + Prepend packets + Pakete vorne einfügen + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Pakete aus der ausgewählten Datei vor der aktuellen Datei einfügen. Der Zeitstempel der Pakete wird dabei ignoriert. + + + Merge chronologically + Chronologisch zusammenführen + + + Insert packets in chronological order. + Pakete in chronologischer Reihenfolge einfügen + + + Append packets + Pakete anfügen + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Pakete aus der ausgewählten Datei nach der aktuellen Datei anfügen. Die Zeitstempel der Pakete werden ignoriert. + + + Read filter: + Lesefilter: + + + Compress with g&zip + Mit g&zip komprimieren + + + Open Capture File + Wireshark: Open Capture File + Mitschnittdatei öffnen + + + Save Capture File As + Wireshark: Save Capture File As + Mitschnittdatei speichern unter + + + Save as: + Speichern als: + + + Export Specified Packets + Wireshark: Export Specified Packets + Ausgewählte Pakete exportieren + + + Export as: + Exportieren als: + + + Merge Capture File + Wireshark: Merge Capture File + Mitschnittdatei zusammenführen + + + Unknown file type returned by save as dialog. + Speichern Dialog meldet einen unbekannten Dateityp + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Bitte melden Sie dies als ein Wireshark Issue unter https://gitlab.com/wireshark/wireshark/-/issues. + + + directory + Verzeichnis + + + unknown file format + unbekanntes Dateiformat + + + error opening file + Fehler beim Öffnen der Datei + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + %1, Fehler nach %Ln Daten Record + %1, Fehler nach %Ln Daten Records + + + + %1, timed out at %Ln data record(s) + + %1, Zeitüberschreitung nach %Ln Dateneintrag + %1, Zeitüberschreitung nach %Ln Dateneinträgen + + + + %1, %Ln data record(s) + + %1, %Ln Dateneintrag + %1, %Ln Dateneinträge + + + + unknown + unbekannt + + + + CaptureFilePropertiesDialog + + Details + Details + + + Capture file comments + Mitschnittdateikommentare + + + Refresh + Aktualisieren + + + Copy To Clipboard + In die Zwischenablage kopieren + + + Save Comments + Kommentar speichern + + + Capture File Properties + Mitschnittdateieigenschaften + + + Unknown + Unbekannt + + + File + Datei + + + Name + Name + + + Length + Länge + + + Hash (SHA256) + Hash (SHA256) + + + Hash (SHA1) + Hash (SHA1) + + + Format + Format + + + Encapsulation + Datenkapselung + + + Snapshot length + Schnappschusslänge + + + Time + Zeit + + + First packet + Erstes Paket + + + Last packet + Letztes Paket + + + Elapsed + Zeitspanne + + + Section %1 + Bereich %1 + + + Capture + Mitschnitt + + + Hardware + Hardware + + + OS + BS + + + Application + Applikation + + + Interfaces + Schnittstellen + + + Interface + Schnittstelle + + + Dropped packets + Verworfene Pakete + + + Capture filter + Mitschnittfilter + + + Link type + Linktyp + + + Packet size limit (snaplen) + Paketgrößenlimit (snaplen) + + + none + keine + + + %1 bytes + %1 Byte + + + Statistics + Statistik + + + Measurement + Messwerte + + + Captured + Aufgezeichnet + + + Displayed + Angezeigt + + + Marked + Markiert + + + Packets + Pakete + + + Time span, s + Zeitspanne, s + + + Average pps + Durchschnittliche pps + + + Average packet size, B + Durschnittliche Paketgröße, B + + + Bytes + Byte + + + Average bytes/s + Durschnittliche Byte/s + + + Average bits/s + Durschnittliche Bit/s + + + Section Comment + Kommentar Bereich + + + Packet Comments + Paketkommentar + + + <p>Frame %1: + <p>Frame %1: + + + Created by Wireshark %1 + + + Erstellt von Wireshark %1 + + + + + + CaptureFilterCombo + + Capture filter selector + Mitschnittfilterauswahl + + + + CaptureFilterEdit + + Capture filter entry + Mitschnittfilter Eintrag + + + Manage saved bookmarks. + Gespeicherte Lesezeichen verwalten + + + Apply this filter string to the display. + Diesen Filter zur Anzeige anwenden. + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + Mehrere Filter ausgewählt. Hier überschreiben oder leer lassen um die Filter beizubehalten. + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>Die ausgewählten Schnittstellen haben unterschiedliche Mitschnittfilter zugeordnet. Ein hier angegebener Filter überschreibt diese. Um die Filter beizubehalten ist keine Aktion notwendig.</p> + + + Enter a capture filter %1 + Geben Sie einen Mitschnittfilter ein %1 + + + Save this filter + Diesen Filter speichern + + + Remove this filter + Diesen Filter entfernen + + + Manage Capture Filters + Mitschnittfilter verwalten + + + + CaptureInfoDialog + + Capture Information + Mitschnittinformationen + + + Stop Capture + Aufzeichnen benden + + + %1 packets, %2:%3:%4 + %1 Pakete, %2:%3:%4 + + + + CaptureInfoModel + + Other + Weitere + + + + CaptureOptionsDialog + + Input + Eingabe + + + Interface + Schnittstelle + + + Traffic + Datenverkehr + + + Link-layer Header + Link-Layer Header + + + Promiscuous + Promiskuitiv + + + Snaplen (B) + Mitschnittlänge (B) + + + Buffer (MB) + Puffer (MB) + + + Monitor Mode + Überwachungsmodus + + + Capture Filter + Mitschnittfilter + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Vermutlich sollten Sie diese Option aktivieren. Normalerweise nimmt eine Netzwerkkarte nur Pakete auf, die an die eigene Adresse adressiert sind. Wenn Sie alle Pakete &quot;sehen&quot; wollen, sollten Sie diese Option aktivieren. In den FAQ stehen weitere Informationen zum Aufzeichnen von Paketen in einer Switch-Netzwerkumgebung.</p></body></html> + + + Enable promiscuous mode on all interfaces + Promiskuitiven Modus für alle Schnittstellen aktivieren + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + Schnittstellen anzeigen und ausblenden, Kommentare hinzufügen, Pipes und entfernte Schnittstellen verwalten. + + + Manage Interfaces… + Schnittstellen verwalten... + + + Capture filter for selected interfaces: + Mitschnittfilter für die ausgewählte Schnittstelle: + + + Compile BPFs + BPF kompilieren + + + Output + Ausgabe + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>Datei angeben, in die die aufgezeichneten Daten geschrieben werden. Standardmäßig wird eine temporäre Datei benutzt.</p></body></html> + + + Capture to a permanent file + In eine bleibende Datei aufzeichnen + + + File: + Datei: + + + Browse… + Öffnen... + + + Output format: + Ausgabeformat: + + + pcapng + pcapng + + + pcap + pcap + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>Anstatt einer einzelnen Datei werden mehrere Dateien erzeugt.</p><p>Der Dateiname enthält eine fortlaufende Nummer und die Startzeit des Mitschnitts.</p><p>Achtung: Es muss mindestens ein Kriterium für die neue Datei ausgewählt sein.</p></body></html> + + + Create a new file automatically… + Automatisch eine neue Datei erzeugen... + + + after + nach + + + Switch to the next file after the specified number of packets have been captured. + Zur nächsten Datei wechseln wenn die angegebene Anzahl an Paketen aufgezeichnet wurden. + + + packets + Pakete + + + Switch to the next file after the file size exceeds the specified file size. + Zur nächsten Datein wechseln wenn die angegebene Dateigröße erreicht wurde. + + + kilobytes + Kilobyte + + + megabytes + Megabyte + + + gigabytes + Gigabyte + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + Zur nächsten Datei wechseln wenn die angegebene Zeit vergangen ist. + + + seconds + Sekunden + + + minutes + Minuten + + + hours + Stunden + + + when time is a multiple of + wenn die Zeit ein Vielfaches von + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + Zur nächsten Datei wechseln wenn die Urzeit ein gerades Vielfaches des angegebenen Intervalls ist. +Um zum Beispiel eine neue Datei zu jeder vollen Stunde zu haben, 1 Stunde angeben. + + + compression + Komprimierung + + + None + Keine + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>Nachdem die Aufzeichnung in die nächste Datei gewechselt hat und die angegebene Anzahl an Dateien erreicht wurde, wird die älteste Datei entfernt.</p></body></html> + + + Use a ring buffer with + Einen Ringpuffer verwenden mit + + + files + Dateien + + + Options + Optionen + + + Display Options + Anzeigeoptionen + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>Mit dieser Option werden alle mitgeschnittenen Pakete sofort angezeigt. Dies kann das Aufzeichnen verlangsamen und so zu verworfenen Paketen führen.</p></body></html> + + + Update list of packets in real-time + Paketliste in Echtzeit aktualisieren + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>Hiermit wird in der &quot;Paketliste&quot; automatisch zum letzen Paket gescrollt wenn die Option &quot;Paketliste in Echtzeit aktualisieren&quot; aktiv ist.</p></body></html> + + + Automatically scroll during live capture + Automatisches Scrollen während des Mitschnitts + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>Mitschnittinformationen während der Aufzeichnung anzeigen.</p></body></html> + + + Show capture information during live capture + Mitschnittinformationen während der Aufzeichnung anzeigen + + + Name Resolution + Namensauflösung + + + Perform MAC layer name resolution while capturing. + Während des mitschneidens MAC Namensauflösung durchführen. + + + Resolve MAC addresses + MAC-Adressen auflösen + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>Namensauflösung während der Aufzeichnung durchführen.</p></body></html> + + + Resolve network names + Netzwerknamen auflösen + + + Perform transport layer name resolution while capturing. + Während des mitschneidens Transport Namensauflösung durchführen. + + + Resolve transport names + Transportschichtnamen auflösen + + + Stop capture automatically after… + Mitschnitt automatisch stoppen nach... + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>Mitschneiden beenden, nachdem die angegebene Anzahl an Paketen mitgeschnitten wurde.</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + Mitschneiden beenden, nachdem die angegebene Anzahl an Paketen mitgeschnitten wurde. + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>Mitschneiden beenden, nachdem die angegebene Anzahl an Paketen mitgeschnitten wurde.</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>Mitschneiden beenden, nachdem die angegebene Datenmenge mitgeschnitten wurde.</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + Mitschneiden beenden, nachdem die angegebene Datenmenge mitgeschnitten wurde. + + + Stop capturing after the specified amount of time has passed. + Mitschneiden beenden, nachdem die angegebene Zeit verstrichen ist. + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>Optional ein temporäres Verzeichnis für unbenannte Mitschnittdateien konfigurieren.</p></body></html> + + + Directory for temporary files + Verzeichnis für temporäre Dateien + + + Capture Options + Aufzeichnungsoptionen + + + Start + Start + + + Leave blank to use a temporary file + Leer lassen um eine temporäre Datei zu verwenden + + + Specify a Capture File + Eine Mitschnittdatei angeben + + + Specify temporary directory + Verzeichnis für temporäre Dateien definieren + + + %1: %2 + %1: %2 + + + Addresses + Adressen + + + Address + Adresse + + + no addresses + keine Adressen + + + Error + Fehler + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + Dateisatz: Angeforderte Dateigröße zu groß. Die Dateigröße kann nicht größer als 2 GiB sein. + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + Dateisatz: Keinen Dateinamen angeben. Wenn ein Dateisatz genutzt werden soll, muss ein Dateiname angegeben werden. + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + Dateisatz: Kein Limit angegeben. Es muss eine Dateigröße, ein Zeitintervall oder eine Paketanzahl definiert werden. + + + + CapturePreferencesFrame + + Frame + Frame + + + Default interface + Standardschnittstelle + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Vermutlich sollten Sie diese Option aktivieren. Normalerweise nimmt eine Netzwerkkarte nur Pakete auf, die an die eigene Adresse adressiert sind. Wenn Sie alle Pakete &quot;sehen&quot; wollen, sollten Sie diese Option aktivieren. In den FAQ stehen weitere Informationen zum Aufzeichnen von Paketen in einer Switch-Netzwerkumgebung.</p></body></html> + + + Capture packets in promiscuous mode + Pakete im promiskuitiven Modus mitschneiden + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Pakete im pcapng Dateiformat aufzeichnen.</p></body></html> + + + Capture packets in pcapng format + Pakete im pcap-ng Format mitschneiden + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Paketliste während dem Aufzeichnen aktualisieren. Dies kann bei einer hohen Bandbreite zu Paketverlusten führen.</p></body></html> + + + Update list of packets in real time + Paketliste in Echtzeit aktualisieren + + + Interval between updates (ms) + Aktualisierungsintervall (ms) + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + <html><head/><body><p>Häufigkeit in dem die GUI von der Aufzeichnung über neue Pakete informiert wird. Beeinflusst wie oft die GUI aktualisiert wird sowie die Granularität der Timer.</p></body></html> + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + <html><head/><body><p>Intervall zwischen Aktualisierung neuer Pakete. Beeinflusst wie oft die GUI aktualisiert wird sowie die Granularität der Timer.</p></body></html> + + + Don't load interfaces on startup + Interfaces nicht beim Start laden + + + Disable external capture interfaces + Externe Mitschnittschnittstellen deaktivieren + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + das "@" Symbol wird ignoriert. + + + + ColoringRulesDialog + + Dialog + Dialog + + + <small><i>A hint.</i></small> + <small><i>Hinweis</i></small> + + + Add a new coloring rule. + Eine neue Einfärbungsregel hinzufügen. + + + Delete this coloring rule. + Diese Einfärbungsregel löschen. + + + Duplicate this coloring rule. + Diese Einfärbungsregel duplizieren. + + + Clear all coloring rules. + Alle Einfärbungsregeln löschen. + + + Set the foreground color for this rule. + Für diese Regel die Vordergrundfarbe festlegen. + + + Foreground + Vordergrund + + + Set the background color for this rule. + Für diese Regel die Hintergrundfarbe festlegen. + + + Background + Hintergrund + + + Set the display filter using this rule. + Anzeigefilter nach dieser Regel setzen. + + + Apply as filter + Als Filter anwenden + + + Select a file and add its filters to the end of the list. + Eine Datei auswählen und die darin enthaltenen Regeln am Ende der Liste einfügen. + + + Save filters in a file. + Filter in eine Datei sichern. + + + Coloring Rules %1 + Einfärbungsregeln %1 + + + Import… + Importieren… + + + Export… + Exportieren… + + + Copy coloring rules from another profile. + Einfärbungsregeln von einem anderen Profil kopieren. + + + Open + Öffnen + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Doppelklicken zum Editieren. Ziehen zum Verschieben. Die Regeln werden der Reihe nach abgearbeitet bis zum ersten Treffer. + + + Import Coloring Rules + Einfärbungsregeln importieren + + + Export %1 Coloring Rules + %1 Einfärbungsregeln exportieren + + + + ColoringRulesModel + + New coloring rule + Neue Einfärbungsregel + + + Unable to save coloring rules: %1 + Einfärbungsregeln können nicht gespeichert werden: %1 + + + Name + Name + + + Filter + Filter + + + + ColumnEditorFrame + + Frame + Frame + + + Title: + Title + Namen: + + + Type: + Type + Typ: + + + Fields: + Fields + Feldname: + + + Occurrence: + Occurrence + Auftrittsreihenfolge: + + + Resolve Names: + Namen auflösen: + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + <html><head/><p>In Feldern menschenleserliche Zeichenketten anstatt Rohwerte anzeigen. Nur auf benutzerdefinierte Spalten mit Zeichenketten anwendbar.</p></body></html> + + + Missing fields. + Fehlende Felder. + + + Invalid fields. + Ungültige Felder. + + + Invalid occurrence value. + Ungültiger Wert. + + + + ColumnListModel + + Displayed + Angezeigt + + + Title + Titel + + + Type + Typ + + + Fields + Feldname + + + Field Occurrence + Feld-Auftrittsreihenfolge + + + Resolved + Aufgelöst + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + <html>In Feldern menschenleserliche Zeichenketten anstatt Rohwerte anzeigen. Nur auf benutzerdefinierte Spalten mit Zeichenketten anwendbar.</html> + + + New Column + Neue Spalte + + + + ColumnPreferencesFrame + + Frame + Frame + + + Add a new column + Neue Spalte hinzufügen + + + Delete selected column + Ausgewählte Spalte löschen + + + Show displayed columns only + Nur aufgelistete Spalten anzeigen + + + Reset all changes + Alle Änderungen zurücksetzen + + + + CompiledFilterOutput + + Compiled Filter Output + Kompilierter Filter + + + Copy + Kopieren + + + Copy filter text to the clipboard. + Filter text in die Zwischenablage kopieren. + + + + ConversationDataModel + + Address A + Adresse A + + + Port A + Port A + + + Address B + Adresse B + + + Port B + Port B + + + Packets + Pakete + + + Bytes + Bytes + + + Stream ID + Stream ID + + + Packets A + Pakete A + + + Bytes A + Bytes A + + + Packets B + Pakete B + + + Bytes B + Bytes B + + + Abs Start + Abs. Start + + + Rel Start + Rel. Start + + + Duration + Dauer + + + Bits/s A + Bits/s A + + + Bits/s B + Bits/s B + + + Total Packets + Pakete gesamt + + + Percent Filtered + Prozent gefiltert + + + + ConversationDialog + + Follow Stream… + Stream folgen… + + + Follow a TCP or UDP stream. + TCP oder UDP Stream folgen. + + + Graph… + Graph… + + + Graph a TCP conversation. + TCP Verbindung als Graph anzeigen. + + + + ConversationHashTablesDialog + + Dialog + Dialog + + + Conversation Hash Tables + Verbindungshashtabelle + + + + CopyFromProfileButton + + Copy from + Kopieren von + + + Copy entries from another profile. + Einträge von einem anderen Profil kopieren. + + + System default + Standard + + + + CredentialsDialog + + Wireshark - Credentials + Wireshark - Anmeldedaten + + + Credentials + Anmeldedaten + + + + CredentialsModel + + Click to select the packet + Klicken um das Paket auszuwählen + + + Click to select the packet with username + Klicken um das Paket mit dem Benutzernamen auszuwählen + + + Username not available + Benutzernamen ist nicht verfügbar + + + Packet No. + Paket Nummer + + + Protocol + Protokoll + + + Username + Benutzername + + + Additional Info + Zusätzliche Information + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + Bytes als Hex + ASCII Dump kopieren + + + Copy packet bytes as a hex and ASCII dump. + Paketbytes als Hex und ASCII Dump kopieren + + + …as Hex Dump + …als Hex Dump + + + Copy packet bytes as a hex dump. + Paketbytes als Hex Dump kopieren + + + …as MIME Data + ...als MIME Daten + + + …as C String + ...als C String + + + Copy packet bytes as printable ASCII characters and escape sequences. + Paket-Bytes als druckbare ASCII-Zeichen und Escape-Sequenzen kopieren. + + + …as a Hex Stream + ...als Hex Stream + + + Copy packet bytes as a stream of hex. + Paketbytes als Hexstream kopieren. + + + …as a Base64 String + ...als Base64 String + + + Copy packet bytes as a base64 encoded string. + Paket-Bytes als Base64-kodierten String kopieren. + + + Copy packet bytes as application/octet-stream MIME data. + Paketbytes als application/octet-stream MIME Daten kopieren. + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + Dissektionsverhalten für ein Protokoll ändern. + + + Remove this dissection behavior. + Dieses Dissektionsverhalten entfernen. + + + Copy this dissection behavior. + Dieses Dissektionsverhalten kopieren. + + + Clear all dissection behaviors. + Alle Dissektoreinstellungen zurücksetzen. + + + Decode As… + Dekodieren als… + + + Open + Öffnen + + + + DecodeAsModel + + Match using this field + Übereinstimmend mit diesem Feld + + + Change behavior when the field matches this value + Wenn dieser Wert auf das Feld zutrifft, soll das Verhalten geändert werden + + + Field value type (and base, if Integer) + Feldwert Typ (und Basis, falls Ganzzahl) + + + Current"Decode As" behavior + Aktuelles "Dekodieren als"-Verhalten + + + Default "Decode As" behavior + Standard "Dekodieren als" Verhalten + + + String + Zeichenkette + + + Integer, base + Integer, base + + + unknown + unbekannt + + + <none> + <none> + + + GUID + GUID + + + Field + Feld + + + Value + Wert + + + Type + Typ + + + Default + Standard + + + Current + Aktuell + + + + DisplayFilterCombo + + Display filter selector + Auswahl Anzeigefilter + + + Select from previously used filters. + Aus zuletzt genutzte Filter auswählen. + + + + DisplayFilterEdit + + Display filter entry + Anzeigefiltereintrag + + + Manage saved bookmarks. + Gespeicherte Lesezeichen verwalten + + + Display Filter Expression… + Anzeigefilterausdruck… + + + Apply a display filter %1 <%2/> + Anzeigefilter anwenden %1 <%2/> + + + Enter a display filter %1 + Geben Sie einen Anzeigefilter ein %1 + + + Clear display filter + Anzeigefilter zurücksetzen + + + Apply display filter + Anzeigenfilter anwenden + + + Left align buttons + Links ausgerichtete Knöpfe + + + Apply a read filter %1 + Lesefilter anwenden %1 + + + Current filter: %1 + Aktueller Filter: %1 + + + Invalid filter: + Ungültiger Filter: + + + Save this filter + Diesen Filter speichern + + + Remove this filter + Diesen Filter entfernen + + + Manage Display Filters + Anzeigefilter verwalten + + + Filter Button Preferences... + Einstellungen Filterknöpfe... + + + + DisplayFilterExpressionDialog + + Dialog + Dialog + + + Select a field to start building a display filter. + Ein Feld auswählen um einen Anzeigefilter zu erstellen. + + + Field Name + Feldname + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Die Liste von Feldnamen durchsuchen.</p></body></html> + + + Search: + Suchen: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>Relations können genutzt werden um Felder auf bestimmte Werte einzugrenzen. Folgende Relationen können verwendet werden:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Jedes Paket, dass dieses Feld enthält</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Vergleicht das Feld auf einen bestimmten Wert..</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Überprüft das Feld auf eine bestimmte Zeichenkette (contains) oder einen rugulären Ausdruck (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Vergleicht das Feld auf eine bestimmte Reihe von Werten</p></td></tr></table></body></html> + + + + + Relation + Relation + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + Standardmäßig sind Vergleiche und contains/matches/in-Relationen bereits wahr, wenn ein Wert zutrifft. Der Quantifikator "Alle" kann benutzt werden, um den Test auf alle Werte anzuwenden. + + + Quantifier + Quantifikator + + + Any + Jedes + + + All + Alle + + + Match against this value. + Überprüft diesen Wert. + + + Value + Wert + + + If the field you have selected has a known set of valid values they will be listed here. + Wenn das ausgewählte Feld bekannte mögliche Werte hat, so werden sie hier aufgelistet. + + + Predefined Values + Vordefinierte Werte + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + Wenn das ausgewählte Feld einen Bereich von Bytes abdeckt (z.B. bei der Auswahl eines Protokolls) kann der Treffer auf einen Bereich von Bytes eingeschränkt werden. + + + Range (offset:length) + Bereich (Versatz:Länge) + + + No display filter + Kein Anzeigefilter vorhanden + + + <small><i>A hint.</i></small> + <small><i>Hinweis</i></small> + + + Display Filter Expression + Anzeigefilterausdruck + + + Select a field name to get started + Einen Feldnamen auswählen um zu starten + + + Click OK to insert this filter + Zum Einfügen dieses Filter OK klicken + + + + DissectorSyntaxLineEdit + + Dissector entry + Dissector-Eintrag + + + Enter a dissector %1 + Dissector eingeben %1 + + + + DissectorTablesDialog + + Dialog + Dialog + + + Search: + Suchen: + + + Dissector Tables + Dissector Tabelle + + + + DissectorTablesProxyModel + + Table Type + Tabellentyp + + + String + Zeichenkette + + + Dissector Description + Dissector Beschreibung + + + Integer + Integer + + + Protocol + Protokoll + + + Short Name + Kurzname + + + Table Name + Tabellenname + + + Selector Name + Selektorname + + + + EnabledProtocolsDialog + + Dialog + Dialog + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>Das Deaktivieren eines Protokolls verhindert die Anzeige von darüber liegenden Protokollen</i></small> + + + Search: + Suchen: + + + in + in + + + Enable All + Alle aktivieren + + + Disable All + Alle deaktivieren + + + Invert + Invertieren + + + Enabled Protocols + Protokolle aktivieren + + + Everywhere + Überall + + + Only Protocols + Nur Protokolle + + + Only Description + Nur Beschreibungen + + + Only enabled protocols + Nur aktivierte Protokolle + + + Only disabled protocols + Nur deaktivierte Protokolle + + + any protocol + alle Protokolle + + + non-heuristic protocols + Protokolle die keine Heuristik verwenden + + + heuristic protocols + Protokolle die Heuristik verwenden + + + + EnabledProtocolsModel + + Protocol + Protokoll + + + Description + Beschreibung + + + + EndpointDataModel + + Address + Adresse + + + Port + Port + + + Packets + Pakete + + + Bytes + Bytes + + + Tx Packets + Tx Pakete + + + Tx Bytes + Tx Bytes + + + Rx Packets + Rx Pakete + + + Rx Bytes + Rx Bytes + + + Country + Land + + + City + Ort + + + Latitude + Breitengrad + + + Longitude + Längengrad + + + AS Number + AS Nummer + + + AS Organization + AS Organisation + + + Total Packets + Pakete gesamt + + + Percent Filtered + Prozent gefiltert + + + + EndpointDialog + + Map + Karte + + + Draw IPv4 or IPv6 endpoints on a map. + IPv4 und IPv6 Endpunkte auf einer Karte anzeigen. + + + Open in browser + Im Browser öffnen + + + Save As… + Speichern als… + + + Map file error + Fehler bei der Kartendatei + + + Save Endpoints Map + Endpunktekarte speichern + + + Failed to save map file %1. + Fehler beim Speichern der Kartendatei %1. + + + + EthernetAddressModel + + Type + Typ + + + Name + Name + + + Address + Adresse + + + All entries + Alle Einträge + + + Hosts + Hosts + + + Ethernet Addresses + Ethernet Adressen + + + Ethernet Manufacturers + Ethernet Hersteller + + + Ethernet Well-Known Addresses + Bekannte Ethernet Adressen + + + + ExpertInfoDialog + + Dialog + Dialog + + + <small><i>A hint.</i></small> + <small><i>Hinweis</i></small> + + + Limit to Display Filter + Auf Anzeigenfilter beschränken + + + Group by summary + Gruppieren nach Zusammenfassung + + + Search expert summaries. + In den Experten Informationen suchen. + + + Search: + Suchen: + + + Show… + Show... + Zeige... + + + Error + Fehler + + + Show error packets. + Pakete mit Fehler anzeigen. + + + Warning + Warnungen + + + Show warning packets. + Pakete mit Warnungen anzeigen. + + + Note + Hinweise + + + Show note packets. + Pakete mit Hinweise anzeigen. + + + Chat + Informationen + + + Show chat packets. + Pakete mit Informationen anzeigen. + + + Comment + Kommentare + + + Show comment packets. + Pakete mit Kommentare anzeigen. + + + Expert Information + Experteninformationen + + + Collapse All + Alles einklappen + + + Expand All + Alles aufklappen + + + Capture file closed. + Mitschnittdatei geschlossen. + + + No display filter + Kein Anzeigefilter vorhanden + + + No display filter set. + Kein Anzeigenfilter angewendet. + + + Limit information to "%1". + Informationen beschränken auf "%1". + + + Display filter: "%1" + Anzeigefilter: "%1" + + + + ExpertInfoProxyModel + + Packet + Paket + + + Severity + Schweregrad + + + Summary + Zusammenfassung + + + Group + Gruppe + + + Protocol + Protokoll + + + Count + Anzahl + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Paketdissektion exportieren + + + Export As: + Export as: + Exportieren als: + + + Plain text (*.txt) + Reiner Text (*.txt) + + + Comma Separated Values - summary (*.csv) + Durch Komma getrennte Werte - Zusammenfassung (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML - Zusammenfassung (*.psmml, *.xml) + + + PDML - details (*.pdml, *.xml) + PDML - Details (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + C Arrays - Bytes (*.c, *h) + + + + ExportObjectDialog + + Dialog + Dialog + + + Content Type: + Content-Typ: + + + Searching for objects + Nach Objekt suchen + + + Text Filter: + Textfilter: + + + Only display entries containing this string + Nur Einträge anzeigen, die diese Zeichenkette enthalten + + + Preview + Vorschau + + + All Content-Types + Alle Conten-Typen + + + Export + Exportieren + + + %1 object list + %1 Objektliste + + + Save Object As… + Objekt speichern unter… + + + Save All Objects In… + Alle Objekte sichern in… + + + + ExportObjectModel + + Packet + Paket + + + Hostname + Hostname + + + Content Type + Content-Type + + + Size + Größe + + + Filename + Dateiname + + + + ExportPDUDialog + + Dialog + Dialog + + + Display filter: + Anzeigefilter: + + + + ExtArgSelector + + Reload data + Daten neu laden + + + + ExtcapArgumentFileSelection + + Clear + Zurücksetzen + + + All Files ( + Alle Dateien ( + + + Open File + Öffne Datei + + + Select File + Datei auswählen + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + Schnittstellenoptionen + + + Start + Start + + + Save + Speichern + + + Default + Standard + + + Restore default value of the item + Standardeinstellungen für den Wert wiederherstellen + + + Extcap Help cannot be found + Hilfe für Extcap kann nicht gefunden werden + + + The help for the extcap interface %1 cannot be found. Given file: %2 + Die Hifle für Extcap-Schnittstelle %1 kann nicht gefunden werden. Angegebene Datei: %2 + + + Save parameter(s) on capture start + Parameter beim Starten des Mitschnitts speichern + + + + FieldFilterEdit + + Display filter entry + Anzeigefiltereintrag + + + Enter a field %1 + Feld %1 eingeben + + + Invalid filter: + Ungültiger Filter: + + + + FileSetDialog + + Dialog + Dialog + + + Directory: + Verzeichnis: + + + No files in Set + Keine Dateien im Dateisatz + + + No capture loaded + Kein Mitschnitt geladen + + + %Ln File(s) in Set + %1 File%2 in Set + + %Ln Datei im Dateisatz + %Ln Dateien im Dateisatz + + + + + FilesetEntryModel + + Open this capture file + Diese Mitschnittdatei laden + + + Filename + Dateiname + + + Created + Erstellt + + + Modified + Modifiziert + + + Size + Größe + + + + FilterAction + + Selected + Ausgewählt + + + Not Selected + nicht das Ausgewählte + + + …and Selected + …und das Ausgewählte + + + …or Selected + …oder das Ausgewählte + + + …and not Selected + …und nicht das Ausgewählte + + + …or not Selected + …oder nicht das Ausgewählte + + + + FilterDialog + + Dialog + Dialog + + + Create a new filter. + Neuen Filter erstellen. + + + Remove this filter. + Remove this profile. + Diesen Filter entfernen. + + + Copy this filter. + Copy this profile. + Diesen Filter kopieren. + + + Capture Filters + Mitschnittfilter + + + Display Filters + Anzeigefilter + + + Open + Öffnen + + + New capture filter + This text is automatically filled in when a new filter is created + Neuer Mitschnittfilter + + + New display filter + This text is automatically filled in when a new filter is created + Neuer Anzeigefilter + + + + FilterExpressionFrame + + Frame + Frame + + + Filter Buttons Preferences… + Einstellungen Filterknopf + + + Label: + Titel: + + + Enter a description for the filter button + Beschreibung für den Filterknopf eingeben + + + Filter: + Filter: + + + Enter a filter expression to be applied + Filterausdruck eingeben + + + Comment: + Kommentar: + + + Enter a comment for the filter button + Kommenter für den Filterknopf eingeben + + + Missing label. + Fehlende Beschriftung. + + + Missing filter expression. + Fehlender Filterausdruck. + + + Invalid filter expression. + Ungültiger Filterausdruck. + + + + FilterExpressionToolBar + + Filter Button Preferences... + Einstellungen Filterknöpfe... + + + Edit + Editieren + + + Disable + Deaktivieren + + + Remove + Entfernen + + + + FilterListModel + + Filter Name + Filtername + + + Filter Expression + Filterausdruck + + + + FindLineEdit + + Textual Find + Text Treffer + + + Regular Expression Find + Regulärer Ausdruck Treffer + + + + FirewallRulesDialog + + Create rules for + Regeln erstellen für + + + Inbound + Eingehend + + + Deny + Verbieten + + + Firewall ACL Rules + Firewall ACL Regeln + + + Copy + Kopieren + + + IPv4 source address. + IPv4 Quelladresse. + + + IPv4 destination address. + IPv4 Zieladresse. + + + Source port. + Quellport. + + + Destination port. + Zielport. + + + IPv4 source address and port. + IPv4 Quelladresse und Port. + + + IPv4 destination address and port. + IPv4 Zieladresse und Port. + + + MAC source address. + MAC Quelladresse. + + + MAC destination address. + MAC Zieladresse. + + + Text file (*.txt);;All Files ( + Textdatei (*.txt);; Alle Dateien ( + + + Warning + Warnungen + + + Unable to save %1 + Kann %1 nicht sichern + + + + FolderListModel + + "File" dialogs + "Datei" Dialoge + + + capture files + Mitschnittdateien + + + Temp + Temp + + + untitled capture files + Unbenannte Mitschnittdateien + + + Personal configuration + Benutzerspezifische Konfiguration + + + Global configuration + Globale Konfiguration + + + dfilters, preferences, ethers, … + dfilters, preferences, ethers, … + + + dfilters, preferences, manuf, … + dfilters, preferences, manuf, … + + + System + System + + + ethers, ipxnets + ethers, ipxnets + + + Program + Programm + + + program files + program files + + + Personal Plugins + Benutzerspezifische Plugins + + + binary plugins + binary plugins + + + Global Plugins + Globale Plugins + + + Personal Lua Plugins + Benutzerspezifische Lua Plugins + + + Global Lua Plugins + Globale Lua Plugins + + + Lua scripts + Lua Skripte + + + Personal Extcap path + Benutzerspezifischer Extcap Pfad + + + external capture (extcap) plugins + Externe Aufzeichnungs-Plug-Ins (extcap) + + + Global Extcap path + Globaler Extcap Pfad + + + MaxMind DB path + MaxMind DB Pfad + + + MaxMind DB database search path + Suchpfad für MaxMind Datenbank + + + MIB/PIB path + MIB/PIB Pfad + + + SMI MIB/PIB search path + Suchpfad für SMI MIB/PIB + + + macOS Extras + macOS Extras + + + Extra macOS packages + Extra macOS Pakete + + + Name + Name + + + Location + Lokation + + + Typical Files + Typische Dateien + + + + FollowStreamAction + + %1 Stream + %1 Stream + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + Diesen Stream filtern + + + Print + Drucken + + + %Ln client pkt(s), + + %Ln Client Paket, + %Ln Client Pakete, + + + + %Ln server pkt(s), + + %Ln Server Paket, + %Ln Server Pakete, + + + + ASCII + ASCII + + + C Arrays + C Arrays + + + EBCDIC + EBCDIC + + + Hex Dump + Hex Dump + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + Roh + + + Save as… + Speichern als… + + + Back + Zurück + + + Packet %1. + Paket %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">Client</span> Paket, + %Ln <span style="color: %1; background-color:%2">Client</span> Pakete, + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">Server</span> Paket, + %Ln <span style="color: %1; background-color:%2">Server</span> Pakete, + + + + %Ln turn(s). + + %Ln Runde. + %Ln Runden. + + + + Click to select. + Zur Auswahl anklicken. + + + Regex Find: + Regex Suchen: + + + No capture file. + Keine Mitschnittdatei. + + + Please make sure you have a capture file opened. + Bitte sicherstellen, dass eine Mitschnittdatei geöffnet ist. + + + Error following stream. + Fehler beim Folgen des Streams. + + + Capture file invalid. + Mitschnittdatei ungültig. + + + Please make sure you have a %1 packet selected. + Bitte ein %1 Paket auswählen. + + + %1 stream not found on the selected packet. + %1 Stream in ausgewähltem Paket nicht gefunden. + + + Entire conversation (%1) + Gesamte Verbindung (%1) + + + Follow %1 Stream (%2) + Folge %1 Stream (%2) + + + Error creating filter for this stream. + Fehler beim Erstellen eines Filters für diesen Stream. + + + Save Stream Content As… + Stream Inhalt speichern als… + + + [Stream output truncated] + [Streamausgabe gekürzt] + + + %Ln total stream(s). + + %n Stream. + &Ln Stream insgesamt. + + + + Max sub stream ID for the selected stream: %Ln + + Max. Sub-Stream ID für ausgewählten Stream: %Ln + Max. Sub-Stream ID für ausgewählten Stream: %Ln + + + + File closed. + Datei geschlossen. + + + Follow Stream + Folge Stream + + + Hint. + Hinweis. + + + Show data as + Show and save data as + Daten anzeigen als + + + Stream + Stream + + + Substream + Sub-Stream + + + Find: + Suchen: + + + Find &Next + &Nächstes suchen + + + + FontColorPreferencesFrame + + Frame + Frame + + + Main window font: + Schriftart Hauptfenster: + + + Select Font + Schriftart auswählen + + + Colors: + Farben: + + + System Default + Standard + + + Solid + Stabil + + + Sample ignored packet text + Beispieltext für ignorierte Pakete + + + Sample marked packet text + Beispieltext für markierte Pakete + + + Sample active selected item + Beispieltext für ein aktives ausgewähltes Element + + + Style: + Stil: + + + Gradient + Gradient + + + Sample inactive selected item + Beispieltext für ein inaktives ausgewähltes Element + + + Sample "Follow Stream" client text + Beispieltext für Daten vom Client bei "Stream folgen" + + + Sample "Follow Stream" server text + Beispieltext für Daten vom Server bei "Stream folgen" + + + Sample valid filter + Gültiger Anzeigefilter + + + Sample invalid filter + Ungültiger Anzeigefilter + + + Sample warning filter + Sample deprecated filter + Beispiel für einen Warnungs Filter + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + Example GIF query packets have jumbo window sizes + + + Lazy badgers move unique waxy jellyfish packets + Lazy badgers move unique waxy jellyfish packets + + + Font + Schriftart + + + + FunnelStringDialog + + Dialog + Dialog + + + + FunnelTextDialog + + Dialog + Dialog + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>Einen Text oder einen regulären Ausdruck eingeben. Dieser wird oben hervorgehoben.</p></body></html> + + + Highlight: + Hervorgehoben: + + + + GsmMapSummaryDialog + + Dialog + Dialog + + + GSM MAP Summary + GSM MAP Zusammenfassung + + + File + Datei + + + Name + Name + + + Length + Länge + + + Format + Format + + + Snapshot length + Snapshot Länge + + + Data + Daten + + + First packet + Erstes Paket + + + Last packet + Letztes Paket + + + Elapsed + Zeitspanne + + + Packets + Pakete + + + Invokes + Invokes + + + Total number of Invokes + Anzahl an Invokes insgesamt + + + Average number of Invokes per second + Durchschnittliche Anzahl an Invokes pro Sekunde + + + Total number of bytes for Invokes + Bytesanzahl für Invokes insgesamt + + + Average number of bytes per Invoke + Durchschnittliche Bytesanzahl pro Invoke + + + Return Results + Return Results + + + Total number of Return Results + Anzahl an Return Results insgesamt + + + Average number of Return Results per second + Durchschnittliche Anzahl an Return Results pro Sekunde + + + Total number of bytes for Return Results + Bytesanzahl für Return Results insgesamt + + + Average number of bytes per Return Result + Durchschnittliche Bytesanzahl pro Return Result + + + Totals + Insgesamt + + + Total number of GSM MAP messages + Anzahl an GSM MAP Nachrichten insgesamt + + + Average number of GSM MAP messages per second + Durchschnittliche Anzahl an GSM MAP Nachrichten pro Sekunde + + + Total number of bytes for GSM MAP messages + Bytesanzahl für GSM MAP Nachrichten insgesamt + + + Average number of bytes per GSM MAP message + Durchschnittliche Bytesanzahl pro GSM MAP Nachricht + + + + IOConsoleDialog + + Dialog + Dialog + + + Enter code + Code eingeben + + + Evaluate + Ausführen + + + Clear + Zurücksetzen + + + Use %1 to evaluate. + Verwende %1 zum Ausführen. + + + + IOGraphDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Nützliche Tastenkürzel zur Zeitersparnis</h3> +<table><tbody> + +<tr><th>+</th><td>Vergrößern</td></th> +<tr><th>-</th><td>Verkleinern</td></th> +<tr><th>x</th><td>X-Achse vergrößern</td></th> +<tr><th>X</th><td>X-Achse verkleinern</td></th> +<tr><th>y</th><td>Y-Achse vergrößern</td></th> +<tr><th>Y</th><td>Y-Achse verkleinern</td></th> +<tr><th>0</th><td>Graph in Ursprungszustand zurücksetzen</td></th> + +<tr><th>→</th><td>10 Bildpunkte nach rechts</td></th> +<tr><th>←</th><td>10 Bildpunkte nach links</td></th> +<tr><th>↑</th><td>10 Bildpunkte nach oben</td></th> +<tr><th>↓</th><td>10 Bildpunkte nach unten</td></th> +<tr><th><i>Shift+</i>→</th><td>1 Bildpunkt nach rechts</td></th> +<tr><th><i>Shift+</i>←</th><td>1 Bildpunkt nach links</td></th> +<tr><th><i>Shift+</i>↑</th><td>1 Bildpunkt nach oben</td></th> +<tr><th><i>Shift+</i>↓</th><td>1 Bildpunkt nach unten</td></th> + +<tr><th>g</th><td>Zum Paket gehen, bei dem sich der Zeiger gerade befindet</td></th> + +<tr><th>z</th><td>Maustastenverhalten umschalten: Verschieben / Zoomen</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Fadenkreuz ein/ausblenden</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + Diesen Graph entfernen. + + + Add a new graph. + Einen neuen Graph hinzufügen. + + + Duplicate this graph. + Diesen Graph duplizieren. + + + Clear all graphs. + Alle Graphen löschen. + + + Move this graph upwards. + Graphen nach oben verschieben. + + + Move this graph downwards. + Graphen nach unten verschieben. + + + Mouse + Maus + + + Drag using the mouse button. + Mit der Maustaste ziehen. + + + drags + ziehen + + + Select using the mouse button. + Mit der Maustaste auswählen. + + + zooms + zoomen + + + Interval + Intervall + + + Time of day + Uhrzeit + + + Log scale + Logarithmische Skala + + + Automatic update + Automatisches aktualisieren + + + Enable legend + Legende einschalten + + + Reset + Zurücksetzen + + + Reset Graph + Graph zurücksetzen + + + Reset the graph to its initial state. + Graph in den Ursprungszustand zurücksetzen. + + + 0 + 0 + + + Zoom In + Vergrößern + + + + + + + + + Zoom Out + Verkleinern + + + - + - + + + Move Up 10 Pixels + Um 10 Bildpunkte nach oben verschieben + + + Up + Rauf + + + Move Left 10 Pixels + Um 10 Bildpunkte nach links verschieben + + + Left + Links + + + Move Right 10 Pixels + Um 10 Bildpunkte nach rechts verschieben + + + Right + Rechts + + + Move Down 10 Pixels + Um 10 Bildpunkte nach unten verschieben + + + Down + Runter + + + Move Up 1 Pixel + Um 1 Bildpunkt nach oben verschieben + + + Shift+Up + Shift+Up + + + Move Left 1 Pixel + Um 1 Bildpunkt nach links verschieben + + + Shift+Left + Shift+Left + + + Move Right 1 Pixel + Um 1 Bildpunkt nach rechts verschieben + + + Shift+Right + Shift+Right + + + Move Down 1 Pixel + Um 1 Bildpunkt nach unten verschieben + + + Move down 1 Pixel + Move down 1 pixel + Um 1 Bildpunkt nach unten verschieben + + + Shift+Down + Shift+Down + + + Go To Packet Under Cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + Go to packet currently under the cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + G + G + + + Drag / Zoom + Verschieben / Zoomen + + + Toggle mouse drag / zoom behavior + Maustastenverhalten umschalten: Verschieben / Zoomen + + + Z + Z + + + Capture / Session Time Origin + Uhrzeit / verstrichene Zeit + + + Toggle capture / session time origin + Umschalten zwischen Uhrzeit und verstrichener Zeit + + + T + T + + + Crosshairs + Fadenkreuz + + + Toggle crosshairs + Fadenkreuz ein/ausblenden + + + Space + Space + + + Zoom In X Axis + X-Achse vergrößern + + + X + X + + + Zoom Out X Axis + X-Achse verkleinern + + + Shift+X + Shift+X + + + Zoom In Y Axis + Y-Achse vergrößern + + + Y + Y + + + Zoom Out Y Axis + Y-Achse verkleinern + + + Shift+Y + Shift+Y + + + 1 sec + 1 Sek. + + + 10 sec + 10 Sek. + + + 1 min + 1 Min. + + + 10 min + 10 Min. + + + Time (s) + Zeit (Sek) + + + I/O Graphs + I/O Graph + + + Save As… + Speichern als… + + + Copy + Kopieren + + + Copy graphs from another profile. + Graph von einem anderen Profil kopieren. + + + 1 ms + 1 ms + + + 2 ms + 2 ms + + + 5 ms + 5 ms + + + 10 ms + 10 ms + + + 20 ms + 20 ms + + + 50 ms + 50 ms + + + 100 ms + 100 ms + + + 200 ms + 200 ms + + + 500 ms + 500 ms + + + 2 sec + 2 Sek. + + + 5 sec + 5 Sek. + + + Wireshark I/O Graphs: %1 + Wireshark I/O Graphen: %1 + + + Filtered packets + Gefilterte Pakete + + + Filtered events + Gefilterte Ereignisse + + + All Packets + Alle Pakete + + + TCP Errors + TCP Fehler + + + All Events + Alle Ereignisse + + + Access Denied + Zugriff verboten + + + Hover over the graph for details. + Für mehr Details Maus über den Graphen bewegen. + + + No packets in interval + Keine Pakete im Intervall + + + No events in interval + Keine Ereignisse im Intervall + + + Click to select packet + Klicken zur Paketauswahl + + + Packet + Paket + + + Click to select event + Klicken um das Ereignis auszuwählen + + + Event + Ereignis + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Loslassen um zu zoomen, x = %1 bis %2, y = %3 bis %4 + + + Unable to select range. + Bereich kann nicht ausgewählt werden. + + + Click to select a portion of the graph. + Klicken um einen Teil des Graphen auszuwählen. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + Komma getrennte Werte (*.csv) + + + Save Graph As… + Graph speichern als… + + + + Iax2AnalysisDialog + + Dialog + Dialog + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Hinweg</span></p><p><span style=" font-size:medium; font-weight:600;">Rückweg</span></p></body></html> + + + Forward + Hinweg + + + Packet + Paket + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter (ms) + + + Bandwidth + Bandbreite + + + Status + Status + + + Length + Länge + + + Reverse + Rückweg + + + Graph + Graph + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>Jitterwerte vom Hinweg anzeigen oder verbergen.</p></body></html> + + + Forward Jitter + Jitter Hinweg + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>Differenzwerte vom Hinweg anzeigen oder verbergen.</p></body></html> + + + Forward Difference + Differenz Hinweg + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>Jitterwerte vom Rückweg anzeigen oder verbergen.</p></body></html> + + + Reverse Jitter + Jitter Rückweg + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>Differenzwerte vom Rückweg anzeigen oder verbergen.</p></body></html> + + + Reverse Difference + Differenz Rückweg + + + <small><i>A hint.</i></small> + <small><i>Hinweis</i></small> + + + Audio + Audio + + + Save the audio data for both channels. + Audiodaten von beiden Kanälen speichern. + + + Forward Stream Audio + Hinweg Audio Stream + + + Save the forward stream audio data. + Speichern der Audiodaten des Hinwegstreams + + + Reverse Stream Audio + Audiostream Rückweg + + + Save the reverse stream audio data. + Speichern der Audiodaten des Rückwegstreams + + + CSV + CSV + + + Save both tables as CSV. + Beide Tabellen als CSV Datei sichern. + + + Forward Stream CSV + Hinwegstream CSV + + + Save the forward table as CSV. + Tabelle Hinweg als CSV speichern. + + + Reverse Stream CSV + Rückwegstream CSV + + + Save the reverse table as CSV. + Rückweg Tabelle als CSV speichern. + + + Save Graph + Graph speichern + + + Save the graph image. + Bild des Graphen speichern. + + + Go to Packet + Gehe zu Paket + + + Select the corresponding packet in the packet list. + Wählt das dazugehörige Paket in der Paketliste aus. + + + G + G + + + Next Problem Packet + Nächstes Paket mit Problemen + + + Go to the next problem packet + Gehe zum nächsten Paket mit Problemen + + + N + N + + + IAX2 Stream Analysis + Analyse IAX2 Stream + + + Unable to save RTP data. + RTP Daten können nicht gesichert werden. + + + Please select an IAX2 packet. + Bitte ein IAX2 Paket auswählen. + + + G: Go to packet, N: Next problem packet + G: Gehe zu Paket, N: Nächstes Paket mit einem Problem + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Graph speichern als… + + + Can't save in a file: Wrong length of captured packets. + Speichern in eine Datei fehlgeschlagen: Falsche Länge des aufgezeichneten Pakets. + + + Can't save in a file: File I/O problem. + Speichern in eine Datei fehlgeschlagen: I/O Problem. + + + Save forward stream audio + Audiostream des Hinwegs speichern + + + Save reverse stream audio + Audiostream des Rückweges speichern + + + Save audio + Audio speichern + + + Sun Audio (*.au) + Sun Audio (*.au) + + + ;;Raw (*.raw) + ;;Raw (*.raw) + + + Warning + Warnungen + + + Unable to save in that format + Dieses Format kann nicht gespeichert werden + + + Unable to save %1 + Kann %1 nicht sichern + + + Saving %1… + Speichere %1… + + + Analyzing IAX2 + Analysiere IAX2 + + + Save forward stream CSV + Hinweg Stream als CSV speichern + + + Save reverse stream CSV + Rückweg Stream als CSV speichern + + + Save CSV + Als CSV speichern + + + Comma-separated values (*.csv) + Komma getrennte Werte (*.csv) + + + + ImportTextDialog + + File: + Datei: + + + Set name of text file to import + Zum Importieren Textdatei angeben + + + Browse for text file to import + Zum Importieren nach einer Textdatei suchen + + + Browse… + Browse... + Öffnen... + + + Hex Dump + Hex Dump + + + Import a standard hex dump as exported by Wireshark + Standard Hexdump der durch Wireshark exportiert wurde importieren + + + Offsets in the text file are in octal notation + Versatz in der Textdatei als Oktalnotation + + + Octal + Oktal + + + Offsets: + Versatz: + + + Offsets in the text file are in hexadecimal notation + Versatz in der Textdatei als Hexadezimalnotation + + + Hexadecimal + Hexadezimal + + + Offsets in the text file are in decimal notation + Versatz in der Textdatei als Dezimalnotation + + + Decimal + Dezimal + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>Soll der Start der ASCII-Repräsentation am Ende einer Hex+ASCII Zeile auch erkannt werden, wenn dieser wie Hex-Bytes aussieht?</p><p>Nicht aktivieren, wenn Hex Dump kein ASCII enthält.</p></body></html> + + + ASCII identification: + ASCII Identifikation: + + + Regular Expression + Regulärer Ausdruck + + + Import a file formatted according to a custom regular expression + Datei mit einer Formatierung nach einem Regulären Ausdruck importieren + + + Packet format regular expression + Regulärer Ausdruck Paketformat + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + <html><head/><body><p>Perl kompatibler regulärer Ausdruck um ein einzelnes Paket in der Datei mit benannte Gruppen zu importieren. Ankerzeichen ^ und $ können auch vor/nach Zeilenumbrüche verwendet werden.</p><p>Benötigt wird nur eine Datengruppe. time, dir und seqno können auch verwendet werden.</p><p> Flags für den regulären Ausdruck: UPNAMES, MULTILINE und NOEMPTY </p></body></html> + + + This is regexHintLabel, it will be set to default_regex_hint + Das ist ein regexHintLabel, es wird durch default_regex_hint gesetzt + + + Data encoding: + Datenkodierung: + + + How data is encoded + Wie die Daten codiert sind + + + encodingRegexExample + EncodingRegexBeispiel + + + List of characters indicating incoming packets + Liste von Zeichen die eingehende Pakete spezifizieren + + + iI< + iI< + + + List of characters indicating outgoing packets + Liste von Zeichen die ausgehende Pakete spezifizieren + + + oO> + oO> + + + Timestamp format: + Zeitstempelformat: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Aktivieren, wenn die Datei Indikatoren für die Richtung der Pakete (Ein- oder Ausgehend) enthält. + + + Direction indication: + Richtungsindikatoren + + + ExportPDU + ExportPDU + + + IP version: + IP Version: + + + Interface name: + Schnittstellennamen: + + + The name of the interface to write to the import capture file + Der Name der Schnittstelle, der in die Import-Mitschnittdatei geschrieben wird + + + Fake IF, Import from Hex Dump + Fake IF, Import aus Hex Dump + + + Maximum frame length: + Maximale Framelänge: + + + Encapsulation + Kapselung + + + The text file has no offset + Die Textdatei hat keinen Versatz + + + None + Keine + + + <small><i>recommended regex:</small></i> + <small><i>Empfohlener regulärer Ausdruck:</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + Format um den Zeitstempel in der Textdatei einzulesen (z.B. %H:%M:%S.). Format basiert auf strptime(3) + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + <html><head/><body><p>Format des Zeitstempel der aus der Textdatei ausgelesen werden soll (z.B. %H:%M:%s.%f).</p><p>Formatzeichen basieren auf strptime(3) mit dem zusätzlichen Zeichen %f für Sekundenbruchteile. Die Länge von %f bestimmt die Präzision.</p></body></html> + + + %H:%M:%S.%f + %H:%M:%S.%f + + + timestampExampleLabel + ZeitstempelBeispielBezeichnung + + + Encapsulation Type: + Datenkapselungstyp: + + + Encapsulation type of the frames in the import capture file + Protokoll in das die Frames der zu importiernden Datei eingebettet sind + + + Prefix each frame with an Ethernet and IP header + Jedem Frame einen Ethernet- und IP-Header voranstellen + + + IP + IP + + + Prefix each frame with an Ethernet, IP and UDP header + Jedem Frame einen Ethernet-, IP- und UDP-Header voranstellen + + + Prefix each frame with an Ethernet, IP and TCP header + Jedem Frame einen Ethernet-, IP- und TCP-Header voranstellen + + + Prefix each frame with an Ethernet, IP and SCTP header + Jedem Frame einen Ethernet-, IP- und SCTP-Header voranstellen + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + Jedem Frame einen Ethernet-, IP- und SCTP-(Daten) Header voranstellen + + + Source address: + Quelladresse: + + + Destination address: + Zieladresse: + + + Dissector + Dissector + + + The IP protocol ID for each frame + Die IP Protokoll ID für jeden Frame + + + The IP source address for each frame + Die Quell IP-Adresse für jeden Frame + + + The IP destination address for each frame + Die Ziel IP-Adresse für jeden Frame + + + The UDP, TCP or SCTP source port for each frame + Der UDP, TCP oder SCTP Quellport für jeden Frame + + + The SCTP DATA payload protocol identifier for each frame + Der SCTP Daten Payload Bezeichner für jeden Frame + + + The UDP, TCP or SCTP destination port for each frame + Der UDP, TCP oder SCTP Zielport für jeden Frame + + + Prefix each frame with an Ethernet header + Jedem Frame einen Ethernetheader voranstellen + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + Protokoll (dez): + + + Leave frames unchanged + Frames unangetastet lassen + + + No dummy header + Kein Dummy Header + + + Tag: + Tag: + + + UDP + UDP + + + Source port: + Quellport: + + + The Ethertype value of each frame + Wert des Ethertype für jeden Frame + + + TCP + TCP + + + The SCTP verification tag for each frame + Der SCTP Verfication Tag für jeden Frame + + + Destination port: + Zielport: + + + Ethertype (hex): + Ethertype (Hex): + + + SCTP (Data) + SCTP (Daten) + + + The dissector to use for each frame + Der zu verwendete Dissector für jeden Frame + + + The IP Version to use for the dummy IP header + Die zu verwendete IP Version für den Dummy IP Header + + + The maximum size of the frames to write to the import capture file (max 256kiB) + Die maximale Framegröße beim Schreiben in die Mitschnittdatei (max 256kiB) + + + Supported fields are data, dir, time, seqno + Unterstützte Felder sind data, dir, time, seqno + + + Missing capturing group data (use (? + Gruppierungskonstrukt für Daten fehlt (bitte (? verwenden + + + Import From Hex Dump + Aus einem Hex Dump importieren + + + Import + Importieren + + + Import Text File + Aus einer Textdatei importieren + + + + InterfaceFrame + + Frame + Frame + + + Wired + Kabelgebunden + + + AirPCAP + AirPCAP + + + Pipe + Pipe + + + STDIN + STDIN + + + Bluetooth + Bluetooth + + + Wireless + Wireless + + + Dial-Up + Einwahl + + + USB + USB + + + External Capture + Externer Mitschnitt + + + Virtual + Virtuell + + + Remote interfaces + Entfernte Schnittstellen + + + Show hidden interfaces + Versteckte Schnittstellen anzeigen + + + External capture interfaces disabled. + Externe Mitschnittschnittstellen deaktiviert. + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + <p>Lokale Schnittstellen sind nicht verfügbar, da kein Mitschnitt Treiber installiert ist.</p><p>Dies kann durch die Installation von <a href="https://nmap.org/npcap/">Npcap</a> oder <a href="https://www.winpcap.org/install/default.htm">WinPcap</a> behoben werden.</p> + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + <p>Lokale Schnittstellen sind nicht verfügbar, da kein Mitschnitt Treiber geladen wurde.</p><p>Dies kann durch das Ausführen von <pre>net start npcap</pre> (wenn Npcap installiert ist) oder <pre>net start npf</pre> (wenn WinPcap installiert ist) behoben werden. Beide Kommandos müssen als Administrator aufgerufen werden.</p> + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + <p>Sie haben keine Berechtigung zum mitschneiden auf der lokalen Schnittstelle.</p><p> Sie können <a href="file://%1">ChmodBPF installieren</a> um dies zu beheben.</p> + + + You don't have permission to capture on local interfaces. + Sie haben keine Berechtigung zum mitschneide auf der lokalen Schnittstelle. + + + No interfaces found. + Keine Schnittstellen gefunden. + + + Interfaces not loaded (due to preference). Go to Capture + Schnittstellen (aufgrund der Einstellungen) nicht geladen. Gehe zu Mitschnitt + + + Start capture + Mitschnitt starten + + + Hide Interface + Schnittstelle verbergen + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + Keine Schnittstelle zum Anzeigen. %1 Schnittstellen versteckt. + + + + InterfaceToolbar + + Frame + Frame + + + Select interface + Schnittstelle auswählen + + + Interface + Schnittstelle + + + + InterfaceToolbarLineEdit + + Apply changes + Änderungen anwenden + + + + InterfaceTreeModel + + Show + Zeige + + + Friendly Name + Kurzname + + + Interface Name + Schnittstellenname + + + No interfaces found. + Keine Schnittstellen gefunden. + + + This version of Wireshark was built without packet capture support. + Diese Version von Wireshark wurde ohne Unterstützung von Paketaufzeichnung kompiliert. + + + Local Pipe Path + Pfad lokale Pipe + + + Comment + Kommentar + + + Link-Layer Header + Link-Layer Header + + + Promiscuous + Promiskuitiv + + + Snaplen (B) + Mitschnittlänge (B) + + + Buffer (MB) + Puffer (MB) + + + Monitor Mode + Überwachungsmodus + + + Capture Filter + Mitschnittfilter + + + Addresses + Adressen + + + Address + Adresse + + + Extcap interface: %1 + Extcap Schnittstelle: %1 + + + No addresses + Keine Adressen + + + No capture filter + Kein Mitschnittfilter + + + Capture filter + Mitschnittfilter + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + LBT-RM Transport Statistik + + + Sources + Quellen + + + Address/Transport + Adresse/Transport + + + Data frames + Datenframes + + + Data bytes + Datenbytes + + + Data frames/bytes + Daten Frames/Bytes + + + Data rate + Datenrate + + + RX data frames + RX Datenframes + + + RX data bytes + RX Datenbytes + + + RX data frames/bytes + RX Daten Frames/Bytes + + + RX data rate + RX Datenrate + + + NCF frames + NCP Frames + + + NCF count + NCF Anzahl + + + NCF bytes + NCF Bytes + + + NCF frames/bytes + NCF Frames/Bytes + + + NCF count/bytes + NCF Anzahl/Bytes + + + NCF frames/count + NCF Frames/Anzahl + + + NCF frames/count/bytes + NCF Frames/Anzahl/Bytes + + + NCF rate + NCF Rate + + + SM frames + SM Frames + + + SM bytes + SM Bytes + + + SM frames/bytes + SM Frames/Bytes + + + SM rate + SM Rate + + + Show + Zeige + + + Data + Daten + + + RX Data + RX Daten + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + Sequenznummern für Transport + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Anzahl + + + Frame + Frame + + + SQN/Reason + SQN/Ursache + + + Receivers + Empfänger + + + NAK frames + NAK Frames + + + NAK count + NAK Anzahl + + + NAK bytes + NAK Bytes + + + NAK rate + NAK Rate + + + NAK sequence numbers for transport + NAK Sequenznummern für Transport + + + Display filter: + Anzeigefilter: + + + Regenerate statistics using this display filter + Statistik mit Anzeigenfilter neu generieren + + + Apply + Anwenden + + + Copy as CSV + Kopieren als CSV + + + Copy the tree as CSV + Baum als CSV kopieren + + + Copy as YAML + Kopieren als YAML + + + Copy the tree as YAML + Baum als YAML kopieren + + + Show the data frames column + Spalte Datenframes anzeigen + + + Show the data bytes column + Spalte Datenbytes anzeigen + + + Show the data frames/bytes column + Spalte Datenframes/Bytes anzeigen + + + Show the RX data frames column + Spalte RX Datenframes anzeigen + + + Show the RX data bytes column + Spalte RX Datenbytes anzeigen + + + Show the RX data frames/bytes column + Spalte RX Datenframes/Bytes anzeigen + + + Show the NCF frames column + Spalte NCF Frames anzeigen + + + Show the NCF bytes column + Spalte NCF Bytes anzeigen + + + Show the NCF count column + Spalte NCP Zähler anzeigen + + + Show the data rate column + Spalte Datenrate anzeigen + + + Show the RX data rate column + Spalte RX Datenrate anzeigen + + + Show the NCF frames/bytes column + Spalte NCF Frames/Bytes anzeigen + + + Show the NCF count/bytes column + Spalte NCF Zähler/Bytes anzeigen + + + Show the NCF frames/count column + Spalte NCP Frames/Zähler anzeigen + + + Show the NCF frames/count/bytes column + Spalte NCP Frames/Zähler/Bytes anzeigen + + + Show the NCF rate column + Spalte NCF Rate anzeigen + + + Show the SM frames column + Spalte SM Frames anzeigen + + + Show the SM bytes column + Spalte SM Bytes anzeigen + + + Show the SM frames/bytes column + Spalte SM Frames/Bytes anzeigen + + + Show the SM rate column + Spalte SM Rate anzeigen + + + Auto-resize columns to content + Spaltenbreite automatisch anpassen + + + Resize columns to content size + Spaltenbreite an Inhalt anpassen + + + LBT-RM Statistics failed to attach to tap + LBT-RM Statistik kann nicht an Tap angebunden werden + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + LBT-RU Transportstatistik + + + Sources + Quellen + + + Address/Transport/Client + Adresse/Transport/Client + + + Data frames + Datenframes + + + Data bytes + Datenbytes + + + Data frames/bytes + Daten Frames/Bytes + + + Data rate + Datenrate + + + RX data frames + RX Datenframes + + + RX data bytes + RX Datenbytes + + + RX data frames/bytes + RX Daten Frames/Bytes + + + RX data rate + RX Datenrate + + + NCF frames + NCP Frames + + + NCF count + NCF Anzahl + + + NCF bytes + NCF Bytes + + + NCF frames/count + NCF Frames/Anzahl + + + NCF frames/bytes + NCF Frames/Bytes + + + NCF count/bytes + NCF Anzahl/Bytes + + + NCF frames/count/bytes + NCF Frames/Anzahl/Bytes + + + NCF rate + NCF Rate + + + SM frames + SM Frames + + + SM bytes + SM Bytes + + + SM frames/bytes + SM Frames/Bytes + + + SM rate + SM Rate + + + RST frames + RST Frames + + + RST bytes + RST Bytes + + + RST frames/bytes + RST Frames/Bytes + + + RST rate + RST Rate + + + Show + Zeige + + + Data SQN + Daten SQN + + + RX Data SQN + RX Daten SQN + + + NCF SQN + NCP SQN + + + SM SQN + SM SQN + + + RST reason + RST Ursachen + + + details for transport + Details für Transport + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Anzahl + + + Frame + Frame + + + Reason + Ursache + + + SQN/Reason + SQN/Ursache + + + Receivers + Empfänger + + + Address/Transport + Adresse/Transport + + + NAK frames + NAK Frames + + + NAK count + NAK Anzahl + + + NAK bytes + NAK Bytes + + + NAK frames/count + NAK Frames/Zähler + + + NAK count/bytes + NAK Zähler/Bytes + + + NAK frames/bytes + NAK Frames/Bytes + + + NAK frames/count/bytes + NAK Frames/Zähler/Bytes + + + NAK rate + NAK Rate + + + ACK frames + ACK Frames + + + ACK bytes + ACK Bytes + + + ACK frames/bytes + ACK Frames/Bytes + + + ACK rate + ACK Rate + + + CREQ frames + CREQ Frames + + + CREQ bytes + CREQ Bytes + + + CREQ frames/bytes + CREQ Frames/Bytes + + + CREQ rate + CREQ Rate + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + CREQ Anfrage + + + Display filter: + Anzeigefilter: + + + Regenerate statistics using this display filter + Statistik mit Anzeigenfilter neu generieren + + + Apply + Anwenden + + + Copy as CSV + Als CSV kopieren + + + Copy the tree as CSV + Baum als CSV kopieren + + + Copy as YAML + Als YAML kopieren + + + Copy the tree as YAML + Baum als YAML kopieren + + + Show the data frames column + Spalte Datenframes anzeigen + + + Show the data bytes column + Spalte Datenbytes anzeigen + + + Show the data frames/bytes column + Spalte Datenframes/Bytes anzeigen + + + Show the data rate column + Spalte Datenrate anzeigen + + + Show the RX data frames column + Spalte RX Datenframes anzeigen + + + Show the RX data bytes column + Spalte RX Datenbytes anzeigen + + + Show the RX data frames/bytes column + Spalte RX Datenframes/Bytes anzeigen + + + Show the RX data rate column + Spalte RX Datenrate anzeigen + + + Show the NCF frames column + Spalte NCF Frames anzeigen + + + Show the NCF count column + Spalte NCP Zähler anzeigen + + + Show the NCF bytes column + Spalte NCF Bytes anzeigen + + + Show the NCF frames/bytes column + Spalte NCF Frames/Bytes anzeigen + + + Show the NCF count/bytes column + Spalte NCF Zähler/Bytes anzeigen + + + Show the NCF frames/count column + Spalte NCP Frames/Zähler anzeigen + + + Show the NCF frames/count/bytes column + Spalte NCP Frames/Zähler/Bytes anzeigen + + + Show the SM frames column + Spalte SM Frames anzeigen + + + Show the SM bytes column + Spalte SM Bytes anzeigen + + + Show the SM frames/bytes column + Spalte SM Frames/Bytes anzeigen + + + Show the SM rate column + Spalte SM Rate anzeigen + + + Show the RST frames column + Spalte RST Frames anzeigen + + + Show the RST bytes column + Spalte RST Bytes anzeigen + + + Show the RST frames/bytes column + Spalte RST Frames/Bytes anzeigen + + + Show the RST rate column + Spalte RST Rate anzeigen + + + Show the NAK frames column + Spalte NAK Frames anzeigen + + + Show the NAK count column + Spalte NAK Zähler anzeigen + + + Show the NAK bytes column + Spalte NAK Bytes anzeigen + + + Show the NAK frames/count column + Spalte NAK Frames/Zähler anzeigen + + + Show the NAK count/bytes column + Spalte NAK Zähler/Bytes anzeigen + + + Show the NAK frames/bytes column + Spalte NAK Frames/Bytes anzeigen + + + Show the NAK frames/count/bytes column + Spalte NAK Frames/Zähler/Bytes anzeigen + + + Show the NAK rate column + Spalte NAK Rate anzeigen + + + Show the ACK frames column + Spalte ACK Frames anzeigen + + + Show the ACK bytes column + Spalte ACK Bytes anzeigen + + + Show the ACK frames/bytes column + Spalte ACK Frames/Bytes anzeigen + + + Show the ACK rate column + Spalte ACK Rate anzeigen + + + Show the CREQ frames column + Spalte CREQ Frames anzeigen + + + Show the CREQ bytes column + Spalte CREQ Bytes anzeigen + + + Show the CREQ frames/bytes column + Spalte CREQ Frames/Bytes anzeigen + + + Show the CREQ rate column + Spalte CREQ Rate anzeigen + + + Auto-resize columns to content + Spaltenbreite automatisch anpassen + + + Resize columns to content size + Spaltenbreite an Inhalt anpassen + + + Show the NCF rate column + Spalte NCF Rate anzeigen + + + LBT-RU Statistics failed to attach to tap + LBT-RU Statistik kann nicht an Tap angebunden werden + + + + LBMStreamDialog + + Dialog + Dialog + + + Stream + Stream + + + Endpoint A + Endpunkt A + + + Endpoint B + Endpunkt B + + + Messages + Nachrichten + + + Bytes + Bytes + + + First Frame + Erster Frame + + + Last Frame + Letzer Frame + + + Display filter: + Anzeigefilter: + + + Regenerate statistics using this display filter + Statistiken basierend auf dem Anzeigenfilter neu erstellen + + + Apply + Anwenden + + + Copy as CSV + Als CSV kopieren + + + Copy the tree as CSV + Baum als CSV kopieren + + + Copy as YAML + Als YAML kopieren + + + Copy the tree as YAML + Baum als YAML kopieren + + + LBM Stream failed to attach to tap + LBM Stream kann nicht angebunden werden + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + LayoutPreferencesFrame + + Frame + Frame + + + Pane 1: + Bereich 1: + + + Packet List + Paketliste + + + Packet Details + Paketdetails + + + Packet Bytes + Paket Bytes + + + Packet Diagram + Paketdiagramm + + + None + Kein + + + Pane 2: + Bereich 2: + + + Pane 3: + Bereich 3: + + + Packet List settings: + Einstellungen Paketliste: + + + Show packet separator + Zeige Pakettrenner + + + Show column definition in column context menu + Spaltendefinition im Spaltenkontextmenü anzeigen + + + Allow the list to be sorted + Erlaube Sortierung der Liste + + + Maximum number of cached rows (affects sorting) + Maximale Anzahl zwischengespeicherter Einträge (beeinflusst Sortierung) + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + <html><head/><body><p>Wenn mehr als diese Anzahl an Einträgen angezeigt werden, wird das Sortieren nach Spalten, die erneuter Dissektion bedürfen, deaktiviert. Das Erhöhen der Anzahl erhöht den Speicherverbrauch durch das Zwischenspeichern von Spaltenwerten.</p></body></html> + + + Enable mouse-over colorization + Mouse-Over Einfärbung aktivieren + + + Status Bar settings: + Einstellungen Statusleiste: + + + Show selected packet number + Nummer des ausgewählten Pakets anzeigen + + + Show file load time + Ladezeit der Datei anzeigen + + + + LteMacStatisticsDialog + + LTE Mac Statistics + LTE Mac Statistiken + + + Include SR frames in filter + SR Frames in Filter inkludieren + + + Include RACH frames in filter + RACH Frames in Filter inkludieren + + + MAC Statistics + MAC Statistiken + + + + LteRlcGraphDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Nützliche Tastenkürzel zur Zeitersparniss</h3> +<table><tbody> + +<tr><th>+</th><td>Vergrößern</td></th> +<tr><th>-</th><td>Verkleinern</td></th> +<tr><th>0</th><td>Graph in Ursprungszustand zurücksetzen</td></th> + +<tr><th>→</th><td>10 Bildpunkte nach rechts</td></th> +<tr><th>←</th><td>10 Bildpunkte nach links</td></th> +<tr><th>↑</th><td>10 Bildpunkte nach oben</td></th> +<tr><th>↓</th><td>10 Bildpunkte nach unten</td></th> +<tr><th><i>Shift+</i>→</th><td>1 Bildpunkt nach rechts</td></th> +<tr><th><i>Shift+</i>←</th><td>1 Bildpunkt nach links</td></th> +<tr><th><i>Shift+</i>↑</th><td>1 Bildpunkt nach oben</td></th> +<tr><th><i>Shift+</i>↓</th><td>1 Bildpunkt nach unten</td></th> + +<tr><th>g</th><td>Zum Paket gehen, bei dem sich der Zeiger gerade befindet</td></th> + +<tr><th>z</th><td>Maustastenverhalten umschalten: Verschieben / Zoomen</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Fadenkreuz ein/ausblenden</td></th> + +</tbody></table> +</body></html> + + + Mouse + Maus + + + Drag using the mouse button. + Mit der Maustaste ziehen. + + + drags + ziehen + + + Select using the mouse button. + Mit der Maustaste auswählen. + + + zooms + zoomen + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Graph auf Ursprungszustand zurücksetzen.</p></body></html> + + + Reset + Zurücksetzen + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Richtung der Verbindung wechseln (den entgegengesetzten Flow anzeigen).</p></body></html> + + + Switch Direction + Richtung wechseln + + + Reset Graph + Graph zurücksetzen + + + Reset the graph to its initial state. + Graph in den Ursprungszustand zurücksetzen. + + + 0 + 0 + + + Zoom In + Vergrößern + + + + + + + + + Zoom Out + Verkleinern + + + - + - + + + Move Up 10 Pixels + Um 10 Bildpunkte nach oben verschieben + + + Up + Rauf + + + Move Left 10 Pixels + Um 10 Bildpunkte nach links verschieben + + + Left + Links + + + Move Right 10 Pixels + Um 10 Bildpunkte nach rechts verschieben + + + Right + Rechts + + + Move Down 10 Pixels + Um 10 Bildpunkte nach unten verschieben + + + Down + Runter + + + Move Up 1 Pixel + Um 1 Bildpunkt nach oben verschieben + + + Shift+Up + Shift+Up + + + Move Left 1 Pixel + Um 1 Bildpunkt nach links verschieben + + + Shift+Left + Shift+Left + + + Move Right 1 Pixel + Um 1 Bildpunkt nach rechts verschieben + + + Shift+Right + Shift+Right + + + Move Down 1 Pixel + Um 1 Bildpunkt nach unten verschieben + + + Move down 1 Pixel + Um 1 Bildpunkt nach unten verschieben + + + Shift+Down + Shift+Down + + + Drag / Zoom + Verschieben / Zoomen + + + Toggle mouse drag / zoom behavior + Maustastenverhalten umschalten: Verschieben / Zoomen + + + Z + Z + + + Crosshairs + Fadenkreuz + + + Toggle crosshairs + Fadenkreuz ein/ausblenden + + + Space + Space + + + Move Up 100 Pixels + Um 100 Bildpunkte nach oben verschieben + + + PgUp + Bild nach oben + + + PgDown + Bild nach unten + + + Go To Packet Under Cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + Go to packet currently under the cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + G + G + + + Zoom In X Axis + X-Achse vergrößern + + + X + X + + + Zoom Out Y Axis + Y-Achse verkleinern + + + Shift+Y + Shift+Y + + + Zoom In Y Axis + Y-Achse vergrößern + + + Y + Y + + + Zoom Out X Axis + X-Achse verkleinern + + + Shift+X + Shift+X + + + Switch direction (swap between UL and DL) + Richtung tauschen (UL und DL vertauschen) + + + D + D + + + Time + Zeit + + + Sequence Number + Sequenznummer + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + LTE RLC Graph (UE=%1 Kanal=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + LTE RLC Graph - Kein Kanal ausgewählt + + + Save As… + Speichern als… + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s Sequenz %4 Länge %5) + + + Click to select packet + Klicken zur Paketauswahl + + + Packet + Paket + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Loslassen um zu zoomen, x = %1 bis %2, y = %3 bis %4 + + + Unable to select range. + Bereich kann nicht ausgewählt werden. + + + Click to select a portion of the graph. + Klicken um einen Teil des Graphen auszuwählen. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Graph speichern als… + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + LTE RLC Statistiken + + + Include SR frames in filter + SR Frames in Filter inkludieren + + + Include RACH frames in filter + RACH Frames in Filter inkludieren + + + Use RLC frames only from MAC frames + RLC Frames nur von MAC Frames verwenden + + + UL Frames + UL Frames + + + UL Bytes + UL Bytes + + + UL MB/s + UL MB/s + + + UL ACKs + UL ACKs + + + UL NACKs + UL NACKs + + + UL Missing + UL Fehlend + + + DL Frames + DL Frames + + + DL Bytes + DL Bytes + + + DL MB/s + DL MB/s + + + DL ACKs + DL ACKs + + + DL NACKs + DL NACKs + + + DL Missing + DL Fehlend + + + RLC Statistics + RLC Statistiken + + + + MainStatusBar + + Ready to load or capture + Bereit zum Laden einer Datei oder zum Aufzeichnen + + + Ready to load file + Bereit eine Datei zu laden + + + Open the Capture File Properties dialog + Eigenschaften der Mitschnittdatei öffnen + + + Profile: %1 + Profil: %1 + + + Manage Profiles… + Profile verwalten… + + + New… + Neu… + + + Edit… + Editieren… + + + Import + Importieren + + + Export + Exportieren + + + Delete + Löschen + + + Switch to + Wechseln zu + + + is the highest expert information level + is the highest expert info level + liefert die detailliertesten Experten-Informationen + + + ERROR + FEHLER + + + WARNING + WARNUNG + + + NOTE + HINWEIS + + + CHAT + INFO + + + No expert information + No expert info + Keine Experten-Informationen + + + %Ln byte(s) + , %1 bytes + + %Ln Byte + %Ln Bytes + + + + Byte %1 + Byte %1 + + + Bytes %1-%2 + Bytes %1-%2 + + + Selected Packet: %1 %2 + Ausgewähltes Paket: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + Pakete: %1 %4 Angezeigt: %2 (%3%) + + + %1 Selected: %2 (%3%) + %1 Ausgewählt: %2 (%3%) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 Markiert: %2 (%3%) + + + %1 Dropped: %2 (%3%) + %1 Verworfen: %2 (%3%) + + + %1 Ignored: %2 (%3%) + %1 Ignoriert: %2 (%3%) + + + %1 Comments: %2 + %1 Kommentare: %2 + + + %1 Load time: %2:%3.%4 + %1 Ladezeit: %2:%3.%4 + + + No Packets + Keine Pakete + + + From Zip File... + Aus ZIP Datei... + + + From Directory... + Aus Verzeichnis... + + + Selected Personal Profile... + Ausgewähltes persönliches Profil... + + + All Personal Profiles... + Alle persönlichen Profile... + + + Packets: %1 + Pakete: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + Frame + + + Checking this will save the size, position, and maximized state of the main window. + Bei Aktivierung dieser Option wird die Größe, die Position und der Zustand des Hauptfensters gespeichert. + + + Remember main window size and placement + Größe und Platzierung des Hauptfensters merken + + + Open files in + Öffne Datei in + + + This folder: + Diesem Ordner: + + + Browse… + Browse... + Öffnen... + + + The most recently used folder + Zuletzt genutztem Ordner + + + Show up to + Anzeigen von maximal + + + filter entries + Filtereinträge + + + recent files + letzte Dateien + + + Confirm unsaved capture files + Bei ungesicherten Mitschnittdateien nachfragen + + + Display autocompletion for filter text + Autovervollständigung für Filtertext anzeigen + + + Main toolbar style: + Stil Hauptleiste + + + Icons only + Nur Icons + + + Text only + Nur Text + + + Icons & Text + Icons & Text + + + Window title + Fenstertitel + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Angepasster Fenstertitel der dem bestehenden Titel angehängt wird<br/>%F = Dateipfad der Mitschnittdatei<br/>%P = Profilname<br/>%S = ein bedingtes Trennzeichen (&quot; - &quot;) dass nur angezeigt wird wenn es von Variablen mit Werten oder statische Text umgeben ist<br/>%V = Versionsinformation</p></body></html> + + + Prepend window title + Fenstertitel anhängen + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Angepasster Fenstertitel der dem bestehenden Titel angehängt wird<br/>%F = Dateipfad der Mitschnittdatei<br/>%P = Profilname<br/>%S = ein bedingtes Trennzeichen (&quot; - &quot;) dass nur angezeigt wird wenn es von Variablen mit Werten oder statische Text umgeben ist<br/>%V = Versionsinformation</p></body></html> + + + Language: + Sprache: + + + Use system setting + Systemeinstellungen verwenden + + + Open Files In + Dateien öffnen in + + + + ManageInterfacesDialog + + Manage Interfaces + Schnittstellen verwalten + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Kontrollbox auswählen, um eine Schnittstelle auszublenden oder anzuzeigen.</p></body></html> + + + Local Interfaces + Lokale Schnittstellen + + + Show + Anzeigen + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Eine Pipe hinzufügen um daraus aufzuzeichnen oder eine existierende Pipe aus der Liste zu löschen.</p></body></html> + + + Pipes + Pipes + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Eine neue Pipe mit Standardeinstellungen hinzufügen.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Ausgewählte Pipe aus der Liste löschen.</p></body></html> + + + Remote Interfaces + Entfernte Schnittstellen + + + Host / Device URL + Host / Geräte URL + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>Ein entferntes System und die dazugehörigen Schnittstellen hinzufügen</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>Ausgewählten Host von der Liste entfernen.</p></body></html> + + + Remote Settings + Entfernte Einstellungen + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Diese Version von Wireshark speichert keine Einstellungen für Pipes. + + + This version of Wireshark does not save remote settings. + Diese Version von Wireshark unterstützt nicht das Sichern von Einstellungen von entfernten Systemen. + + + This version of Wireshark does not support remote interfaces. + Diese Version von Wireshark unterstützt keine entfernten Schnittstellen. + + + New Pipe + Neue Pipe + + + + ManufDialog + + MAC Address Blocks + MAC-Adressblöcke + + + Search MAC address or address prefix. Special purpose bits are masked. + Suche nach MAC-Adresse oder -Adresspräfix. Bits mit speziellem Zweck werden maskiert. + + + MAC Address + MAC Adresse + + + Search vendor name using a case-insentitive regular expression. + Suche nach Hersteller mit regulärem Ausdruck (Groß-/Kleinschreibung irrelevant). + + + Vendor Name + Herstellername + + + Show short name column. + Spalte Kurzname anzeigen. + + + Short name + Kurzname + + + Select all + Alle auswählen + + + Copy + Kopieren + + + Find + Suchen + + + Clear + Zurücksetzen + + + + ManufTableModel + + Address Block + Adressblock + + + Short Name + Kurzname + + + Vendor Name + Herstellername + + + + ModulePreferencesScrollArea + + ScrollArea + Bildlaufbereich + + + + Mtp3SummaryDialog + + Dialog + Dialog + + + MTP3 Summary + MTP3-Zusammenfassung + + + File + Datei + + + Name + Name + + + Length + Länge + + + Format + Format + + + Snapshot length + Schnappschusslänge + + + Data + Daten + + + First packet + Erstes Paket + + + Last packet + Letztes Paket + + + Elapsed + Zeitspanne + + + Packets + Pakete + + + Service Indicator (SI) Totals + Service Indicator (SI) Insgesamt + + + SI + SI + + + MSUs + MSUs + + + MSUs/s + MSUs/s + + + Bytes + Byte + + + Bytes/MSU + Byte/MSU + + + Bytes/s + Byte/s + + + Totals + Insgesamt + + + Total MSUs + MSUs insgesamt + + + Total Bytes + Byte insgesamt + + + Average Bytes/MSU + Durchschnittliche Byte/MSU + + + Average Bytes/s + Durchschnittliche Byte/s + + + + MulticastStatisticsDialog + + UDP Multicast Streams + UDP Multicast Streams + + + Source Address + Quelladresse + + + Source Port + Quellport + + + Destination Address + Zieladresse + + + Destination Port + Zielport + + + Packets + Pakete + + + Packets/s + Pakete/s + + + Avg BW (bps) + Durchschnittliche Bandbreite (bps) + + + Max BW (bps) + Maximale Bandbreite (bps) + + + Max Burst + Maximaler Burst + + + Burst Alarms + Burst Alarme + + + Max Buffers (B) + Maximaler Puffer (B) + + + Buffer Alarms + Pufferalarme + + + Burst measurement interval (ms): + Burst Messintervall (ms): + + + Burst alarm threshold (packets): + Burst Alarmschwellwert (Pakete): + + + Buffer alarm threshold (B): + Puffer Alarmschwellwert (B): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + Entleerungsrate Stream-Puffer (Kb/s): + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + Entleerungsrate Gesamt-Puffer (Kb/s): + + + The burst interval must be between 1 and 1000. + Das Burstintervall muss zwischen 1 und 1000 liegen. + + + The burst alarm threshold isn't valid. + Der Burst Alarmschwellwert ist ungültig. + + + The buffer alarm threshold isn't valid. + Der Puffer Alarmschwellwert ist ungültig. + + + The stream empty speed should be between 1 and 10000000. + Die Entleerungsrate für den Stream-Puffer muss zwischen 1 und 10000000 liegen. + + + The total empty speed should be between 1 and 10000000. + Die Entleerungsrate für den Gesamt-Puffer muss zwischen 1 und 10000000 liegen. + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + %1 Streams, Durchschnittliche Bandbreite: %2bps, Max Bandbreite: %3bps, Max Burst: %4 / %5ms, Max Puffer: %6B + + + + PacketCommentDialog + + Edit Packet Comment + Paketkommentar bearbeiten + + + Add Packet Comment + Paketkommentar hinzufügen + + + + PacketDiagram + + Packet diagram + Paketdiagramm + + + Show Field Values + Zeige Feldwerte + + + Save Diagram As… + Diagramm speichern als… + + + Copy as Raster Image + Als Rasterbild kopieren + + + …as SVG + …als SVG + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + Scalable Vector Graphics (*.svg) + + + Save Graph As… + Graph speichern als… + + + + PacketDialog + + Dialog + Dialog + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + Paketbytes anzeigen + + + Packet %1 + Paket %1 + + + [%1 closed] + [%1 geschlossen] + + + Byte %1 + Byte %1 + + + Bytes %1-%2 + Bytes %1-%2 + + + %Ln byte(s) + + %Ln Byte + %Ln Bytes + + + + + PacketFormatGroupBox + + GroupBox + Gruppierungsbox + + + Packet Format + Paketformat + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>Paketübersichtszeile gleich der Paketliste</p></body></html> + + + Summary line + Übersichtszeile + + + Include column headings + Spaltenüberschrift inkludieren + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>Paketdetails gleich dem Protokollbaum</p></body></html> + + + Details: + Details: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>Nur die obersten Paketdetaileinträge exportieren</p></body></html> + + + All co&llapsed + Alle eingek&lappt + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>Paketdetails so ausgeklappt lassen wie sie eben angezeigt werden.</p></body></html> + + + As displa&yed + Wie ange&zeigt + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>Alle Paketdetaileinträge exportieren</p></body></html> + + + All e&xpanded + Alle auf&geklappt + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>Paketdaten als Hexdump exportieren (ähnlich der Paketbyte-Ansicht)</p></body></html> + + + Bytes + Byte + + + Include secondary data sources + Zweite Datenquelle inkludieren + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + <html><head/><body><p>Generiere zusätzlich zum Frame Hex Dumps für sekundäre Datenquellen, wie zusammengefügte oder entschlüsselte Puffer.</p></body></html> + + + + PacketList + + Protocol Preferences + Protokolleinstellungen + + + Summary as Text + Zusammenfassung als Text + + + …as CSV + …als CSV + + + …as YAML + …als YAML + + + Decode As… + Dekodieren als… + + + Frame %1: %2 + + + Frame %1: %2 + + + + + [ Comment text exceeds %1. Stopping. ] + [ Kommentartext überschreitet %1. Anhalten der Verarbeitung. ] + + + + PacketListHeader + + Align Left + Links ausrichten + + + Align Center + Zentriert ausrichten + + + Align Right + Rechts ausrichten + + + Edit Column + Spalte editieren + + + Resize to Contents + Größe an Inhalt anpassen + + + Column Preferences… + Spalteneigenschaft… + + + Resize Column to Width… + Spaltenbreite an Inhalt anpassen… + + + Resolve Names + Namen auflösen + + + Remove this Column + Spalte entfernen + + + Column %1 + Spalte %1 + + + Width: + Breite: + + + + PacketListModel + + Column + Spalte + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + %1 kann nur mit %2 oder weniger sichtbaren Einträgen sortiert werden; Zwischenspeichergröße in Layout-Einstellungen erhöhen + + + Sorting "%1"… + "%1" wird sortiert… + + + Sorting … + Sortiere ... + + + + PacketRangeGroupBox + + Form + Anordnung + + + Packet Range + Paketbereich + + + - + - + + + Displayed + Angezeigt + + + &Marked packets only + Nur &markierte Pakete + + + &Range: + &Bereich: + + + Remove &ignored packets + &Ignorierte Pakete löschen + + + Include &depended upon packets + Abhängige Pakete einschließen (&d) + + + Also include packets depended upon, such as those used to reassemble displayed packets + Auch abhängige Pakete einschließen, z. B. solche aus zusammengefügten Paketen + + + First &to last marked + Vom ersten bis &zum letzten markierten + + + &All packets + &Alle Pakete + + + &Selected packets only + Nur &selektierte Pakete + + + Captured + Aufgezeichnet + + + + PathSelectionDelegate + + Open a pipe + Öffne eine Pipe + + + + PathSelectionEdit + + Browse + Öffnen + + + Select a path + Pfad auswählen + + + + PluginListModel + + Name + Name + + + Version + Version + + + Type + Typ + + + Path + Pfad + + + + PortsModel + + All entries + Alle Einträge + + + tcp + TCP + + + udp + UDP + + + sctp + SCTP + + + dccp + DCCP + + + Name + Name + + + Port + Port + + + Type + Typ + + + + PreferenceEditorFrame + + Frame + Frame + + + … + ... + + + a preference + eine Einstellung + + + Browse… + Öffnen... + + + Open %1 preferences… + %1 Einstellungen öffnen… + + + Invalid value. + Ungültiger Wert. + + + + PreferencesDialog + + Search: + Suchen: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + Einstellungen + + + + PrefsModel + + Advanced + Erweitert + + + Appearance + Darstellung + + + Layout + Ansicht + + + Columns + Spalten + + + Font and Colors + Schriftart und Farben + + + Capture + Mitschnitt + + + Expert + Experte + + + Filter Buttons + Filterknopf + + + RSA Keys + RSA-Schlüssel + + + + PrintDialog + + Packet Format + Paketformat + + + Print each packet on a new page + Jedes Paket auf eine neue Seite drucken + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>Dateiinformation auf jeder Seite hinzufügen</p></body></html> + + + Capture information header + Kopfzeile mit Mitschnittinformationen + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>&quot;+&quot; und &quot;-&quot; Tasten benutzen, um die Vorschau zu vergrößern oder zu verkleinern. Mit der &quot;0&quot; Taste wird der Zoom zurück gesetzt.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ und - zum Vergrößern/Verkleinern, 0 zum Zurücksetzen</span></p></body></html> + + + Packet Range + Paketbereich + + + Print + Drucken + + + &Print… + Drucken… (&P) + + + Page &Setup… + &Seite einrichten… + + + %1 %2 total packets, %3 shown + %1 %2 Pakete insgesamt, %3 angezeigt + + + Print Error + Fehler beim Drucken + + + Unable to print to %1. + Drucken nach %1 nicht möglich. + + + + ProfileDialog + + Search for profile … + Nach Profil suchen ... + + + Create a new profile using default settings. + Ein neues Profil mit den Standardeinstellungen erstellen. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>Dieses Profil löschen. Systemprofile können nicht gelöscht werden. Das Standardprofil wird beim Löschen zurückgesetzt.</p></body></html> + + + Copy this profile. + Dieses Profil kopieren. + + + Configuration Profiles + Profile konfigurieren + + + Import + noun + Importieren + + + Export + noun + Exportieren + + + From Zip File... + Aus ZIP Datei... + + + From Directory... + Aus Verzeichnis... + + + %Ln Selected Personal Profile(s)... + + %Ln ausgewählte(s) persönliche(s) Profil(e)... + %Ln ausgewählte(s) persönliche(s) Profil(e)... + + + + All Personal Profiles... + Alle persönlichen Profile... + + + New profile + Neues Profil + + + Profile Error + Profil Fehler + + + Exporting profiles + Profile exportieren + + + No profiles found for export + Keine Profile zum Exportieren gefunden + + + Select zip file for export + Zum Exportieren eine Zipdatei auswählen + + + … %Ln selected personal profile(s) + + … %Ln ausgewähltes persönliches Profil + … %Ln ausgewählte persönliche Profile + + + + %Ln selected personal profile(s) + + %Ln ausgewähltes persönliches Profil + %Ln ausgewählte persönliche Profile + + + + An import of profiles is not allowed, while changes are pending + Solange Änderungen noch anstehen können Profile nicht importiert werden + + + An import is pending to be saved. Additional imports are not allowed + Ein Import steht zum sichern aus. Weitere Importe sind nicht erlaubt + + + An export of profiles is only allowed for personal profiles + Es können nur persönliche Profile exportiert werden + + + An export of profiles is not allowed, while changes are pending + Solange Änderungen noch ausstehen dürfen Profile nicht exportiert werden + + + %Ln profile(s) exported + + %Ln Profil exportiert + %Ln Profile exportiert + + + + Select zip file for import + Zum Importieren eine Zipdatei auswählen + + + Select directory for import + Zum Importieren ein Verzeichnis auswählen + + + Zip File (*.zip) + Zipdatei (*.zip) + + + Error + Fehler + + + An error has occurred while exporting profiles + Ein Fehler ist beim exportieren der Profile aufgetreten + + + No profiles found for import in %1 + Keine Profile unter %1 zum Importieren gefunden + + + %Ln profile(s) imported + + %Ln Profil importiert + %Ln Profile importiert + + + + , %Ln profile(s) skipped + + , %Ln Profil übersprungen + , %Ln Profile übersprungen + + + + Importing profiles + Profile importieren + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + ProfileModel + + Resetting to default + Auf Standard zurücksetzen + + + Imported profile + Importierte Profile + + + This is a system provided profile + Dies ist ein vorgegebenes Profil + + + A profile change for this name is pending + Eine Namensänderung für dieses Profil ist ausstehend + + + (See: %1) + (Siehe: %1) + + + This is an invalid profile definition + Profildefinition ist nicht gültig + + + A profile already exists with this name + Ein Profil mit diesem Namen existiert bereits + + + A profile with this name is being deleted + Ein Profil mit diesem Namen wird gelöscht + + + Created from default settings + Erstellt aus den Standardeinstellungen + + + system provided + vom System vorgegeben + + + deleted + gelöscht + + + copy + noun + Kopie + + + Exporting profiles while changes are pending is not allowed + Solange Änderungen noch anstehen können Profile nicht exportiert werden. + + + No profiles found to export + Keine Profile zum Exportieren gefunden + + + Can't delete profile directory + Profilverzeichnis kann nicht gelöscht werden + + + A profile name cannot contain the following characters: %1 + Ein Profilname darf keines der folgenden Zeichen enthalten: %1 + + + A profile name cannot contain the '/' character + Ein Profilname darf kein '/' Zeichen enthalten + + + A profile cannot start or end with a period (.) + Ein Profil darf nicht mit einem Punkt (.) starten oder enden + + + Default + Standard + + + Global + Global + + + Personal + Persönliche + + + Renamed from: %1 + Umbenannt von: %1 + + + Copied from: %1 + Kopiert von: %1 + + + renamed to %1 + umbenannt zu %1 + + + Profile + Profile + + + Type + Typ + + + + ProfileSortModel + + All profiles + Alle Profile + + + Personal profiles + Persönliche Profile + + + Global profiles + Globale Profile + + + + ProgressFrame + + Frame + Frame + + + Loading + Laden + + + + ProtoTree + + Packet details + Paketdetails + + + Not a field or protocol + Kein Feld oder Protokoll + + + No field reference available for text labels. + Keine Feldbeschreibung als Textbezeichnung verfügbar + + + Expand Subtrees + Unterzweig aufklappen + + + Collapse Subtrees + Teilbaum einklappen + + + Expand All + Alles aufklappen + + + Collapse All + Alles einklappen + + + Copy + Kopieren + + + All Visible Items + Alle sichtbaren Elemente + + + All Visible Selected Tree Items + Alle sichtbare ausgewählten Elemente + + + Description + Beschreibung + + + Field Name + Feldname + + + Value + Wert + + + As Filter + Als Filter + + + Wiki Protocol Page + Wikiseite Protokolle (en) + + + Filter Field Reference + Referenz Filterfelder + + + Copied + Kopiert + + + Wiki Page for %1 + Wikiseite für &1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Das Wireshark Wiki wird durch die Community gepflegt.</p><p>Die Seite, die aufgerufen werden soll, kann wundervoll, unvollständig, falsch oder gar nicht vorhanden sein.</p><p>Zum Wiki gehen?</p> + + + Colorize with Filter + Mit Filter einfärben + + + + ProtocolHierarchyDialog + + Dialog + Dialog + + + Protocol + Protokoll + + + Percent Packets + Prozentualer Anteil bei den Paketen + + + Packets + Pakete + + + Percent Bytes + Prozentualer Anteil der Bytes + + + Bytes + Bytes + + + Bits/s + Bits/s + + + End Packets + Pakete (bei denen das Protokoll die höchste Ebene hat) + + + End Bytes + Bytes (bei denen das Protokoll die höchste Ebene hat) + + + End Bits/s + Bits/s (bei denen das Protokoll die höchste Ebene hat) + + + PDUs + PDUs + + + <small><i>A hint.</i></small> + <small><i>Hinweis</i></small> + + + Copy as CSV + Als CSV kopieren + + + Copy stream list as CSV. + Liste der Streams als CSV kopieren. + + + Copy as YAML + Als YAML kopieren + + + Copy stream list as YAML. + Liste der Streams als YAML kopieren. + + + Copy short names + Kurznamen kopieren + + + Copy short protocol names in use. + Protokollkurznamen in Verwendung kopieren. + + + Disable unused protocols + Unbenutzte Protokolle deaktivieren + + + Disable all protocols but those listed. + Alle Protokolle außer gelisteten deaktivieren. + + + Re-enable unused protocols + Unbenutzte Protokolle reaktivieren + + + Re-enable protocols that were disabled in this dialog. + In diesem Dialog deaktivierte Protokolle reaktivieren. + + + Protocol Hierarchy Statistics + Statistiken der Protokollhierarchie + + + Copy + Kopieren + + + as CSV + als CSV + + + as YAML + als YAML + + + protocol short names + Protokollkurznamen + + + Protocols + Protokolle + + + Disable unused + Unbenutzte deaktivieren + + + Revert changes + Änderungen zurücksetzen + + + No display filter. + Kein Anzeigefilter. + + + Display filter: %1 + Anzeigefilter: %1 + + + Unused protocols have been disabled. + Unbenutzte Protokolle wurden deaktiviert. + + + Protocol changes have been reverted. + Protokolländerungen wurden zurückgesetzt. + + + + ProtocolPreferencesMenu + + Protocol Preferences + Protokolleinstellungen + + + No protocol preferences available + Keine Protokolleinstellungen verfügbar + + + Disable %1 + Deaktivere %1 + + + %1 has no preferences + %1 hat keine Einstellungen + + + Open %1 preferences… + %1 Einstellungen öffnen… + + + + QObject + + Average Throughput (bits/s) + Durchschnittlicher Durchsatz (Bits/s) + + + Round Trip Time (ms) + Round-Trip-Zeit (ms) + + + Segment Length (B) + Segmentlänge (B) + + + Sequence Number (B) + Sequenznummer (B) + + + Time (s) + Zeit (s) + + + Window Size (B) + Window Größe (B) + + + [no capture file] + [keine Mitschnittdatei] + + + Conversation + Verbindungen + + + Bars show the relative timeline for each conversation. + Balken zeigen relative Zeitlinie für jede Verbindung. + + + Endpoint + Endpunkt + + + Apply as Filter + Als Filter anwenden + + + Prepare as Filter + Als Filter vorbereiten + + + Find + Finden + + + Colorize + Einfärben + + + Look Up + Nachschlagen + + + Copy + Kopieren + + + UNKNOWN + Unbekannt + + + Selected + Ausgewählt + + + Not Selected + nicht das Ausgewählte + + + …and Selected + …und das Ausgewählte + + + …or Selected + …oder das Ausgewählte + + + …and not Selected + …und nicht das Ausgewählte + + + …or not Selected + …oder nicht das Ausgewählte + + + A + A + + + B + B + + + Any + Alle + + + Don't show this message again. + Diese Mitteilung nicht mehr anzeigen. + + + Multiple problems found + Mehrere Probleme gefunden + + + %1 (%L2%) + %1 (%L2%) + + + No entries. + Keine Einträge. + + + %1 entries. + %1 Einträge. + + + Base station + Base Station + + + <Broadcast> + <Broadcast> + + + <Hidden> + <Hidden> + + + BSSID + BSSID + + + Beacons + Beacons + + + Data Pkts + Data Pakete + + + Protection + Protection + + + Address + Adresse + + + Pkts Sent + Pakete gesendet + + + Pkts Received + Pakete empfangen + + + Comment + Kommentare + + + Wrong sequence number + Falsche Sequenznummer + + + Payload changed to PT=%1 + Payload geändert zu PT=%1 + + + Incorrect timestamp + Falscher Zeitstempel + + + Marker missing? + Marker fehlt? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + Typ + + + UEId + UEId + + + UL Frames + UL Frames + + + UL Bytes + UL Bytes + + + UL MB/s + UL MB/s + + + UL Padding % + UL Padding % + + + UL Re TX + UL Re TX + + + DL Frames + DL Frames + + + DL Bytes + DL Bytes + + + DL MB/s + DL MB/s + + + DL Padding % + DL Padding % + + + DL CRC Failed + DL CRC Fehlerhaft + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + Vordefiniert + + + Unknown (%1) + Unbekannt (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + Unbekannt + + + UE Id + UE Id + + + Name + Name + + + Mode + Modus + + + Priority + Priorität + + + default + Standard + + + DLT %1 + DLT %1 + + + Invalid Display Filter + Ungültiger Anzeigefilter + + + The filter expression %1 isn't a valid display filter. (%2). + Der Filterausdruck %1 ist kein gültiger Anzeigefilter. (%2). + + + Error + Fehler + + + No remote interfaces found. + Keine entfernte Schnittstelle gefunden. + + + PCAP not found + Kein PCAP gefunden + + + Unknown error + Unbekannter Fehler + + + Default + Standard + + + Changed + Geändert + + + Has this preference been changed? + Wurde diese Einstellung geändert? + + + Default value is empty + Standardwert ist leer + + + Gap in dissection + Lücke bei der Dissection + + + Edit… + Editieren… + + + Browse… + Öffnen… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + Entfernte Schnittstelle + + + Host: + Host: + + + Port: + Port: + + + Authentication + Authentifizierung + + + Null authentication + Keine Authentifizierung + + + Password authentication + Authentifizierung mit Passwort + + + Username: + Benutzername: + + + Password: + Passwort: + + + Clear list + Liste löschen + + + Error + Fehler + + + No remote interfaces found. + Keine entfernte Schnittstelle gefunden. + + + PCAP not found + Kein PCAP gefunden + + + + RemoteSettingsDialog + + Remote Capture Settings + Einstellungen für entfernte Aufzeichnung + + + Capture Options + Aufzeichnungsoptionen + + + Do not capture own RPCAP traffic + Eigenen RPCAP Verkehr nicht aufzeichnen + + + Use UDP for data transfer + UDP für Datentransfer nutzen + + + Sampling Options + Sampling Optionen + + + None + Keine + + + 1 of + 1 von + + + packets + Pakete + + + 1 every + 1 jede + + + milliseconds + Millisekunden + + + + ResolvedAddressesDialog + + Dialog + Dialog + + + Hosts + Hosts + + + Search for entry (min 3 characters) + Nach Eintrag suchen (min. 3 Zeichen) + + + Ports + Ports + + + Search for port or name + Nach Port oder Name suchen + + + Capture File Comments + Mitschnittdateikommentare + + + Comment + Kommentare + + + Show the comment. + Kommentar anzeigen. + + + IPv4 Hash Table + IPv4 Hashtabelle + + + Show the IPv4 hash table entries. + Einträge IPv4 Hashtabelle anzeigen. + + + IPv6 Hash Table + IPv6 Hashtabelle + + + Show the IPv6 hash table entries. + Einträge IPv6 Hashtabelle anzeigen. + + + Show All + Alle anzeigen + + + Show all address types. + Alle Adresstypen anzeigen. + + + Hide All + Alle verbergen + + + Hide all address types. + Alle Adresstypen verbergen. + + + IPv4 and IPv6 Addresses (hosts) + IPv4 und IPv6 Adressen (Hosts) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + Zeige auflösbare IPv4 und IPv6 Hostnamen im "Hosts" Format. + + + Port names (services) + Portnamen (Services) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + Zeige auflösbare Portnamen im "Services" Format. + + + Ethernet Addresses + Ethernet Adressen + + + Show resolved Ethernet addresses in "ethers" format. + Zeige auflösbare Ethernet Adressen im "Ethers" Format. + + + Ethernet Well-Known Addresses + Bekannte Ethernet Adressen + + + Show well-known Ethernet addresses in "ethers" format. + Zeige bekannte Ethernet Adressen im "Ethers" Format. + + + Ethernet Manufacturers + Ethernet Hersteller + + + Show Ethernet manufacturers in "ethers" format. + Zeige Ethernet Hersteller im "Ethers" Format. + + + [no file] + [keine Datei] + + + Resolved Addresses + Aufgelöste Adressen + + + # Resolved addresses found in %1 + # Resolved addresses found in %1 + + + # Comments +# +# + # Comments +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + %1 Statistik Verzögerung der Antwortzeiten + + + Type + Typ + + + Messages + Nachrichten + + + Min SRT + In SRT + + + Max SRT + Max SRT + + + Avg SRT + Durchschnittliche SRT + + + Min in Frame + Min im Frame + + + Max in Frame + Max im Frame + + + Open Requests + Offene Anfragen + + + Discarded Responses + Verworfene Antworten + + + Repeated Requests + Wiederholte Anfragen + + + Repeated Responses + Wiederholte Antworten + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>Programm und Version auswählen und bei Bedarf einen Filter eingeben.</i></small> + + + Version: + Version: + + + Program: + Programm: + + + DCE-RPC Service Response Times + DCE RPC Service Antwortzeiten + + + ONC-RPC Service Response Times + ONC-RPC Service Antwortzeiten + + + + RsaKeysFrame + + RSA Keys + RSA-Schlüssel + + + RSA private keys are loaded from a file or PKCS #11 token. + Private RSA Schlüssel werden aus einer Datei oder einem PKCS#11 Token geladen. + + + Add new keyfile… + Neue Schlüsseldatei hinzufügen… + + + Add new token… + Neuen Token hinzufügen… + + + Remove key + Schlüssel entfernen + + + PKCS #11 provider libraries. + PKCS#11 Provider Bibliothek. + + + Add new provider… + Neuen Anbieter hinzufügen… + + + Remove provider + Anbieter entfernen + + + Add PKCS #11 token or key + Füge PKCS#11 Token oder Schlüssel hinzu + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + Keinen neuen PKCS#11 Token oder Schlüssel gefunden, ggf. PKCS#11 Provider hinzufügen. + + + Select a new PKCS #11 token or key + Einen neuen PKCS#11 Token oder Schlüssel auswählen + + + PKCS #11 token or key + PKCS#11 Token oder Schlüssel + + + Enter PIN or password for %1 (it will be stored unencrypted) + PIN oder Passwort für %1 eingeben (dies wird unverschlüsselt gespeichert) + + + Enter PIN or password for key + Geben Sie PIN oder Passwort für den Schlüssel ein + + + Key could not be added: %1 + Schlüssel konnte nicht hinzugefügt werden: %1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + privater RSA Schlüssel (*.pem *.p12 *.pfx *.key);;Alle Dateien ( + + + Select RSA private key file + Wählen Sie die private RSA-Schlüsseldatei aus + + + Libraries (*.dll) + Bibliotheken (*.dll) + + + Libraries (*.so) + Bibliotheken (*.so) + + + Select PKCS #11 Provider Library + PKCS#11 Provider Bibliothek auswählen + + + Changes will apply after a restart + Änderungen werden nach einem Neustart wirksam + + + PKCS #11 provider %1 will be removed after the next restart. + PKCS#11 Provider %1 wird nach dem nächsten Neustart entfernt. + + + + RtpAnalysisDialog + + Dialog + Dialog + + + Packet + Paket + + + Sequence + Sequenz + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter + Jitter (ms) + + + Skew + Versatz + + + Bandwidth + Bandbreite + + + Marker + Marker + + + Status + Status + + + Stream %1 + Stream %1 + + + Stream %1 Jitter + Jitter Stream %1 + + + Stream %1 Difference + Stream %1 Unterschied + + + Stream %1 Delta + Delta Stream %1 + + + %1 streams, + %1 Streams, + + + Save one stream CSV + Speichern ein Stream CSV + + + Save all stream's CSV + Speichern alle Streams CSV + + + &Analyze + &Analysieren + + + Open the analysis window for the selected stream(s) + Analysefenster für die ausgewählten Streams anzeigen + + + &Set List + Liste &setzen + + + &Add to List + Zur Liste hinzufügen &a + + + &Remove from List + Aus Liste entfe&rnen + + + Replace existing list in RTP Analysis Dialog with new one + Vorhandene Liste in RTP Analyse Dialog mit neuer ersetzen + + + Add new set to existing list in RTP Analysis Dialog + Neuen Satz zu vorhandener Liste im RTP Analyse Dialog hinzufügen + + + Remove selected streams from list in RTP Analysis Dialog + Ausgewählte Streams aus RTP Analyse Dialog Liste entfernen + + + Graph + Graph + + + <small><i>A hint.</i></small> + <small><i>Hinweis</i></small> + + + &Export + &Exportieren + + + Open export menu + Exportmenü öffnen + + + CSV + CSV + + + Save tables as CSV. + Tabellen aus CSV speichern. + + + Current Tab Stream CSV + Aktueller Tab Stream CSV + + + Save the table on the current tab as CSV. + Speichere die Tabelle des aktuellen Tabs als CSV + + + All Tab Streams CSV + Alle Tabs Stream CSV + + + Save the table from all tabs as CSV. + Tabellen aller Tabs als CSV Datei sichern. + + + Save Graph + Graph speichern + + + Save the graph image. + Bild des Graphen speichern. + + + Go to Packet + Gehe zu Paket + + + Select the corresponding packet in the packet list. + Wählt das dazugehörige Paket in der Paketliste aus. + + + G + G + + + Next Problem Packet + Nächstes Paket mit Problemen + + + Go to the next problem packet + Gehe zum nächsten Paket mit Problemen + + + N + N + + + Prepare &Filter + &Filter vorbereiten + + + Prepare a filter matching the selected stream(s). + Filter für ausgewählte Streams vorbereiten. + + + &Current Tab + Aktueller Tab (&C) + + + Prepare a filter matching current tab. + Filter für aktuellen Tab vorbereiten. + + + &All Tabs + &Alle Tabs + + + Prepare a filter matching all tabs. + Filter für alle Tabs vorbereiten. + + + RTP Stream Analysis + RTP Stream Analyse + + + Save Graph As… + Graph speichern als… + + + G: Go to packet, N: Next problem packet + G: Gehe zu Paket, N: Nächstes Paket mit einem Problem + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + Komma getrennte Werte (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + % unterstützt nicht PCM bei %2. Bevorzugtes Format ist %3 + + + + RtpPlayerDialog + + RTP Player + RTP Player + + + Play + Abspielen + + + Source Address + Quelladresse + + + Source Port + Quellport + + + Destination Address + Zieladresse + + + Destination Port + Zielport + + + SSRC + SSRC + + + Setup Frame + Setup Frame + + + Packets + Pakete + + + Time Span (s) + Zeitspanne (s) + + + Payloads + Payloads + + + <small><i>No audio</i></small> + <small><i>Kein Audio</i></small> + + + Start playback of all unmuted streams + Abspielen aller nicht Stumm geschalteten Streams starten + + + Pause/unpause playback + Abspielen pausieren/starten + + + Stop playback + Abspielen stoppen + + + Enable/disable skipping of silence during playback + Überspringen von Stille während dem Abspielen aktivieren/deaktivieren + + + Min silence: + Min Stille: + + + Minimum silence duration to skip in seconds + Mindestdauer der Stille in Sekunden zum überspringen + + + Output Device: + Ausgabegerät: + + + Output Audio Rate: + Audio Ausgaberate: + + + Jitter Buffer: + Jitter Puffer: + + + The simulated jitter buffer in milliseconds. + Simulierter Jitter Puffer in Millisekunden. + + + Playback Timing: + Zeitsteuerung beim Abspielen: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Puffer</strong>: Jitter Puffer verwenden um den RTP Stream so darzustellen wie er vom Anwender gehört wurde. +<br/> +<strong>RTP Zeitstempel</strong>: RTP Zeitstempel anstatt der Paketankunftszeit verwenden. Dies gibt einen RTP Stream nicht in der Form wieder wie es der Anwender wahrgenommen hat. Es kann aber hilfreich sein, wenn RTP durch einen Tunnel versendet wurde und die Original Paketzeit fehlt. +<br/> +<strong>Unterbrechungsfreier Modus</strong>: Ignoriert den RTP Zeitstemel. Spielt den zusammengesetzten RTP Stream ab. Kann hilfreich sein, wenn RTP Zeitstempel fehlen. + + + Jitter Buffer + Jitter Puffer + + + RTP Timestamp + RTP Zeitstempel + + + Uninterrupted Mode + Unterbrechungsfreier Modus + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>Zeitstempel als Uhrzeit anzeigen (ausgewählt) oder als Sekunden seit dem Start des Mitschnitts (nicht angewählt).</p></body></html> + + + Time of Day + Uhrzeit + + + &Export + &Exportieren + + + Export audio of all unmuted selected channels or export payload of one channel. + Audio aller nicht stummen und markierten Kanäle exportieren oder Payload eines Kanals exportieren. + + + From &cursor + Von Zeiger &C + + + Save audio data started at the cursor + Audiodaten beginnend ab dem Zeiger speichern + + + &Stream Synchronized Audio + &Synchronisiertes Audio streamen + + + Save audio data synchronized to start of the earliest stream. + Audiodaten synchronisiert zum Startzeitpunkt des früheren Streams speichern. + + + &File Synchronized Audio + Synchronisiertes Audio der Datei (&F) + + + Save audio data synchronized to start of the capture file. + Audiodaten synchronisiert zum Startzeitpunkt der Mitschnittdatei speichern. + + + &Payload + Nutzdaten &P + + + Save RTP payload of selected stream. + RTP Inhalt des ausgewählten Stream speichern. + + + Reset Graph + Graph zurücksetzen + + + Reset the graph to its initial state. + Graph in den Ursprungszustand zurücksetzen. + + + Go To Setup Packet + Zur Paketeinstellung gehen + + + Go to setup packet of stream currently under the cursor + Zur Paketeinstellung für den Stream gehen auf dem aktueller der Mauszeiger zeigt + + + Mute + Stummschalten + + + Mute selected streams + Ausgewählten Stream stumm schalten + + + Unmute + Stummschaltung aufheben + + + Unmute selected streams + Stummschaltung ausgewählter Streams aufheben + + + Invert muting of selected streams + Stummschaltung ausgewählter Streams umkehren + + + Route audio to left channel of selected streams + Audio für ausgewählte Streams auf linken Kanal lenken + + + Route audio to left and right channel of selected streams + Audio für ausgewählte Streams auf linken und rechten Kanal lenken + + + Route audio to right channel of selected streams + Audio für ausgewählte Streams auf rechten Kanal lenken + + + Remove Streams + Streams entfernen + + + Remove selected streams from the list + Ausgewählten Stream aus der Liste entfernen + + + All + Alle + + + Select all + Alle auswählen + + + None + Keine + + + Clear selection + Auswahl löschen + + + Invert + Invertieren + + + Invert selection + Auswahl umkehren + + + Play/Pause + Abspielen/Pause + + + Start playing or pause playing + Abspielen starten oder pausieren + + + Stop + Stoppen + + + Stop playing + Abspielen stoppen + + + I&naudible streams + U&nhörbare Streams + + + Select/Deselect inaudible streams + Unhörbare Streams auswählen/abwählen + + + Inaudible streams + Unhörbare Streams + + + &Select + Au&swählen + + + Select inaudible streams + Unhörbare Streams wählen + + + &Deselect + Abwählen (&D) + + + Deselect inaudible streams + Unhörbare Streams abwählen + + + Prepare &Filter + &Filter vorbereiten + + + Prepare a filter matching the selected stream(s). + Filter für ausgewählte Streams vorbereiten. + + + R&efresh streams + Str&eams aktualisieren + + + Read captured packets from capture in progress to player + Mitgeschnittene Pakete aus laufendem Mitschnitt in Wiedergabefenster laden + + + Zoom In + Vergrößern + + + SR (Hz) + SR (Hz) + + + Sample rate of codec + Abtastrate des Codec + + + PR (Hz) + PR (Hz) + + + Play rate of decoded audio (depends e. g. on selected sound card) + Wiedergaberate des dekodierten Audio (abhängig von z. B. Soundkarte) + + + Zoom Out + Verkleinern + + + Move Left 10 Pixels + Um 10 Bildpunkte nach links verschieben + + + Move Right 10 Pixels + Um 10 Bildpunkte nach rechts verschieben + + + Move Left 1 Pixels + Um 1 Bildpunkt nach links verschieben + + + Move Right 1 Pixels + Um 1 Bildpunkt nach rechts verschieben + + + Go To Packet Under Cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + Go to packet currently under the cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + Play the stream + Stream abspielen + + + To Left + nach Links + + + Left + Right + Links + Rechts + + + To Right + nach Rechts + + + Invert Muting + Stummschaltung umkehren + + + No devices available + Keine Geräte gefunden + + + Select + Auswählen + + + Audio Routing + Audio Routing + + + &Play Streams + Streams abs&pielen + + + Open RTP player dialog + RTP Abspieldialog öffnen + + + &Set playlist + Wiedergabeliste &setzen + + + Replace existing playlist in RTP Player with new one + Vorhandene Wiedergabeliste im RTP Player mit neuer ersetzen + + + &Add to playlist + Zur Wiedergabeliste hinzufügen (&A) + + + Add new set to existing playlist in RTP Player + Neuen Satz zu vorhandener Wiedergabeliste im RTP Player hinzufügen + + + &Remove from playlist + Von Wiedergabeliste entfe&rnen + + + Remove selected streams from playlist in RTP Player + Ausgewählte Streams aus RTP Player Wiedergabeliste entfernen + + + No Audio + Kein Ton + + + Decoding streams... + Streams dekodieren + + + Out of Sequence + Außerhalb der Reihe + + + Jitter Drops + Jitter Drops + + + Wrong Timestamps + Falscher Zeitstempel + + + Inserted Silence + Eingefügte Stille + + + Double click on cell to change audio routing + Doppelklick auf Zelle um Audiorouting zu ändern + + + %1 streams + %1 Streams + + + , %1 selected + , %1 ausgewählt + + + , %1 not muted + , %1 nicht stumm + + + , start: %1. Double click on graph to set start of playback. + , Start: %1. Doppelklicken im Graphen um Wiedergabestart festzulegen. + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + , Start: %1, Cursor: %2. "G" drücken um zu Paket %3 zu springen. Doppelklicken im Graphen um Wiedergabestart festzulegen. + + + Playback of stream %1 failed! + Wiedergabe von Stream %1 fehlgeschlagen! + + + Automatic + Automatisch + + + WAV (*.wav) + WAV (*.wav) + + + Sun Audio (*.au) + Sun Audio (*.au) + + + Save audio + Audio speichern + + + Raw (*.raw) + Raw (*.raw) + + + Save payload + Nutzdaten speichern + + + Warning + Warnung + + + No stream selected or none of selected streams provide audio + Kein Stream ausgewählt oder die ausgewählten Streams beinhalten keinen Ton + + + Error + Fehler + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + Alle ausgewählten Streams müssen dieselbe Ausgaberate verwenden. Manuelles Setzen der Audio Ausgaberate hilft möglicherweise. + + + No streams are suitable for save + Keine Streams können gespeichert werden + + + Save failed! + Speichern fehlgeschlagen! + + + Can't write header of AU file + Kopfdaten der AU Datei können nicht geschrieben werden + + + Can't write header of WAV file + Header der WAV Datei kann nicht gespeichert werden + + + Payload save works with just one audio stream. + Speichern des Payloads funktioniert nur mit einem Stream. + + + Double click to change audio routing + Doppelklicken um das Audiorouting zu ändern + + + Preparing to play... + Zum Abspielen vorbereiten... + + + Unknown + Unbekannt + + + + RtpStreamDialog + + Dialog + Dialog + + + Source Address + Quelladresse + + + Source Port + Quellport + + + Destination Address + Zieladresse + + + Destination Port + Zielport + + + SSRC + SSRC + + + Start Time + Startzeitpunkt + + + Duration + Dauer + + + Payload + Nutzdaten + + + Packets + Pakete + + + Lost + Verloren + + + Max Delta (ms) + Max. Delta (ms) + + + Max Jitter + Maximaler Jitter + + + Mean Jitter + Mittlerer Jitter + + + Status + Status + + + <small><i>A hint.</i></small> + <small><i>Hinweis</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Nur Verbindungen anzeigen, die dem aktuellen Anzeigenfilter entsprechen</p></body></html> + + + Limit to display filter + Auf Anzeigefilter einschränken + + + Time of Day + Uhrzeit + + + Find &Reverse + Den Entgegengesetzten finden (&R) + + + Prepare &Filter + &Filter vorbereiten + + + &Export + &Exportieren + + + &Analyze + Analysieren + + + Open the analysis window for the selected stream(s) and add it to it + Analysenfenster für die ausgewählten Streams anzeigen und zu diesem hinzufügen + + + Find the reverse stream matching the selected forward stream. + Den entgegengesetzten Stream des ausgewählten Streams finden. + + + Min Delta (ms) + Min Delta (ms) + + + Mean Delta (ms) + Durchschnittliches Delta (ms) + + + Min Jitter + Min Jitter + + + All forward/reverse stream actions + Alle vorwärts/rückwärts Stream Aktionen + + + R + R + + + Find All &Pairs + Finde alle &Paare + + + Select all streams which are paired in forward/reverse relation + Wähle alle Streams, die in Vorwärts- und Rückwärtsrichtung verbunden sind + + + Shift+R + Umschalttaste+R + + + Find Only &Singles + Einzelne &suchen + + + Find all streams which don't have paired reverse stream + Finde Streams ohne zugehörige entgegengesetzte Streams + + + Ctrl+R + Strg+R + + + Mark Packets + Pakete markieren + + + Mark the packets of the selected stream(s). + Pakete des ausgewählten Streams markieren. + + + M + M + + + All + Alle + + + Select all + Alle auswählen + + + None + Keine + + + Clear selection + Auswahl löschen + + + Invert + Invertieren + + + Invert selection + Auswahl umkehren + + + Go To Setup + Zum ersten Paket gehen + + + Go to the setup packet for this stream. + Zum ersten Paket dieses Streams gehen. + + + G + G + + + Prepare a filter matching the selected stream(s). + Filter für ausgewählte Streams vorbereiten. + + + P + P + + + Export the stream payload as rtpdump + Streaminhalt als rtpdump exportieren + + + E + E + + + A + A + + + Cop&y + Kopieren (&y) + + + Open copy menu + Kopiermenü öffnen + + + Copy as CSV + Als CSV kopieren + + + Copy stream list as CSV. + Streamliste als CSV kopieren. + + + Copy as YAML + Als YAML kopieren + + + Copy stream list as YAML. + Streamliste als YAML kopieren. + + + RTP Streams + RTP Stream + + + Select + Auswählen + + + as CSV + als CSV + + + as YAML + als YAML + + + %1 streams + %1 Streams + + + , %1 selected, %2 total packets + , %1 ausgewählt, %2 Pakete insgesamt + + + Save RTPDump As… + RTPDump speichern als… + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark - SCTP Associations + + + ID + ID + + + Port 1 + Port 1 + + + Port 2 + Port 2 + + + Number of Packets + Anzahl an Paketen + + + Number of DATA Chunks + Anzahl an DATA Chunks + + + Number of Bytes + Anzahl an Bytes + + + Filter Selected Association + Ausgewählte Association filtern + + + Analyze + Analysieren + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark - Association Analyse + + + TabWidget + TabWidget + + + Statistics + Statistik + + + Chunk Statistics + Chunk Statistik + + + Filter Association + Association filtern + + + Number of Data Chunks from EP2 to EP1: + Anzahl an Datenchunks von EP2 zu EP1: + + + Checksum Type: + Prüfsummentypen: + + + Number of Data Chunks from EP1 to EP2: + Anzahl an Datenchunks von EP1 zum EP2: + + + Number of Data Bytes from EP1 to EP2: + Anzahl an Datenbytes von EP1 zum EP2: + + + Number of Data Bytes from EP2 to EP1: + Anzahl an Datenbytes vom EP2 zum EP1: + + + Endpoint 1 + Endpunkt 1 + + + Graph TSN + TSN Graph + + + Graph Bytes + Bytes Graph + + + Requested Number of Inbound Streams: + Angefrage Anzahl an Inbound Streams: + + + Port: + Port: + + + Sent Verification Tag: + Gesendetes Verification Tag: + + + Minimum Number of Inbound Streams: + Minimale Anzahl an Inbound Streams: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + Komplette Liste aller IP Adressen aus dem INIT Chunk: + + + Minimum Number of Outbound Streams: + Minimale Anzahl an Outbound Streams: + + + Graph Arwnd + Arwnd Graph + + + Endpoint 2 + Endpunkt 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + Komplette Liste aller IP Adressen aus dem INIT_ACK Chunk: + + + Provided Number of Outbound Streams: + Anzahl an zur Verfügung gestellten Outbound Streams: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + SCTP Association Analyse : %1 Port1 %2 Port2 %3 + + + No Association found for this packet. + Keine Association für dieses Paket gefunden. + + + Warning + Warnung + + + Could not find SCTP Association with id: %1 + Es konnten keine SCTP Associationen mit ID %1 gefunden werden: + + + Complete list of IP addresses from INIT Chunk: + Komplette Liste aller IP Adressen aus dem INIT Chunk: + + + Complete list of IP addresses from INIT_ACK Chunk: + Komplette Liste aller IP Adressen aus dem INIT_ACK Chunk: + + + List of Used IP Addresses + Liste an genutzten IP Adressen + + + Used Number of Inbound Streams: + Genutzte Anzahl an Inbound Streams: + + + Used Number of Outbound Streams: + Genutzte Anzahl an Outbound Streams: + + + + SCTPChunkStatisticsDialog + + Dialog + Dialog + + + Association + Association + + + Endpoint 1 + Endpunkt 1 + + + Endpoint 2 + Endpunkt 2 + + + Save Chunk Type Order + Reihenfolge Chunktypen speichern + + + Hide Chunk Type + Chunktypen verbergen + + + Remove the chunk type from the table + Chunktyp aus der Tabelle entfernen + + + Chunk Type Preferences + Einstellungen Chunktyp + + + Go to the chunk type preferences dialog to show or hide other chunk types + In die Chunktypeneinstellung gehen um andere Chunktypen anzuzeigen oder zu verbergen + + + Show All Registered Chunk Types + Alle registrierten Chunktypen anzeigen + + + Show all chunk types with defined names + Alle Chunktypen mit definierten Namen anzeigen + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + SCTP Chunk Statistik: %1 Port1 %2 Port2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + SCTP Graph + + + Reset to full size + Auf volle Größe zurücksetzen + + + Save Graph + Graph speichern + + + goToPacket + Gehe zu Paket + + + Go to Packet + Gehe zu Paket + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP Daten und Adv. Rec. Window im zeitlichen Verlauf: %1 Port1 %2 Port2 %3 + + + No Data Chunks sent + Es wurden keine Datenchunks gesendet + + + Arwnd + Arwnd + + + time [secs] + Zeit [Sek] + + + Advertised Receiver Window [Bytes] + Advertised Receiver Window [Bytes] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>Graph %1: a_rwnd=%2 Zeit=%3 Sekunden </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + SCTP Graph + + + Reset to full size + Auf volle Größe zurücksetzen + + + Save Graph + Graph speichern + + + goToPacket + Gehe zu Paket + + + Go to Packet + Gehe zum Paket + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP Daten und Adv. Rec. Window im zeitlichen Verlauf: %1 Port1 %2 Port2 %3 + + + No Data Chunks sent + Es wurden keine Datenchunks gesendet + + + Bytes + Bytes + + + time [secs] + Zeit [Sek] + + + Received Bytes + Empfangene Bytes + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>Graph %1: Empfangene Bytes=%2 Zeit=%3 Sekunden </i></small> + + + + SCTPGraphDialog + + SCTP Graph + SCTP Graph + + + Relative TSNs + Relative TSNs + + + Only SACKs + Nur SACKs + + + Only TSNs + Nur TSNs + + + Show both + Beide anzeigen + + + Reset to full size + Auf volle Größe zurücksetzen + + + Save Graph + Graph speichern + + + goToPacket + Gehe zu Paket + + + Go to Packet + Gehe zu Paket + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + SCTP TSNs und SACKs im zeitlichen Verlauf: %1 Port1 %2 Port2 %3 + + + No Data Chunks sent + Es wurden keine Datenchunks gesendet + + + CumTSNAck + CumTSNAck + + + Gap Ack + Gap Ack + + + NR Gap Ack + NR Gap Ack + + + Duplicate Ack + Doppelte Ack + + + TSN + TSN + + + time [secs] + Zeit [Sek] + + + TSNs + TSNs + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 Zeit: %3 Sekunden </i></small> + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Graph speichern als… + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <small><i>Ein Kommando auswählen und bei Bedarf einen Filter eingeben. Danach Anwenden.</i></small> + + + Command: + Kommando: + + + SCSI Service Response Times + SCSI Service Antwortzeit + + + + SearchFrame + + Frame + Frame + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>Durchsuchen der Infospalte der Paketliste (Zusammenfassungs-Fenster) oder die Beschriftung der Pakete (Baumansicht-Fenster) oder die nach ASCII konvertierten Paketdaten (Hex-Ansichts-Fenster).</p></body></html> + + + Packet list + Paketliste + + + Packet details + Paketdetails + + + Packet bytes + Paket Bytes + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Zeichenketten suchen, die schmale (UTF-8 oder ASCII) or breite (UTF-16) Zeichen enthalten.</p></body></html> + + + Narrow & Wide + Schmal & breit + + + Narrow (UTF-8 / ASCII) + Schmal (UTF-8 / ASCII) + + + Wide (UTF-16) + Breite Zeichen (UTF-16) + + + Case sensitive + Groß- / Kleinschreibung beachten + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>Nach Daten suchen, durch Angabe eines Anzeigefilters (z.B. ip.addr==10.1.1.1), oder mit einer Hexadezimalen Zeichenkette (z.B. fffffda5) oder einer einfachen Zeichenkette (z.B. Meine Zeichenkette), oder eines regulären Ausdrucks (z.B. colou?r).</p></body></html> + + + Display filter + Anzeigefilter + + + Hex value + Hex-Wert + + + String + Zeichenkette + + + Regular Expression + Regulärer Ausdruck + + + Find + Finden + + + Cancel + Abbrechen + + + No valid search type selected. Please report this to the development team. + Kein gültiger Suchtyp ausgewählt. Bitte melden Sie dies dem Entwickler-Team. + + + Invalid filter. + Ungültiger Filter. + + + That filter doesn't test anything. + Dieser Filter hat keine Auswirkung. + + + That's not a valid hex string. + Keine gültige Hex-Zeichenkette. + + + You didn't specify any text for which to search. + Es wurde kein Text zum Suchen angegeben. + + + No valid character set selected. Please report this to the development team. + Kein gültiger Zeichensatz ausgewählt. Bitte melden Sie dies dem Entwickler-Team. + + + No valid search area selected. Please report this to the development team. + Keinen gültigen Suchbereich ausgewählt. Bitte melden Sie dies dem Entwickler-Team. + + + Searching for %1… + Suche nach %1… + + + No packet contained those bytes. + Keine Pakete mit dieser Bytefolge gefunden. + + + No packet contained that string in its Info column. + Keine Pakete mit dieser Zeichenkette in der Infospalte gefunden. + + + No packet contained that string in its dissected display. + Keine Pakete mit dieser Zeichenkette in der Beschreibung gefunden. + + + No packet contained that string in its converted data. + Keine Pakete mit dieser Zeichenkette in den Rohdaten gefunden. + + + No packet matched that filter. + Keine Pakete entsprechen dem Filter. + + + + SequenceDialog + + Call Flow + Anruf Flow + + + Time + Zeit + + + Comment + Kommentar + + + No data + Keine Daten + + + %Ln node(s) + + %Ln Node + %Ln Nodes + + + + %Ln item(s) + + %Ln Element + %Ln Elemente + + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII (*.txt) + + + Save Graph As… + Graph speichern als… + + + Flow + Flow + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Nützliche Tastenkürzel zur Zeitersparnis</h3> +<table><tbody> + +<tr><th>+</th><td>Vergrößern</td></th> +<tr><th>-</th><td>Verkleinern</td></th> +<tr><th>0</th><td>Graph in Ursprungszustand zurücksetzen</td></th> + +<tr><th>→</th><td>10 Bildpunkte nach rechts</td></th> +<tr><th>←</th><td>10 Bildpunkte nach links</td></th> +<tr><th>↑</th><td>10 Bildpunkte nach oben</td></th> +<tr><th>↓</th><td>10 Bildpunkte nach unten</td></th> +<tr><th><i>Umschalttaste+</i>→</th><td>1 Bildpunkt nach rechts</td></th> +<tr><th><i>Umschalttaste+</i>←</th><td>1 Bildpunkt nach links</td></th> +<tr><th><i>Umschalttaste+</i>↑</th><td>1 Bildpunkt nach oben</td></th> +<tr><th><i>Umschalttaste+</i>↓</th><td>1 Bildpunkt nach unten</td></th> + +<tr><th>g</th><td>Zum Paket gehen, bei dem sich der Zeiger gerade befindet</td></th> +<tr><th>n</th><td>Zum nächsten Paket gehen</td></th> +<tr><th>p</th><td>Zum vorherigen Paket gehen</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>Hinweis</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>Nur Flows anzeigen, die dem aktuellen Anzeigenfilter entsprechen</p></body></html> + + + Limit to display filter + Auf Anzeigefilter einschränken + + + Flow type: + Flow Typ: + + + Addresses: + Adressen: + + + Any + Alle + + + Network + Netzwerk + + + Reset Diagram + Diagramm zurücksetzen + + + Reset &Diagram + &Diagramm zurücksetzen + + + Reset the diagram to its initial state. + Diagramm in Ursprungszustand zurücksetzen + + + 0 + 0 + + + &Reset Diagram + Diagramm zu&rücksetzen + + + Reset the diagram to its initial state + Diagramm in Ursprungszustand zurücksetzen + + + &Export + &Exportieren + + + Export diagram + Diagramm exportieren + + + Zoom In + Vergrößern + + + + + + + + + Zoom Out + Verkleinern + + + - + - + + + Move Up 10 Pixels + Um 10 Bildpunkte nach oben verschieben + + + Up + Rauf + + + Move Left 10 Pixels + Um 10 Bildpunkte nach links verschieben + + + Left + Links + + + Move Right 10 Pixels + Um 10 Bildpunkte nach rechts verschieben + + + Right + Rechts + + + Move Down 10 Pixels + Um 10 Bildpunkte nach unten verschieben + + + Down + Runter + + + Move Up 1 Pixel + Um 1 Bildpunkt nach oben verschieben + + + Shift+Up + Shift+Up + + + Move Left 1 Pixel + Um 1 Bildpunkt nach links verschieben + + + Shift+Left + Shift+Left + + + Move Right 1 Pixel + Um 1 Bildpunkt nach rechts verschieben + + + Shift+Right + Shift+Right + + + Move Down 1 Pixel + Um 1 Bildpunkt nach unten verschieben + + + Shift+Down + Shift+Down + + + Go To Packet Under Cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + Go to packet currently under the cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + G + G + + + All Flows + Alle Flows + + + Show flows for all packets + Flows für alle Pakete anzeigen + + + 1 + 1 + + + TCP Flows + TCP Flows + + + Show only TCP flow information + Nur TCP Flow Informationen anzeigen + + + Go To Next Packet + Zum nächsten Paket gehen + + + Go to the next packet + Zum nächsten Paket gehen + + + N + N + + + Go To Previous Packet + Zum vorherigen Paket gehen + + + Go to the previous packet + Zum vorherigen Paket gehen + + + P + P + + + Select RTP Stream + RTP Stream auswählen + + + Select RTP stream in RTP Streams dialog + RTP Stream im RTP Streams Dialog auswählen + + + S + S + + + Deselect RTP Stream + RTP Stream abwählen + + + Deselect RTP stream in RTP Streams dialog + RTP Stream im RTP Streams Dialog abwählen + + + D + D + + + + ShortcutListModel + + Shortcut + Tastaturkürzel + + + Name + Name + + + Description + Beschreibung + + + + ShowPacketBytesDialog + + Show Packet Bytes + Paketbytes anzeigen + + + Hint. + Hinweis. + + + Decode as + Dekodieren als + + + Show as + Anzeigen als + + + Start + Start + + + End + Ende + + + Find: + Suchen: + + + Find &Next + &Nächstes suchen + + + Frame %1, %2, %Ln byte(s). + + Frame %1, %2, %Ln Byte. + Frame %1, %2, %Ln Bytes. + + + + None + Keine + + + Base64 + Base64 + + + Compressed + Komprimiert + + + Hex Digits + Hexzahlen + + + Percent-Encoding + Prozent-Kodierung + + + Quoted-Printable + Zitiert-Druckbar + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII & Control + + + C Array + C Array + + + EBCDIC + EBCDIC + + + Hex Dump + Hex Dump + + + HTML + HTML + + + Image + Abbild + + + Raw + Roh + + + Rust Array + Rust Array + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Drucken + + + Copy + Kopieren + + + Save as… + Speichern als… + + + Save Selected Packet Bytes As… + Ausgewählte Paketbytes speichern als… + + + Displaying %Ln byte(s). + + Zeige %Ln Byte an. + Zeige %Ln Bytes an. + + + + JSON + JSON + + + Regex Find: + Regex Suchen: + + + + ShowPacketBytesTextEdit + + Show Selected + Ausgewählte anzeigen + + + Show All + Alle anzeigen + + + + SplashOverlay + + Initializing dissectors + Dissektoren initialisieren + + + Initializing tap listeners + Tap Listener initialisieren + + + Initializing external capture plugins + Initialisieren von externen Mitschnitt Plugins + + + Registering dissectors + Dissektoren registrieren + + + Registering plugins + Registering dissector + Plugins registrieren + + + Handing off dissectors + Dissectoren übergeben + + + Handing off plugins + Plugins übergeben + + + Loading Lua plugins + Lua Plugins laden + + + Removing Lua plugins + Lua Plugins entfernen + + + Loading module preferences + Moduleinstellungen laden + + + Finding local interfaces + Lokale Schnittstellen finden + + + Applying changed preferences + Geänderte Einstellungen anwenden + + + (Unknown action) + (Unbekannte Aktion) + + + + StatsTreeDialog + + Configuration not found + Konfiguration nicht gefunden + + + Unable to find configuration for %1. + Konfiguration für %1 nicht auffindbar. + + + + StripHeadersDialog + + Dialog + Dialog + + + Display filter: + Anzeigefilter: + + + + SupportedProtocolsDialog + + Dialog + Dialog + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Die Liste von Feldnamen durchsuchen.</p></body></html> + + + Search: + Suchen: + + + <small><i>Gathering protocol information…</i></small> + <small><i>Sammle Protokollinformationen...</i></small> + + + Supported Protocols + Unterstützte Protokolle + + + %1 protocols, %2 fields. + %1 Protokolle, %2 Felder. + + + + SupportedProtocolsModel + + Name + Name + + + Filter + Filter + + + Type + Typ + + + Description + Beschreibung + + + + SyntaxLineEdit + + Invalid filter: %1 + Ungültiger Filter: %1 + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + "%1" wurde durch "%2" abgelöst. Siehe Benutzerhandbuch Abschnitt 6.4.8 für weitere Details. + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>>Nützliche Tastenkürzel zur Zeitersparnis</h3> +<table><tbody> + +<tr><th>+</th><td>Vergrößern</td></th> +<tr><th>-</th><td>Verkleinern</td></th> +<tr><th>x</th><td>X-Achse vergrößern</td></th> +<tr><th>X</th><td>X-Achse verkleinern</td></th> +<tr><th>y</th><td>Y-Achse vergrößern</td></th> +<tr><th>Y</th><td>Y-Achse verkleinern</td></th> +<tr><th>0</th><td>Graph in Ursprungszustand zurücksetzen</td></th> + +<tr><th>→</th><td>10 Bildpunkte nach rechts</td></th> +<tr><th>←</th><td>10 Bildpunkte nach links</td></th> +<tr><th>↑</th><td>10 Bildpunkte nach oben</td></th> +<tr><th>↓</th><td>10 Bildpunkte nach unten</td></th> +<tr><th><i>Umschalttaste+</i>→</th><td>1 Bildpunkt nach rechts</td></th> +<tr><th><i>Umschalttaste+</i>←</th><td>1 Bildpunkt nach links</td></th> +<tr><th><i>Umschalttaste+</i>↑</th><td>1 Bildpunkt nach oben</td></th> +<tr><th><i>Umschalttaste+</i>↓</th><td>1 Bildpunkt nach unten</td></th> + +<tr><th><i>Bild nach oben</i></th><td>Nächster Stream</td></th> +<tr><th><i>Bild nach unten</i></th><td>Vorheriger Stream</td></th> +<tr><th>d</th><td>Richtung tauschen (TCP Endpunkte tauschen)</td></th> +<tr><th>g</th><td>Zum Paket gehen, bei dem sich der Zeiger gerade befindet</td></th> + +<tr><th>z</th><td>Maustastenverhalten umschalten: Verschieben / Zoomen</td></th> +<tr><th>s</th><td>Umschalten relative/absolute Sequenznummern</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Leertaste</th><td>Fadenkreuz ein/ausblenden</td></th> + +<tr><th>1</th><td>Round-Trip-Zeit-Graph</td></th> +<tr><th>2</th><td>Durchsatzgraph</td></th> +<tr><th>3</th><td>Stevens-Stil Zeit / Sequenznummern Graph</td></th> +<tr><th>4</th><td> tcptrace-Stil Zeit / Sequenznummern Graph</td></th> +<tr><th>5</th><td>Window Scaling Graph</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>Zum Anzeigen der Tastenkürzel mit dem Mauszeiger überfahren</i></small> + + + Type + Typ + + + MA Window (s) + MA Fenster (s) + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + Sowohl SACK Segmente wie auf Datenpakete bei einem Klick auf den Graphen auswählen + + + Select SACKs + select SACKs + SACKs auswählen + + + Stream + Stream + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Richtung der Verbindung wechseln (den entgegengesetzten Flow anzeigen).</p></body></html> + + + Switch Direction + Richtung wechseln + + + Mouse + Maus + + + Drag using the mouse button. + Mit der Maustaste ziehen. + + + drags + Ziehen + + + Select using the mouse button. + Mit der Maustaste auswählen. + + + zooms + Zoomen + + + Display Round Trip Time vs Sequence Number + Zeige Round-Trip-Zeit zur Sequenznummer an + + + RTT By Sequence Number + RTT pro Sequenznummer + + + Display graph of Segment Length vs Time + Graph Segmentlänge zu Zeit anzeigen + + + Segment Length + Segmentlänge + + + Display graph of Mean Transmitted Bytes vs Time + Graph durchschnittliche übertragene Bytes zu Zeit anzeigen + + + Display graph of Mean ACKed Bytes vs Time + Graph durchschnittliche bestätigte Bytes (ACK) zu Zeit anzeigen + + + Goodput + Goodput + + + Display graph of Receive Window Size vs Time + Graph Empfange Bytes zu Zeit anzeigen + + + Rcv Win + Empfangsfenster + + + Display graph of Outstanding Bytes vs Time + Zeige Graph für ausstehende Bytes zu Zeit + + + Bytes Out + Ausgehende Bytes + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Graph auf Ursprungszustand zurücksetzen.</p></body></html> + + + Reset + Zurücksetzen + + + Reset Graph + Graph zurücksetzen + + + Reset the graph to its initial state. + Graph in den Ursprungszustand zurücksetzen. + + + 0 + 0 + + + Zoom In + Vergrößern + + + + + + + + + Zoom Out + Verkleinern + + + - + - + + + Move Up 10 Pixels + Um 10 Bildpunkte nach oben verschieben + + + Up + Rauf + + + Move Left 10 Pixels + Um 10 Bildpunkte nach links verschieben + + + Left + Links + + + Move Right 10 Pixels + Um 10 Bildpunkte nach rechts verschieben + + + Right + Rechts + + + Move Down 10 Pixels + Um 10 Bildpunkte nach unten verschieben + + + Down + Runter + + + Move Up 1 Pixel + Um 1 Bildpunkt nach oben verschieben + + + Shift+Up + Shift+Up + + + Move Left 1 Pixel + Um 1 Bildpunkt nach links verschieben + + + Shift+Left + Shift+Left + + + Move Right 1 Pixel + Um 1 Bildpunkt nach rechts verschieben + + + Shift+Right + Shift+Right + + + Move Down 1 Pixel + Um 1 Bildpunkt nach unten verschieben + + + Shift+Down + Shift+Down + + + Next Stream + Nächster Stream + + + Go to the next stream in the capture + Zum nächsten Stream im Mitschnitt wechseln + + + PgUp + Bild nach oben + + + Previous Stream + Vorheriger Stream + + + Go to the previous stream in the capture + Zum vorherigen Stream im Mitschnitt wechseln + + + PgDown + Bild nach unten + + + Switch direction (swap TCP endpoints) + Richtung tauschen (TCP Endpunkte vertauschen) + + + D + D + + + Go To Packet Under Cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + Go to packet currently under the cursor + Zum Paket gehen, auf das der Mauszeiger gerade zeigt + + + G + G + + + Drag / Zoom + Verschieben / Zoomen + + + Toggle mouse drag / zoom behavior + Maustastenverhalten umschalten: Verschieben / Zoomen + + + Z + Z + + + Relative / Absolute Sequence Numbers + Relative / Absolute Sequenznummer + + + Toggle relative / absolute sequence numbers + Umschalten zwischen relativer und absoluter Sequenznummer + + + S + S + + + Capture / Session Time Origin + Uhrzeit / verstrichene Zeit + + + Toggle capture / session time origin + Umschalten zwischen Uhrzeit und verstrichener Zeit + + + T + T + + + Crosshairs + Fadenkreuz + + + Toggle crosshairs + Fadenkreuz ein-/ausblenden + + + Space + Space + + + Round Trip Time + Round-Trip-Zeit + + + Switch to the Round Trip Time graph + Zum Round-Trip-Zeit-Graph wechseln + + + 1 + 1 + + + Throughput + Durchsatz + + + Switch to the Throughput graph + Zum Durchsatz Graph wechseln + + + 2 + 2 + + + Time / Sequence (Stevens) + Zeit / Sequenznummern (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + Zum Stevens-Stil Zeit / Sequenznummern Graph wechseln + + + 3 + 3 + + + Window Scaling + Window Skalierung + + + Switch to the Window Scaling graph + Zum Window Scaling Graph wechseln + + + 5 + 5 + + + Time / Sequence (tcptrace) + Zeit / Sequenznummern (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + Zum tcptrace-Stil Zeit / Sequenznummern Graph wechseln + + + 4 + 4 + + + Zoom In X Axis + X-Achse vergrößern + + + X + X + + + Zoom Out X Axis + X-Achse verkleinern + + + Shift+X + Shift+X + + + Zoom In Y Axis + Y-Achse vergrößern + + + Y + Y + + + Zoom Out Y Axis + Y-Achse verkleinern + + + Shift+Y + Shift+Y + + + Save As… + Speichern als… + + + No Capture Data + Keine aufgezeichneten Daten + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 %2 Pakete, %3 %4 %5 Pakete, %6 + + + Sequence Numbers (Stevens) + Sequenznummern (Stevens) + + + Sequence Numbers (tcptrace) + Sequenznummern (tcptrace) + + + (MA) + (MA) + + + (%1 Segment MA) + (%1 Segment MA) + + + [not enough data] + [Nicht genügend Daten vorhanden] + + + for %1:%2 %3 %4:%5 + von %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + Click to select packet + Klicken um ein Paket auszuwählen + + + Packet + Paket + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Loslassen um zu zoomen, x = %1 bis %2, y = %3 bis %4 + + + Unable to select range. + Bereich kann nicht ausgewählt werden. + + + Click to select a portion of the graph. + Klicken um ein Teil des Graphen auszuwählen. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Graph speichern als… + + + + TLSKeylogDialog + + Dialog + Dialog + + + Browse… + + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + Speichern + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + Dialog + + + Item + Element + + + <small><i>A hint.</i></small> + <small><i>Hinweis</i></small> + + + Display filter: + Anzeigefilter: + + + Regenerate statistics using this display filter + Statistiken basierend auf dem Anzeigenfilter neu erstellen + + + Apply + Anwenden + + + Copy + Kopieren + + + Copy a text representation of the tree to the clipboard + Baum in Textform in die Zwischenablage kopieren + + + Save as… + Save as... + Speichern als... + + + Save the displayed data in various formats + Die angezeigten Daten in verschiedenen Formaten speichern + + + Collapse All + Alles einklappen + + + Expand All + Alles aufklappen + + + Save Statistics As… + Statistik speichern als… + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + Reine Textdatei (*.txt);;Komma getrennte Werte (*.csv);;XML Dokument (*.xml);;YAML Dokument (*.yaml) + + + Plain text file (*.txt) + Reine Textdatei (*.txt) + + + Error saving file %1 + Fehler beim Speichern der Datei %1 + + + + TimeShiftDialog + + Shift all packets by + Alle Paket verschieben um + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + Zeit setzen für Paket + + + to + bis + + + …then set packet + ...then set packet + ...und für das Paket + + + and extrapolate the time for all other packets + und die Zeit für die restlichen Pakete daraus ableiten + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[JJJJ-MM-TT] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + Verschieben rückgängig machen + + + Time Shift + Zeit verschieben + + + Frame numbers must be between 1 and %1. + Framenummer muss zwischen 1 und %1 liegen. + + + Invalid frame number. + Ungültige Framenummer. + + + Time shifting is not available while capturing packets. + Zeitverschieben ist während der Aufzeichnung nicht möglich. + + + + TrafficTab + + Map file error + Fehler bei der Kartendatei + + + Could not open base file %1 for reading: %2 + Datei %1 konnte nicht zum Lesen geöffnet werden: %2 + + + No endpoints available to map + Keine Geräte gefunden + + + Unable to create temporary file + Fehler beim Erstellen der temporären Datei + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>Anstatt einfachen Werten die aufgelösten Adressnamen und Portnamen anzeigen. Die entsprechende Namenauflösung muss hierfür aktiviert sein.</p></body></html> + + + Name resolution + Namensauflösung + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Nur Verbindungen anzeigen, die dem aktuellen Anzeigenfilter entsprechen</p></body></html> + + + Limit to display filter + Auf Anzeigenfilter einschränken + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + <html><head/><body><p>Nur Typen anzeigen, die zum Filterwert passen</p></body></html> + + + Filter list for specific type + Filtere Liste nach spezifischem Typ + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>Absolute Zeit in der Spalte Startzeit anzeigen.</p></body></html> + + + GroupBox + Gruppierungsbox + + + Absolute start time + Absolute Startzeit + + + Copy + Kopieren + + + Unknown + Unbekannt + + + + TrafficTree + + Resize all columns to content + Alle Spaltenbreiten an Inhalt anpassen + + + Filter on stream id + Filtere nach Stream ID + + + Copy %1 table + Kopiere %1 Tabelle + + + as CSV + als CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + Alle Werte dieser Seite im CSV-Format (Comma Separated Values) in die Zwischenablage kopieren. + + + as YAML + als YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + Alle Werte dieser Seite im YAML-Format in die Zwischenablage kopieren. + + + as JSON + Als JSON + + + Copy all values of this page to the clipboard in the JSON data serialization format. + Alle Werte dieser Seite im JSON-Format in die Zwischenablage kopieren. + + + Save data as raw + Rohdaten speichern + + + Disable data formatting for export/clipboard and save as raw data + Datenformatierung für Export/Zwischenablage und Speichern deaktivieren + + + + TrafficTreeHeaderView + + Less than + Kleiner als + + + Greater than + Größer als + + + Equal + Gleich + + + Columns to display + Anzuzeigende Spalten + + + Filter %1 by + %1 filtern nach + + + Enter filter value + Filterwert eingeben + + + + TrafficTypesModel + + Protocol + Protokoll + + + + UatDialog + + Create a new entry. + Einen neuen Eintrag erstellen. + + + Remove this entry. + Remove this profile. + Diesen Eintrag entfernen. + + + Copy this entry. + Copy this profile. + Diesen Eintrag kopieren. + + + Move entry up. + Eintrag nach oben verschieben. + + + Move entry down. + Eintrag nach unten verschieben. + + + Clear all entries. + Alle Einträge löschen. + + + Unknown User Accessible Table + Unbekannte User Accessible Table + + + Open + Öffnen + + + + UatFrame + + Frame + Frame + + + Create a new entry. + Einen neuen Eintrag erstellen. + + + Remove this entry. + Diesen Eintrag entfernen. + + + Copy this entry. + Diesen Eintrag kopieren. + + + Move entry up. + Eintrag nach oben verschieben. + + + Move entry down. + Eintrag nach unten verschieben. + + + Clear all entries. + Alle Einträge löschen. + + + Copy entries from another profile. + Einträge von einem anderen Profil kopieren. + + + Copy from + Kopieren von + + + Unknown User Accessible Table + Unbekannte User Accessible Table + + + Open + Öffnen + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Nur Verbindungen anzeigen, die dem aktuellen Anzeigenfilter entsprechen</p></body></html> + + + Limit to display filter + Auf Anzeigefilter einschränken + + + Time of Day + Uhrzeit + + + Flow &Sequence + Flow &Sequenzen + + + Show flow sequence for selected call(s). + Flow Sequenz für ausgewählte/n Anruf/e anzeigen. + + + Prepare &Filter + &Filter vorbereiten + + + Prepare a filter matching the selected calls(s). + Filter für ausgewählte Anrufe vorbereiten. + + + Cop&y + Kopieren (&y) + + + Open copy menu + Kopiermenü öffnen + + + All + Alle + + + Select all + Alle auswählen + + + None + Keine + + + Invert + Invertieren + + + Invert selection + Auswahl umkehren + + + Select related RTP streams + Zugehörige RTP Streams auswählen + + + Select RTP streams related to selected calls in RTP Streams dialog + Zu den im RTP Streams Dialog ausgewählten Anrufen zugehörige RTP Streams auswählen + + + S + S + + + Deselect related RTP Streams + Zugehörige RTP Streams abwählen + + + D + D + + + Clear selection + Auswahl löschen + + + Display time as time of day + Uhrzeit als Tageszeit anzeigen + + + Copy as CSV + Als CSV kopieren + + + Copy stream list as CSV. + Liste der Streams als CSV kopieren. + + + Copy as YAML + Als YAML kopieren + + + Copy stream list as YAML. + Liste der Streams als YAML kopieren. + + + SIP Flows + SIP Flows + + + VoIP Calls + VoIP Anrufe + + + as CSV + als CSV + + + as YAML + als YAML + + + Select + Auswählen + + + + VoipCallsInfoModel + + On + An + + + Off + Aus + + + Tunneling: %1 Fast Start: %2 + Tunneling: %1 Fast Start: %2 + + + Start Time + Startzeitpunkt + + + Stop Time + Endzeitpunkt + + + Initial Speaker + Initiierender Sprecher + + + From + Von + + + To + Nach + + + Protocol + Protokoll + + + Duration + Dauer + + + Packets + Pakete + + + State + Zustand + + + Comments + Kommentare + + + + WelcomePage + + Form + Anordnung + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Willkommen zu Wireshark</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>Eine Datei aus dem Dateisystem öffnen</p></body></html> + + + <h2>Open</h2> + <h2>Öffnen</h2> + + + Recent capture files + Zuletzt verwendete Mitschnittdateien + + + Capture files that have been opened previously + Zuletzt geöffnete Mitschnittdateien + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>Live Pakete vom Netzwerk aufzeichnen.</p></body></html> + + + <h2>Capture</h2> + <h2>Aufzeichnen</h2> + + + …using this filter: + ...mit diesem Filter: + + + Interface list + Schnittstellenliste + + + List of available capture interfaces + Liste von verfügbaren Mitschnittschnittstellen + + + <h2>Learn</h2> + <h2>Dokumentation</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">Benutzerhandbuch</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Fragen und Antworten</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Listen</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Spenden</a></th> + +</tr></table> +</body></html> + + + Show in Finder + Im Finder anzeigen + + + Show in Folder + Im Ordner anzeigen + + + Welcome to %1 + Willkommen zu %1 + + + All interfaces shown + Alle Schnittstellen anzeigen + + + %n interface(s) shown, %1 hidden + + %n Schnittstelle angezeigt, %1 versteckt + %n Schnittstellen angezeigt, %1 versteckt + + + + You are sniffing the glue that holds the Internet together using Wireshark + Mit Wireshark den Kleber schnüffeln, der das Internet zusammenhält. + + + You are running Wireshark + Sie nutzen Wireshark + + + You receive automatic updates. + Updates werden automatisch heruntergeladen. + + + You have disabled automatic updates. + Automatische Aktualisierung ist deaktiviert. + + + not found + nicht gefunden + + + Copy file path + Dateipfad kopieren + + + Remove from list + Aus Liste entfernen + + + + WirelessFrame + + Frame + Frame + + + Interface + Schnittstelle + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>802.11 Kanal einstellen.</p></body></html> + + + Channel + Kanal + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>Beim Aufzeichenen sowohl Frames mit gültiger Prüfsumme (FCS) als auch mit ungültiger FCS anzeigen.</p></body></html> + + + FCS Filter + FCS Filter + + + All Frames + Alle Frames + + + Valid Frames + Gültige Frames + + + Invalid Frames + Ungültige Frames + + + Wireless controls are not supported in this version of Wireshark. + Wireless Steuerung wird in dieser Wireshark Version nicht unterstützt. + + + External Helper + Externe Helfer + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>IEEE 802.11 Einstellungen (inklusive der Verschlüsselungsschlüssel) anzeigen.</p></body></html> + + + 802.11 Preferences + 802.11-Einstellungen + + + AirPcap Control Panel + AirPcap-Kontrollfeld + + + Open the AirPcap Control Panel + AirPcap-Kontrollfeld öffnen + + + Unable to set channel or offset. + Kanal oder Versatz kann nicht gesetzt werden. + + + Unable to set FCS validation behavior. + FCS Überprüfungsverhalten kann nicht gesetzt werden. + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + Paketnummer %1 enthält keinen TSF Zeitstempel. Zeitschiene wird nicht angezeigt. + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + Paketnummer %u hat einen großen, negativen Sprung bei TSF. Zeitschiene wird nicht angezeigt. Eventuell wurde der TSF Referenzpunkt falsch gesetzt. + + + + WiresharkDialog + + Failed to attach to tap "%1" + Tap "%1" kann nicht angebunden werden + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Gehe zu Paket + + + Cancel + Abbrechen + + + File Set + Dateisatz + + + Export Packet Dissections + Paketdissektion exportieren + + + Export Objects + Objekte exportieren + + + &Zoom + &Zoomen + + + &Time Display Format + Format der &Zeitanzeige + + + Copy + Kopieren + + + Manual pages + Man-Pages + + + Apply as Filter + Als Filter anwenden + + + Prepare as Filter + Als Filter vorbereiten + + + SCTP + SCTP + + + TCP Stream Graphs + TCP Stream Graphen + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &Datei + + + &Capture + &Aufzeichnen + + + &Help + &Hilfe + + + &Go + &Navigation + + + &View + &Ansicht + + + &Analyze + Anal&yse + + + Follow + Folgen + + + &Statistics + &Statistiken + + + 29West + 29West + + + Topics + Themen + + + Queues + Queues + + + UIM + UIM + + + Telephon&y + Telephonie + + + RTSP + RTSP + + + &Edit + &Bearbeiten + + + Packet Comments + Paketkommentare + + + Main Toolbar + Hauptleiste + + + Display Filter Toolbar + Anzeigefilter Werkzeugleiste + + + Open a capture file + Mitschnittdatei öffnen + + + Quit Wireshark + Wireshark beenden + + + &Start + &Starten + + + Start capturing packets + Aufzeichnen von Paketen starten + + + S&top + S&toppen + + + Stop capturing packets + Aufzeichnen von Paketen beenden + + + No files found + Keine Dateien gefunden + + + &Contents + &Inhalt + + + Wireshark Filter + Wireshark Filter + + + TShark + TShark + + + Rawshark + Rawshark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + Text2cap + + + Website + Webseite + + + Downloads + Downloads + + + Wiki + Wiki (en) + + + Sample Captures + Dateien mit Beispielmitschnitten + + + &About Wireshark + &Über Wireshark + + + Ask (Q&&A) + Fragen (Q&&A) (en) + + + Next Packet + Nächstes Paket + + + Go to the next packet + Zum nächsten Paket gehen + + + Previous Packet + Vorheriges Paket + + + Go to the previous packet + Zum vorherigen Paket gehen + + + First Packet + Erstes Paket + + + Go to the first packet + Zum ersten Paket gehen + + + Last Packet + Letztes Paket + + + Go to the last packet + Zum letzten Paket gehen + + + E&xpand Subtrees + Unterzweige aufklappen + + + Expand the current packet detail + Details für das aktuelle Paket aufklappen + + + &Expand All + Alles aufklappen + + + Expand packet details + Paketdetails aufklappen + + + Collapse &All + &Alles einklappen + + + Collapse all packet details + Alle Paketdetails einklappen + + + Go to specified packet + Zum angegebenen Paket gehen + + + Merge one or more files + Ein oder mehrere Dateien zusammenfügen + + + Import a file + Eine Datei importieren + + + &Save + &Speichern + + + Save as a different file + Speichern unter... + + + Export specified packets + Ausgewählte Pakete exportieren + + + Export TLS Session Keys… + TLS Sitzungsschlüssel exportieren... + + + List Files + Dateien auflisten + + + Next File + Nächste Datei + + + Previous File + Vorherige Datei + + + &Reload + Neu laden + + + Options + Optionen + + + Capture options + Aufzeichnungsoptionen + + + Capture filters + Mitschnittfilter + + + Refresh Interfaces + Schnittstellen aktualisieren + + + Refresh interfaces + Schnittstellen aktualisieren + + + &Restart + Neusta&rten + + + Restart current capture + Aktuelle Aufzeichnung neu starten + + + As &CSV… + Als &CSV... + + + As "C" &Arrays… + Als "C" &Array... + + + As P&SML XML… + Als P&SML XML... + + + As P&DML XML… + Als P&DML XML... + + + As &JSON… + Als &JSON... + + + Description + Beschreibung + + + Field Name + Feldname + + + Value + Wert + + + As Filter + Als Filter + + + Close this capture file + Diese Mitschnittdatei schließen + + + Packet: + Paket: + + + Interface Toolbars + Schnittstellen Werkzeugleiste + + + Colorize Conversation + Verbindung einfärben + + + Internals + Internals + + + Additional Toolbars + Zusätzliche Leiste + + + Conversation Filter + Verbindungsfilter + + + Reliable Server Pooling (RSerPool) + Reliable Server Pooling (RSerPool) + + + SOME/IP + SOME/IP + + + &DTN + &DTN + + + Osmux + Osmux + + + &Tools + Tools + &Tools + + + Wireless Toolbar + Wireless Toolbar + + + Help contents + Hilfe Inhalt + + + FAQs + Häufige Fragen (en) + + + Next Packet in Conversation + Nächstes Paket dieser Verbindung + + + Go to the next packet in this conversation + Gehe zum nächsten Paket in dieser Verbindung + + + Previous Packet in Conversation + Vorheriges Paket dieser Verbindung + + + Go to the previous packet in this conversation + Gehe zum vorherigen Paket in dieser Verbindung + + + Next Packet In History + Nächstes Paket in der Historie + + + Go to the next packet in your selection history + Gehe zum nächsten Paket in der ausgewählten Historie + + + Previous Packet In History + Vorheriges Paket in der Historie + + + Go to the previous packet in your selection history + Gehe zum vorherigen Paket in der ausgewählten Historie + + + Collapse Subtrees + Teilbaum einklappen + + + Collapse the current packet detail + Details für das aktuelle Paket einklappen + + + Go to Packet… + Gehe zu Paket... + + + &Merge… + Zusa&mmenführen... + + + &Import from Hex Dump… + Aus Hexdump &importieren... + + + Save this capture file + Speichern dieser Mitschnittdatei + + + Save &As… + Speichern &als... + + + Export Specified Packets… + Spezielle Pakete exportieren... + + + Export Packet &Bytes… + Paket&bytes exportieren... + + + &Print… + Drucken... + + + Reload this file + Datei neu laden + + + Reload as File Format/Capture + Als Datei/Mitschnitt neu laden + + + Copy this item's description + Beschreibung des Elements kopieren + + + Copy this item's field name + Feldnamen des Elements kopieren + + + Copy this item's value + Werte des Elements kopieren + + + Copy this item as a display filter + Dieses Element als Anzeigefilter kopieren + + + Apply as Column + Als Spalte anwenden + + + Create a packet list column from the selected field. + Spalte mit dem ausgewählten Feld in der Paketliste erstellen. + + + Find a packet + Paket suchen + + + Find the next packet + Nächstes Paket finden + + + Find the previous packet + Vorheriges Paket finden + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + Paket &markieren + + + Mark All Displayed + Alle angezeigte Pakete markieren + + + Mark all displayed packets + Alle angezeigte Pakete markieren + + + Unmark all displayed packets + Markierung bei allen angzeigten Pakete entfernen + + + Next Mark + Nächste Markierung + + + Go to the next marked packet + Zum nächsten markierten Paket gehen + + + Previous Mark + Vorherige Markierung + + + Go to the previous marked packet + Zum vorherigen markierten Paket gehen + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + Paket &ignorieren bzw. zurücksetzen + + + Ignore All Displayed + Alle angezeigten Pakete ignorieren + + + Ignore all displayed packets + Alle angezeigten Pakete ignorieren + + + Set/Unset Time Reference + Zeitreferenz setzen/zurücksetzen + + + Set or unset a time reference for this packet + Zeitreferenz für dieses Paket setzen oder zurücksetzen + + + Unset All Time References + Alle Zeitreferenzen zurücksetzen + + + Remove all time references + Alle Zeitreferenzen löschen + + + Next Time Reference + Nächste Zeitreferenz + + + Go to the next time reference + Zur nächsten Zeitreferenz gehen + + + Previous Time Reference + Vorherige Zeitreferenz + + + Go to the previous time reference + Zur vorherigen Zeitreferenz gehen + + + Shift or change packet timestamps + Paketzeitstempel verschieben oder ändern + + + Delete All Packet Comments + Alle Paketkommentare löschen + + + Remove all packet comments in the capture file + Alle Paketkommentare in der Mitschnittdatei löschen + + + &Configuration Profiles… + Profile konfigurieren... (&C) + + + Configuration profiles + Profile konfigurieren + + + Manage your configuration profiles + Konfigurationsprofile verwalten + + + Manage Wireshark's preferences + Einstellungen von Wireshark verwalten + + + Capture File Properties + Eigenschaften der Mitschnittdatei + + + Capture file properties + Eigenschaften der Mitschnittdatei + + + &Protocol Hierarchy + &Protokollhierarchie + + + Show a summary of protocols present in the capture file. + Eine Zusammenfassung über die vorhandenen Protokolle in dieser Mitschnittdatei anzeigen. + + + Capinfos + Capinfos + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + Zeit / Sequenznummern (Stevens) + + + TCP time sequence graph (Stevens) + TCP Zeit / Sequenznummern Graph (Stevens) + + + Throughput + Durchsatz + + + Round Trip Time + Round-Trip-Zeit + + + TCP round trip time + TCP Round-Trip-Zeit + + + Window Scaling + Window Skalierung + + + TCP window scaling + TCP Window Skalierung + + + HTTP/2 Stream + HTTP/2 Stream + + + SIP Call + SIP Anfruf + + + Time Sequence (tcptrace) + Zeit / Sequenznummern (tcptrace) + + + TCP time sequence graph (tcptrace) + TCP Zeit / Sequenznummern Graph (tcptrace) + + + Analyse this Association + Diese Verknüpfung analysieren + + + Show All Associations + Zeige alle Verknüpfungen + + + Flow Graph + Flow Graph + + + Flow sequence diagram + Flow Sequenzdiagramm + + + ANCP + ANCP + + + ANCP statistics + ANCP Statistiken + + + Packets sorted by Instance ID + Pakete sortiert nach Instanz ID + + + BACapp statistics sorted by instance ID + BACapp Statistik sortiert nach Instanz ID + + + Packets sorted by IP + Pakete sortiert nach IP + + + BACapp statistics sorted by IP + BACapp Statistik sortiert nach IP + + + Packets sorted by object type + Pakete sortiert nach Objekttyp + + + BACapp statistics sorted by object type + BACapp Statistik sortiert nach Objekttyp + + + Packets sorted by service + Pakete sortiert nach Dienst + + + BACapp statistics sorted by service + BACapp Statistik sortiert nach Dienst + + + Collectd + Collectd + + + Collectd statistics + Collectd Statistik + + + DNS + DNS + + + DNS statistics + DNS Statistiken + + + HART-IP + HART-IP + + + HART-IP statistics + HART-IP Statistik + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + HPFEEDS Statistik + + + HTTP2 + HTTP2 + + + HTTP2 statistics + HTTP2 Statistiken + + + Packet Counter + Anzahl Pakete + + + HTTP packet counter + Anzahl HTTP Pakete + + + Requests + Anfragen + + + HTTP requests + HTTP Anfragen + + + Load Distribution + Lastverteilung + + + HTTP load distribution + HTTP Lastverteilung + + + Packet Lengths + Paketlängen + + + Packet length statistics + Paketlängenstatistik + + + Sametime + Sametime + + + Sametime statistics + Sametime Statistik + + + SOME/IP Messages + SOME/IP Nachrichten + + + SOME/IP Message statistics + SOME/IP Nachrichten Statistiken + + + SOME/IP-SD Entries + SOME/IP-SD-Einträge + + + SOME/IP-SD Entries statistics + SOME/IP-SD-Einträge Statistiken + + + &LTP + &LTP + + + LTP segment and block statistics + LTP Segment und Block Statistiken + + + &ISUP Messages + &ISUP Nachrichten + + + ISUP message statistics + ISUP Nachrichten Statistik + + + Osmux packet counts + Anzahl Osmux Pakete + + + RTSP packet counts + RTSP Paketzähler + + + SM&PP Operations + SM&PP Transaktionen + + + SMPP operation statistics + SMPP Transaktionen Statistik + + + &UCP Messages + &UCP Nachrichten + + + UCP message statistics + UCP Nachrichten Statistik + + + F1AP + F1AP + + + F1AP Messages + F1AP Nachrichten + + + NGAP + NGAP + + + NGAP Messages + NGAP Nachrichten + + + Change the way packets are dissected + Verändert die Dekodierung der Pakete + + + Reload Lua Plugins + Lua Plugins neu laden + + + Reload Lua plugins + Lua Plugins neu laden + + + Advertisements by Topic + Advertisements nach Thema + + + Advertisements by Source + Advertisements nach Quelle + + + Advertisements by Transport + Advertisements nach Transportschicht + + + Queries by Topic + Anfragen nach Thema + + + Queries by Receiver + Anfragen nach Empfänger + + + Wildcard Queries by Pattern + Wildcard Anfragen nach Muster + + + Wildcard Queries by Receiver + Wildcard Anfragen nach Empfänger + + + Advertisements by Queue + Advertisements nach Abarbeitungsschlange + + + Queries by Queue + Anfragen nach Abarbeitungsschlange + + + Streams + Streams + + + LBT-RM + LBT-RM + + + LBT-RU + LBR-RU + + + Filter this Association + Diese Verknüpfung filtern + + + Strip Headers… + Kopfdaten entfernen... + + + Strip headers and export higher level encapsulations to file + Kopfdaten entfernen und Verkapselungen höherer Schichten in Datei exportieren + + + &I/O Graphs + &I/O Graph + + + &Conversations + Verbindungen (&C) + + + &Endpoints + &Endpunkte + + + Shrink the main window text + Text im Hauptfenster verkleinern + + + Return the main window text to its normal size + Text im Hauptfenster auf normale Größe zurücksetzen + + + Reset Layout + Ansicht zurücksetzen + + + Reset appearance layout to default size + Anzeige auf Ausgangsgröße zurücksetzen + + + Seconds Since First Captured Packet + Sekunden seit erstem aufgezeichneten Paket + + + Show packet times as the seconds since the first captured packet. + Zeit des Pakets in Sekunden seit dem ersten aufgezeichneten Paket anzeigen. + + + Tenths of a millisecond + Zehntelmillisekunde + + + Hundredths of a millisecond + Hundertstelmillisekunde + + + Tenths of a microsecond + Zehntelmikrosekunde + + + Hundredths of a microsecond + Hundertstelmikrosekunde + + + Packet &Diagram + Paket&diagramm + + + Show or hide the packet diagram + Paketdiagramm anzeigen oder verstecken + + + Show each conversation hash table + Jede Verbindungshashtabelle zeigen + + + Show each dissector table and its entries + Jede Dissectortabelle und die dazugehörigen Einträge anzeigen + + + Show the currently supported protocols and display filter fields + Die aktuell unterstützten Protokolle und Anzeigefilterfelder anzeigen + + + MAC Statistics + MAC Statistiken + + + LTE MAC statistics + LTE MAC Statistiken + + + RLC Statistics + RLC Statistiken + + + LTE RLC statistics + LTE RLC Statistiken + + + LTE RLC graph + LTE RLC Graph + + + MTP3 Summary + MTP3 Zusammenfassung + + + MTP3 summary statistics + Statistiken MTP3 Zusammenfassung + + + Bluetooth Devices + Bluetooth Geräte + + + Bluetooth HCI Summary + Bluetooth HCI Zusammenfassung + + + Display Filter &Expression… + Anzeigefilt&erausdruck + + + Display Filter Expression… + Anzeigefilterausdruck + + + REGISTER_STAT_GROUP_RSERPOOL + REGISTER_STAT_GROUP_RSERPOOL + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + Beginn von "REGISTER_STAT_GROUP_RSERPOOL" + + + No GSM statistics registered + Keine GSM Statistik registriert + + + No LTE statistics registered + Keine LTE Statistik registriert + + + No MTP3 statistics registered + Keine MTP3 Statistik registriert + + + IAX2 Stream Analysis + Analyse IAX2 Stream + + + Show Packet Bytes… + Paketbytes anzeigen... + + + Go to &Linked Packet + Zu verknüpften Paket gehen (&L) + + + UDP Multicast Streams + UDP Multicast Streams + + + Show UTP multicast stream statistics. + UTP Multicaststream Statistik anzeigen + + + WLAN Traffic + WLAN Traffic + + + Show IEEE 802.11 wireless LAN statistics. + IEEE 802.11 Wireless LAN Statistik anzeigen. + + + Add a display filter button. + Anzeigefilterknopf hinzufügen. + + + Firewall ACL Rules + Firewall ACL Regeln + + + Create firewall ACL rules + Firewall ACL Regeln erstellen + + + &Full Screen + &Vollbild + + + Credentials + Anmeldedaten + + + MAC Address Blocks + MAC Adressblöcke + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + &Optionen... + + + &Wireless + &Wireless + + + Capture &Filters… + Mitschnitt&filter... + + + As Plain &Text… + Als reiner &Text... + + + As Plain &Text + Als reiner &Text... + + + As &CSV + Als &CSV + + + As &YAML + Als &YAML + + + All Visible Items + Alle sichtbaren Elemente + + + All Visible Selected Tree Items + Alle sichtbare ausgewählten Elemente + + + Display Filter &Macros… + Anzeigefilter &Makros... + + + &Find Packet… + Paket &finden... + + + Find Ne&xt + Nächstes finden + + + Find Pre&vious + Vorheriges finden + + + Mark or unmark each selected packet + Jedes ausgewählte Paket markieren bzw. Markierung rückgängig machen + + + Ignore or unignore each selected packet + Jedes ausgewählte Paket ignorieren bzw. Ignorieren zurücksetzen + + + U&nignore All Displayed + Alle angezeigte Pakete nicht mehr ig&norieren + + + Unignore all displayed packets + Alle angezeigte Pakete nicht mehr ignorieren + + + Time Shift… + Zeitverschieben... + + + Inject TLS Secrets + TLS-Secrets einbinden + + + Embed used TLS secrets in the capture file + Verwendete TLS-Secrets in Aufzeichnungsdatei einbetten + + + Discard All Secrets + Alle Secrets verwerfen + + + Discard all decryption secrets in the capture file + Alle Entschlüsselungs-Secrets in Aufzeichnungsdatei verwerfen + + + &Preferences… + Einstellungen... + + + TCP throughput + TCP Durchsatz + + + Request Sequences + Anfrage Sequenzen + + + HTTP Request Sequences + HTTP Anfrage Sequenzen + + + Decode &As… + Dekodieren &als... + + + Export PDUs to File… + PDUs in Datei exportieren... + + + Create graphs based on display filter fields + Graph mit den Feldern des Anzeigefilters erstellen + + + &Main Toolbar + &Hauptleiste + + + Show or hide the main toolbar + Hauptleiste anzeigen oder verstecken + + + &Filter Toolbar + &Filter Werkzeugleiste + + + Show or hide the display filter toolbar + Anzeigefilter-Werkzeugleiste anzeigen oder verstecken + + + Conversations at different protocol levels + Verbindungen auf einer anderen Protokollebene + + + Endpoints at different protocol levels + Endpunkte auf einer anderen Protokollebene + + + Colorize Packet List + Paketliste einfärben + + + Draw packets using your coloring rules + Pakete einfärben + + + &Zoom In + Ver&größern + + + Enlarge the main window text + Text vergrößern + + + Zoom Out + Verkleinern + + + Normal Size + Normale Größe + + + Resize Columns + Spaltengröße anpassen + + + Resize packet list columns to fit contents + Spaltengröße an den Inhalt anpassen + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Datum und Uhrzeit (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + Paket mit Datum und Uhrzeit anzeigen. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Jahr, Tag des Jahres, Uhrzeit (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Zeit des Pakets mit Jahr, Tag des Jahres und Uhrzeit anzeigen. + + + Time of Day (01:02:03.123456) + Uhrzeit (01:02:03.123456) + + + Seconds Since 1970-01-01 + Sekunden seit 1970-01-01 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Zeit des Pakets in Sekunden seit der UNIX / POSIX Epoche (1970-01-01) anzeigen. + + + Seconds Since Previous Captured Packet + Sekunden seit vorherigem aufgezeichneten Paket + + + Show packet times as the seconds since the previous captured packet. + Zeit des Pakets in Sekunden seit dem vorherigen aufgezeichneten Paket anzeigen. + + + Seconds Since Previous Displayed Packet + Sekunden seit dem vorherigen angezeigten Paket + + + Show packet times as the seconds since the previous displayed packet. + Zeit des Pakets in Sekunden seit dem vorherigen angezeigten Paket anzeigen. + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + UTC Datum und Uhrzeit (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Zeit des Pakets mit UTC Datum und Uhrzeit anzeigen. + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + UTC Jahr, Tag des Jahres und Uhrzeit (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + Zeit des Pakets in UTC Jahr, Tag des Jahres und Uhrzeit anzeigen. + + + UTC Time of Day (01:02:03.123456) + UTC Uhrzeit (01:02:03.123456) + + + Show packet times as the UTC time of day. + Zeit des Pakets in UTC Uhrzeit anzeigen + + + Automatic (from capture file) + Automatisch (aus Mitschnittdatei) + + + Use the time precision indicated in the capture file. + Zeitpräzision aus Mitschnittdatei verwenden. + + + Seconds + Sekunden + + + Tenths of a second + Zehntelsekunde + + + Hundredths of a second + Hundertstel + + + Milliseconds + Millisekunden + + + Microseconds + Mikrosekunden + + + Nanoseconds + Nanosekunden + + + Display Seconds With Hours and Minutes + Sekunden mit Stunden und Minuten anzeigen + + + Display seconds with hours and minutes + Sekunden mit Minuten und Stunden anzeigen + + + Resolve &Physical Addresses + &Physische Adressen auflösen + + + Show names for known MAC addresses. Lookups use a local database. + Namen von bekannten MAC Adressen anzeigen. Es wird eine lokale Datenbank hierfür verwendet. + + + Resolve &Network Addresses + &Netzwerkadressen auflösen + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + Namen von bekannten IPv4, IPv6 und IPX Adressen anzeigen. Dies kann Datenverkehr verursachen. + + + Resolve &Transport Addresses + &Transportadressen auflösen + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + Namen von bekannten TCP, UDP und SCTP Diensten anzeigen. Dies kann Datenverkehr verursachen. + + + Wire&less Toolbar + Wire&lessleiste + + + Show or hide the wireless toolbar + Wirelessleiste anzeigen oder verstecken + + + &Status Bar + &Statusleiste + + + Show or hide the status bar + Statusleiste anzeigen oder verstecken + + + Packet &List + Paket&liste + + + Show or hide the packet list + Paketliste anzeigen oder verstecken + + + Packet &Details + Paket&details + + + Show or hide the packet details + Paketdetails anzeigen oder verstecken + + + Packet &Bytes + Paket&bytes + + + Show or hide the packet bytes + Paketbytes anzeigen oder verstecken + + + &Conversation Hash Tables + Verbindungshashtabelle (&C) + + + &Dissector Tables + &Dissector Tabelle + + + &Supported Protocols + Unter&stützte Protokolle + + + MAP Summary + MAP Zusammenfassung + + + GSM MAP summary statistics + GSM MAP Zusammenfassung Statistik + + + RLC &Graph + LRC &Graph + + + &Coloring Rules… + Einfärbungsregeln... + + + Show Linked Packet in New Window + Verknüpftes Paket in einem neuen Fenster anzeigen + + + New Coloring Rule… + New Conversation Rule… + Neue Einfärbungsregel + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + RTP Stream-Analyse für ausgewählten Stream. STRG drücken um entgegengesetzten Stream auch auszuwählen. + + + RTP Player + RTP Player + + + Play selected stream. Press CTRL key for playing reverse stream too. + Ausgewählten Stream abspielen. STRG drücken um entgegengesetzten Stream auch abzuspielen. + + + IA&X2 Stream Analysis + Analyse IA&X2 Stream + + + Enabled Protocols… + Enable Protocols… + Protokolle aktivieren... + + + Wiki Protocol Page + Wikiseite Protokolle (en) + + + Open the Wireshark wiki page for this protocol. + Wireshark Wikiseite für dieses Protokoll öffnen. + + + Filter Field Reference + Referenz Filterfelder + + + Open the display filter reference page for this filter field. + Die Anzeigefilter Rerferenzseite für dieses Feld öffnen. + + + Go to the packet referenced by the selected field. + Zum Paket gehen auf das das ausgewählte Feld referenziert. + + + &VoIP Calls + &VoIP Anrufe + + + Open &Recent + Zuletzt geöffnete Dateien (&R) + + + Name Resol&ution + Namensauflös&ung + + + Service &Response Time + Service Antwo&rtzeit + + + &RTP + &RTP + + + S&CTP + S&CTP + + + &ANSI + &ANSI + + + &GSM + &GSM + + + &LTE + &LTE + + + &MTP3 + &MTP3 + + + &Open + Öffnen (&O) + + + &Quit + Beenden (&Q) + + + &Close + Schließen (&C) + + + Display &Filters… + Anzeige&filter... + + + &Unmark All Displayed + Markier&ung bei allen angezeigten Paketen entfernen + + + All VoIP Calls + Alle VoIP Anrufe + + + SIP &Flows + SIP &Flows + + + SIP Flows + SIP Flows + + + RTP Streams + RTP Streams + + + Edit the packet list coloring rules. + Editieren der Einfärbungsregeln der Paketliste. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Bluetooth ATT Server Attribute + + + Show Packet in New &Window + Paket in einem neuen &Fenster anzeigen + + + Show this packet in a separate window. + Paket in einem separaten Fenster anzeigen. + + + Show the linked packet in a separate window. + Das verknüpfte Paket in einem separaten Fenster anzeigen. + + + Auto Scroll in Li&ve Capture + Während der Aufzeichnung automatisch scrollen + + + Automatically scroll to the last packet during a live capture. + Automatisches Scrollen während der Aufzeichnung. + + + Expert Information + Experteninformationen + + + Show expert notifications + Experteninformationen anzeigen + + + Add an expression to the display filter. + Einen Ausruck zum Anzeigefilter hinzufügen. + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + Beginn von "REGISTER_STAT_GROUP_UNSORTED" + + + No ANSI statistics registered + No tools registered + Keine ANSI Statistik registriert + + + Resolved Addresses + Aufgelöste Adressen + + + Show each table of resolved addresses as copyable text. + Zeige jede Tabelle von ausflösbaren Adressen in Textform an. + + + Color &1 + Farbe &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + Die aktuelle Verbindung einfärben. + + + Color &2 + Farbe &2 + + + Color &3 + Farbe &3 + + + Color &4 + Farbe &4 + + + Color &5 + Farbe &5 + + + Color &6 + Farbe &6 + + + Color &7 + Farbe &7 + + + Color &8 + Farbe &8 + + + Color &9 + Farbe &9 + + + Color 1&0 + Farbe 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + Eine neue Einfärbungsregel basierend auf diesem Feld erstellen. + + + Reset Colorization + Einfärben zurücksetzen + + + Reset colorized conversations. + Einfärbung von Verbindungen zurücksetzen + + + RTP Stream Analysis + RTP Stream Analyse + + + Edit Resolved Name + Auflösbare Namen editieren + + + Manually edit a name resolution entry. + Namensauflösung manuell bearbeiten. + + + Enable and disable specific protocols + Einzelne Protokolle aktivieren/deaktivieren + + + before quitting + vor dem Schließen + + + Save packets before merging? + Pakete vor dem Zusammenführen speichern? + + + A temporary capture file can't be merged. + Eine temporäre Mitschnittdatei kann nicht zusammengeführt werden. + + + Save changes in "%1" before merging? + Vor der Zusammenführung Änderungen in "%1" speichern? + + + Changes must be saved before the files can be merged. + Änderungen müssen vor dem Zusammenführen gespeichert werden. + + + Invalid Display Filter + Ungültiger Anzeigefilter + + + Invalid Read Filter + Ungültiger Lesefilter + + + The filter expression %1 isn't a valid read filter. (%2). + Der Filterausdruck %1 ist kein gültiger Lesefilter. (%2). + + + before importing a capture + before importing a new capture + vor dem Importieren einer Aufzeichnung + + + Unable to export to "%1". + Kann nicht nach "%1" exportieren. + + + You cannot export packets to the current capture file. + Pakete können nicht in die aktuelle Mitschnittdatei exportiert werden. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + Wollen Sie die durchgeführten Änderungen speichern%1? + + + Your captured packets will be lost if you don't save them. + Die aufgezeichneten Pakete gehen verloren wenn sie nicht gespeichert werden. + + + Do you want to save the changes you've made to the capture file "%1"%2? + Sollen die Änderungen an der Mitschnittdatei "%1"%2 gespeichert werden? + + + Your changes will be lost if you don't save them. + Änderungen gehen verloren, wenn sie nicht gesichert werden. + + + Check for Updates… + Nach Aktualisierungen suchen… + + + Unable to drop files during capture. + Dateien können während dem Mitschnitt nicht verworfen werden. + + + Unknown file type returned by merge dialog. + Zusammenführen Dialog meldet einen unbekannten Dateityp + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Bitte melden Sie dies als ein Wireshark Issue unter https://gitlab.com/wireshark/wireshark/-/issues. + + + Unknown file type returned by export dialog. + Exportieren Dialog meldet einen unbekannten Dateityp + + + Do you want to stop the capture and save the captured packets%1? + Wollen Sie die Aufzeichnung stoppen und die aufgezeichneten Pakete speichern%1? + + + Do you want to save the captured packets%1? + Wollen Sie die aufgezeichneten Pakete speichern%1? + + + Save before Continue + Vor dem Fortsetzen speichern + + + Stop and Save + Stoppen und speichern + + + Stop and Quit &without Saving + Stop and Quit without Saving + Stoppen und beenden &ohne zu Speichern + + + Quit &without Saving + Quit without Saving + &Ohne Speichern beenden + + + There is no "rtp.ssrc" field in this version of Wireshark. + In dieser Wireshark-Version ist kein "rtp.ssrc" Feld vorhanden. + + + Please select an RTPv2 packet with an SSRC value + Bitte ein RTPv2 Paket mit einem SSRC Wert auswählen + + + SSRC value not found. + SSRC Wert nicht gefunden + + + Show or hide the toolbar + Werkzeugleise anzeigen oder verstecken + + + Continue &without Saving + Continue without Saving + Fortsetzen &ohne zu Speichern + + + Stop and Continue &without Saving + Stop and Continue without Saving + Stoppen und Fortsetzen &ohne zu Speichern + + + The Wireshark Network Analyzer + Die Wireshark Netzwerk Analysesoftware + + + Capturing from %1 + Aufzeichnen von %1 + + + before opening another file + vor dem Öffnen einer anderen Datei + + + Merging files. + Dateien zusammenfügen + + + %1: %2 + %1: %2 + + + Clear Menu + Menüeinträge löschen + + + before closing the file + vor dem Schließen der Datei + + + Export Selected Packet Bytes + Ausgewählte Paketbytes exportieren + + + No Keys + Keine Schlüssel + + + Export SSL Session Keys (%Ln key(s)) + Export SSL Session Keys (%1 key%2 + + SSL Session Keys exportieren (%Ln Key) + SSL Session Keys exportieren (%Ln Keys) + + + + Raw data (*.bin *.dat *.raw);;All Files ( + Rohdaten (*.bin *.dat *.raw);; Alle Dateien ( + + + Couldn't copy text. Try another item. + Text konnte nicht kopiert werden. Probieren sie einen anderen Eintrag. + + + Are you sure you want to remove all packet comments? + Wollen Sie alle Paketkommentare löschen? + + + Unable to build conversation filter. + Verbindungsfilter kann nicht erstellt werden. + + + before reloading the file + vor dem Neuladen der Datei + + + Error compiling filter for this conversation. + Fehler beim Erstellen eines Filters für diese Verbindung. + + + No previous/next packet in conversation. + Kein vorheriges/nächstes Paket in dieser Verbindung. + + + No interface selected. + Keine Schnittstelle ausgewählt + + + Saving %1… + Speichere %1… + + + Configure all extcaps before start of capture. + Alle Extcaps vor dem Start des Mitschnitts konfigurieren + + + Invalid capture filter. + Ungültiger Mitschnittfilter + + + (empty comment) + placeholder for empty comment + (leerer Kommentar) + + + Add New Comment… + Neuen Kommentar hinzufügen.. + + + Edit "%1" + edit packet comment + Editiere "%1" + + + Delete "%1" + delete packet comment + Lösche "%1" + + + Delete packet comments + Paketkommentare löschen + + + Delete comments from %n packet(s) + + Kommentar von %n Paket löschen + Kommentare von %n Paketen löschen + + + + before starting a new capture + vor dem Starten einer neuen Aufzeichnung + + + before reloading Lua plugins + vor dem Neuladen der Lua Plugins + + + Please wait while Wireshark is initializing… + Bitte Warten während Wireshark initialisiert wird… + + + before updating + vor Aktualisierung + + + There are no TLS Session Keys to save. + Es sind keine TLS Sitzungsschlüssel zum speichern vorhanden. + + + Export TLS Session Keys (%Ln key(s)) + + TLS Sitzungsschlüssel exportieren (%Ln Schlüssel) + TLS Sitzungsschlüssel exportieren (%Ln Schlüssel) + + + + TLS Session Keys (*.keys *.txt);;All Files ( + TLS Sitzungsschlüssel (*.keys *.txt);; Alle Dateien ( + + + No TLS Secrets + Keine TLS-Secrets + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + In der Aufzeichnungsdatei sind keine Secrets zur Entschlüsselung von TLS-Datenverkehr vorhanden. Möchten Sie weitere Informationen zum Entschlüsseln von TLS-Datenverkehr im Wiki anzeigen? + + + Are you sure you want to discard all decryption secrets? + Sind Sie sicher, dass alle Entschlüsselungs-Secrets verworfen werden sollen? + + + No filter available. Try another %1. + Kein Filter verfügbar. Probieren Sie einen anderen %1. + + + column + Spalte + + + item + Element + + + The "%1" column already exists. + Die Spalte "%1" existiert bereits. + + + The "%1" column already exists as "%2". + Die Spalte "%1" existiert bereits als "%2". + + + RTP packet search failed + RTP Paketsuche fehlgeschlagen + + + No Interface Selected. + Keine Schnittstelle ausgewählt. + + + before restarting the capture + vor dem Neustart der Aufzeichnung + + + Wiki Page for %1 + Wikiseite für &1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Das Wireshark Wiki wird durch die Community gepflegt.</p><p>Die Seite, die aufgerufen werden soll, kann wundervoll, unvollständig, falsch oder gar nicht vorhanden sein.</p><p>Zum Wiki gehen?</p> + + + Loading + Laden + + + Reloading + Reloading + + + Rescanning + Rescanning + + + + WlanStatisticsDialog + + Wireless LAN Statistics + WLAN-Statistiken + + + Channel + Kanal + + + SSID + SSID + + + Percent Packets + Prozentualer Anteil bei den Paketen + + + Percent Retry + Prozentsatz Wiederholungen + + + Probe Reqs + Probe Anfragen + + + Probe Resp + Probe Antworten + + + Auths + Auths + + + Retry + Wiederholung + + + Deauths + Dauths + + + Other + Weitere + + + diff --git a/ui/qt/wireshark_dialog.cpp b/ui/qt/wireshark_dialog.cpp new file mode 100644 index 00000000..4045868f --- /dev/null +++ b/ui/qt/wireshark_dialog.cpp @@ -0,0 +1,177 @@ +/* wireshark_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include + +#include "cfile.h" + +#include +#include + +#include "main_application.h" +#include "wireshark_dialog.h" +#include +#include "ui/recent.h" +#include "ui/ws_ui_util.h" + +#include + + +// To do: +// - Use a dynamic property + Q_PROPERTY for the subtitle. +// - Make our nested event loop more robust. See tryDeleteLater for details. + +WiresharkDialog::WiresharkDialog(QWidget &parent, CaptureFile &capture_file) : + GeometryStateDialog(&parent, Qt::Window), + cap_file_(capture_file), + file_closed_(false), + retap_depth_(0), + dialog_closed_(false) +{ + setWindowIcon(mainApp->normalIcon()); + setWindowSubtitle(QString()); + + connect(&cap_file_, &CaptureFile::captureEvent, this, &WiresharkDialog::captureEvent); +} + +void WiresharkDialog::accept() +{ + QDialog::accept(); + dialogCleanup(true); +} + +// XXX Should we do this in WiresharkDialog? +void WiresharkDialog::reject() +{ + QDialog::reject(); + dialogCleanup(true); +} + +void WiresharkDialog::setWindowSubtitle(const QString &subtitle) +{ + subtitle_ = subtitle; + + QString title = mainApp->windowTitleString(QStringList() << subtitle_ << cap_file_.fileTitle()); + QDialog::setWindowTitle(title); +} + +// See if we can destroy ourselves. The user may have clicked "Close" while +// we were deep in the bowels of a routine that retaps packets. Track our +// tapping state using retap_depth_ and our closed state using dialog_closed_. +// +// The Delta Object Rules page on nested event loops: +// +// https://jblog.andbit.net/2007/04/28/delta-object-rules/ +// +// effectively says "don't do that." However, we don't really have a choice +// if we want to have a usable application that retaps packets. + +void WiresharkDialog::dialogCleanup(bool closeDialog) +{ + if (closeDialog) + { + // Cancel any taps in progress? + // cap_file_.setCaptureStopFlag(); + removeTapListeners(); + dialog_closed_ = true; + } + + if (retap_depth_ < 1 && dialog_closed_) { + disconnect(); + deleteLater(); + } +} + +void WiresharkDialog::updateWidgets() +{ + setWindowSubtitle(subtitle_); +} + +bool WiresharkDialog::registerTapListener(const char *tap_name, void *tap_data, const char *filter, guint flags, tap_reset_cb tap_reset, tap_packet_cb tap_packet, tap_draw_cb tap_draw) +{ + GString *error_string = register_tap_listener(tap_name, tap_data, filter, flags, + tap_reset, tap_packet, tap_draw, NULL); + if (error_string) { + QMessageBox::warning(this, tr("Failed to attach to tap \"%1\"").arg(tap_name), + error_string->str); + g_string_free(error_string, TRUE); + return false; + } + + tap_listeners_ << tap_data; + return true; +} + +void WiresharkDialog::captureEvent(CaptureEvent e) +{ + switch (e.captureContext()) + { + case CaptureEvent::Retap: + switch (e.eventType()) + { + case CaptureEvent::Started: + beginRetapPackets(); + break; + case CaptureEvent::Finished: + endRetapPackets(); + break; + default: + break; + } + break; + case CaptureEvent::File: + switch (e.eventType()) + { + case CaptureEvent::Closing: + captureFileClosing(); + break; + case CaptureEvent::Closed: + file_closed_ = true; + captureFileClosed(); + break; + default: + break; + } + break; + default: + break; + } + +} + +void WiresharkDialog::beginRetapPackets() +{ + retap_depth_++; +} + +void WiresharkDialog::endRetapPackets() +{ + retap_depth_--; + dialogCleanup(); +} + +void WiresharkDialog::removeTapListeners() +{ + while (!tap_listeners_.isEmpty()) { + remove_tap_listener(tap_listeners_.takeFirst()); + } +} + +void WiresharkDialog::captureFileClosing() +{ + removeTapListeners(); + updateWidgets(); +} + +void WiresharkDialog::captureFileClosed() +{ + updateWidgets(); +} diff --git a/ui/qt/wireshark_dialog.h b/ui/qt/wireshark_dialog.h new file mode 100644 index 00000000..ad557ebc --- /dev/null +++ b/ui/qt/wireshark_dialog.h @@ -0,0 +1,148 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WIRESHARK_DIALOG_H +#define WIRESHARK_DIALOG_H + +/* + * @file General dialog base class + * + * Base class which provides convenience methods for dialogs that handle + * capture files. + * + * This class attempts to destroy itself when closed. Doing this safely and + * properly can be a bit tricky while scanning and tapping packets since + */ + +// "General" is a misnomer but we already have a class named +// "CaptureFileDialog". Suggestions for a better name from +// https://code.wireshark.org/review/#/c/9739/: +// BaseCaptureDialog, CaptureHelperDialog (or rename CaptureFileDialog to something else - WiresharkFileDialog). +// TapDialog might make sense as well. + +#include + +#include "capture_file.h" +#include "geometry_state_dialog.h" + +class WiresharkDialog : public GeometryStateDialog +{ + Q_OBJECT + +public: + // XXX Unlike the entire QWidget API, parent is mandatory here. + explicit WiresharkDialog(QWidget &parent, CaptureFile &capture_file); + +protected: + virtual void keyPressEvent(QKeyEvent *event) { QDialog::keyPressEvent(event); } + virtual void accept(); + virtual void reject(); + + /** + * @brief Mark the start of a code block that retaps packets. If the user + * closes the dialog while tapping, the dialog will not be destroyed until + * endRetapPackets is called. + * + * This is automatically called when tapping begins, but might need to be + * called explicilty if any member functions are called or variables are + * accessed after tapping is finished. + */ + + void beginRetapPackets(); + /** + * @brief Mark the end of a code block that retaps packets. If the user + * has closed the dialog it will be desroyed at this point. + * + * This is automatically called when tapping ends, but might need to be + * called explicilty if any member functions are called or variables are + * accessed after tapping is finished. + */ + virtual void endRetapPackets(); + + /** + * @brief Set the window subtitle, e.g. "Foo Timeouts". The subtitle and + * file name will be added to the dialog window title. + * @param subtitle The subtitle to add. It should be unique, short, and + * descriptive. + */ + void setWindowSubtitle(const QString &subtitle); + const QString &windowSubtitle() { return subtitle_; } + virtual void updateWidgets(); + + // Capture file and tapping + CaptureFile &cap_file_; + /** + * @brief Convenience wrapper for register_tap_listener. Tap + * listeners registered via this function are automatically + * removed during destruction. They can also be explicitly + * removed using remove_tap_listener or removeTapListeners. + * + * Shows a warning dialog if registration is unsuccessful. + * @param tap_name A registered tap name. + * @param tap_data A unique pointer. Usually 'this'. + * @param filter A display filter. + * @param flags See register_tap_listener. + * @param tap_reset Reset callback. + * @param tap_packet Per-packet callback. + * @param tap_draw Draw callback. + */ + bool registerTapListener(const char *tap_name, void *tap_data, + const char *filter, guint flags, + tap_reset_cb tap_reset, + tap_packet_cb tap_packet, + tap_draw_cb tap_draw); + + /** + * @brief Remove all tap listeners registered via registerTapListener. + */ + virtual void removeTapListeners(); + + /** + * @brief true if the file has been closed, false otherwise. + */ + // XXX Needs a getter? + bool file_closed_; + + /** + * @brief Check to see if the user has closed (and not minimized) the dialog. + * @return true if the dialog has been closed, false otherwise. + */ + bool dialogClosed() { return dialog_closed_; } + + /** + * @brief Called when the capture file is about to close. This can be + * used to disconnect taps and similar actions. + * updateWidgets() is called at the end. + * To enable/disable widgets captureFileClosed() is more suitable. + */ + virtual void captureFileClosing(); + + /** + * @brief Called when the capture file was closed. This can be + * used to enable or disable widgets according to the state of + * file_closed_. + * updateWidgets() is called at the end. + */ + virtual void captureFileClosed(); + +protected slots: + void captureEvent(CaptureEvent); + +private: + void dialogCleanup(bool closeDialog = false); + + QString subtitle_; + QList tap_listeners_; + int retap_depth_; + bool dialog_closed_; + +private slots: +}; + +#endif // WIRESHARK_DIALOG_H diff --git a/ui/qt/wireshark_en.ts b/ui/qt/wireshark_en.ts new file mode 100644 index 00000000..7f183d0c --- /dev/null +++ b/ui/qt/wireshark_en.ts @@ -0,0 +1,14673 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + + + + Wireshark + + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + + + + Copy the version information to the clipboard + + + + Copy to Clipboard + + + + Authors + + + + Search Authors + + + + Folders + + + + Filter by path + + + + Plugins + + + + No plugins found. + + + + Search Plugins + + + + Filter by type: + + + + Keyboard Shortcuts + + + + Search Shortcuts + + + + Acknowledgments + + + + License + + + + The directory does not exist + + + + Should the directory %1 be created? + + + + The directory could not be created + + + + The directory %1 could not be created. + + + + Show in Finder + + + + Show in Folder + + + + Copy + + + + Copy Row(s) + + Copy Row + Copy Rows + + + + + AddressEditorFrame + + Frame + + + + Name Resolution Preferences… + Name Resolution Preferences... + + + + Address: + + + + Name: + + + + Can't assign %1 to %2. + + + + + AdvancedPrefsModel + + Name + + + + Status + + + + Type + + + + Value + + + + + ApplyLineEdit + + Apply changes + + + + + AuthorListModel + + Name + + + + Email + + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + + + + Handle + + + + UUID + + + + UUID Name + + + + All Interfaces + + + + All Devices + + + + Remove duplicates + + + + Copy Cell + + + + Copy Rows + + + + Copy All + + + + Save as image + + + + Mark/Unmark Row + + + + Ctrl-M + + + + Mark/Unmark Cell + + + + Save Table Image + + + + PNG Image (*.png) + + + + + BluetoothDeviceDialog + + Bluetooth Device + + + + BD_ADDR + + + + OUI + + + + Name + + + + Class of Device + + + + LMP Version + + + + LMP Subversion + + + + Manufacturer + + + + HCI Version + + + + HCI Revision + + + + Scan + + + + Authentication + + + + Encryption + + + + ACL MTU + + + + ACL Total Packets + + + + SCO MTU + + + + SCO Total Packets + + + + LE ACL MTU + + + + LE ACL Total Packets + + + + LE ISO MTU + + + + LE ISO Total Packets + + + + Inquiry Mode + + + + Page Timeout + + + + Simple Pairing Mode + + + + Voice Setting + + + + Value + + + + Changes + + + + %1 changes + + + + Copy Cell + + + + Copy Rows + + + + Copy All + + + + Save as image + + + + Mark/Unmark Row + + + + Ctrl+M + + + + Mark/Unmark Cell + + + + Unknown + + + + Bluetooth Device - %1%2 + + + + enabled + + + + disabled + + + + %1 ms (%2 slots) + + + + Save Table Image + + + + PNG Image (*.png) + + + + + BluetoothDevicesDialog + + Bluetooth Devices + + + + BD_ADDR + + + + OUI + + + + Name + + + + LMP Version + + + + LMP Subversion + + + + Manufacturer + + + + HCI Version + + + + HCI Revision + + + + Is Local Adapter + + + + All Interfaces + + + + Show information steps + + + + %1 items; Right click for more option; Double click for device details + + + + Copy Cell + + + + Copy Rows + + + + Copy All + + + + Save as image + + + + Mark/Unmark Row + + + + Ctrl-M + + + + Mark/Unmark Cell + + + + true + + + + Save Table Image + + + + PNG Image (*.png) + + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + + + + Name + + + + OGF + + + + OCF + + + + Opcode + + + + Event + + + + Subevent + + + + Status + + + + Reason + + + + Hardware Error + + + + Occurrence + + + + Link Control Commands + + + + 0x01 + + + + 0 + + + + Link Policy Commands + + + + 0x02 + + + + Controller & Baseband Commands + + + + 0x03 + + + + Informational Parameters + + + + 0x04 + + + + Status Parameters + + + + 0x05 + + + + Testing Commands + + + + 0x06 + + + + LE Controller Commands + + + + 0x08 + + + + Bluetooth Logo Testing Commands + + + + 0x3E + + + + Vendor-Specific Commands + + + + 0x3F + + + + Unknown OGF + + + + Events + + + + Hardware Errors + + + + Results filter: + + + + Display filter: + + + + All Interfaces + + + + All Adapters + + + + Copy Cell + + + + Copy Rows + + + + Copy All + + + + Save as image + + + + Mark/Unmark Row + + + + Ctrl+M + + + + Mark/Unmark Cell + + + + Unknown + + + + Adapter %1 + + + + Frame %1 + + + + Pending + + + + Save Table Image + + + + PNG Image (*.png) + + + + + ByteViewTab + + Packet bytes + + + + + ByteViewText + + Allow hover highlighting + + + + Show bytes as hexadecimal + + + + …as decimal + + + + …as octal + + + + …as bits + + + + Show text based on packet + + + + …as ASCII + + + + …as EBCDIC + + + + + CaptureFile + + [closing] + + + + [closed] + + + + + CaptureFileDialog + + This capture file contains comments. + + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + + + + Discard comments and save + + + + Save in another format + + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + + + + All Files ( + + + + All Capture Files + + + + Format: + + + + Size: + + + + Start / elapsed: + + + + Automatically detect file type + + + + %1, error after %Ln packet(s) + %1, error after %2 packets + + %1, error after %Ln packet + %1, error after %Ln packets + + + + %1, timed out at %Ln packet(s) + %1, timed out at %2 packets + + %1, timed out at %Ln packet + %1, timed out at %Ln packets + + + + %1, %Ln packet(s) + + %1, %Ln packet + %1, %Ln packets + + + + Prepend packets + + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + + + + Merge chronologically + + + + Insert packets in chronological order. + + + + Append packets + + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + + + + Read filter: + + + + Compress with g&zip + + + + Open Capture File + Wireshark: Open Capture File + + + + Save Capture File As + Wireshark: Save Capture File As + + + + Save as: + + + + Export Specified Packets + Wireshark: Export Specified Packets + + + + Export as: + + + + Merge Capture File + Wireshark: Merge Capture File + + + + Unknown file type returned by save as dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + directory + + + + unknown file format + + + + error opening file + + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + %1, error after %Ln data record + %1, error after %Ln data records + + + + %1, timed out at %Ln data record(s) + + %1, timed out at %Ln data record + %1, timed out at %Ln data records + + + + %1, %Ln data record(s) + + %1, %Ln data record + %1, %Ln data records + + + + unknown + + + + + CaptureFilePropertiesDialog + + Details + + + + Capture file comments + + + + Refresh + + + + Copy To Clipboard + + + + Save Comments + + + + Capture File Properties + + + + Unknown + + + + File + + + + Name + + + + Length + + + + Hash (SHA256) + + + + Hash (SHA1) + + + + Format + + + + Encapsulation + + + + Snapshot length + + + + Time + + + + First packet + + + + Last packet + + + + Elapsed + + + + Section %1 + + + + Capture + + + + Hardware + + + + OS + + + + Application + + + + Interfaces + + + + Interface + + + + Dropped packets + + + + Capture filter + + + + Link type + + + + Packet size limit (snaplen) + + + + none + + + + %1 bytes + + + + Statistics + + + + Measurement + + + + Captured + + + + Displayed + + + + Marked + + + + Packets + + + + Time span, s + + + + Average pps + + + + Average packet size, B + + + + Bytes + + + + Average bytes/s + + + + Average bits/s + + + + Section Comment + + + + Packet Comments + + + + <p>Frame %1: + + + + Created by Wireshark %1 + + + + + + + CaptureFilterCombo + + Capture filter selector + + + + + CaptureFilterEdit + + Capture filter entry + + + + Manage saved bookmarks. + + + + Apply this filter string to the display. + + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + + + + Enter a capture filter %1 + + + + Save this filter + + + + Remove this filter + + + + Manage Capture Filters + + + + + CaptureInfoDialog + + Capture Information + + + + Stop Capture + + + + %1 packets, %2:%3:%4 + + + + + CaptureInfoModel + + Other + + + + + CaptureOptionsDialog + + Input + + + + Interface + + + + Traffic + + + + Link-layer Header + + + + Promiscuous + + + + Snaplen (B) + + + + Buffer (MB) + + + + Monitor Mode + + + + Capture Filter + + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + + + + Enable promiscuous mode on all interfaces + + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + + + + Manage Interfaces… + + + + Capture filter for selected interfaces: + + + + Compile BPFs + + + + Output + + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + + + + Capture to a permanent file + + + + File: + + + + Browse… + + + + Output format: + + + + pcapng + + + + pcap + + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + + + + Create a new file automatically… + + + + after + + + + Switch to the next file after the specified number of packets have been captured. + + + + packets + + + + Switch to the next file after the file size exceeds the specified file size. + + + + kilobytes + + + + megabytes + + + + gigabytes + + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + + + + seconds + + + + minutes + + + + hours + + + + when time is a multiple of + + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + + + + compression + + + + None + + + + gzip + + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + + + + Use a ring buffer with + + + + files + + + + Options + + + + Display Options + + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + + + + Update list of packets in real-time + + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + + + + Automatically scroll during live capture + + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + + + + Show capture information during live capture + + + + Name Resolution + + + + Perform MAC layer name resolution while capturing. + + + + Resolve MAC addresses + + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + + + + Resolve network names + + + + Perform transport layer name resolution while capturing. + + + + Resolve transport names + + + + Stop capture automatically after… + + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + + + + Stop capturing after the specified number of packets have been captured. + + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + + + + Stop capturing after the specified amount of data has been captured. + + + + Stop capturing after the specified amount of time has passed. + + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + + + + Directory for temporary files + + + + Capture Options + + + + Start + + + + Leave blank to use a temporary file + + + + Specify a Capture File + + + + Specify temporary directory + + + + %1: %2 + + + + Addresses + + + + Address + + + + no addresses + + + + Error + + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + + + + + CapturePreferencesFrame + + Frame + + + + Default interface + + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + + + + Capture packets in promiscuous mode + + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + + + + Capture packets in pcapng format + + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + + + + Update list of packets in real time + + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + + + + Disable external capture interfaces + + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + + + + + ColoringRulesDialog + + Dialog + + + + <small><i>A hint.</i></small> + + + + Add a new coloring rule. + + + + Delete this coloring rule. + + + + Duplicate this coloring rule. + + + + Clear all coloring rules. + + + + Set the foreground color for this rule. + + + + Foreground + + + + Set the background color for this rule. + + + + Background + + + + Set the display filter using this rule. + + + + Apply as filter + + + + Select a file and add its filters to the end of the list. + + + + Save filters in a file. + + + + Coloring Rules %1 + + + + Import… + + + + Export… + + + + Copy coloring rules from another profile. + + + + Open + + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + + + + Import Coloring Rules + + + + Export %1 Coloring Rules + + + + + ColoringRulesModel + + New coloring rule + + + + Unable to save coloring rules: %1 + + + + Name + + + + Filter + + + + + ColumnEditorFrame + + Frame + + + + Title: + Title + + + + Type: + Type + + + + Fields: + Fields + + + + Occurrence: + Occurrence + + + + Resolve Names: + + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + + + + Missing fields. + + + + Invalid fields. + + + + Invalid occurrence value. + + + + + ColumnListModel + + Displayed + + + + Title + + + + Type + + + + Fields + + + + Field Occurrence + + + + Resolved + + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + + + + New Column + + + + + ColumnPreferencesFrame + + Frame + + + + Add a new column + + + + Delete selected column + + + + Show displayed columns only + + + + Reset all changes + + + + + CompiledFilterOutput + + Compiled Filter Output + + + + Copy + + + + Copy filter text to the clipboard. + + + + + ConversationDataModel + + Address A + + + + Port A + + + + Address B + + + + Port B + + + + Packets + + + + Bytes + + + + Stream ID + + + + Packets A + + + + Bytes A + + + + Packets B + + + + Bytes B + + + + Abs Start + + + + Rel Start + + + + Duration + + + + Bits/s A + + + + Bits/s B + + + + Total Packets + + + + Percent Filtered + + + + + ConversationDialog + + Follow Stream… + + + + Follow a TCP or UDP stream. + + + + Graph… + + + + Graph a TCP conversation. + + + + + ConversationHashTablesDialog + + Dialog + + + + Conversation Hash Tables + + + + + CopyFromProfileButton + + Copy from + + + + Copy entries from another profile. + + + + System default + + + + + CredentialsDialog + + Wireshark - Credentials + + + + Credentials + + + + + CredentialsModel + + Click to select the packet + + + + Click to select the packet with username + + + + Username not available + + + + Packet No. + + + + Protocol + + + + Username + + + + Additional Info + + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + + + + Copy packet bytes as a hex and ASCII dump. + + + + …as Hex Dump + + + + Copy packet bytes as a hex dump. + + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + + + + Copy packet bytes as a stream of hex. + + + + …as a Base64 String + + + + Copy packet bytes as a base64 encoded string. + + + + Copy packet bytes as application/octet-stream MIME data. + + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + + + + Remove this dissection behavior. + + + + Copy this dissection behavior. + + + + Clear all dissection behaviors. + + + + Decode As… + + + + Open + + + + + DecodeAsModel + + Match using this field + + + + Change behavior when the field matches this value + + + + Field value type (and base, if Integer) + + + + Current"Decode As" behavior + + + + Default "Decode As" behavior + + + + String + + + + Integer, base + + + + unknown + + + + <none> + + + + GUID + + + + Field + + + + Value + + + + Type + + + + Default + + + + Current + + + + + DisplayFilterCombo + + Display filter selector + + + + Select from previously used filters. + + + + + DisplayFilterEdit + + Display filter entry + + + + Manage saved bookmarks. + + + + Display Filter Expression… + + + + Apply a display filter %1 <%2/> + + + + Enter a display filter %1 + + + + Clear display filter + + + + Apply display filter + + + + Left align buttons + + + + Apply a read filter %1 + + + + Current filter: %1 + + + + Invalid filter: + + + + Save this filter + + + + Remove this filter + + + + Manage Display Filters + + + + Filter Button Preferences... + + + + + DisplayFilterExpressionDialog + + Dialog + + + + Select a field to start building a display filter. + + + + Field Name + + + + <html><head/><body><p>Search the list of field names.</p></body></html> + + + + Search: + + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + + + + Relation + + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + + + + Quantifier + + + + Any + + + + All + + + + Match against this value. + + + + Value + + + + If the field you have selected has a known set of valid values they will be listed here. + + + + Predefined Values + + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + + + + Range (offset:length) + + + + No display filter + + + + <small><i>A hint.</i></small> + + + + Display Filter Expression + + + + Select a field name to get started + + + + Click OK to insert this filter + + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + + + + Search: + + + + Dissector Tables + + + + + DissectorTablesProxyModel + + Table Type + + + + String + + + + Dissector Description + + + + Integer + + + + Protocol + + + + Short Name + + + + Table Name + + + + Selector Name + + + + + EnabledProtocolsDialog + + Dialog + + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + + + + Search: + + + + in + + + + Enable All + + + + Disable All + + + + Invert + + + + Enabled Protocols + + + + Everywhere + + + + Only Protocols + + + + Only Description + + + + Only enabled protocols + + + + Only disabled protocols + + + + any protocol + + + + non-heuristic protocols + + + + heuristic protocols + + + + + EnabledProtocolsModel + + Protocol + + + + Description + + + + + EndpointDataModel + + Address + + + + Port + + + + Packets + + + + Bytes + + + + Tx Packets + + + + Tx Bytes + + + + Rx Packets + + + + Rx Bytes + + + + Country + + + + City + + + + Latitude + + + + Longitude + + + + AS Number + + + + AS Organization + + + + Total Packets + + + + Percent Filtered + + + + + EndpointDialog + + Map + + + + Draw IPv4 or IPv6 endpoints on a map. + + + + Open in browser + + + + Save As… + + + + Map file error + + + + Save Endpoints Map + + + + Failed to save map file %1. + + + + + EthernetAddressModel + + Type + + + + Name + + + + Address + + + + All entries + + + + Hosts + + + + Ethernet Addresses + + + + Ethernet Manufacturers + + + + Ethernet Well-Known Addresses + + + + + ExpertInfoDialog + + Dialog + + + + <small><i>A hint.</i></small> + + + + Limit to Display Filter + + + + Group by summary + + + + Search expert summaries. + + + + Search: + + + + Show… + Show... + + + + Error + + + + Show error packets. + + + + Warning + + + + Show warning packets. + + + + Note + + + + Show note packets. + + + + Chat + + + + Show chat packets. + + + + Comment + + + + Show comment packets. + + + + Expert Information + + + + Collapse All + + + + Expand All + + + + Capture file closed. + + + + No display filter + + + + No display filter set. + + + + Limit information to "%1". + + + + Display filter: "%1" + + + + + ExpertInfoProxyModel + + Packet + + + + Severity + + + + Summary + + + + Group + + + + Protocol + + + + Count + + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + + + + Export As: + Export as: + + + + Plain text (*.txt) + + + + Comma Separated Values - summary (*.csv) + + + + PSML - summary (*.psml, *.xml) + + + + PDML - details (*.pdml, *.xml) + + + + JSON (*.json) + + + + C Arrays - bytes (*.c, *.h) + + + + + ExportObjectDialog + + Dialog + + + + Content Type: + + + + Searching for objects + + + + Text Filter: + + + + Only display entries containing this string + + + + Preview + + + + All Content-Types + + + + Export + + + + %1 object list + + + + Save Object As… + + + + Save All Objects In… + + + + + ExportObjectModel + + Packet + + + + Hostname + + + + Content Type + + + + Size + + + + Filename + + + + + ExportPDUDialog + + Dialog + + + + Display filter: + + + + + ExtArgSelector + + Reload data + + + + + ExtcapArgumentFileSelection + + Clear + + + + All Files ( + + + + Open File + + + + Select File + + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + + + + Start + + + + Save + + + + Default + + + + Restore default value of the item + + + + Extcap Help cannot be found + + + + The help for the extcap interface %1 cannot be found. Given file: %2 + + + + Save parameter(s) on capture start + Save parameter on capture start + + + + FieldFilterEdit + + Display filter entry + + + + Enter a field %1 + + + + Invalid filter: + + + + + FileSetDialog + + Dialog + + + + Directory: + + + + No files in Set + + + + No capture loaded + + + + %Ln File(s) in Set + %1 File%2 in Set + + %Ln File in Set + %Ln Files in Set + + + + + FilesetEntryModel + + Open this capture file + + + + Filename + + + + Created + + + + Modified + + + + Size + + + + + FilterAction + + Selected + + + + Not Selected + + + + …and Selected + + + + …or Selected + + + + …and not Selected + + + + …or not Selected + + + + + FilterDialog + + Dialog + + + + Create a new filter. + + + + Remove this filter. + Remove this profile. + + + + Copy this filter. + Copy this profile. + + + + Capture Filters + + + + Display Filters + + + + Open + + + + New capture filter + This text is automatically filled in when a new filter is created + + + + New display filter + This text is automatically filled in when a new filter is created + + + + + FilterExpressionFrame + + Frame + + + + Filter Buttons Preferences… + + + + Label: + + + + Enter a description for the filter button + + + + Filter: + + + + Enter a filter expression to be applied + + + + Comment: + + + + Enter a comment for the filter button + + + + Missing label. + + + + Missing filter expression. + + + + Invalid filter expression. + + + + + FilterExpressionToolBar + + Filter Button Preferences... + + + + Edit + + + + Disable + + + + Remove + + + + + FilterListModel + + Filter Name + + + + Filter Expression + + + + + FindLineEdit + + Textual Find + + + + Regular Expression Find + + + + + FirewallRulesDialog + + Create rules for + + + + Inbound + + + + Deny + + + + Firewall ACL Rules + + + + Copy + + + + IPv4 source address. + + + + IPv4 destination address. + + + + Source port. + + + + Destination port. + + + + IPv4 source address and port. + + + + IPv4 destination address and port. + + + + MAC source address. + + + + MAC destination address. + + + + Text file (*.txt);;All Files ( + + + + Warning + + + + Unable to save %1 + + + + + FolderListModel + + "File" dialogs + + + + capture files + + + + Temp + + + + untitled capture files + + + + Personal configuration + + + + Global configuration + + + + dfilters, preferences, ethers, … + + + + dfilters, preferences, manuf, … + + + + System + + + + ethers, ipxnets + + + + Program + + + + program files + + + + Personal Plugins + + + + binary plugins + + + + Global Plugins + + + + Personal Lua Plugins + + + + Global Lua Plugins + + + + Lua scripts + + + + Personal Extcap path + + + + external capture (extcap) plugins + + + + Global Extcap path + + + + MaxMind DB path + + + + MaxMind DB database search path + + + + MIB/PIB path + + + + SMI MIB/PIB search path + + + + macOS Extras + + + + Extra macOS packages + + + + Name + + + + Location + + + + Typical Files + + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + + + + Print + + + + %Ln client pkt(s), + + %Ln client pkt, + %Ln client pkts, + + + + %Ln server pkt(s), + + %Ln server pkt, + %Ln server pkts, + + + + ASCII + + + + C Arrays + + + + EBCDIC + + + + Hex Dump + + + + UTF-8 + + + + YAML + + + + Raw + + + + Save as… + + + + Back + + + + Packet %1. + + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">client</span> pkt, + %Ln <span style="color: %1; background-color:%2">client</span> pkts, + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">server</span> pkt, + %Ln <span style="color: %1; background-color:%2">server</span> pkts, + + + + %Ln turn(s). + + %Ln turn. + %Ln turns. + + + + Click to select. + + + + Regex Find: + + + + No capture file. + + + + Please make sure you have a capture file opened. + + + + Error following stream. + + + + Capture file invalid. + + + + Please make sure you have a %1 packet selected. + + + + %1 stream not found on the selected packet. + + + + Entire conversation (%1) + + + + Follow %1 Stream (%2) + + + + Error creating filter for this stream. + + + + Save Stream Content As… + + + + [Stream output truncated] + + + + %Ln total stream(s). + + %Ln stream. + %Ln total streams. + + + + Max sub stream ID for the selected stream: %Ln + + + + + + + File closed. + + + + Follow Stream + + + + Hint. + + + + Show data as + Show and save data as + + + + Stream + + + + Substream + + + + Find: + + + + Find &Next + + + + + FontColorPreferencesFrame + + Frame + + + + Main window font: + + + + Select Font + + + + Colors: + + + + System Default + + + + Solid + + + + Sample ignored packet text + + + + Sample marked packet text + + + + Sample active selected item + + + + Style: + + + + Gradient + + + + Sample inactive selected item + + + + Sample "Follow Stream" client text + + + + Sample "Follow Stream" server text + + + + Sample valid filter + + + + Sample invalid filter + + + + Sample warning filter + Sample deprecated filter + + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + + + + Lazy badgers move unique waxy jellyfish packets + + + + Font + + + + + FunnelStringDialog + + Dialog + + + + + FunnelTextDialog + + Dialog + + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + + + + Highlight: + + + + + GsmMapSummaryDialog + + Dialog + + + + GSM MAP Summary + + + + File + + + + Name + + + + Length + + + + Format + + + + Snapshot length + + + + Data + + + + First packet + + + + Last packet + + + + Elapsed + + + + Packets + + + + Invokes + + + + Total number of Invokes + + + + Average number of Invokes per second + + + + Total number of bytes for Invokes + + + + Average number of bytes per Invoke + + + + Return Results + + + + Total number of Return Results + + + + Average number of Return Results per second + + + + Total number of bytes for Return Results + + + + Average number of bytes per Return Result + + + + Totals + + + + Total number of GSM MAP messages + + + + Average number of GSM MAP messages per second + + + + Total number of bytes for GSM MAP messages + + + + Average number of bytes per GSM MAP message + + + + + IOConsoleDialog + + Dialog + + + + Enter code + + + + Evaluate + + + + Clear + + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + + Remove this graph. + Remove this dissection behavior. + + + + Add a new graph. + + + + Duplicate this graph. + + + + Clear all graphs. + + + + Move this graph upwards. + + + + Move this graph downwards. + + + + Mouse + + + + Drag using the mouse button. + + + + drags + + + + Select using the mouse button. + + + + zooms + + + + Interval + + + + Time of day + + + + Log scale + + + + Automatic update + + + + Enable legend + + + + Reset + + + + Reset Graph + + + + Reset the graph to its initial state. + + + + 0 + + + + Zoom In + + + + + + + + + Zoom Out + + + + - + + + + Move Up 10 Pixels + + + + Up + + + + Move Left 10 Pixels + + + + Left + + + + Move Right 10 Pixels + + + + Right + + + + Move Down 10 Pixels + + + + Down + + + + Move Up 1 Pixel + + + + Shift+Up + + + + Move Left 1 Pixel + + + + Shift+Left + + + + Move Right 1 Pixel + + + + Shift+Right + + + + Move Down 1 Pixel + + + + Move down 1 Pixel + Move down 1 pixel + + + + Shift+Down + + + + Go To Packet Under Cursor + + + + Go to packet currently under the cursor + + + + G + + + + Drag / Zoom + + + + Toggle mouse drag / zoom behavior + + + + Z + + + + Capture / Session Time Origin + + + + Toggle capture / session time origin + + + + T + + + + Crosshairs + + + + Toggle crosshairs + + + + Space + + + + Zoom In X Axis + + + + X + + + + Zoom Out X Axis + + + + Shift+X + + + + Zoom In Y Axis + + + + Y + + + + Zoom Out Y Axis + + + + Shift+Y + + + + 1 sec + + + + 10 sec + + + + 1 min + + + + 10 min + + + + Time (s) + + + + I/O Graphs + + + + Save As… + + + + Copy + + + + Copy graphs from another profile. + + + + 1 ms + + + + 2 ms + + + + 5 ms + + + + 10 ms + + + + 20 ms + + + + 50 ms + + + + 100 ms + + + + 200 ms + + + + 500 ms + + + + 2 sec + + + + 5 sec + + + + Wireshark I/O Graphs: %1 + + + + Filtered packets + + + + Filtered events + + + + All Packets + + + + TCP Errors + + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + + + + No packets in interval + + + + No events in interval + + + + Click to select packet + + + + Packet + + + + Click to select event + + + + Event + + + + %1 (%2s%3). + + + + Release to zoom, x = %1 to %2, y = %3 to %4 + + + + Unable to select range. + + + + Click to select a portion of the graph. + + + + Portable Document Format (*.pdf) + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + Comma Separated Values (*.csv) + + + + Save Graph As… + + + + + Iax2AnalysisDialog + + Dialog + + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + + + + Forward + + + + Packet + + + + Delta (ms) + + + + Jitter (ms) + + + + Bandwidth + + + + Status + + + + Length + + + + Reverse + + + + Graph + + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + + + + Forward Jitter + + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + + + + Forward Difference + + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + + + + Reverse Jitter + + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + + + + Reverse Difference + + + + <small><i>A hint.</i></small> + + + + Audio + + + + Save the audio data for both channels. + + + + Forward Stream Audio + + + + Save the forward stream audio data. + + + + Reverse Stream Audio + + + + Save the reverse stream audio data. + + + + CSV + + + + Save both tables as CSV. + + + + Forward Stream CSV + + + + Save the forward table as CSV. + + + + Reverse Stream CSV + + + + Save the reverse table as CSV. + + + + Save Graph + + + + Save the graph image. + + + + Go to Packet + + + + Select the corresponding packet in the packet list. + + + + G + + + + Next Problem Packet + + + + Go to the next problem packet + + + + N + + + + IAX2 Stream Analysis + + + + Unable to save RTP data. + + + + Please select an IAX2 packet. + + + + G: Go to packet, N: Next problem packet + + + + Portable Document Format (*.pdf) + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + Save Graph As… + + + + Can't save in a file: Wrong length of captured packets. + + + + Can't save in a file: File I/O problem. + + + + Save forward stream audio + + + + Save reverse stream audio + + + + Save audio + + + + Sun Audio (*.au) + + + + ;;Raw (*.raw) + + + + Warning + + + + Unable to save in that format + + + + Unable to save %1 + + + + Saving %1… + + + + Analyzing IAX2 + + + + Save forward stream CSV + + + + Save reverse stream CSV + + + + Save CSV + + + + Comma-separated values (*.csv) + + + + + ImportTextDialog + + File: + + + + Set name of text file to import + + + + Browse for text file to import + + + + Browse… + Browse... + + + + Hex Dump + + + + Import a standard hex dump as exported by Wireshark + + + + Offsets in the text file are in octal notation + + + + Octal + + + + Offsets: + + + + Offsets in the text file are in hexadecimal notation + + + + Hexadecimal + + + + Offsets in the text file are in decimal notation + + + + Decimal + + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + + + + ASCII identification: + + + + Regular Expression + + + + Import a file formatted according to a custom regular expression + + + + Packet format regular expression + + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + + + + This is regexHintLabel, it will be set to default_regex_hint + + + + Data encoding: + + + + How data is encoded + + + + encodingRegexExample + + + + List of characters indicating incoming packets + + + + iI< + + + + List of characters indicating outgoing packets + + + + oO> + + + + Timestamp format: + + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + + + + Direction indication: + + + + ExportPDU + + + + IP version: + + + + Interface name: + + + + The name of the interface to write to the import capture file + + + + Fake IF, Import from Hex Dump + + + + Maximum frame length: + + + + Encapsulation + + + + The text file has no offset + + + + None + + + + <small><i>recommended regex:</small></i> + + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + + + + %H:%M:%S.%f + + + + timestampExampleLabel + + + + Encapsulation Type: + + + + Encapsulation type of the frames in the import capture file + + + + Prefix each frame with an Ethernet and IP header + + + + IP + + + + Prefix each frame with an Ethernet, IP and UDP header + + + + Prefix each frame with an Ethernet, IP and TCP header + + + + Prefix each frame with an Ethernet, IP and SCTP header + + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + + + + Source address: + + + + Destination address: + + + + Dissector + + + + The IP protocol ID for each frame + + + + The IP source address for each frame + + + + The IP destination address for each frame + + + + The UDP, TCP or SCTP source port for each frame + + + + The SCTP DATA payload protocol identifier for each frame + + + + The UDP, TCP or SCTP destination port for each frame + + + + Prefix each frame with an Ethernet header + + + + Ethernet + + + + SCTP + + + + PPI: + + + + Protocol (dec): + + + + Leave frames unchanged + + + + No dummy header + + + + Tag: + + + + UDP + + + + Source port: + + + + The Ethertype value of each frame + + + + TCP + + + + The SCTP verification tag for each frame + + + + Destination port: + + + + Ethertype (hex): + + + + SCTP (Data) + + + + The dissector to use for each frame + + + + The IP Version to use for the dummy IP header + + + + The maximum size of the frames to write to the import capture file (max 256kiB) + + + + Supported fields are data, dir, time, seqno + + + + Missing capturing group data (use (? + + + + Import From Hex Dump + + + + Import + + + + Import Text File + + + + + InterfaceFrame + + Frame + + + + Wired + + + + AirPCAP + + + + Pipe + + + + STDIN + + + + Bluetooth + + + + Wireless + + + + Dial-Up + + + + USB + + + + External Capture + + + + Virtual + + + + Remote interfaces + + + + Show hidden interfaces + + + + External capture interfaces disabled. + + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + + + + You don't have permission to capture on local interfaces. + + + + No interfaces found. + + + + Interfaces not loaded (due to preference). Go to Capture + + + + Start capture + + + + Hide Interface + + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + + + + + InterfaceToolbar + + Frame + + + + Select interface + + + + Interface + + + + + InterfaceToolbarLineEdit + + Apply changes + + + + + InterfaceTreeModel + + Show + + + + Friendly Name + + + + Interface Name + + + + No interfaces found. + + + + This version of Wireshark was built without packet capture support. + + + + Local Pipe Path + + + + Comment + + + + Link-Layer Header + + + + Promiscuous + + + + Snaplen (B) + + + + Buffer (MB) + + + + Monitor Mode + + + + Capture Filter + + + + Addresses + + + + Address + + + + Extcap interface: %1 + + + + No addresses + + + + No capture filter + + + + Capture filter + + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + + + + Sources + + + + Address/Transport + + + + Data frames + + + + Data bytes + + + + Data frames/bytes + + + + Data rate + + + + RX data frames + + + + RX data bytes + + + + RX data frames/bytes + + + + RX data rate + + + + NCF frames + + + + NCF count + + + + NCF bytes + + + + NCF frames/bytes + + + + NCF count/bytes + + + + NCF frames/count + + + + NCF frames/count/bytes + + + + NCF rate + + + + SM frames + + + + SM bytes + + + + SM frames/bytes + + + + SM rate + + + + Show + + + + Data + + + + RX Data + + + + NCF + Nak ConFirmation + + + + SM + Session Message + + + + sequence numbers for transport + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + + SQN + + + + Count + + + + Frame + + + + SQN/Reason + + + + Receivers + + + + NAK frames + + + + NAK count + + + + NAK bytes + + + + NAK rate + + + + NAK sequence numbers for transport + + + + Display filter: + + + + Regenerate statistics using this display filter + + + + Apply + + + + Copy as CSV + + + + Copy the tree as CSV + + + + Copy as YAML + + + + Copy the tree as YAML + + + + Show the data frames column + + + + Show the data bytes column + + + + Show the data frames/bytes column + + + + Show the RX data frames column + + + + Show the RX data bytes column + + + + Show the RX data frames/bytes column + + + + Show the NCF frames column + + + + Show the NCF bytes column + + + + Show the NCF count column + + + + Show the data rate column + + + + Show the RX data rate column + + + + Show the NCF frames/bytes column + + + + Show the NCF count/bytes column + + + + Show the NCF frames/count column + + + + Show the NCF frames/count/bytes column + + + + Show the NCF rate column + + + + Show the SM frames column + + + + Show the SM bytes column + + + + Show the SM frames/bytes column + + + + Show the SM rate column + + + + Auto-resize columns to content + + + + Resize columns to content size + + + + LBT-RM Statistics failed to attach to tap + + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + + + + Sources + + + + Address/Transport/Client + + + + Data frames + + + + Data bytes + + + + Data frames/bytes + + + + Data rate + + + + RX data frames + + + + RX data bytes + + + + RX data frames/bytes + + + + RX data rate + + + + NCF frames + + + + NCF count + + + + NCF bytes + + + + NCF frames/count + + + + NCF frames/bytes + + + + NCF count/bytes + + + + NCF frames/count/bytes + + + + NCF rate + + + + SM frames + + + + SM bytes + + + + SM frames/bytes + + + + SM rate + + + + RST frames + + + + RST bytes + + + + RST frames/bytes + + + + RST rate + + + + Show + + + + Data SQN + + + + RX Data SQN + + + + NCF SQN + + + + SM SQN + + + + RST reason + + + + details for transport + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + + SQN + + + + Count + + + + Frame + + + + Reason + + + + SQN/Reason + + + + Receivers + + + + Address/Transport + + + + NAK frames + + + + NAK count + + + + NAK bytes + + + + NAK frames/count + + + + NAK count/bytes + + + + NAK frames/bytes + + + + NAK frames/count/bytes + + + + NAK rate + + + + ACK frames + + + + ACK bytes + + + + ACK frames/bytes + + + + ACK rate + + + + CREQ frames + + + + CREQ bytes + + + + CREQ frames/bytes + + + + CREQ rate + + + + NAK SQN + + + + ACK SQN + + + + CREQ request + + + + Display filter: + + + + Regenerate statistics using this display filter + + + + Apply + + + + Copy as CSV + + + + Copy the tree as CSV + + + + Copy as YAML + + + + Copy the tree as YAML + + + + Show the data frames column + + + + Show the data bytes column + + + + Show the data frames/bytes column + + + + Show the data rate column + + + + Show the RX data frames column + + + + Show the RX data bytes column + + + + Show the RX data frames/bytes column + + + + Show the RX data rate column + + + + Show the NCF frames column + + + + Show the NCF count column + + + + Show the NCF bytes column + + + + Show the NCF frames/bytes column + + + + Show the NCF count/bytes column + + + + Show the NCF frames/count column + + + + Show the NCF frames/count/bytes column + + + + Show the SM frames column + + + + Show the SM bytes column + + + + Show the SM frames/bytes column + + + + Show the SM rate column + + + + Show the RST frames column + + + + Show the RST bytes column + + + + Show the RST frames/bytes column + + + + Show the RST rate column + + + + Show the NAK frames column + + + + Show the NAK count column + + + + Show the NAK bytes column + + + + Show the NAK frames/count column + + + + Show the NAK count/bytes column + + + + Show the NAK frames/bytes column + + + + Show the NAK frames/count/bytes column + + + + Show the NAK rate column + + + + Show the ACK frames column + + + + Show the ACK bytes column + + + + Show the ACK frames/bytes column + + + + Show the ACK rate column + + + + Show the CREQ frames column + + + + Show the CREQ bytes column + + + + Show the CREQ frames/bytes column + + + + Show the CREQ rate column + + + + Auto-resize columns to content + + + + Resize columns to content size + + + + Show the NCF rate column + + + + LBT-RU Statistics failed to attach to tap + + + + + LBMStreamDialog + + Dialog + + + + Stream + + + + Endpoint A + + + + Endpoint B + + + + Messages + + + + Bytes + + + + First Frame + + + + Last Frame + + + + Display filter: + + + + Regenerate statistics using this display filter + + + + Apply + + + + Copy as CSV + + + + Copy the tree as CSV + + + + Copy as YAML + + + + Copy the tree as YAML + + + + LBM Stream failed to attach to tap + + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + LayoutPreferencesFrame + + Frame + + + + Pane 1: + + + + Packet List + + + + Packet Details + + + + Packet Bytes + + + + Packet Diagram + + + + None + + + + Pane 2: + + + + Pane 3: + + + + Packet List settings: + + + + Show packet separator + + + + Show column definition in column context menu + + + + Allow the list to be sorted + + + + Maximum number of cached rows (affects sorting) + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + Enable mouse-over colorization + + + + Status Bar settings: + + + + Show selected packet number + + + + Show file load time + + + + + LteMacStatisticsDialog + + LTE Mac Statistics + + + + Include SR frames in filter + + + + Include RACH frames in filter + + + + MAC Statistics + + + + + LteRlcGraphDialog + + Dialog + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + + Mouse + + + + Drag using the mouse button. + + + + drags + + + + Select using the mouse button. + + + + zooms + + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + + + + Reset + + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + + + + Switch Direction + + + + Reset Graph + + + + Reset the graph to its initial state. + + + + 0 + + + + Zoom In + + + + + + + + + Zoom Out + + + + - + + + + Move Up 10 Pixels + + + + Up + + + + Move Left 10 Pixels + + + + Left + + + + Move Right 10 Pixels + + + + Right + + + + Move Down 10 Pixels + + + + Down + + + + Move Up 1 Pixel + + + + Shift+Up + + + + Move Left 1 Pixel + + + + Shift+Left + + + + Move Right 1 Pixel + + + + Shift+Right + + + + Move Down 1 Pixel + + + + Move down 1 Pixel + + + + Shift+Down + + + + Drag / Zoom + + + + Toggle mouse drag / zoom behavior + + + + Z + + + + Crosshairs + + + + Toggle crosshairs + + + + Space + + + + Move Up 100 Pixels + + + + PgUp + + + + PgDown + + + + Go To Packet Under Cursor + + + + Go to packet currently under the cursor + + + + G + + + + Zoom In X Axis + + + + X + + + + Zoom Out Y Axis + + + + Shift+Y + + + + Zoom In Y Axis + + + + Y + + + + Zoom Out X Axis + + + + Shift+X + + + + Switch direction (swap between UL and DL) + + + + D + + + + Time + + + + Sequence Number + + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + + + + LTE RLC Graph - no channel selected + + + + Save As… + + + + %1 %2 (%3s seq %4 len %5) + + + + Click to select packet + + + + Packet + + + + Release to zoom, x = %1 to %2, y = %3 to %4 + + + + Unable to select range. + + + + Click to select a portion of the graph. + + + + Portable Document Format (*.pdf) + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + Save Graph As… + + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + + + + Include SR frames in filter + + + + Include RACH frames in filter + + + + Use RLC frames only from MAC frames + + + + UL Frames + + + + UL Bytes + + + + UL MB/s + + + + UL ACKs + + + + UL NACKs + + + + UL Missing + + + + DL Frames + + + + DL Bytes + + + + DL MB/s + + + + DL ACKs + + + + DL NACKs + + + + DL Missing + + + + RLC Statistics + + + + + MainStatusBar + + Ready to load or capture + + + + Ready to load file + + + + Open the Capture File Properties dialog + + + + Profile: %1 + + + + Manage Profiles… + + + + New… + + + + Edit… + + + + Import + + + + Export + + + + Delete + + + + Switch to + + + + is the highest expert information level + is the highest expert info level + + + + ERROR + + + + WARNING + + + + NOTE + + + + CHAT + + + + No expert information + No expert info + + + + %Ln byte(s) + , %1 bytes + + %Ln byte + %Ln bytes + + + + Byte %1 + + + + Bytes %1-%2 + + + + Selected Packet: %1 %2 + + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + + + + %1 Selected: %2 (%3%) + + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + + + + %1 Dropped: %2 (%3%) + + + + %1 Ignored: %2 (%3%) + + + + %1 Comments: %2 + + + + %1 Load time: %2:%3.%4 + + + + No Packets + + + + From Zip File... + + + + From Directory... + + + + Selected Personal Profile... + + + + All Personal Profiles... + + + + Packets: %1 + + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + + + + Checking this will save the size, position, and maximized state of the main window. + + + + Remember main window size and placement + + + + Open files in + + + + This folder: + + + + Browse… + Browse... + + + + The most recently used folder + + + + Show up to + + + + filter entries + + + + recent files + + + + Confirm unsaved capture files + + + + Display autocompletion for filter text + + + + Main toolbar style: + + + + Icons only + + + + Text only + + + + Icons & Text + + + + Window title + + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + Prepend window title + + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + Language: + + + + Use system setting + + + + Open Files In + + + + + ManageInterfacesDialog + + Manage Interfaces + + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + + + + Local Interfaces + + + + Show + + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + + + + Pipes + + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + + + + Remote Interfaces + + + + Host / Device URL + + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + + + + Remote Settings + + + + <small><i></i></small> + + + + This version of Wireshark does not save pipe settings. + + + + This version of Wireshark does not save remote settings. + + + + This version of Wireshark does not support remote interfaces. + + + + New Pipe + + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + + + + Copy + + + + Find + + + + Clear + + + + + ManufTableModel + + Address Block + + + + Short Name + + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + + + + + Mtp3SummaryDialog + + Dialog + + + + MTP3 Summary + + + + File + + + + Name + + + + Length + + + + Format + + + + Snapshot length + + + + Data + + + + First packet + + + + Last packet + + + + Elapsed + + + + Packets + + + + Service Indicator (SI) Totals + + + + SI + + + + MSUs + + + + MSUs/s + + + + Bytes + + + + Bytes/MSU + + + + Bytes/s + + + + Totals + + + + Total MSUs + + + + Total Bytes + + + + Average Bytes/MSU + + + + Average Bytes/s + + + + + MulticastStatisticsDialog + + UDP Multicast Streams + + + + Source Address + + + + Source Port + + + + Destination Address + + + + Destination Port + + + + Packets + + + + Packets/s + + + + Avg BW (bps) + + + + Max BW (bps) + + + + Max Burst + + + + Burst Alarms + + + + Max Buffers (B) + + + + Buffer Alarms + + + + Burst measurement interval (ms): + + + + Burst alarm threshold (packets): + + + + Buffer alarm threshold (B): + + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + + + + The burst interval must be between 1 and 1000. + + + + The burst alarm threshold isn't valid. + + + + The buffer alarm threshold isn't valid. + + + + The stream empty speed should be between 1 and 10000000. + + + + The total empty speed should be between 1 and 10000000. + + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + + + + + PacketCommentDialog + + Edit Packet Comment + + + + Add Packet Comment + + + + + PacketDiagram + + Packet diagram + + + + Show Field Values + + + + Save Diagram As… + + + + Copy as Raster Image + + + + …as SVG + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + Scalable Vector Graphics (*.svg) + + + + Save Graph As… + + + + + PacketDialog + + Dialog + + + + <small><i></i></small> + + + + Show packet bytes + + + + Packet %1 + + + + [%1 closed] + + + + Byte %1 + + + + Bytes %1-%2 + + + + %Ln byte(s) + + %Ln byte + %Ln bytes + + + + + PacketFormatGroupBox + + GroupBox + + + + Packet Format + + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + + + + Summary line + + + + Include column headings + + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + + + + Details: + + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + + + + All co&llapsed + + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + + + + As displa&yed + + + + <html><head/><body><p>Export all packet detail items</p></body></html> + + + + All e&xpanded + + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + + + + Bytes + + + + Include secondary data sources + + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + + + + + PacketList + + Protocol Preferences + + + + Summary as Text + + + + …as CSV + + + + …as YAML + + + + Decode As… + + + + Frame %1: %2 + + + + + + [ Comment text exceeds %1. Stopping. ] + + + + + PacketListHeader + + Align Left + + + + Align Center + + + + Align Right + + + + Edit Column + + + + Resize to Contents + + + + Column Preferences… + + + + Resize Column to Width… + + + + Resolve Names + + + + Remove this Column + + + + Column %1 + + + + Width: + + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + + + + Sorting "%1"… + + + + Sorting … + + + + + PacketRangeGroupBox + + Form + + + + Packet Range + + + + - + + + + Displayed + + + + &Marked packets only + + + + &Range: + + + + Remove &ignored packets + + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + + + + &All packets + + + + &Selected packets only + + + + Captured + + + + + PathSelectionDelegate + + Open a pipe + + + + + PathSelectionEdit + + Browse + + + + Select a path + + + + + PluginListModel + + Name + + + + Version + + + + Type + + + + Path + + + + + PortsModel + + All entries + + + + tcp + + + + udp + + + + sctp + + + + dccp + + + + Name + + + + Port + + + + Type + + + + + PreferenceEditorFrame + + Frame + + + + … + + + + a preference + + + + Browse… + + + + Open %1 preferences… + + + + Invalid value. + + + + + PreferencesDialog + + Search: + + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + + + + + PrefsModel + + Advanced + + + + Appearance + + + + Layout + + + + Columns + + + + Font and Colors + + + + Capture + + + + Expert + + + + Filter Buttons + + + + RSA Keys + + + + + PrintDialog + + Packet Format + + + + Print each packet on a new page + + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + + + + Capture information header + + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + + + + Packet Range + + + + Print + + + + &Print… + + + + Page &Setup… + + + + %1 %2 total packets, %3 shown + + + + Print Error + + + + Unable to print to %1. + + + + + ProfileDialog + + Search for profile … + + + + Create a new profile using default settings. + + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + + + + Copy this profile. + + + + Configuration Profiles + + + + Import + noun + + + + Export + noun + + + + From Zip File... + + + + From Directory... + + + + %Ln Selected Personal Profile(s)... + + + + + + + All Personal Profiles... + + + + New profile + + + + Profile Error + + + + Exporting profiles + + + + No profiles found for export + + + + Select zip file for export + + + + … %Ln selected personal profile(s) + + … %Ln selected personal profile + … %Ln selected personal profiles + + + + %Ln selected personal profile(s) + + %Ln selected personal profile + %Ln selected personal profiles + + + + An import of profiles is not allowed, while changes are pending + + + + An import is pending to be saved. Additional imports are not allowed + + + + An export of profiles is only allowed for personal profiles + + + + An export of profiles is not allowed, while changes are pending + + + + %Ln profile(s) exported + + %Ln profile exported + %Ln profiles exported + + + + Select zip file for import + + + + Select directory for import + + + + Zip File (*.zip) + + + + Error + + + + An error has occurred while exporting profiles + + + + No profiles found for import in %1 + + + + %Ln profile(s) imported + + %Ln profile imported + %Ln profiles imported + + + + , %Ln profile(s) skipped + + , %Ln profile skipped + , %Ln profiles skipped + + + + Importing profiles + + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + ProfileModel + + Resetting to default + + + + Imported profile + + + + This is a system provided profile + + + + A profile change for this name is pending + + + + (See: %1) + + + + This is an invalid profile definition + + + + A profile already exists with this name + + + + A profile with this name is being deleted + + + + Created from default settings + + + + system provided + + + + deleted + + + + copy + noun + + + + Exporting profiles while changes are pending is not allowed + + + + No profiles found to export + + + + Can't delete profile directory + + + + A profile name cannot contain the following characters: %1 + + + + A profile name cannot contain the '/' character + + + + A profile cannot start or end with a period (.) + + + + Default + + + + Global + + + + Personal + + + + Renamed from: %1 + + + + Copied from: %1 + + + + renamed to %1 + + + + Profile + + + + Type + + + + + ProfileSortModel + + All profiles + + + + Personal profiles + + + + Global profiles + + + + + ProgressFrame + + Frame + + + + Loading + + + + + ProtoTree + + Packet details + + + + Not a field or protocol + + + + No field reference available for text labels. + + + + Expand Subtrees + + + + Collapse Subtrees + + + + Expand All + + + + Collapse All + + + + Copy + + + + All Visible Items + + + + All Visible Selected Tree Items + + + + Description + + + + Field Name + + + + Value + + + + As Filter + + + + Wiki Protocol Page + + + + Filter Field Reference + + + + Copied + + + + Wiki Page for %1 + + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + + + + Colorize with Filter + + + + + ProtocolHierarchyDialog + + Dialog + + + + Protocol + + + + Percent Packets + + + + Packets + + + + Percent Bytes + + + + Bytes + + + + Bits/s + + + + End Packets + + + + End Bytes + + + + End Bits/s + + + + PDUs + + + + <small><i>A hint.</i></small> + + + + Copy as CSV + + + + Copy stream list as CSV. + + + + Copy as YAML + + + + Copy stream list as YAML. + + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + + + + Copy + + + + as CSV + + + + as YAML + + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + + + + Display filter: %1 + + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + + + + No protocol preferences available + + + + Disable %1 + + + + %1 has no preferences + + + + Open %1 preferences… + + + + + QObject + + Average Throughput (bits/s) + + + + Round Trip Time (ms) + + + + Segment Length (B) + + + + Sequence Number (B) + + + + Time (s) + + + + Window Size (B) + + + + [no capture file] + + + + Conversation + + + + Bars show the relative timeline for each conversation. + + + + Endpoint + + + + Apply as Filter + + + + Prepare as Filter + + + + Find + + + + Colorize + + + + Look Up + + + + Copy + + + + UNKNOWN + + + + Selected + + + + Not Selected + + + + …and Selected + + + + …or Selected + + + + …and not Selected + + + + …or not Selected + + + + A + + + + B + + + + Any + + + + Don't show this message again. + + + + Multiple problems found + + + + %1 (%L2%) + + + + No entries. + + + + %1 entries. + + + + Base station + + + + <Broadcast> + + + + <Hidden> + + + + BSSID + + + + Beacons + + + + Data Pkts + + + + Protection + + + + Address + + + + Pkts Sent + + + + Pkts Received + + + + Comment + + + + Wrong sequence number + + + + Payload changed to PT=%1 + + + + Incorrect timestamp + + + + Marker missing? + + + + C-RNTI + + + + SPS-RNTI + + + + RNTI + + + + Type + + + + UEId + + + + UL Frames + + + + UL Bytes + + + + UL MB/s + + + + UL Padding % + + + + UL Re TX + + + + DL Frames + + + + DL Bytes + + + + DL MB/s + + + + DL Padding % + + + + DL CRC Failed + + + + DL ReTX + + + + LCID 1 + + + + LCID 2 + + + + LCID 3 + + + + LCID 4 + + + + LCID 5 + + + + LCID 6 + + + + LCID 7 + + + + LCID 8 + + + + LCID 9 + + + + LCID 10 + + + + LCID 32 + + + + LCID 33 + + + + LCID 34 + + + + LCID 35 + + + + LCID 36 + + + + LCID 37 + + + + LCID 38 + + + + TM + + + + UM + + + + AM + + + + Predef + + + + Unknown (%1) + + + + CCCH + + + + SRB-%1 + + + + DRB-%1 + + + + Unknown + + + + UE Id + + + + Name + + + + Mode + + + + Priority + + + + default + + + + DLT %1 + + + + Invalid Display Filter + + + + The filter expression %1 isn't a valid display filter. (%2). + + + + Error + + + + No remote interfaces found. + + + + PCAP not found + + + + Unknown error + + + + Default + + + + Changed + + + + Has this preference been changed? + + + + Default value is empty + + + + Gap in dissection + + + + Edit… + + + + Browse… + + + + + QObject::QObject + + CCCH + + + + + RemoteCaptureDialog + + Remote Interface + + + + Host: + + + + Port: + + + + Authentication + + + + Null authentication + + + + Password authentication + + + + Username: + + + + Password: + + + + Clear list + + + + Error + + + + No remote interfaces found. + + + + PCAP not found + + + + + RemoteSettingsDialog + + Remote Capture Settings + + + + Capture Options + + + + Do not capture own RPCAP traffic + + + + Use UDP for data transfer + + + + Sampling Options + + + + None + + + + 1 of + + + + packets + + + + 1 every + + + + milliseconds + + + + + ResolvedAddressesDialog + + Dialog + + + + Hosts + + + + Search for entry (min 3 characters) + + + + Ports + + + + Search for port or name + + + + Capture File Comments + + + + Comment + + + + Show the comment. + + + + IPv4 Hash Table + + + + Show the IPv4 hash table entries. + + + + IPv6 Hash Table + + + + Show the IPv6 hash table entries. + + + + Show All + + + + Show all address types. + + + + Hide All + + + + Hide all address types. + + + + IPv4 and IPv6 Addresses (hosts) + + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + + + + Port names (services) + + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + + + + Ethernet Addresses + + + + Show resolved Ethernet addresses in "ethers" format. + + + + Ethernet Well-Known Addresses + + + + Show well-known Ethernet addresses in "ethers" format. + + + + Ethernet Manufacturers + + + + Show Ethernet manufacturers in "ethers" format. + + + + [no file] + + + + Resolved Addresses + + + + # Resolved addresses found in %1 + + + + # Comments +# +# + + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + + + + Type + + + + Messages + + + + Min SRT + + + + Max SRT + + + + Avg SRT + + + + Min in Frame + + + + Max in Frame + + + + Open Requests + + + + Discarded Responses + + + + Repeated Requests + + + + Repeated Responses + + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + + + + Version: + + + + Program: + + + + DCE-RPC Service Response Times + + + + ONC-RPC Service Response Times + + + + + RsaKeysFrame + + RSA Keys + + + + RSA private keys are loaded from a file or PKCS #11 token. + + + + Add new keyfile… + + + + Add new token… + + + + Remove key + + + + PKCS #11 provider libraries. + + + + Add new provider… + + + + Remove provider + + + + Add PKCS #11 token or key + + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + + + + Select a new PKCS #11 token or key + + + + PKCS #11 token or key + + + + Enter PIN or password for %1 (it will be stored unencrypted) + + + + Enter PIN or password for key + + + + Key could not be added: %1 + + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + + + + Select RSA private key file + + + + Libraries (*.dll) + + + + Libraries (*.so) + + + + Select PKCS #11 Provider Library + + + + Changes will apply after a restart + + + + PKCS #11 provider %1 will be removed after the next restart. + + + + + RtpAnalysisDialog + + Dialog + + + + Packet + + + + Sequence + + + + Delta (ms) + + + + Jitter (ms) + Jitter + + + + Skew + + + + Bandwidth + + + + Marker + + + + Status + + + + Stream %1 + + + + Stream %1 Jitter + + + + Stream %1 Difference + + + + Stream %1 Delta + + + + %1 streams, + + + + Save one stream CSV + + + + Save all stream's CSV + + + + &Analyze + + + + Open the analysis window for the selected stream(s) + + + + &Set List + + + + &Add to List + + + + &Remove from List + + + + Replace existing list in RTP Analysis Dialog with new one + + + + Add new set to existing list in RTP Analysis Dialog + + + + Remove selected streams from list in RTP Analysis Dialog + + + + Graph + + + + <small><i>A hint.</i></small> + + + + &Export + + + + Open export menu + + + + CSV + + + + Save tables as CSV. + + + + Current Tab Stream CSV + + + + Save the table on the current tab as CSV. + + + + All Tab Streams CSV + + + + Save the table from all tabs as CSV. + + + + Save Graph + + + + Save the graph image. + + + + Go to Packet + + + + Select the corresponding packet in the packet list. + + + + G + + + + Next Problem Packet + + + + Go to the next problem packet + + + + N + + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + + + + &Current Tab + + + + Prepare a filter matching current tab. + + + + &All Tabs + + + + Prepare a filter matching all tabs. + + + + RTP Stream Analysis + + + + Save Graph As… + + + + G: Go to packet, N: Next problem packet + + + + Portable Document Format (*.pdf) + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + Comma-separated values (*.csv) + + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + + + + + RtpPlayerDialog + + RTP Player + + + + Play + + + + Source Address + + + + Source Port + + + + Destination Address + + + + Destination Port + + + + SSRC + + + + Setup Frame + + + + Packets + + + + Time Span (s) + + + + Payloads + + + + <small><i>No audio</i></small> + + + + Start playback of all unmuted streams + + + + Pause/unpause playback + + + + Stop playback + + + + Enable/disable skipping of silence during playback + + + + Min silence: + + + + Minimum silence duration to skip in seconds + + + + Output Device: + + + + Output Audio Rate: + + + + Jitter Buffer: + + + + The simulated jitter buffer in milliseconds. + + + + Playback Timing: + + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + + + + Jitter Buffer + + + + RTP Timestamp + + + + Uninterrupted Mode + + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + + + + Time of Day + + + + &Export + + + + Export audio of all unmuted selected channels or export payload of one channel. + + + + From &cursor + + + + Save audio data started at the cursor + + + + &Stream Synchronized Audio + + + + Save audio data synchronized to start of the earliest stream. + + + + &File Synchronized Audio + + + + Save audio data synchronized to start of the capture file. + + + + &Payload + + + + Save RTP payload of selected stream. + + + + Reset Graph + + + + Reset the graph to its initial state. + + + + Go To Setup Packet + + + + Go to setup packet of stream currently under the cursor + + + + Mute + + + + Mute selected streams + + + + Unmute + + + + Unmute selected streams + + + + Invert muting of selected streams + + + + Route audio to left channel of selected streams + + + + Route audio to left and right channel of selected streams + + + + Route audio to right channel of selected streams + + + + Remove Streams + + + + Remove selected streams from the list + + + + All + + + + Select all + + + + None + + + + Clear selection + + + + Invert + + + + Invert selection + + + + Play/Pause + + + + Start playing or pause playing + + + + Stop + + + + Stop playing + + + + I&naudible streams + + + + Select/Deselect inaudible streams + + + + Inaudible streams + + + + &Select + + + + Select inaudible streams + + + + &Deselect + + + + Deselect inaudible streams + + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + + + + R&efresh streams + + + + Read captured packets from capture in progress to player + + + + Zoom In + + + + SR (Hz) + + + + Sample rate of codec + + + + PR (Hz) + + + + Play rate of decoded audio (depends e. g. on selected sound card) + + + + Zoom Out + + + + Move Left 10 Pixels + + + + Move Right 10 Pixels + + + + Move Left 1 Pixels + + + + Move Right 1 Pixels + + + + Go To Packet Under Cursor + + + + Go to packet currently under the cursor + + + + Play the stream + + + + To Left + + + + Left + Right + + + + To Right + + + + Invert Muting + + + + No devices available + + + + Select + + + + Audio Routing + + + + &Play Streams + + + + Open RTP player dialog + + + + &Set playlist + + + + Replace existing playlist in RTP Player with new one + + + + &Add to playlist + + + + Add new set to existing playlist in RTP Player + + + + &Remove from playlist + + + + Remove selected streams from playlist in RTP Player + + + + No Audio + + + + Decoding streams... + + + + Out of Sequence + + + + Jitter Drops + + + + Wrong Timestamps + + + + Inserted Silence + + + + Double click on cell to change audio routing + + + + %1 streams + + + + , %1 selected + + + + , %1 not muted + + + + , start: %1. Double click on graph to set start of playback. + + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + + + + Playback of stream %1 failed! + + + + Automatic + + + + WAV (*.wav) + + + + Sun Audio (*.au) + + + + Save audio + + + + Raw (*.raw) + + + + Save payload + + + + Warning + + + + No stream selected or none of selected streams provide audio + + + + Error + + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + + + + No streams are suitable for save + + + + Save failed! + + + + Can't write header of AU file + + + + Can't write header of WAV file + + + + Payload save works with just one audio stream. + + + + Double click to change audio routing + + + + Preparing to play... + + + + Unknown + + + + + RtpStreamDialog + + Dialog + + + + Source Address + + + + Source Port + + + + Destination Address + + + + Destination Port + + + + SSRC + + + + Start Time + + + + Duration + + + + Payload + + + + Packets + + + + Lost + + + + Max Delta (ms) + + + + Max Jitter + + + + Mean Jitter + + + + Status + + + + <small><i>A hint.</i></small> + + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + + Limit to display filter + + + + Time of Day + + + + Find &Reverse + + + + Prepare &Filter + + + + &Export + + + + &Analyze + + + + Open the analysis window for the selected stream(s) and add it to it + + + + Find the reverse stream matching the selected forward stream. + + + + Min Delta (ms) + + + + Mean Delta (ms) + + + + Min Jitter + + + + All forward/reverse stream actions + + + + R + + + + Find All &Pairs + + + + Select all streams which are paired in forward/reverse relation + + + + Shift+R + + + + Find Only &Singles + + + + Find all streams which don't have paired reverse stream + + + + Ctrl+R + + + + Mark Packets + + + + Mark the packets of the selected stream(s). + + + + M + + + + All + + + + Select all + + + + None + + + + Clear selection + + + + Invert + + + + Invert selection + + + + Go To Setup + + + + Go to the setup packet for this stream. + + + + G + + + + Prepare a filter matching the selected stream(s). + + + + P + + + + Export the stream payload as rtpdump + + + + E + + + + A + + + + Cop&y + + + + Open copy menu + + + + Copy as CSV + + + + Copy stream list as CSV. + + + + Copy as YAML + + + + Copy stream list as YAML. + + + + RTP Streams + + + + Select + + + + as CSV + + + + as YAML + + + + %1 streams + + + + , %1 selected, %2 total packets + + + + Save RTPDump As… + + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + + + + ID + + + + Port 1 + + + + Port 2 + + + + Number of Packets + + + + Number of DATA Chunks + + + + Number of Bytes + + + + Filter Selected Association + + + + Analyze + + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + + + + TabWidget + + + + Statistics + + + + Chunk Statistics + + + + Filter Association + + + + Number of Data Chunks from EP2 to EP1: + + + + Checksum Type: + + + + Number of Data Chunks from EP1 to EP2: + + + + Number of Data Bytes from EP1 to EP2: + + + + Number of Data Bytes from EP2 to EP1: + + + + Endpoint 1 + + + + Graph TSN + + + + Graph Bytes + + + + Requested Number of Inbound Streams: + + + + Port: + + + + Sent Verification Tag: + + + + Minimum Number of Inbound Streams: + + + + - + + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + + + + Minimum Number of Outbound Streams: + + + + Graph Arwnd + + + + Endpoint 2 + + + + Complete List of IP addresses from INIT_ACK Chunk: + + + + Provided Number of Outbound Streams: + + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + + + + No Association found for this packet. + + + + Warning + + + + Could not find SCTP Association with id: %1 + + + + Complete list of IP addresses from INIT Chunk: + + + + Complete list of IP addresses from INIT_ACK Chunk: + + + + List of Used IP Addresses + + + + Used Number of Inbound Streams: + + + + Used Number of Outbound Streams: + + + + + SCTPChunkStatisticsDialog + + Dialog + + + + Association + + + + Endpoint 1 + + + + Endpoint 2 + + + + Save Chunk Type Order + + + + Hide Chunk Type + + + + Remove the chunk type from the table + + + + Chunk Type Preferences + + + + Go to the chunk type preferences dialog to show or hide other chunk types + + + + Show All Registered Chunk Types + + + + Show all chunk types with defined names + + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + + + + + SCTPGraphArwndDialog + + SCTP Graph + + + + Reset to full size + + + + Save Graph + + + + goToPacket + + + + Go to Packet + + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + Arwnd + + + + time [secs] + + + + Advertised Receiver Window [Bytes] + + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + + + + + SCTPGraphByteDialog + + SCTP Graph + + + + Reset to full size + + + + Save Graph + + + + goToPacket + + + + Go to Packet + + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + Bytes + + + + time [secs] + + + + Received Bytes + + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + + + + + SCTPGraphDialog + + SCTP Graph + + + + Relative TSNs + + + + Only SACKs + + + + Only TSNs + + + + Show both + + + + Reset to full size + + + + Save Graph + + + + goToPacket + + + + Go to Packet + + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + CumTSNAck + + + + Gap Ack + + + + NR Gap Ack + + + + Duplicate Ack + + + + TSN + + + + time [secs] + + + + TSNs + + + + <small><i>%1: %2 Time: %3 secs </i></small> + + + + Portable Document Format (*.pdf) + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + Save Graph As… + + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + + + + Command: + + + + SCSI Service Response Times + + + + + SearchFrame + + Frame + + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + + + + Packet list + + + + Packet details + + + + Packet bytes + + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + + + + Narrow & Wide + + + + Narrow (UTF-8 / ASCII) + + + + Wide (UTF-16) + + + + Case sensitive + + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + + + + Display filter + + + + Hex value + + + + String + + + + Regular Expression + + + + Find + + + + Cancel + + + + No valid search type selected. Please report this to the development team. + + + + Invalid filter. + + + + That filter doesn't test anything. + + + + That's not a valid hex string. + + + + You didn't specify any text for which to search. + + + + No valid character set selected. Please report this to the development team. + + + + No valid search area selected. Please report this to the development team. + + + + Searching for %1… + + + + No packet contained those bytes. + + + + No packet contained that string in its Info column. + + + + No packet contained that string in its dissected display. + + + + No packet contained that string in its converted data. + + + + No packet matched that filter. + + + + + SequenceDialog + + Call Flow + + + + Time + + + + Comment + + + + No data + + + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + Portable Document Format (*.pdf) + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + ASCII (*.txt) + + + + Save Graph As… + + + + Flow + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + + + + <small><i>A hint</i></small> + + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + + + + Limit to display filter + + + + Flow type: + + + + Addresses: + + + + Any + + + + Network + + + + Reset Diagram + + + + Reset &Diagram + + + + Reset the diagram to its initial state. + + + + 0 + + + + &Reset Diagram + + + + Reset the diagram to its initial state + + + + &Export + + + + Export diagram + + + + Zoom In + + + + + + + + + Zoom Out + + + + - + + + + Move Up 10 Pixels + + + + Up + + + + Move Left 10 Pixels + + + + Left + + + + Move Right 10 Pixels + + + + Right + + + + Move Down 10 Pixels + + + + Down + + + + Move Up 1 Pixel + + + + Shift+Up + + + + Move Left 1 Pixel + + + + Shift+Left + + + + Move Right 1 Pixel + + + + Shift+Right + + + + Move Down 1 Pixel + + + + Shift+Down + + + + Go To Packet Under Cursor + + + + Go to packet currently under the cursor + + + + G + + + + All Flows + + + + Show flows for all packets + + + + 1 + + + + TCP Flows + + + + Show only TCP flow information + + + + Go To Next Packet + + + + Go to the next packet + + + + N + + + + Go To Previous Packet + + + + Go to the previous packet + + + + P + + + + Select RTP Stream + + + + Select RTP stream in RTP Streams dialog + + + + S + + + + Deselect RTP Stream + + + + Deselect RTP stream in RTP Streams dialog + + + + D + + + + + ShortcutListModel + + Shortcut + + + + Name + + + + Description + + + + + ShowPacketBytesDialog + + Show Packet Bytes + + + + Hint. + + + + Decode as + + + + Show as + + + + Start + + + + End + + + + Find: + + + + Find &Next + + + + Frame %1, %2, %Ln byte(s). + + Frame %1, %2, %Ln byte. + Frame %1, %2, %Ln bytes. + + + + None + + + + Base64 + + + + Compressed + + + + Hex Digits + + + + Percent-Encoding + + + + Quoted-Printable + + + + ROT13 + + + + ASCII + + + + ASCII & Control + + + + C Array + + + + EBCDIC + + + + Hex Dump + + + + HTML + + + + Image + + + + Raw + + + + Rust Array + + + + UTF-8 + + + + YAML + + + + Print + + + + Copy + + + + Save as… + + + + Save Selected Packet Bytes As… + + + + Displaying %Ln byte(s). + + Displaying %Ln byte. + Displaying %Ln bytes. + + + + JSON + + + + Regex Find: + + + + + ShowPacketBytesTextEdit + + Show Selected + + + + Show All + + + + + SplashOverlay + + Initializing dissectors + + + + Initializing tap listeners + + + + Initializing external capture plugins + + + + Registering dissectors + + + + Registering plugins + Registering dissector + + + + Handing off dissectors + + + + Handing off plugins + + + + Loading Lua plugins + + + + Removing Lua plugins + + + + Loading module preferences + + + + Finding local interfaces + + + + Applying changed preferences + + + + (Unknown action) + + + + + StatsTreeDialog + + Configuration not found + + + + Unable to find configuration for %1. + + + + + StripHeadersDialog + + Dialog + + + + Display filter: + + + + + SupportedProtocolsDialog + + Dialog + + + + <html><head/><body><p>Search the list of field names.</p></body></html> + + + + Search: + + + + <small><i>Gathering protocol information…</i></small> + + + + Supported Protocols + + + + %1 protocols, %2 fields. + + + + + SupportedProtocolsModel + + Name + + + + Filter + + + + Type + + + + Description + + + + + SyntaxLineEdit + + Invalid filter: %1 + + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + + + + %1 + + + + + TCPStreamDialog + + Dialog + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + + + + <small><i>Mouse over for shortcuts</i></small> + + + + Type + + + + MA Window (s) + + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + + + + Select SACKs + select SACKs + + + + Stream + + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + + + + Switch Direction + + + + Mouse + + + + Drag using the mouse button. + + + + drags + + + + Select using the mouse button. + + + + zooms + + + + Display Round Trip Time vs Sequence Number + + + + RTT By Sequence Number + + + + Display graph of Segment Length vs Time + + + + Segment Length + + + + Display graph of Mean Transmitted Bytes vs Time + + + + Display graph of Mean ACKed Bytes vs Time + + + + Goodput + + + + Display graph of Receive Window Size vs Time + + + + Rcv Win + + + + Display graph of Outstanding Bytes vs Time + + + + Bytes Out + + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + + + + Reset + + + + Reset Graph + + + + Reset the graph to its initial state. + + + + 0 + + + + Zoom In + + + + + + + + + Zoom Out + + + + - + + + + Move Up 10 Pixels + + + + Up + + + + Move Left 10 Pixels + + + + Left + + + + Move Right 10 Pixels + + + + Right + + + + Move Down 10 Pixels + + + + Down + + + + Move Up 1 Pixel + + + + Shift+Up + + + + Move Left 1 Pixel + + + + Shift+Left + + + + Move Right 1 Pixel + + + + Shift+Right + + + + Move Down 1 Pixel + + + + Shift+Down + + + + Next Stream + + + + Go to the next stream in the capture + + + + PgUp + + + + Previous Stream + + + + Go to the previous stream in the capture + + + + PgDown + + + + Switch direction (swap TCP endpoints) + + + + D + + + + Go To Packet Under Cursor + + + + Go to packet currently under the cursor + + + + G + + + + Drag / Zoom + + + + Toggle mouse drag / zoom behavior + + + + Z + + + + Relative / Absolute Sequence Numbers + + + + Toggle relative / absolute sequence numbers + + + + S + + + + Capture / Session Time Origin + + + + Toggle capture / session time origin + + + + T + + + + Crosshairs + + + + Toggle crosshairs + + + + Space + + + + Round Trip Time + + + + Switch to the Round Trip Time graph + + + + 1 + + + + Throughput + + + + Switch to the Throughput graph + + + + 2 + + + + Time / Sequence (Stevens) + + + + Switch to the Stevens-style Time / Sequence graph + + + + 3 + + + + Window Scaling + + + + Switch to the Window Scaling graph + + + + 5 + + + + Time / Sequence (tcptrace) + + + + Switch to the tcptrace-style Time / Sequence graph + + + + 4 + + + + Zoom In X Axis + + + + X + + + + Zoom Out X Axis + + + + Shift+X + + + + Zoom In Y Axis + + + + Y + + + + Zoom Out Y Axis + + + + Shift+Y + + + + Save As… + + + + No Capture Data + + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + + + + Sequence Numbers (Stevens) + + + + Sequence Numbers (tcptrace) + + + + (MA) + + + + (%1 Segment MA) + + + + [not enough data] + + + + for %1:%2 %3 %4:%5 + + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + + Click to select packet + + + + Packet + + + + Release to zoom, x = %1 to %2, y = %3 to %4 + + + + Unable to select range. + + + + Click to select a portion of the graph. + + + + Portable Document Format (*.pdf) + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + Save Graph As… + + + + + TLSKeylogDialog + + Dialog + + + + Browse… + + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + + + + Item + + + + <small><i>A hint.</i></small> + + + + Display filter: + + + + Regenerate statistics using this display filter + + + + Apply + + + + Copy + + + + Copy a text representation of the tree to the clipboard + + + + Save as… + Save as... + + + + Save the displayed data in various formats + + + + Collapse All + + + + Expand All + + + + Save Statistics As… + + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + + + + Plain text file (*.txt) + + + + Error saving file %1 + + + + + TimeShiftDialog + + Shift all packets by + + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + + Set the time for packet + + + + to + + + + …then set packet + ...then set packet + + + + and extrapolate the time for all other packets + + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + + Undo all shifts + + + + Time Shift + + + + Frame numbers must be between 1 and %1. + + + + Invalid frame number. + + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + + + + Could not open base file %1 for reading: %2 + + + + No endpoints available to map + + + + Unable to create temporary file + + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + + + + Name resolution + + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + + Limit to display filter + + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + + + + Filter list for specific type + + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + + + + GroupBox + + + + Absolute start time + + + + Copy + + + + Unknown + + + + + TrafficTree + + Resize all columns to content + + + + Filter on stream id + + + + Copy %1 table + + + + as CSV + + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + + + + as YAML + + + + Copy all values of this page to the clipboard in the YAML data serialization format. + + + + as JSON + + + + Copy all values of this page to the clipboard in the JSON data serialization format. + + + + Save data as raw + + + + Disable data formatting for export/clipboard and save as raw data + + + + + TrafficTreeHeaderView + + Less than + + + + Greater than + + + + Equal + + + + Columns to display + + + + Filter %1 by + + + + Enter filter value + + + + + TrafficTypesModel + + Protocol + + + + + UatDialog + + Create a new entry. + + + + Remove this entry. + Remove this profile. + + + + Copy this entry. + Copy this profile. + + + + Move entry up. + + + + Move entry down. + + + + Clear all entries. + + + + Unknown User Accessible Table + + + + Open + + + + + UatFrame + + Frame + + + + Create a new entry. + + + + Remove this entry. + + + + Copy this entry. + + + + Move entry up. + + + + Move entry down. + + + + Clear all entries. + + + + Copy entries from another profile. + + + + Copy from + + + + Unknown User Accessible Table + + + + Open + + + + + VoipCallsDialog + + <small></small> + + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + + Limit to display filter + + + + Time of Day + + + + Flow &Sequence + + + + Show flow sequence for selected call(s). + + + + Prepare &Filter + + + + Prepare a filter matching the selected calls(s). + + + + Cop&y + + + + Open copy menu + + + + All + + + + Select all + + + + None + + + + Invert + + + + Invert selection + + + + Select related RTP streams + + + + Select RTP streams related to selected calls in RTP Streams dialog + + + + S + + + + Deselect related RTP Streams + + + + D + + + + Clear selection + + + + Display time as time of day + + + + Copy as CSV + + + + Copy stream list as CSV. + + + + Copy as YAML + + + + Copy stream list as YAML. + + + + SIP Flows + + + + VoIP Calls + + + + as CSV + + + + as YAML + + + + Select + + + + + VoipCallsInfoModel + + On + + + + Off + + + + Tunneling: %1 Fast Start: %2 + + + + Start Time + + + + Stop Time + + + + Initial Speaker + + + + From + + + + To + + + + Protocol + + + + Duration + + + + Packets + + + + State + + + + Comments + + + + + WelcomePage + + Form + + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + + + + <html><head/><body><p>Open a file on your file system</p></body></html> + + + + <h2>Open</h2> + + + + Recent capture files + + + + Capture files that have been opened previously + + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + + + + <h2>Capture</h2> + + + + …using this filter: + + + + Interface list + + + + List of available capture interfaces + + + + <h2>Learn</h2> + + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + + + + Show in Finder + + + + Show in Folder + + + + Welcome to %1 + + + + All interfaces shown + + + + %n interface(s) shown, %1 hidden + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + You are sniffing the glue that holds the Internet together using Wireshark + + + + You are running Wireshark + + + + You receive automatic updates. + + + + You have disabled automatic updates. + + + + not found + + + + Copy file path + + + + Remove from list + + + + + WirelessFrame + + Frame + + + + Interface + + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + + + + Channel + + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + + + + FCS Filter + + + + All Frames + + + + Valid Frames + + + + Invalid Frames + + + + Wireless controls are not supported in this version of Wireshark. + + + + External Helper + + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + + + + 802.11 Preferences + + + + AirPcap Control Panel + + + + Open the AirPcap Control Panel + + + + Unable to set channel or offset. + + + + Unable to set FCS validation behavior. + + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + + + + + WiresharkDialog + + Failed to attach to tap "%1" + + + + + WiresharkMainWindow + + Wireshark + + + + Go to packet + + + + Cancel + + + + File Set + + + + Export Packet Dissections + + + + Export Objects + + + + &Zoom + + + + &Time Display Format + + + + Copy + + + + Manual pages + + + + Apply as Filter + + + + Prepare as Filter + + + + SCTP + + + + TCP Stream Graphs + + + + BACnet + + + + HTTP + + + + &File + + + + &Capture + + + + &Help + + + + &Go + + + + &View + + + + &Analyze + + + + Follow + + + + &Statistics + + + + 29West + + + + Topics + + + + Queues + + + + UIM + + + + Telephon&y + + + + RTSP + + + + &Edit + + + + Packet Comments + + + + Main Toolbar + + + + Display Filter Toolbar + + + + Open a capture file + + + + Quit Wireshark + + + + &Start + + + + Start capturing packets + + + + S&top + + + + Stop capturing packets + + + + No files found + + + + &Contents + + + + Wireshark Filter + + + + TShark + + + + Rawshark + + + + Dumpcap + + + + Mergecap + + + + Editcap + + + + Text2pcap + + + + Website + + + + Downloads + + + + Wiki + + + + Sample Captures + + + + &About Wireshark + + + + Ask (Q&&A) + + + + Next Packet + + + + Go to the next packet + + + + Previous Packet + + + + Go to the previous packet + + + + First Packet + + + + Go to the first packet + + + + Last Packet + + + + Go to the last packet + + + + E&xpand Subtrees + + + + Expand the current packet detail + + + + &Expand All + + + + Expand packet details + + + + Collapse &All + + + + Collapse all packet details + + + + Go to specified packet + + + + Merge one or more files + + + + Import a file + + + + &Save + + + + Save as a different file + + + + Export specified packets + + + + Export TLS Session Keys… + + + + List Files + + + + Next File + + + + Previous File + + + + &Reload + + + + Options + + + + Capture options + + + + Capture filters + + + + Refresh Interfaces + + + + Refresh interfaces + + + + &Restart + + + + Restart current capture + + + + As &CSV… + + + + As "C" &Arrays… + + + + As P&SML XML… + + + + As P&DML XML… + + + + As &JSON… + + + + Description + + + + Field Name + + + + Value + + + + As Filter + + + + Close this capture file + + + + Packet: + + + + Interface Toolbars + + + + Colorize Conversation + + + + Internals + + + + Additional Toolbars + + + + Conversation Filter + + + + Reliable Server Pooling (RSerPool) + + + + SOME/IP + + + + &DTN + + + + Osmux + + + + &Tools + Tools + + + + Wireless Toolbar + + + + Help contents + + + + FAQs + + + + Next Packet in Conversation + + + + Go to the next packet in this conversation + + + + Previous Packet in Conversation + + + + Go to the previous packet in this conversation + + + + Next Packet In History + + + + Go to the next packet in your selection history + + + + Previous Packet In History + + + + Go to the previous packet in your selection history + + + + Collapse Subtrees + + + + Collapse the current packet detail + + + + Go to Packet… + + + + &Merge… + + + + &Import from Hex Dump… + + + + Save this capture file + + + + Save &As… + + + + Export Specified Packets… + + + + Export Packet &Bytes… + + + + &Print… + + + + Reload this file + + + + Reload as File Format/Capture + + + + Copy this item's description + + + + Copy this item's field name + + + + Copy this item's value + + + + Copy this item as a display filter + + + + Apply as Column + + + + Create a packet list column from the selected field. + + + + Find a packet + + + + Find the next packet + + + + Find the previous packet + + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + &Mark/Unmark Packet + + + Mark All Displayed + + + + Mark all displayed packets + + + + Unmark all displayed packets + + + + Next Mark + + + + Go to the next marked packet + + + + Previous Mark + + + + Go to the previous marked packet + + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + &Ignore/Unignore Packet + + + Ignore All Displayed + + + + Ignore all displayed packets + + + + Set/Unset Time Reference + + + + Set or unset a time reference for this packet + + + + Unset All Time References + + + + Remove all time references + + + + Next Time Reference + + + + Go to the next time reference + + + + Previous Time Reference + + + + Go to the previous time reference + + + + Shift or change packet timestamps + + + + Delete All Packet Comments + + + + Remove all packet comments in the capture file + + + + &Configuration Profiles… + + + + Configuration profiles + + + + Manage your configuration profiles + + + + Manage Wireshark's preferences + + + + Capture File Properties + + + + Capture file properties + + + + &Protocol Hierarchy + + + + Show a summary of protocols present in the capture file. + + + + Capinfos + + + + Reordercap + + + + Time Sequence (Stevens) + + + + TCP time sequence graph (Stevens) + + + + Throughput + + + + Round Trip Time + + + + TCP round trip time + + + + Window Scaling + + + + TCP window scaling + + + + HTTP/2 Stream + + + + SIP Call + + + + Time Sequence (tcptrace) + + + + TCP time sequence graph (tcptrace) + + + + Analyse this Association + + + + Show All Associations + + + + Flow Graph + + + + Flow sequence diagram + + + + ANCP + + + + ANCP statistics + + + + Packets sorted by Instance ID + + + + BACapp statistics sorted by instance ID + + + + Packets sorted by IP + + + + BACapp statistics sorted by IP + + + + Packets sorted by object type + + + + BACapp statistics sorted by object type + + + + Packets sorted by service + + + + BACapp statistics sorted by service + + + + Collectd + + + + Collectd statistics + + + + DNS + + + + DNS statistics + + + + HART-IP + + + + HART-IP statistics + + + + HPFEEDS + + + + hpfeeds statistics + + + + HTTP2 + + + + HTTP2 statistics + + + + Packet Counter + + + + HTTP packet counter + + + + Requests + + + + HTTP requests + + + + Load Distribution + + + + HTTP load distribution + + + + Packet Lengths + + + + Packet length statistics + + + + Sametime + + + + Sametime statistics + + + + SOME/IP Messages + + + + SOME/IP Message statistics + + + + SOME/IP-SD Entries + + + + SOME/IP-SD Entries statistics + + + + &LTP + + + + LTP segment and block statistics + + + + &ISUP Messages + + + + ISUP message statistics + + + + Osmux packet counts + + + + RTSP packet counts + + + + SM&PP Operations + + + + SMPP operation statistics + + + + &UCP Messages + + + + UCP message statistics + + + + F1AP + + + + F1AP Messages + + + + NGAP + + + + NGAP Messages + + + + Change the way packets are dissected + + + + Reload Lua Plugins + + + + Reload Lua plugins + + + + Advertisements by Topic + + + + Advertisements by Source + + + + Advertisements by Transport + + + + Queries by Topic + + + + Queries by Receiver + + + + Wildcard Queries by Pattern + + + + Wildcard Queries by Receiver + + + + Advertisements by Queue + + + + Queries by Queue + + + + Streams + + + + LBT-RM + + + + LBT-RU + + + + Filter this Association + + + + Strip Headers… + + + + Strip headers and export higher level encapsulations to file + + + + &I/O Graphs + + + + &Conversations + + + + &Endpoints + + + + Shrink the main window text + + + + Return the main window text to its normal size + + + + Reset Layout + + + + Reset appearance layout to default size + + + + Seconds Since First Captured Packet + + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + + + + Show or hide the packet diagram + + + + Show each conversation hash table + + + + Show each dissector table and its entries + + + + Show the currently supported protocols and display filter fields + + + + MAC Statistics + + + + LTE MAC statistics + + + + RLC Statistics + + + + LTE RLC statistics + + + + LTE RLC graph + + + + MTP3 Summary + + + + MTP3 summary statistics + + + + Bluetooth Devices + + + + Bluetooth HCI Summary + + + + Display Filter &Expression… + + + + Display Filter Expression… + + + + REGISTER_STAT_GROUP_RSERPOOL + + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + + + + No GSM statistics registered + + + + No LTE statistics registered + + + + No MTP3 statistics registered + + + + IAX2 Stream Analysis + + + + Show Packet Bytes… + + + + Go to &Linked Packet + + + + UDP Multicast Streams + + + + Show UTP multicast stream statistics. + + + + WLAN Traffic + + + + Show IEEE 802.11 wireless LAN statistics. + + + + Add a display filter button. + + + + Firewall ACL Rules + + + + Create firewall ACL rules + + + + &Full Screen + + + + Credentials + + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + + + + &Wireless + + + + Capture &Filters… + + + + As Plain &Text… + + + + As Plain &Text + + + + As &CSV + + + + As &YAML + + + + All Visible Items + + + + All Visible Selected Tree Items + + + + Display Filter &Macros… + + + + &Find Packet… + + + + Find Ne&xt + + + + Find Pre&vious + + + + Mark or unmark each selected packet + + + + Ignore or unignore each selected packet + + + + U&nignore All Displayed + + + + Unignore all displayed packets + + + + Time Shift… + + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + + + + TCP throughput + + + + Request Sequences + + + + HTTP Request Sequences + + + + Decode &As… + + + + Export PDUs to File… + + + + Create graphs based on display filter fields + + + + &Main Toolbar + + + + Show or hide the main toolbar + + + + &Filter Toolbar + + + + Show or hide the display filter toolbar + + + + Conversations at different protocol levels + + + + Endpoints at different protocol levels + + + + Colorize Packet List + + + + Draw packets using your coloring rules + + + + &Zoom In + + + + Enlarge the main window text + + + + Zoom Out + + + + Normal Size + + + + Resize Columns + + + + Resize packet list columns to fit contents + + + + Date and Time of Day (1970-01-01 01:02:03.123456) + + + + Show packet times as the date and time of day. + + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + + + + Show packet times as the year, day of the year and time of day. + + + + Time of Day (01:02:03.123456) + + + + Seconds Since 1970-01-01 + + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + + + + Seconds Since Previous Captured Packet + + + + Show packet times as the seconds since the previous captured packet. + + + + Seconds Since Previous Displayed Packet + + + + Show packet times as the seconds since the previous displayed packet. + + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + + + + Show packet times as the UTC date and time of day. + + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + + + + Show packet times as the UTC year, day of the year and time of day. + + + + UTC Time of Day (01:02:03.123456) + + + + Show packet times as the UTC time of day. + + + + Automatic (from capture file) + + + + Use the time precision indicated in the capture file. + + + + Seconds + + + + Tenths of a second + + + + Hundredths of a second + + + + Milliseconds + + + + Microseconds + + + + Nanoseconds + + + + Display Seconds With Hours and Minutes + + + + Display seconds with hours and minutes + + + + Resolve &Physical Addresses + + + + Show names for known MAC addresses. Lookups use a local database. + + + + Resolve &Network Addresses + + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + + + + Resolve &Transport Addresses + + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + + + + Wire&less Toolbar + + + + Show or hide the wireless toolbar + + + + &Status Bar + + + + Show or hide the status bar + + + + Packet &List + + + + Show or hide the packet list + + + + Packet &Details + + + + Show or hide the packet details + + + + Packet &Bytes + + + + Show or hide the packet bytes + + + + &Conversation Hash Tables + + + + &Dissector Tables + + + + &Supported Protocols + + + + MAP Summary + + + + GSM MAP summary statistics + + + + RLC &Graph + + + + &Coloring Rules… + + + + Show Linked Packet in New Window + + + + New Coloring Rule… + New Conversation Rule… + + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + + + + RTP Player + + + + Play selected stream. Press CTRL key for playing reverse stream too. + + + + IA&X2 Stream Analysis + + + + Enabled Protocols… + Enable Protocols… + + + + Wiki Protocol Page + + + + Open the Wireshark wiki page for this protocol. + + + + Filter Field Reference + + + + Open the display filter reference page for this filter field. + + + + Go to the packet referenced by the selected field. + + + + &VoIP Calls + + + + Open &Recent + + + + Name Resol&ution + + + + Service &Response Time + + + + &RTP + + + + S&CTP + + + + &ANSI + + + + &GSM + + + + &LTE + + + + &MTP3 + + + + &Open + + + + &Quit + + + + &Close + + + + Display &Filters… + + + + &Unmark All Displayed + + + + All VoIP Calls + + + + SIP &Flows + + + + SIP Flows + + + + RTP Streams + + + + Edit the packet list coloring rules. + + + + Bluetooth ATT Server Attributes + ATT Server Attributes + + + + Show Packet in New &Window + + + + Show this packet in a separate window. + + + + Show the linked packet in a separate window. + + + + Auto Scroll in Li&ve Capture + + + + Automatically scroll to the last packet during a live capture. + + + + Expert Information + + + + Show expert notifications + + + + Add an expression to the display filter. + + + + REGISTER_STAT_GROUP_UNSORTED + + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + + + + No ANSI statistics registered + No tools registered + + + + Resolved Addresses + + + + Show each table of resolved addresses as copyable text. + + + + Color &1 + + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + + + + Color &2 + + + + Color &3 + + + + Color &4 + + + + Color &5 + + + + Color &6 + + + + Color &7 + + + + Color &8 + + + + Color &9 + + + + Color 1&0 + + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + + + + Reset Colorization + + + + Reset colorized conversations. + + + + RTP Stream Analysis + + + + Edit Resolved Name + + + + Manually edit a name resolution entry. + + + + Enable and disable specific protocols + + + + before quitting + + + + Save packets before merging? + + + + A temporary capture file can't be merged. + + + + Save changes in "%1" before merging? + + + + Changes must be saved before the files can be merged. + + + + Invalid Display Filter + + + + Invalid Read Filter + + + + The filter expression %1 isn't a valid read filter. (%2). + + + + before importing a capture + before importing a new capture + + + + Unable to export to "%1". + + + + You cannot export packets to the current capture file. + + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + + + + Your captured packets will be lost if you don't save them. + + + + Do you want to save the changes you've made to the capture file "%1"%2? + + + + Your changes will be lost if you don't save them. + + + + Check for Updates… + + + + Unable to drop files during capture. + + + + Unknown file type returned by merge dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + Unknown file type returned by export dialog. + + + + Do you want to stop the capture and save the captured packets%1? + + + + Do you want to save the captured packets%1? + + + + Save before Continue + + + + Stop and Save + + + + Stop and Quit &without Saving + Stop and Quit without Saving + + + + Quit &without Saving + Quit without Saving + + + + There is no "rtp.ssrc" field in this version of Wireshark. + + + + Please select an RTPv2 packet with an SSRC value + + + + SSRC value not found. + + + + Show or hide the toolbar + + + + Continue &without Saving + Continue without Saving + + + + Stop and Continue &without Saving + Stop and Continue without Saving + + + + The Wireshark Network Analyzer + + + + Capturing from %1 + + + + before opening another file + + + + Merging files. + + + + %1: %2 + + + + Clear Menu + + + + before closing the file + + + + Export Selected Packet Bytes + + + + No Keys + + + + Export SSL Session Keys (%Ln key(s)) + Export SSL Session Keys (%1 key%2 + + Export SSL Session Keys (%Ln key) + Export SSL Session Keys (%Ln keys) + + + + Raw data (*.bin *.dat *.raw);;All Files ( + + + + Couldn't copy text. Try another item. + + + + Are you sure you want to remove all packet comments? + + + + Unable to build conversation filter. + + + + before reloading the file + + + + Error compiling filter for this conversation. + + + + No previous/next packet in conversation. + + + + No interface selected. + + + + Saving %1… + + + + Configure all extcaps before start of capture. + + + + Invalid capture filter. + + + + (empty comment) + placeholder for empty comment + + + + Add New Comment… + + + + Edit "%1" + edit packet comment + + + + Delete "%1" + delete packet comment + + + + Delete packet comments + + + + Delete comments from %n packet(s) + + + + + + + before starting a new capture + + + + before reloading Lua plugins + + + + Please wait while Wireshark is initializing… + + + + before updating + + + + There are no TLS Session Keys to save. + + + + Export TLS Session Keys (%Ln key(s)) + + Export TLS Session Keys (%Ln key) + Export TLS Session Keys (%Ln keys) + + + + TLS Session Keys (*.keys *.txt);;All Files ( + + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + + + + column + + + + item + + + + The "%1" column already exists. + + + + The "%1" column already exists as "%2". + + + + RTP packet search failed + + + + No Interface Selected. + + + + before restarting the capture + + + + Wiki Page for %1 + + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + + + + Loading + + + + Reloading + + + + Rescanning + + + + + WlanStatisticsDialog + + Wireless LAN Statistics + + + + Channel + + + + SSID + + + + Percent Packets + + + + Percent Retry + + + + Probe Reqs + + + + Probe Resp + + + + Auths + + + + Retry + + + + Deauths + + + + Other + + + + diff --git a/ui/qt/wireshark_es.ts b/ui/qt/wireshark_es.ts new file mode 100644 index 00000000..f4183d93 --- /dev/null +++ b/ui/qt/wireshark_es.ts @@ -0,0 +1,14690 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + Acerca de Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Analizador de protocolo de red</span> + + + Copy the version information to the clipboard + Copia la información de versión al portapapeles + + + Copy to Clipboard + Copiar al portapapeles + + + Authors + Autores + + + Search Authors + Buscar autores + + + Folders + Carpetas + + + Filter by path + Filtrar por ruta + + + Plugins + Complementos + + + No plugins found. + No se encontraron complementos. + + + Search Plugins + Buscar complementos + + + Filter by type: + Filtrar por tipo: + + + Keyboard Shortcuts + Atajos de teclado + + + Search Shortcuts + Buscar atajos + + + Acknowledgments + Agradecimientos + + + License + Licencia + + + The directory does not exist + El directorio no existe + + + Should the directory %1 be created? + ¿Debería ser creado el directorio %1? + + + The directory could not be created + El directorio no se ha podido crear + + + The directory %1 could not be created. + No se ha podido crear el directorio %1. + + + Show in Finder + Mostrar en Finder + + + Show in Folder + Mostrar en carpeta + + + Copy + Copiar + + + Copy Row(s) + + + + + + + + AddressEditorFrame + + Frame + Frame + + + Name Resolution Preferences… + Name Resolution Preferences... + Preferencias de resolución de nombre… + + + Address: + Dirección: + + + Name: + Nombre: + + + Can't assign %1 to %2. + + + + + AdvancedPrefsModel + + Name + Nombre + + + Status + Estado + + + Type + Tipo + + + Value + Valor + + + + ApplyLineEdit + + Apply changes + + + + + AuthorListModel + + Name + Nombre + + + Email + Correo electrónico + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Atributos de servidor ATT bluetooth + + + Handle + Identificador + + + UUID + UUID + + + UUID Name + Nombre UUID + + + All Interfaces + Todas las interfaces + + + All Devices + Todos los dispositivos + + + Remove duplicates + Eliminar duplicados + + + Copy Cell + Copiar celda + + + Copy Rows + Copiar filas + + + Copy All + Copiar todo + + + Save as image + Guardar como imagen + + + Mark/Unmark Row + Marcar/Desmarcar fila + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Marcar/Desmarcar celda + + + Save Table Image + Guardar imagen de tabla + + + PNG Image (*.png) + + + + + BluetoothDeviceDialog + + Bluetooth Device + Dispositivo bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Nombre + + + Class of Device + Clase de dispositivo + + + LMP Version + Versión LMP + + + LMP Subversion + Subversión LMP + + + Manufacturer + Fabricante + + + HCI Version + Versión HCI + + + HCI Revision + Revisión HCI + + + Scan + + + + Authentication + Autenticación + + + Encryption + Cifrado + + + ACL MTU + MTU ACL + + + ACL Total Packets + Paquetes ACL totales + + + SCO MTU + MTU SCO + + + SCO Total Packets + Paquetes SCO totales + + + LE ACL MTU + MTU LE ACL + + + LE ACL Total Packets + + + + LE ISO MTU + + + + LE ISO Total Packets + + + + Inquiry Mode + + + + Page Timeout + + + + Simple Pairing Mode + + + + Voice Setting + + + + Value + Valor + + + Changes + Cambios + + + %1 changes + %1 cambios + + + Copy Cell + Copiar celda + + + Copy Rows + Copiar filas + + + Copy All + Copiar todo + + + Save as image + Guardar como imagen + + + Mark/Unmark Row + Marcar/Desmarcar fila + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Marcar/Desmarcar celda + + + Unknown + Desconocida + + + Bluetooth Device - %1%2 + Dispositivo bluetooth - %1%2 + + + enabled + + + + disabled + + + + %1 ms (%2 slots) + + + + Save Table Image + Guardar imagen de tabla + + + PNG Image (*.png) + + + + + BluetoothDevicesDialog + + Bluetooth Devices + Dispositivos bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Nombre + + + LMP Version + Versión LMP + + + LMP Subversion + Subversión LMP + + + Manufacturer + Fabricante + + + HCI Version + Versión HCI + + + HCI Revision + Revisión HCI + + + Is Local Adapter + Es adaptador local + + + All Interfaces + Todas las interfaces + + + Show information steps + + + + %1 items; Right click for more option; Double click for device details + %1 ítems; Clic derecho para más opción; Doble clic para detalles de dispositivo + + + Copy Cell + Copiar celda + + + Copy Rows + Copiar filas + + + Copy All + Copiar todo + + + Save as image + Guardar como imagen + + + Mark/Unmark Row + Marcar/Desmarcar fila + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Marcar/Desmarcar celda + + + true + + + + Save Table Image + Guardar imagen de tabla + + + PNG Image (*.png) + + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Informe HCI de bluetooth + + + Name + Nombre + + + OGF + OGF + + + OCF + OCF + + + Opcode + Código de operación + + + Event + Evento + + + Subevent + Subevento + + + Status + Estado + + + Reason + Causa + + + Hardware Error + Error de hardware + + + Occurrence + + + + Link Control Commands + Comandos de control de enlace + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Comandos de política de enlace + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Comandos de controlador & banda base + + + 0x03 + 0x03 + + + Informational Parameters + Parámetros informativos + + + 0x04 + 0x04 + + + Status Parameters + Parámetros de estado + + + 0x05 + 0x05 + + + Testing Commands + Comandos de prueba + + + 0x06 + 0x06 + + + LE Controller Commands + Comandos de controlador LE + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Comandos específicos de proveedor + + + 0x3F + 0x3F + + + Unknown OGF + OGF desconocido + + + Events + Eventos + + + Hardware Errors + Errores de hardware + + + Results filter: + Filtro de resultados: + + + Display filter: + Filtro de visualización: + + + All Interfaces + Todas las interfaces + + + All Adapters + Todos los adaptadores + + + Copy Cell + Copiar celda + + + Copy Rows + Copiar filas + + + Copy All + Copiar todo + + + Save as image + Guardar como imagen + + + Mark/Unmark Row + Marcar/Desmarcar fila + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Marcar/Desmarcar celda + + + Unknown + Desconocido + + + Adapter %1 + Adaptador %1 + + + Frame %1 + Trama %1 + + + Pending + + + + Save Table Image + Guardar imagen de tabla + + + PNG Image (*.png) + + + + + ByteViewTab + + Packet bytes + Bytes de paquete + + + + ByteViewText + + Allow hover highlighting + + + + Show bytes as hexadecimal + Mostrar bytes como hexadecimal + + + …as decimal + + + + …as octal + + + + …as bits + …como bits + + + Show text based on packet + Mostrar texto basado en paquete + + + …as ASCII + …como ASCII + + + …as EBCDIC + …como EBCDIC + + + + CaptureFile + + [closing] + + + + [closed] + + + + + CaptureFileDialog + + This capture file contains comments. + Este archivo de captura contiene comentarios. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + El formato de archivo que eligió no soporta comentarios. ¿Desea guardar la captura en un formato que soporte comentarios o descartar los comentarios y guardar en el formato que eligió? + + + Discard comments and save + Descartar comentarios y guardar + + + Save in another format + Guardar en otro formato + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + + + + All Files ( + Todos los archivos ( + + + All Capture Files + Todos los archivos de captura + + + Format: + Formato: + + + Size: + Tamaño: + + + Start / elapsed: + Inicio / transcurrido: + + + Automatically detect file type + Detectar automáticamente tipo de archivo + + + Prepend packets + Anteponer paquetes + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Inserta paquetes desde el archivo seleccionado antes del archivo actual. Las marcas de tiempo de los paquetes serán ignoradas. + + + Merge chronologically + Fusionar cronológicamente + + + Insert packets in chronological order. + Inserta paquetes en orden cronológico. + + + Append packets + Añadir paquetes + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Inserta paquetes desde el archivo seleccionado después del archivo actual. Las marcas de tiempo de los paquetes serán ignoradas. + + + Read filter: + Filtro de lectura: + + + Compress with g&zip + Comprimir con g&zip + + + Open Capture File + Wireshark: Open Capture File + Abrir archivo de captura + + + Save Capture File As + Wireshark: Save Capture File As + Guardar archivo de captura como + + + Save as: + Guardar como: + + + Export Specified Packets + Wireshark: Export Specified Packets + Exportar paquetes especificados + + + Export as: + Exportar como: + + + Merge Capture File + Wireshark: Merge Capture File + Fusionar archivo de captura + + + Unknown file type returned by save as dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + directory + directorio + + + unknown file format + formato de archivo desconocido + + + error opening file + + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + + + + + + %1, timed out at %Ln data record(s) + + + + + + + %1, %Ln data record(s) + + + + + + + unknown + desconocido + + + + CaptureFilePropertiesDialog + + Details + Detalles + + + Capture file comments + Comentarios de archivo de captura + + + Refresh + Actualizar + + + Copy To Clipboard + Copiar al portapapeles + + + Save Comments + Guardar comentarios + + + Capture File Properties + Propiedades de archivo de captura + + + Unknown + Desconocido + + + File + Archivo + + + Name + Nombre + + + Length + Longitud + + + Hash (SHA256) + Hash (SHA256) + + + Hash (SHA1) + Hash (SHA1) + + + Format + Formato + + + Encapsulation + Encapsulado + + + Snapshot length + Longitud de instantánea + + + Time + Intervalo + + + First packet + Primer paquete + + + Last packet + Último paquete + + + Elapsed + Transcurrido + + + Section %1 + + + + Capture + Captura + + + Hardware + Hardware + + + OS + SO + + + Application + Aplicación + + + Interfaces + Interfaces + + + Interface + Interfaz + + + Dropped packets + Paquetes perdidos + + + Capture filter + Filtro de captura + + + Link type + Tipo de enlace + + + Packet size limit (snaplen) + + + + none + ninguno + + + %1 bytes + %1 bytes + + + Statistics + Estadísticas + + + Measurement + Medida + + + Captured + Capturado + + + Displayed + Mostrado + + + Marked + Marcado + + + Packets + Paquetes + + + Time span, s + Espacio de tiempo, s + + + Average pps + Promedio pps + + + Average packet size, B + Promedio de tamaño de paquete, B + + + Bytes + Bytes + + + Average bytes/s + Promedio de bytes/s + + + Average bits/s + Promedio de bits/s + + + Section Comment + Sección de comentarios + + + Packet Comments + Comentarios de paquete + + + <p>Frame %1: + <p>Trama %1: + + + Created by Wireshark %1 + + + + + + + CaptureFilterCombo + + Capture filter selector + + + + + CaptureFilterEdit + + Capture filter entry + + + + Manage saved bookmarks. + Administrar marcadores guardados. + + + Apply this filter string to the display. + + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + + + + Enter a capture filter %1 + Introduzca un filtro de captura %1 + + + Save this filter + Guardar este filtro + + + Remove this filter + Eliminar este filtro + + + Manage Capture Filters + Administrar filtros de captura + + + + CaptureInfoDialog + + Capture Information + Información de captura + + + Stop Capture + Detener captura + + + %1 packets, %2:%3:%4 + %1 paquetes, %2:%3:%4 + + + + CaptureInfoModel + + Other + Otro + + + + CaptureOptionsDialog + + Input + Entrada + + + Interface + Interfaz + + + Traffic + Tráfico + + + Link-layer Header + Cabecera de capa de enlace + + + Promiscuous + Promiscuo + + + Snaplen (B) + Longitud de instantánea (B) + + + Buffer (MB) + Buffer (MB) + + + Monitor Mode + Modo monitor + + + Capture Filter + Filtro de captura + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Probablemente desea activar esto. Usualmente una tarjeta de red solo capturará el tráfico enviado a su propia dirección de red. Si desea capturar todo el tráfico que la tarjeta de red puede &quot;ver&quot;, marque esta opción. Consulte las FAQ para más detalles sobre captura de paquetes desde una red conmutada.</p></body></html> + + + Enable promiscuous mode on all interfaces + Activar modo promiscuo en todas las interfaces + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + Muestra y oculta interfaces, añade comentarios, y administra tuberías y interfaces remotos. + + + Manage Interfaces… + Administrar interfaces… + + + Capture filter for selected interfaces: + Filtro de captura para interfaces seleccionados: + + + Compile BPFs + Compilar BPFs + + + Output + Salida + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>Introduzca el nombre de archivo en el cual se escribirán los datos capturados. Por defecto, se usará un archivo temporal.</p></body></html> + + + Capture to a permanent file + Capturar a archivo permanente + + + File: + Archivo: + + + Browse… + Explorar… + + + Output format: + Formato de salida: + + + pcapng + pcapng + + + pcap + pcap + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>En lugar de usar un solo archivo de captura, se crearán varios archivos.</p><p>Los nombres de archivo generados contendrán un número creciente y la hora de inicio de la captura.</p><p>NOTA: Si activado, al menos un criterio de nuevo archivo DEBE estar seleccionado.</p></body></html> + + + Create a new file automatically… + Crear un nuevo archivo automáticamente… + + + after + después de + + + Switch to the next file after the specified number of packets have been captured. + Cambia al siguiente archivo después de haber sido capturado el número especificado de paquetes. + + + packets + paquetes + + + Switch to the next file after the file size exceeds the specified file size. + Cambia al siguiente archivo después de que el tamaño de archivo exceda el tamaño de archivo especificado. + + + kilobytes + kilobytes + + + megabytes + megabytes + + + gigabytes + gigabytes + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + Cambia al siguiente archivo cuando el tiempo de captura del archivo actual excede el tiempo especificado. + + + seconds + segundos + + + minutes + minutos + + + hours + horas + + + when time is a multiple of + cuando el tiempo es múltiplo de + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + Cambia al siguiente archivo cuando el (reloj de pared) tiempo es múltiplo del intervalo especificado. +Por ejemplo, use 1 hora para tener creado un nuevo archivo cada hora en punto. + + + compression + compresión + + + None + Ninguna + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>Después de que la captura haya cambiado al siguiente archivo y el número proporcionado haya excedido, se eliminará el archivo más antiguo.</p></body></html> + + + Use a ring buffer with + Usar un buffer cíclico con + + + files + archivos + + + Options + Opciones + + + Display Options + Opciones de visualización + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>Usando esta opción mostrará los paquetes capturados inmediatamente en la pantalla principal. Tenga en cuenta: esto ralentizará la captura, puede aparecer mayor incremento de paquetes perdidos.</p></body></html> + + + Update list of packets in real-time + Actualizar listado de paquetes en tiempo real + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>Esto desplazará el &quot;Listado de paquetes&quot; automáticamente al último paquete capturado, cuando la opción &quot;Actualizar listado de paquetes en tiempo real&quot; es usada.</p></body></html> + + + Automatically scroll during live capture + Desplazar automáticamente durante captura en vivo + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>Muestra el cuadro de diálogo de información de captura mientras que captura</p></body></html> + + + Show capture information during live capture + Mostrar información de captura durante captura en vivo + + + Name Resolution + Resolución de nombre + + + Perform MAC layer name resolution while capturing. + Realiza la resolución de nombre de la capa MAC mientras que captura. + + + Resolve MAC addresses + Resolver direcciones MAC + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>Realiza la resolución de nombre de la capa de red mientras que captura.</p></body></html> + + + Resolve network names + Resolver nombres de red + + + Perform transport layer name resolution while capturing. + Realiza la resolución de nombre de la capa de transporte mientras que captura. + + + Resolve transport names + Resolver nombres de transporte + + + Stop capture automatically after… + Detener captura automáticamente después de… + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>Detiene la captura después de haber sido capturado el número especificado de paquetes.</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + Detiene la captura después de haber sido capturado el número especificado de paquetes. + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>Detiene la captura después de haber sido creado el número especificado de archivos.</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>Detiene la captura después de haber sido capturada la cantidad de datos especificada.</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + Detiene la captura después de haber sido capturada la cantidad de datos especificada. + + + Stop capturing after the specified amount of time has passed. + Detiene la captura después de que haya pasado la cantidad de tiempo especificada. + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>Opcionalmente especifica un directorio temporal para archivos de captura sin nombre.</p></body></html> + + + Directory for temporary files + Directorio para archivos temporales + + + Capture Options + Opciones de captura + + + Start + Iniciar + + + Leave blank to use a temporary file + Deje en blanco para usar archivo temporal + + + Specify a Capture File + Especifique un archivo de captura + + + Specify temporary directory + + + + %1: %2 + %1: %2 + + + Addresses + Direcciones + + + Address + Dirección + + + no addresses + No hay direcciones + + + Error + Error + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + Varios archivos: El tamaño de archivo solicitado es demasiado grande. El tamaño de archivo no puede ser mayor de 2 GiB. + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + Varios archivos: Nombre de archivo de captura no proporcionado. Debe especificar un nombre de archivo si desea usar varios archivos. + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + Varios archivos: No hay límite de archivo proporcionado. Debe especificar un tamaño de archivo, intervalo, o número de paquetes para cada archivo. + + + + CapturePreferencesFrame + + Frame + Frame + + + Default interface + Interfaz predeterminado + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Probablemente desea activar esto. Usualmente una tarjeta de red solo capturará el tráfico enviado a su propia dirección de red. Si desea capturar todo el tráfico que la tarjeta de red puede &quot;ver&quot;, marque esta opción. Consulte las FAQ para más detalles sobre captura de paquetes desde una red conmutada.</p></body></html> + + + Capture packets in promiscuous mode + Capturar paquetes en modo promiscuo + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Captura paquetes en formato de archivo de captura de próxima generación.</p></body></html> + + + Capture packets in pcapng format + Capturar paquetes en formato pcapng + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Actualiza el listado de paquetes mientras la captura está en progreso. Esto puede provocar la pérdida de paquetes en redes de alta velocidad.</p></body></html> + + + Update list of packets in real time + Actualizar listado de paquetes en tiempo real + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + No cargar interfaces al inicio + + + Disable external capture interfaces + Desactivar interfaces de captura externas + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + + + + + ColoringRulesDialog + + Dialog + Dialog + + + <small><i>A hint.</i></small> + <small><i>A hint.</i></small> + + + Add a new coloring rule. + Añade una nueva regla de coloreado. + + + Delete this coloring rule. + Elimina esta regla de coloreado. + + + Duplicate this coloring rule. + Duplica esta regla de coloreado. + + + Clear all coloring rules. + Vacía todas las reglas de coloreado. + + + Set the foreground color for this rule. + Establece el color principal para esta regla. + + + Foreground + Primer plano + + + Set the background color for this rule. + Establece el color de fondo para esta regla. + + + Background + Fondo + + + Set the display filter using this rule. + Establece el filtro de visualización usando esta regla. + + + Apply as filter + Aplicar como filtro + + + Select a file and add its filters to the end of the list. + Selecciona un archivo y añade sus filtros al final de la lista. + + + Save filters in a file. + Guarda filtros en un archivo. + + + Coloring Rules %1 + Reglas de coloreado %1 + + + Import… + Importar… + + + Export… + Exportar… + + + Copy coloring rules from another profile. + Copia reglas de coloreado desde otro perfil. + + + Open + Abrir + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Doble clic para editar. Arrastrar para mover. Las reglas son procesadas en orden hasta que una coincidencia es encontrada. + + + Import Coloring Rules + Importar reglas de coloreado + + + Export %1 Coloring Rules + Exportar regla de coloreado %1 + + + + ColoringRulesModel + + New coloring rule + Nueva regla de coloreado + + + Unable to save coloring rules: %1 + + + + Name + Nombre + + + Filter + Filtro + + + + ColumnEditorFrame + + Frame + Frame + + + Title: + Title + Título: + + + Type: + Type + Tipo: + + + Fields: + Fields + Campos: + + + Occurrence: + Occurrence + + + + Resolve Names: + + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + + + + Missing fields. + + + + Invalid fields. + + + + Invalid occurrence value. + + + + + ColumnListModel + + Displayed + Mostrado + + + Title + Nombre + + + Type + Tipo + + + Fields + Campos + + + Field Occurrence + + + + Resolved + + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + + + + New Column + Nueva columna + + + + ColumnPreferencesFrame + + Frame + Frame + + + Add a new column + Añade una nueva columna + + + Delete selected column + Elimina columna seleccionada + + + Show displayed columns only + Mostrar solo columnas mostradas + + + Reset all changes + Restablecer todos los cambios + + + + CompiledFilterOutput + + Compiled Filter Output + Salida de filtro compilada + + + Copy + Copiar + + + Copy filter text to the clipboard. + Copia texto de filtro al portapapeles. + + + + ConversationDataModel + + Address A + Dirección A + + + Port A + Puerto A + + + Address B + Dirección B + + + Port B + Puerto B + + + Packets + Paquetes + + + Bytes + Bytes + + + Stream ID + + + + Packets A + Paquetes A + + + Bytes A + Bytes A + + + Packets B + Paquetes B + + + Bytes B + Bytes B + + + Abs Start + Inicio abs + + + Rel Start + Inicio rel + + + Duration + Duración + + + Bits/s A + Bits/s A + + + Bits/s B + Bits/s B + + + Total Packets + Paquetes totales + + + Percent Filtered + Porcentaje filtrado + + + + ConversationDialog + + Follow Stream… + Seguir secuencia… + + + Follow a TCP or UDP stream. + Sigue una secuencia TCP o UDP. + + + Graph… + Gráfica… + + + Graph a TCP conversation. + Gráfica de conversación TCP. + + + + ConversationHashTablesDialog + + Dialog + Dialog + + + Conversation Hash Tables + Tablas hash de conversación + + + + CopyFromProfileButton + + Copy from + Copiar desde + + + Copy entries from another profile. + Copia entradas desde otro perfil. + + + System default + + + + + CredentialsDialog + + Wireshark - Credentials + Wireshark - Credenciales + + + Credentials + Credenciales + + + + CredentialsModel + + Click to select the packet + Haga clic para seleccionar el paquete + + + Click to select the packet with username + Haga clic para seleccionar el paquete con el nombre de usuario + + + Username not available + Nombre de usuario no disponible + + + Packet No. + Paquete no. + + + Protocol + Protocolo + + + Username + Nombre de usuario + + + Additional Info + Info adicional + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + Copiar bytes como volcado hexadecimal + ASCII + + + Copy packet bytes as a hex and ASCII dump. + + + + …as Hex Dump + …como volcado hexadecimal + + + Copy packet bytes as a hex dump. + + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + + + + Copy packet bytes as a stream of hex. + + + + …as a Base64 String + + + + Copy packet bytes as a base64 encoded string. + + + + Copy packet bytes as application/octet-stream MIME data. + + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + + + + Remove this dissection behavior. + + + + Copy this dissection behavior. + + + + Clear all dissection behaviors. + + + + Decode As… + Decodificar como… + + + Open + Abrir + + + + DecodeAsModel + + Match using this field + + + + Change behavior when the field matches this value + + + + Field value type (and base, if Integer) + + + + Current"Decode As" behavior + + + + Default "Decode As" behavior + + + + String + Cadena + + + Integer, base + + + + unknown + desconocido + + + <none> + <none> + + + GUID + GUID + + + Field + Campo + + + Value + Valor + + + Type + Tipo + + + Default + Predeterminado + + + Current + + + + + DisplayFilterCombo + + Display filter selector + + + + Select from previously used filters. + Seleccione entre filtros usados anteriormente. + + + + DisplayFilterEdit + + Display filter entry + Muestra entrada de filtro + + + Manage saved bookmarks. + Administra marcadores guardados. + + + Display Filter Expression… + Mostrar expresión de filtro… + + + Apply a display filter %1 <%2/> + Aplique un filtro de visualización %1 <%2/> + + + Enter a display filter %1 + + + + Clear display filter + Vaciar filtro de visualización + + + Apply display filter + Aplicar filtro de visualización + + + Left align buttons + Alinear botones a la izquierda + + + Apply a read filter %1 + + + + Current filter: %1 + + + + Invalid filter: + + + + Save this filter + Guardar este filtro + + + Remove this filter + Eliminar este filtro + + + Manage Display Filters + Administrar filtros de visualización + + + Filter Button Preferences... + Preferencias de botón de filtro... + + + + DisplayFilterExpressionDialog + + Dialog + Dialog + + + Select a field to start building a display filter. + Seleccione un campo para comenzar a crear un filtro de visualización. + + + Field Name + Nombre de campo + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Busca en la lista de los nombres de campo.</p></body></html> + + + Search: + Buscar: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>Las relaciones pueden ser usadas para restringir campos a valores específicos. Cada relación hace lo siguiente:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Coincide cualquier paquete que contiene este campo</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compara el campo con un valor específico.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Comprueba el campo con una cadena (contains) o una expresión regular (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compara el campo con un conjunto específico de valores</p></td></tr></table></body></html> + + + + + Relation + Relación + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + Por defecto las comparaciones y las relaciones contains/matches/in son verdaderas si cualquier valor coincide. El cuantificador "todos" se puede usar para aplicar el análisis a todos los valores de una trama. + + + Quantifier + Cuantificador + + + Any + Cualquiera + + + All + Todos + + + Match against this value. + Coincide con este valor. + + + Value + Valor + + + If the field you have selected has a known set of valid values they will be listed here. + Si el campo que ha seleccionado tiene un conjunto conocido de valores válidos serán enumerados aquí. + + + Predefined Values + Valores predefinidos + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + Si el campo que ha seleccionado cubre un rango de bytes (e.j. ha seleccionado un protocolo) puede restringir la correspondencia a un rango de bytes aquí. + + + Range (offset:length) + Rango (intervalo:longitud) + + + No display filter + No hay filtro de visualización + + + <small><i>A hint.</i></small> + <small><i>A hint.</i></small> + + + Display Filter Expression + Mostrar expresión de filtro + + + Select a field name to get started + Seleccione un nombre de campo para comenzar + + + Click OK to insert this filter + Haga clic en ACEPTAR para insertar este filtro + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + Dialog + + + Search: + Buscar: + + + Dissector Tables + + + + + DissectorTablesProxyModel + + Table Type + Tipo de tabla + + + String + Cadena + + + Dissector Description + + + + Integer + Entero + + + Protocol + Protocolo + + + Short Name + Nombre corto + + + Table Name + Nombre de tabla + + + Selector Name + + + + + EnabledProtocolsDialog + + Dialog + Dialog + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>Desactivar un protocolo evita mostrarse protocolos de capa superior</i></small> + + + Search: + Buscar: + + + in + en + + + Enable All + Activar todo + + + Disable All + Desactivar todo + + + Invert + Invertir + + + Enabled Protocols + Protocolos activados + + + Everywhere + Cualquiera + + + Only Protocols + Solo protocolos + + + Only Description + Solo descripción + + + Only enabled protocols + Solo protocolos activados + + + Only disabled protocols + Solo protocolos desactivados + + + any protocol + Cualquier protocolo + + + non-heuristic protocols + Protocolos no heurísticos + + + heuristic protocols + Protocolos heurísticos + + + + EnabledProtocolsModel + + Protocol + Protocolo + + + Description + Descripción + + + + EndpointDataModel + + Address + Dirección + + + Port + + + + Packets + Paquetes + + + Bytes + Bytes + + + Tx Packets + + + + Tx Bytes + + + + Rx Packets + + + + Rx Bytes + + + + Country + + + + City + + + + Latitude + + + + Longitude + + + + AS Number + + + + AS Organization + + + + Total Packets + Paquetes totales + + + Percent Filtered + Porcentaje filtrado + + + + EndpointDialog + + Map + Mapa + + + Draw IPv4 or IPv6 endpoints on a map. + Dibuja los puntos finales IPv4 o IPv6 en un mapa. + + + Open in browser + Abrir en navegador + + + Save As… + Guardar como… + + + Map file error + + + + Save Endpoints Map + + + + Failed to save map file %1. + + + + + EthernetAddressModel + + Type + Tipo + + + Name + Nombre + + + Address + Dirección + + + All entries + + + + Hosts + Equipos + + + Ethernet Addresses + Direcciones ethernet + + + Ethernet Manufacturers + Fabricantes de ethernet + + + Ethernet Well-Known Addresses + Direcciones ethernet conocidas + + + + ExpertInfoDialog + + Dialog + Dialog + + + <small><i>A hint.</i></small> + <small><i>A hint.</i></small> + + + Limit to Display Filter + Limitar filtro de visualización + + + Group by summary + Agrupar por informe + + + Search expert summaries. + Busca resúmenes especializados. + + + Search: + Buscar: + + + Show… + Show... + Mostrar… + + + Error + Error + + + Show error packets. + Muestra paquetes de error. + + + Warning + Advertencia + + + Show warning packets. + Muestra paquetes de advertencia. + + + Note + Nota + + + Show note packets. + Muestra paquetes de nota. + + + Chat + Conversación + + + Show chat packets. + Muestra paquetes de conversación. + + + Comment + Comentario + + + Show comment packets. + Muestra paquetes de comentario. + + + Expert Information + Información especializada + + + Collapse All + Contraer todo + + + Expand All + Expandir todo + + + Capture file closed. + + + + No display filter + No hay filtro de visualización + + + No display filter set. + No hay conjunto de filtro de visualización. + + + Limit information to "%1". + + + + Display filter: "%1" + Filtro de visualización: "%1" + + + + ExpertInfoProxyModel + + Packet + Paquete + + + Severity + Gravedad + + + Summary + Informe + + + Group + Grupo + + + Protocol + Protocolo + + + Count + Recuento + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Exportar análisis de paquete + + + Export As: + Export as: + Exportar como: + + + Plain text (*.txt) + Texto plano (*.txt) + + + Comma Separated Values - summary (*.csv) + Informe - valores separados por coma (*.csv) + + + PSML - summary (*.psml, *.xml) + Informe - PSML (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + Detalles - PDML (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + Bytes - matrices C (*.c, *.h) + + + + ExportObjectDialog + + Dialog + Dialog + + + Content Type: + Tipo de contenido: + + + Searching for objects + Buscando objetos + + + Text Filter: + Filtro de texto: + + + Only display entries containing this string + Solo muestra entradas que contiene esta cadena + + + Preview + + + + All Content-Types + Todos los tipos de contenido + + + Export + Exportar + + + %1 object list + Listado de objetos %1 + + + Save Object As… + + + + Save All Objects In… + + + + + ExportObjectModel + + Packet + Paquete + + + Hostname + Nombre de equipo + + + Content Type + Tipo de contenido + + + Size + Tamaño + + + Filename + Nombre de archivo + + + + ExportPDUDialog + + Dialog + Dialog + + + Display filter: + Filtro de visualización: + + + + ExtArgSelector + + Reload data + + + + + ExtcapArgumentFileSelection + + Clear + + + + All Files ( + Todos los archivos ( + + + Open File + + + + Select File + + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + + + + Start + + + + Save + + + + Default + Predeterminado + + + Restore default value of the item + + + + Extcap Help cannot be found + + + + The help for the extcap interface %1 cannot be found. Given file: %2 + + + + Save parameter(s) on capture start + Guardar parámetro al iniciar captura + + + + FieldFilterEdit + + Display filter entry + Muestra entrada de filtro + + + Enter a field %1 + + + + Invalid filter: + + + + + FileSetDialog + + Dialog + Dialog + + + Directory: + Directorio: + + + No files in Set + + + + No capture loaded + + + + %Ln File(s) in Set + %1 File%2 in Set + + + + + + + + FilesetEntryModel + + Open this capture file + Abre este archivo de captura + + + Filename + Nombre de archivo + + + Created + Creado + + + Modified + Modificado + + + Size + Tamaño + + + + FilterAction + + Selected + Seleccionado + + + Not Selected + No seleccionado + + + …and Selected + …y seleccionado + + + …or Selected + …O seleccionado + + + …and not Selected + …Y no seleccionado + + + …or not Selected + …O no seleccionado + + + + FilterDialog + + Dialog + Dialog + + + Create a new filter. + Crea un nuevo filtro. + + + Remove this filter. + Remove this profile. + Elimina este filtro. + + + Copy this filter. + Copy this profile. + Copia este filtro. + + + Capture Filters + Filtros de captura + + + Display Filters + Filtros de visualización + + + Open + Abrir + + + New capture filter + This text is automatically filled in when a new filter is created + Nuevo filtro de captura + + + New display filter + This text is automatically filled in when a new filter is created + Nuevo filtro de visualización + + + + FilterExpressionFrame + + Frame + Frame + + + Filter Buttons Preferences… + Preferencias de botones de filtro… + + + Label: + Etiqueta: + + + Enter a description for the filter button + Introduzca una descripción para el botón de filtro + + + Filter: + Filtro: + + + Enter a filter expression to be applied + Introduzca una expresión de filtro para ser aplicado + + + Comment: + Comentario: + + + Enter a comment for the filter button + Introduzca un comentario para el botón de filtro + + + Missing label. + + + + Missing filter expression. + + + + Invalid filter expression. + + + + + FilterExpressionToolBar + + Filter Button Preferences... + Preferencias de botón de filtro... + + + Edit + Editar + + + Disable + Desactivar + + + Remove + Eliminar + + + + FilterListModel + + Filter Name + Nombre de filtro + + + Filter Expression + Expresión de filtro + + + + FindLineEdit + + Textual Find + + + + Regular Expression Find + + + + + FirewallRulesDialog + + Create rules for + Crear reglas para + + + Inbound + Entrante + + + Deny + Denegar + + + Firewall ACL Rules + Reglas ACL de cortafuegos + + + Copy + Copiar + + + IPv4 source address. + Dirección IPv4 de origen. + + + IPv4 destination address. + Dirección IPv4 de destino. + + + Source port. + Puerto de origen. + + + Destination port. + Puerto de destino. + + + IPv4 source address and port. + Dirección IPv4 y puerto de origen. + + + IPv4 destination address and port. + Dirección IPv4 y puerto de destino. + + + MAC source address. + Dirección MAC de origen. + + + MAC destination address. + Dirección MAC de destino. + + + Text file (*.txt);;All Files ( + Archivo de texto (*.txt);;Todos los archivos ( + + + Warning + Advertencia + + + Unable to save %1 + No puede guardar %1 + + + + FolderListModel + + "File" dialogs + Diálogos de "Archivo" + + + capture files + archivos de captura + + + Temp + Temporal + + + untitled capture files + archivos de captura sin título + + + Personal configuration + Configuración personal + + + Global configuration + Configuración global + + + dfilters, preferences, ethers, … + + + + dfilters, preferences, manuf, … + + + + System + Sistema + + + ethers, ipxnets + ethers, ipxnets + + + Program + Programa + + + program files + archivos de programa + + + Personal Plugins + Complementos personales + + + binary plugins + complementos binarios + + + Global Plugins + Complementos globales + + + Personal Lua Plugins + Complementos personales de Lua + + + Global Lua Plugins + Complementos globales de Lua + + + Lua scripts + + + + Personal Extcap path + Ruta personal de Extcap + + + external capture (extcap) plugins + + + + Global Extcap path + Ruta global de Extcap + + + MaxMind DB path + Ruta de MaxMind DB + + + MaxMind DB database search path + Ruta de búsqueda de la base de datos MaxMind DB + + + MIB/PIB path + Ruta de MIB/PIB + + + SMI MIB/PIB search path + Ruta de búsqueda de SMI MIB/PIB + + + macOS Extras + Extras de macOS + + + Extra macOS packages + Paquetes extra de macOS + + + Name + Nombre + + + Location + Ubicación + + + Typical Files + Archivos típicos + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + Filtrar secuencia + + + Print + Imprimir + + + ASCII + ASCII + + + C Arrays + Matrices C + + + EBCDIC + EBCDIC + + + Hex Dump + Volcado hexadecimal + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + Raw + + + Save as… + Guardar como… + + + Back + Atrás + + + Packet %1. + Paquete %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + + + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + + + + + + %Ln turn(s). + + + + + + + Click to select. + Clic para seleccionar. + + + Regex Find: + + + + No capture file. + + + + Please make sure you have a capture file opened. + + + + Error following stream. + + + + Capture file invalid. + + + + Please make sure you have a %1 packet selected. + + + + %1 stream not found on the selected packet. + + + + Entire conversation (%1) + Conversación completa (%1) + + + Follow %1 Stream (%2) + Seguir secuencia %1 (%2) + + + Error creating filter for this stream. + + + + Save Stream Content As… + Guardar contenido de secuencia como… + + + [Stream output truncated] + + + + %Ln total stream(s). + + + + + + + Max sub stream ID for the selected stream: %Ln + + + + + + + File closed. + + + + Follow Stream + + + + Hint. + Hint. + + + Show data as + Show and save data as + Mostrar datos como + + + Stream + Secuencia + + + Substream + Subsecuencia + + + Find: + Buscar: + + + Find &Next + Buscar &siguiente + + + + FontColorPreferencesFrame + + Frame + Frame + + + Main window font: + Fuente de ventana principal: + + + Select Font + Seleccionar fuente + + + Colors: + Colores: + + + System Default + Predeterminado de sistema + + + Solid + Sólido + + + Sample ignored packet text + Ejemplo de texto de paquete ignorado + + + Sample marked packet text + Ejemplo de texto de paquete marcado + + + Sample active selected item + Ejemplo de ítem seleccionado activo + + + Style: + Estilo: + + + Gradient + Degradado + + + Sample inactive selected item + Ejemplo de ítem seleccionado inactivo + + + Sample "Follow Stream" client text + Ejemplo de texto de cliente "Seguir secuencia" + + + Sample "Follow Stream" server text + Ejemplo de texto de servidor "Seguir secuencia" + + + Sample valid filter + Ejemplo de filtro válido + + + Sample invalid filter + Ejemplo de filtro inválido + + + Sample warning filter + Sample deprecated filter + Ejemplo de filtro de advertencia + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + Ejemplo de paquetes de consulta GIF tiene tamaños de ventana jumbo + + + Lazy badgers move unique waxy jellyfish packets + Los tejones perezosos mueven únicamente paquetes de medusas de cera + + + Font + Fuente + + + + FunnelStringDialog + + Dialog + Dialog + + + + FunnelTextDialog + + Dialog + Dialog + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + + + + Highlight: + + + + + GsmMapSummaryDialog + + Dialog + Dialog + + + GSM MAP Summary + Informe GSM MAP + + + File + Archivo + + + Name + Nombre + + + Length + Longitud + + + Format + Formato + + + Snapshot length + Longitud de instantánea + + + Data + Datos + + + First packet + Primer paquete + + + Last packet + Último paquete + + + Elapsed + Transcurrido + + + Packets + Paquetes + + + Invokes + Invocaciones + + + Total number of Invokes + Número total de invocaciones + + + Average number of Invokes per second + Número promedio de invocaciones por segundo + + + Total number of bytes for Invokes + Número total de bytes por invocaciones + + + Average number of bytes per Invoke + Número promedio de bytes por invocación + + + Return Results + Resultados devueltos + + + Total number of Return Results + Número total de resultados devueltos + + + Average number of Return Results per second + Número promedio de resultados devueltos por segundo + + + Total number of bytes for Return Results + Número total de bytes por resultados devueltos + + + Average number of bytes per Return Result + Número promedio de bytes por resultado devuelto + + + Totals + Totales + + + Total number of GSM MAP messages + Número total de mensajes GSM MAP + + + Average number of GSM MAP messages per second + Número promedio de mensajes GSM MAP por segundo + + + Total number of bytes for GSM MAP messages + Número total de bytes por mensajes GSM MAP + + + Average number of bytes per GSM MAP message + Número promedio de bytes por mensaje GSM MAP + + + + IOConsoleDialog + + Dialog + Dialog + + + Enter code + + + + Evaluate + + + + Clear + + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Atajos de teclado valiosos y sorprendentes para ahorrar tiempo</h3> +<table><tbody> + +<tr><th>+</th><td>Aumentar zoom</td></th> +<tr><th>-</th><td>Reducir zoom</td></th> +<tr><th>x</th><td>Aumentar zoom en eje X</td></th> +<tr><th>X</th><td>Reducir zoom en eje X</td></th> +<tr><th>y</th><td>Aumentar zoom en eje Y</td></th> +<tr><th>Y</th><td>Reducir zoom en eje Y</td></th> +<tr><th>0</th><td>Restablecer gráfica al estado inicial</td></th> + +<tr><th>→</th><td>Mover a la derecha 10 píxeles</td></th> +<tr><th>←</th><td>Mover a la izquierda 10 píxeles</td></th> +<tr><th>↑</th><td>Mover arriba 10 píxeles</td></th> +<tr><th>↓</th><td>Mover abajo 10 píxeles</td></th> +<tr><th><i>Shift+</i>→</th><td>Mover a la derecha 1 píxel</td></th> +<tr><th><i>Shift+</i>←</th><td>Mover a la izquierda 1 píxel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Mover arriba 1 píxel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Mover abajo 1 píxel</td></th> + +<tr><th>g</th><td>Ir a paquete bajo cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Espacio</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + Elimina esta gráfica. + + + Add a new graph. + Añade una nueva gráfica. + + + Duplicate this graph. + Duplica esta gráfica. + + + Clear all graphs. + Vacía todas las gráficas. + + + Move this graph upwards. + + + + Move this graph downwards. + + + + Mouse + Ratón + + + Drag using the mouse button. + Arrastra usando el botón de ratón. + + + drags + arrastrar + + + Select using the mouse button. + Selecciona usando el botón de ratón. + + + zooms + zoom + + + Interval + Intervalo + + + Time of day + Hora de día + + + Log scale + Escala logarítmica + + + Automatic update + + + + Enable legend + + + + Reset + Restablecer + + + Reset Graph + Restablecer gráfica + + + Reset the graph to its initial state. + Restablece la gráfica al estado inicial. + + + 0 + 0 + + + Zoom In + Aumentar zoom + + + + + + + + + Zoom Out + Reducir zoom + + + - + - + + + Move Up 10 Pixels + Mover arriba 10 píxeles + + + Up + + + + Move Left 10 Pixels + Mover a la izquierda 10 píxeles + + + Left + + + + Move Right 10 Pixels + Mover a la derecha 10 píxeles + + + Right + + + + Move Down 10 Pixels + Mover abajo 10 píxeles + + + Down + + + + Move Up 1 Pixel + Mover arriba 1 píxel + + + Shift+Up + + + + Move Left 1 Pixel + Mover a la izquierda 1 píxel + + + Shift+Left + + + + Move Right 1 Pixel + Mover a la derecha 1 píxel + + + Shift+Right + + + + Move Down 1 Pixel + Mover abajo 1 píxel + + + Move down 1 Pixel + Move down 1 pixel + + + + Shift+Down + + + + Go To Packet Under Cursor + Ir a paquete bajo cursor + + + Go to packet currently under the cursor + + + + G + G + + + Drag / Zoom + Arrastrar / Zoom + + + Toggle mouse drag / zoom behavior + + + + Z + Z + + + Capture / Session Time Origin + Captura / Tiempo original de sesión + + + Toggle capture / session time origin + + + + T + T + + + Crosshairs + Retícula + + + Toggle crosshairs + + + + Space + Espacio + + + Zoom In X Axis + Aumentar zoom en eje X + + + X + X + + + Zoom Out X Axis + Reducir zoom en eje X + + + Shift+X + + + + Zoom In Y Axis + Aumentar zoom en eje Y + + + Y + Y + + + Zoom Out Y Axis + Reducir zoom en eje Y + + + Shift+Y + + + + 1 sec + 1 seg + + + 10 sec + 10 seg + + + 1 min + 1 min + + + 10 min + 10 min + + + Time (s) + Intervalo (s) + + + I/O Graphs + Gráficas de E/S + + + Save As… + Guardar como… + + + Copy + Copiar + + + Copy graphs from another profile. + Copia gráficas desde otro perfil. + + + 1 ms + 1 ms + + + 2 ms + 2 ms + + + 5 ms + 5 ms + + + 10 ms + 10 ms + + + 20 ms + 20 ms + + + 50 ms + 50 ms + + + 100 ms + 100 ms + + + 200 ms + 200 ms + + + 500 ms + 500 ms + + + 2 sec + 2 sec + + + 5 sec + 5 sec + + + Wireshark I/O Graphs: %1 + Gráficas E/S de Wireshark: %1 + + + Filtered packets + Paquetes filtrados + + + Filtered events + + + + All Packets + Todos los paquetes + + + TCP Errors + Errores TCP + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + Desplace sobre la gráfica para detalles. + + + No packets in interval + No hay paquetes en intervalo + + + No events in interval + + + + Click to select packet + Clic para seleccionar paquete + + + Packet + Paquete + + + Click to select event + + + + Event + Evento + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Soltar para zoom, x = %1 a %2, y = %3 a %4 + + + Unable to select range. + No puede seleccionar rango. + + + Click to select a portion of the graph. + Clic para seleccionar una parte de la gráfica. + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Portable Network Graphics (*.png) + Gráficos de red portable (*.png) + + + Windows Bitmap (*.bmp) + Mapa de bits de Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Formato de intercambio de archivo JPEG (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + Valores separados por coma (*.csv) + + + Save Graph As… + Guardar gráfica como… + + + + Iax2AnalysisDialog + + Dialog + Dialog + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Envío</span></p><p><span style=" font-size:medium; font-weight:600;">Retorno</span></p></body></html> + + + Forward + Envío + + + Packet + Paquete + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter (ms) + + + Bandwidth + Ancho de banda + + + Status + Estado + + + Length + Longitud + + + Reverse + Retorno + + + Graph + Gráfica + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>Muestra o oculta los valores de jitter de envío.</p></body></html> + + + Forward Jitter + Jitter de envío + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>Muestra o oculta los valores de diferencia de envío.</p></body></html> + + + Forward Difference + Diferencia de envío + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>Muestra o oculta los valores de jitter de retorno.</p></body></html> + + + Reverse Jitter + Jitter de retorno + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>Muestra o oculta los valores de diferencia de retorno.</p></body></html> + + + Reverse Difference + Diferencia de retorno + + + <small><i>A hint.</i></small> + <small><i>A hint.</i></small> + + + Audio + Audio + + + Save the audio data for both channels. + + + + Forward Stream Audio + Secuencia de envío de audio + + + Save the forward stream audio data. + + + + Reverse Stream Audio + Secuencia de retorno de audio + + + Save the reverse stream audio data. + + + + CSV + CSV + + + Save both tables as CSV. + + + + Forward Stream CSV + Secuencia de envío a CSV + + + Save the forward table as CSV. + + + + Reverse Stream CSV + Secuencia de retorno a CSV + + + Save the reverse table as CSV. + + + + Save Graph + Guardar gráfica + + + Save the graph image. + + + + Go to Packet + Ir a paquete + + + Select the corresponding packet in the packet list. + + + + G + G + + + Next Problem Packet + Paquete de problema siguiente + + + Go to the next problem packet + + + + N + N + + + IAX2 Stream Analysis + Análisis de secuencia IAX2 + + + Unable to save RTP data. + + + + Please select an IAX2 packet. + Por favor seleccione un paquete IAX2. + + + G: Go to packet, N: Next problem packet + G: Ir a paquete, N: Paquete de problema siguiente + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Portable Network Graphics (*.png) + Gráficos de red portable (*.png) + + + Windows Bitmap (*.bmp) + Mapa de bits de Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Formato de intercambio de archivo JPEG (*.jpeg *.jpg) + + + Save Graph As… + Guardar gráfica como… + + + Can't save in a file: Wrong length of captured packets. + + + + Can't save in a file: File I/O problem. + + + + Save forward stream audio + Guardar secuencia de envío a audio + + + Save reverse stream audio + Guardar secuencia de retorno a audio + + + Save audio + Guardar audio + + + Sun Audio (*.au) + Sun Audio (*.au) + + + ;;Raw (*.raw) + ;;Raw (*.raw) + + + Warning + Advertencia + + + Unable to save in that format + + + + Unable to save %1 + No puede guardar %1 + + + Saving %1… + + + + Analyzing IAX2 + + + + Save forward stream CSV + Guardar secuencia de envío a CSV + + + Save reverse stream CSV + Guardar secuencia de retorno a CSV + + + Save CSV + Guardar CSV + + + Comma-separated values (*.csv) + + + + + ImportTextDialog + + File: + Archivo: + + + Set name of text file to import + Establece el nombre de archivo de texto para importar + + + Browse for text file to import + Busque el archivo de texto para importar + + + Browse… + Browse... + Explorar… + + + Hex Dump + Volcado hexadecimal + + + Import a standard hex dump as exported by Wireshark + Importa un volcado hexadecimal estándar como exportado por Wireshark + + + Offsets in the text file are in octal notation + Los intervalos en el archivo de texto están en notación octal + + + Octal + Octal + + + Offsets: + Intervalos: + + + Offsets in the text file are in hexadecimal notation + Los intervalos en el archivo de texto están en notación hexadecimal + + + Hexadecimal + Hexadecimal + + + Offsets in the text file are in decimal notation + Los intervalos en el archivo de texto están en notación decimal + + + Decimal + Decimal + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>Si debe realizar un procesado adicional para detectar el inicio de la representación ASCII al final de una línea hexadecimal+ASCII incluso si parecen bytes hexadecimales.</p><p>No active si el volcado hexadecimal no contiene ASCII.</p></body></html> + + + ASCII identification: + Identificación ASCII: + + + Regular Expression + Expresión regular + + + Import a file formatted according to a custom regular expression + Importa un archivo formateado según una expresión regular personalizada + + + Packet format regular expression + Expresión regular de formato de paquete + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + + + + This is regexHintLabel, it will be set to default_regex_hint + This is regexHintLabel, it will be set to default_regex_hint + + + Data encoding: + Codificación de datos: + + + How data is encoded + Como están codificados los datos + + + encodingRegexExample + encodingRegexExample + + + List of characters indicating incoming packets + Lista de caracteres que indican paquetes entrantes + + + iI< + iI< + + + List of characters indicating outgoing packets + Lista de caracteres que indican paquetes salientes + + + oO> + oO> + + + Timestamp format: + Formato de hora: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Si el archivo contiene o no información indicando la dirección (entrada o salida) del paquete. + + + Direction indication: + Indicación de dirección: + + + ExportPDU + + + + IP version: + Versión IP: + + + Interface name: + + + + The name of the interface to write to the import capture file + + + + Fake IF, Import from Hex Dump + + + + Maximum frame length: + Tamaño máximo de trama: + + + Encapsulation + Encapsulado + + + The text file has no offset + El archivo de texto no tiene intervalo + + + None + Ninguno + + + <small><i>recommended regex:</small></i> + <small><i>expresión regular recomendada:</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + El formato en el que analizar horas en el archivo de texto (e.j. %H:%M:%S.). Los especificadores de formato están basados en strptime(3) + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + + + + %H:%M:%S.%f + %H:%M:%S.%f + + + timestampExampleLabel + timestampExampleLabel + + + Encapsulation Type: + Tipo de encapsulado: + + + Encapsulation type of the frames in the import capture file + Tipo de encapsulado de las tramas en el archivo de importación de captura + + + Prefix each frame with an Ethernet and IP header + Prefijo para cada trama con una cabecera ethernet y IP + + + IP + + + + Prefix each frame with an Ethernet, IP and UDP header + Prefijo para cada trama con una cabecera ethernet, IP y UDP + + + Prefix each frame with an Ethernet, IP and TCP header + Prefijo para cada trama con una cabecera ethernet, IP y TCP + + + Prefix each frame with an Ethernet, IP and SCTP header + Prefijo para cada trama con una cabecera ethernet, IP y SCTP + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + Prefijo para cada trama con una cabecera ethernet, IP y SCTP (Datos) + + + Source address: + + + + Destination address: + + + + Dissector + + + + The IP protocol ID for each frame + + + + The IP source address for each frame + La dirección IP de origen para cada trama + + + The IP destination address for each frame + La dirección IP de destino para cada trama + + + The UDP, TCP or SCTP source port for each frame + El puerto de origen UDP, TCP o SCTP para cada trama + + + The SCTP DATA payload protocol identifier for each frame + + + + The UDP, TCP or SCTP destination port for each frame + El puerto de destino UDP, TCP o SCTP para cada trama + + + Prefix each frame with an Ethernet header + Prefijo para cada trama con una cabecera ethernet + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + Protocolo (dec): + + + Leave frames unchanged + Dejar tramas igual + + + No dummy header + Sin cabecera simulada + + + Tag: + Etiqueta: + + + UDP + UDP + + + Source port: + Puerto de origen: + + + The Ethertype value of each frame + El valor Ethertype para cada trama + + + TCP + TCP + + + The SCTP verification tag for each frame + La etiqueta de verificación SCTP para cada trama + + + Destination port: + Puerto de destino: + + + Ethertype (hex): + Ethertype (hex): + + + SCTP (Data) + SCTP (Datos) + + + The dissector to use for each frame + + + + The IP Version to use for the dummy IP header + + + + The maximum size of the frames to write to the import capture file (max 256kiB) + El tamaño máximo de trama para escribir en el archivo de importación de captura (máx 256kiB) + + + Supported fields are data, dir, time, seqno + Los campos soportados son data, dir, time, seqno + + + Missing capturing group data (use (? + Falta captura de datos de grupo (use (? + + + Import From Hex Dump + Importar desde volcado hexadecimal + + + Import + Importar + + + Import Text File + Importe archivo de texto + + + + InterfaceFrame + + Frame + Frame + + + Wired + Cableado + + + AirPCAP + AirPCAP + + + Pipe + + + + STDIN + STDIN + + + Bluetooth + Bluetooth + + + Wireless + Wireless + + + Dial-Up + + + + USB + USB + + + External Capture + Captura externa + + + Virtual + + + + Remote interfaces + + + + Show hidden interfaces + + + + External capture interfaces disabled. + + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + + + + You don't have permission to capture on local interfaces. + + + + No interfaces found. + + + + Interfaces not loaded (due to preference). Go to Capture + + + + Start capture + Iniciar captura + + + Hide Interface + Ocultar interfaz + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + No hay interfaces para ser mostrados. %1 interfaces ocultos. + + + + InterfaceToolbar + + Frame + Frame + + + Select interface + + + + Interface + Interfaz + + + + InterfaceToolbarLineEdit + + Apply changes + + + + + InterfaceTreeModel + + Show + Mostrar + + + Friendly Name + Nombre descriptivo + + + Interface Name + Nombre de interfaz + + + No interfaces found. + + + + This version of Wireshark was built without packet capture support. + + + + Local Pipe Path + Ruta de tubería local + + + Comment + Comentario + + + Link-Layer Header + + + + Promiscuous + Promiscuo + + + Snaplen (B) + Longitud de instantánea (B) + + + Buffer (MB) + Buffer (MB) + + + Monitor Mode + Modo monitor + + + Capture Filter + Filtro de captura + + + Addresses + Direcciones + + + Address + Dirección + + + Extcap interface: %1 + Interfaz extcap: %1 + + + No addresses + No hay direcciones + + + No capture filter + No hay filtro de captura + + + Capture filter + Filtro de captura + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + + + + Sources + Fuentes + + + Address/Transport + + + + Data frames + Tramas de datos + + + Data bytes + Bytes de datos + + + Data frames/bytes + + + + Data rate + Tasa de datos + + + RX data frames + Tramas de datos RX + + + RX data bytes + Bytes de datos RX + + + RX data frames/bytes + + + + RX data rate + Tasa de datos RX + + + NCF frames + Tramas NCF + + + NCF count + Recuento NCF + + + NCF bytes + Bytes NCF + + + NCF frames/bytes + + + + NCF count/bytes + + + + NCF frames/count + + + + NCF frames/count/bytes + + + + NCF rate + Tasa NCF + + + SM frames + Tramas SM + + + SM bytes + Bytes SM + + + SM frames/bytes + + + + SM rate + Tasa SM + + + Show + Mostrar + + + Data + Datos + + + RX Data + Datos RX + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + números de secuencia para transporte + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Recuento + + + Frame + Trama + + + SQN/Reason + + + + Receivers + Destinos + + + NAK frames + Tramas NAK + + + NAK count + Recuento NAK + + + NAK bytes + Bytes NAK + + + NAK rate + Tasa NAK + + + NAK sequence numbers for transport + Números de secuencia NAK para transporte + + + Display filter: + Filtro de visualización: + + + Regenerate statistics using this display filter + Regenera estadísticas usando este filtro de visualización + + + Apply + Aplicar + + + Copy as CSV + Copiar como CSV + + + Copy the tree as CSV + + + + Copy as YAML + Copiar como YAML + + + Copy the tree as YAML + + + + Show the data frames column + + + + Show the data bytes column + + + + Show the data frames/bytes column + + + + Show the RX data frames column + + + + Show the RX data bytes column + + + + Show the RX data frames/bytes column + + + + Show the NCF frames column + + + + Show the NCF bytes column + + + + Show the NCF count column + + + + Show the data rate column + + + + Show the RX data rate column + + + + Show the NCF frames/bytes column + + + + Show the NCF count/bytes column + + + + Show the NCF frames/count column + + + + Show the NCF frames/count/bytes column + + + + Show the NCF rate column + + + + Show the SM frames column + + + + Show the SM bytes column + + + + Show the SM frames/bytes column + + + + Show the SM rate column + + + + Auto-resize columns to content + Cambiar automáticamente tamaño de columna al contenido + + + Resize columns to content size + + + + LBT-RM Statistics failed to attach to tap + + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + + + + Sources + Fuentes + + + Address/Transport/Client + + + + Data frames + Tramas de datos + + + Data bytes + Bytes de datos + + + Data frames/bytes + + + + Data rate + Tasa de datos + + + RX data frames + Tramas de datos RX + + + RX data bytes + Bytes de datos RX + + + RX data frames/bytes + + + + RX data rate + Tasa de datos RX + + + NCF frames + Tramas NCF + + + NCF count + Recuento NCF + + + NCF bytes + Bytes NCF + + + NCF frames/count + + + + NCF frames/bytes + + + + NCF count/bytes + + + + NCF frames/count/bytes + + + + NCF rate + Tasa NCF + + + SM frames + Tramas SM + + + SM bytes + Bytes SM + + + SM frames/bytes + + + + SM rate + Tasa SM + + + RST frames + Tramas RST + + + RST bytes + Bytes RST + + + RST frames/bytes + + + + RST rate + Tasa RST + + + Show + Mostrar + + + Data SQN + + + + RX Data SQN + + + + NCF SQN + + + + SM SQN + + + + RST reason + + + + details for transport + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Recuento + + + Frame + Trama + + + Reason + Causa + + + SQN/Reason + + + + Receivers + Destinos + + + Address/Transport + + + + NAK frames + Tramas NAK + + + NAK count + Recuento NAK + + + NAK bytes + Bytes NAK + + + NAK frames/count + + + + NAK count/bytes + + + + NAK frames/bytes + + + + NAK frames/count/bytes + + + + NAK rate + Tasa NAK + + + ACK frames + Tramas ACK + + + ACK bytes + Bytes ACK + + + ACK frames/bytes + + + + ACK rate + Tasa ACK + + + CREQ frames + + + + CREQ bytes + + + + CREQ frames/bytes + + + + CREQ rate + + + + NAK SQN + + + + ACK SQN + + + + CREQ request + + + + Display filter: + Filtro de visualización: + + + Regenerate statistics using this display filter + Regenera estadísticas usando este filtro de visualización + + + Apply + Aplicar + + + Copy as CSV + Copiar como CSV + + + Copy the tree as CSV + + + + Copy as YAML + Copiar como YAML + + + Copy the tree as YAML + + + + Show the data frames column + + + + Show the data bytes column + + + + Show the data frames/bytes column + + + + Show the data rate column + + + + Show the RX data frames column + + + + Show the RX data bytes column + + + + Show the RX data frames/bytes column + + + + Show the RX data rate column + + + + Show the NCF frames column + + + + Show the NCF count column + + + + Show the NCF bytes column + + + + Show the NCF frames/bytes column + + + + Show the NCF count/bytes column + + + + Show the NCF frames/count column + + + + Show the NCF frames/count/bytes column + + + + Show the SM frames column + + + + Show the SM bytes column + + + + Show the SM frames/bytes column + + + + Show the SM rate column + + + + Show the RST frames column + + + + Show the RST bytes column + + + + Show the RST frames/bytes column + + + + Show the RST rate column + + + + Show the NAK frames column + + + + Show the NAK count column + + + + Show the NAK bytes column + + + + Show the NAK frames/count column + + + + Show the NAK count/bytes column + + + + Show the NAK frames/bytes column + + + + Show the NAK frames/count/bytes column + + + + Show the NAK rate column + + + + Show the ACK frames column + + + + Show the ACK bytes column + + + + Show the ACK frames/bytes column + + + + Show the ACK rate column + + + + Show the CREQ frames column + + + + Show the CREQ bytes column + + + + Show the CREQ frames/bytes column + + + + Show the CREQ rate column + + + + Auto-resize columns to content + Cambiar automáticamente tamaño de columna al contenido + + + Resize columns to content size + + + + Show the NCF rate column + + + + LBT-RU Statistics failed to attach to tap + + + + + LBMStreamDialog + + Dialog + Dialog + + + Stream + Secuencia + + + Endpoint A + + + + Endpoint B + + + + Messages + + + + Bytes + Bytes + + + First Frame + + + + Last Frame + + + + Display filter: + Filtro de visualización: + + + Regenerate statistics using this display filter + Regenera estadísticas usando este filtro de visualización + + + Apply + Aplicar + + + Copy as CSV + Copiar como CSV + + + Copy the tree as CSV + + + + Copy as YAML + Copiar como YAML + + + Copy the tree as YAML + + + + LBM Stream failed to attach to tap + + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + LayoutPreferencesFrame + + Frame + Frame + + + Pane 1: + Panel 1: + + + Packet List + Listado de paquetes + + + Packet Details + Detalles de paquete + + + Packet Bytes + Bytes de paquete + + + Packet Diagram + Diagrama de paquete + + + None + Ninguno + + + Pane 2: + Panel 2: + + + Pane 3: + Panel 3: + + + Packet List settings: + Ajustes de listado de paquetes: + + + Show packet separator + Mostrar separador de paquete + + + Show column definition in column context menu + Mostrar definición de columna en menú contextual de columna + + + Allow the list to be sorted + + + + Maximum number of cached rows (affects sorting) + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + Enable mouse-over colorization + Activar coloración mouse-over + + + Status Bar settings: + Ajustes de barra de estado: + + + Show selected packet number + Mostrar número de paquete seleccionado + + + Show file load time + Mostrar tiempo de carga de archivo + + + + LteMacStatisticsDialog + + LTE Mac Statistics + + + + Include SR frames in filter + Incluir tramas SR en filtro + + + Include RACH frames in filter + Incluir tramas RACH en filtro + + + MAC Statistics + Estadísticas MAC + + + + LteRlcGraphDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + + Mouse + Ratón + + + Drag using the mouse button. + Arrastra usando el botón de ratón. + + + drags + arrastrar + + + Select using the mouse button. + Selecciona usando el botón de ratón. + + + zooms + zoom + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + + + + Reset + Restablecer + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Cambia la dirección de la conexión (ver el flujo opuesto).</p></body></html> + + + Switch Direction + Cambiar dirección + + + Reset Graph + Restablecer gráfica + + + Reset the graph to its initial state. + Restablece la gráfica al estado inicial. + + + 0 + 0 + + + Zoom In + Aumentar zoom + + + + + + + + + Zoom Out + Reducir zoom + + + - + - + + + Move Up 10 Pixels + Mover arriba 10 píxeles + + + Up + + + + Move Left 10 Pixels + Mover a la izquierda 10 píxeles + + + Left + + + + Move Right 10 Pixels + Mover a la derecha 10 píxeles + + + Right + + + + Move Down 10 Pixels + Mover abajo 10 píxeles + + + Down + + + + Move Up 1 Pixel + Mover arriba 1 píxel + + + Shift+Up + + + + Move Left 1 Pixel + Mover a la izquierda 1 píxel + + + Shift+Left + + + + Move Right 1 Pixel + Mover a la derecha 1 píxel + + + Shift+Right + + + + Move Down 1 Pixel + Mover abajo 1 píxel + + + Move down 1 Pixel + + + + Shift+Down + + + + Drag / Zoom + Arrastrar / Zoom + + + Toggle mouse drag / zoom behavior + + + + Z + Z + + + Crosshairs + Retícula + + + Toggle crosshairs + + + + Space + Espacio + + + Move Up 100 Pixels + Mover arriba 10 píxeles {100 ?} + + + PgUp + + + + PgDown + + + + Go To Packet Under Cursor + Ir a paquete bajo cursor + + + Go to packet currently under the cursor + + + + G + G + + + Zoom In X Axis + Aumentar zoom en eje X + + + X + X + + + Zoom Out Y Axis + Reducir zoom en eje Y + + + Shift+Y + + + + Zoom In Y Axis + Aumentar zoom en eje Y + + + Y + Y + + + Zoom Out X Axis + Reducir zoom en eje X + + + Shift+X + + + + Switch direction (swap between UL and DL) + + + + D + D + + + Time + Intervalo + + + Sequence Number + + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + + + + LTE RLC Graph - no channel selected + + + + Save As… + Guardar como… + + + %1 %2 (%3s seq %4 len %5) + + + + Click to select packet + Clic para seleccionar paquete + + + Packet + Paquete + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Soltar para zoom, x = %1 a %2, y = %3 a %4 + + + Unable to select range. + No puede seleccionar rango. + + + Click to select a portion of the graph. + Clic para seleccionar una parte de la gráfica. + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Portable Network Graphics (*.png) + Gráficos de red portable (*.png) + + + Windows Bitmap (*.bmp) + Mapa de bits de Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Formato de intercambio de archivo JPEG (*.jpeg *.jpg) + + + Save Graph As… + Guardar gráfica como… + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + + + + Include SR frames in filter + Incluir tramas SR en filtro + + + Include RACH frames in filter + Incluir tramas RACH en filtro + + + Use RLC frames only from MAC frames + + + + UL Frames + Tramas UL + + + UL Bytes + Bytes UL + + + UL MB/s + MB/s UL + + + UL ACKs + + + + UL NACKs + + + + UL Missing + + + + DL Frames + Tramas DL + + + DL Bytes + Bytes DL + + + DL MB/s + MB/s DL + + + DL ACKs + + + + DL NACKs + + + + DL Missing + + + + RLC Statistics + Estadísticas RLC + + + + MainStatusBar + + Ready to load or capture + Preparado para cargar o capturar + + + Ready to load file + Preparado para cargar archivo + + + Open the Capture File Properties dialog + Abrir cuadro de diálogo Propiedades de archivo de captura + + + Profile: %1 + Perfil: %1 + + + Manage Profiles… + Administrar perfiles… + + + New… + Nuevo… + + + Edit… + Editar… + + + Import + Importar + + + Export + Exportar + + + Delete + Eliminar + + + Switch to + Cambiar a + + + is the highest expert information level + is the highest expert info level + es el nivel más alto de información especializada + + + ERROR + ERROR + + + WARNING + ADVERTENCIA + + + NOTE + NOTA + + + CHAT + CONVERSACIÓN + + + No expert information + No expert info + No hay información especializada + + + %Ln byte(s) + , %1 bytes + + + + + + + Byte %1 + Byte %1 + + + Bytes %1-%2 + Bytes %1-%2 + + + Selected Packet: %1 %2 + Paquete seleccionado: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + Paquetes: %1 %4 Mostrado: %2 (%3%) + + + %1 Selected: %2 (%3%) + %1 Seleccionado: %2 (%3%) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 Marcado: %2 (%3%) + + + %1 Dropped: %2 (%3%) + %1 Perdido: %2 (%3%) + + + %1 Ignored: %2 (%3%) + %1 Ignorado: %2 (%3%) + + + %1 Comments: %2 + %1 Comentarios: %2 + + + %1 Load time: %2:%3.%4 + %1 Tiempo de carga: %2:%3.%4 + + + No Packets + No hay paquetes + + + From Zip File... + + + + From Directory... + + + + Selected Personal Profile... + + + + All Personal Profiles... + + + + Packets: %1 + Paquetes: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + Frame + + + Checking this will save the size, position, and maximized state of the main window. + Marcando esto guardará el tamaño, posición, y estado maximizado de la ventana principal. + + + Remember main window size and placement + Recordar tamaño y ubicación de ventana principal + + + Open files in + Abrir archivos en + + + This folder: + Esta carpeta: + + + Browse… + Browse... + Explorar… + + + The most recently used folder + La carpeta usada más recientemente + + + Show up to + Mostrar hasta + + + filter entries + entradas de filtro + + + recent files + archivos recientes + + + Confirm unsaved capture files + Confirmar archivos de captura no guardados + + + Display autocompletion for filter text + Mostrar completado automático para texto de filtro + + + Main toolbar style: + Estilo de barra de herramientas principal: + + + Icons only + Solo iconos + + + Text only + Solo texto + + + Icons & Text + Iconos & texto + + + Window title + Título de ventana + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Título de ventana personalizado que se añade al título existente<br/>%F = ruta de archivo del archivo de captura<br/>%P = nombre de perfil<br/>%S = un separador condicional (&quot; - &quot;) que solo se muestra al rodearse por variables con valores o texto estático<br/>%V = información de versión</p></body></html> + + + Prepend window title + Prefijo de título de ventana + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Título de ventana personalizado que se antepone al título existente<br/>%F = ruta de archivo del archivo de captura<br/>%P = nombre de perfil<br/>%S = un separador condicional (&quot; - &quot;) que solo se muestra al rodearse por variables con valores o texto estático<br/>%V = información de versión</p></body></html> + + + Language: + Lenguaje: + + + Use system setting + Usar configuración del sistema + + + Open Files In + Abrir archivos en + + + + ManageInterfacesDialog + + Manage Interfaces + Administrar interfaces + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Haga clic en la casilla de selección para ocultar o mostrar una interfaz oculta.</p></body></html> + + + Local Interfaces + Interfaces locales + + + Show + Mostrar + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Añada una tubería para capturar o eliminar una tubería existente de la lista.</p></body></html> + + + Pipes + Tuberías + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Añadir una nueva tubería usando ajustes predeterminados.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Eliminar la tubería seleccionada de la lista.</p></body></html> + + + Remote Interfaces + Interfaces remotos + + + Host / Device URL + Equipo / URL de dispositivo + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + + + + Remote Settings + Ajustes de remoto + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Esta versión de Wireshark no guarda ajustes de tubería. + + + This version of Wireshark does not save remote settings. + Esta versión de Wireshark no guarda ajustes de remoto. + + + This version of Wireshark does not support remote interfaces. + + + + New Pipe + Nueva tubería + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + Selecciona todo + + + Copy + Copiar + + + Find + Buscar + + + Clear + + + + + ManufTableModel + + Address Block + + + + Short Name + Nombre corto + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + + + + + Mtp3SummaryDialog + + Dialog + Dialog + + + MTP3 Summary + Informe de MTP3 + + + File + Archivo + + + Name + Nombre + + + Length + Longitud + + + Format + Formato + + + Snapshot length + Longitud de instantánea + + + Data + Datos + + + First packet + Primer paquete + + + Last packet + Último paquete + + + Elapsed + Transcurrido + + + Packets + Paquetes + + + Service Indicator (SI) Totals + + + + SI + + + + MSUs + + + + MSUs/s + + + + Bytes + Bytes + + + Bytes/MSU + + + + Bytes/s + + + + Totals + Totales + + + Total MSUs + + + + Total Bytes + + + + Average Bytes/MSU + + + + Average Bytes/s + + + + + MulticastStatisticsDialog + + UDP Multicast Streams + Secuencias multicast de UDP + + + Source Address + + + + Source Port + + + + Destination Address + + + + Destination Port + + + + Packets + Paquetes + + + Packets/s + + + + Avg BW (bps) + + + + Max BW (bps) + + + + Max Burst + + + + Burst Alarms + + + + Max Buffers (B) + + + + Buffer Alarms + + + + Burst measurement interval (ms): + + + + Burst alarm threshold (packets): + + + + Buffer alarm threshold (B): + + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + + + + The burst interval must be between 1 and 1000. + + + + The burst alarm threshold isn't valid. + + + + The buffer alarm threshold isn't valid. + + + + The stream empty speed should be between 1 and 10000000. + + + + The total empty speed should be between 1 and 10000000. + + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + + + + + PacketCommentDialog + + Edit Packet Comment + + + + Add Packet Comment + + + + + PacketDiagram + + Packet diagram + + + + Show Field Values + + + + Save Diagram As… + + + + Copy as Raster Image + + + + …as SVG + + + + Portable Network Graphics (*.png) + Gráficos de red portable (*.png) + + + Windows Bitmap (*.bmp) + Mapa de bits de Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Formato de intercambio de archivo JPEG (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + + + + Save Graph As… + Guardar gráfica como… + + + + PacketDialog + + Dialog + Dialog + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + Mostrar bytes de paquete + + + Packet %1 + Paquete %1 + + + [%1 closed] + + + + Byte %1 + Byte %1 + + + Bytes %1-%2 + Bytes %1-%2 + + + %Ln byte(s) + + + + + + + + PacketFormatGroupBox + + GroupBox + GroupBox + + + Packet Format + Formato de paquete + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>Líneas de informe de paquete similar al listado de paquetes</p></body></html> + + + Summary line + Línea de informe + + + Include column headings + Incluir encabezados de columna + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>Detalles de paquete similar al árbol de protocolo</p></body></html> + + + Details: + Detalles: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>Exporta solo ítems de detalle de paquete de nivel superior</p></body></html> + + + All co&llapsed + Todo co&ntraído + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>Expande y contrae detalles de paquete como está mostrado actualmente.</p></body></html> + + + As displa&yed + Como mostra&do + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>Exporta todos los ítems de detalles de paquete</p></body></html> + + + All e&xpanded + Todo e&xpandido + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>Exporta un volcado hex de datos de paquete similar a la vista de bytes de paquete</p></body></html> + + + Bytes + Bytes + + + Include secondary data sources + + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + + + + + PacketList + + Protocol Preferences + Preferencias de protocolo + + + Summary as Text + + + + …as CSV + + + + …as YAML + + + + Decode As… + Decodificar como… + + + Frame %1: %2 + + + + + + [ Comment text exceeds %1. Stopping. ] + + + + + PacketListHeader + + Align Left + Alinear a la izquierda + + + Align Center + Alinear al centro + + + Align Right + Alinear a la derecha + + + Edit Column + Editar columna + + + Resize to Contents + Redimensionar al contenido + + + Column Preferences… + Preferencias de columna… + + + Resize Column to Width… + Redimensionar columna a anchura… + + + Resolve Names + Resolver nombres + + + Remove this Column + Borrar esta columna + + + Column %1 + + + + Width: + Anchura: + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + + + + Sorting "%1"… + + + + Sorting … + + + + + PacketRangeGroupBox + + Form + Form + + + Packet Range + Rango de paquete + + + - + - + + + Displayed + Mostrado + + + &Marked packets only + &Solo paquetes marcados + + + &Range: + &Rango: + + + Remove &ignored packets + Eliminar &paquetes ignorados + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + Primero &al último marcado + + + &All packets + &Todos los paquetes + + + &Selected packets only + &Solo paquetes seleccionados + + + Captured + Capturado + + + + PathSelectionDelegate + + Open a pipe + + + + + PathSelectionEdit + + Browse + + + + Select a path + + + + + PluginListModel + + Name + Nombre + + + Version + + + + Type + Tipo + + + Path + + + + + PortsModel + + All entries + + + + tcp + tcp + + + udp + udp + + + sctp + + + + dccp + + + + Name + Nombre + + + Port + + + + Type + Tipo + + + + PreferenceEditorFrame + + Frame + Frame + + + … + + + + a preference + + + + Browse… + Explorar… + + + Open %1 preferences… + Abrir preferencias de %1… + + + Invalid value. + + + + + PreferencesDialog + + Search: + Buscar: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + + + + + PrefsModel + + Advanced + Avanzado + + + Appearance + Apariencia + + + Layout + Diseño + + + Columns + Columnas + + + Font and Colors + Fuente y colores + + + Capture + Captura + + + Expert + Experto + + + Filter Buttons + Botones de filtro + + + RSA Keys + Claves RSA + + + + PrintDialog + + Packet Format + Formato de paquete + + + Print each packet on a new page + Imprimir cada paquete en una nueva página + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>Imprime información de archivo de captura en cada página</p></body></html> + + + Capture information header + Encabezado de información de captura + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>Use las teclas &quot;+&quot; y &quot;-&quot; para aumentar y reducir la vista previa. Use la tecla &quot;0&quot; para restablecer el nivel de zoom.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ y - zoom, 0 restablecer</span></p></body></html> + + + Packet Range + Rango de paquete + + + Print + Imprimir + + + &Print… + &Imprimir… + + + Page &Setup… + &Configurar página… + + + %1 %2 total packets, %3 shown + %1 %2 paquetes totales, %3 mostrados + + + Print Error + + + + Unable to print to %1. + + + + + ProfileDialog + + Search for profile … + Buscar perfil … + + + Create a new profile using default settings. + Crea un nuevo perfil usando los ajustes predeterminados. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>Elimina este perfil. Los perfiles proporcionados por el sistema no puede ser eliminados. El perfil predeterminado se restablecerá al eliminarlo.</p></body></html> + + + Copy this profile. + Copia este perfil. + + + Configuration Profiles + Configuración de perfiles + + + Import + noun + Importar + + + Export + noun + Exportar + + + From Zip File... + + + + From Directory... + + + + %Ln Selected Personal Profile(s)... + + + + + + + All Personal Profiles... + + + + New profile + Nuevo perfil + + + Profile Error + + + + Exporting profiles + Exportando perfiles + + + No profiles found for export + No se encontraron perfiles para exportar + + + Select zip file for export + Seleccione archivo zip para exportar + + + An import of profiles is not allowed, while changes are pending + + + + An import is pending to be saved. Additional imports are not allowed + Una importación está pendiente de ser guardada. No están permitidas importaciones adicionales + + + An export of profiles is only allowed for personal profiles + Una exportación de perfiles solo está permitida para perfiles personales + + + An export of profiles is not allowed, while changes are pending + + + + %Ln profile(s) exported + + + + + + + Select zip file for import + Seleccione archivo zip para importar + + + Select directory for import + Seleccione directorio para importar + + + Zip File (*.zip) + Archivo zip (*.zip) + + + Error + Error + + + An error has occurred while exporting profiles + + + + No profiles found for import in %1 + No se encontraron perfiles para importar en %1 + + + %Ln profile(s) imported + + + + + + + , %Ln profile(s) skipped + + + + + + + Importing profiles + + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + ProfileModel + + Resetting to default + Restablecer predeterminado + + + Imported profile + Perfil importado + + + This is a system provided profile + Este es un perfil proporcionado por el sistema + + + A profile change for this name is pending + + + + (See: %1) + + + + This is an invalid profile definition + + + + A profile already exists with this name + Ya existe un perfil con este nombre + + + A profile with this name is being deleted + + + + Created from default settings + Creado desde ajustes predeterminados + + + system provided + + + + deleted + + + + copy + noun + + + + Exporting profiles while changes are pending is not allowed + + + + No profiles found to export + + + + Can't delete profile directory + + + + A profile name cannot contain the following characters: %1 + + + + A profile name cannot contain the '/' character + El nombre de perfil no puede contener el carácter '/' + + + A profile cannot start or end with a period (.) + + + + Default + Predeterminado + + + Global + Global + + + Personal + Personal + + + Renamed from: %1 + + + + Copied from: %1 + Copiado de: %1 + + + renamed to %1 + + + + Profile + Perfil + + + Type + Tipo + + + + ProfileSortModel + + All profiles + Todos los perfiles + + + Personal profiles + Perfiles personales + + + Global profiles + Perfiles globales + + + + ProgressFrame + + Frame + Frame + + + Loading + Cargando + + + + ProtoTree + + Packet details + Detalles de paquete + + + Not a field or protocol + + + + No field reference available for text labels. + + + + Expand Subtrees + Expandir subárboles + + + Collapse Subtrees + Contraer subárboles + + + Expand All + Expandir todo + + + Collapse All + Contraer todo + + + Copy + Copiar + + + All Visible Items + Todos los ítems visibles + + + All Visible Selected Tree Items + Todos los ítems de árbol seleccionados + + + Description + Descripción + + + Field Name + Nombre de campo + + + Value + Valor + + + As Filter + Como filtro + + + Wiki Protocol Page + Página Wiki de protocolo + + + Filter Field Reference + + + + Copied + + + + Wiki Page for %1 + + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>La wiki de Wireshark está mantenida por la comunidad.</p><p>La página que está a punto de cargar puede ser maravillosa, incompleta, incorrecta, o inexistente.</p><p>¿Continuar con la wiki?</p> + + + Colorize with Filter + Colorear con filtro + + + + ProtocolHierarchyDialog + + Dialog + Dialog + + + Protocol + Protocolo + + + Percent Packets + Porcentaje de paquetes + + + Packets + Paquetes + + + Percent Bytes + Porcentaje de bytes + + + Bytes + Bytes + + + Bits/s + Bits/s + + + End Packets + + + + End Bytes + + + + End Bits/s + + + + PDUs + + + + <small><i>A hint.</i></small> + <small><i>A hint.</i></small> + + + Copy as CSV + Copiar como CSV + + + Copy stream list as CSV. + Copia el listado de secuencias como CSV. + + + Copy as YAML + Copiar como YAML + + + Copy stream list as YAML. + Copia el listado de secuencias como YAML. + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + Estadísticas de jerarquía de protocolo + + + Copy + Copiar + + + as CSV + como CSV + + + as YAML + como YAML + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + No hay filtro de visualización. + + + Display filter: %1 + Filtro de visualización: %1 + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + Preferencias de protocolo + + + No protocol preferences available + + + + Disable %1 + Desactivar %1 + + + %1 has no preferences + + + + Open %1 preferences… + Abrir preferencias de %1… + + + + QObject + + Average Throughput (bits/s) + + + + Round Trip Time (ms) + + + + Segment Length (B) + + + + Sequence Number (B) + + + + Time (s) + Intervalo (s) + + + Window Size (B) + + + + [no capture file] + [no hay archivo de captura] + + + Conversation + + + + Bars show the relative timeline for each conversation. + + + + Endpoint + + + + Apply as Filter + Aplicar como filtro + + + Prepare as Filter + Preparar como filtro + + + Find + Buscar + + + Colorize + Colorear + + + Look Up + + + + Copy + Copiar + + + UNKNOWN + + + + Selected + Seleccionado + + + Not Selected + No seleccionado + + + …and Selected + …y seleccionado + + + …or Selected + …O seleccionado + + + …and not Selected + …Y no seleccionado + + + …or not Selected + …O no seleccionado + + + A + A + + + B + B + + + Any + Cualquiera + + + Don't show this message again. + + + + Multiple problems found + + + + %1 (%L2%) + + + + No entries. + + + + %1 entries. + + + + Base station + + + + <Broadcast> + <Broadcast> + + + <Hidden> + <Hidden> + + + BSSID + BSSID + + + Beacons + + + + Data Pkts + + + + Protection + Protección + + + Address + Dirección + + + Pkts Sent + + + + Pkts Received + + + + Comment + Comentario + + + Wrong sequence number + + + + Payload changed to PT=%1 + + + + Incorrect timestamp + + + + Marker missing? + + + + C-RNTI + + + + SPS-RNTI + + + + RNTI + RNTI + + + Type + Tipo + + + UEId + UEId + + + UL Frames + Tramas UL + + + UL Bytes + Bytes UL + + + UL MB/s + MB/s UL + + + UL Padding % + + + + UL Re TX + + + + DL Frames + Tramas DL + + + DL Bytes + Bytes DL + + + DL MB/s + MB/s DL + + + DL Padding % + + + + DL CRC Failed + + + + DL ReTX + + + + LCID 1 + + + + LCID 2 + + + + LCID 3 + + + + LCID 4 + + + + LCID 5 + + + + LCID 6 + + + + LCID 7 + + + + LCID 8 + + + + LCID 9 + + + + LCID 10 + + + + LCID 32 + + + + LCID 33 + + + + LCID 34 + + + + LCID 35 + + + + LCID 36 + + + + LCID 37 + + + + LCID 38 + + + + TM + TM + + + UM + UM + + + AM + UM + + + Predef + + + + Unknown (%1) + + + + CCCH + CCCH + + + SRB-%1 + + + + DRB-%1 + + + + Unknown + + + + UE Id + UE Id + + + Name + Nombre + + + Mode + + + + Priority + + + + default + + + + DLT %1 + + + + Invalid Display Filter + + + + The filter expression %1 isn't a valid display filter. (%2). + + + + Error + Error + + + No remote interfaces found. + + + + PCAP not found + + + + Unknown error + + + + Default + Predeterminado + + + Changed + Modificado + + + Has this preference been changed? + ¿Se ha modificado esta preferencia? + + + Default value is empty + El valor predeterminado está vacío + + + Gap in dissection + + + + Edit… + Editar… + + + Browse… + Explorar… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + Interfaz remoto + + + Host: + Equipo: + + + Port: + Puerto: + + + Authentication + Autenticación + + + Null authentication + Autenticación nula + + + Password authentication + Autenticación de contraseña + + + Username: + Nombre de usuario: + + + Password: + Contraseña: + + + Clear list + + + + Error + Error + + + No remote interfaces found. + + + + PCAP not found + + + + + RemoteSettingsDialog + + Remote Capture Settings + Ajustes de captura remota + + + Capture Options + Opciones de captura + + + Do not capture own RPCAP traffic + No capturar nuestro tráfico RPCAP + + + Use UDP for data transfer + Usar UDP para transferir datos + + + Sampling Options + Opciones de muestreo + + + None + Ninguna + + + 1 of + 1 de + + + packets + paquetes + + + 1 every + 1 cada + + + milliseconds + milisegundos + + + + ResolvedAddressesDialog + + Dialog + Dialog + + + Hosts + Equipos + + + Search for entry (min 3 characters) + Buscar entrada (mín 3 caracteres) + + + Ports + Puertos + + + Search for port or name + Buscar puerto o nombre + + + Capture File Comments + Comentarios de archivo de captura + + + Comment + Comentario + + + Show the comment. + Muestra el comentario. + + + IPv4 Hash Table + Tabla hash de IPv4 + + + Show the IPv4 hash table entries. + Muestra las entradas de la tabla hash de IPv4. + + + IPv6 Hash Table + Tabla hash de IPv6 + + + Show the IPv6 hash table entries. + Muestra las entradas de la tabla hash de IPv6. + + + Show All + Mostrar todo + + + Show all address types. + Muestra todos los tipos de direcciones. + + + Hide All + Ocultar todo + + + Hide all address types. + Oculta todos los tipos de direcciones. + + + IPv4 and IPv6 Addresses (hosts) + Direcciones IPv4 y IPv6 (equipos) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + Muestra los nombres de equipos IPv4 y IPv6 resueltos en formato "equipos" . + + + Port names (services) + Nombres de puertos (servicios) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + Muestra los nombres de puertos resueltos en formato "servicios" . + + + Ethernet Addresses + Direcciones ethernet + + + Show resolved Ethernet addresses in "ethers" format. + Muestra las direcciones ethernet resueltas en formato "ethers" . + + + Ethernet Well-Known Addresses + Direcciones ethernet conocidas + + + Show well-known Ethernet addresses in "ethers" format. + Muestra las direcciones ethernet conocidas en formato "ethers" . + + + Ethernet Manufacturers + Fabricantes de ethernet + + + Show Ethernet manufacturers in "ethers" format. + Muestra los fabricantes de ethernet en formato "ethers" . + + + [no file] + [ningún archivo] + + + Resolved Addresses + Direcciones resueltas + + + # Resolved addresses found in %1 + # Direcciones resultas encontradas en %1 + + + # Comments +# +# + # Comentarios +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + + + + Type + Tipo + + + Messages + + + + Min SRT + + + + Max SRT + + + + Avg SRT + + + + Min in Frame + + + + Max in Frame + + + + Open Requests + + + + Discarded Responses + + + + Repeated Requests + + + + Repeated Responses + + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + + + + Version: + + + + Program: + + + + DCE-RPC Service Response Times + + + + ONC-RPC Service Response Times + + + + + RsaKeysFrame + + RSA Keys + Claves RSA + + + RSA private keys are loaded from a file or PKCS #11 token. + Las claves RSA privadas se cargan desde un archivo o identificador PKCS #11. + + + Add new keyfile… + Añadir nuevo archivo de clave… + + + Add new token… + Añadir nuevo identificador… + + + Remove key + Eliminar clave + + + PKCS #11 provider libraries. + Librerías de proveedores de PKCS #11. + + + Add new provider… + Añadir nuevo proveedor… + + + Remove provider + Eliminar proveedor + + + Add PKCS #11 token or key + Añadir identificador PKCS #11 o clave + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + Identificadores PKCS #11 o claves nuevas no encontradas, considere añadir un proveedor de PKCS #11. + + + Select a new PKCS #11 token or key + + + + PKCS #11 token or key + + + + Enter PIN or password for %1 (it will be stored unencrypted) + + + + Enter PIN or password for key + + + + Key could not be added: %1 + + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + Clave privada RSA (*.pem *.p12 *.pfx *.key);;Todos los archivos ( + + + Select RSA private key file + Seleccione archivo de clave privada RSA + + + Libraries (*.dll) + Librerías (*.dll) + + + Libraries (*.so) + Librerías (*.so) + + + Select PKCS #11 Provider Library + Seleccione la librería de proveedores de PKCS #11 + + + Changes will apply after a restart + Aplicar cambios después de reinicio + + + PKCS #11 provider %1 will be removed after the next restart. + El proveedor de PKCS #11 %1 se eliminará después del próximo reinicio. + + + + RtpAnalysisDialog + + Dialog + Dialog + + + Packet + Paquete + + + Sequence + + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter + Jitter (ms) + + + Skew + + + + Bandwidth + Ancho de banda + + + Marker + + + + Status + Estado + + + Stream %1 + + + + Stream %1 Jitter + + + + Stream %1 Difference + + + + Stream %1 Delta + + + + %1 streams, + + + + Save one stream CSV + + + + Save all stream's CSV + + + + &Analyze + &Analizar + + + Open the analysis window for the selected stream(s) + + + + &Set List + + + + &Add to List + + + + &Remove from List + + + + Replace existing list in RTP Analysis Dialog with new one + + + + Add new set to existing list in RTP Analysis Dialog + + + + Remove selected streams from list in RTP Analysis Dialog + + + + Graph + Gráfica + + + <small><i>A hint.</i></small> + <small><i>A hint.</i></small> + + + &Export + &Exportar + + + Open export menu + + + + CSV + CSV + + + Save tables as CSV. + + + + Current Tab Stream CSV + + + + Save the table on the current tab as CSV. + + + + All Tab Streams CSV + + + + Save the table from all tabs as CSV. + + + + Save Graph + Guardar gráfica + + + Save the graph image. + + + + Go to Packet + Ir a paquete + + + Select the corresponding packet in the packet list. + + + + G + G + + + Next Problem Packet + Paquete de problema siguiente + + + Go to the next problem packet + + + + N + N + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + + + + &Current Tab + + + + Prepare a filter matching current tab. + + + + &All Tabs + + + + Prepare a filter matching all tabs. + + + + RTP Stream Analysis + + + + Save Graph As… + Guardar gráfica como… + + + G: Go to packet, N: Next problem packet + G: Ir a paquete, N: Paquete de problema siguiente + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Portable Network Graphics (*.png) + Gráficos de red portable (*.png) + + + Windows Bitmap (*.bmp) + Mapa de bits de Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Formato de intercambio de archivo JPEG (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + + + + + RtpPlayerDialog + + RTP Player + Reproductor RTP + + + Play + + + + Source Address + + + + Source Port + + + + Destination Address + + + + Destination Port + + + + SSRC + + + + Setup Frame + + + + Packets + Paquetes + + + Time Span (s) + + + + Payloads + + + + <small><i>No audio</i></small> + + + + Start playback of all unmuted streams + + + + Pause/unpause playback + + + + Stop playback + + + + Enable/disable skipping of silence during playback + + + + Min silence: + + + + Minimum silence duration to skip in seconds + + + + Output Device: + Dispositivo de salida: + + + Output Audio Rate: + + + + Jitter Buffer: + + + + The simulated jitter buffer in milliseconds. + + + + Playback Timing: + + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + + + + Jitter Buffer + + + + RTP Timestamp + + + + Uninterrupted Mode + + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + + + + Time of Day + Hora de día + + + &Export + &Exportar + + + Export audio of all unmuted selected channels or export payload of one channel. + + + + From &cursor + Desde &cursor + + + Save audio data started at the cursor + + + + &Stream Synchronized Audio + + + + Save audio data synchronized to start of the earliest stream. + + + + &File Synchronized Audio + + + + Save audio data synchronized to start of the capture file. + + + + &Payload + + + + Save RTP payload of selected stream. + + + + Reset Graph + Restablecer gráfica + + + Reset the graph to its initial state. + Restablece la gráfica al estado inicial. + + + Go To Setup Packet + + + + Go to setup packet of stream currently under the cursor + + + + Mute + Silenciar + + + Mute selected streams + Silencia las secuencias seleccionadas + + + Unmute + + + + Unmute selected streams + + + + Invert muting of selected streams + + + + Route audio to left channel of selected streams + + + + Route audio to left and right channel of selected streams + + + + Route audio to right channel of selected streams + + + + Remove Streams + Eliminar secuencias + + + Remove selected streams from the list + Elimina secuencias seleccionadas de la lista + + + All + Todo + + + Select all + Selecciona todo + + + None + + + + Clear selection + + + + Invert + Invertir + + + Invert selection + + + + Play/Pause + Reproducir/Pausa + + + Start playing or pause playing + Inicia la reproducción o pausa la reproducción + + + Stop + Detener + + + Stop playing + Detiene la reproducción + + + I&naudible streams + Secuencias i&naudibles + + + Select/Deselect inaudible streams + Marca/Desmarca secuencias inaudibles + + + Inaudible streams + + + + &Select + &Marcar + + + Select inaudible streams + Marca secuencias inaudibles + + + &Deselect + &Desmarcar + + + Deselect inaudible streams + Desmarca secuencias inaudibles + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + + + + R&efresh streams + + + + Read captured packets from capture in progress to player + + + + Zoom In + Aumentar zoom + + + SR (Hz) + + + + Sample rate of codec + + + + PR (Hz) + + + + Play rate of decoded audio (depends e. g. on selected sound card) + + + + Zoom Out + Reducir zoom + + + Move Left 10 Pixels + Mover a la izquierda 10 píxeles + + + Move Right 10 Pixels + Mover a la derecha 10 píxeles + + + Move Left 1 Pixels + Mover a la izquierda 10 píxeles {1 ?} + + + Move Right 1 Pixels + Mover a la derecha 10 píxeles {1 ?} + + + Go To Packet Under Cursor + Ir a paquete bajo cursor + + + Go to packet currently under the cursor + + + + Play the stream + + + + To Left + + + + Left + Right + + + + To Right + + + + Invert Muting + + + + No devices available + + + + Select + + + + Audio Routing + + + + &Play Streams + + + + Open RTP player dialog + + + + &Set playlist + + + + Replace existing playlist in RTP Player with new one + + + + &Add to playlist + + + + Add new set to existing playlist in RTP Player + + + + &Remove from playlist + + + + Remove selected streams from playlist in RTP Player + + + + No Audio + + + + Decoding streams... + + + + Out of Sequence + + + + Jitter Drops + + + + Wrong Timestamps + + + + Inserted Silence + + + + Double click on cell to change audio routing + + + + %1 streams + + + + , %1 selected + + + + , %1 not muted + + + + , start: %1. Double click on graph to set start of playback. + + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + + + + Playback of stream %1 failed! + + + + Automatic + + + + WAV (*.wav) + + + + Sun Audio (*.au) + Sun Audio (*.au) + + + Save audio + Guardar audio + + + Raw (*.raw) + + + + Save payload + + + + Warning + Advertencia + + + No stream selected or none of selected streams provide audio + + + + Error + Error + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + + + + No streams are suitable for save + + + + Save failed! + + + + Can't write header of AU file + + + + Can't write header of WAV file + + + + Payload save works with just one audio stream. + + + + Double click to change audio routing + + + + Preparing to play... + + + + Unknown + + + + + RtpStreamDialog + + Dialog + Dialog + + + Source Address + + + + Source Port + + + + Destination Address + + + + Destination Port + + + + SSRC + + + + Start Time + + + + Duration + Duración + + + Payload + + + + Packets + Paquetes + + + Lost + + + + Max Delta (ms) + + + + Max Jitter + + + + Mean Jitter + + + + Status + Estado + + + <small><i>A hint.</i></small> + <small><i>A hint.</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Muestra solo conversaciones que coinciden con el filtro de visualización actual</p></body></html> + + + Limit to display filter + Limitar filtro de visualización + + + Time of Day + Hora de día + + + Find &Reverse + + + + Prepare &Filter + + + + &Export + &Exportar + + + &Analyze + &Analizar + + + Open the analysis window for the selected stream(s) and add it to it + + + + Find the reverse stream matching the selected forward stream. + + + + Min Delta (ms) + + + + Mean Delta (ms) + + + + Min Jitter + + + + All forward/reverse stream actions + + + + R + R + + + Find All &Pairs + + + + Select all streams which are paired in forward/reverse relation + + + + Shift+R + + + + Find Only &Singles + + + + Find all streams which don't have paired reverse stream + + + + Ctrl+R + + + + Mark Packets + + + + Mark the packets of the selected stream(s). + + + + M + M + + + All + + + + Select all + Selecciona todo + + + None + + + + Clear selection + + + + Invert + Invertir + + + Invert selection + + + + Go To Setup + + + + Go to the setup packet for this stream. + + + + G + G + + + Prepare a filter matching the selected stream(s). + + + + P + P + + + Export the stream payload as rtpdump + + + + E + E + + + A + A + + + Cop&y + Cop&iar + + + Open copy menu + Abre menú copiar + + + Copy as CSV + Copiar como CSV + + + Copy stream list as CSV. + Copia el listado de secuencias como CSV. + + + Copy as YAML + Copiar como YAML + + + Copy stream list as YAML. + Copia el listado de secuencias como YAML. + + + RTP Streams + Secuencias RTP + + + Select + + + + as CSV + como CSV + + + as YAML + como YAML + + + %1 streams + + + + , %1 selected, %2 total packets + + + + Save RTPDump As… + + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + + + + ID + + + + Port 1 + Puerto 1 + + + Port 2 + Puerto 2 + + + Number of Packets + Número de paquetes + + + Number of DATA Chunks + + + + Number of Bytes + Número de bytes + + + Filter Selected Association + + + + Analyze + Analizar + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + + + + TabWidget + + + + Statistics + Estadísticas + + + Chunk Statistics + + + + Filter Association + + + + Number of Data Chunks from EP2 to EP1: + + + + Checksum Type: + Tipo de suma de verificación: + + + Number of Data Chunks from EP1 to EP2: + + + + Number of Data Bytes from EP1 to EP2: + + + + Number of Data Bytes from EP2 to EP1: + + + + Endpoint 1 + Punto final 1 + + + Graph TSN + Gráfica de TSN + + + Graph Bytes + Gráfica de bytes + + + Requested Number of Inbound Streams: + + + + Port: + Puerto: + + + Sent Verification Tag: + + + + Minimum Number of Inbound Streams: + + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + + + + Minimum Number of Outbound Streams: + + + + Graph Arwnd + + + + Endpoint 2 + Punto final 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + + + + Provided Number of Outbound Streams: + + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + + + + No Association found for this packet. + + + + Warning + Advertencia + + + Could not find SCTP Association with id: %1 + + + + Complete list of IP addresses from INIT Chunk: + + + + Complete list of IP addresses from INIT_ACK Chunk: + + + + List of Used IP Addresses + Lista de direcciones IP usadas + + + Used Number of Inbound Streams: + + + + Used Number of Outbound Streams: + + + + + SCTPChunkStatisticsDialog + + Dialog + Dialog + + + Association + Asociación + + + Endpoint 1 + Punto final 1 + + + Endpoint 2 + Punto final 2 + + + Save Chunk Type Order + + + + Hide Chunk Type + Ocultar tipo de fragmento + + + Remove the chunk type from the table + Elimina el tipo de fragmento de la tabla + + + Chunk Type Preferences + Preferencias de tipo de fragmento + + + Go to the chunk type preferences dialog to show or hide other chunk types + + + + Show All Registered Chunk Types + Mostrar todos los tipos registrados de fragmentos + + + Show all chunk types with defined names + Muestra todos los tipos de fragmentos con nombres definidos + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + Estadísticas de fragmentos SCTP: %1 Puerto1 %2 Puerto2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + + + + Reset to full size + + + + Save Graph + Guardar gráfica + + + goToPacket + + + + Go to Packet + Ir a paquete + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + Arwnd + + + + time [secs] + + + + Advertised Receiver Window [Bytes] + + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + + + + + SCTPGraphByteDialog + + SCTP Graph + + + + Reset to full size + + + + Save Graph + Guardar gráfica + + + goToPacket + + + + Go to Packet + Ir a paquete + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + Bytes + Bytes + + + time [secs] + + + + Received Bytes + + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + + + + + SCTPGraphDialog + + SCTP Graph + + + + Relative TSNs + TSNs relativos + + + Only SACKs + Solo SACKs + + + Only TSNs + Solo TSNs + + + Show both + Mostrar ambos + + + Reset to full size + + + + Save Graph + Guardar gráfica + + + goToPacket + + + + Go to Packet + Ir a paquete + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + CumTSNAck + + + + Gap Ack + + + + NR Gap Ack + + + + Duplicate Ack + + + + TSN + + + + time [secs] + + + + TSNs + + + + <small><i>%1: %2 Time: %3 secs </i></small> + + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Portable Network Graphics (*.png) + Gráficos de red portable (*.png) + + + Windows Bitmap (*.bmp) + Mapa de bits de Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Formato de intercambio de archivo JPEG (*.jpeg *.jpg) + + + Save Graph As… + Guardar gráfica como… + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + + + + Command: + + + + SCSI Service Response Times + + + + + SearchFrame + + Frame + Frame + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>Busca en la columna de información de la lista de paquetes (panel de informe), etiquetas de visualización de paquete decodificado (panel de vista de árbol) o datos de paquete convertido en ASCII (panel de vista hexadecimal).</p></body></html> + + + Packet list + Listado de paquetes + + + Packet details + Detalles de paquete + + + Packet bytes + Bytes de paquete + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Busca cadenas que contienen caracteres reducidos (UTF-8 y ASCII) o ampliados (UTF-16).</p></body></html> + + + Narrow & Wide + Reducido & ampliado + + + Narrow (UTF-8 / ASCII) + Reducido (UTF-8 / ASCII) + + + Wide (UTF-16) + Ampliado (UTF-16) + + + Case sensitive + Mayúsculas y minúsculas + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>Busca datos usando sintaxis de filtro de visualización (e.j. ip.addr==10.1.1.1), una cadena hexadecimal (e.j. fffffda5), una cadena simple (e.j. mi cadena) o una expresión regular (e.j. colo?r).</p></body></html> + + + Display filter + Filtro de visualización + + + Hex value + Valor hexadecimal + + + String + Cadena + + + Regular Expression + Expresión regular + + + Find + Buscar + + + Cancel + Cancelar + + + No valid search type selected. Please report this to the development team. + + + + Invalid filter. + Filtro no valido. + + + That filter doesn't test anything. + Este filtro no comprueba nada. + + + That's not a valid hex string. + Esta no es una cadena hexadecimal valida. + + + You didn't specify any text for which to search. + No especificó ningún texto para buscar. + + + No valid character set selected. Please report this to the development team. + + + + No valid search area selected. Please report this to the development team. + + + + Searching for %1… + Buscando %1… + + + No packet contained those bytes. + Ningún paquete contiene estos bytes. + + + No packet contained that string in its Info column. + Ningún paquete contiene esta cadena en su columna de información. + + + No packet contained that string in its dissected display. + Ningún paquete contiene esta cadena en su pantalla de análisis. + + + No packet contained that string in its converted data. + Ningún paquete contiene esta cadena en sus datos convertidos. + + + No packet matched that filter. + Ningún paquete coincide con este filtro. + + + + SequenceDialog + + Call Flow + + + + Time + Intervalo + + + Comment + Comentario + + + No data + + + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Portable Network Graphics (*.png) + Gráficos de red portable (*.png) + + + Windows Bitmap (*.bmp) + Mapa de bits de Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Formato de intercambio de archivo JPEG (*.jpeg *.jpg) + + + ASCII (*.txt) + + + + Save Graph As… + Guardar gráfica como… + + + Flow + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + + + + <small><i>A hint</i></small> + <small><i>A hint.</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + + + + Limit to display filter + Limitar filtro de visualización + + + Flow type: + Tipo de flujo: + + + Addresses: + Direcciones: + + + Any + Cualquiera + + + Network + Red + + + Reset Diagram + + + + Reset &Diagram + + + + Reset the diagram to its initial state. + + + + 0 + 0 + + + &Reset Diagram + + + + Reset the diagram to its initial state + + + + &Export + &Exportar + + + Export diagram + + + + Zoom In + Aumentar zoom + + + + + + + + + Zoom Out + Reducir zoom + + + - + - + + + Move Up 10 Pixels + Mover arriba 10 píxeles + + + Up + + + + Move Left 10 Pixels + Mover a la izquierda 10 píxeles + + + Left + + + + Move Right 10 Pixels + Mover a la derecha 10 píxeles + + + Right + + + + Move Down 10 Pixels + Mover abajo 10 píxeles + + + Down + + + + Move Up 1 Pixel + Mover arriba 1 píxel + + + Shift+Up + + + + Move Left 1 Pixel + Mover a la izquierda 1 píxel + + + Shift+Left + + + + Move Right 1 Pixel + Mover a la derecha 1 píxel + + + Shift+Right + + + + Move Down 1 Pixel + Mover abajo 1 píxel + + + Shift+Down + + + + Go To Packet Under Cursor + Ir a paquete bajo cursor + + + Go to packet currently under the cursor + + + + G + G + + + All Flows + Todos los flujos + + + Show flows for all packets + + + + 1 + 1 + + + TCP Flows + Flujos TCP + + + Show only TCP flow information + + + + Go To Next Packet + Ir a paquete siguiente + + + Go to the next packet + Va al paquete siguiente + + + N + N + + + Go To Previous Packet + Ir a paquete anterior + + + Go to the previous packet + Va al paquete anterior + + + P + P + + + Select RTP Stream + + + + Select RTP stream in RTP Streams dialog + + + + S + S + + + Deselect RTP Stream + + + + Deselect RTP stream in RTP Streams dialog + + + + D + D + + + + ShortcutListModel + + Shortcut + + + + Name + Nombre + + + Description + Descripción + + + + ShowPacketBytesDialog + + Show Packet Bytes + + + + Hint. + Hint. + + + Decode as + Decodificar como + + + Show as + Mostrar como + + + Start + Inicio + + + End + Fin + + + Find: + Buscar: + + + Find &Next + Buscar &siguiente + + + Frame %1, %2, %Ln byte(s). + + + + + + + None + Ninguno + + + Base64 + Base64 + + + Compressed + Comprimido + + + Hex Digits + + + + Percent-Encoding + + + + Quoted-Printable + + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII & control + + + C Array + Matriz C + + + EBCDIC + EBCDIC + + + Hex Dump + Volcado hexadecimal + + + HTML + HTML + + + Image + Imagen + + + Raw + Raw + + + Rust Array + + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Imprimir + + + Copy + Copiar + + + Save as… + Guardar como… + + + Save Selected Packet Bytes As… + + + + Displaying %Ln byte(s). + + + + + + + JSON + JSON + + + Regex Find: + + + + + ShowPacketBytesTextEdit + + Show Selected + Mostrar seleccionado + + + Show All + Mostrar todo + + + + SplashOverlay + + Initializing dissectors + + + + Initializing tap listeners + + + + Initializing external capture plugins + + + + Registering dissectors + + + + Registering plugins + Registering dissector + + + + Handing off dissectors + + + + Handing off plugins + + + + Loading Lua plugins + + + + Removing Lua plugins + + + + Loading module preferences + Cargando módulo de preferencias + + + Finding local interfaces + Buscando interfaces locales + + + Applying changed preferences + + + + (Unknown action) + + + + + StatsTreeDialog + + Configuration not found + + + + Unable to find configuration for %1. + + + + + StripHeadersDialog + + Dialog + Dialog + + + Display filter: + Filtro de visualización: + + + + SupportedProtocolsDialog + + Dialog + Dialog + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Busca en la lista de los nombres de campo.</p></body></html> + + + Search: + Buscar: + + + <small><i>Gathering protocol information…</i></small> + <small><i>Recopilando información de protocolo…</i></small> + + + Supported Protocols + Protocolos soportados + + + %1 protocols, %2 fields. + %1 protocolos, %2 campos. + + + + SupportedProtocolsModel + + Name + Nombre + + + Filter + Filtro + + + Type + Tipo + + + Description + Descripción + + + + SyntaxLineEdit + + Invalid filter: %1 + + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + + + + <small><i>Mouse over for shortcuts</i></small> + + + + Type + Tipo + + + MA Window (s) + + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + Permite que los segmentos SACK como los paquetes de datos sean seleccionados al hacer clic en la gráfica + + + Select SACKs + select SACKs + Seleccionar SACKs + + + Stream + Secuencia + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Cambia la dirección de la conexión (ver el flujo opuesto).</p></body></html> + + + Switch Direction + Cambiar dirección + + + Mouse + Ratón + + + Drag using the mouse button. + Arrastra usando el botón de ratón. + + + drags + arrastrar + + + Select using the mouse button. + Selecciona usando el botón de ratón. + + + zooms + zoom + + + Display Round Trip Time vs Sequence Number + + + + RTT By Sequence Number + RTT por número de secuencia + + + Display graph of Segment Length vs Time + + + + Segment Length + Longitud de segmento + + + Display graph of Mean Transmitted Bytes vs Time + + + + Display graph of Mean ACKed Bytes vs Time + + + + Goodput + + + + Display graph of Receive Window Size vs Time + + + + Rcv Win + + + + Display graph of Outstanding Bytes vs Time + + + + Bytes Out + + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + + + + Reset + Restablecer + + + Reset Graph + Restablecer gráfica + + + Reset the graph to its initial state. + Restablece la gráfica al estado inicial. + + + 0 + 0 + + + Zoom In + Aumentar zoom + + + + + + + + + Zoom Out + Reducir zoom + + + - + - + + + Move Up 10 Pixels + Mover arriba 10 píxeles + + + Up + + + + Move Left 10 Pixels + Mover a la izquierda 10 píxeles + + + Left + + + + Move Right 10 Pixels + Mover a la derecha 10 píxeles + + + Right + + + + Move Down 10 Pixels + Mover abajo 10 píxeles + + + Down + + + + Move Up 1 Pixel + Mover arriba 1 píxel + + + Shift+Up + + + + Move Left 1 Pixel + Mover a la izquierda 1 píxel + + + Shift+Left + + + + Move Right 1 Pixel + Mover a la derecha 1 píxel + + + Shift+Right + + + + Move Down 1 Pixel + Mover abajo 1 píxel + + + Shift+Down + + + + Next Stream + + + + Go to the next stream in the capture + + + + PgUp + + + + Previous Stream + + + + Go to the previous stream in the capture + + + + PgDown + + + + Switch direction (swap TCP endpoints) + + + + D + D + + + Go To Packet Under Cursor + Ir a paquete bajo cursor + + + Go to packet currently under the cursor + + + + G + G + + + Drag / Zoom + Arrastrar / Zoom + + + Toggle mouse drag / zoom behavior + + + + Z + Z + + + Relative / Absolute Sequence Numbers + + + + Toggle relative / absolute sequence numbers + + + + S + S + + + Capture / Session Time Origin + Captura / Tiempo original de sesión + + + Toggle capture / session time origin + + + + T + T + + + Crosshairs + Retícula + + + Toggle crosshairs + + + + Space + Espacio + + + Round Trip Time + + + + Switch to the Round Trip Time graph + + + + 1 + 1 + + + Throughput + Rendimiento + + + Switch to the Throughput graph + + + + 2 + 2 + + + Time / Sequence (Stevens) + + + + Switch to the Stevens-style Time / Sequence graph + + + + 3 + 3 + + + Window Scaling + Escalado de ventana + + + Switch to the Window Scaling graph + + + + 5 + 5 + + + Time / Sequence (tcptrace) + + + + Switch to the tcptrace-style Time / Sequence graph + + + + 4 + 4 + + + Zoom In X Axis + Aumentar zoom en eje X + + + X + X + + + Zoom Out X Axis + Reducir zoom en eje X + + + Shift+X + + + + Zoom In Y Axis + Aumentar zoom en eje Y + + + Y + Y + + + Zoom Out Y Axis + Reducir zoom en eje Y + + + Shift+Y + + + + Save As… + Guardar como… + + + No Capture Data + + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + + + + Sequence Numbers (Stevens) + + + + Sequence Numbers (tcptrace) + + + + (MA) + + + + (%1 Segment MA) + + + + [not enough data] + + + + for %1:%2 %3 %4:%5 + + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + + Click to select packet + Clic para seleccionar paquete + + + Packet + Paquete + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Soltar para zoom, x = %1 a %2, y = %3 a %4 + + + Unable to select range. + No puede seleccionar rango. + + + Click to select a portion of the graph. + Clic para seleccionar una parte de la gráfica. + + + Portable Document Format (*.pdf) + Formato de documento portable (*.pdf) + + + Portable Network Graphics (*.png) + Gráficos de red portable (*.png) + + + Windows Bitmap (*.bmp) + Mapa de bits de Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Formato de intercambio de archivo JPEG (*.jpeg *.jpg) + + + Save Graph As… + Guardar gráfica como… + + + + TLSKeylogDialog + + Dialog + Dialog + + + Browse… + Explorar… + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + Dialog + + + Item + Item + + + <small><i>A hint.</i></small> + <small><i>A hint.</i></small> + + + Display filter: + Filtro de visualización: + + + Regenerate statistics using this display filter + Regenera estadísticas usando este filtro de visualización + + + Apply + Aplicar + + + Copy + Copiar + + + Copy a text representation of the tree to the clipboard + Copia una descripción de texto del árbol en el portapapeles + + + Save as… + Save as... + Guardar como… + + + Save the displayed data in various formats + Guarda los datos mostrados en varios formatos + + + Collapse All + Contraer todo + + + Expand All + Expandir todo + + + Save Statistics As… + Guardar estadísticas como… + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + Archivo de texto plano (*.txt);;Valores separados por coma (*.csv);;Documento XML (*.xml);;Documento YAML (*.yaml) + + + Plain text file (*.txt) + Archivo de texto plano (*.txt) + + + Error saving file %1 + + + + + TimeShiftDialog + + Shift all packets by + Modificar todos los paquetes por + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + Establecer horario para paquete + + + to + a + + + …then set packet + ...then set packet + …después establecer paquete + + + and extrapolate the time for all other packets + y extrapolar el horario para todos los otros paquetes + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[AAAA-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + Deshacer todas las modificaciones + + + Time Shift + Modificar horario + + + Frame numbers must be between 1 and %1. + Los números de trama deben estar entre 1 y %1. + + + Invalid frame number. + + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + + + + Could not open base file %1 for reading: %2 + + + + No endpoints available to map + + + + Unable to create temporary file + + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>Muestra direcciones resueltas y nombres de puertos en lugar de valores simples. La preferencia correspondiente de resolución de nombre debe estar activada.</p></body></html> + + + Name resolution + Resolución de nombre + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Muestra solo conversaciones que coinciden con el filtro de visualización actual</p></body></html> + + + Limit to display filter + Limitar filtro de visualización + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + + + + Filter list for specific type + Lista de filtros para tipo específico + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>Muestra tiempos absolutos en la columna de hora de inicio.</p></body></html> + + + GroupBox + GroupBox + + + Absolute start time + Hora de inicio absoluta + + + Copy + Copiar + + + Unknown + + + + + TrafficTree + + Resize all columns to content + Redimensionar todas las columnas al contenido + + + Filter on stream id + + + + Copy %1 table + Copiar tabla %1 + + + as CSV + como CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + + + + as YAML + como YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + + + + as JSON + + + + Copy all values of this page to the clipboard in the JSON data serialization format. + + + + Save data as raw + + + + Disable data formatting for export/clipboard and save as raw data + + + + + TrafficTreeHeaderView + + Less than + Menor que + + + Greater than + Mayor que + + + Equal + Igual + + + Columns to display + Columnas para mostrar + + + Filter %1 by + Filtrar %1 por + + + Enter filter value + Introduzca valor de filtro + + + + TrafficTypesModel + + Protocol + Protocolo + + + + UatDialog + + Create a new entry. + Crea una nueva entrada. + + + Remove this entry. + Remove this profile. + Elimina esta entrada. + + + Copy this entry. + Copy this profile. + Copia esta entrada. + + + Move entry up. + Mueve entrada hacia arriba. + + + Move entry down. + Mueve entrada hacia abajo. + + + Clear all entries. + Vacía todas las entradas. + + + Unknown User Accessible Table + + + + Open + Abrir + + + + UatFrame + + Frame + Frame + + + Create a new entry. + Crea una nueva entrada. + + + Remove this entry. + Elimina esta entrada. + + + Copy this entry. + Copia esta entrada. + + + Move entry up. + Mueve entrada hacia arriba. + + + Move entry down. + Mueve entrada hacia abajo. + + + Clear all entries. + Vacía todas las entradas. + + + Copy entries from another profile. + Copia entradas desde otro perfil. + + + Copy from + Copiar desde + + + Unknown User Accessible Table + + + + Open + Abrir + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Muestra solo conversaciones que coinciden con el filtro de visualización actual</p></body></html> + + + Limit to display filter + Limitar filtro de visualización + + + Time of Day + Hora de día + + + Flow &Sequence + + + + Show flow sequence for selected call(s). + + + + Prepare &Filter + + + + Prepare a filter matching the selected calls(s). + + + + Cop&y + Cop&iar + + + Open copy menu + Abre menú copiar + + + All + + + + Select all + Selecciona todo + + + None + + + + Invert + Invertir + + + Invert selection + + + + Select related RTP streams + + + + Select RTP streams related to selected calls in RTP Streams dialog + + + + S + S + + + Deselect related RTP Streams + + + + D + D + + + Clear selection + + + + Display time as time of day + + + + Copy as CSV + Copiar como CSV + + + Copy stream list as CSV. + Copia el listado de secuencias como CSV. + + + Copy as YAML + Copiar como YAML + + + Copy stream list as YAML. + Copia el listado de secuencias como YAML. + + + SIP Flows + Flujos SIP + + + VoIP Calls + + + + as CSV + como CSV + + + as YAML + como YAML + + + Select + + + + + VoipCallsInfoModel + + On + + + + Off + + + + Tunneling: %1 Fast Start: %2 + + + + Start Time + + + + Stop Time + + + + Initial Speaker + Interlocutor inicial + + + From + Desde + + + To + A + + + Protocol + Protocolo + + + Duration + Duración + + + Packets + Paquetes + + + State + Estado + + + Comments + Comentarios + + + + WelcomePage + + Form + Form + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Bienvenidos a Wireshark</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>Abre un archivo en su sistema de archivos</p></body></html> + + + <h2>Open</h2> + <h2>Abrir</h2> + + + Recent capture files + Archivos de captura recientes + + + Capture files that have been opened previously + Archivos de captura que han sido abiertos anteriormente + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>Captura en vivo paquetes desde su red.</p></body></html> + + + <h2>Capture</h2> + <h2>Capturar</h2> + + + …using this filter: + …usando este filtro: + + + Interface list + Listado de interfaz + + + List of available capture interfaces + Listado de interfaces de captura disponibles + + + <h2>Learn</h2> + <h2>Descubrir</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">Guía de usuario</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Preguntas y respuestas</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Listas de correo</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Discord de Wireshark</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donar</a></th> + +</tr></table> +</body></html> + + + Show in Finder + Mostrar en Finder + + + Show in Folder + Mostrar en carpeta + + + Welcome to %1 + + + + All interfaces shown + Todas las interfaces mostradas + + + %n interface(s) shown, %1 hidden + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + You are sniffing the glue that holds the Internet together using Wireshark + Está absorbiendo el pegamento que mantiene unido internet usando Wireshark + + + You are running Wireshark + Está ejecutando Wireshark + + + You receive automatic updates. + Recibe actualizaciones automáticas. + + + You have disabled automatic updates. + Ha desactivado actualizaciones automáticas + + + not found + no encontrado + + + Copy file path + Copiar ruta de archivo + + + Remove from list + Quitar de la lista + + + + WirelessFrame + + Frame + Frame + + + Interface + Interfaz + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>Establece el canal 802.11</p></body></html> + + + Channel + Canal + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>Al capturar, muestra todas las tramas, unas que tienen una secuencia de verificación de trama válida (FCS), o unas con una FCS inválida.</p></body></html> + + + FCS Filter + Filtro FCS + + + All Frames + Todas las tramas + + + Valid Frames + Tramas validas + + + Invalid Frames + Tramas no validas + + + Wireless controls are not supported in this version of Wireshark. + Los controles wireless no son compatibles en esta versión de Wireshark. + + + External Helper + Ayuda externa + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>Muestra las preferencias de IEEE 802.11, incluidas las claves de decodificación.</p></body></html> + + + 802.11 Preferences + Preferencias de 802.11 + + + AirPcap Control Panel + Panel de control de AirPcap + + + Open the AirPcap Control Panel + Abre el panel de control de AirPcap + + + Unable to set channel or offset. + + + + Unable to set FCS validation behavior. + + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + + + + + WiresharkDialog + + Failed to attach to tap "%1" + + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Ir a paquete + + + Cancel + Cancelar + + + File Set + Conjunto de archivo + + + Export Packet Dissections + Exportar análisis de paquete + + + Export Objects + Exportar objetos + + + &Zoom + &Zoom + + + &Time Display Format + &Formato de visualización de fecha + + + Copy + Copiar + + + Manual pages + Páginas de manual + + + Apply as Filter + Aplicar como filtro + + + Prepare as Filter + Preparar como filtro + + + SCTP + SCTP + + + TCP Stream Graphs + Gráficas de secuencia TCP + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &Archivo + + + &Capture + &Captura + + + &Help + &Ayuda + + + &Go + &Ir + + + &View + &Visualización + + + &Analyze + &Analizar + + + Follow + Seguir + + + &Statistics + &Estadísticas + + + 29West + 29West + + + Topics + + + + Queues + + + + UIM + UIM + + + Telephon&y + Telefon&ía + + + RTSP + RTSP + + + &Edit + &Edición + + + Packet Comments + Comentarios de paquete + + + Main Toolbar + Barra de herramientas principal + + + Display Filter Toolbar + + + + Open a capture file + Abre un archivo de captura + + + Quit Wireshark + Sale de Wireshark + + + &Start + &Iniciar + + + Start capturing packets + Inicia captura de paquetes + + + S&top + D&etener + + + Stop capturing packets + Detiene captura de paquetes + + + No files found + No se encontraron archivos + + + &Contents + &Contenidos + + + Wireshark Filter + Filtro de Wireshark + + + TShark + TShark + + + Rawshark + Rawshark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + Text2pcap + + + Website + Página web + + + Downloads + Descargas + + + Wiki + Wiki + + + Sample Captures + Capturas de ejemplo + + + &About Wireshark + &Acerca de Wireshark + + + Ask (Q&&A) + Consultar (Q&&A) + + + Next Packet + Paquete siguiente + + + Go to the next packet + Va al paquete siguiente + + + Previous Packet + Paquete anterior + + + Go to the previous packet + Va al paquete anterior + + + First Packet + Primer paquete + + + Go to the first packet + Va al primer paquete + + + Last Packet + Último paquete + + + Go to the last packet + Va al último paquete + + + E&xpand Subtrees + E&xpandir subárboles + + + Expand the current packet detail + Expande los detalles de paquete actual + + + &Expand All + &Expandir todo + + + Expand packet details + Expande los detalles de paquete + + + Collapse &All + Contraer &todo + + + Collapse all packet details + Contrae todos los detalles de paquete + + + Go to specified packet + Va a paquete especificado + + + Merge one or more files + Fusiona uno o más archivos + + + Import a file + Importa un archivo + + + &Save + &Guardar + + + Save as a different file + Guarda como un archivo diferente + + + Export specified packets + Exporta paquetes especificados + + + Export TLS Session Keys… + Exportar claves de sesión TLS… + + + List Files + Enumerar archivos + + + Next File + Archivo siguiente + + + Previous File + Archivo anterior + + + &Reload + &Volver a cargar + + + Options + Opciones + + + Capture options + Opciones de captura + + + Capture filters + Filtros de captura + + + Refresh Interfaces + Actualizar interfaces + + + Refresh interfaces + Actualiza los interfaces + + + &Restart + &Volver a iniciar + + + Restart current capture + Vuelve a iniciar captura actual + + + As &CSV… + Como &CSV… + + + As "C" &Arrays… + Como &matrices "C"… + + + As P&SML XML… + Como P&SML XML… + + + As P&DML XML… + Como P&DML XML… + + + As &JSON… + Como &JSON… + + + Description + Descripción + + + Field Name + Nombre de campo + + + Value + Valor + + + As Filter + Como filtro + + + Close this capture file + Cierra este archivo de captura + + + Packet: + Paquete: + + + Interface Toolbars + Barra de herramientas de interfaces + + + Colorize Conversation + Colorear conversación + + + Internals + Internas + + + Additional Toolbars + + + + Conversation Filter + Filtro de conversación + + + Reliable Server Pooling (RSerPool) + + + + SOME/IP + SOME/IP + + + &DTN + &DTN + + + Osmux + Osmux + + + &Tools + Tools + &Herramientas + + + Wireless Toolbar + Barra de herramientas de wireless + + + Help contents + Contenidos de ayuda + + + FAQs + FAQs + + + Next Packet in Conversation + Paquete siguiente en conversación + + + Go to the next packet in this conversation + Va al paquete siguiente en esta conversación + + + Previous Packet in Conversation + Paquete anterior en conversación + + + Go to the previous packet in this conversation + Va al paquete anterior en esta conversación + + + Next Packet In History + Paquete siguiente en historial + + + Go to the next packet in your selection history + Va al paquete siguiente en su historial de selección + + + Previous Packet In History + Paquete anterior en historial + + + Go to the previous packet in your selection history + Va al paquete anterior en su historial de selección + + + Collapse Subtrees + Contraer subárboles + + + Collapse the current packet detail + Contrae los detalles de paquete actual + + + Go to Packet… + Ir a paquete… + + + &Merge… + &Fusionar… + + + &Import from Hex Dump… + &Importar desde volcado hexadecimal… + + + Save this capture file + Guarda este archivo de captura + + + Save &As… + Guardar &como… + + + Export Specified Packets… + Exportar paquetes especificados… + + + Export Packet &Bytes… + Exportar &bytes de paquete… + + + &Print… + &Imprimir… + + + Reload this file + Vuelve a cargar este archivo + + + Reload as File Format/Capture + Volver a cargar como formato/captura de archivo + + + Copy this item's description + Copia la descripción de este ítem + + + Copy this item's field name + Copia el nombre de campo de este ítem + + + Copy this item's value + Copia el valor de este ítem + + + Copy this item as a display filter + Copia este ítem como filtro de visualización + + + Apply as Column + Aplicar como columna + + + Create a packet list column from the selected field. + Crea una columna de lista de paquetes desde el campo seleccionado. + + + Find a packet + Busca un paquete + + + Find the next packet + Busca el paquete siguiente + + + Find the previous packet + Busca el paquete anterior + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + &Marcar/Desmarcar paquete + + + Mark All Displayed + Marcar todos los mostrados + + + Mark all displayed packets + Marca todos los paquetes mostrados + + + Unmark all displayed packets + Desmarca todos los paquetes mostrados + + + Next Mark + Marcar siguiente + + + Go to the next marked packet + Va al paquete marcado siguiente + + + Previous Mark + Marcar anterior + + + Go to the previous marked packet + Va al paquete marcado anterior + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + &Ignorar/No ignorar paquete + + + Ignore All Displayed + Ignorar todos los mostrados + + + Ignore all displayed packets + Ignora todos los paquetes mostrados + + + Set/Unset Time Reference + Establecer/Anular referencia de tiempo + + + Set or unset a time reference for this packet + Establece o anula una referencia de tiempo para este paquete + + + Unset All Time References + Anular todas las referencias de tiempo + + + Remove all time references + Borra todas las referencias de tiempo + + + Next Time Reference + Referencia de tiempo siguiente + + + Go to the next time reference + Va a la referencia de tiempo siguiente + + + Previous Time Reference + Referencia de tiempo anterior + + + Go to the previous time reference + Va a la referencia de tiempo anterior + + + Shift or change packet timestamps + Cambia o modifica las marcas horarias de los paquetes + + + Delete All Packet Comments + Eliminar todos los comentarios de paquete + + + Remove all packet comments in the capture file + Borra todos los comentarios de paquete en el archivo de captura + + + &Configuration Profiles… + &Configuración de perfiles… + + + Configuration profiles + + + + Manage your configuration profiles + Administra su configuración de perfiles + + + Manage Wireshark's preferences + Administra las preferencia de Wireshark + + + Capture File Properties + Propiedades de archivo de captura + + + Capture file properties + Propiedades de archivo de captura + + + &Protocol Hierarchy + &Jerarquía de protocolo + + + Show a summary of protocols present in the capture file. + Muestra un informe de los protocolos presentes en el archivo de captura. + + + Capinfos + Capinfos + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + Duración de secuencia (Stevens) + + + TCP time sequence graph (Stevens) + Gráfica de duración de secuencia TCP (Stevens) + + + Throughput + Rendimiento + + + Round Trip Time + + + + TCP round trip time + + + + Window Scaling + Escalado de ventana + + + TCP window scaling + Escalado de ventana TCP + + + HTTP/2 Stream + Secuencia HTTP/2 + + + SIP Call + Llamada SIP + + + Time Sequence (tcptrace) + Duración de secuencia (tcptrace) + + + TCP time sequence graph (tcptrace) + Gráfica de duración de secuencia (tcptrace) + + + Analyse this Association + Analizar esta asociación + + + Show All Associations + Mostrar todas las asociaciones + + + Flow Graph + Gráfica de flujo + + + Flow sequence diagram + Diagrama de flujo de secuencia + + + ANCP + ANCP + + + ANCP statistics + Estadísticas de ANCP + + + Packets sorted by Instance ID + Paquetes ordenados por ID de instancia + + + BACapp statistics sorted by instance ID + Estadísticas de BACapp ordenadas por ID de instancia + + + Packets sorted by IP + Paquetes ordenados por IP + + + BACapp statistics sorted by IP + Estadísticas de BACapp ordenadas por IP + + + Packets sorted by object type + Paquetes ordenados por tipo de objeto + + + BACapp statistics sorted by object type + Estadísticas de BACapp ordenadas por tipo de objecto + + + Packets sorted by service + Paquetes ordenados por servicio + + + BACapp statistics sorted by service + Estadísticas de BACapp ordenadas por servicio + + + Collectd + Collectd + + + Collectd statistics + Estadísticas de Collectd + + + DNS + DNS + + + DNS statistics + Estadísticas de DNS + + + HART-IP + HART-IP + + + HART-IP statistics + Estadísticas de HART-IP + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + Estadísticas de hpfeeds + + + HTTP2 + HTTP2 + + + HTTP2 statistics + Estadísticas de HTTP2 + + + Packet Counter + Contador de paquetes + + + HTTP packet counter + Contador de paquetes HTTP + + + Requests + Peticiones + + + HTTP requests + Peticiones HTTP + + + Load Distribution + Distribución de carga + + + HTTP load distribution + Distribución de carga HTTP + + + Packet Lengths + Longitudes de paquete + + + Packet length statistics + Estadísticas de longitudes de paquete + + + Sametime + Sametime + + + Sametime statistics + Estadísticas de Sametime + + + SOME/IP Messages + Mensajes de SOME/IP + + + SOME/IP Message statistics + Estadísticas de mensajes de SOME/IP + + + SOME/IP-SD Entries + + + + SOME/IP-SD Entries statistics + + + + &LTP + &LTP + + + LTP segment and block statistics + + + + &ISUP Messages + &Mensajes ISUP + + + ISUP message statistics + Estadísticas de mensajes ISUP + + + Osmux packet counts + Contador de paquetes Osmux + + + RTSP packet counts + Contador de paquetes RTSP + + + SM&PP Operations + Operaciones SM&PP + + + SMPP operation statistics + Estadísticas de operaciones SMPP + + + &UCP Messages + &Mensajes UCP + + + UCP message statistics + Estadísticas de mensajes UCP + + + F1AP + F1AP + + + F1AP Messages + Mensajes F1AP + + + NGAP + NGAP + + + NGAP Messages + Mensajes NGAP + + + Change the way packets are dissected + + + + Reload Lua Plugins + Volver a cargar complementos Lua + + + Reload Lua plugins + Vuelve a cargar los complementos Lua + + + Advertisements by Topic + + + + Advertisements by Source + + + + Advertisements by Transport + + + + Queries by Topic + + + + Queries by Receiver + + + + Wildcard Queries by Pattern + + + + Wildcard Queries by Receiver + + + + Advertisements by Queue + + + + Queries by Queue + + + + Streams + Secuencias + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + Filtrar esta asociación + + + Strip Headers… + Quitar cabeceras… + + + Strip headers and export higher level encapsulations to file + Quita las cabeceras y exporta los encapsulados de nivel superior a un archivo + + + &I/O Graphs + &Gráficas de E/S + + + &Conversations + &Conversaciones + + + &Endpoints + &Puntos finales + + + Shrink the main window text + Reduce el texto de la ventana principal + + + Return the main window text to its normal size + Devuelve el texto de la ventana principal a su tamaño normal + + + Reset Layout + Restablecer diseño + + + Reset appearance layout to default size + Restablece el diseño de apariencia al tamaño por defecto + + + Seconds Since First Captured Packet + Segundos desde primer paquete capturado + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + &Diagrama de paquete + + + Show or hide the packet diagram + Muestra o oculta el diagrama de paquete + + + Show each conversation hash table + Muestra cada tabla hash de conversación + + + Show each dissector table and its entries + + + + Show the currently supported protocols and display filter fields + Muestra los protocolos soportados actualmente y los campos de filtro de visualización + + + MAC Statistics + Estadísticas MAC + + + LTE MAC statistics + Estadísticas MAC de LTE + + + RLC Statistics + Estadísticas RLC + + + LTE RLC statistics + Estadísticas RLC de LTE + + + LTE RLC graph + Gráfica RLC de LTE + + + MTP3 Summary + Informe de MTP3 + + + MTP3 summary statistics + Estadísticas de informe de MTP3 + + + Bluetooth Devices + Dispositivos bluetooth + + + Bluetooth HCI Summary + Informe HCI de bluetooth + + + Display Filter &Expression… + Mostrar &expresión de filtro… + + + Display Filter Expression… + Mostrar expresión de filtro… + + + REGISTER_STAT_GROUP_RSERPOOL + + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + + + + No GSM statistics registered + No hay estadísticas GSM registradas + + + No LTE statistics registered + No hay estadísticas LTE registradas + + + No MTP3 statistics registered + No hay estadísticas MTP3 registradas + + + IAX2 Stream Analysis + Análisis de secuencia IAX2 + + + Show Packet Bytes… + Mostrar bytes de paquete… + + + Go to &Linked Packet + Ir a &paquete enlazado + + + UDP Multicast Streams + Secuencias multicast de UDP + + + Show UTP multicast stream statistics. + Muestra las estadísticas de secuencia de difusión múltiple UTP. + + + WLAN Traffic + Tráfico WLAN + + + Show IEEE 802.11 wireless LAN statistics. + Muestra las estadísticas de red inalámbrica IEEE 802.11 + + + Add a display filter button. + Añade un botón de filtro de visualización. + + + Firewall ACL Rules + Reglas ACL de cortafuegos + + + Create firewall ACL rules + Crea una regla ACL de cortafuegos + + + &Full Screen + &Pantalla completa + + + Credentials + Credenciales + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + &Opciones… + + + &Wireless + &Wireless + + + Capture &Filters… + Filtros de &captura… + + + As Plain &Text… + Como &texto plano… + + + As Plain &Text + Como &texto plano + + + As &CSV + Como &CSV + + + As &YAML + Como &YAML + + + All Visible Items + Todos los ítems visibles + + + All Visible Selected Tree Items + Todos los ítems de árbol seleccionados + + + Display Filter &Macros… + Mostrar &macros de filtro… + + + &Find Packet… + &Buscar paquete… + + + Find Ne&xt + Buscar si&guiente + + + Find Pre&vious + Buscar ant&erior + + + Mark or unmark each selected packet + Marca o desmarca cada paquete seleccionado + + + Ignore or unignore each selected packet + Ignora o no ignora cada paquete seleccionado + + + U&nignore All Displayed + N&o ignorar todos los mostrados + + + Unignore all displayed packets + No ignora todos los paquetes mostrados + + + Time Shift… + Modificar horario… + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + &Preferencias… + + + TCP throughput + Rendimiento TCP + + + Request Sequences + Secuencias de petición + + + HTTP Request Sequences + Secuencias de solicitud HTTP + + + Decode &As… + Decodificar &como… + + + Export PDUs to File… + Exportar PDUs a archivo… + + + Create graphs based on display filter fields + Crea gráficas basadas en campos de filtros de visualización + + + &Main Toolbar + &Barra de herramientas principal + + + Show or hide the main toolbar + Muestra o oculta la barra de herramientas principal + + + &Filter Toolbar + &Barra de herramientas de filtro + + + Show or hide the display filter toolbar + Muestra o oculta la barra de herramientas de filtro de visualización + + + Conversations at different protocol levels + Conversaciones en diferentes niveles de protocolo + + + Endpoints at different protocol levels + Puntos finales en diferentes niveles de protocolo + + + Colorize Packet List + Colorear listado de paquetes + + + Draw packets using your coloring rules + Dibuja paquetes usando sus reglas de coloreado + + + &Zoom In + &Aumentar zoom + + + Enlarge the main window text + Amplía el texto de la ventana principal + + + Zoom Out + Reducir zoom + + + Normal Size + Tamaño normal + + + Resize Columns + Cambiar tamaño de columnas + + + Resize packet list columns to fit contents + Cambia el tamaño de las columnas de listado de paquetes para ajustar los contenidos + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Fecha y hora de día (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + Muestra la fecha de los paquetes como la fecha y la hora del día. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Año, día de año, y hora de día (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Muestra la fecha de los paquetes como el año, el día del año y la hora del día. + + + Time of Day (01:02:03.123456) + Hora de día (01:02:03.123456) + + + Seconds Since 1970-01-01 + Segundos desde 1970-01-01 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Muestra la fecha de los paquetes como los segundos desde la etapa de UNIX / POSIX (1970-01-01). + + + Seconds Since Previous Captured Packet + Segundos desde paquete capturado anterior + + + Show packet times as the seconds since the previous captured packet. + Muestra la fecha de los paquetes como los segundos desde el paquete anterior capturado. + + + Seconds Since Previous Displayed Packet + Segundos desde paquete mostrado anterior + + + Show packet times as the seconds since the previous displayed packet. + + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + Fecha y hora de día UTC (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Muestra la fecha de los paquetes como la fecha UTC y la hora del día. + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Año, día de año, y hora de día UTC (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + + + + UTC Time of Day (01:02:03.123456) + Hora de día UTC (01:02:03.123456) + + + Show packet times as the UTC time of day. + Muestra la fecha de los paquetes como la fecha UTC del día. + + + Automatic (from capture file) + Automático (desde archivo de captura) + + + Use the time precision indicated in the capture file. + + + + Seconds + Segundos + + + Tenths of a second + Décimas de segundo + + + Hundredths of a second + Centésimas de segundo + + + Milliseconds + Milisegundos + + + Microseconds + Microsegundos + + + Nanoseconds + Nanosegundos + + + Display Seconds With Hours and Minutes + Mostrar segundos con horas y minutos + + + Display seconds with hours and minutes + Muestra segundos con horas y minutos + + + Resolve &Physical Addresses + Resolver &direcciones físicas + + + Show names for known MAC addresses. Lookups use a local database. + + + + Resolve &Network Addresses + Resolver &direcciones de red + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + + + + Resolve &Transport Addresses + Resolver &direcciones de transporte + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + + + + Wire&less Toolbar + Barra de herramientas de Wire&less + + + Show or hide the wireless toolbar + Muestra o oculta la barra de herramientas de wireless + + + &Status Bar + &Barra de estado + + + Show or hide the status bar + + + + Packet &List + &Listado de paquetes + + + Show or hide the packet list + + + + Packet &Details + &Detalles de paquete + + + Show or hide the packet details + + + + Packet &Bytes + &Bytes de paquete + + + Show or hide the packet bytes + + + + &Conversation Hash Tables + &Tablas hash de conversación + + + &Dissector Tables + + + + &Supported Protocols + &Protocolos compatibles + + + MAP Summary + + + + GSM MAP summary statistics + + + + RLC &Graph + &Gráfica RLC + + + &Coloring Rules… + &Reglas de coloreado… + + + Show Linked Packet in New Window + Mostrar paquete enlazado en nueva ventana + + + New Coloring Rule… + New Conversation Rule… + Nueva regla de coloreado… + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + + + + RTP Player + Reproductor RTP + + + Play selected stream. Press CTRL key for playing reverse stream too. + Reproduce la secuencia seleccionada. Pulse la tecla CTRL para reproducir también la secuencia inversa. + + + IA&X2 Stream Analysis + + + + Enabled Protocols… + Enable Protocols… + Protocolos activados… + + + Wiki Protocol Page + Página Wiki de protocolo + + + Open the Wireshark wiki page for this protocol. + + + + Filter Field Reference + + + + Open the display filter reference page for this filter field. + + + + Go to the packet referenced by the selected field. + + + + &VoIP Calls + &Llamadas VoIP + + + Open &Recent + Abrir &reciente + + + Name Resol&ution + Resol&ución de nombre + + + Service &Response Time + Tiempo de &respuesta de servicio + + + &RTP + &RTP + + + S&CTP + S&CTP + + + &ANSI + &ANSI + + + &GSM + &GSM + + + &LTE + &LTE + + + &MTP3 + &MTP3 + + + &Open + &Abrir + + + &Quit + &Salir + + + &Close + &Cerrar + + + Display &Filters… + Mostrar &filtros… + + + &Unmark All Displayed + &Desmarcar todos los mostrados + + + All VoIP Calls + Todas las llamadas VoIP + + + SIP &Flows + &Flujos SIP + + + SIP Flows + Flujos SIP + + + RTP Streams + Secuencias RTP + + + Edit the packet list coloring rules. + Edita las reglas de coloreado de la lista de paquetes. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Atributos de servidor ATT bluetooth + + + Show Packet in New &Window + Mostrar paquete en nueva &ventana + + + Show this packet in a separate window. + Muestra este paquete en una ventana separada. + + + Show the linked packet in a separate window. + Muestra el paquete enlazado en una ventana separada. + + + Auto Scroll in Li&ve Capture + Desplazamiento automático en ca&ptura en vivo + + + Automatically scroll to the last packet during a live capture. + Desplaza automáticamente al último paquete durante la captura en vivo + + + Expert Information + Información especializada + + + Show expert notifications + + + + Add an expression to the display filter. + Añade una expresión al filtro de visualización. + + + REGISTER_STAT_GROUP_UNSORTED + + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + + + + No ANSI statistics registered + No tools registered + No hay estadísticas ANSI registradas + + + Resolved Addresses + Direcciones resueltas + + + Show each table of resolved addresses as copyable text. + Muestra cada tabla de direcciones resueltas como texto que se puede copiar. + + + Color &1 + Color &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + Marca la conversación actual con su propio color. + + + Color &2 + Color &2 + + + Color &3 + Color &3 + + + Color &4 + Color &4 + + + Color &5 + Color &5 + + + Color &6 + Color &6 + + + Color &7 + Color &7 + + + Color &8 + Color &8 + + + Color &9 + Color &9 + + + Color 1&0 + Color 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + Crea una nueva regla de coloreado basada en este campo. + + + Reset Colorization + Restablecer coloreado + + + Reset colorized conversations. + Restablece las conversaciones coloreadas. + + + RTP Stream Analysis + + + + Edit Resolved Name + Editar nombre resuelto + + + Manually edit a name resolution entry. + Edita manualmente una entrada de resolución de nombre. + + + Enable and disable specific protocols + Activa y desactiva los protocolos específicos + + + before quitting + antes de salir + + + Save packets before merging? + ¿Guardar paquetes antes de fusionar? + + + A temporary capture file can't be merged. + Un archivo de captura temporal no puede ser fusionado. + + + Save changes in "%1" before merging? + ¿Guardar cambios en "%1" antes de fusionar? + + + Changes must be saved before the files can be merged. + Los cambios se deben guardar antes de que los archivos se puedan fusionar. + + + Invalid Display Filter + + + + Invalid Read Filter + + + + The filter expression %1 isn't a valid read filter. (%2). + + + + before importing a capture + before importing a new capture + + + + Unable to export to "%1". + + + + You cannot export packets to the current capture file. + + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + + + + Your captured packets will be lost if you don't save them. + Sus paquetes capturados se perderán si no los guarda + + + Do you want to save the changes you've made to the capture file "%1"%2? + ¿Desea guardar los cambios que ha realizado en el archivo de captura "%1"%2? + + + Your changes will be lost if you don't save them. + Sus cambios se perderán si no los guarda. + + + Check for Updates… + Buscar actualizaciones… + + + Unable to drop files during capture. + + + + Unknown file type returned by merge dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + Unknown file type returned by export dialog. + + + + Do you want to stop the capture and save the captured packets%1? + ¿Desea detener la captura y guardar los paquetes capturados%1? + + + Do you want to save the captured packets%1? + ¿Desea guardar los paquetes capturados%1? + + + Save before Continue + Guardar antes de continuar + + + Stop and Save + Detener y guardar + + + Stop and Quit &without Saving + Stop and Quit without Saving + Detener y salir &sin guardar + + + Quit &without Saving + Quit without Saving + Salir &sin guardar + + + There is no "rtp.ssrc" field in this version of Wireshark. + + + + Please select an RTPv2 packet with an SSRC value + + + + SSRC value not found. + + + + Show or hide the toolbar + + + + Continue &without Saving + Continue without Saving + Continuar &sin guardar + + + Stop and Continue &without Saving + Stop and Continue without Saving + + + + The Wireshark Network Analyzer + + + + Capturing from %1 + Capturando desde %1 + + + before opening another file + + + + Merging files. + + + + %1: %2 + %1: %2 + + + Clear Menu + Vaciar menú + + + before closing the file + + + + Export Selected Packet Bytes + + + + No Keys + + + + Raw data (*.bin *.dat *.raw);;All Files ( + + + + Couldn't copy text. Try another item. + + + + Are you sure you want to remove all packet comments? + + + + Unable to build conversation filter. + + + + before reloading the file + + + + Error compiling filter for this conversation. + + + + No previous/next packet in conversation. + + + + No interface selected. + + + + Saving %1… + + + + Configure all extcaps before start of capture. + + + + Invalid capture filter. + + + + (empty comment) + placeholder for empty comment + + + + Add New Comment… + Añadir nuevo comentario… + + + Edit "%1" + edit packet comment + Editar "%1" + + + Delete "%1" + delete packet comment + Eliminar "%1" + + + Delete packet comments + Eliminar comentarios de paquete + + + Delete comments from %n packet(s) + + + + + + + before starting a new capture + antes de iniciar una nueva captura + + + before reloading Lua plugins + + + + Please wait while Wireshark is initializing… + Por favor espere mientras Wireshark se inicia… + + + before updating + + + + There are no TLS Session Keys to save. + + + + Export TLS Session Keys (%Ln key(s)) + + + + + + + TLS Session Keys (*.keys *.txt);;All Files ( + + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + + + + column + + + + item + + + + The "%1" column already exists. + + + + The "%1" column already exists as "%2". + + + + RTP packet search failed + + + + No Interface Selected. + + + + before restarting the capture + antes de reiniciar la captura + + + Wiki Page for %1 + + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>La wiki de Wireshark está mantenida por la comunidad.</p><p>La página que está a punto de cargar puede ser maravillosa, incompleta, incorrecta, o inexistente.</p><p>¿Continuar con la wiki?</p> + + + Loading + Cargando + + + Reloading + + + + Rescanning + Volver a escanear + + + + WlanStatisticsDialog + + Wireless LAN Statistics + Estadísticas LAN wireless + + + Channel + Canal + + + SSID + SSID + + + Percent Packets + Porcentaje de paquetes + + + Percent Retry + Porcentaje de intentos + + + Probe Reqs + Solicitudes de prueba + + + Probe Resp + Respuestas de prueba + + + Auths + Autenticaciones + + + Retry + Intentos + + + Deauths + Deauts + + + Other + Otro + + + diff --git a/ui/qt/wireshark_fr.ts b/ui/qt/wireshark_fr.ts new file mode 100644 index 00000000..f96a8317 --- /dev/null +++ b/ui/qt/wireshark_fr.ts @@ -0,0 +1,14743 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + À propos de Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Analyseur de Protocole réseau</span> + + + Copy the version information to the clipboard + Copier les informations de version dans le presse-papier + + + Copy to Clipboard + + + + Authors + Auteurs + + + Search Authors + Recherche des Auteurs + + + Folders + Dossiers + + + Filter by path + Filtrer par chemin + + + Plugins + Modules complémentaires + + + No plugins found. + Aucun plugin trouvé. + + + Search Plugins + Rechercher des plugins + + + Filter by type: + Filtrer par type : + + + Keyboard Shortcuts + Raccourcis clavier + + + Search Shortcuts + Raccourcis de recherche + + + Acknowledgments + Validations + + + License + Licence + + + The directory does not exist + Le dossier n'existe pas + + + Should the directory %1 be created? + Le dossier %1 doit-il être créé ? + + + The directory could not be created + Le dossier n'a pas pu être créé + + + The directory %1 could not be created. + Le dossier %1 n'a pas pu être créé. + + + Show in Finder + Afficher dans le Finder + + + Show in Folder + Afficher dans le Dossier + + + Copy + Copier + + + Copy Row(s) + + + + + + + + AddressEditorFrame + + Frame + Trame + + + Name Resolution Preferences… + Name Resolution Preferences... + Préférences de résolution de nom... + + + Address: + Adresse : + + + Name: + Nom : + + + Can't assign %1 to %2. + Impossible d'affecter %1 à %2. + + + + AdvancedPrefsModel + + Name + Nom + + + Status + État + + + Type + Type + + + Value + Valeur + + + + ApplyLineEdit + + Apply changes + Appliquer les modifications + + + + AuthorListModel + + Name + Nom + + + Email + Courriel + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Attributs Server Bluetooth ATT + + + Handle + Poignée + + + UUID + UUID + + + UUID Name + Nom UUID + + + All Interfaces + Toutes les interfaces + + + All Devices + Tous les équipements + + + Remove duplicates + Supprimer les doublons + + + Copy Cell + Copier la cellule + + + Copy Rows + Copier la ligne + + + Copy All + Copier Tout + + + Save as image + Enregistrer en tant qu'image + + + Mark/Unmark Row + Marquer/Démarquer la ligne + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Marquer/Démarquer la cellule + + + Save Table Image + Sauvegarder Image Tableau + + + PNG Image (*.png) + Image PNG (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + Équipement Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Nom + + + Class of Device + Classe d'équipement + + + LMP Version + Version LMP + + + LMP Subversion + Sous-version LMP + + + Manufacturer + Fabricant + + + HCI Version + Version HCI + + + HCI Revision + Révision HCI + + + Scan + Scan + + + Authentication + Authentification + + + Encryption + Chiffrement + + + ACL MTU + MTU de la liste de contrôle d'accès + + + ACL Total Packets + Nombre total de paquets ACL + + + SCO MTU + MTU de l'OCS + + + SCO Total Packets + Nombre total de paquets SCO + + + LE ACL MTU + LE ACL MTU + + + LE ACL Total Packets + Nombre total de paquets LE ACL + + + LE ISO MTU + LE ISO MTU + + + LE ISO Total Packets + Nombre total de paquets LE ISO + + + Inquiry Mode + Mode d'enquête + + + Page Timeout + Page Expirée + + + Simple Pairing Mode + Mode Simple Appairage + + + Voice Setting + Paramètres Voix + + + Value + Valeur + + + Changes + Changements + + + %1 changes + %1 changements + + + Copy Cell + Copier la Cellule + + + Copy Rows + Copier les Lignes + + + Copy All + Copier Tout + + + Save as image + Enregistrer en tant qu'image + + + Mark/Unmark Row + Marquer/Démarquer la ligne + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Marquer/Démarquer la cellule + + + Unknown + Inconnu + + + Bluetooth Device - %1%2 + Équipement Bluetooth - %1%2 + + + enabled + activé + + + disabled + désactivé + + + %1 ms (%2 slots) + %1 ms (%2 tranches) + + + Save Table Image + Enregistrer l'image du tableau + + + PNG Image (*.png) + Image PNG (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Équipements Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Nom + + + LMP Version + Version LMP + + + LMP Subversion + Subversion LMP + + + Manufacturer + Fabricant + + + HCI Version + Version HCI + + + HCI Revision + Révision HCI + + + Is Local Adapter + Est Adaptateur Local + + + All Interfaces + Toutes les interfaces + + + Show information steps + Afficher étapes d'information + + + %1 items; Right click for more option; Double click for device details + %1 éléments; Cliquez-droit pour plus d'options; Double-cliquez pour les détails de l'équipement + + + Copy Cell + Copier la cellule + + + Copy Rows + Copier les lignes + + + Copy All + Copier Tout + + + Save as image + Enregistrer en tant qu'image + + + Mark/Unmark Row + Marquer/Démarquer la ligne + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Marquer/Démarquer la cellule + + + true + vrai + + + Save Table Image + Sauvegarder Image Tableau + + + PNG Image (*.png) + Image PNG (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Résumé Bluetooth HCI + + + Name + Nom + + + OGF + OGF + + + OCF + OCF + + + Opcode + Opcode + + + Event + Évènement + + + Subevent + Sous-évènement + + + Status + État + + + Reason + Raison + + + Hardware Error + Erreur matérielle + + + Occurrence + Occurrence + + + Link Control Commands + Commandes Link Control + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Commandes Link Policy + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Commandes de &bande de base du contrôleur + + + 0x03 + 0x03 + + + Informational Parameters + Paramètres Informationnels + + + 0x04 + 0x04 + + + Status Parameters + Paramètres Statut + + + 0x05 + 0x05 + + + Testing Commands + Commandes de Test + + + 0x06 + 0x06 + + + LE Controller Commands + Commandes de Contrôleur LE + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + Commandes de test de logo Bluetooth + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Commandes Vendeur-Spécifiques + + + 0x3F + 0x3F + + + Unknown OGF + OGF Inconnu + + + Events + Evénements + + + Hardware Errors + Erreurs matérielles + + + Results filter: + Filtre des résultats : + + + Display filter: + Filtre d'affichage : + + + All Interfaces + Toutes les interfaces + + + All Adapters + Tous les Adaptateurs + + + Copy Cell + Copier la cellule + + + Copy Rows + Copier les lignes + + + Copy All + Copier Tout + + + Save as image + Enregistrer en tant qu'image + + + Mark/Unmark Row + Marquer/Démarquer la ligne + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Marquer/Démarquer la cellule + + + Unknown + Inconnu + + + Adapter %1 + Adaptateur %1 + + + Frame %1 + Trame %1 + + + Pending + En attente + + + Save Table Image + Sauvegarder Image Tableau + + + PNG Image (*.png) + Image PNG (*.png) + + + + ByteViewTab + + Packet bytes + Taille du paquet + + + + ByteViewText + + Allow hover highlighting + Autoriser la surbrillance au survol + + + Show bytes as hexadecimal + Afficher les octets en hexadécimal + + + …as decimal + + + + …as octal + + + + …as bits + …comme bits + + + Show text based on packet + Affiche le texte basé sur le paquet + + + …as ASCII + …comme ASCII + + + …as EBCDIC + …comme EBCDIC + + + + CaptureFile + + [closing] + [Fermeture] + + + [closed] + [Fermer] + + + + CaptureFileDialog + + This capture file contains comments. + Le fichier de capture contient des commentaires. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + Le format de fichier que vous avez choisi ne supporte pas les commentaires. Voulez-vous enregistrer la capture dans un format qui prend en charge les commentaires ou ignorer les commentaires et les enregistrer dans le format que vous avez choisi ? + + + Discard comments and save + Ignorer commentaires et sauvegarder + + + Save in another format + Sauvegarder dans un autre format + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + Le format de fichier dans lequel vous voulez sauvegarder ne prendre pas en charge les commentaires. Voulez-vous supprimer les commentaires et enregistrer dans le format que vous avez choisi ? + + + All Files ( + Tous les Fichiers ( + + + All Capture Files + Tous les fichiers de capture + + + Format: + Format : + + + Size: + Taille : + + + Start / elapsed: + Début / écoulé : + + + Automatically detect file type + Détecter automatiquement le type de fichier + + + Prepend packets + Ajout de paquets + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Insérer les paquets à partir du fichier sélectionné avant que le fichier actuel. Horodatage des paquets seront ignorés. + + + Merge chronologically + Fusionner chronologiquement + + + Insert packets in chronological order. + Insérer des paquets dans l'ordre chronologique. + + + Append packets + Ajout de paquets + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Insérer des paquets à partir du fichier sélectionné après le fichier en cours. Horodatage des paquets seront ignorés. + + + Read filter: + Filtre de lecture : + + + Compress with g&zip + Compresser avec g&zip + + + Open Capture File + Wireshark: Open Capture File + Ouvrir un fichier de capture + + + Save Capture File As + Wireshark: Save Capture File As + Wireshark: Sauvegarder le fichier de capture sous + + + Save as: + Sauvegarder sous : + + + Export Specified Packets + Wireshark: Export Specified Packets + Exporter les paquets séléctionnés + + + Export as: + Exporter sous : + + + Merge Capture File + Wireshark: Merge Capture File + Fusionner des captures + + + Unknown file type returned by save as dialog. + Type de fichier inconnu renvoyé par la boîte de dialogue enregistrer sous. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Veuillez signaler cela comme un problème Wireshark à l'adresse https://gitlab.com/wireshark/wireshark/-/issues. + + + directory + dossier + + + unknown file format + format du fichier inconnu + + + error opening file + erreur pour ouvrir le fichier + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + + + + + + %1, timed out at %Ln data record(s) + + + + + + + %1, %Ln data record(s) + + + + + + + unknown + inconnu + + + + CaptureFilePropertiesDialog + + Details + Détails + + + Capture file comments + Commentaires du fichier de capture + + + Refresh + Rafraîchir + + + Copy To Clipboard + Copier dans le Presse-papiers + + + Save Comments + Enregister les commentaires + + + Capture File Properties + Propriétés du fichier de capture + + + Unknown + Inconnu + + + File + Fichier + + + Name + Nom + + + Length + Longueur + + + Hash (SHA256) + Hachage (SHA256) + + + Hash (SHA1) + Hachage (SHA1) + + + Format + Format + + + Encapsulation + Encapsulation + + + Snapshot length + Taille Snapshot + + + Time + Temps + + + First packet + Premier paquet + + + Last packet + Dernier paquet + + + Elapsed + Temps écoulé + + + Section %1 + Section %1 + + + Capture + Capture + + + Hardware + Hardware + + + OS + Système d'exploitation + + + Application + Application + + + Interfaces + Interfaces + + + Interface + Interface + + + Dropped packets + Paquets rejetés + + + Capture filter + Filtre de capture + + + Link type + Type de lien + + + Packet size limit (snaplen) + Limite de taille de paquet (snaplen) + + + none + aucun + + + %1 bytes + %1 octets + + + Statistics + Statistiques + + + Measurement + Mesure + + + Captured + Capturés + + + Displayed + Affichés + + + Marked + Marqués + + + Packets + Paquets + + + Time span, s + Temps, s + + + Average pps + Moyenne de pps + + + Average packet size, B + Taille des paquets moyenne, O + + + Bytes + Octets + + + Average bytes/s + Débit moyen (octets/s) + + + Average bits/s + Débit moyen (bits/s) + + + Section Comment + Commentaire de la Section + + + Packet Comments + Commentaires du Paquet + + + <p>Frame %1: + <p>Trame %1 : + + + Created by Wireshark %1 + + + Créé par Wireshark %1 + + + + + + CaptureFilterCombo + + Capture filter selector + Selecteur de filtre de capture + + + + CaptureFilterEdit + + Capture filter entry + Capturer l'entrée du filtre + + + Manage saved bookmarks. + Gérer les signets sauvegardés. + + + Apply this filter string to the display. + Appliquer cette chaine de filtre à l'affichage. + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + Plusieurs filtres sélectionnés. Les écraser ici ou laisser vide pour les préserver. + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>Les interfaces séléctionnées ont des filtres de capture différents. Entrer un filtre ici va les écraser. Ne rien entrer va les préserver.</p> + + + Enter a capture filter %1 + Entrer un filtre de capture %1 + + + Save this filter + Sauvegarder ce filtre + + + Remove this filter + Supprimer ce filtre + + + Manage Capture Filters + Gérer les Filtres de Capture + + + + CaptureInfoDialog + + Capture Information + Information sur la Capture + + + Stop Capture + Arrêter la Capture + + + %1 packets, %2:%3:%4 + %1 paquets, %2:%3:%4 + + + + CaptureInfoModel + + Other + Autre + + + + CaptureOptionsDialog + + Input + Entrée + + + Interface + Interface + + + Traffic + Trafic + + + Link-layer Header + Entête de couche de liaison + + + Promiscuous + Promiscuité + + + Snaplen (B) + Snaplen (B) + + + Buffer (MB) + Tampon (Mo) + + + Monitor Mode + Mode moniteur + + + Capture Filter + Filtre de capture + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Vous voulez sans doute pour activer cela. Habituellement, une carte réseau ne capture que le trafic envoyé à sa propre adresse réseau. Si vous voulez capturer tout le trafic que la carte réseau peut &quot;voir&quot;, cochez cette case. Voir la FAQ pour plus de détails sur la capture des paquets à partir d'un réseau commuté.</p></body></html> + + + Enable promiscuous mode on all interfaces + Activer le mode promiscuité sur toutes les interfaces + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + Montrer et cacher des interfaces, ajouter des commentaires et gérer les canalisations et interfaces distantes. + + + Manage Interfaces… + Gestion des Interfaces… + + + Capture filter for selected interfaces: + Filtre de capture pour les interfaces sélectionnées : + + + Compile BPFs + Compiler les BPFs + + + Output + Sortie + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>Entrez le nom du fichier où les données capturées seront enregistrés. Par défaut, un fichier temporaire sera utilisé.</p></body></html> + + + Capture to a permanent file + Capturer vers un fichier permanent + + + File: + Fichier : + + + Browse… + Parcourir… + + + Output format: + Format de sortie : + + + pcapng + pcapng + + + pcap + pcap + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>Au lieu d'utiliser un fichier de capture unique, plusieurs fichiers seront créés.</p><p>Les noms des fichiers générés contiendront un nombre croissant et le temps de départ de la capture.</p><p>NOTE : Si activé, au moins un des critères de nouveau fichier DOIT être sélectionné.</p></body></html> + + + Create a new file automatically… + Crée un nouveau fichier automatiquement… + + + after + après + + + Switch to the next file after the specified number of packets have been captured. + Passe au fichier suivant après avoir capturé le nombre de paquets indiqué. + + + packets + paquets + + + Switch to the next file after the file size exceeds the specified file size. + Passe au fichier suivant après avoir dépassé la taille de fichier indiqué. + + + kilobytes + kilo-octets + + + megabytes + méga-octets + + + gigabytes + giga-octets + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + Passe au fichier suivant après avoir dépassé le temps de capture indiqué. + + + seconds + secondes + + + minutes + minutes + + + hours + heures + + + when time is a multiple of + quand le temps est un multiple de + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + Passe au fichier suivant quand le temps (de l'horloge murale) est un multiple pair de l'intervale indiqué. +Pas exemple, inquiquez 1 heure pour avoir un nouveau fichier créé toutes les heures à l'heure. + + + compression + compression + + + None + Aucun + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>Après basculement de la capture vers le fichier suivant, si le nombre de fichiers indiqué est dépassé, le fichier le plus ancien sera supprimé.</p></body></html> + + + Use a ring buffer with + Utiliser un tampon circulaire avec + + + files + fichiers + + + Options + Options + + + Display Options + Option d'affichage + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>Cette option permet d'afficher les paquets capturés immédiatement à l'écran principal. Note : cela ralentira la capture, donc des pertes de paquets pourraient apparaître.</p></body></html> + + + Update list of packets in real-time + Mettre à jour la liste des paquets en temps en réel + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>Cela va faire défiler la &quot;Liste des Paquets&quot; automatiquement jusqu'au dernier paquet capturé, quand l'option &quot;Mettre à jour la liste des paquets en temps réél&quot; est utilisé.</p></body></html> + + + Automatically scroll during live capture + Défilement automatique pendant la capture + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>Montre la boite d'information de capture pendant la capture.</p></body></html> + + + Show capture information during live capture + Afficher les informations de capture pendant la capture + + + Name Resolution + Résolution de nom + + + Perform MAC layer name resolution while capturing. + Effectuer la résolution de nom sur la couche MAC pendant la capture. + + + Resolve MAC addresses + Résoudre les adresses MAC + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>Effectue la résolution de nom de la couche réseau durant la capture.</p></body></html> + + + Resolve network names + Résoudre les noms réseaux + + + Perform transport layer name resolution while capturing. + Effectuer la résolution de nom sur la couche transport pendant la capture. + + + Resolve transport names + Résoudre les noms de ports + + + Stop capture automatically after… + Arrêter la capture automatiquement après… + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>Arrête la capture après avoir capturé le nombre de paquets indiqués.</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + Arrête la capture après avoir capturé le nombre de paquets indiqué. + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>Arrête la capture après avoir créé le nombre de fichiers indiqués.</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>Arrêter la capture après avoir capturé le volume de données indiqué.</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + Arrête la capture après avoir capturé le volume de données indiqué. + + + Stop capturing after the specified amount of time has passed. + Arrête la capture à la fin du temps défini. + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>Spécifier éventuellement un répertoire temporaire pour les fichiers de capture sans nom.</p></body></html> + + + Directory for temporary files + Répertoire des fichiers temporaires + + + Capture Options + Options de Capture + + + Start + Démarrer + + + Leave blank to use a temporary file + Laisser vide pour utiliser un fichier temporaire + + + Specify a Capture File + Préciser un fichier de capture + + + Specify temporary directory + Spécifier le répertoire temporaire + + + %1: %2 + %1 : %2 + + + Addresses + Adresses + + + Address + Adresse + + + no addresses + pas d'adresses + + + Error + Erreur + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + Fichiers multiples : la taille demandée est trop élevée. La taille d'un fichier ne doit pas dépasser 2 Gio. + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + Fichiers multiples : pas de nom de fichier de capture précisé. Vous devez préciser un nom de fichier pour utiliser les fichiers multiples. + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + Fichiers multiples : Pas de limite de fichiers précisé. Vous devez préciser une taille, un intervalle ou un nombre de paquet pour chaque fichier. + + + + CapturePreferencesFrame + + Frame + Trame + + + Default interface + Interface par défaut + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Vous voulez sans doute pour activer cela. Habituellement, une carte réseau ne capture que le trafic envoyé à sa propre adresse réseau. Si vous voulez capturer tout le trafic que la carte réseau peut recevoi», cochez cette case. Voir la FAQ pour plus de détails sur la capture des paquets à partir d'une commutation</p></body></html> + + + Capture packets in promiscuous mode + Capture de Paquets en mode promiscuous + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Paquets de capture dans le nouveau format (pcap-ng) de fichier de capture.</p></body></html> + + + Capture packets in pcapng format + Capture de paquet au format pcapng + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Mettre à jour la liste des paquets tandis que la capture est en cours. Cela peut entraîner la perte de paquets sur les réseaux à grande vitesse.</p></body></html> + + + Update list of packets in real time + Mettre la liste de paquet à jour en temps réel + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + Ne pas charger les interface au démarrage + + + Disable external capture interfaces + Désactiver les interfaces de capture externes + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + le caractère "@" sera ignoré. + + + + ColoringRulesDialog + + Dialog + Dialogue + + + <small><i>A hint.</i></small> + <small><i>Un indice.</i></small> + + + Add a new coloring rule. + Ajouter une règle de coloration. + + + Delete this coloring rule. + Supprimer cette règle de coloration. + + + Duplicate this coloring rule. + Dupliquer cette règle de coloration. + + + Clear all coloring rules. + Effacer toutes les règles de coloration. + + + Set the foreground color for this rule. + Définir la couleur de police. + + + Foreground + Couleur de police + + + Set the background color for this rule. + Définir la couleur d'arrière-plan. + + + Background + Arrière-plan + + + Set the display filter using this rule. + Définir le filtre d'affichage avec cette règle. + + + Apply as filter + Appliquer comme filtre + + + Select a file and add its filters to the end of the list. + Sélectionner un fichier et ajouter ses filtres à la fin de la liste. + + + Save filters in a file. + Sauvegarder les filtres dans un fichier. + + + Coloring Rules %1 + Règles de coloration %1 + + + Import… + Importer… + + + Export… + Exporter… + + + Copy coloring rules from another profile. + Copier les règles de coloration depuis un autre profil. + + + Open + Ouvrir + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Double-cliquer pour éditer. Glisser pour déplacer. Les règles sont traitées dans l'ordre jusqu'à trouver une correspondance. + + + Import Coloring Rules + Importer des règles de coloration + + + Export %1 Coloring Rules + Exporter %1 règles de coloration + + + + ColoringRulesModel + + New coloring rule + Nouvelle règle de coloration + + + Unable to save coloring rules: %1 + Impossible d'enregistrer les règles de coloration %1 + + + Name + Nom + + + Filter + Filtre + + + + ColumnEditorFrame + + Frame + Trame + + + Title: + Title + Titre : + + + Type: + Type + Type : + + + Fields: + Fields + Champs : + + + Occurrence: + Occurrence + Occurrence : + + + Resolve Names: + Résoudre les noms : + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + <html><head/><p>Afficher des chaînes lisibles par l'homme au lieu de valeurs brutes pour les champs. Applicable uniquement aux colonnes personnalisées avec des champs contenant des chaînes de valeur.</body> + + + Missing fields. + Champs manquants. + + + Invalid fields. + Champs invalides. + + + Invalid occurrence value. + Valeur d'occurence invalide. + + + + ColumnListModel + + Displayed + Affichés + + + Title + Titre + + + Type + Type + + + Fields + Champs + + + Field Occurrence + Occurrence d'un champ + + + Resolved + Résolu + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + <html>Afficher des chaînes lisibles par l'homme au lieu de valeurs brutes pour les champs. Applicable uniquement aux colonnes personnalisées avec des champs contenant des chaînes de valeur. + + + New Column + Nouvelle colonne + + + + ColumnPreferencesFrame + + Frame + Trame + + + Add a new column + Ajouter une nouvelle colonne + + + Delete selected column + Supprimer la colonne sélectionnée + + + Show displayed columns only + Montrer uniquement les colonnes affichées + + + Reset all changes + Réinitialiser toutes les modifications + + + + CompiledFilterOutput + + Compiled Filter Output + Sortie de filtre compilée + + + Copy + Copier + + + Copy filter text to the clipboard. + Copier le texte du filtre vers le bloc-notes + + + + ConversationDataModel + + Address A + Adresse A + + + Port A + Port A + + + Address B + Adresse B + + + Port B + Port B + + + Packets + Paquets + + + Bytes + Octets + + + Stream ID + ID de flux + + + Packets A + Paquets A + + + Bytes A + Octets A + + + Packets B + + + + Bytes B + Octets B + + + Abs Start + Début Abs + + + Rel Start + Début Rel + + + Duration + Durée + + + Bits/s A + Bits/s A + + + Bits/s B + Bits/s B + + + Total Packets + Paquets totaux + + + Percent Filtered + Pourcentage filtré + + + + ConversationDialog + + Follow Stream… + Suivre le flux… + + + Follow a TCP or UDP stream. + Afficher un flux TCP ou UDP. + + + Graph… + Graphique… + + + Graph a TCP conversation. + Décrire graphiquement une conversation TCP + + + + ConversationHashTablesDialog + + Dialog + Dialogue + + + Conversation Hash Tables + Tables des hachages de la conversation + + + + CopyFromProfileButton + + Copy from + Copier à partir de + + + Copy entries from another profile. + Copier les entrées depuis un autre profil. + + + System default + Valeur par défaut du système + + + + CredentialsDialog + + Wireshark - Credentials + Wireshark - Identifiants + + + Credentials + Identifiants + + + + CredentialsModel + + Click to select the packet + Cliquez pour sélectionner le paquet + + + Click to select the packet with username + Cliquez pour sélectionner le paquet avec nom d'utilisateur + + + Username not available + Nom d'utilisateur non disponible + + + Packet No. + N° de paquet + + + Protocol + Protocole + + + Username + Nom d'utilisateur + + + Additional Info + Information complémentaire + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + Copier les octets au format Hex + ASCII Dump + + + Copy packet bytes as a hex and ASCII dump. + Copier les octets du paquet sous forme de vidage Hex et ASCII. + + + …as Hex Dump + …comme vidage Hex + + + Copy packet bytes as a hex dump. + Copier les octets du paquet sous forme de vidage Hex. + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + …sous forme de flux Hex + + + Copy packet bytes as a stream of hex. + Copier les octets du paquet sous forme de flux Hex. + + + …as a Base64 String + + + + Copy packet bytes as a base64 encoded string. + + + + Copy packet bytes as application/octet-stream MIME data. + Copier les octets du paquet en tant que données MIME d'application/de flux d'octets. + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + Modifier le comportement de dissection d'un protocole. + + + Remove this dissection behavior. + Supprimer la dissection. + + + Copy this dissection behavior. + Copier la dissection. + + + Clear all dissection behaviors. + Supprimer toutes les dissections. + + + Decode As… + Décoder comme… + + + Open + Ouvrir + + + + DecodeAsModel + + Match using this field + Faire correspondre en utilisant ce champ + + + Change behavior when the field matches this value + Modifier le comportement lorsque le champ correspond à cette valeur + + + Field value type (and base, if Integer) + Type de valeur de champ (et base, si entier) + + + Current"Decode As" behavior + Comportement "Décoder comme" actuel + + + Default "Decode As" behavior + Comportement "Décoder comme" par défaut + + + String + Chaine de Caractères + + + Integer, base + Nombre entier, base + + + unknown + inconnu + + + <none> + <aucun> + + + GUID + GUID + + + Field + Champ + + + Value + Valeur + + + Type + Type + + + Default + Défaut + + + Current + Actuel + + + + DisplayFilterCombo + + Display filter selector + Sélecteur de filtre d'affichage + + + Select from previously used filters. + Sélectionnez parmi les filtres utilisés précédemment. + + + + DisplayFilterEdit + + Display filter entry + Entrée du filtre d'affichage + + + Manage saved bookmarks. + Gérer les signets sauvegardés. + + + Display Filter Expression… + Afficher l'expression du filtre… + + + Apply a display filter %1 <%2/> + Appliquer un filtre d'affichage %1 <%2/> + + + Enter a display filter %1 + Entrer un filtre d'affichage %1 + + + Clear display filter + Effacer le filtre d'affichage + + + Apply display filter + Appliquer le filtre d'affichage + + + Left align buttons + Boutons alignés à gauche + + + Apply a read filter %1 + Appliquer un filtre de lecture %1 + + + Current filter: %1 + Filtre actuel : %1 + + + Invalid filter: + Filtre invalide : + + + Save this filter + Sauvegarder ce filtre + + + Remove this filter + Supprimer ce filtre + + + Manage Display Filters + Gérer les filtres d'affichage + + + Filter Button Preferences... + Préférences du bouton de filtre... + + + + DisplayFilterExpressionDialog + + Dialog + Dialogue + + + Select a field to start building a display filter. + Sélectionner un champ pour commencer à construire un filtre d'affichage + + + Field Name + Nom du champ + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Chercher dans la liste des noms de champ.</p></body></html> + + + Search: + Recherche : + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>Les relations peuvent être utilisées pour limiter les champs à des valeurs spécifiques. Chaque relation effectue les opérations suivantes :</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellpacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">est présente</span></p></td><td><p>Correspond à n'importe quel paquet contenant ce champ</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Comparer le champ à une valeur spécifique.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contient, correspond à</span></p></td><td><p>Vérifier le champ par rapport à une chaîne (contient) ou à une expression régulière (correspond à)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Comparer le champ à un ensemble spécifique de valeurs</p></td></tr></table></body></html> + + + Relation + Relation + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + + + + Quantifier + + + + Any + Tout + + + All + Tout + + + Match against this value. + Correspond à cette valeur + + + Value + Valeur + + + If the field you have selected has a known set of valid values they will be listed here. + Si le champ que vous avez sélectionné a un ensemble connu de valeurs valides elles seront listées ici + + + Predefined Values + Valeurs prédéfinies + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + Si le champ que vous avez sélectionné couvre une série d'octets (p.ex. vous avez sélectionné un protocole) vous pouvez limiter la correspondance à une série d'octets ici. + + + Range (offset:length) + Série (offset:longueur) + + + No display filter + Pas de filtre d'affichage. + + + <small><i>A hint.</i></small> + <small><i>Un indice.</i></small> + + + Display Filter Expression + Affiche Expression de Filtre + + + Select a field name to get started + Sélectionner un nom de champ pour commencer + + + Click OK to insert this filter + Cliquer OK pour insérer ce filtre + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + Dialogue + + + Search: + Recherche : + + + Dissector Tables + Tables de dissecteur + + + + DissectorTablesProxyModel + + Table Type + Type de Tableau + + + String + Chaine de Caractères + + + Dissector Description + + + + Integer + Nombre entier + + + Protocol + Protocole + + + Short Name + Nom Court + + + Table Name + Nom du Tableau + + + Selector Name + Nom du Sélecteur + + + + EnabledProtocolsDialog + + Dialog + Dialogue + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>Désactiver un protocole empêche les protocoles de couches supérieures d'être affichés</i></small> + + + Search: + Recherche : + + + in + dans + + + Enable All + Activer tout + + + Disable All + Désactiver tout + + + Invert + Inverser + + + Enabled Protocols + Protocoles activés + + + Everywhere + Partout + + + Only Protocols + Uniquement les protocoles + + + Only Description + Uniquement les descriptions + + + Only enabled protocols + Uniquement les protocoles activés + + + Only disabled protocols + Uniquement les protocoles désactivés + + + any protocol + tout protocole + + + non-heuristic protocols + protocoles non-heuristiques + + + heuristic protocols + protocoles heuristiques + + + + EnabledProtocolsModel + + Protocol + Protocole + + + Description + Description + + + + EndpointDataModel + + Address + Adresse + + + Port + Port + + + Packets + Paquets + + + Bytes + Octets + + + Tx Packets + Paquets Tx + + + Tx Bytes + Octets Tx + + + Rx Packets + Paquets reçus + + + Rx Bytes + Octets reçus + + + Country + Pays + + + City + Ville + + + Latitude + + + + Longitude + + + + AS Number + Numéro AS + + + AS Organization + Organisation AS + + + Total Packets + Paquets totaux + + + Percent Filtered + Pourcentage filtré + + + + EndpointDialog + + Map + Carte + + + Draw IPv4 or IPv6 endpoints on a map. + Dessine les points de terminaison IPv4 ou IPv6 sur une carte. + + + Open in browser + Ouvrir dans un navigateur + + + Save As… + Enregistrer sous… + + + Map file error + Erreur du fichier de cartographie + + + Save Endpoints Map + Sauvegarder la carte des points de terminaison + + + Failed to save map file %1. + Échec de sauvegarde du fichier de cartographie %1. + + + + EthernetAddressModel + + Type + Type + + + Name + Nom + + + Address + Adresse + + + All entries + Toutes les entrées + + + Hosts + Hôtes + + + Ethernet Addresses + Adresses Ethernet + + + Ethernet Manufacturers + Fabricants Ethernet + + + Ethernet Well-Known Addresses + Adresses Ethernet bien connues + + + + ExpertInfoDialog + + Dialog + Dialogue + + + <small><i>A hint.</i></small> + <small><i>Un indice.</i></small> + + + Limit to Display Filter + Limiter au Filtre d'Affichage + + + Group by summary + Regrouper par résumé + + + Search expert summaries. + Cherche dans les résumés expert. + + + Search: + Recherche : + + + Show… + Show... + Afficher... + + + Error + Erreur + + + Show error packets. + Afficher les paquets d'erreur. + + + Warning + Avertissement + + + Show warning packets. + Afficher les paquets d'avertissement. + + + Note + Note + + + Show note packets. + Afficher les paquets de note. + + + Chat + Chat + + + Show chat packets. + Afficher les paquets du chat. + + + Comment + Commentaire + + + Show comment packets. + Montre paquets de commentaires + + + Expert Information + Information Expert + + + Collapse All + Réduire Tout + + + Expand All + Étendre Tout + + + Capture file closed. + Fichier de capture fermé + + + No display filter + Pas de filtre d'affichage. + + + No display filter set. + Filtre d'affichage non défini. + + + Limit information to "%1". + Limiter l'information à "%1". + + + Display filter: "%1" + Filtre d'affichage : "%1" + + + + ExpertInfoProxyModel + + Packet + Paquet + + + Severity + Sévérité + + + Summary + Résumé + + + Group + Groupe + + + Protocol + Protocole + + + Count + Compter + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Exporter analyse des paquets + + + Export As: + Export as: + Exporter comme : + + + Plain text (*.txt) + Texte (*.txt) + + + Comma Separated Values - summary (*.csv) + CSV Séparateur: point-virgule) (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML - résumé (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PDML - détails (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + Tableau C - octets (*.c, .h) + + + + ExportObjectDialog + + Dialog + Dialogue + + + Content Type: + Type de contenu : + + + Searching for objects + Chercher les objects + + + Text Filter: + Filtre de texte : + + + Only display entries containing this string + Afficher uniquement les entrées contenant cette chaine de caractères + + + Preview + Prévisualisation + + + All Content-Types + Tous les types-de-contenu + + + Export + Exporter + + + %1 object list + Liste d'objets %1 + + + Save Object As… + Enregistrer l'objet sous… + + + Save All Objects In… + Enregistrer tous les objets dans… + + + + ExportObjectModel + + Packet + Paquet + + + Hostname + Nom d'hôte + + + Content Type + Type de contenu + + + Size + Taille + + + Filename + Nom du fichier + + + + ExportPDUDialog + + Dialog + Dialogue + + + Display filter: + Filtre d'affichage : + + + + ExtArgSelector + + Reload data + Recharger les données + + + + ExtcapArgumentFileSelection + + Clear + Vider + + + All Files ( + Tous les Fichiers ( + + + Open File + Ouvrir un fichier + + + Select File + Sélectionner un fichier + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + Options d'interface + + + Start + Démarrer + + + Save + Sauvegarder + + + Default + Défaut + + + Restore default value of the item + Restaurer la valeur par défaut de l'élément + + + Extcap Help cannot be found + L'aide Extcap est introuvable + + + The help for the extcap interface %1 cannot be found. Given file: %2 + L'aide pour cette interface extcap %1 est introuvable. Fichier concerné : %2 + + + Save parameter(s) on capture start + Enregistrer le paramètre au début de la capture + + + + FieldFilterEdit + + Display filter entry + Afficher l'entrée du filtre + + + Enter a field %1 + Saisir un champ %1 + + + Invalid filter: + Filtre invalide : + + + + FileSetDialog + + Dialog + Dialogue + + + Directory: + Dossier : + + + No files in Set + Pas de fichier dans le lot + + + No capture loaded + Aucune capture chargée + + + %Ln File(s) in Set + %1 File%2 in Set + + + + + + + + FilesetEntryModel + + Open this capture file + Ouvrir ce fichier de capture + + + Filename + Nom de fichier + + + Created + Établi + + + Modified + Modifié + + + Size + Taille + + + + FilterAction + + Selected + Sélectionné + + + Not Selected + Non Sélectionné + + + …and Selected + …et Sélectionné + + + …or Selected + …ou Sélectionné + + + …and not Selected + …et non Sélectionné + + + …or not Selected + …ou non Sélectionné + + + + FilterDialog + + Dialog + Dialogue + + + Create a new filter. + Créer un nouveau filtre. + + + Remove this filter. + Remove this profile. + Supprimer ce filtre. + + + Copy this filter. + Copy this profile. + Copier ce filtre. + + + Capture Filters + Filtres de capture + + + Display Filters + Filtres d'affichage + + + Open + Ouvrir + + + New capture filter + This text is automatically filled in when a new filter is created + Nouveau filtre de capture + + + New display filter + This text is automatically filled in when a new filter is created + Nouveau filtre d'affichage + + + + FilterExpressionFrame + + Frame + Trame + + + Filter Buttons Preferences… + Préférences des boutons de filtre… + + + Label: + Étiquette : + + + Enter a description for the filter button + Entrer une description pour le bouton de filtre + + + Filter: + Filtre : + + + Enter a filter expression to be applied + Entrer une expression de filtre à appliquer + + + Comment: + Commentaire : + + + Enter a comment for the filter button + Entrer un commentaire pour le bouton de filtre + + + Missing label. + Étiquette manquante. + + + Missing filter expression. + Expression de filtre manquante. + + + Invalid filter expression. + Expression de filtre non valide. + + + + FilterExpressionToolBar + + Filter Button Preferences... + Préférences du bouton de filtre... + + + Edit + Éditer + + + Disable + Désactiver + + + Remove + Retirer + + + + FilterListModel + + Filter Name + Nom du filtre + + + Filter Expression + Expression de filtre + + + + FindLineEdit + + Textual Find + Recherche textuelle + + + Regular Expression Find + Recherche d'expressions régulières + + + + FirewallRulesDialog + + Create rules for + Créer des règles pour + + + Inbound + Entrant + + + Deny + Refuser + + + Firewall ACL Rules + Règles ACL du pare-feu + + + Copy + Copie + + + IPv4 source address. + Adresse source IPv4. + + + IPv4 destination address. + Adresse de destination IPv4. + + + Source port. + Port source. + + + Destination port. + Le port de destination. + + + IPv4 source address and port. + Adresse source et port IPv4. + + + IPv4 destination address and port. + Adresse et port de destination IPv4. + + + MAC source address. + Adresse source MAC. + + + MAC destination address. + Adresse de destination MAC. + + + Text file (*.txt);;All Files ( + Fichier texte (*.txt);;Tous les fichiers ( + + + Warning + Avertissement + + + Unable to save %1 + Impossible d'enregistrer %1 + + + + FolderListModel + + "File" dialogs + "Fichier" dialogues + + + capture files + capturer des fichiers + + + Temp + Temp + + + untitled capture files + fichiers de capture sans titre + + + Personal configuration + Paramétrage personnel + + + Global configuration + Configuration globale + + + dfilters, preferences, ethers, … + dfiltres, préférences, éthers, … + + + dfilters, preferences, manuf, … + filtres, préférences, fabrication, … + + + System + Système + + + ethers, ipxnets + éthers, IPXnets + + + Program + Programme + + + program files + fichiers de programme + + + Personal Plugins + Plugins personnels + + + binary plugins + plugins binaires + + + Global Plugins + Plugins globaux + + + Personal Lua Plugins + Plugins Lua personnels + + + Global Lua Plugins + Plugins Lua globaux + + + Lua scripts + + + + Personal Extcap path + Chemin d'accès Extcap personnel + + + external capture (extcap) plugins + + + + Global Extcap path + Chemin d'accès global à l'extcap + + + MaxMind DB path + Chemin de la base de données MaxMind + + + MaxMind DB database search path + Chemin de recherche de la base de données MaxMind DB + + + MIB/PIB path + Chemin MIB/PIB + + + SMI MIB/PIB search path + Chemin de recherche SMI MIB/PIB + + + macOS Extras + Suppléments macOS + + + Extra macOS packages + Forfaits macOS supplémentaires + + + Name + Nom + + + Location + Emplacement + + + Typical Files + Fichiers typiques + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + Filtrer ce flux + + + Print + Imprimer + + + ASCII + ASCII + + + C Arrays + Tableaux C + + + EBCDIC + EBCDIC + + + Hex Dump + Hexdump + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + Brut + + + Save as… + Enregistrer sous… + + + Back + Retour + + + Packet %1. + Paquet %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + + + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + + + + + + %Ln turn(s). + + + + + + + Click to select. + Cliquez pour sélectionner + + + Regex Find: + Trouver par Regex : + + + No capture file. + Pas de fichier de capture. + + + Please make sure you have a capture file opened. + S'il vous plaît assurez-vous que vous avez un fichier de capture ouvert. + + + Error following stream. + Erreur pour suivre le flux. + + + Capture file invalid. + Fichier de capture invalide. + + + Please make sure you have a %1 packet selected. + Assurez-vous d'avoir sélectionné un paquet %1. + + + %1 stream not found on the selected packet. + + + + Entire conversation (%1) + Conversation entière (%1) + + + Follow %1 Stream (%2) + Suivre le flux %1 (%2) + + + Error creating filter for this stream. + Erreur pour la création du filtre pour ce flux. + + + Save Stream Content As… + Enregistrer le contenu du flux sous… + + + [Stream output truncated] + [Sortie du flux tronquée] + + + %Ln total stream(s). + + + + + + + Max sub stream ID for the selected stream: %Ln + + + + + + + File closed. + Fichier fermé. + + + Follow Stream + Suivre flux + + + Hint. + Astuce. + + + Show data as + Show and save data as + Afficher les données comme + + + Stream + Flux + + + Substream + Sous-flux + + + Find: + Trouver : + + + Find &Next + Trouver Suiva&nt + + + + FontColorPreferencesFrame + + Frame + Trame + + + Main window font: + Police de la fenêtre principale : + + + Select Font + Selectionner police + + + Colors: + Couleurs : + + + System Default + Défaut du système + + + Solid + Solide + + + Sample ignored packet text + Exemple de texte de paquets ignorés + + + Sample marked packet text + Exemple de texte de paquets marqués + + + Sample active selected item + Exemple d'élément sélectionné actif + + + Style: + Style : + + + Gradient + Pente + + + Sample inactive selected item + Exemple d'élément sélectionné inactif + + + Sample "Follow Stream" client text + Exemple de texte de suivi de flux (coté client) + + + Sample "Follow Stream" server text + Exemple de texte de suivi de flux (coté serveur) + + + Sample valid filter + Exemple filtre valide + + + Sample invalid filter + Exemple filtre invalide + + + Sample warning filter + Sample deprecated filter + Exemple de filtre d'avertissement + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + Les exemples de paquets de requête GIF ont des tailles de fenêtre jumbo + + + Lazy badgers move unique waxy jellyfish packets + Les blaireaux paresseux déplacent des paquets uniques de méduses cireuses + + + Font + Police d'écriture + + + + FunnelStringDialog + + Dialog + Dialogue + + + + FunnelTextDialog + + Dialog + Dialogue + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>Introduisez du texte ou une expression régulière. Ça sera surligné au-dessus.</p></body></html> + + + Highlight: + Surligner : + + + + GsmMapSummaryDialog + + Dialog + Dialogue + + + GSM MAP Summary + Sommaire GSM MAP + + + File + Fichier + + + Name + Nom + + + Length + Longueur + + + Format + Format + + + Snapshot length + Taille Snapshot + + + Data + Données + + + First packet + Premier paquet + + + Last packet + Dernier paquet + + + Elapsed + Temps écoulé + + + Packets + Paquets + + + Invokes + Invokes + + + Total number of Invokes + Nombre total d'Invokes + + + Average number of Invokes per second + Nombre moyen d'Invokes par seconde + + + Total number of bytes for Invokes + Nombre total d'octets pour les Invokes + + + Average number of bytes per Invoke + Nombre moyen d'octets par Invoke + + + Return Results + Résultats Retournés + + + Total number of Return Results + Nombre total de Résultats Retournés + + + Average number of Return Results per second + Nombre moyen de Résultats Retournés par seconde + + + Total number of bytes for Return Results + Nombre total d'octets pour les Résultats Retournés + + + Average number of bytes per Return Result + Nombre moyen d'octets par Résultat Retourné + + + Totals + Totaux + + + Total number of GSM MAP messages + Nombre total de messages GSM MAP + + + Average number of GSM MAP messages per second + Moyenne du nombre de messages GSM MAP par secondes + + + Total number of bytes for GSM MAP messages + Nombre total d'octets pour les messages GSM MAP + + + Average number of bytes per GSM MAP message + Moyenne du nombre d'octets par message GSM MAP + + + + IOConsoleDialog + + Dialog + Dialogue + + + Enter code + + + + Evaluate + + + + Clear + Vider + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + Dialogue + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Raccourcis-clavier précieux pour gains de temps surprenants</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom avant</td></th> +<tr><th>-</th><td>Zoom arrière</td></th> +<tr><th>x</th><td>Zoom avant axe X</td></th> +<tr><th>X</th><td>Zoom arrière axe X</td></th> +<tr><th>y</th><td>Zoom avant axe Y</td></th> +<tr><th>Y</th><td>Zoom arrière axe Y</td></th> +<tr><th>0</th><td>Réinitialise le graphique à son état initial</td></th> + +<tr><th>→</th><td>Droite 10 pixels</td></th> +<tr><th>←</th><td>Gauche 10 pixels</td></th> +<tr><th>↑</th><td>Haut 10 pixels</td></th> +<tr><th>↓</th><td>Bas 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Droite 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Gauche 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Haut 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Bas 1 pixel</td></th> +</th> + +<tr><th>g</th><td>Aller (go) au paquet sous le curseur</td></th> + +<tr><th>z</th><td>Bascule trainée souris / zoom</td></th> +<tr><th>t</th><td>Bascule origine du temps capture / session</td></th> +<tr><th>Espace</th><td>Basculer réticule</td></th> + + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + Enlever ce graphique. + + + Add a new graph. + Ajouter un nouveau graphique. + + + Duplicate this graph. + Dupliquer ce graphique. + + + Clear all graphs. + Effacer tous les graphiques. + + + Move this graph upwards. + + + + Move this graph downwards. + + + + Mouse + Souris + + + Drag using the mouse button. + Faites glisser à l'aide du bouton de la souris. + + + drags + Glissement + + + Select using the mouse button. + Sélectionner à l'aide du bouton de la souris. + + + zooms + Zoom + + + Interval + Intervale + + + Time of day + Heure du jour + + + Log scale + échelle logarithmique + + + Automatic update + + + + Enable legend + + + + Reset + Réinitialiser + + + Reset Graph + Réinitialiser le Graphique + + + Reset the graph to its initial state. + Réinitialiser le graphique à son état initial. + + + 0 + 0 + + + Zoom In + Zoom Avant + + + + + + + + + Zoom Out + Zoom arrière + + + - + - + + + Move Up 10 Pixels + Monter de 10 Pixels + + + Up + Monter + + + Move Left 10 Pixels + Déplacer à Gauche de 10 Pixels + + + Left + Gauche + + + Move Right 10 Pixels + Déplacer à Droite de 10 Pixels + + + Right + Droite + + + Move Down 10 Pixels + Descendre de 10 Pixels + + + Down + Descendre + + + Move Up 1 Pixel + Monter d'1 Pixel + + + Shift+Up + Maj+Haut + + + Move Left 1 Pixel + Déplacer à Gauche d'1 Pixel + + + Shift+Left + Maj+Gauche + + + Move Right 1 Pixel + Déplacer à Droite d'1 Pixel + + + Shift+Right + Maj+Droite + + + Move Down 1 Pixel + Descendre d'1 Pixel + + + Move down 1 Pixel + Move down 1 pixel + Descendre d'1 pixel + + + Shift+Down + Maj+Bas + + + Go To Packet Under Cursor + Aller au paquet sous le curseur + + + Go to packet currently under the cursor + Aller au paquet actuellement sous le curseur + + + G + G + + + Drag / Zoom + Glisser / Zoom + + + Toggle mouse drag / zoom behavior + Basculer comportant de la souris glisser / zoom + + + Z + Z + + + Capture / Session Time Origin + Capture / Temps de Session d'origine + + + Toggle capture / session time origin + Basculer du temps d'origine capture / session + + + T + T + + + Crosshairs + Réticule + + + Toggle crosshairs + Basculer réticule + + + Space + Espace + + + Zoom In X Axis + Zoom avant Axe X + + + X + X + + + Zoom Out X Axis + Zoom arrière Axe X + + + Shift+X + Majuscule+X + + + Zoom In Y Axis + Zoom avant Axe Y + + + Y + Y + + + Zoom Out Y Axis + Zoom arrière Axe Y + + + Shift+Y + Majuscule+Y + + + 1 sec + 1 sec + + + 10 sec + 10 sec + + + 1 min + 1 min + + + 10 min + 10 min + + + Time (s) + Temps (s) + + + I/O Graphs + Graphiques d'E/S + + + Save As… + Enregistrer sous… + + + Copy + Copier + + + Copy graphs from another profile. + Copier les graphiques d'un autre profil. + + + 1 ms + 1 ms + + + 2 ms + 2 ms + + + 5 ms + 5 ms + + + 10 ms + 10 ms + + + 20 ms + 20 ms + + + 50 ms + 50 ms + + + 100 ms + 100 ms + + + 200 ms + 200 ms + + + 500 ms + 500 ms + + + 2 sec + 2 sec + + + 5 sec + 5 s + + + Wireshark I/O Graphs: %1 + Graphiques E/S Wireshark : %1 + + + Filtered packets + Paquets filtrés + + + Filtered events + + + + All Packets + Tous les paquets + + + TCP Errors + Erreurs TCP + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + Survolez le graphique pour plus de détails. + + + No packets in interval + Aucun paquet dans l'intervale + + + No events in interval + + + + Click to select packet + Cliquez pour sélectionner le paquet + + + Packet + Paquet + + + Click to select event + + + + Event + Évènement + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Relâchez pour agrandir, x = %1 de %2, y = %3 de %4 + + + Unable to select range. + Impossible de sélectionner la plage. + + + Click to select a portion of the graph. + Cliquez pour sélectionner une partie du graphique. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Fichier JPEG (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + Valeurs séparées par des virgules (*.csv) + + + Save Graph As… + Enregistrer le graphique sous… + + + + Iax2AnalysisDialog + + Dialog + Dialogue + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Aller</span></p><p><span style=" font-size:medium; font-weight:600;">Retour</span></p></body></html> + + + Forward + Aller + + + Packet + Paquet + + + Delta (ms) + Delta(ms) + + + Jitter (ms) + Gigue(ms) + + + Bandwidth + Bande passante + + + Status + Etat + + + Length + Longueur + + + Reverse + Retour + + + Graph + Graphique + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>Affiche ou masque les valeurs de gigue aller.</p></body></html> + + + Forward Jitter + Gigue aller + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>Affiche ou masque les valeurs de différence aller.</p></body></html> + + + Forward Difference + Différence Aller + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>Affiche ou masque les valeurs de gigueretour.</p></body></html> + + + Reverse Jitter + Gigue Retour + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>Affiche ou masqe les valeurs de différences retour.</p></body></html> + + + Reverse Difference + Différence Retour + + + <small><i>A hint.</i></small> + <small><i>Un indice.</i></small> + + + Audio + Audio + + + Save the audio data for both channels. + Sauvegarder les données audio pour les deux canaux. + + + Forward Stream Audio + Flux Audio Aller + + + Save the forward stream audio data. + Sauvegarder les données audio du flux aller. + + + Reverse Stream Audio + Flux Audio Retour + + + Save the reverse stream audio data. + Sauvegarder les données de flux audio retour. + + + CSV + CSV + + + Save both tables as CSV. + Sauvegarder les deux tables en CSV. + + + Forward Stream CSV + Transférer le flux CSV + + + Save the forward table as CSV. + Sauvegarder la table aller en CSV. + + + Reverse Stream CSV + CSV Flux Retour + + + Save the reverse table as CSV. + Sauvegarder la table inverse en CSV. + + + Save Graph + Sauvegarder Graphique + + + Save the graph image. + Sauvegarder le graphique. + + + Go to Packet + Aller au Paquet + + + Select the corresponding packet in the packet list. + Sélectionner le paquet correspondant dans la liste des paquets. + + + G + G + + + Next Problem Packet + Paquet problématique suivant + + + Go to the next problem packet + Aller au prochain paquet problématique + + + N + N + + + IAX2 Stream Analysis + Analyse Flux IAX2 + + + Unable to save RTP data. + Impossible de sauvegarder les données RTP. + + + Please select an IAX2 packet. + Veuillez sélectionner un paquet IAX2. + + + G: Go to packet, N: Next problem packet + G : Aller au paquet, N : Paquet à problème suivant + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Fichier JPEG (*.jpeg *.jpg) + + + Save Graph As… + Enregistrer le graphique sous… + + + Can't save in a file: Wrong length of captured packets. + Ne peut être sauvegardé dans un fichier : longueur incorrecte des paquets capturés. + + + Can't save in a file: File I/O problem. + Ne peut être sauvegardé dans un fichier : problème d'E/S fichier. + + + Save forward stream audio + Sauvegarder flux audio aller + + + Save reverse stream audio + Sauvegarder flux audio retour + + + Save audio + Sauvegarder l'audio + + + Sun Audio (*.au) + Sun Audio (*.au) + + + ;;Raw (*.raw) + ;;Raw (*.raw) + + + Warning + Avertissement + + + Unable to save in that format + Impossible de sauvegarder dans ce format + + + Unable to save %1 + Impossible de sauvegarder %1 + + + Saving %1… + Enregistrement de %1… + + + Analyzing IAX2 + Analyse IAX2 + + + Save forward stream CSV + Sauvegarder CSV flux aller + + + Save reverse stream CSV + Sauvegarder CSV flux retour + + + Save CSV + Sauvegarder le CSV + + + Comma-separated values (*.csv) + Comma-separated values (*.csv) + + + + ImportTextDialog + + File: + Fichier : + + + Set name of text file to import + Définir le nom du fichier à importer + + + Browse for text file to import + Parcourir le fichier à importer + + + Browse… + Browse... + Feuilleter... + + + Hex Dump + Vidage Hex + + + Import a standard hex dump as exported by Wireshark + Importer un vidage Hex standard tel qu'exporté par Wireshark + + + Offsets in the text file are in octal notation + Décalage dans le fichier texte est en notation octale + + + Octal + Octale + + + Offsets: + Décalages : + + + Offsets in the text file are in hexadecimal notation + Décalage dans le fichier texte est en notation hexadecimal + + + Hexadecimal + Hexadecimal + + + Offsets in the text file are in decimal notation + Décalage dans le fichier texte est en notation decimal + + + Decimal + Décimal + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>S'il faut effectuer un traitement supplémentaire en détectant le début de la représentation ASCII à la fin d'une ligne hexadécimale + ASCII même si elle ressemble à des octets hexadécimaux.</p><p>Ne pas activer si le vidage hexadécimal ne contient pas d'ASCII.</p></body></html> + + + ASCII identification: + Identification ASCII : + + + Regular Expression + Expression régulière + + + Import a file formatted according to a custom regular expression + Importer un fichier formaté selon une expression régulière personnalisée + + + Packet format regular expression + Expression régulière de format de paquet + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + <html><head/><body><p>Expression régulière compatible Perl capturant un seul paquet dans le fichier avec des groupes nommés identifiant les données à importer. Les ancres ^ et $ correspondent également avant/après les retours à la ligne </p><p>Required est uniquement un groupe de données, également pris en charge sont time, dir et seqno.</p><p>Flags Regex : DUPNAMES, MULTILINE et NOEMPTY</p></body></html> + + + This is regexHintLabel, it will be set to default_regex_hint + Ceci est regexHintLabel, il sera défini sur default_regex_hint + + + Data encoding: + Codage des données : + + + How data is encoded + Comment les données sont encodées + + + encodingRegexExample + encodingRegexExample + + + List of characters indicating incoming packets + Liste des caractères indiquant les paquets entrants + + + iI< + iI< + + + List of characters indicating outgoing packets + Liste des caractères indiquant les paquets sortants + + + oO> + oO> + + + Timestamp format: + Format horodatage : + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Que ce soit ou non le fichier contient des informations indiquant la direction (entrant ou sortant) du paquet. + + + Direction indication: + Indication de direction : + + + ExportPDU + ExportPDU + + + IP version: + Version IP : + + + Interface name: + Nom de l'interface : + + + The name of the interface to write to the import capture file + Le nom de l'interface à écrire dans le fichier de capture d'importation + + + Fake IF, Import from Hex Dump + Faux IF, Importer depuis Hex Dump + + + Maximum frame length: + Longueur maximale de la trame : + + + Encapsulation + Encapsulation + + + The text file has no offset + Le fichier texte n'a pas de décalage + + + None + Aucun + + + <small><i>recommended regex:</small></i> + <small><i>regex recommendée :</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + Le format dans lequel analyser les horodatages dans le fichier texte (par exemple %H:%M:%S.). Les spécificateurs de format sont basés sur strptime(3) + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + <html><head/><body><p>Format d'analyse des horodatages dans le fichier texte (par exemple %H:%M:%S.%f).</p><p>Les spécificateurs de format sont basés sur strptime(3) avec l'ajout de %f pour les secondes fractions. La précision de %f est déterminée à partir de sa longueur.</p></body></html> + + + %H:%M:%S.%f + %H:%M:%S.%f + + + timestampExampleLabel + timestampExampleLabel + + + Encapsulation Type: + Type d'Encapsulation : + + + Encapsulation type of the frames in the import capture file + Type d'encapsulation des trames dans le fichier de capture d'import + + + Prefix each frame with an Ethernet and IP header + Préfixer chaque trame avec un en-tête Ethernet et IP + + + IP + IP + + + Prefix each frame with an Ethernet, IP and UDP header + Préfixer chaque trame avec un en-tête Ethernet, IP et UDP + + + Prefix each frame with an Ethernet, IP and TCP header + Préfixer chaque trame avec un en-tête Ethernet, IP et TCP + + + Prefix each frame with an Ethernet, IP and SCTP header + Préfixer chaque trame avec un en-tête Ethernet, IP et SCTP + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + Préfixer chaque trame avec un en-tête Ethernet, IP et SCTP (DATA) + + + Source address: + Adresse d'origine : + + + Destination address: + Adresse de destination : + + + Dissector + Dissecteur + + + The IP protocol ID for each frame + L'ID de protocole IP pour chaque trame + + + The IP source address for each frame + L'adresse IP source de chaque trame + + + The IP destination address for each frame + L'adresse IP de destination de chaque trame + + + The UDP, TCP or SCTP source port for each frame + Le port source (UDP, TCP ou SCTP) pour chaque trame + + + The SCTP DATA payload protocol identifier for each frame + L'identifiant SCTP DATA payload pour chaque trame + + + The UDP, TCP or SCTP destination port for each frame + Le port destination (UDP, TCP or SCTP) pour chaque trame + + + Prefix each frame with an Ethernet header + Préfixe pour chaque trame avec un entête Ethernet + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + Protocole (dec) : + + + Leave frames unchanged + Laisser les trames inchangées + + + No dummy header + Aucune entête factice + + + Tag: + Balise : + + + UDP + UDP + + + Source port: + Port source : + + + The Ethertype value of each frame + La valeur Ethertype de chaque trame + + + TCP + TCP + + + The SCTP verification tag for each frame + La balise verification SCTP pour chaque trame + + + Destination port: + Port de destination : + + + Ethertype (hex): + Type ether (hex) : + + + SCTP (Data) + SCTP (Données) + + + The dissector to use for each frame + Le dissecteur à utiliser pour chaque trame + + + The IP Version to use for the dummy IP header + La version IP à utiliser pour l'en-tête IP factice + + + The maximum size of the frames to write to the import capture file (max 256kiB) + La taille maximale des trames à écrire dans le fichier de capture d'importation (max 256 ko) + + + Supported fields are data, dir, time, seqno + Les champs pris en charge sont data, dir, time, seqno + + + Missing capturing group data (use (? + Données de groupe de capture manquantes (utiliser (? + + + Import From Hex Dump + Importer Hexdump + + + Import + Importer + + + Import Text File + Importer Fichier Texte + + + + InterfaceFrame + + Frame + Trame + + + Wired + Filaire + + + AirPCAP + AirPCAP + + + Pipe + Tuyau + + + STDIN + STDIN + + + Bluetooth + Bluetooth + + + Wireless + Sans fil + + + Dial-Up + Accès commuté + + + USB + USB + + + External Capture + Capture externe + + + Virtual + Virtuel + + + Remote interfaces + Interfaces distantes + + + Show hidden interfaces + Afficher les interfaces cachées + + + External capture interfaces disabled. + Interfaces de capture externes désactivées. + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + <p>Les interfaces locales ne sont pas disponibles car aucun pilote de capture de paquets n'est installé.</p><p>Vous pouvez résoudre ce problème en installant <a href="https://npcap.com/"> Npcap</a>.</p> + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + <p>Les interfaces locales ne sont pas disponibles car le pilote de capture de paquets n'est pas chargé.</p><p>Vous pouvez résoudre ce problème en exécutant <pre>net start npcap</pre> si Npcap est installé ou <pre>net start npf</pre> si vous avez installé WinPcap. Les deux commandes doivent être exécutées en tant qu'administrateur.</p> + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + <p>Vous n'êtes pas autorisé à capturer sur les interfaces locales.</p><p>Vous pouvez résoudre ce problème en <a href="file://%1">installant ChmodBPF</a>.</p> + + + You don't have permission to capture on local interfaces. + Vous n'êtes pas autorisé à capturer sur les interfaces locales. + + + No interfaces found. + Aucune interface trouvée. + + + Interfaces not loaded (due to preference). Go to Capture + Interfaces non chargées (par préférence). Aller à Capturer + + + Start capture + Commencer la capture + + + Hide Interface + Masquer l'interface + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + Aucune interface à afficher. %1 interfaces masquées. + + + + InterfaceToolbar + + Frame + Trame + + + Select interface + Sélectionner l'interface + + + Interface + Interface + + + + InterfaceToolbarLineEdit + + Apply changes + Appliquer les modifications + + + + InterfaceTreeModel + + Show + Montrer + + + Friendly Name + Nom familier + + + Interface Name + Nom de l'interface + + + No interfaces found. + Aucune interface trouvée. + + + This version of Wireshark was built without packet capture support. + Cette version de Wireshark a été conçue sans prise en charge de la capture de paquets. + + + Local Pipe Path + Chemin de tuyau local + + + Comment + Commentaire + + + Link-Layer Header + En-tête de la couche de liaison + + + Promiscuous + promiscuité + + + Snaplen (B) + Snaplen (B) + + + Buffer (MB) + Tampon (Mo) + + + Monitor Mode + Mode moniteur + + + Capture Filter + Filtre de capture + + + Addresses + Adresses + + + Address + Adresse + + + Extcap interface: %1 + Interface d'extension : %1 + + + No addresses + Aucune adresse + + + No capture filter + Pas de filtre de capture + + + Capture filter + Filtre de capture + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + Statistiques de transport LBT-RM + + + Sources + Sources + + + Address/Transport + Adresse/Transport + + + Data frames + Trames de données + + + Data bytes + Octets de données + + + Data frames/bytes + Trames/octets de données + + + Data rate + Débit de données + + + RX data frames + Trames de données RX + + + RX data bytes + Octets de données RX + + + RX data frames/bytes + Trames/Octets de données RX + + + RX data rate + Débit de données RX + + + NCF frames + Trames NCF + + + NCF count + Compteur NCF + + + NCF bytes + Octets NCF + + + NCF frames/bytes + Trames/octets NCF + + + NCF count/bytes + Compteur/octets NCF + + + NCF frames/count + Trames/compteur NCF + + + NCF frames/count/bytes + Trames/compteur/octets NCF + + + NCF rate + Débit NCF + + + SM frames + Trames SM + + + SM bytes + Octets SM + + + SM frames/bytes + Trames/octets SM + + + SM rate + Débit SM + + + Show + Afficher + + + Data + Données + + + RX Data + Données RX + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + numéros de séquence pour transport + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Compteur + + + Frame + Trame + + + SQN/Reason + SQN/Raison + + + Receivers + Destinataires + + + NAK frames + Trames NAK + + + NAK count + Compteur NAK + + + NAK bytes + Octets NAK + + + NAK rate + Débit NAK + + + NAK sequence numbers for transport + numéros de séquence NAK pour transport + + + Display filter: + Filtre d'affichage: + + + Regenerate statistics using this display filter + Régénérer les statistiques en utilisant ce filtre d'affichage + + + Apply + Appliquer + + + Copy as CSV + Copier en tant que CSV + + + Copy the tree as CSV + Copier l'arbre en tant que CSV + + + Copy as YAML + Copier en tant que YAML + + + Copy the tree as YAML + Copier l'arbre en tant que YAML + + + Show the data frames column + Afficher la colonne des trames de données + + + Show the data bytes column + Afficher la colonne des octets de données + + + Show the data frames/bytes column + Afficher la colonne des trames/octets de données + + + Show the RX data frames column + Afficher la colonne des trames de données RX + + + Show the RX data bytes column + Afficher la colonne des octets de données RX + + + Show the RX data frames/bytes column + Afficher la colonne des trames/octets de données RX + + + Show the NCF frames column + Afficher la colonne des trames de données NCF + + + Show the NCF bytes column + Afficher la colonne des octets de données NCF + + + Show the NCF count column + Afficher la colonne des compteurs de données NCF + + + Show the data rate column + Afficher la colonne du débit de données + + + Show the RX data rate column + Afficher la colonne du débit de données RX + + + Show the NCF frames/bytes column + Afficher la colonne des trames/octets NCF + + + Show the NCF count/bytes column + Afficher la colonne des compteurs/octets NCF + + + Show the NCF frames/count column + Afficher la colonne des trames/compteurs NCF + + + Show the NCF frames/count/bytes column + Afficher la colonne des trames/compteurs/octets NCF + + + Show the NCF rate column + Afficher la colonne du débit NCF + + + Show the SM frames column + Afficher la colonne des trames SM + + + Show the SM bytes column + Afficher la colonne des octets SM + + + Show the SM frames/bytes column + Afficher la colonne des trames/octets SM + + + Show the SM rate column + Afficher la colonne du débit SM + + + Auto-resize columns to content + Ajuster automatiquement la taille des colonnes au contenu + + + Resize columns to content size + Ajuster la taille des colonnes au contenu + + + LBT-RM Statistics failed to attach to tap + Échec d'attache des Statistiques LBT-RM au tap + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + Statistiques de transport LBT-RU + + + Sources + Sources + + + Address/Transport/Client + Adresse/Transport/Client + + + Data frames + Trames de données + + + Data bytes + Octets de données + + + Data frames/bytes + Trames/octets de données + + + Data rate + Débit de données + + + RX data frames + Trames de données RX + + + RX data bytes + Octets de données RX + + + RX data frames/bytes + Trames/Octets de données RX + + + RX data rate + Débit de données RX + + + NCF frames + Trames NCF + + + NCF count + Compteur NCF + + + NCF bytes + Octets NCF + + + NCF frames/count + Trames/compteur NCF + + + NCF frames/bytes + Trames/octets NCF + + + NCF count/bytes + Compteur/octets NCF + + + NCF frames/count/bytes + Trames/compteur/octets NCF + + + NCF rate + Débit NCF + + + SM frames + Trames SM + + + SM bytes + Octets SM + + + SM frames/bytes + Trames/octets SM + + + SM rate + Débit SM + + + RST frames + Trames RST + + + RST bytes + Octets RST + + + RST frames/bytes + Trames/octets RST + + + RST rate + Débit RST + + + Show + Afficher + + + Data SQN + Données SQN + + + RX Data SQN + RX Données SQN + + + NCF SQN + NCF SQN + + + SM SQN + SM SQN + + + RST reason + Raison du RST + + + details for transport + détails du transport + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Compteur + + + Frame + Trame + + + Reason + Raison + + + SQN/Reason + SQN/Raison + + + Receivers + Destinataires + + + Address/Transport + Adresse/Transport + + + NAK frames + Trames NAK + + + NAK count + Compteur NAK + + + NAK bytes + Octets NAK + + + NAK frames/count + Trames/compteur NAK + + + NAK count/bytes + Compteur/octets NAK + + + NAK frames/bytes + Trames/octets NAK + + + NAK frames/count/bytes + Trames/compteur/octets NAK + + + NAK rate + Débit NAK + + + ACK frames + Trames ACK + + + ACK bytes + Octets ACK + + + ACK frames/bytes + Trames/octets ACK + + + ACK rate + Débit ACK + + + CREQ frames + Trames CREQ + + + CREQ bytes + Octets CREQ + + + CREQ frames/bytes + Trames/octets CREQ + + + CREQ rate + Débit CREQ + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + Requête CREQ + + + Display filter: + Filtre d'affichage : + + + Regenerate statistics using this display filter + Régénérer les statistiques en utilisant ce filtre d'affichage + + + Apply + Appliquer + + + Copy as CSV + Copier en tant que CSV + + + Copy the tree as CSV + Copier l'arbre en tant que CSV + + + Copy as YAML + Copier en tant que YAML + + + Copy the tree as YAML + Copier l'arbre en tant que YAML + + + Show the data frames column + Afficher la colonne des trames de données + + + Show the data bytes column + Afficher la colonne des octets de données + + + Show the data frames/bytes column + Afficher la colonne des trames/octets de données + + + Show the data rate column + Afficher la colonne du débit de données + + + Show the RX data frames column + Afficher la colonne des trames de données RX + + + Show the RX data bytes column + Afficher la colonne des octets de données RX + + + Show the RX data frames/bytes column + Afficher la colonne des trames/octets de données RX + + + Show the RX data rate column + Afficher la colonne du débit de données RX + + + Show the NCF frames column + Afficher la colonne des trames de données NCF + + + Show the NCF count column + Afficher la colonne des compteurs de données NCF + + + Show the NCF bytes column + Afficher la colonne des octets de données NCF + + + Show the NCF frames/bytes column + Afficher la colonne des trames/octets NCF + + + Show the NCF count/bytes column + Afficher la colonne des compteurs/octets NCF + + + Show the NCF frames/count column + Afficher la colonne des trames/compteurs NCF + + + Show the NCF frames/count/bytes column + Afficher la colonne des trames/compteurs/octets NCF + + + Show the SM frames column + Afficher la colonne des trames SM + + + Show the SM bytes column + Afficher la colonne des octets SM + + + Show the SM frames/bytes column + Afficher la colonne des trames/octets SM + + + Show the SM rate column + Afficher la colonne du débit SM + + + Show the RST frames column + Afficher la colonne des trames RST + + + Show the RST bytes column + Afficher la colonne des octets RST + + + Show the RST frames/bytes column + Afficher la colonne des trames/octets RST + + + Show the RST rate column + Afficher la colonne du débit RST + + + Show the NAK frames column + Afficher la colonne des trames NAK + + + Show the NAK count column + Afficher la colonne des compteurs NAK + + + Show the NAK bytes column + Afficher la colonne des octets NAK + + + Show the NAK frames/count column + Afficher la colonne des trames/compteurs NAK + + + Show the NAK count/bytes column + Afficher la colonne des compteurs/octets NAK + + + Show the NAK frames/bytes column + Afficher la colonne des trames/octets NAK + + + Show the NAK frames/count/bytes column + Afficher la colonne des trames/compteurs/octets NAK + + + Show the NAK rate column + Afficher la colonne du débit NAK + + + Show the ACK frames column + Afficher la colonne des trames ACK + + + Show the ACK bytes column + Afficher la colonne des octets ACK + + + Show the ACK frames/bytes column + Afficher la colonne des trames/octets ACK + + + Show the ACK rate column + Afficher la colonne du débit ACK + + + Show the CREQ frames column + Afficher la colonne des trames CREQ + + + Show the CREQ bytes column + Afficher la colonne des octets CREQ + + + Show the CREQ frames/bytes column + Afficher la colonne des trames/octets CREQ + + + Show the CREQ rate column + Afficher la colonne du débit CREQ + + + Auto-resize columns to content + Ajuster automatiquement la taille des colonnes au contenu + + + Resize columns to content size + Ajuster la taille des colonnes au contenu + + + Show the NCF rate column + Afficher la colonne du débit NCF + + + LBT-RU Statistics failed to attach to tap + Échec d'attache des Statistiques LBT-RU au tap + + + + LBMStreamDialog + + Dialog + Dialogue + + + Stream + Flux + + + Endpoint A + Terminal A + + + Endpoint B + Terminal B + + + Messages + Messages + + + Bytes + Octets + + + First Frame + Première Trame + + + Last Frame + Dernière Trame + + + Display filter: + Filtre d'affichage : + + + Regenerate statistics using this display filter + Régénérer les statistiques en utilisant ce filtre d'affichage + + + Apply + Appliquer + + + Copy as CSV + Copier en tant que CSV + + + Copy the tree as CSV + Copier l'arbre en tant que CSV + + + Copy as YAML + Copier en tant que YAML + + + Copy the tree as YAML + Copier l'arbre en tant que YAML + + + LBM Stream failed to attach to tap + Échec d'attache des Statistiques LBM au tap + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + LayoutPreferencesFrame + + Frame + Trame + + + Pane 1: + Panneau 1 : + + + Packet List + Liste de paquet + + + Packet Details + Détails des paquets + + + Packet Bytes + Octets des paquets + + + Packet Diagram + Diagramme de paquets + + + None + Aucun + + + Pane 2: + Panneau 2 : + + + Pane 3: + Panneau 3 : + + + Packet List settings: + Paramètre de la liste des paquests : + + + Show packet separator + Afficher le séparateur de paquets + + + Show column definition in column context menu + Afficher la définition de la colonne dans le menu contextuel de la colonne + + + Allow the list to be sorted + Autoriser le tri de la liste + + + Maximum number of cached rows (affects sorting) + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + Enable mouse-over colorization + Activer la colorisation au survol de la souris + + + Status Bar settings: + Paramètres de la Barre de Statut : + + + Show selected packet number + Afficher le numéro de paquet sélectionné + + + Show file load time + Afficher le temps de chargement du fichier + + + + LteMacStatisticsDialog + + LTE Mac Statistics + Statistiques LTE Mac + + + Include SR frames in filter + Inclure les trames SR dans le filtre + + + Include RACH frames in filter + Inclure les trames RACH dans le filtre + + + MAC Statistics + Statistiques MAC + + + + LteRlcGraphDialog + + Dialog + Dialogue + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Raccourcis-clavier précieux pour gains de temps surprenants</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom avant</td></th> +<tr><th>-</th><td>Zoom arrière</td></th> +<tr><th>0</th><td>Réinitialise le graphique à son état initial</td></th> + +<tr><th>→</th><td>Droite 10 pixels</td></th> +<tr><th>←</th><td>Gauche 10 pixels</td></th> +<tr><th>↑</th><td>Haut 10 pixels</td></th> +<tr><th>↓</th><td>Bas 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Droite 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Gauche 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Haut 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Bas1 pixel</td></th> + + <tr><th>g</th><td>Aller (go) au paquet sous le curseur</td></th> + +<tr><th>z</th><td>Bascule trainée souris / zoom</td></th> +<tr><th>t</th><td>Bascule origine du temps capture / session</td></th> +<tr><th>Espace</th><td>Basculer réticule</td></th> + +</tbody></table> </body></html> + + + Mouse + Souris + + + Drag using the mouse button. + Faites glisser à l'aide du bouton de la souris. + + + drags + glissements + + + Select using the mouse button. + Sélectionner à l'aide du bouton de la souris. + + + zooms + zooms + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Réinitialiser le graphique à son état initial.</p></body></html> + + + Reset + Réinitialiser + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Inverser le sens de la connexion (voir le flux opposé).</p></body></html> + + + Switch Direction + Inverser la direction + + + Reset Graph + Réinitialiser le Graphique + + + Reset the graph to its initial state. + Réinitialiser le graphique à son état initial. + + + 0 + 0 + + + Zoom In + Zoom Avant + + + + + + + + + Zoom Out + Zoom arrière + + + - + - + + + Move Up 10 Pixels + Monter de 10 Pixels + + + Up + Monter + + + Move Left 10 Pixels + Déplacer à Gauche de 10 Pixels + + + Left + Gauche + + + Move Right 10 Pixels + Déplacer à Droite de 10 Pixels + + + Right + Droite + + + Move Down 10 Pixels + Descendre de 10 Pixels + + + Down + Descendre + + + Move Up 1 Pixel + Monter d'1 Pixel + + + Shift+Up + Maj+Haut + + + Move Left 1 Pixel + Déplacer à Gauche d'1 Pixel + + + Shift+Left + Maj+Gauche + + + Move Right 1 Pixel + Déplacer à Droite d'1 Pixel + + + Shift+Right + Maj+Droite + + + Move Down 1 Pixel + Descendre d'1 Pixel + + + Move down 1 Pixel + Descendre d'1 pixel + + + Shift+Down + Maj+Bas + + + Drag / Zoom + Glisser / Zoom + + + Toggle mouse drag / zoom behavior + Basculer comportant de la souris glisser / zoom + + + Z + Z + + + Crosshairs + Réticule + + + Toggle crosshairs + Basculer réticule + + + Space + Espace + + + Move Up 100 Pixels + Haut 100 pixels + + + PgUp + PgUp + + + PgDown + PgDown + + + Go To Packet Under Cursor + Aller au paquet sous le curseur + + + Go to packet currently under the cursor + Aller au paquet actuellement sous le curseur + + + G + G + + + Zoom In X Axis + Zoom avant Axe X + + + X + X + + + Zoom Out Y Axis + Zoom arrière Axe Y + + + Shift+Y + Majuscule+Y + + + Zoom In Y Axis + Zoom avant Axe Y + + + Y + Y + + + Zoom Out X Axis + Zoom arrière Axe X + + + Shift+X + Majuscule-X + + + Switch direction (swap between UL and DL) + Sens de commutation (permutation entre UL et DL) + + + D + D + + + Time + Temps + + + Sequence Number + Numéro de Séquence + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + Graphique LTE RLC (UE=%1 chan=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + Graphique LTE RLC - aucun canal sélectionné + + + Save As… + Enregistrer sous… + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s seq %4 len %5) + + + Click to select packet + Cliquez pour sélectionner le paquet + + + Packet + Paquet + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Relâchez pour agrandir, x = %1 de %2, y = %3 de %4 + + + Unable to select range. + Impossible de sélectionner la plage. + + + Click to select a portion of the graph. + Cliquez pour sélectionner une partie du graphique. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Fichier JPEG (*.jpeg *.jpg) + + + Save Graph As… + Enregistrer le graphique sous… + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + Statistiques LTE RLC + + + Include SR frames in filter + Inclure les trames SR dans le filtre + + + Include RACH frames in filter + Inclure les trames RACH dans le filtre + + + Use RLC frames only from MAC frames + Utiliser les trames RLC uniquement à partir des trames MAC + + + UL Frames + UL Trames + + + UL Bytes + UL Octets + + + UL MB/s + UL MB/s + + + UL ACKs + UL ACKs + + + UL NACKs + UL NACKs + + + UL Missing + UL Manquants + + + DL Frames + DL Trames + + + DL Bytes + DL Octets + + + DL MB/s + DL MB/s + + + DL ACKs + DL ACKs + + + DL NACKs + DL NACKs + + + DL Missing + DL Manquants + + + RLC Statistics + Statistiques RLC + + + + MainStatusBar + + Ready to load or capture + Prêt pour charger ou capturer + + + Ready to load file + Prêt pour charger un fichier + + + Open the Capture File Properties dialog + Ouvre le dialogue des Propriétés de Fichier de Capture + + + Profile: %1 + Profil : %1 + + + Manage Profiles… + Gérer les profils… + + + New… + Nouveau… + + + Edit… + Éditer… + + + Import + Importer + + + Export + Exporter + + + Delete + Supprimer + + + Switch to + Passer à + + + is the highest expert information level + is the highest expert info level + est le niveau d'information expert le plus élevé + + + ERROR + ERREUR + + + WARNING + ATTENTION + + + NOTE + NOTE + + + CHAT + CHAT + + + No expert information + No expert info + Pas d'information expert + + + %Ln byte(s) + , %1 bytes + + + + + + + Byte %1 + Octet %1 + + + Bytes %1-%2 + Octets %1-%2 + + + Selected Packet: %1 %2 + Paquet sélectionné : %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + Paquets : %1 %4 Affichés : %2 (%3%) + + + %1 Selected: %2 (%3%) + %1 Sélectionnés : %2 (%3%) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 Marqués : %2 (%3%) + + + %1 Dropped: %2 (%3%) + %1 Perdus : %2 (%3%) + + + %1 Ignored: %2 (%3%) + %1 Ignorés : %2 (%3%) + + + %1 Comments: %2 + %1 Commentaires : %2 + + + %1 Load time: %2:%3.%4 + %1 Temps de chargement : %2:%3.%4 + + + No Packets + Pas de paquets + + + From Zip File... + + + + From Directory... + + + + Selected Personal Profile... + + + + All Personal Profiles... + + + + Packets: %1 + Paquets : %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + Trame + + + Checking this will save the size, position, and maximized state of the main window. + Cocher cette option permet de sauvegarder la taille, position et l'état de la fenetre principale. + + + Remember main window size and placement + Se souvenir de la taille et du positionnement de la fenêtre principale + + + Open files in + Ouvrir les fichiers dans + + + This folder: + Ce Dossier : + + + Browse… + Browse... + Feuilleter... + + + The most recently used folder + Le dernier dossier utilisé + + + Show up to + Afficher + + + filter entries + filtres + + + recent files + Fichiers recents + + + Confirm unsaved capture files + Toujours Confirmer avant un fichier de capture non sauvegardé + + + Display autocompletion for filter text + Afficher la saisie semi-automatique pour le texte du filtre + + + Main toolbar style: + Style de la barre d'outils principale : + + + Icons only + Icones seulement + + + Text only + Texte seulement + + + Icons & Text + Icones et Texte + + + Window title + Titre de la fenêtre + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Titre de fenêtre personnalisé à ajouter au titre existant<br/>%F = chemin du fichier de capture<br/>%P = nom du profil<br/>%S = un séparateur conditionnel (" - ") qui s'affiche uniquement lorsqu'il est entouré de variables avec des valeurs ou du texte statique<br/>%V = informations sur la version</p></body></html> + + + Prepend window title + Ajouter le titre de la fenêtre + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Titre de fenêtre personnalisé à ajouter au titre existant<br/>%F = chemin du fichier de capture<br/>%P = nom du profil<br/>%S = un séparateur conditionnel (" - ") qui s'affiche uniquement lorsqu'il est entouré de variables avec des valeurs ou du texte statique<br/>%V = informations sur la version</p></body></html> + + + Language: + Langue : + + + Use system setting + Utiliser les paramètres système + + + Open Files In + Ouvrir les fichiers dans + + + + ManageInterfacesDialog + + Manage Interfaces + Gérer Interfaces + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Cliquez la case à cocher pour masquer ou afficher une interface masquée.</p></body></html> + + + Local Interfaces + Interfaces locales + + + Show + Afficher + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Ajoute un pipe d'où capturer, ou supprimer un pipe existant de la liste.</p></body></html> + + + Pipes + Pipes + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Ajoute un nouveau pipe en utilisant les parmètres par défaut.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Supprime le pipe sélectioné de la liste.</p></body></html> + + + Remote Interfaces + Interfaces Distantes + + + Host / Device URL + URL Hôte / Périphérique + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>Ajouter un hôte distant et ses interfaces</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>Supprimer l'hôte selectionné de la liste.</p></body></html> + + + Remote Settings + Paramètres Distants + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Cette version de Wireshark ne sauvegarde pas les paramètres de pipe. + + + This version of Wireshark does not save remote settings. + Cette version de Wireshark ne sauvegarde pas les paramètres distants. + + + This version of Wireshark does not support remote interfaces. + Cette version de Wireshark ne supporte pas les interfaces distantes. + + + New Pipe + Nouveau tuyau + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + Tout sélectionner + + + Copy + + + + Find + Chercher + + + Clear + Vider + + + + ManufTableModel + + Address Block + + + + Short Name + Nom Court + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + ZoneDéfilement + + + + Mtp3SummaryDialog + + Dialog + Dialogue + + + MTP3 Summary + Résumé MTP3 + + + File + Fichier + + + Name + Nom + + + Length + Longueur + + + Format + Format + + + Snapshot length + Taille Snapshot + + + Data + Données + + + First packet + Premier paquet + + + Last packet + Dernier paquet + + + Elapsed + Temps écoulé + + + Packets + Paquets + + + Service Indicator (SI) Totals + Totaux des indicateurs de service (SI) + + + SI + SI + + + MSUs + MSUs + + + MSUs/s + MSUs/s + + + Bytes + Octets + + + Bytes/MSU + Octets/MSU + + + Bytes/s + Octets/s + + + Totals + Totaux + + + Total MSUs + MSUs Totaux + + + Total Bytes + Octets Totaux + + + Average Bytes/MSU + Moyenne Octets/MSU + + + Average Bytes/s + Débit moyen (octets/s) + + + + MulticastStatisticsDialog + + UDP Multicast Streams + Flux Multicast UDP + + + Source Address + Adresse Source + + + Source Port + Port Source + + + Destination Address + Adresse Destination + + + Destination Port + Port Destination + + + Packets + Paquets + + + Packets/s + Paquets/s + + + Avg BW (bps) + BP Moyenne (bps) + + + Max BW (bps) + BP Max (bps) + + + Max Burst + Burst Ma + + + Burst Alarms + Alarmes Burst + + + Max Buffers (B) + Tampon Max (B) + + + Buffer Alarms + Alarmes Tampon + + + Burst measurement interval (ms): + Intervalle de mesure des Rafales (ms) : + + + Burst alarm threshold (packets): + Seuil d'alarme de Rafales (paquets) : + + + Buffer alarm threshold (B): + Seuil d'alarme Tampon (B) : + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + Vitesse de flux vide (Kb/s) : + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + Vitesse totale à vide (Kb/s) : + + + The burst interval must be between 1 and 1000. + L'intervale burst doit être entre 1 et 1000. + + + The burst alarm threshold isn't valid. + Le seuil d'alarme burst n'est pas valide + + + The buffer alarm threshold isn't valid. + Le seuil d'alarme tampon n'est pas valide + + + The stream empty speed should be between 1 and 10000000. + La vitesse de flux vide doit être comprise entre 1 et 10000000. + + + The total empty speed should be between 1 and 10000000. + La vitesse à vide totale doit être comprise entre 1 et 10000000. + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + %1 flux, bande passante moyenne : %2bps, bande passante max : %3bps, rafale max : %4 / %5ms, tampon max : %6B + + + + PacketCommentDialog + + Edit Packet Comment + Modifier le commentaire du paquet + + + Add Packet Comment + Ajouter un commentaire de paquet + + + + PacketDiagram + + Packet diagram + Diagramme de paquet + + + Show Field Values + Afficher les valeurs de champ + + + Save Diagram As… + Enregistrer le diagramme sous… + + + Copy as Raster Image + Copier en tant qu'image raster + + + …as SVG + …comme SVG + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Bitmap Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Format d'échange de fichiers JPEG (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + Graphiques vectoriels évolutifs (*.svg) + + + Save Graph As… + Enregistrer le graphique sous… + + + + PacketDialog + + Dialog + Dialogue + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + Afficher les octets du paquet + + + Packet %1 + Paquet %1 + + + [%1 closed] + [%1 fermé] + + + Byte %1 + Octet %1 + + + Bytes %1-%2 + Octets %1-%2 + + + %Ln byte(s) + + + + + + + + PacketFormatGroupBox + + GroupBox + GroupBox + + + Packet Format + Format de paquet + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>Lignes de paquet résumé similaire à la liste des paquets</p></body></html> + + + Summary line + Résumé + + + Include column headings + Inclure les en-têtes de colonne + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>Détails des paquets similaire à l'arbre de protocole</p></body></html> + + + Details: + Détails : + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>Exporter seulement le détails des paquets de haut niveau</p></body></html> + + + All co&llapsed + Tout &Reduire + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>Etendre ou reduire les détails des paquets actuellements affichés.</p></body></html> + + + As displa&yed + Comme &Affichés + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>Exporter tous les détails des paquets</p></body></html> + + + All e&xpanded + Tout E&tendre + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>Exporter en hexdump les données des paquets simailaire au paquets affichées</p></body></html> + + + Bytes + Octets + + + Include secondary data sources + Inclure les sources de données secondaires + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + <html><head/><body><p>Générer des vidages hexadécimaux pour les sources de données secondaires telles que les tampons réassemblés ou déchiffrés en plus de la trame</p></body></html> + + + + PacketList + + Protocol Preferences + Préférences de protocole + + + Summary as Text + Résumé en Texte + + + …as CSV + …au format CSV + + + …as YAML + …comme YAML + + + Decode As… + Décoder comme… + + + Frame %1: %2 + + + Trame %1 : %2 + + + + + [ Comment text exceeds %1. Stopping. ] + [ Taille maximum de commentaire dépassé %1 Arrêt !] + + + + PacketListHeader + + Align Left + Aligner à gauche + + + Align Center + Aligner le centre + + + Align Right + Aligner à droite + + + Edit Column + Modifier la colonne + + + Resize to Contents + Redimensionner au contenu + + + Column Preferences… + Préférences de colonne… + + + Resize Column to Width… + Redimensionner la colonne à la largeur… + + + Resolve Names + Résoudre les noms + + + Remove this Column + Supprimer cette colonne + + + Column %1 + Colonne %1 + + + Width: + Largeur : + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + + + + Sorting "%1"… + Tri de "%1"… + + + Sorting … + + + + + PacketRangeGroupBox + + Form + Formulaire + + + Packet Range + Plage de Paquets + + + - + - + + + Displayed + Affichés + + + &Marked packets only + Paquets &marqués seulement + + + &Range: + &Plage : + + + Remove &ignored packets + Supprimer les paquets &ignorés + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + Premiere jusqu'au &dernier marqué + + + &All packets + &Tous les paquets + + + &Selected packets only + Paquet &sélectionné seulement + + + Captured + Capturés + + + + PathSelectionDelegate + + Open a pipe + Ouvrir un tuyau + + + + PathSelectionEdit + + Browse + Parcourir + + + Select a path + Sélectionner un chemin + + + + PluginListModel + + Name + Nom + + + Version + Version + + + Type + Type + + + Path + Chemin + + + + PortsModel + + All entries + Toutes les entrées + + + tcp + TCP + + + udp + UDP + + + sctp + scp + + + dccp + dccp + + + Name + Nom + + + Port + Port + + + Type + Type + + + + PreferenceEditorFrame + + Frame + Trame + + + … + + + + a preference + une préférence + + + Browse… + Parcourir… + + + Open %1 preferences… + Ouvrir %1 préférences… + + + Invalid value. + Valeur invalide. + + + + PreferencesDialog + + Search: + Recherche : + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + Préférences + + + + PrefsModel + + Advanced + Avancé + + + Appearance + Apparence + + + Layout + Disposition + + + Columns + Colonnes + + + Font and Colors + Police et couleurs + + + Capture + Capture + + + Expert + Expert + + + Filter Buttons + Boutons de filtre + + + RSA Keys + Clés RSA + + + + PrintDialog + + Packet Format + Format des Paquets + + + Print each packet on a new page + Imprimer chaque paquet dans une nouvelle page + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>Imprimer les informations du fichier de capture sur chaque page</p></body></html> + + + Capture information header + Capturer l'en-tête des informations + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>Utiliser &quot;+&quot; et &quot;-&quot; pour agrandir ou reduire dans la prévisualisation. Utiliser &quot;0&quot; pour annuler le zoom.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ et - zoom, 0 resets</span></p></body></html> + + + Packet Range + Plage de Paquet + + + Print + Imprimer + + + &Print… + &Imprimer… + + + Page &Setup… + Page de &configuration… + + + %1 %2 total packets, %3 shown + %1 %2 paques totales, %3 affichés + + + Print Error + Erreur d'impression + + + Unable to print to %1. + Impossible d'imprimer sur %1. + + + + ProfileDialog + + Search for profile … + Rechercher un profil… + + + Create a new profile using default settings. + Crée un nouveau profil en utilisant les paramètres par défaut. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>Supprimer ce profil. Les profils fournis par le système ne peuvent pas être supprimés. Le profil par défaut sera réinitialisé lors de la suppression.</p></body></html> + + + Copy this profile. + Copier ce profil. + + + Configuration Profiles + Profils de configuration + + + Import + noun + Importer + + + Export + noun + Exporter + + + From Zip File... + + + + From Directory... + + + + %Ln Selected Personal Profile(s)... + + + + + + + All Personal Profiles... + + + + New profile + Nouveau profile + + + Profile Error + Erreur de Profil + + + Exporting profiles + Exportation de profils + + + No profiles found for export + Aucun profil trouvé pour l'exportation + + + Select zip file for export + Sélectionner le fichier zip pour l'exportation + + + An import of profiles is not allowed, while changes are pending + Une importation de profils n'est pas autorisée, tant que des modifications sont en attente + + + An import is pending to be saved. Additional imports are not allowed + Une importation est en attente d'enregistrement. Les importations supplémentaires ne sont pas autorisées + + + An export of profiles is only allowed for personal profiles + Une exportation de profils n'est autorisée que pour les profils personnels + + + An export of profiles is not allowed, while changes are pending + Une exportation de profils n'est pas autorisée, tant que des modifications sont en attente + + + %Ln profile(s) exported + + + + + + + Select zip file for import + Sélectionner le fichier zip à importer + + + Select directory for import + Sélectionner le répertoire pour l'importation + + + Zip File (*.zip) + Fichier zip (*.zip) + + + Error + Erreur + + + An error has occurred while exporting profiles + Une erreur s'est produite lors de l'exportation des profils + + + No profiles found for import in %1 + Aucun profil trouvé pour l'importation dans %1 + + + %Ln profile(s) imported + + + + + + + , %Ln profile(s) skipped + + + + + + + Importing profiles + Importation de profils + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + ProfileModel + + Resetting to default + Réinitialisation par défaut + + + Imported profile + Profil importé + + + This is a system provided profile + Ceci est un profil fourni par le système + + + A profile change for this name is pending + Un changement de profil pour ce nom est en attente + + + (See: %1) + (Voir : %1) + + + This is an invalid profile definition + Ceci est une définition de profil invalide + + + A profile already exists with this name + Un profil existe déjà avec ce nom + + + A profile with this name is being deleted + Un profil portant ce nom est en cours de suppression + + + Created from default settings + Créé à partir des paramètres par défaut + + + system provided + système fourni + + + deleted + supprimé + + + copy + noun + copier + + + Exporting profiles while changes are pending is not allowed + L'exportation de profils alors que des modifications sont en attente n'est pas autorisée + + + No profiles found to export + Aucun profil trouvé à exporter + + + Can't delete profile directory + Impossible de supprimer le répertoire de profil + + + A profile name cannot contain the following characters: %1 + Un nom de profil ne peut contenir aucun des caractères suivants : %1 + + + A profile name cannot contain the '/' character + Un nom de profil ne peut pas contenir le '/' personnage + + + A profile cannot start or end with a period (.) + Un profil ne peut pas commencer ou se terminer par un point (.) + + + Default + Défaut + + + Global + Global + + + Personal + Personnel + + + Renamed from: %1 + Renomé à partir de : %1 + + + Copied from: %1 + Copié à partir de : %1 + + + renamed to %1 + renommé en %1 + + + Profile + Profil + + + Type + Type + + + + ProfileSortModel + + All profiles + Tous les profils + + + Personal profiles + Profils personnels + + + Global profiles + Profils globaux + + + + ProgressFrame + + Frame + Trame + + + Loading + Chargement + + + + ProtoTree + + Packet details + Détail du paquet + + + Not a field or protocol + Pas un champ ou un protocole + + + No field reference available for text labels. + Aucune référence de champ disponible pour les étiquettes de texte. + + + Expand Subtrees + Développer les sous-arborescences + + + Collapse Subtrees + Réduire les sous-arborescences + + + Expand All + Développer tout + + + Collapse All + Réduire tout + + + Copy + Copie + + + All Visible Items + Tous les éléments visibles + + + All Visible Selected Tree Items + Sélectionner tous les éléments de l'arborescence visibles + + + Description + Description + + + Field Name + Nom de domaine + + + Value + Valeur + + + As Filter + Comme filtre + + + Wiki Protocol Page + Page Protocole Wiki + + + Filter Field Reference + Référence des champs de filtre + + + Copied + Copié + + + Wiki Page for %1 + Page wiki pour %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Le wiki Wireshark est géré par la communauté.</p><p>La page que vous êtes sur le point de charger peut être merveilleuse, incomplète, erronée ou inexistante.</p><p>Continuer au wiki ?</p> + + + Colorize with Filter + Colorier avec un Filtre + + + + ProtocolHierarchyDialog + + Dialog + Dialogue + + + Protocol + Protocole + + + Percent Packets + Pourcent Paquets + + + Packets + Paquets + + + Percent Bytes + Pourcent Octets + + + Bytes + Octets + + + Bits/s + Bits/s + + + End Packets + Paquets de Fin + + + End Bytes + Octets de Fin + + + End Bits/s + Octets/s de Fin + + + PDUs + PDUs + + + <small><i>A hint.</i></small> + <small><i>Un indice.</i></small> + + + Copy as CSV + Copier en tant que CSV + + + Copy stream list as CSV. + Copier la liste des flux en tant que CSV. + + + Copy as YAML + Copier en tant que YAML + + + Copy stream list as YAML. + Copie la liste des flux en YAML. + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + Statistiques de la Hiérarchie des Protocoles + + + Copy + Copier + + + as CSV + en CSV + + + as YAML + en YAML + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + Aucun filtre d'affichage. + + + Display filter: %1 + Filtre d'affichage : %1 + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + Préférences du Protocole + + + No protocol preferences available + Aucune préférence de protocole disponible + + + Disable %1 + Désactiver %1 + + + %1 has no preferences + %1 n'a pas de préférences + + + Open %1 preferences… + Ouvrir %1 préférences… + + + + QObject + + Average Throughput (bits/s) + Débit moyen (bits/s) + + + Round Trip Time (ms) + Round Trip Time (ms) + + + Segment Length (B) + Longueur des segments (B) + + + Sequence Number (B) + Numéro de séquence (B) + + + Time (s) + Temps (s) + + + Window Size (B) + Taille de fenetres (b) + + + [no capture file] + [pas de fichier de capture] + + + Conversation + Conversation + + + Bars show the relative timeline for each conversation. + Les barres montrent la chronologie relative pour chaque conversation. + + + Endpoint + Terminal + + + Apply as Filter + Appliquer comme un Filtre + + + Prepare as Filter + Préparer comme filtre + + + Find + Chercher + + + Colorize + Colorier + + + Look Up + Chercher + + + Copy + Copier + + + UNKNOWN + INCONNU + + + Selected + Sélectionné + + + Not Selected + Non Sélectionné + + + …and Selected + …et sélectionné + + + …or Selected + …ou sélectionné + + + …and not Selected + …et non sélectionné + + + …or not Selected + …ou non sélectionné + + + A + A + + + B + B + + + Any + Tout + + + Don't show this message again. + Ne plus afficher ce message. + + + Multiple problems found + Plusieurs problèmes trouvés + + + %1 (%L2%) + %1 (%L2%) + + + No entries. + Aucune entrées. + + + %1 entries. + %1 entrées. + + + Base station + Station de base + + + <Broadcast> + <Broadcast> + + + <Hidden> + <Masqué> + + + BSSID + BSSID + + + Beacons + Balises + + + Data Pkts + Pqts Données + + + Protection + Protection + + + Address + Adresse + + + Pkts Sent + Pqts Envoyés + + + Pkts Received + Pqts Reçus + + + Comment + Commentaire + + + Wrong sequence number + Mauvais numéro de séquence + + + Payload changed to PT=%1 + Charge utile changée en PT=%1 + + + Incorrect timestamp + Timestamp incorrect + + + Marker missing? + Marqueur manquant? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + Type + + + UEId + UEId + + + UL Frames + UL Trames + + + UL Bytes + UL Octets + + + UL MB/s + UL MB/s + + + UL Padding % + Rembourrage UL % + + + UL Re TX + UL Re TX + + + DL Frames + DL Trames + + + DL Bytes + DL Octets + + + DL MB/s + DL MB/s + + + DL Padding % + Rembourrage DL % + + + DL CRC Failed + Échec DL CRC + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + Predef + + + Unknown (%1) + Inconnu (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + Inconnu + + + UE Id + Id UE + + + Name + Nom + + + Mode + Mode + + + Priority + Priorité + + + default + défaut + + + DLT %1 + DLT %1 + + + Invalid Display Filter + Filtre d'affichage non valide + + + The filter expression %1 isn't a valid display filter. (%2). + L'expression de filtre %1 n'est pas un filtre d'affichage valide. (%2). + + + Error + Erreur + + + No remote interfaces found. + Aucune interface distante trouvée. + + + PCAP not found + PCAP introuvable + + + Unknown error + Erreur inconnue + + + Default + Défaut + + + Changed + Modifié + + + Has this preference been changed? + Cette préférence a-t-elle été modifiée ? + + + Default value is empty + La valeur par défaut est vide + + + Gap in dissection + Lacune dans la dissection + + + Edit… + Éditer… + + + Browse… + Parcourir… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + Interface Distante + + + Host: + Hôte : + + + Port: + Port : + + + Authentication + Authentification + + + Null authentication + Authentification Nulle + + + Password authentication + Authentification Mot de Passe + + + Username: + Nom d'usager : + + + Password: + Mot de passe : + + + Clear list + Effacer liste + + + Error + Erreur + + + No remote interfaces found. + Aucune interface distante trouvée. + + + PCAP not found + PCAP non trouvé + + + + RemoteSettingsDialog + + Remote Capture Settings + Paramètres de Capture Distante + + + Capture Options + Options de Capture + + + Do not capture own RPCAP traffic + Ne pas capturer son propre trafic RPCAP + + + Use UDP for data transfer + Utiliser UDP pour le transfer de données + + + Sampling Options + Options d'échantillonnage + + + None + Aucun + + + 1 of + 1 de + + + packets + paquets + + + 1 every + 1 chaque + + + milliseconds + millisecondes + + + + ResolvedAddressesDialog + + Dialog + Dialogue + + + Hosts + Hôtes + + + Search for entry (min 3 characters) + Rechercher une entrée (3 caractères min) + + + Ports + Ports + + + Search for port or name + Rechercher un port ou un nom + + + Capture File Comments + Capturer les commentaires du fichier + + + Comment + Commentaire + + + Show the comment. + Afficher le commentaire. + + + IPv4 Hash Table + Table Hash IPv4 + + + Show the IPv4 hash table entries. + Afficher les entrées de la table hash IPv4 + + + IPv6 Hash Table + Table Hash IPv6 + + + Show the IPv6 hash table entries. + Afficher les entrées de la table hash IPv6 + + + Show All + Afficher tout + + + Show all address types. + Afficher tous les types d'adresses. + + + Hide All + Masquer tout + + + Hide all address types. + Masquer tous les types d'adresses + + + IPv4 and IPv6 Addresses (hosts) + Adresses IPv4 et IPv6 (hôtes) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + Afficher les noms d'hôtes IPv4 et IPv6 résolus en format "hosts". + + + Port names (services) + Noms de ports (services) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + Afficher les noms de ports réslus en format "services" + + + Ethernet Addresses + Adresses Ethernet + + + Show resolved Ethernet addresses in "ethers" format. + Afficher les adresses Ethernet résolues en format "ethers" + + + Ethernet Well-Known Addresses + Adresses Ethernet Bien-Connues + + + Show well-known Ethernet addresses in "ethers" format. + Afficher les adresses Ethernet bien-connues en format "ethers" + + + Ethernet Manufacturers + Fabricants Ethernet + + + Show Ethernet manufacturers in "ethers" format. + Afficher les fabricants Ethernet en format "ethers". + + + [no file] + [pas de fichier] + + + Resolved Addresses + Adresses résolues + + + # Resolved addresses found in %1 + # Adresses résolues trouvées dans %1 + + + # Comments +# +# + # Commentaires +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + %1 Statistiques Délai Temps de Réponse + + + Type + Type + + + Messages + Messages + + + Min SRT + SRT Min + + + Max SRT + SRT Max + + + Avg SRT + SRT Moyen + + + Min in Frame + Min dans Trame + + + Max in Frame + Max dans Trame + + + Open Requests + Requêtes Ouvertes + + + Discarded Responses + Réponses Rejetées + + + Repeated Requests + Requêtes Répétées + + + Repeated Responses + Réponses Répétées + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>Sélectionner un programme et une version et entrer un filtre si désiré, puis presser Appliquer.</i></small> + + + Version: + Version : + + + Program: + Programme : + + + DCE-RPC Service Response Times + Temps de Réponse Service DCE-RPC + + + ONC-RPC Service Response Times + Temps de Réponse Service ONC-RPC + + + + RsaKeysFrame + + RSA Keys + Clés RSA + + + RSA private keys are loaded from a file or PKCS #11 token. + Les clés privées RSA sont chargées à partir d'un fichier ou d'un jeton PKCS #11. + + + Add new keyfile… + Ajouter un nouveau fichier clé… + + + Add new token… + Ajouter un nouveau jeton… + + + Remove key + Supprimer la clé + + + PKCS #11 provider libraries. + Bibliothèques de fournisseur PKCS #11. + + + Add new provider… + Ajouter un nouveau fournisseur… + + + Remove provider + Supprimer le fournisseur + + + Add PKCS #11 token or key + Ajouter un jeton ou une clé PKCS #11 + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + Aucun nouveau jeton ou clé PKCS #11 trouvé, envisager d'ajouter un fournisseur PKCS #11. + + + Select a new PKCS #11 token or key + Sélectionner un nouveau jeton ou une nouvelle clé PKCS #11 + + + PKCS #11 token or key + Jeton ou clé PKCS #11 + + + Enter PIN or password for %1 (it will be stored unencrypted) + Entrer le code PIN ou le mot de passe pour %1 (il sera stocké non crypté) + + + Enter PIN or password for key + Entrer le code PIN ou le mot de passe pour la clé + + + Key could not be added: %1 + La clé n'a pas pu être ajoutée : %1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + Clé privée RSA (*.pem *.p12 *.pfx *.key);;Tous les fichiers ( + + + Select RSA private key file + Sélectionner le fichier de clé privée RSA + + + Libraries (*.dll) + Bibliothèques (*.dll) + + + Libraries (*.so) + Bibliothèques (*.so) + + + Select PKCS #11 Provider Library + Sélectionner la bibliothèque du fournisseur PKCS #11 + + + Changes will apply after a restart + Les modifications s'appliqueront après un redémarrage + + + PKCS #11 provider %1 will be removed after the next restart. + Le fournisseur PKCS #11 %1 sera supprimé après le prochain redémarrage. + + + + RtpAnalysisDialog + + Dialog + Dialogue + + + Packet + Paquet + + + Sequence + Séquence + + + Delta (ms) + Delta(ms) + + + Jitter (ms) + Jitter + Gigue(ms) + + + Skew + Déviation + + + Bandwidth + Bande passante + + + Marker + Marqueur + + + Status + Etat + + + Stream %1 + Flux %1 + + + Stream %1 Jitter + Flux %1 gigue + + + Stream %1 Difference + Flux %1 Différence + + + Stream %1 Delta + Delta du flux %1 + + + %1 streams, + %1 flux, + + + Save one stream CSV + Enregistrer un flux CSV + + + Save all stream's CSV + Enregistrer le CSV de tous les flux + + + &Analyze + &Analyser + + + Open the analysis window for the selected stream(s) + Ouvrir la fenêtre d'analyse du ou des flux sélectionnés + + + &Set List + &Définir une liste + + + &Add to List + &Ajouter à la liste + + + &Remove from List + &Supprimer de la liste + + + Replace existing list in RTP Analysis Dialog with new one + Remplacer la liste existante dans la boîte de dialogue d'analyse RTP par une nouvelle + + + Add new set to existing list in RTP Analysis Dialog + Ajouter un nouvel ensemble à la liste existante dans la boîte de dialogue d'analyse RTP + + + Remove selected streams from list in RTP Analysis Dialog + Supprimer les flux sélectionnés de la liste dans la boîte de dialogue d'analyse RTP + + + Graph + Graphique + + + <small><i>A hint.</i></small> + <small><i>Un indice.</i></small> + + + &Export + &Exporter + + + Open export menu + Ouvrir le menu d'exportation + + + CSV + CSV + + + Save tables as CSV. + Enregistrer les tableaux au format CSV. + + + Current Tab Stream CSV + CSV du flux d'onglets actuel + + + Save the table on the current tab as CSV. + Enregistrer le tableau sur l'onglet actuel au format CSV. + + + All Tab Streams CSV + Tous les flux d'onglets CSV + + + Save the table from all tabs as CSV. + Enregistrer le tableau de tous les onglets au format CSV. + + + Save Graph + Sauvegarder Graphique + + + Save the graph image. + Sauvegarder le graphique. + + + Go to Packet + Aller au Paquet + + + Select the corresponding packet in the packet list. + Sélectionner le paquet correspondant dans la liste des paquets. + + + G + G + + + Next Problem Packet + Paquet problématique suivant + + + Go to the next problem packet + Aller au prochain paquet problématique + + + N + N + + + Prepare &Filter + Préparer le &filtre + + + Prepare a filter matching the selected stream(s). + Préparer un filtre correspondant aux flux sélectionnés. + + + &Current Tab + &Onglet Actuel + + + Prepare a filter matching current tab. + Préparer un filtre correspondant à l'onglet actuel. + + + &All Tabs + &Tous les onglets + + + Prepare a filter matching all tabs. + Préparer un filtre correspondant à tous les onglets. + + + RTP Stream Analysis + Analyse flux RTP + + + Save Graph As… + Enregistrer le graphique sous… + + + G: Go to packet, N: Next problem packet + G : Aller au paquet, N : Paquet à problème suivant + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Fichier JPEG (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + Comma-separated values (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %1 ne prend pas en charge PCM à %2. Le format préféré est %3 + + + + RtpPlayerDialog + + RTP Player + Player RTP + + + Play + Jouer + + + Source Address + Adresse Source + + + Source Port + Port Source + + + Destination Address + Adresse Destination + + + Destination Port + Port Destination + + + SSRC + SSRC + + + Setup Frame + Trame de configuration + + + Packets + Paquets + + + Time Span (s) + Étendue Temps (s) + + + Payloads + Payloads + + + <small><i>No audio</i></small> + <small><i>Pas d'audio</i></small> + + + Start playback of all unmuted streams + Démarrer la lecture de tous les flux non mis en sourdine + + + Pause/unpause playback + Interrompre/reprendre la lecture + + + Stop playback + Arrêter la lecture + + + Enable/disable skipping of silence during playback + Activer/désactiver le saut de silence pendant la lecture + + + Min silence: + Silence minimum : + + + Minimum silence duration to skip in seconds + Durée minimale du silence à ignorer en secondes + + + Output Device: + Équipement de sortie : + + + Output Audio Rate: + Débit de sortie audio : + + + Jitter Buffer: + Tampon de Gigue : + + + The simulated jitter buffer in milliseconds. + Le tampon de gigue simulé en millisecondes. + + + Playback Timing: + Durée relecture : + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Tampon de gigue</strong>: Utiliser le tampon de gigue pour simuler le flux RTP tel qu'il est entendu par l'utilisateur final. +<br/> +<strong>Horodatage RTP</strong> : Utiliser l'horodatage RTP au lieu de l'heure d'arrivée du paquet. Cela ne reproduira pas le flux RTP tel que l'utilisateur l'a entendu, mais est utile lorsque le RTP est tunnellisé et que la synchronisation du paquet d'origine est manquante. +<br/> +<strong>Mode ininterrompu</strong> : Ignorer l'horodatage RTP. Jouer le flux au fur et à mesure qu'il est terminé. Ceci est utile lorsque l'horodatage RTP est manquant. + + + Jitter Buffer + Tampon Gigue + + + RTP Timestamp + Cachet RTP + + + Uninterrupted Mode + Mode Ininterrompu + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>Voir les cachets comme heure du jour (coché) ou comme secondes depuis le début de la capture (décoché).</p></body></html> + + + Time of Day + Heure du Jour + + + &Export + &Exporter + + + Export audio of all unmuted selected channels or export payload of one channel. + Exporter l'audio de tous les canaux sélectionnés non mis en sourdine ou exporter la charge utile d'un canal. + + + From &cursor + À partir du &curseur + + + Save audio data started at the cursor + Enregistrer les données audio commencées au curseur + + + &Stream Synchronized Audio + &Diffusion audio synchronisée + + + Save audio data synchronized to start of the earliest stream. + Enregistrer les données audio synchronisées au début du premier flux. + + + &File Synchronized Audio + & Fichier audio synchronisé + + + Save audio data synchronized to start of the capture file. + Enregistrer les données audio synchronisées au début du fichier de capture. + + + &Payload + &Charge utile + + + Save RTP payload of selected stream. + Enregistrer la charge utile RTP du flux sélectionné. + + + Reset Graph + Réinitialiser le Graphique + + + Reset the graph to its initial state. + Réinitialiser le graphique à son état initial. + + + Go To Setup Packet + Aller au paquet de configuration + + + Go to setup packet of stream currently under the cursor + Accéder au paquet de configuration du flux actuellement sous le curseur + + + Mute + Muet + + + Mute selected streams + Désactiver les flux sélectionnés + + + Unmute + Unmute + + + Unmute selected streams + Réactiver les flux sélectionnés + + + Invert muting of selected streams + Inverser la mise en sourdine des flux sélectionnés + + + Route audio to left channel of selected streams + Acheminer l'audio vers le canal gauche des flux sélectionnés + + + Route audio to left and right channel of selected streams + Acheminer l'audio vers les canaux gauche et droit des flux sélectionnés + + + Route audio to right channel of selected streams + Acheminer l'audio vers le canal droit des flux sélectionnés + + + Remove Streams + Supprimer les flux + + + Remove selected streams from the list + Supprimer les flux sélectionnés de la liste + + + All + Tout + + + Select all + Tout sélectionner + + + None + Aucun + + + Clear selection + Effacer la sélection + + + Invert + Inverser + + + Invert selection + Inverser la sélection + + + Play/Pause + Jouer/Pause + + + Start playing or pause playing + Commencer à jouer ou mettre en pause la lecture + + + Stop + Arrêter + + + Stop playing + Arrête de jouer + + + I&naudible streams + Flux i&naudibles + + + Select/Deselect inaudible streams + Sélectionner/Désélectionner les flux inaudibles + + + Inaudible streams + Flux inaudibles + + + &Select + &Sélectionner + + + Select inaudible streams + Sélectionner les flux inaudibles + + + &Deselect + &Désélectionner + + + Deselect inaudible streams + Désélectionner les flux inaudibles + + + Prepare &Filter + Préparer le &filtre + + + Prepare a filter matching the selected stream(s). + Préparer un filtre correspondant aux flux sélectionnés. + + + R&efresh streams + R&actualiser les flux + + + Read captured packets from capture in progress to player + Lire les paquets capturés de la capture en cours au lecteur + + + Zoom In + Zoom Avant + + + SR (Hz) + RS (Hz) + + + Sample rate of codec + Taux d'échantillonnage du codec + + + PR (Hz) + PR (Hz) + + + Play rate of decoded audio (depends e. g. on selected sound card) + Taux de lecture de l'audio décodé (dépend par exemple de la carte son sélectionnée) + + + Zoom Out + Zoom arrière + + + Move Left 10 Pixels + Déplacer à Gauche de 10 Pixels + + + Move Right 10 Pixels + Déplacer à Droite de 10 Pixels + + + Move Left 1 Pixels + Déplacer à Gauche d'1 Pixel + + + Move Right 1 Pixels + Déplacer à Droite d'1 Pixel + + + Go To Packet Under Cursor + Aller au paquet sous le curseur + + + Go to packet currently under the cursor + Aller au paquet actuellement sous le curseur + + + Play the stream + Lire le flux + + + To Left + À gauche + + + Left + Right + Gauche + Droite + + + To Right + À droite + + + Invert Muting + Inverser le muet + + + No devices available + Aucun appareil disponible + + + Select + Sélectionner + + + Audio Routing + Routage audio + + + &Play Streams + &Lire les flux + + + Open RTP player dialog + Ouvrir la boîte de dialogue du lecteur RTP + + + &Set playlist + &Définir la liste de lecture + + + Replace existing playlist in RTP Player with new one + Remplacer la liste de lecture existante dans RTP Player par une nouvelle + + + &Add to playlist + &Ajouter à la liste de lecture + + + Add new set to existing playlist in RTP Player + Ajouter un nouvel ensemble à la liste de lecture existante dans RTP Player + + + &Remove from playlist + &Supprimer de la liste de lecture + + + Remove selected streams from playlist in RTP Player + Supprimer les flux sélectionnés de la liste de lecture dans RTP Player + + + No Audio + Pas de son + + + Decoding streams... + Décodage des flux... + + + Out of Sequence + Hors de Séquence + + + Jitter Drops + Chutes de Gigue + + + Wrong Timestamps + Mauvais Cachets + + + Inserted Silence + Silence Inséré + + + Double click on cell to change audio routing + Double-cliquer sur la cellule pour modifier le routage audio + + + %1 streams + %1 flux + + + , %1 selected + , %1 sélectionné + + + , %1 not muted + , %1 non désactivé + + + , start: %1. Double click on graph to set start of playback. + , début : %1. Double-cliquer sur le graphique pour définir le début de la lecture. + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + , début : %1, curseur : %2. Appuyer sur "G" pour aller au paquet %3. Double-cliquer sur le graphique pour définir le début de la lecture. + + + Playback of stream %1 failed! + La lecture du flux %1 a échoué ! + + + Automatic + Automatique + + + WAV (*.wav) + WAV (*.wav) + + + Sun Audio (*.au) + Sun Audio (*.au) + + + Save audio + Enregistrer le son + + + Raw (*.raw) + Brut (*.raw) + + + Save payload + Enregistrer la charge utile + + + Warning + Avertissement + + + No stream selected or none of selected streams provide audio + Aucun flux sélectionné ou aucun des flux sélectionnés ne fournit d'audio + + + Error + Erreur + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + Tous les flux sélectionnés doivent utiliser le même taux de lecture. Un réglage manuel du débit audio de sortie peut aider. + + + No streams are suitable for save + Aucun flux ne convient à l'enregistrement + + + Save failed! + Échec de la sauvegarde ! + + + Can't write header of AU file + Impossible d'écrire l'en-tête du fichier AU + + + Can't write header of WAV file + Impossible d'écrire l'en-tête du fichier WAV + + + Payload save works with just one audio stream. + La sauvegarde de la charge utile fonctionne avec un seul flux audio. + + + Double click to change audio routing + Double-cliquer pour modifier le routage audio + + + Preparing to play... + Se prépare à jouer... + + + Unknown + Inconnu + + + + RtpStreamDialog + + Dialog + Dialogue + + + Source Address + Adresse Source + + + Source Port + Port Source + + + Destination Address + Adresse Destination + + + Destination Port + Port Destination + + + SSRC + SSRC + + + Start Time + Heure de début + + + Duration + Durée + + + Payload + Payload + + + Packets + Paquets + + + Lost + Perdu + + + Max Delta (ms) + Delta Max (ms) + + + Max Jitter + Gigue Max + + + Mean Jitter + Gigue Moyenne + + + Status + Etat + + + <small><i>A hint.</i></small> + <small><i>Un indice.</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Afficher uniquement les conversations correspondant au filtre d'affichage actuel</p></body></html> + + + Limit to display filter + Limiter au filtre d'affichage + + + Time of Day + Moment de la journée + + + Find &Reverse + Rechercher &Inverser + + + Prepare &Filter + Préparer le &filtre + + + &Export + &Exporter + + + &Analyze + &Analyser + + + Open the analysis window for the selected stream(s) and add it to it + Ouvrir la fenêtre d'analyse du ou des flux sélectionnés et y ajouter + + + Find the reverse stream matching the selected forward stream. + Trouver le flux retour correspondant au flux aller sélectionné. + + + Min Delta (ms) + Delta minimum (ms) + + + Mean Delta (ms) + Delta moyen (ms) + + + Min Jitter + Gigue minimale + + + All forward/reverse stream actions + Toutes les actions de flux avant/arrière + + + R + R + + + Find All &Pairs + Trouver toutes les &paires + + + Select all streams which are paired in forward/reverse relation + Sélectionner tous les flux qui sont appariés en relation directe/inverse + + + Shift+R + Maj+R + + + Find Only &Singles + Rechercher uniquement les &célibataires + + + Find all streams which don't have paired reverse stream + Trouver tous les flux qui n'ont pas de flux inversé couplé + + + Ctrl+R + Ctrl+R + + + Mark Packets + Marquer des Paquets + + + Mark the packets of the selected stream(s). + Marqer les paquets des + + + M + M + + + All + Tout + + + Select all + Tout sélectionner + + + None + Aucun + + + Clear selection + Effacer la sélection + + + Invert + Inverser + + + Invert selection + Inverser la sélection + + + Go To Setup + Aller au Setup + + + Go to the setup packet for this stream. + Aller au paquet + + + G + G + + + Prepare a filter matching the selected stream(s). + Prépare un filtre correspondant au(x) flux sélectioné(s). + + + P + P + + + Export the stream payload as rtpdump + Exporter le payload du flux en rtpdump + + + E + E + + + A + A + + + Cop&y + Cop&ier + + + Open copy menu + Ouvrir le menu de copie + + + Copy as CSV + Copier en tant que CSV + + + Copy stream list as CSV. + Copier la liste des flux en tant que CSV. + + + Copy as YAML + Copier en tant que YAML + + + Copy stream list as YAML. + Copie la liste des flux en YAML. + + + RTP Streams + Flux RTP + + + Select + Sélectionner + + + as CSV + en CSV + + + as YAML + en YAML + + + %1 streams + %1 flux + + + , %1 selected, %2 total packets + ,%1 sélectionné, %2 paquets au total + + + Save RTPDump As… + Enregistrer RTPDump sous… + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark - SCTP Associations + + + ID + ID + + + Port 1 + Port 1 + + + Port 2 + Port 2 + + + Number of Packets + Nombre de Paquets + + + Number of DATA Chunks + Nombre de donnée Chunks + + + Number of Bytes + Nombre d'octets + + + Filter Selected Association + Filtre d'association sélectionné + + + Analyze + Analyse + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark - Association Analyse + + + TabWidget + TabWidget + + + Statistics + Statistiques + + + Chunk Statistics + Statistiques Chunk + + + Filter Association + Filtrer l'association + + + Number of Data Chunks from EP2 to EP1: + Nombre de bloc de donnée de EP2 à EP1 : + + + Checksum Type: + Type de somme de contrôle : + + + Number of Data Chunks from EP1 to EP2: + Nombre de bloc de donnée de EP1 à EP2 : + + + Number of Data Bytes from EP1 to EP2: + Nombre de donnée octets de EP1 à EP2: + + + Number of Data Bytes from EP2 to EP1: + Nombre de donnée octets de EP2 à EP1: + + + Endpoint 1 + Terminal 1 + + + Graph TSN + Graphique TSN + + + Graph Bytes + Graphique Octets + + + Requested Number of Inbound Streams: + Nombre demandé de flux entrants : + + + Port: + Port : + + + Sent Verification Tag: + Tag de Vérification envoyé : + + + Minimum Number of Inbound Streams: + Nombre minimum de flux entrants : + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + Liste complète des adresses IP de INIT Chunk : + + + Minimum Number of Outbound Streams: + Nombre minimum de flux sortants : + + + Graph Arwnd + Graphique Arwnd + + + Endpoint 2 + Terminal 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + Liste complète des adresses IP du bloc INIT_ACK : + + + Provided Number of Outbound Streams: + Nombre fourni de flux sortants : + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + Analyse Association SCTP %1 port1 %2 Port2 %3 + + + No Association found for this packet. + Aucune association trouvé pour ce paquet. + + + Warning + Avertissement + + + Could not find SCTP Association with id: %1 + Impossible de trouver l'association SCTP avec l'ID : %1 + + + Complete list of IP addresses from INIT Chunk: + Liste complète des adresses IP d'INIT Chunk : + + + Complete list of IP addresses from INIT_ACK Chunk: + Liste complète des adresses IP du bloc INIT_ACK : + + + List of Used IP Addresses + Liste des adresses IP utilisées + + + Used Number of Inbound Streams: + Nombre utilisé de flux entrants : + + + Used Number of Outbound Streams: + Nombre utilisé de flux sortants : + + + + SCTPChunkStatisticsDialog + + Dialog + Dialogue + + + Association + Association + + + Endpoint 1 + Terminal 1 + + + Endpoint 2 + Terminal 2 + + + Save Chunk Type Order + Sauvegarder type Chunk Order + + + Hide Chunk Type + Cacher Type Chunk + + + Remove the chunk type from the table + Supprimer le type chunk de la table + + + Chunk Type Preferences + Type Chunk Préferences + + + Go to the chunk type preferences dialog to show or hide other chunk types + Aller au préférence Chunk type pour afficher ou cache les autres types Chunk + + + Show All Registered Chunk Types + Afficher tous les chunk types enregistrés + + + Show all chunk types with defined names + Afficher tous les chunk types avec un nom défini + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + SCTP Statistiques Chunk: %1 Port1 %2 Port2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + Graphique SCTP + + + Reset to full size + Remise à taille réelle + + + Save Graph + Sauvegarder Graphique + + + goToPacket + AllerAuPaquet + + + Go to Packet + Aller au Paquet + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP donnée et Avc Rec fenetre au fil du temps %1 Port1 %2 Port %3 + + + No Data Chunks sent + Aucune Donnée Chunks envoiés + + + Arwnd + Arwnd + + + time [secs] + Temps [secs] + + + Advertised Receiver Window [Bytes] + Fenetre de reception diffusée [Octets] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>Graphique %1: a_rwnd=%2 Temps=%3 secs </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + Graphique SCTP + + + Reset to full size + Remise à taille réelle + + + Save Graph + Sauvegarder Graphique + + + goToPacket + AllerAuPaquet + + + Go to Packet + Aller au Paquet + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP donnée et Avc Rec fenetre au fil du temps %1 Port1 %2 Port %3 + + + No Data Chunks sent + Aucune Donnée Chunks envoiés + + + Bytes + Octets + + + time [secs] + Temps [secs] + + + Received Bytes + Octets Reçus + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>Graphique %1: Octets Reçus=%2 Time=%3 secs </i></small> + + + + SCTPGraphDialog + + SCTP Graph + Graphiquie SCTP + + + Relative TSNs + TSN relatifs + + + Only SACKs + Seulement SACKs + + + Only TSNs + Seulement TSNs + + + Show both + Afficher tout + + + Reset to full size + Remise à taille réelle + + + Save Graph + Sauvegarder Graphique + + + goToPacket + AllerAuPaquet + + + Go to Packet + Aller au Paquet + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + SCTP TSNs et SACKs au fils du temps: %1 Port1 %2 Port2 %3 + + + No Data Chunks sent + Aucune Donnée Chunks envoiés + + + CumTSNAck + CumTSNack + + + Gap Ack + Confirmation d'écart + + + NR Gap Ack + Confirmation de l'écart NR + + + Duplicate Ack + ACK dupliqué + + + TSN + TSN + + + time [secs] + Temps [secs] + + + TSNs + TSNs + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 Temps: %3 secs </i></small> + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Fichier JPEG (*.jpeg *.jpg) + + + Save Graph As… + Enregistrer le graphique sous… + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <petit><i>Sélectionner une commande et saisir un filtre si vous le souhaitez, puis appuyer sur Appliquer.</i></petit> + + + Command: + Commande : + + + SCSI Service Response Times + Temps de réponse du service SCSI + + + + SearchFrame + + Frame + Trame + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>Recherche la colonne Info dans la liste des paquets (vue sommaire), Paquet décodé par afffichage d'étiquettes (vue arboresence) or les paquets convertir en ASCII (vue d'affichage hexa).</p></body></html> + + + Packet list + Liste de Paquet + + + Packet details + Détail du paquet + + + Packet bytes + Taille du paquet + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Recherche les chaines contenant UTF-8 / ASCII ou UTF-16 caractères.</p></body></html> + + + Narrow & Wide + UTF-8 / ASCII / UTF-16 + + + Narrow (UTF-8 / ASCII) + UTF-8 / ASCII + + + Wide (UTF-16) + UTF-16 + + + Case sensitive + Sensible à la casse + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>Rechercher des données à l'aide de la syntaxe du filtre d'affichage (par exemple, ip.addr==10.1.1.1), une chaîne hexadécimale (par exemple, fffffda5), une chaîne simple (par exemple, My String) ou une expression régulière (par exemple, colou?r).</p></body></html> + + + Display filter + Filtre d'affichage + + + Hex value + Valeur hexa + + + String + Chaine de Caractères + + + Regular Expression + Expression régulière + + + Find + Chercher + + + Cancel + Annuler + + + No valid search type selected. Please report this to the development team. + Aucun type de recherche valide sélectionné. Merci de le reporter à l'équipe de développement. + + + Invalid filter. + Filtre Invalide. + + + That filter doesn't test anything. + Ce filtre ne teste rien. + + + That's not a valid hex string. + Ce n'est pas un filtre hexa valide. + + + You didn't specify any text for which to search. + Vous n'avez pas spécifié de texte à rechercher. + + + No valid character set selected. Please report this to the development team. + Aucun caractères valide sélectionné. Merci de le reporter à l'équipe de développement. + + + No valid search area selected. Please report this to the development team. + Aucune zone de recherche valide sélectionné. Merci de le reporter à l'équipe de développement. + + + Searching for %1… + Recherche de %1… + + + No packet contained those bytes. + Aucun paquet contient ces octets. + + + No packet contained that string in its Info column. + Aucun paquet contient cette chaine dans la colonne Info. + + + No packet contained that string in its dissected display. + Aucun paquet contient cette chaine de caracetere dans les paquets analysés. + + + No packet contained that string in its converted data. + Aucun paquet contient cette chaine dans les paquets convertis. + + + No packet matched that filter. + Aucun paquet corresponds a ce filtre. + + + + SequenceDialog + + Call Flow + Flux d'Appel + + + Time + Temps + + + Comment + Commentaire + + + No data + Pas de données + + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Fichier JPEG (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII (*.txt) + + + Save Graph As… + Enregistrer le graphique sous… + + + Flow + Flux + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> +<h3>Raccourcis clavier précieux et étonnants pour gagner du temps</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom avant</td></th> +<tr><th>-</th><td>Zoom arrière</td></th> +<tr><th>0</th><td>Remettre le graphique à son état initial</td></th> +<tr><th>→</th><td>Déplacer vers la droite de 10 pixels</td></th> +<tr><th>←</th><td>Déplacer vers la gauche de 10 pixels</td></th> +<tr><th>↑</th><td>Remonter de 10 pixels</td></th> +<tr><th>↓</th><td>Descendre de 10 pixels</td></th> +<tr><th><i>Maj+</i>→</th><td>Déplacer vers la droite d'1 pixel</td></th> +<tr><th><i>Maj+</i>←</th><td>Déplacer vers la gauche d'1 pixel</td></th> +<tr><th><i>Maj+</i>↑</th><td>Déplacer d'un pixel vers le haut</td></th> +<tr><th><i>Maj+</i>↓</th><td>Déplacer vers le bas d'1 pixel</td></th> + +<tr><th>g</th><td>Aller au paquet sous le curseur</td></th> +<tr><th>n</th><td>Aller au paquet suivant</td></th> +<tr><th>p</th><td>Aller au paquet précédent</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>Astuce</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>Afficher uniquement les flux correspondant au filtre d'affichage actuel</p></body></html> + + + Limit to display filter + Limiter au filtre d'affichage + + + Flow type: + Type de flux : + + + Addresses: + Adresses : + + + Any + Tout + + + Network + Réseau + + + Reset Diagram + Réinitialiser Diagramme + + + Reset &Diagram + Réinitialiser le &diagramme + + + Reset the diagram to its initial state. + Réinitialiser le diagramme à l'état inital. + + + 0 + 0 + + + &Reset Diagram + &Réinitialiser le &diagramme + + + Reset the diagram to its initial state + Réinitialiser le diagramme à son état initial + + + &Export + &Exporter + + + Export diagram + Exporter le diagramme + + + Zoom In + Agrandir + + + + + + + + + Zoom Out + Dézoomer + + + - + - + + + Move Up 10 Pixels + Monter de 10 Pixels + + + Up + Monter + + + Move Left 10 Pixels + Déplacer à Gauche de 10 Pixels + + + Left + Gauche + + + Move Right 10 Pixels + Déplacer à Droite de 10 Pixels + + + Right + Droite + + + Move Down 10 Pixels + Descendre de 10 Pixels + + + Down + Bas + + + Move Up 1 Pixel + Monter d'1 Pixel + + + Shift+Up + Maj+Haut + + + Move Left 1 Pixel + Déplacer à Gauche d'1 Pixel + + + Shift+Left + Maj+Gauche + + + Move Right 1 Pixel + Déplacer à Droite d'1 Pixel + + + Shift+Right + Maj+Droite + + + Move Down 1 Pixel + Descendre d'1 Pixel + + + Shift+Down + Maj+Bas + + + Go To Packet Under Cursor + Aller au paquet sous le curseur + + + Go to packet currently under the cursor + Aller au paquet actuellement sous le curseur + + + G + G + + + All Flows + Tous les flux + + + Show flows for all packets + Affichers tous les flux de tous les paquets + + + 1 + 1 + + + TCP Flows + Flux TCP + + + Show only TCP flow information + Afficher seulement les informations des flux TCP + + + Go To Next Packet + Aller au paquet suivant + + + Go to the next packet + Aller au paquet suivant + + + N + N + + + Go To Previous Packet + Aller au paquet précédent + + + Go to the previous packet + Aller au paquet précédent + + + P + P + + + Select RTP Stream + Sélectionner le flux RTP + + + Select RTP stream in RTP Streams dialog + Sélectionner le flux RTP dans la boîte de dialogue Flux RTP + + + S + S + + + Deselect RTP Stream + Désélectionner le flux RTP + + + Deselect RTP stream in RTP Streams dialog + Désélectionner le flux RTP dans la boîte de dialogue Flux RTP + + + D + D + + + + ShortcutListModel + + Shortcut + Raccourci + + + Name + Nom + + + Description + Description + + + + ShowPacketBytesDialog + + Show Packet Bytes + Afficher les octets de paquet + + + Hint. + Indice. + + + Decode as + Décoder comme + + + Show as + Montrer comme + + + Start + Commencer + + + End + Fin + + + Find: + Trouver : + + + Find &Next + Rechercher le &suivant + + + Frame %1, %2, %Ln byte(s). + + + + + + + None + Aucun + + + Base64 + Base64 + + + Compressed + Comprimé + + + Hex Digits + Chiffres Hex + + + Percent-Encoding + + + + Quoted-Printable + Quoted-Printable + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII & Contrôle + + + C Array + Tableau C + + + EBCDIC + EBCDIC + + + Hex Dump + Vidage Hex + + + HTML + HTML + + + Image + Image + + + Raw + Brut + + + Rust Array + Tableau de rouille + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Imprimer + + + Copy + Copier + + + Save as… + Enregistrer sous… + + + Save Selected Packet Bytes As… + Enregistrer les octets de paquet sélectionnés sous… + + + Displaying %Ln byte(s). + + + + + + + JSON + JSON + + + Regex Find: + Trouver par Regex : + + + + ShowPacketBytesTextEdit + + Show Selected + Afficher la sélection + + + Show All + Afficher tout + + + + SplashOverlay + + Initializing dissectors + Initialisation dissecteurs + + + Initializing tap listeners + Initialisation tap + + + Initializing external capture plugins + Initialisation des plugins de capture externes + + + Registering dissectors + Enregistrer dissecteurs + + + Registering plugins + Registering dissector + Enregistrement plugins + + + Handing off dissectors + Remise de dissecteurs + + + Handing off plugins + Remise de plugins + + + Loading Lua plugins + Chargement plugins Lua + + + Removing Lua plugins + Suppression plugins Lua + + + Loading module preferences + Changement du module de Préferences + + + Finding local interfaces + Recherche d'interfaces locales + + + Applying changed preferences + + + + (Unknown action) + (Action inconnue) + + + + StatsTreeDialog + + Configuration not found + Configuration non trouvée + + + Unable to find configuration for %1. + Impossible de trouver la configuration pour %1. + + + + StripHeadersDialog + + Dialog + Dialogue + + + Display filter: + Filtre d'affichage : + + + + SupportedProtocolsDialog + + Dialog + Dialogue + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Chercher dans la liste des noms de champ.</p></body></html> + + + Search: + Recherche : + + + <small><i>Gathering protocol information…</i></small> + <small><i>Regroupement de l'information de protocole…</i></small> + + + Supported Protocols + Protocoles supportés + + + %1 protocols, %2 fields. + %1 protocoles, %2 champs. + + + + SupportedProtocolsModel + + Name + Nom + + + Filter + Filtre + + + Type + Type + + + Description + Description + + + + SyntaxLineEdit + + Invalid filter: %1 + + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + "%1" est obsolète au profit de "%2". Voir la section Aide 6.4.8 pour plus de détails. + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + Dialogue + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Raccourcis-clavier précieux pour gains de temps surprenants</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom avant</td></th> +<tr><th>-</th><td>Zoom arrière</td></th> +<tr><th>y</th><td>Zoom avant axe Y</td></th> +<tr><th>Y</th><td>Zoom arrière axe Y</td></th> +<tr><th>0</th><td>Réinitialise le graphique à son état initial</td></th> + +<tr><th>→</th><td>Droite 10 pixels</td></th> +<tr><th>←</th><td>Gauche 10 pixels</td></th> +<tr><th>↑</th><td>Haut 10 pixels</td></th> +<tr><th>↓</th><td>Bas 10 pixels</td></th> +<tr><th><i>Maj+</i>→</th><td>Droite 1 pixel</td></th> +<tr><th><i>Maj+</i>←</th><td>Gauche 1 pixel</td></th> +<tr><th><i>Maj+</i>↑</th><td>Haut 1 pixel</td></th> +<tr><th><i>Maj+</i>↓</th><td>Bas 1 pixel</td></th> +</th> + +<tr><th><i>Pg Up</i></th><td>Flux suivant</td></th> +<tr><th><i>Pg Dn</i></th><td>Flux précédent</td></th> +<tr><th>d</th><td>Inverser direction (échange endpoints TCP)</td></th> +<tr><th>g</th><td>Aller (go) au paquet sous le curseur</td></th> + +<tr><th>z</th><td>Bascule trainée souris / zoom</td></th> +<tr><th>s</th><td>Bascule numéros de séquence relatifs / absolus</td></th> +<tr><th>t</th><td>Bascule origine du temps capture / session</td></th> +<tr><th>Espace</th><td>Basculer réticule</td></th> + +<tr><th>1</th><td>Graphique Aller-Retour (Round Trip Time)</td></th> +<tr><th>2</th><td>Graphique Débit (Throughput)</td></th> + +<tr><th>3</th><td>Graphique temps/séquence style Stevens</td></th> +<tr><th>4</th><td>Graphique temps/séquence style tcptrace</td></th> +<tr><th>5</th><td>Graphique taille de la Fenêtre (Window Scaling)</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>Passez la souris sur les raccourcis</i></small> + + + Type + Type + + + MA Window (s) + Fenêtre(s) MA + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + Autoriser la sélection des segments SACK ainsi que des paquets de données en cliquant sur le graphique + + + Select SACKs + select SACKs + Sélectionner SACK + + + Stream + Flux + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p> +Changer la direction de la connexion (voir le flux inverse).</p></body></html> + + + Switch Direction + Changer de direction + + + Mouse + Souris + + + Drag using the mouse button. + Faites glisser à l'aide du bouton de la souris. + + + drags + Glissement + + + Select using the mouse button. + Sélectionner à l'aide du bouton de la souris. + + + zooms + Zoom + + + Display Round Trip Time vs Sequence Number + Afficher le temps d'aller-retour par rapport au numéro de séquence + + + RTT By Sequence Number + RTT par numéro de séquence + + + Display graph of Segment Length vs Time + Afficher le graphique de la longueur du segment en fonction du temps + + + Segment Length + Longueur de segment + + + Display graph of Mean Transmitted Bytes vs Time + Afficher le graphique de la moyenne des octets transmis en fonction du temps + + + Display graph of Mean ACKed Bytes vs Time + Afficher le graphique de la moyenne des octets ACKed par rapport au temps + + + Goodput + Goodput + + + Display graph of Receive Window Size vs Time + Afficher le graphique de la taille de la fenêtre de réception en fonction du temps + + + Rcv Win + Rcv Win + + + Display graph of Outstanding Bytes vs Time + Afficher le graphique des octets en attente par rapport au temps + + + Bytes Out + Octets sortants + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Réinitialiser le graphique à son état initial.</p></body></html> + + + Reset + Réinitialiser + + + Reset Graph + Réinitialiser le Graphique + + + Reset the graph to its initial state. + Réinitialiser le graphique à son état initial. + + + 0 + 0 + + + Zoom In + Zoom Avant + + + + + + + + + Zoom Out + Zoom arrière + + + - + - + + + Move Up 10 Pixels + Monter de 10 Pixels + + + Up + Monter + + + Move Left 10 Pixels + Déplacer à Gauche de 10 Pixels + + + Left + Gauche + + + Move Right 10 Pixels + Déplacer à Droite de 10 Pixels + + + Right + Droite + + + Move Down 10 Pixels + Descendre de 10 Pixels + + + Down + Bas + + + Move Up 1 Pixel + Monter d'1 Pixel + + + Shift+Up + Maj+Haut + + + Move Left 1 Pixel + Déplacer à Gauche d'1 Pixel + + + Shift+Left + Maj+Gauche + + + Move Right 1 Pixel + Déplacer à Droite d'1 Pixel + + + Shift+Right + Maj+Droite + + + Move Down 1 Pixel + Descendre d'1 Pixel + + + Shift+Down + Maj+Bas + + + Next Stream + Flux suivant + + + Go to the next stream in the capture + Allez au prochain flux dans la capture + + + PgUp + PgUp + + + Previous Stream + Flux précedent + + + Go to the previous stream in the capture + Allez au précedent flux dans la capture + + + PgDown + PgDown + + + Switch direction (swap TCP endpoints) + Switch direction (Inverse les extrimités TCP) + + + D + D + + + Go To Packet Under Cursor + Aller au paquet sous le curseur + + + Go to packet currently under the cursor + Aller au paquet actuellement sous le curseur + + + G + G + + + Drag / Zoom + Glisser / Zoom + + + Toggle mouse drag / zoom behavior + Basculer comportant de la souris glisser / zoom + + + Z + Z + + + Relative / Absolute Sequence Numbers + Numéros de séquence Relative / Absolue + + + Toggle relative / absolute sequence numbers + Bascule de Numéros de séquence Relative à Absolue + + + S + S + + + Capture / Session Time Origin + Capture / Temps de Session d'origine + + + Toggle capture / session time origin + Basculer du temps d'origine capture / session + + + T + T + + + Crosshairs + Réticule + + + Toggle crosshairs + Basculer réticule + + + Space + Espace + + + Round Trip Time + Temps d'Aller-Retour + + + Switch to the Round Trip Time graph + Changer pour le Round Trip Time graphique + + + 1 + 1 + + + Throughput + Débit + + + Switch to the Throughput graph + Changer pour le graphique de débit + + + 2 + 2 + + + Time / Sequence (Stevens) + Temps / Séquence (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + Changer pour Stevens-style Time / Sequence Graphique + + + 3 + 3 + + + Window Scaling + Mise à l'échelle Fenêtre + + + Switch to the Window Scaling graph + Changer pour le Window Scaling graphique + + + 5 + 5 + + + Time / Sequence (tcptrace) + Temps / Séquence (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + Changer pour tcptrace style Time / Sequence graphique + + + 4 + 4 + + + Zoom In X Axis + Zoom avant Axe X + + + X + X + + + Zoom Out X Axis + Zoom arrière Axe X + + + Shift+X + Majuscule+X + + + Zoom In Y Axis + Zoom avant Axe Y + + + Y + Y + + + Zoom Out Y Axis + Zoom arrière Axe Y + + + Shift+Y + Majuscule+Y + + + Save As… + Enregistrer sous… + + + No Capture Data + Aucune donnée de capture + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 %2 pqts, %3 %4 %5 pqts, %6 + + + Sequence Numbers (Stevens) + Numéros de Séquence (Stevens) + + + Sequence Numbers (tcptrace) + Numéros de Séquence (tcptrace) + + + (MA) + (MA) + + + (%1 Segment MA) + (%1 Segment MA) + + + [not enough data] + [Pas assez de donné] + + + for %1:%2 %3 %4:%5 + pour %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + Click to select packet + Cliquez pour sélectionner le paquet + + + Packet + Paquet + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Relâchez pour agrandir, x = %1 de %2, y = %3 de %4 + + + Unable to select range. + Impossible de sélectionner la plage. + + + Click to select a portion of the graph. + Cliquez pour sélectionner une partie du graphique. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Fichier JPEG (*.jpeg *.jpg) + + + Save Graph As… + Enregistrer le graphique sous… + + + + TLSKeylogDialog + + Dialog + Dialogue + + + Browse… + + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + Sauvegarder + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + Dialogue + + + Item + élément + + + <small><i>A hint.</i></small> + <small><i>Un indice.</i></small> + + + Display filter: + Filtre d'affichage : + + + Regenerate statistics using this display filter + Régénérer les statistiques en utilisant ce filtre d'affichage + + + Apply + Appliquer + + + Copy + Copier + + + Copy a text representation of the tree to the clipboard + Copie une représentation texte de l'arborescence dans le presse-papiers + + + Save as… + Save as... + Sauvegarder sous... + + + Save the displayed data in various formats + Sauvegarde l'information affichée sous divers formats + + + Collapse All + Tout Réduire + + + Expand All + Tout Développer + + + Save Statistics As… + Enregistrer les statistiques sous… + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + Fichier plat (*.txt);;Valeurs séparées par des virgules (*.csv);;Document XML (*.xml);; Document YAML (*.yaml) + + + Plain text file (*.txt) + Fichier plat (*.txt) + + + Error saving file %1 + Erreur en sauvegardant le fichier %1 + + + + TimeShiftDialog + + Shift all packets by + Décaler tous les paquets par + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + Réglez l'heure de paquets + + + to + à + + + …then set packet + ...then set packet + ...puis régler paquet + + + and extrapolate the time for all other packets + et extrapoler le temps pour tous les autres paquets + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + Annuler tous les décalages + + + Time Shift + Décalage de temps + + + Frame numbers must be between 1 and %1. + Les numeros de trames doit être entre 1 and %1. + + + Invalid frame number. + Numero de trame invalide. + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + Erreur de fichier de carte + + + Could not open base file %1 for reading: %2 + Impossible d'ouvrir le fichier de base %1 en lecture : %2 + + + No endpoints available to map + Aucun point de terminaison disponible pour mapper + + + Unable to create temporary file + Impossible de créer un fichier temporaire + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>Affiche les adresses résolues et les noms de ports plutôt que les valeurs simples. La préférence de résolution de nom correspondante doit être activée.</p></body></html> + + + Name resolution + Résolution de nom + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Ne montre que les conversations correspondantes au filtre d'affichage courant</p></body></html> + + + Limit to display filter + Limiter au Filtre d'Affichage + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + <html><head/><body><p>Afficher uniquement les types correspondant à la valeur du filtre</p></body></html> + + + Filter list for specific type + Filtrer la liste pour un type spécifique + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>Afficher les heures absolues dans la colonne de l'heure de début.</p></body></html> + + + GroupBox + GroupBox + + + Absolute start time + Heure de début absolue + + + Copy + Copier + + + Unknown + Inconnu + + + + TrafficTree + + Resize all columns to content + Redimensionner toutes les colonnes au contenu + + + Filter on stream id + Filtrer sur l'ID de flux + + + Copy %1 table + Copier la table %1 + + + as CSV + au format CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + Copier toutes les valeurs de cette page dans le presse-papiers au format CSV (Comma Separated Values). + + + as YAML + comme YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + Copier toutes les valeurs de cette page dans le presse-papiers au format de sérialisation des données YAML. + + + as JSON + comme JSON + + + Copy all values of this page to the clipboard in the JSON data serialization format. + Copier toutes les valeurs de cette page dans le presse-papiers au format de sérialisation des données JSON. + + + Save data as raw + Enregistrer les données au format brut + + + Disable data formatting for export/clipboard and save as raw data + Désactiver le formatage des données pour l'exportation/le presse-papiers et enregistrer en tant que données brutes + + + + TrafficTreeHeaderView + + Less than + Moins que + + + Greater than + Plus grand que + + + Equal + Égal + + + Columns to display + Colonnes à afficher + + + Filter %1 by + Filtrer %1 par + + + Enter filter value + Entrer la valeur du filtre + + + + TrafficTypesModel + + Protocol + Protocole + + + + UatDialog + + Create a new entry. + Créer une nouvelle entrée + + + Remove this entry. + Remove this profile. + Supprimer cette entrée. + + + Copy this entry. + Copy this profile. + Copier cette entrée. + + + Move entry up. + Déplacer l'entrée vers le haut. + + + Move entry down. + Déplacer l'entrée vers le bas. + + + Clear all entries. + Effacer toutes les entrées. + + + Unknown User Accessible Table + inconnu UAT + + + Open + Ouvrir + + + + UatFrame + + Frame + Trame + + + Create a new entry. + Créer une nouvelle entrée. + + + Remove this entry. + Supprimer cette entrée. + + + Copy this entry. + Copier cette entrée. + + + Move entry up. + Déplacer l'entrée vers le haut. + + + Move entry down. + Déplacer l'entrée vers le bas. + + + Clear all entries. + Effacer toutes les entrées. + + + Copy entries from another profile. + Copier les entrées d'un autre profil. + + + Copy from + Copier à partir de + + + Unknown User Accessible Table + Table accessible aux utilisateurs inconnus + + + Open + Ouvrir + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Afficher uniquement les conversations correspondant au filtre d'affichage actuel</p></body></html> + + + Limit to display filter + Limiter au filtre d'affichage + + + Time of Day + Temps de la journée + + + Flow &Sequence + Flux &séquence + + + Show flow sequence for selected call(s). + Afficher la séquence de flux pour les appels sélectionnés. + + + Prepare &Filter + Préparer le &filtre + + + Prepare a filter matching the selected calls(s). + Préparer un filtre correspondant au(x) appel(s) sélectionné(s). + + + Cop&y + Cop&ier + + + Open copy menu + Ouvrir le menu de copie + + + All + Tout + + + Select all + Tout sélectionner + + + None + Aucun + + + Invert + Inverser + + + Invert selection + Inverser la sélection + + + Select related RTP streams + Sélectionner les flux RTP associés + + + Select RTP streams related to selected calls in RTP Streams dialog + Sélectionner les flux RTP liés aux appels sélectionnés dans la boîte de dialogue Flux RTP + + + S + S + + + Deselect related RTP Streams + Désélectionner les flux RTP associés + + + D + D + + + Clear selection + Effacer la sélection + + + Display time as time of day + Afficher l'heure en tant qu'heure de la journée + + + Copy as CSV + Copier au format CSV + + + Copy stream list as CSV. + Copier la liste des flux au format CSV. + + + Copy as YAML + Copier en tant que YAML + + + Copy stream list as YAML. + Copier la liste des flux au format YAML. + + + SIP Flows + Flux SIP + + + VoIP Calls + Appels VoIP + + + as CSV + au format CSV + + + as YAML + comme YAML + + + Select + Sélectionner + + + + VoipCallsInfoModel + + On + En marche + + + Off + Éteint + + + Tunneling: %1 Fast Start: %2 + Tunnelisation  : %1 Démarrage rapide  : %2 + + + Start Time + Heure de Début + + + Stop Time + Heure de Fin + + + Initial Speaker + Conférencier initial + + + From + De + + + To + À + + + Protocol + Protocole + + + Duration + Durée + + + Packets + Paquets + + + State + État + + + Comments + Commentaires + + + + WelcomePage + + Form + Formulaire + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Bienvenue dans Wireshark</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>Ouvrir un fichier sur votre système de fichiers</p></body></html> + + + <h2>Open</h2> + <h2>Ouvrir</h2> + + + Recent capture files + Fichiers de capture récents + + + Capture files that have been opened previously + Fichiers de capture ouverts précédemment + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>Capturer des paquets en direct depuis votre réseau.</p></body></html> + + + <h2>Capture</h2> + <h2>Capture</h2> + + + …using this filter: + …en utilisant ce filtre : + + + Interface list + Liste des interfaces + + + List of available capture interfaces + Liste des interfaces de capture disponibles + + + <h2>Learn</h2> + <h2>Découvrir</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + + + + Show in Finder + Afficher dans le Finder + + + Show in Folder + Afficher dans le Dossier + + + Welcome to %1 + Bienvenue dans %1 + + + All interfaces shown + Toutes les interfaces affichées + + + %n interface(s) shown, %1 hidden + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + You are sniffing the glue that holds the Internet together using Wireshark + Vous reniflez la colle qui maintient Internet ensemble à l'aide de Wireshark + + + You are running Wireshark + Vous exécutez Wireshark + + + You receive automatic updates. + Vous recevez les mises à jour automatiques. + + + You have disabled automatic updates. + Vous avez désactivé les mises à jour automatiques. + + + not found + non trouvé + + + Copy file path + Copier le chemin du fichier + + + Remove from list + Supprimer de la liste + + + + WirelessFrame + + Frame + Trame + + + Interface + Interface + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>Définit le canal 802.11 .</p></body></html> + + + Channel + Canal + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>Pendant la capture, montre toutes les trames, celles qui ont une séquence de contrôle de trame valide (Frame Check Sequence FCS), ou celles qui ont une FCS invalide.</p></body></html> + + + FCS Filter + Filtre FCS + + + All Frames + Toutes les Trames + + + Valid Frames + Trames Valides + + + Invalid Frames + Trames Invalides + + + Wireless controls are not supported in this version of Wireshark. + Les contrôles Sans-Fil ne sont pas supportés dans cette version de Wireshark. + + + External Helper + Helper Externe + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>Affiche les préférences IEEE 802.11, y compris les clés de décryption.</p></body></html> + + + 802.11 Preferences + Préférences 802.11 + + + AirPcap Control Panel + Panneau de Configuration AirPcap + + + Open the AirPcap Control Panel + Ouvrir le Panneau de Configuration AirPcap + + + Unable to set channel or offset. + Impossible de définir le canal ou le décalage. + + + Unable to set FCS validation behavior. + Impossible de définir le comportement de validation FCS. + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + Le paquet numéro %1 n'inclut pas l'horodatage TSF, n'affichant pas la chronologie. + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + Le paquet numéro %u a un saut négatif important dans la TSF, n'affichant pas la chronologie. Peut-être que le point de référence TSF est mal défini ? + + + + WiresharkDialog + + Failed to attach to tap "%1" + Échec d'attache au tap "%1" + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Aller au paquet + + + Cancel + Annuler + + + File Set + Fichier + + + Export Packet Dissections + Exporter analyse des paquets + + + Export Objects + Exporter Objets + + + &Zoom + &Zoom + + + &Time Display Format + &Format d'Affichage de l'Heure + + + Copy + Copier + + + Manual pages + Page du manuel + + + Apply as Filter + Appliquer comme un Filtre + + + Prepare as Filter + Préparer comme filtre + + + SCTP + SCTP + + + TCP Stream Graphs + Graphique des flux TCP + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &Fichier + + + &Capture + &Capture + + + &Help + &Aide + + + &Go + &Aller + + + &View + &Vue + + + &Analyze + &Analyser + + + Follow + Suivre + + + &Statistics + &Statistiques + + + 29West + 29West + + + Topics + Sujets + + + Queues + Queues + + + UIM + UIM + + + Telephon&y + Telephon&ie + + + RTSP + RTSP + + + &Edit + &Editer + + + Packet Comments + Commentaires sur les paquets + + + Main Toolbar + Barre d'outils principale + + + Display Filter Toolbar + Barre de filtrage d'affichage + + + Open a capture file + Ouvrir un fichier de capture + + + Quit Wireshark + Quitter Wireshark + + + &Start + &Démarrer + + + Start capturing packets + Démarrer la capture de paquet + + + S&top + Arrê&ter + + + Stop capturing packets + Arrêter la capturer de paquet + + + No files found + Pas de fichier trouvé + + + &Contents + &Aide + + + Wireshark Filter + Filtre Wireshark + + + TShark + TShark + + + Rawshark + Rawshark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + Texte2pcap + + + Website + Site Web + + + Downloads + Téléchargements + + + Wiki + Wiki + + + Sample Captures + Captures d'Exemple + + + &About Wireshark + &A Propos de Wireshark + + + Ask (Q&&A) + Questions (Q&&A) + + + Next Packet + Paquet suivant + + + Go to the next packet + ALler au paquet suviant + + + Previous Packet + Paquet précédent + + + Go to the previous packet + Aller au paquet précédent + + + First Packet + Premier paquet + + + Go to the first packet + Aller au premier paquet + + + Last Packet + Dernier paquet + + + Go to the last packet + Aller au dernier paquet + + + E&xpand Subtrees + E&tendre les sous menus + + + Expand the current packet detail + Etendre les détails du paquet en cours + + + &Expand All + &Etendre tout + + + Expand packet details + Etendre les détails du paquet + + + Collapse &All + Reduire &Tout + + + Collapse all packet details + Reduire les détails de tous les paquets + + + Go to specified packet + Aller au paquet + + + Merge one or more files + Fusionner un ou plusieurs fichiers + + + Import a file + Importer un fichier + + + &Save + &Sauvegarder + + + Save as a different file + Sauvegarder dans un fichier différent + + + Export specified packets + Exporter les paquets specifiés + + + Export TLS Session Keys… + Exporter les clés de session TLS… + + + List Files + Liste des fichiers + + + Next File + Fichier suivant + + + Previous File + Ficher précedent + + + &Reload + &Recharger + + + Options + Options + + + Capture options + Options de capture + + + Capture filters + Filtres de Capture + + + Refresh Interfaces + Actualiser les interfaces + + + Refresh interfaces + Actualiser les interfaces + + + &Restart + &Redémarrer + + + Restart current capture + Redémarrer la capture en cours + + + As &CSV… + Au format &CSV… + + + As "C" &Arrays… + Comme "C" &Tableaux… + + + As P&SML XML… + Comme P&SML XML… + + + As P&DML XML… + Comme P&DML XML… + + + As &JSON… + Comme &JSON… + + + Description + Description + + + Field Name + Nom du champs + + + Value + Valeur + + + As Filter + Comme un Filtre + + + Close this capture file + Fermer ce fichier de capture + + + Packet: + Paquets : + + + Interface Toolbars + Barres d'outils de l'interface + + + Colorize Conversation + Colorier la Conversation + + + Internals + Internes + + + Additional Toolbars + Barres d'outils supplémentaires + + + Conversation Filter + Filtre de Conversation + + + Reliable Server Pooling (RSerPool) + Mise en commun fiable de serveurs (RSerPool) + + + SOME/IP + SOME/IP + + + &DTN + + + + Osmux + Osmux + + + &Tools + Tools + &Outils + + + Wireless Toolbar + Barre d'outils Wireless + + + Help contents + Contenu de l'aide + + + FAQs + FAQ + + + Next Packet in Conversation + Paquet Suivant dans la Conversation + + + Go to the next packet in this conversation + Aller au paquet suivant de cette conversation + + + Previous Packet in Conversation + Paquet Précédent dans la Conversation + + + Go to the previous packet in this conversation + Aller au paquet précédent de cette conversation + + + Next Packet In History + Paquet suivant dans l'historique + + + Go to the next packet in your selection history + Passer au paquet suivant dans votre historique de sélection + + + Previous Packet In History + Paquet précédent dans l'historique + + + Go to the previous packet in your selection history + Aller au paquet précédent dans votre historique de sélection + + + Collapse Subtrees + Réduire les sous-arborescences + + + Collapse the current packet detail + Réduire le détail du paquet actuel + + + Go to Packet… + Aller au Paquet... + + + &Merge… + &Fusionner... + + + &Import from Hex Dump… + &Importer depuis Dump Hex... + + + Save this capture file + Sauvegarder ce fichier de capture + + + Save &As… + Sauvegarder &Sous + + + Export Specified Packets… + Exporter Paquets Spécifiques... + + + Export Packet &Bytes… + Exporter Paquets &Octets... + + + &Print… + &Imprimer... + + + Reload this file + Recharger ce fichier + + + Reload as File Format/Capture + Recharger en tant que format de fichier/capture + + + Copy this item's description + Copier la description de cet élément + + + Copy this item's field name + Copier le nom de cet élément + + + Copy this item's value + Copier la valeur de cet élément + + + Copy this item as a display filter + Copier cet élément comme un filtre d'affichage + + + Apply as Column + Appliquer en Colonne + + + Create a packet list column from the selected field. + Crée une colonne de liste de paquets d'après le champ sélectionné. + + + Find a packet + Trouver un paquet + + + Find the next packet + Trouver le paquet suivant + + + Find the previous packet + Trouver le paquet précédent + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + &Marquer/Démarquer le(s) paquet(s) + + + Mark All Displayed + Marquer tout comme Affichées + + + Mark all displayed packets + Marquer tous les paquets affichés + + + Unmark all displayed packets + Démarquer tous paquets affichés + + + Next Mark + Marque suivante + + + Go to the next marked packet + Aller au prochain paquet marqués + + + Previous Mark + Marque Précédente + + + Go to the previous marked packet + Aller au paquet précédent marqué + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + &Ignorer/Annuler le(s) paquet(s) + + + Ignore All Displayed + Ignorer tous les affichés + + + Ignore all displayed packets + Ignorer tous les paquets affichés + + + Set/Unset Time Reference + Fixer/Defixer le Temps de Réference + + + Set or unset a time reference for this packet + Fixer ou défixer le temps de réference de ce paquet + + + Unset All Time References + Défixer tous les Temps de Réferences + + + Remove all time references + Supprimer tous les temps de réferences + + + Next Time Reference + Prochain Temps de Réference + + + Go to the next time reference + Aller au prochain temps de réference + + + Previous Time Reference + Précédent Temps de réference + + + Go to the previous time reference + Aller au précédent temps de réference + + + Shift or change packet timestamps + Décalager ou changer le temps des paquets + + + Delete All Packet Comments + Supprimer tous les commentaires de paquet + + + Remove all packet comments in the capture file + Supprimer tous les commentaires de paquet dans le fichier de capture + + + &Configuration Profiles… + &Profils de configuration… + + + Configuration profiles + Profils de configuration + + + Manage your configuration profiles + Gerer mes profils de configuration + + + Manage Wireshark's preferences + Gerer les préferences de Wireshark + + + Capture File Properties + Propriétés du fichier de capture + + + Capture file properties + Propriétés du fichier de capture + + + &Protocol Hierarchy + &Hiérarchie des Protocoles + + + Show a summary of protocols present in the capture file. + Montre un résumé des protocoles présents dans le fichier de capture + + + Capinfos + Capinfos + + + Reordercap + Réoragnisercap + + + Time Sequence (Stevens) + Séquence de temps (Stevens) + + + TCP time sequence graph (Stevens) + Diagramme de séquence de temps de TCP (Stevens) + + + Throughput + Débit + + + Round Trip Time + Temps d'Aller-Retour + + + TCP round trip time + Temps d'aller-retour TCP + + + Window Scaling + Mise à l'échelle Fenêtre + + + TCP window scaling + Mise à l'échelle de la fenêtre TCP + + + HTTP/2 Stream + Flux HTTP/2 + + + SIP Call + Appel SIP + + + Time Sequence (tcptrace) + Séquence de temps (tcptrace) + + + TCP time sequence graph (tcptrace) + Diagramme de séquence de temps de TCP (tcptrace) + + + Analyse this Association + Analyser cette association + + + Show All Associations + Afficher toutes les association + + + Flow Graph + Graphique des flux + + + Flow sequence diagram + Diagramme de séquence des flux + + + ANCP + ANCP + + + ANCP statistics + statistiques ANCP + + + Packets sorted by Instance ID + Paquets triés par Instance ID + + + BACapp statistics sorted by instance ID + Statistiques BACAPP triés par instance ID + + + Packets sorted by IP + Paquets triés par IP + + + BACapp statistics sorted by IP + Statistiques BACapp triés par IP + + + Packets sorted by object type + Paquets triés par object type + + + BACapp statistics sorted by object type + Statistiques BACap triés par objet type + + + Packets sorted by service + Paquets triés par service + + + BACapp statistics sorted by service + Statistiques BACapp triés par service + + + Collectd + Collectd + + + Collectd statistics + Statistiques Collectd + + + DNS + DNS + + + DNS statistics + Statistiques DNS + + + HART-IP + HART-IP + + + HART-IP statistics + Statistiques HART-IP + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + statistiques hpfeeds + + + HTTP2 + HTTP2 + + + HTTP2 statistics + Statistiques HTTP2 + + + Packet Counter + Compteur de paquet + + + HTTP packet counter + Compteur de paquet HTTP + + + Requests + Requetes + + + HTTP requests + Requetes HTTP + + + Load Distribution + Répartiton de charge + + + HTTP load distribution + Répartition de charge HTTP + + + Packet Lengths + Longueur de Paquet + + + Packet length statistics + Statistiques de longueur de paquet + + + Sametime + Sametime + + + Sametime statistics + Statistiques Sametime + + + SOME/IP Messages + SOME/messages IP + + + SOME/IP Message statistics + Statistiques des messages SOME/IP + + + SOME/IP-SD Entries + Entrées SOME/IP-SD + + + SOME/IP-SD Entries statistics + Statistiques des entrées SOME/IP-SD + + + &LTP + + + + LTP segment and block statistics + + + + &ISUP Messages + Messages &ISUP + + + ISUP message statistics + Statistiques de message ISUP + + + Osmux packet counts + Nombre de paquets Osmux + + + RTSP packet counts + Compteur de paquet RTSP + + + SM&PP Operations + Opérations SM&PP + + + SMPP operation statistics + Statistiques des opérations SMPP + + + &UCP Messages + Messages &UCP + + + UCP message statistics + statistiques des messages UCP + + + F1AP + F1AP + + + F1AP Messages + Messagerie F1AP + + + NGAP + NGAP + + + NGAP Messages + Messages NGAP + + + Change the way packets are dissected + Modifier la manière dont les paquets sont analysés + + + Reload Lua Plugins + Recharger Plugins Lua + + + Reload Lua plugins + Recharger plugins Lua + + + Advertisements by Topic + Annonces par Sujet + + + Advertisements by Source + Annonces par Source + + + Advertisements by Transport + Annonces par Transport + + + Queries by Topic + Requêtes par élément + + + Queries by Receiver + Requêtes par Récepteur + + + Wildcard Queries by Pattern + Requêtes génériques par modèle + + + Wildcard Queries by Receiver + Requêtes génériques par récepteur + + + Advertisements by Queue + Annonces par Queue + + + Queries by Queue + Requêtes par Queue + + + Streams + Flux + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + Filtrer cette association + + + Strip Headers… + En-têtes de bande… + + + Strip headers and export higher level encapsulations to file + Supprimer les en-têtes et exporter les encapsulations de niveau supérieur vers un fichier + + + &I/O Graphs + &Graphiques d'E/S + + + &Conversations + &Conversations + + + &Endpoints + &Points de terminaison + + + Shrink the main window text + Rétrécir le texte de la fenêtre principale + + + Return the main window text to its normal size + Ramener le texte de la fenêtre principale à sa taille normale + + + Reset Layout + Réinitialiser la mise en page + + + Reset appearance layout to default size + Réinitialiser la disposition de l'apparence à la taille par défaut + + + Seconds Since First Captured Packet + Secondes depuis le premier paquet capturé + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + Paquet du &diagramme + + + Show or hide the packet diagram + Afficher ou masquer le diagramme de paquet + + + Show each conversation hash table + Afficher chaque table de hachage de conversation + + + Show each dissector table and its entries + Montrer chaque table de dissecteur et ses entrées + + + Show the currently supported protocols and display filter fields + Afficher les protocoles actuellement pris en charge et afficher les champs de filtre + + + MAC Statistics + Statistiques MAC + + + LTE MAC statistics + Statistiques LTE MAC + + + RLC Statistics + Statistiques RLC + + + LTE RLC statistics + Statistiques LTE RLC + + + LTE RLC graph + Graphique LTE RLC + + + MTP3 Summary + Résumé MTP3 + + + MTP3 summary statistics + Résumé des statistiques MTP3 + + + Bluetooth Devices + Équipements Bluetooth + + + Bluetooth HCI Summary + Résumé Bluetooth HCI + + + Display Filter &Expression… + Filtre d'affichage d'&expression… + + + Display Filter Expression… + Afficher l'expression du filtre… + + + REGISTER_STAT_GROUP_RSERPOOL + REGISTER_STAT_GROUP_RSERPOOL + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + Début de "REGISTER_STAT_GROUP_RSERPOOL" + + + No GSM statistics registered + Aucune statistique GSM enregistrée + + + No LTE statistics registered + Aucune statistique LTE enregistrée + + + No MTP3 statistics registered + Aucune statistique MTP3 enregistrée + + + IAX2 Stream Analysis + Analyse Flux IAX2 + + + Show Packet Bytes… + Afficher les octets de paquet… + + + Go to &Linked Packet + Aller à &paquet lié + + + UDP Multicast Streams + Flux Multicast UDP + + + Show UTP multicast stream statistics. + Afficher les statistiques de flux de multidiffusion UTP. + + + WLAN Traffic + Trafic WLAN + + + Show IEEE 802.11 wireless LAN statistics. + Afficher les statistiques du LAN sans fil IEEE 802.11. + + + Add a display filter button. + Ajouter un bouton filtre d'affichage + + + Firewall ACL Rules + Règles ACL du pare-feu + + + Create firewall ACL rules + Créer des règles ACL de pare-feu + + + &Full Screen + &Plein écran + + + Credentials + Identifiants + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + &Options... + + + &Wireless + &Wireless + + + Capture &Filters… + &Filtres de Capture... + + + As Plain &Text… + En tant que &texte brut… + + + As Plain &Text + En tant que &texte brut + + + As &CSV + Au format &CSV + + + As &YAML + Comme &YAML + + + All Visible Items + Tous les Éléments Visibles + + + All Visible Selected Tree Items + Tous les éléments de l'arborescence sélectionnés visibles + + + Display Filter &Macros… + Afficher les Filtres &Macros... + + + &Find Packet… + &Trouver un Paquet... + + + Find Ne&xt + Trouver Suiva&nt + + + Find Pre&vious + Trouver Pré&cédent + + + Mark or unmark each selected packet + Marquer ou décocher chaque paquet sélectionné + + + Ignore or unignore each selected packet + Ignorer ou désignorer chaque paquet sélectionné + + + U&nignore All Displayed + Ne pas &ignorer tous les éléments affichés + + + Unignore all displayed packets + Ne plus ignorer tous les paquets affichés + + + Time Shift… + Décalage dans le temps… + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + &Préférences... + + + TCP throughput + Débit TCP + + + Request Sequences + Séquences de requêtes + + + HTTP Request Sequences + Séquences de requête HTTP + + + Decode &As… + Décoder &Comme... + + + Export PDUs to File… + Exporter PDU vers un Fichier... + + + Create graphs based on display filter fields + Créer des graphiques basés sur des filtres d'affichage + + + &Main Toolbar + &Barre d'Outils Principale + + + Show or hide the main toolbar + Afficher ou masquer la barre d'outils principale + + + &Filter Toolbar + &Barre d'outils Filtre + + + Show or hide the display filter toolbar + Afficher/Masquer la barre d'outils filtre d'affichage + + + Conversations at different protocol levels + Conversations à différents niveaux de protocole + + + Endpoints at different protocol levels + Terminaux à différents niveaux de protocole + + + Colorize Packet List + Coloriser la liste de paquets + + + Draw packets using your coloring rules + Dessiner des paquets en utilisant vos règles de coloration + + + &Zoom In + Zoom Avant + + + Enlarge the main window text + Agrandir le texte de la fenêtre principale + + + Zoom Out + Zoom arrière + + + Normal Size + Taille Normale + + + Resize Columns + Redimensionner les Colonnes + + + Resize packet list columns to fit contents + Ajuster les colonnes de la liste des paquets au contenu + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Date et Heure du Jour (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + Voir les temps paquets comme la date et l'heure du jour. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Année, Jour de l'Année, et Heure du Jour (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Afficher les temps paquets avec l'année, le jour de l'année et l'heure du jour. + + + Time of Day (01:02:03.123456) + Heure du Jour (01:02:03.123456) + + + Seconds Since 1970-01-01 + Secondes depuis 01-01-1970 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Afficher les temps paquets en secondes depuis UNIX / POSIX epoch (1970-01-01). + + + Seconds Since Previous Captured Packet + Secondes Depuis le Paquet Capturé Précédent + + + Show packet times as the seconds since the previous captured packet. + Afficher les temps paquets en secondes depuis le précédent paquet capturé. + + + Seconds Since Previous Displayed Packet + Secondes Depuis le Paquet Affiché Précédent + + + Show packet times as the seconds since the previous displayed packet. + Afficher les temps paquets en secondes depuis le précédent paquet affiché. + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + Date et Heure du jour UTC (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Voir les paquets avec la date et l'heure du jour UTC. + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Année UTC, Jour de l'Année, et Heure du Jour (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + Afficher les temps paquets avec l'Année UTC, jour de l'année et heure du jour. + + + UTC Time of Day (01:02:03.123456) + Heure du Jour UTC (01:02:03.123456) + + + Show packet times as the UTC time of day. + Afficher les paquets avec l'heure du jour UTC. + + + Automatic (from capture file) + Automatique (du fichier de capture) + + + Use the time precision indicated in the capture file. + Utiliser la précision temporelle indiquée dans le fichier de capture. + + + Seconds + Secondes + + + Tenths of a second + Dixièmes de secondes + + + Hundredths of a second + Centièmes de secondes + + + Milliseconds + Millisecondes + + + Microseconds + Microsecondes + + + Nanoseconds + Nanosecondes + + + Display Seconds With Hours and Minutes + Afficher les Secondes Avec les Heures et Minutes + + + Display seconds with hours and minutes + Afficher les secondes avec les heures et minutes + + + Resolve &Physical Addresses + Résoudre &Adresse Physique + + + Show names for known MAC addresses. Lookups use a local database. + Afficher les noms des adresses MAC connues. Les recherches utilisent une base de données locale. + + + Resolve &Network Addresses + Résoudre &Adresse Réseau + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + Afficher les noms des adresses IPv4, IPv6 et IPX connues. Les recherches peuvent générer du trafic réseau. + + + Resolve &Transport Addresses + Résoudre &Adresse Transport + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + Afficher les noms des services TCP, UDP et SCTP connus. Les recherches peuvent générer du trafic sur certains systèmes. + + + Wire&less Toolbar + Barre d'Outils Wireless + + + Show or hide the wireless toolbar + Afficher/Masquer la barre d'outils Wireless + + + &Status Bar + &Barre de Statut + + + Show or hide the status bar + Afficher ou masquer la barre de statut + + + Packet &List + Paquets &List + + + Show or hide the packet list + Afficher ou masquer la liste de paquet + + + Packet &Details + Paquet &Détails + + + Show or hide the packet details + Afficher ou masquer les détails des paquets + + + Packet &Bytes + Paquets &Octets + + + Show or hide the packet bytes + Afficher ou masquer les octets du paquet + + + &Conversation Hash Tables + &Tables de hachage des conversations + + + &Dissector Tables + &Tables de dissecteurs + + + &Supported Protocols + &Protocoles pris en charge + + + MAP Summary + Résumé MAP + + + GSM MAP summary statistics + Résumé des statistiques GSM MAP + + + RLC &Graph + RLC &graphique + + + &Coloring Rules… + &Règles de coloriage… + + + Show Linked Packet in New Window + Afficher le paquet lié dans une nouvelle fenêtre + + + New Coloring Rule… + New Conversation Rule… + Nouvelle règle de coloration… + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + Analyse de flux RTP pour le flux sélectionné. Appuyer sur la touche CTRL pour ajouter également le flux inverse. + + + RTP Player + Lecteur RTP + + + Play selected stream. Press CTRL key for playing reverse stream too. + Lire le flux sélectionné. Appuyer sur la touche CTRL pour lire également le flux inverse. + + + IA&X2 Stream Analysis + Analyse de flux IA&X2 + + + Enabled Protocols… + Enable Protocols… + Protocoles activés... + + + Wiki Protocol Page + Page Wiki + + + Open the Wireshark wiki page for this protocol. + Ouvrir la page wiki Wireshark pour ce protocole. + + + Filter Field Reference + Référence des champs de filtre + + + Open the display filter reference page for this filter field. + Ouvrir la page de référence du filtre d'affichage pour ce champ de filtre. + + + Go to the packet referenced by the selected field. + Aller au paquet référencé par le champ sélectionné. + + + &VoIP Calls + &Appels VoIP + + + Open &Recent + Ouvrir &récent + + + Name Resol&ution + Nom Résol&ution + + + Service &Response Time + &Temps de réponse du service + + + &RTP + &RTP + + + S&CTP + S&CTP + + + &ANSI + &ANSI + + + &GSM + &GSM + + + &LTE + &LTE + + + &MTP3 + &MTP3 + + + &Open + &Ouvrir + + + &Quit + &Quitter + + + &Close + &Fermer + + + Display &Filters… + Afficher les &filtres… + + + &Unmark All Displayed + &Désélectionner tous les éléments affichés + + + All VoIP Calls + Tous les Appels VoIP + + + SIP &Flows + SIP &Flux + + + SIP Flows + Flux SIP + + + RTP Streams + Flux RTP + + + Edit the packet list coloring rules. + Editer les règles de coloriage de la liste des paquets. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Attributs Server Bluetooth ATT + + + Show Packet in New &Window + Affiche Paquet dans Nouvelle &Fenêtre + + + Show this packet in a separate window. + Affiche ce paquet dans une fenêtre séparée. + + + Show the linked packet in a separate window. + Affiche le paquet lié dans une fenêtre séparée. + + + Auto Scroll in Li&ve Capture + Défilement Auto en Capture &Vivante + + + Automatically scroll to the last packet during a live capture. + Défiler automatiquement jusqu'au dernier paquet durant une capture en temps réel. + + + Expert Information + Information Expert + + + Show expert notifications + Afficher notifications expert + + + Add an expression to the display filter. + Ajoute une expression au filtre d'affichage. + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + Début de "REGISTER_STAT_GROUP_UNSORTED" + + + No ANSI statistics registered + No tools registered + Aucune statistique ANSI enregistrée + + + Resolved Addresses + Adresses résolues + + + Show each table of resolved addresses as copyable text. + Affiche chaque table d'adresses résolues en texte copiable. + + + Color &1 + Couleur &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + Marquer la conversation courante avec sa propre couleur. + + + Color &2 + Couleur &2 + + + Color &3 + Couleur &3 + + + Color &4 + Color &5 + + + Color &5 + Color &6 + + + Color &6 + Color & + + + Color &7 + Color &7 + + + Color &8 + Color &8 + + + Color &9 + Couleur &9 + + + Color 1&0 + Couleur 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + Créer une nouvelle règle de coloration basée sur ce champ. + + + Reset Colorization + Rétablit Coloriage + + + Reset colorized conversations. + Rétablit conversations coloriées. + + + RTP Stream Analysis + Analyse flux RTP + + + Edit Resolved Name + Editer Nom Résolu + + + Manually edit a name resolution entry. + Edite manuellement une entrée de résolution de nom. + + + Enable and disable specific protocols + Active et désactive des protocoles spécifiques + + + before quitting + avant de quitter + + + Save packets before merging? + Sauvegarder les paquets avant la fusion? + + + A temporary capture file can't be merged. + une capture temporaire ne peut pas etre fusionner. + + + Save changes in "%1" before merging? + Sauvegarder les changements dans "%1" avant la fusion? + + + Changes must be saved before the files can be merged. + Les changements doivent etre sauvegarder avant la fusion de fichier. + + + Invalid Display Filter + Filtre d'affichage invalide + + + Invalid Read Filter + Filtre de Lecture Invalide + + + The filter expression %1 isn't a valid read filter. (%2). + L'expression de filtre %1 n'est pas un filtre de lecture valide. (%2). + + + before importing a capture + before importing a new capture + avant d'importer une capture + + + Unable to export to "%1". + Impossible d'exporter dans "%1". + + + You cannot export packets to the current capture file. + Vous ne pouvez pas exporter les paquets de la capture en cours. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + Voulez-vous enregistrer les modifications que vous aver apportées %1 ? + + + Your captured packets will be lost if you don't save them. + Votre capture de paquet sera perdu si vous ne les enregistrez pas. + + + Do you want to save the changes you've made to the capture file "%1"%2? + Voulez-vous enregistrer les modifications que vous avez apportées au fichier de capture "%1"%2? + + + Your changes will be lost if you don't save them. + Vos changements seront perdus si vous ne les enregistrez pas. + + + Check for Updates… + Vérifier les mises à jour… + + + Unable to drop files during capture. + Impossible de déposer des fichiers pendant la capture. + + + Unknown file type returned by merge dialog. + Type de fichier inconnu renvoyé par la boîte de dialogue de fusion. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Veuillez signaler ce problème comme un problème Wireshark sur https://gitlab.com/wireshark/wireshark/-/issues. + + + Unknown file type returned by export dialog. + Type de fichier inconnu renvoyé par la boîte de dialogue d'exportation. + + + Do you want to stop the capture and save the captured packets%1? + Voulez-vous arrêter la capture et enregistrer les paquets capturés %1 ? + + + Do you want to save the captured packets%1? + Voulez-vous enregistrer les paquets capturés %1 ? + + + Save before Continue + Enregistrer avant de continuer + + + Stop and Save + Arrêter et Sauvegarder + + + Stop and Quit &without Saving + Stop and Quit without Saving + Arrêter et Quitter sans Sauvegarder + + + Quit &without Saving + Quit without Saving + Quitter &sans enregistrer + + + There is no "rtp.ssrc" field in this version of Wireshark. + Il n'y a pas de "rtp.ssrc" champ dans cette version de Wireshark. + + + Please select an RTPv2 packet with an SSRC value + Veuillez sélectionner un paquet RTPv2 avec une valeur SSRC + + + SSRC value not found. + Valeur SSRC introuvable. + + + Show or hide the toolbar + Afficher ou masquer la barre d'outils + + + Continue &without Saving + Continue without Saving + Continuer &sans enregistrer + + + Stop and Continue &without Saving + Stop and Continue without Saving + Arrêter et continuer &sans enregistrer + + + The Wireshark Network Analyzer + L'analyseur de réseau Wireshark + + + Capturing from %1 + Capture en cours de %1 + + + before opening another file + avant d'ouvrir un autre fichier + + + Merging files. + Fusion de fichiers. + + + %1: %2 + %1 : %2 + + + Clear Menu + Effacer les fichiers récemment ouverts + + + before closing the file + avant de fermer le dossier + + + Export Selected Packet Bytes + Exporter Octets de Paquets Sélectionnés + + + No Keys + Aucune clés + + + Raw data (*.bin *.dat *.raw);;All Files ( + Données brutes (*.bin *.dat *.raw);;Tous les fichiers ( + + + Couldn't copy text. Try another item. + Impossible de copier le texte. Essayer un autre item. + + + Are you sure you want to remove all packet comments? + Voulez-vous vraiment supprimer tous les commentaires sur les paquets ? + + + Unable to build conversation filter. + Impossible de construire un filtre de conversation. + + + before reloading the file + avant de recharger le fichier + + + Error compiling filter for this conversation. + Erreur dans la compilation du filtre pour cette conversation. + + + No previous/next packet in conversation. + Aucun paquet précédent/suivant dans la conversation + + + No interface selected. + Aucune interface sélectionnée. + + + Saving %1… + Enregistrement de %1… + + + Configure all extcaps before start of capture. + Configurer tous les extcaps avant le début de la capture. + + + Invalid capture filter. + Filtre de capture invalide. + + + (empty comment) + placeholder for empty comment + (commentaire vide) + + + Add New Comment… + Ajouter un nouveau commentaire… + + + Edit "%1" + edit packet comment + Modifier "%1" + + + Delete "%1" + delete packet comment + Supprimer "%1" + + + Delete packet comments + Supprimer les commentaires du paquet + + + Delete comments from %n packet(s) + + + + + + + before starting a new capture + avant de commencer une nouvelle capture + + + before reloading Lua plugins + avant de recharger les plugins Lua + + + Please wait while Wireshark is initializing… + Veuillez patienter pendant l'initialisation de Wireshark… + + + before updating + + + + There are no TLS Session Keys to save. + Il n'y a pas de clés de session TLS à enregistrer. + + + Export TLS Session Keys (%Ln key(s)) + + + + + + + TLS Session Keys (*.keys *.txt);;All Files ( + Clés de session TLS (*.keys *.txt);;Tous les fichiers ( + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + Aucun filtre disponible. Essayer un autre %1. + + + column + colonne + + + item + Objet + + + The "%1" column already exists. + Le "%1" colonne existe déjà. + + + The "%1" column already exists as "%2". + Le "%1" existe déjà en tant que "%2". + + + RTP packet search failed + La recherche de paquet RTP a échoué + + + No Interface Selected. + Aucune interface sélectionnée. + + + before restarting the capture + avant de relancer la capture + + + Wiki Page for %1 + Page Wiki pour %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Le Wiki Wireshark est maintenu par la communauté</p> +<p>La page que vous vous apprêtez à charger peut être magnifique, incomplète, fausse ou inexistante</p> +<p>Poursuivre vers le wiki?</p> + + + Loading + Chargement + + + Reloading + Rechargement + + + Rescanning + Rebalayage + + + + WlanStatisticsDialog + + Wireless LAN Statistics + Statistiques LAN Wireless + + + Channel + Canal + + + SSID + SSID + + + Percent Packets + Pourcent Paquets + + + Percent Retry + Pourcentage de retransmission + + + Probe Reqs + Reqts Sonde + + + Probe Resp + Rép Sonde + + + Auths + Auths + + + Retry + Retransmission + + + Deauths + Désauthentification + + + Other + Autre + + + diff --git a/ui/qt/wireshark_it.ts b/ui/qt/wireshark_it.ts new file mode 100644 index 00000000..19227f66 --- /dev/null +++ b/ui/qt/wireshark_it.ts @@ -0,0 +1,14839 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + Informazioni su Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Analizzatore di protocolli di rete</span> + + + Copy the version information to the clipboard + Copia le informazioni di versione negli appunti + + + Copy to Clipboard + Copia negli appunti + + + Authors + Autori + + + Search Authors + Cerca autori + + + Folders + Cartelle + + + Filter by path + Filtro per percorso + + + Plugins + Plugin + + + No plugins found. + Non è stato trovato alcun plugin. + + + Search Plugins + Cerca plugin + + + Filter by type: + Filtro per tipo: + + + Keyboard Shortcuts + Scorciatoie da tastiera + + + Search Shortcuts + Scorciatoie di ricerca + + + Acknowledgments + Riconoscimenti + + + License + Licenza + + + The directory does not exist + La cartella non esiste + + + Should the directory %1 be created? + Dovrebbe essere creata la cartella %1? + + + The directory could not be created + La cartella non può essere creata + + + The directory %1 could not be created. + La cartella %1 non può essere creata. + + + Show in Finder + Mostra in Finder + + + Show in Folder + Mostra in Finder + + + Copy + Copia + + + Copy Row(s) + + Copia riga + Copia righe + + + + + AddressEditorFrame + + Frame + Frame + + + Name Resolution Preferences… + Name Resolution Preferences... + Preferenze di risoluzione dei nomi... + + + Address: + Indirizzo: + + + Name: + Nome: + + + Can't assign %1 to %2. + Impossibile assegnare %1 a %2. + + + + AdvancedPrefsModel + + Name + Nome + + + Status + Stato + + + Type + Tipo + + + Value + Valore + + + + ApplyLineEdit + + Apply changes + Applica modifiche + + + + AuthorListModel + + Name + Nome + + + Email + Email + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Attributi server ATT Bluetooth + + + Handle + Handle + + + UUID + UUID + + + UUID Name + Nome UUID + + + All Interfaces + Tutte le interfacce + + + All Devices + Tutti i dispositivi + + + Remove duplicates + Rimuovi i duplicati + + + Copy Cell + Copia cella + + + Copy Rows + Copia righe + + + Copy All + Copia tutto + + + Save as image + Salva come immagine + + + Mark/Unmark Row + Marca/Deseleziona riga + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Marca/Deseleziona cella + + + Save Table Image + Salva immagine tabella + + + PNG Image (*.png) + Immagine PNG (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + Dispositivo Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Nome + + + Class of Device + Classe del dispositivo + + + LMP Version + Versione LMP + + + LMP Subversion + Sottoversione LMP + + + Manufacturer + Produttore + + + HCI Version + Versione HCI + + + HCI Revision + Revisione HCI + + + Scan + Scansione + + + Authentication + Autenticazione + + + Encryption + Cifratura + + + ACL MTU + MTU ACL + + + ACL Total Packets + Pacchetti totali ACL + + + SCO MTU + MTU SCO + + + SCO Total Packets + Pacchetti totali SCO + + + LE ACL MTU + MTU ACL LE + + + LE ACL Total Packets + Pacchetti totali ACL LE + + + LE ISO MTU + MTU ISO LE + + + LE ISO Total Packets + Pacchetti totali ISO LE + + + Inquiry Mode + Modalità di richiesta + + + Page Timeout + Timeout pagina + + + Simple Pairing Mode + Modalità di accoppiamento semplice + + + Voice Setting + Impostazioni voce + + + Value + Valore + + + Changes + Modifiche + + + %1 changes + %1 modifiche + + + Copy Cell + Copia cella + + + Copy Rows + Copia righe + + + Copy All + Copia tutto + + + Save as image + Salva come immagine + + + Mark/Unmark Row + Marca/Deseleziona riga + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Marca/Deseleziona cella + + + Unknown + Sconosciuto + + + Bluetooth Device - %1%2 + Dispositivo Bluetooth - %1%2 + + + enabled + abilitato + + + disabled + disabilitato + + + %1 ms (%2 slots) + %1 ms (%2 slot) + + + Save Table Image + Salva immagine tabella + + + PNG Image (*.png) + Immagine PNG (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Dispositivi Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Nome + + + LMP Version + Versione LMP + + + LMP Subversion + Sottoversione LMP + + + Manufacturer + Produttore + + + HCI Version + Versione HCI + + + HCI Revision + Revisione HCI + + + Is Local Adapter + è un adattatore locale + + + All Interfaces + Tutte le interfacce + + + Show information steps + Mostra i passaggi informativi + + + %1 items; Right click for more option; Double click for device details + %1 elementi; clic del tasto destro per altre opzioni; doppio clic per i dettagli del dispositivo + + + Copy Cell + Copia cella + + + Copy Rows + Copia righe + + + Copy All + Copia tutto + + + Save as image + Salva come immagine + + + Mark/Unmark Row + Marca/Deseleziona riga + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Marca/Deseleziona cella + + + true + vero + + + Save Table Image + Salva immagine tabella + + + PNG Image (*.png) + Immagine PNG (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Riepilogo HCI Bluetooth + + + Name + Nome + + + OGF + OGF + + + OCF + OCF + + + Opcode + Opcode + + + Event + Evento + + + Subevent + Sottoevento + + + Status + Stato + + + Reason + Motivo + + + Hardware Error + Errore hardware + + + Occurrence + Occorrenza + + + Link Control Commands + Comandi di controllo del link + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Comandi di policy del link + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Comandi controller e banda base + + + 0x03 + 0x03 + + + Informational Parameters + Parametri informativi + + + 0x04 + 0x04 + + + Status Parameters + Parametri di stato + + + 0x05 + 0x05 + + + Testing Commands + Comandi di test + + + 0x06 + 0x06 + + + LE Controller Commands + Comandi controller LE + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + Comandi di prova logo Bluetooth + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Comandi specifici del produttore + + + 0x3F + 0x3F + + + Unknown OGF + OGF sconosciuto + + + Events + Eventi + + + Hardware Errors + Errori hardware + + + Results filter: + Filtro risultati: + + + Display filter: + Filtro di visualizzazione: + + + All Interfaces + Tutte le interfacce + + + All Adapters + Tutte le schede + + + Copy Cell + Copia cella + + + Copy Rows + Copia righe + + + Copy All + Copia tutto + + + Save as image + Salva come immagine + + + Mark/Unmark Row + Marca/Deseleziona riga + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Marca/Deseleziona cella + + + Unknown + Sconosciuto + + + Adapter %1 + Scheda %1 + + + Frame %1 + Frame %1 + + + Pending + In corso + + + Save Table Image + Salva immagine tabella + + + PNG Image (*.png) + Immagine PNG (*.png) + + + + ByteViewTab + + Packet bytes + Byte del pacchetto + + + + ByteViewText + + Allow hover highlighting + Consenti la selezione al passaggio del mouse + + + Show bytes as hexadecimal + Mostra byte in esadecimale + + + …as decimal + …come decimale + + + …as octal + …come ottale + + + …as bits + …come bit + + + Show text based on packet + Mostra testo sulla base del pacchetto + + + …as ASCII + …come ASCII + + + …as EBCDIC + …come EBCDIC + + + + CaptureFile + + [closing] + [chiusura in corso] + + + [closed] + [chiuso] + + + + CaptureFileDialog + + This capture file contains comments. + Questo filtro di cattura contiene dei commenti. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + Il formato del file selezionato non supporta i commenti. Vuoi salvare la cattura in un formato che supporta i commenti o scartare i commenti e salvare nel formato scelto? + + + Discard comments and save + Scarta i commenti e salva + + + Save in another format + Salva in un altro formato + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + Nessun formato di file in cui può essere salvato supporta i commenti. Vuoi scartare i commenti e salvare nel formato scelto? + + + All Files ( + Tutti i file ( + + + All Capture Files + Tutti i file delle catture + + + Format: + Formato: + + + Size: + Dimensione: + + + Start / elapsed: + Inizio / trascorso: + + + Automatically detect file type + Rileva automaticamente il tipo di file + + + %1, error after %Ln packet(s) + %1, error after %2 packets + + %1, errore dopo %Ln pacchetto + %1, errore dopo %Ln pacchetti + + + + %1, timed out at %Ln packet(s) + %1, timed out at %2 packets + + %1, scaduto dopo %Ln pacchetto + %1, scaduto dopo %Ln pacchetti + + + + %1, %Ln packet(s) + + %1, %Ln pacchetto + %1, %Ln pacchetti + + + + Prepend packets + Aggiungi pacchetti in testa + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Inserisci pacchetti dal file selezionato prima del file corrente. La marca temporale dei pacchetti sarà ignorata. + + + Merge chronologically + Unisci cronologicamente + + + Insert packets in chronological order. + Inserisci pacchetti in ordine cronologico. + + + Append packets + Aggiungi pacchetti in coda + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Inserisci pacchetti dal file selezionato dopo il file corrente. La marca temporale dei pacchetti sarà ignorata. + + + Read filter: + Filtro di lettura: + + + Compress with g&zip + Comprimi con g&zip + + + Open Capture File + Wireshark: Open Capture File + Apri un file di cattura + + + Save Capture File As + Wireshark: Save Capture File As + Salva il file di cattura come + + + Save as: + Salva come: + + + Export Specified Packets + Wireshark: Export Specified Packets + Esporta i pacchetti specificati + + + Export as: + Esporta come: + + + Merge Capture File + Wireshark: Merge Capture File + Unisci file di cattura + + + Unknown file type returned by save as dialog. + Tipo di file sconosciuto restituito dalla finestra di dialogo Salva come. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Segnalalo come un problema di Wireshark su https://gitlab.com/wireshark/wireshark/-/issues. + + + directory + cartella + + + unknown file format + formato dii file sconosciuto + + + error opening file + errore nell'apertura del file + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + %1, errore dopo %Ln record di dati + %1, errore dopo %Ln record di dati + + + + %1, timed out at %Ln data record(s) + + %1, tempo scaduto a %Ln record di dati + %1, tempo scaduto a %Ln record di dati + + + + %1, %Ln data record(s) + + %1, %Ln record di dati + %1, %Ln record di dati + + + + unknown + sconosciuto + + + + CaptureFilePropertiesDialog + + Details + Dettagli + + + Capture file comments + Commenti del file di cattura + + + Refresh + Aggiorna + + + Copy To Clipboard + Copia negli appunti + + + Save Comments + Salva i commenti + + + Capture File Properties + Proprietà del file di cattura + + + Unknown + Sconosciuto + + + File + File + + + Name + Nome + + + Length + Lunghezza + + + Hash (SHA256) + Hash (SHA256) + + + Hash (SHA1) + Hash (SHA1) + + + Format + Formato + + + Encapsulation + Incapsulamento + + + Snapshot length + Lunghezza istantanea + + + Time + Tempo + + + First packet + Primo pacchetto + + + Last packet + Ultimo pacchetto + + + Elapsed + Trascorso + + + Section %1 + Sezione %1 + + + Capture + Cattura + + + Hardware + Hardware + + + OS + OS + + + Application + Applicazione + + + Interfaces + Interfacce + + + Interface + Interfaccia + + + Dropped packets + Pacchetti persi + + + Capture filter + Filtro di cattura + + + Link type + Tipo di link + + + Packet size limit (snaplen) + Dimensione limite del pacchetto (snaplen) + + + none + nessuno + + + %1 bytes + %1 byte + + + Statistics + Statistiche + + + Measurement + Misure + + + Captured + Catturati + + + Displayed + Visualizzati + + + Marked + Marcati + + + Packets + Pacchetti + + + Time span, s + Tempo, s + + + Average pps + PPS medi + + + Average packet size, B + Dimensione media dei pacchetti, B + + + Bytes + Byte + + + Average bytes/s + Byte/s medi + + + Average bits/s + Bit/s medi + + + Section Comment + Commento sezione + + + Packet Comments + Commenti pacchetto + + + <p>Frame %1: + <p>Frame %1: + + + Created by Wireshark %1 + + + Creato da Wireshark %1 + + + + + + CaptureFilterCombo + + Capture filter selector + Selettore del file di cattura + + + + CaptureFilterEdit + + Capture filter entry + Voce del filtro di cattura + + + Manage saved bookmarks. + Gestisci i segnalibri salvati. + + + Apply this filter string to the display. + Applica questa stringa di filtro alla visualizzazione. + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + Più filtri selezionati. Sovrascrivili qui o lascia vuoto per preservarli. + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>Le interfacce che hai selezionato hanno diversi filtri di cattura. Se digiti un filtro qui, avrà la precedenza su tali filtri. Se lasci vuoto, i filtri saranno preservati.</p> + + + Enter a capture filter %1 + Inserisci un filtro di cattura %1 + + + Save this filter + Salva questo filtro + + + Remove this filter + Rimuovi questo filtro + + + Manage Capture Filters + Gestisci filtri di cattura + + + + CaptureInfoDialog + + Capture Information + Informazioni di cattura + + + Stop Capture + Ferma cattura + + + %1 packets, %2:%3:%4 + %1 pacchetti, %2:%3:%4 + + + + CaptureInfoModel + + Other + Altro + + + + CaptureOptionsDialog + + Input + Ingresso + + + Interface + Interfaccia + + + Traffic + Traffico + + + Link-layer Header + Intestazione del livello di collegamento + + + Promiscuous + Promiscua + + + Snaplen (B) + Lunghezza di cattura (B) + + + Buffer (MB) + Buffer (MB) + + + Monitor Mode + Modalità di monitoraggio + + + Capture Filter + Filtro di cattura + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Probabilmente vuoi abilitare questa opzione. Di solito una scheda di rete cattura solo il traffico inviato al proprio indirizzo. Se vuoi catturare tutto il traffico che la scheda di rete può &quot;vedere&quot; marca questa opzione. Leggi le FAQ per avere più dettagli sul come catturare pacchetti in una rete con switch.</p></body></html> + + + Enable promiscuous mode on all interfaces + Abilita la modalità promiscua su tutte le interfacce + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + Mostra e nascondi le interfacce, aggiungi commenti, e gestisci pipe e interfacce remote. + + + Manage Interfaces… + Gestisci interfacce... + + + Capture filter for selected interfaces: + Filtro di cattura per le interfacce selezionate: + + + Compile BPFs + Compila BPF + + + Output + Uscita + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>Inserisci il nome del file su cui i dati catturati saranno scritti. In modo predefinito, sarà utilizzato un file temporaneo.</p></body></html> + + + Capture to a permanent file + Cattura su un file permanente + + + File: + File: + + + Browse… + Sfoglia... + + + Output format: + Formato di uscita: + + + pcapng + pcapng + + + pcap + pcap + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>Invece di usare un singolo file di cattura, saranno creati file multipli.</p><p>I nomi di file generati conterranno un numero incrementale e l'orario di inizio della cattura.</p><p>NOTA: se abilitata, almeno uno dei criteri per i nuovi file DEVE essere selezionato.</p></body></html> + + + Create a new file automatically… + Crea un nuovo file automaticamente... + + + after + dopo + + + Switch to the next file after the specified number of packets have been captured. + Passa al file successivo dopo aver catturato il numero di pacchetti specificato. + + + packets + pacchetti + + + Switch to the next file after the file size exceeds the specified file size. + Passa al file successivo quando la dimensione dei file supera la dimensione dei file specificata. + + + kilobytes + kilobyte + + + megabytes + megabyte + + + gigabytes + gigabyte + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + Passa al file successivo quando il tempo di cattura del file corrente supera il tempo specificato. + + + seconds + secondi + + + minutes + minuti + + + hours + ore + + + when time is a multiple of + quando il tempo è un multiplo di + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + Passa al file successivo quando l'ora (orologio a muro) è un multiplo pari dell'intervallo specificato. +Ad esempio, usa 1 ora per fare in modo che un nuovo file sia creato ogni ora. + + + compression + compressione + + + None + Nessuna + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>Dopo che la cattura è passata al file successivo e il numero di file prescelto è stato superato, il file più vecchio sarà rimosso.</p></body></html> + + + Use a ring buffer with + Usa un buffer circolare con + + + files + file + + + Options + Opzioni + + + Display Options + Opzioni di visualizzazione + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>Selezionando questa opzione i pacchetti catturati saranno visualizzati immediatamente nella schermata principale. Nota: ciò rallenterà la cattura, quindi si potrà osservare un incremento del numero di pacchetti persi.</p></body></html> + + + Update list of packets in real-time + Aggiorna l'elenco dei pacchetti in tempo reale + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>Questa opzione permette di scorrere l'&quot;elenco dei pacchetti&quot; automaticamente fino all'ultimo pacchetto catturato, quando l'opzione &quot;Aggiorna l'elenco dei pacchetti in tempo reale&quot; è stata selezionata.</p></body></html> + + + Automatically scroll during live capture + Scorri automaticamente durante la cattura + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>Mostra la finestra di informazioni durante la cattura.</p></body></html> + + + Show capture information during live capture + Mostra le informazioni di cattura durante l'esecuzione della cattura + + + Name Resolution + Risoluzione dei nomi + + + Perform MAC layer name resolution while capturing. + Effettua la risoluzione dei nomi del livello MAC durante la cattura. + + + Resolve MAC addresses + Risolvi gli indirizzi MAC + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>Effettua la risoluzione dei nomi del livello di rete durante la cattura.</p></body></html> + + + Resolve network names + Risolvi i nomi di rete + + + Perform transport layer name resolution while capturing. + Risolvi i nomi del livello di trasporto durante la cattura. + + + Resolve transport names + Risolvi i nomi di trasporto + + + Stop capture automatically after… + Ferma la cattura automaticamente dopo... + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>Ferma la cattura dopo che il numero di pacchetti specificato è stato catturato.</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + Ferma la cattura dopo che un certo numero di pacchetti è stato catturato. + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>Ferma la cattura dopo che il numero di file specificato è stato creato.</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>Ferma la cattura dopo che una certa quantità di dati è stata catturata.</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + Ferma la cattura dopo che una certa quantità di dati è stata catturata. + + + Stop capturing after the specified amount of time has passed. + Ferma la cattura dopo che la quantità di tempo specificato è trascorsa. + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>Specifica opzionalmente una cartella temporanea per i file catturati senza nome.</p></body></html> + + + Directory for temporary files + Cartella per i file temporanei + + + Capture Options + Opzioni di cattura + + + Start + Avvia + + + Leave blank to use a temporary file + Lascia vuoto per utilizzare un file temporaneo + + + Specify a Capture File + Specifica un file di cattura + + + Specify temporary directory + Specifica cartella temporanea + + + %1: %2 + %1: %2 + + + Addresses + Indirizzi + + + Address + Indirizzo + + + no addresses + nessun indirizzo + + + Error + Errore + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + File multipli: la dimensione del file richiesta è troppo grande. La dimensione del file non può essere superiore a 2 GiB. + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + File multipli: non è stato fornito un nome del file di cattura! Devi specificare un nome del file se vuoi usare più file. + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + File multipli: non è stato fornito alcun limite di file. Devi specificare una dimensione del file, l'intervallo o il numero di pacchetti per ogni file. + + + + CapturePreferencesFrame + + Frame + Frame + + + Default interface + Interfaccia predefinita + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Probabilmente vuoi abilitare questa opzione. Di solito una scheda di rete cattura solo il traffico inviato al proprio indirizzo. Se vuoi catturare tutto il traffico che la scheda di rete può &quot;vedere&quot; marca questa opzione. Leggi le FAQ per avere più dettagli sul come catturare pacchetti in una rete con switch.</p></body></html> + + + Capture packets in promiscuous mode + Cattura i pacchetti in modalità promiscua + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Cattura i pacchetti nel formato di file di nuova generazione.</p></body></html> + + + Capture packets in pcapng format + Cattura i pacchetti nel formato pcapng + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Aggiorna la lista dei pacchetti mentre la cattura è in corso. Questo può risultare in una perdita di pacchetti su reti ad alta velocità.</p></body></html> + + + Update list of packets in real time + Aggiorna l'elenco dei pacchetti in tempo reale + + + Interval between updates (ms) + Intervallo tra gli aggiornamenti (ms) + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + <html><head/><body><p>La frequenza con cui l'acquisizione notifica alla GUI i nuovi pacchetti. Influisce sulla frequenza degli aggiornamenti della GUI e sulla granularità dei timer.</p></body></html> + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + <html><head/><body><p>L'intervallo tra gli aggiornamenti dei nuovi pacchetti. Influisce sulla frequenza degli aggiornamenti della GUI e sulla granularità dei timer.</p></body></html> + + + Don't load interfaces on startup + Non caricare le interfacce all'avvio + + + Disable external capture interfaces + Disabilita le interfacce di cattura esterne + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + il simbolo "@" sarà ignorato. + + + + ColoringRulesDialog + + Dialog + Finestra + + + <small><i>A hint.</i></small> + <small><i>Un suggerimento.</i></small> + + + Add a new coloring rule. + Aggiungi una nuova regola di colorazione. + + + Delete this coloring rule. + Elimina questa regola di colorazione. + + + Duplicate this coloring rule. + Duplica questa regola di colorazione. + + + Clear all coloring rules. + Cancella tutte le regole di colorazione. + + + Set the foreground color for this rule. + Imposta il colore di primo piano per questa regola. + + + Foreground + Primo piano + + + Set the background color for this rule. + Imposta il colore di sfondo per questa regola. + + + Background + Sfondo + + + Set the display filter using this rule. + Imposta il filtro di visualizzazione utilizzando questa regola. + + + Apply as filter + Applica come filtro + + + Select a file and add its filters to the end of the list. + Seleziona un file e aggiungi i suoi filtri alla fine dell'elenco. + + + Save filters in a file. + Salva i filtri in un file. + + + Coloring Rules %1 + Regole di colorazione %1 + + + Import… + Importa… + + + Export… + Esporta + + + Copy coloring rules from another profile. + Copia regole di colorazione da un altro profilo. + + + Open + Apri + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Doppio clic per modificare. Trascina per spostare. Le regole sono elaborate in ordine fino alla prima corrispondenza. + + + Import Coloring Rules + Importazione regole di colorazione + + + Export %1 Coloring Rules + Esporta %1 regole di colorazione + + + + ColoringRulesModel + + New coloring rule + Nuova regola di colorazione + + + Unable to save coloring rules: %1 + Impossibile salvare le regole di colorazione: %1 + + + Name + Nome + + + Filter + Filtro + + + + ColumnEditorFrame + + Frame + Frame + + + Title: + Title + Titolo: + + + Type: + Type + Tipo: + + + Fields: + Fields + Campi: + + + Occurrence: + Occurrence + Occorrenza: + + + Resolve Names: + Risolvi nomi: + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + <html><head/><p>Mostra stringhe comprensibili invece che i valori grezzi dei campi. Applicabile solo alle colonne personalizzate con campi che hanno stringhe con valori.</p></body></html> + + + Missing fields. + Campi mancanti. + + + Invalid fields. + Campi non validi. + + + Invalid occurrence value. + Valore dell'occorrenza non valido. + + + + ColumnListModel + + Displayed + Visualizzati + + + Title + Titolo + + + Type + Tipo + + + Fields + Campi + + + Field Occurrence + Occorrenze del campo + + + Resolved + Risolto + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + <html>Mostra stringhe comprensibili invece che i valori grezzi dei campi. Applicabile solo alle colonne personalizzate con campi che hanno stringhe con valori.</html> + + + New Column + Nuova colonna + + + + ColumnPreferencesFrame + + Frame + Frame + + + Add a new column + Aggiungi una nuova colonna + + + Delete selected column + Elimina colonna selezionata + + + Show displayed columns only + Mostra solo le colonne visualizzate + + + Reset all changes + Ripristina tutte le modifiche + + + + CompiledFilterOutput + + Compiled Filter Output + Output del filtro compilato + + + Copy + Copia + + + Copy filter text to the clipboard. + Copia il testo del filtro negli appunti. + + + + ConversationDataModel + + Address A + Indirizzo A + + + Port A + Porta A + + + Address B + Indirizzo B + + + Port B + Porta B + + + Packets + Pacchetti + + + Bytes + Byte + + + Stream ID + ID flusso + + + Packets A + Pacchetti A + + + Bytes A + Byte A + + + Packets B + Pacchetti B + + + Bytes B + Byte B + + + Abs Start + Inizio Abs + + + Rel Start + Inizio Rel + + + Duration + Durata + + + Bits/s A + Bit/s A + + + Bits/s B + Bit/s B + + + Total Packets + Pacchetti totali + + + Percent Filtered + Percentuale filtrati + + + + ConversationDialog + + Follow Stream… + Segui il flusso... + + + Follow a TCP or UDP stream. + Segui un flusso TCP o UDP. + + + Graph… + Grafico... + + + Graph a TCP conversation. + Crea il grafico di una connessione TCP. + + + + ConversationHashTablesDialog + + Dialog + Finestra + + + Conversation Hash Tables + Tabella hash di conversazione + + + + CopyFromProfileButton + + Copy from + Copia da + + + Copy entries from another profile. + Copia voci da un altro profilo. + + + System default + Predefinito di sistema + + + + CredentialsDialog + + Wireshark - Credentials + Wireshark - Credenziali + + + Credentials + Credenziali + + + + CredentialsModel + + Click to select the packet + Fai clic per selezionare il pacchetto + + + Click to select the packet with username + Fai clic per selezionare il pacchetto con il nome utente + + + Username not available + Nome utente non disponibile + + + Packet No. + Pacchetto n. + + + Protocol + Protocollo + + + Username + Nome utente + + + Additional Info + Informazioni aggiuntive + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + Copia byte come dump esadecimale + ASCII + + + Copy packet bytes as a hex and ASCII dump. + Copia i byte del pacchetto come un dump esadecimale e ASCII. + + + …as Hex Dump + ... come dump esadecimale + + + Copy packet bytes as a hex dump. + Copia i byte del pacchetto come un dump esadecimale. + + + …as MIME Data + …come dati MIME + + + …as C String + ...come stringa C + + + Copy packet bytes as printable ASCII characters and escape sequences. + Copia i byte dei pacchetti come caratteri ASCII stampabili e sequenze di escape. + + + …as a Hex Stream + ...come un flusso esadecimale + + + Copy packet bytes as a stream of hex. + Copia i byte del pacchetto come un flusso esadecimale. + + + …as a Base64 String + ...come una stringa Base64 + + + Copy packet bytes as a base64 encoded string. + Copia i byte del pacchetto come stringa codificata in Base64. + + + Copy packet bytes as application/octet-stream MIME data. + Copia i byte del pacchetto come dati MIME application/octet-stream. + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + Cambia il comportamento di una decodifica per un protocollo. + + + Remove this dissection behavior. + Rimuovi il comportamento di questa decodifica. + + + Copy this dissection behavior. + Copia il comportamento di questa decodifica. + + + Clear all dissection behaviors. + Cancella tutti i comportamenti delle decodifiche. + + + Decode As… + Decodifica come... + + + Open + Apri + + + + DecodeAsModel + + Match using this field + Cerca usando questo campo + + + Change behavior when the field matches this value + Cambia il comportamento quando il campo corrisponde a questo valore + + + Field value type (and base, if Integer) + Tipo di valore del campo (e base, se intero) + + + Current"Decode As" behavior + Comportamento attuale di "Decodifica come" + + + Default "Decode As" behavior + Comportamento predefinito di "Decodifica come" + + + String + Stringa + + + Integer, base + Intero, base + + + unknown + sconosciuto + + + <none> + <none> + + + GUID + GUID + + + Field + Campo + + + Value + Valore + + + Type + Tipo + + + Default + Predefinito + + + Current + Attuale + + + + DisplayFilterCombo + + Display filter selector + Visualizza il selettore del filtro + + + Select from previously used filters. + Selezione tra i filtri precedentemente utilizzati. + + + + DisplayFilterEdit + + Display filter entry + Voce del filtro di visualizzazione + + + Manage saved bookmarks. + Gestisci i segnalibri salvati. + + + Display Filter Expression… + Espressione del filtro di visualizzazione... + + + Apply a display filter %1 <%2/> + Applica un filtro di visualizzazione %1 <%2/> + + + Enter a display filter %1 + Digita un filtro di visualizzazione %1 + + + Clear display filter + Cancella filtro di visualizzazione + + + Apply display filter + Applica filtro alla visualizzazione. + + + Left align buttons + Allinea i pulsanti a sinistra + + + Apply a read filter %1 + Applica un filtro di lettura %1 + + + Current filter: %1 + Filtro attuale: %1 + + + Invalid filter: + Filtro non valido: + + + Save this filter + Salva questo filtro + + + Remove this filter + Rimuovi questo filtro + + + Manage Display Filters + Gestisci filtri di visualizzazione + + + Filter Button Preferences... + Preferenze pulsante di filtro... + + + + DisplayFilterExpressionDialog + + Dialog + Finestra + + + Select a field to start building a display filter. + Seleziona un campo per avviare la creazione di un filtro di visualizzazione. + + + Field Name + Nome campo + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Cerca nell'elenco dei nomi di campo.</p></body></html> + + + Search: + Cerca: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>Le relazioni possono essere utilizzate per restringere i campi a valori specifici. Ogni relazione fa ciò che segue:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">è presente</span></p></td><td><p>Verifica qualsiasi pacchetto che contiene questo campo</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, ecc.</span></p></td><td><p>Confronta il campo con un valore specifico.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contiene, corrisponde a</span></p></td><td><p>Verifica il campo con una stringa (contiene) o un'espressione regolare (corrisponde a)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Confronta il campo con uno specifico insieme di valori</p></td></tr></table></body></html> + + + + + Relation + Relazione + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + In modo predefinito, le comparazioni di ordine e le relazioni contiene/corrisponde/in sono vere se qualsiasi valore corrisponde. Il quantificatore "tutto" può essere utilizzato per applicare il test a tutti i valori in un frame. + + + Quantifier + Quantificatore + + + Any + Qualsiasi + + + All + Tutto + + + Match against this value. + Verifica questo valore. + + + Value + Valore + + + If the field you have selected has a known set of valid values they will be listed here. + Se il campo selezionato ha un insieme noto di valori validi, questi valori saranno elencati qui. + + + Predefined Values + Valori predefiniti + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + Se il campo selezionato copre un intervallo di byte (ad es. hai selezionato un protocollo), puoi restringere qui la corrispondenza a un intervallo di byte. + + + Range (offset:length) + Intervallo (offset:lunghezza) + + + No display filter + Nessun filtro di visualizzazione + + + <small><i>A hint.</i></small> + <small><i>Un suggerimento.</i></small> + + + Display Filter Expression + Espressione del filtro di visualizzazione + + + Select a field name to get started + Seleziona il nome di un campo per iniziare + + + Click OK to insert this filter + Fai clic su OK per inserire questo filtro + + + + DissectorSyntaxLineEdit + + Dissector entry + Inserimento decodificatore + + + Enter a dissector %1 + Inserisci un decodificatore %1 + + + + DissectorTablesDialog + + Dialog + Finestra + + + Search: + Cerca: + + + Dissector Tables + Tabelle dei decodificatori + + + + DissectorTablesProxyModel + + Table Type + Tipo tabella + + + String + Stringa + + + Dissector Description + Descrizione decodificatore + + + Integer + Intero + + + Protocol + Protocollo + + + Short Name + Nome breve + + + Table Name + Nome tabella + + + Selector Name + Nome selettore + + + + EnabledProtocolsDialog + + Dialog + Finestra + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>La disabilitazione di un protocollo impedisce che i protocolli di livello più alto siano visualizzati</i></small> + + + Search: + Cerca: + + + in + in + + + Enable All + Abilita tutto + + + Disable All + Disabilita tutto + + + Invert + Inverti + + + Enabled Protocols + Protocolli abilitati + + + Everywhere + Ovunque + + + Only Protocols + Solo protocolli + + + Only Description + Solo descrizione + + + Only enabled protocols + Solo i protocolli abilitati + + + Only disabled protocols + Solo i protocolli disabilitati + + + any protocol + qualsiasi protocollo + + + non-heuristic protocols + protocolli non euristici + + + heuristic protocols + protocolli euristici + + + + EnabledProtocolsModel + + Protocol + Protocollo + + + Description + Descrizione + + + + EndpointDataModel + + Address + Indirizzo + + + Port + Porta + + + Packets + Pacchetti + + + Bytes + Byte + + + Tx Packets + Pacchetti trasmessi + + + Tx Bytes + Byte trasmessi + + + Rx Packets + Pacchetti ricevuti + + + Rx Bytes + Byte ricevuti + + + Country + Nazione + + + City + Città + + + Latitude + Latitudine + + + Longitude + Longitudine + + + AS Number + Numero AS + + + AS Organization + Organizzazione AS + + + Total Packets + Pacchetti totali + + + Percent Filtered + Percentuale filtrati + + + + EndpointDialog + + Map + Mappa + + + Draw IPv4 or IPv6 endpoints on a map. + Disegna i terminatori IPv4 o IPv6 su una mappa. + + + Open in browser + Apri nel browser + + + Save As… + Salva come... + + + Map file error + Errore nel file di mappa + + + Save Endpoints Map + Salva mappa dei terminatori + + + Failed to save map file %1. + Impossibile salvare il file di mappa %1. + + + + EthernetAddressModel + + Type + Tipo + + + Name + Nome + + + Address + Indirizzo + + + All entries + Tutte le voci. + + + Hosts + Host + + + Ethernet Addresses + Indirizzi Ethernet + + + Ethernet Manufacturers + Produttori Ethernet + + + Ethernet Well-Known Addresses + Indirizzi Ethernet conosciuti + + + + ExpertInfoDialog + + Dialog + Finestra + + + <small><i>A hint.</i></small> + <small><i>Un suggerimento.</i></small> + + + Limit to Display Filter + Limita al filtro di visualizzazione + + + Group by summary + Raggruppa per riepilogo + + + Search expert summaries. + Cerca riepiloghi per esperti. + + + Search: + Cerca: + + + Show… + Show... + Mostra... + + + Error + Errore + + + Show error packets. + Mostra i pacchetti di errore. + + + Warning + Avviso + + + Show warning packets. + Mostra i pacchetti di avviso. + + + Note + Nota + + + Show note packets. + Mostra i pacchetti di nota. + + + Chat + Conversazione + + + Show chat packets. + Mostra i pacchetti di conversazione. + + + Comment + Commento + + + Show comment packets. + Mostra i pacchetti di commento. + + + Expert Information + Informazioni per esperti + + + Collapse All + Contrai tutti + + + Expand All + Espandi tutti + + + Capture file closed. + File di cattura chiuso. + + + No display filter + Nessun filtro di visualizzazione + + + No display filter set. + Nessun filtro di visualizzazione impostato. + + + Limit information to "%1". + Limita l'informazione a "%1". + + + Display filter: "%1" + Filtro di visualizzazione: "%1" + + + + ExpertInfoProxyModel + + Packet + Pacchetto + + + Severity + Gravità + + + Summary + Riepilogo + + + Group + Gruppo + + + Protocol + Protocollo + + + Count + Conteggio + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Esporta le decodifiche del pacchetto + + + Export As: + Export as: + Esporta come: + + + Plain text (*.txt) + Testo (*.txt) + + + Comma Separated Values - summary (*.csv) + Valori separati da virgola - sommario (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML - sommario (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PDML - dettagli (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + Array C - byte (*.c, *.h) + + + + ExportObjectDialog + + Dialog + Finestra + + + Content Type: + Tipo di contenuto: + + + Searching for objects + Ricerca oggetti + + + Text Filter: + Filtro di testo: + + + Only display entries containing this string + Visualizza solo le voci che contengono questa stringa + + + Preview + Anteprima + + + All Content-Types + Tutti i tipi di contenuto + + + Export + Esporta + + + %1 object list + %1 elenco oggetti + + + Save Object As… + Salva oggetto come... + + + Save All Objects In… + Salva tutti gli oggetti in... + + + + ExportObjectModel + + Packet + Pacchetto + + + Hostname + Nome host + + + Content Type + Tipo di contenuto + + + Size + Dimensione + + + Filename + Nome del file + + + + ExportPDUDialog + + Dialog + Finestra + + + Display filter: + Filtro di visualizzazione: + + + + ExtArgSelector + + Reload data + Ricarica i dati + + + + ExtcapArgumentFileSelection + + Clear + Pulisci + + + All Files ( + Tutti i file ( + + + Open File + Apri file + + + Select File + Seleziona file + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + Opzioni interfaccia + + + Start + Avvia + + + Save + Salva + + + Default + Predefinito + + + Restore default value of the item + Ripristina il valore predefinito dell'elemento + + + Extcap Help cannot be found + Guida Extcap non disponibile + + + The help for the extcap interface %1 cannot be found. Given file: %2 + La guida per l'interfaccia %1 non è disponibile. File specificato: %2 + + + Save parameter(s) on capture start + Salva parametri all'avvio della cattura + + + + FieldFilterEdit + + Display filter entry + Voce del filtro di visualizzazione + + + Enter a field %1 + Digita un campo %1 + + + Invalid filter: + Filtro non valido: + + + + FileSetDialog + + Dialog + Finestra + + + Directory: + Directory: + + + No files in Set + Nessun file nel gruppo + + + No capture loaded + Nessuna cattura caricata + + + %Ln File(s) in Set + %1 File%2 in Set + + %Ln file nel gruppo + %Ln file nel gruppo + + + + + FilesetEntryModel + + Open this capture file + Apri questo file di cattura + + + Filename + Nome del file + + + Created + Creato + + + Modified + Modificato + + + Size + Dimensione + + + + FilterAction + + Selected + Selezionati + + + Not Selected + Non selezionati + + + …and Selected + ...e selezionati + + + …or Selected + ...o selezionati + + + …and not Selected + ...e non selezionati + + + …or not Selected + ...o non selezionati + + + + FilterDialog + + Dialog + Finestra + + + Create a new filter. + Crea un nuovo filtro. + + + Remove this filter. + Remove this profile. + Rimuovi questo filtro. + + + Copy this filter. + Copy this profile. + Copia questo filtro. + + + Capture Filters + Filtri di cattura + + + Display Filters + Filtri di visualizzazione + + + Open + Apri + + + New capture filter + This text is automatically filled in when a new filter is created + Nuovo filtro di cattura + + + New display filter + This text is automatically filled in when a new filter is created + Nuovo filtro di visualizzazione + + + + FilterExpressionFrame + + Frame + Frame + + + Filter Buttons Preferences… + Preferenze pulsanti di filtro... + + + Label: + Etichetta: + + + Enter a description for the filter button + Digita una descrizione per il pulsante di filtro + + + Filter: + Filtro + + + Enter a filter expression to be applied + Digita un espressione di filtro da applicare + + + Comment: + Commento: + + + Enter a comment for the filter button + Digita un commento per il pulsante di filtro + + + Missing label. + Etichetta mancante. + + + Missing filter expression. + Espressione di filtro mancante. + + + Invalid filter expression. + Espressioni di filtro non valida. + + + + FilterExpressionToolBar + + Filter Button Preferences... + Preferenze pulsante di filtro... + + + Edit + Modifica + + + Disable + Disabilita + + + Remove + Rimuovi + + + + FilterListModel + + Filter Name + Nome filtro + + + Filter Expression + Espressione del filtro + + + + FindLineEdit + + Textual Find + Trova testuale + + + Regular Expression Find + Trova con espressione regolare + + + + FirewallRulesDialog + + Create rules for + Creare regole per + + + Inbound + In ingresso + + + Deny + Nega + + + Firewall ACL Rules + Regole ACL firewall + + + Copy + Copia + + + IPv4 source address. + Indirizzo sorgente IPv4. + + + IPv4 destination address. + Indirizzo di destinazione IPv4. + + + Source port. + Porta sorgente. + + + Destination port. + Porta di destinazione. + + + IPv4 source address and port. + Indirizzo sorgente IPv4 e porta. + + + IPv4 destination address and port. + Indirizzo di destinazione IPv4 e porta. + + + MAC source address. + Indirizzo sorgente MAC. + + + MAC destination address. + Indirizzo di destinazione MAC. + + + Text file (*.txt);;All Files ( + File di testo (*.txt);;Tutti i file ( + + + Warning + Avviso + + + Unable to save %1 + Impossibile salvare %1 + + + + FolderListModel + + "File" dialogs + Finestre "File" + + + capture files + file di cattura + + + Temp + Temporanei + + + untitled capture files + file di cattura senza titolo + + + Personal configuration + Configurazione personale + + + Global configuration + Configurazione globale + + + dfilters, preferences, ethers, … + dfilters, preferences, ethers, + + + dfilters, preferences, manuf, … + dfilters, preferences, manuf, + + + System + Sistema + + + ethers, ipxnets + ethers, ipxnets + + + Program + Programma + + + program files + file di programma + + + Personal Plugins + Plugin personali + + + binary plugins + plugin binari + + + Global Plugins + Plugin globali + + + Personal Lua Plugins + Plugin Lua personali + + + Global Lua Plugins + Plugin Lua globali + + + Lua scripts + Script Lua + + + Personal Extcap path + Percorso Extcap personale + + + external capture (extcap) plugins + estensioni di cattura esterna (extcap) + + + Global Extcap path + Percorso Extcap globale + + + MaxMind DB path + Percorso MaxMind DB + + + MaxMind DB database search path + Percorso di ricerca database MaxMind DB + + + MIB/PIB path + Percorso MIB/PIB + + + SMI MIB/PIB search path + Percorso di ricerca MIB/PIB SMI + + + macOS Extras + macOS Extras + + + Extra macOS packages + Pacchetti aggiuntivi di macOS + + + Name + Nome + + + Location + Posizione + + + Typical Files + File tipici + + + + FollowStreamAction + + %1 Stream + %1 flusso + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + Filtra questo flusso + + + Print + Stampa + + + %Ln client pkt(s), + + %Ln pacchetto client, + %Ln pacchetti client, + + + + %Ln server pkt(s), + + %Ln pacchetto server, + %Ln pacchetti server, + + + + ASCII + ASCII + + + C Arrays + Array C + + + EBCDIC + EBCDIC + + + Hex Dump + Dump Esadecimale + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + Grezzo + + + Save as… + Salva come... + + + Back + Indietro + + + Packet %1. + Pacchetto %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + %Ln pacchetto <span style="color: %1; background-color:%2">client</span>, + %Ln pacchetti <span style="color: %1; background-color:%2">client</span>, + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + %Ln pacchetto <span style="color: %1; background-color:%2">server</span>, + %Ln pacchetti <span style="color: %1; background-color:%2">server</span>, + + + + %Ln turn(s). + + %Ln turno. + %Ln turni. + + + + Click to select. + Fai clic per selezionare. + + + Regex Find: + Trova espressione regolare: + + + No capture file. + Nessun file di cattura. + + + Please make sure you have a capture file opened. + Assicurati di avere un file di cattura aperto. + + + Error following stream. + Errore seguendo il flusso. + + + Capture file invalid. + File di cattura non valido. + + + Please make sure you have a %1 packet selected. + Assicurati di aver selezionato un pacchetto %1. + + + %1 stream not found on the selected packet. + Flussi %1 non trovati nel pacchetto selezionato. + + + Entire conversation (%1) + Conversazione intera (%1) + + + Follow %1 Stream (%2) + Segui flusso %1 (%2) + + + Error creating filter for this stream. + Errore nella creazione di un filtro per questo flusso. + + + Save Stream Content As… + Salva il contenuto del flusso come... + + + [Stream output truncated] + [Flusso di output troncato] + + + %Ln total stream(s). + + %Ln flusso. + %Ln flussi totali. + + + + Max sub stream ID for the selected stream: %Ln + + ID massimo del flusso secondario per il flusso selezionato: %Ln + ID massimo del flusso secondario per il flusso selezionato: %Ln + + + + File closed. + File chiuso. + + + Follow Stream + Segui il flusso + + + Hint. + Suggerimento. + + + Show data as + Show and save data as + Mostra dati come + + + Stream + Flusso + + + Substream + Sottoflusso + + + Find: + Trova: + + + Find &Next + Trova &successivo + + + + FontColorPreferencesFrame + + Frame + Frame + + + Main window font: + Carattere per la finestra principale: + + + Select Font + Seleziona carattere + + + Colors: + Colori: + + + System Default + Predefinito di sistema + + + Solid + Solido + + + Sample ignored packet text + Esempio di testo per pacchetto ignorato + + + Sample marked packet text + Esempio di testo per pacchetto marcato + + + Sample active selected item + Esempio di elemento attivo selezionato + + + Style: + Stile + + + Gradient + Sfumatura + + + Sample inactive selected item + Esempio di elemento attivo selezionato + + + Sample "Follow Stream" client text + Esempio di testo per "Segui flusso", lato client + + + Sample "Follow Stream" server text + Esempio di testo per "Segui flusso", lato server + + + Sample valid filter + Esempio di filtro valido + + + Sample invalid filter + Esempio di filtro non valido + + + Sample warning filter + Sample deprecated filter + Esempio di filtro di avviso + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + I pacchetti di query GIF zelanti hanno dimensioni di finestra jumbo + + + Lazy badgers move unique waxy jellyfish packets + Quel vituperabile xenofobo zelante sposta pacchetti mentre assaggia il whisky ed esclama: alleluja! + + + Font + Carattere + + + + FunnelStringDialog + + Dialog + Finestra + + + + FunnelTextDialog + + Dialog + Finestra + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>Digita del testo o un'espressione regolare. Sarà evidenziato sopra.</p></body></html> + + + Highlight: + Evidenzia: + + + + GsmMapSummaryDialog + + Dialog + Finestra + + + GSM MAP Summary + Riepilogo GSM MAP + + + File + File + + + Name + Name + + + Length + Lunghezza + + + Format + Formato + + + Snapshot length + Lunghezza istantanea + + + Data + Dati + + + First packet + Primo pacchetto + + + Last packet + Ultimo pacchetto + + + Elapsed + Trascorso + + + Packets + Pacchetti + + + Invokes + Invoke + + + Total number of Invokes + Numero totale di Invoke + + + Average number of Invokes per second + Numero medio di Invoke al secondo + + + Total number of bytes for Invokes + Numero totale di byte per Invoke + + + Average number of bytes per Invoke + Numero medio di byte per Invoke + + + Return Results + Return Result + + + Total number of Return Results + Numero totale di Return Result + + + Average number of Return Results per second + Numero medio di Return Result al secondo + + + Total number of bytes for Return Results + Numero totale di byte di Return Result + + + Average number of bytes per Return Result + Numero medio di byte di Return Result + + + Totals + Totali + + + Total number of GSM MAP messages + Numero totale di messaggi GSM MAP + + + Average number of GSM MAP messages per second + Numero medio di messaggi GSM MAP al secondo + + + Total number of bytes for GSM MAP messages + Numero totale di byte per messaggio GSM MAP + + + Average number of bytes per GSM MAP message + Numero medio di byte per messaggio GSM MAP + + + + IOConsoleDialog + + Dialog + Finestra + + + Enter code + Digita codice + + + Evaluate + Valuta + + + Clear + Pulisci + + + Use %1 to evaluate. + Usa %1 per valutare. + + + + IOGraphDialog + + Dialog + Finestra + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Scorciatoie di tastiera preziose e che risparmiano tempo</h3> +<table><tbody> + +<tr><th>+</th><td>Ingrandisci</td></th> +<tr><th>-</th><td>Rimpicciolisci</td></th> +<tr><th>x</th><td>Ingrandisci asse X</td></th> +<tr><th>X</th><td>Rimpicciolisci asse X</td></th> +<tr><th>y</th><td>Ingrandisci asse Y</td></th> +<tr><th>Y</th><td>Rimpicciolisci asse Y</td></th> +<tr><th>0</th><td>Reimposta il grafico al suo stato iniziale</td></th> + +<tr><th>→</th><td>Sposta a destra di 10 pixel</td></th> +<tr><th>←</th><td>Sposta a sinistra di 10 pixel</td></th> +<tr><th>↑</th><td>Sposta in su di 10 pixel</td></th> +<tr><th>↓</th><td>Sposta in giù di 10 pixel</td></th> +<tr><th><i>Maiusc+</i>→</th><td>Sposta a destra di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>←</th><td>Sposta a sinistra di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>↑</th><td>Sposta in su di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>↓</th><td>Sposta in giù di 1 pixel</td></th> + +<tr><th>g</th><td>Vai al pacchetto sotto il cursore</td></th> + +<tr><th>z</th><td>Inverti il trascinamento / ingrandimento del mouse</td></th> +<tr><th>t</th><td>Commuta l'origine dell'orario della cattura / sessione</td></th> +<tr><th>Spazio</th><td>Commuta il reticolo</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + Rimuovi questo grafico. + + + Add a new graph. + Aggiungi un nuovo grafico. + + + Duplicate this graph. + Duplica questo grafico. + + + Clear all graphs. + Cancella tutti i grafici. + + + Move this graph upwards. + Sposta questo grafico verso l'alto. + + + Move this graph downwards. + Sposta questo grafico verso il basso. + + + Mouse + Mouse + + + Drag using the mouse button. + Trascina utilizzando il pulsante del mouse. + + + drags + trascinamenti + + + Select using the mouse button. + Seleziona utilizzando il pulsante del mouse. + + + zooms + zoom + + + Interval + intervallo + + + Time of day + Ora del giorno + + + Log scale + Scala logaritmica + + + Automatic update + Aggiornamento automatico + + + Enable legend + Abilita la legenda + + + Reset + Ripristina + + + Reset Graph + Ripristina il grafico + + + Reset the graph to its initial state. + Ripristina il grafico al suo stato iniziale. + + + 0 + 0 + + + Zoom In + Ingrandisci + + + + + + + + + Zoom Out + Rimpicciolisci + + + - + - + + + Move Up 10 Pixels + Sposta in su di 10 pixel + + + Up + Su + + + Move Left 10 Pixels + Sposta a sinistra di 10 pixel + + + Left + Sinistra + + + Move Right 10 Pixels + Sposta a destra di 10 pixel + + + Right + Destra + + + Move Down 10 Pixels + Sposta in giù di 10 pixel + + + Down + Giù + + + Move Up 1 Pixel + Sposta in su di 1 pixel + + + Shift+Up + Shift+Su + + + Move Left 1 Pixel + Sposta a sinistra di 1 pixel + + + Shift+Left + Shift+Sinistra + + + Move Right 1 Pixel + Sposta a destra di 1 pixel + + + Shift+Right + Shift+Destra + + + Move Down 1 Pixel + Sposta in giù di 1 pixel + + + Move down 1 Pixel + Move down 1 pixel + Sposta in giù di 1 pixel + + + Shift+Down + Shift+Giù + + + Go To Packet Under Cursor + Vai al pacchetto sotto il cursore + + + Go to packet currently under the cursor + Vai al pacchetto attualmente sotto il cursore + + + G + G + + + Drag / Zoom + Trascina/Zoom + + + Toggle mouse drag / zoom behavior + Inverti il comportamento di trascina/zoom del mouse + + + Z + Z + + + Capture / Session Time Origin + Orario di origine della cattura/sessione + + + Toggle capture / session time origin + Inverti il tempo di origine della cattura/sessione + + + T + T + + + Crosshairs + Reticolo + + + Toggle crosshairs + Inverti reticolo + + + Space + Spazio + + + Zoom In X Axis + Ingrandisci asse X + + + X + X + + + Zoom Out X Axis + Rimpicciolisci asse X + + + Shift+X + Maiusc+X + + + Zoom In Y Axis + Ingrandisci asse Y + + + Y + Y + + + Zoom Out Y Axis + Rimpicciolisci asse Y + + + Shift+Y + Maiusc+Y + + + 1 sec + 1 sec + + + 10 sec + 10 sec + + + 1 min + 1 min + + + 10 min + 10 min + + + Time (s) + Tempo (s) + + + I/O Graphs + Grafici I/O + + + Save As… + Salva come... + + + Copy + Copia + + + Copy graphs from another profile. + Copia grafici da un altro profilo. + + + 1 ms + 1 ms + + + 2 ms + 2 ms + + + 5 ms + 5 ms + + + 10 ms + 10 ms + + + 20 ms + 20 ms + + + 50 ms + 50 ms + + + 100 ms + 100 ms + + + 200 ms + 200 ms + + + 500 ms + 500 ms + + + 2 sec + 2 sec + + + 5 sec + 5 sec + + + Wireshark I/O Graphs: %1 + Grafici di I/O di Wireshark: %1 + + + Filtered packets + Pacchetti filtrati + + + Filtered events + Eventi filtrati + + + All Packets + Tutti i pacchetti + + + TCP Errors + Errori TCP + + + All Events + Tutti gli eventi + + + Access Denied + Accesso negato + + + Hover over the graph for details. + Sposta il mouse sul grafico per i dettagli. + + + No packets in interval + Nessun pacchetto nell'intervallo + + + No events in interval + Nessun evento nell'intervallo + + + Click to select packet + Fai clic per selezionare il pacchetto + + + Packet + Pacchetto + + + Click to select event + Fai clic per selezionare l'evento + + + Event + Evento + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Rilascia per lo zoom, x = %1 a %2, y = %3 a %4 + + + Unable to select range. + Impossibile selezionare la serie. + + + Click to select a portion of the graph. + Fai clic per selezionare una porzione del grafico. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + Valori separati da virgola (*.csv) + + + Save Graph As… + Salva il grafico come... + + + + Iax2AnalysisDialog + + Dialog + Finestra + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Diretto</span></p><p><span style=" font-size:medium; font-weight:600;">Inverso</span></p></body></html> + + + Forward + Diretto + + + Packet + Pacchetto + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter (ms) + + + Bandwidth + Larghezza di banda + + + Status + Stato + + + Length + Lunghezza + + + Reverse + Inverso + + + Graph + Grafico + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>Mostra o nascondi i valori di jitter diretto.</p></body></html> + + + Forward Jitter + Jitter diretto + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>Mostra o nascondi i valori di differenza diretta.</p></body></html> + + + Forward Difference + Differenza diretta + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>Mostra o nascondi i valori di jitter inverso.</p></body></html> + + + Reverse Jitter + Jitter inverso + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>Mostra o nascondi i valori di differenza inversa.</p></body></html> + + + Reverse Difference + Differenza inversa + + + <small><i>A hint.</i></small> + <small><i>Un suggerimento.</i></small> + + + Audio + Audio + + + Save the audio data for both channels. + Salva i dati audio per entrambi i canali. + + + Forward Stream Audio + Audio flusso diretto + + + Save the forward stream audio data. + Salva i dati audio del flusso diretto. + + + Reverse Stream Audio + Audio flusso inverso + + + Save the reverse stream audio data. + Salva i dati audio del flusso inverso. + + + CSV + CSV + + + Save both tables as CSV. + Salva entrambe le tabelle come CSV. + + + Forward Stream CSV + CSV flusso diretto + + + Save the forward table as CSV. + Salva la tabella diretta come CSV. + + + Reverse Stream CSV + CSV flusso inverso + + + Save the reverse table as CSV. + Salva la tabella inversa come CSV. + + + Save Graph + Salva grafico + + + Save the graph image. + Salva l'immagine del grafico. + + + Go to Packet + Vai al pacchetto + + + Select the corresponding packet in the packet list. + Seleziona il pacchetto corrispondente nell'elenco dei pacchetti. + + + G + G + + + Next Problem Packet + Pacchetto problematico successivo + + + Go to the next problem packet + Vai al successivo pacchetto problematico + + + N + N + + + IAX2 Stream Analysis + Analisi flusso IAX2 + + + Unable to save RTP data. + Impossibile salvare i dati RTP. + + + Please select an IAX2 packet. + Seleziona un pacchetto IAX2. + + + G: Go to packet, N: Next problem packet + G: Vai al pacchetto, N: Pacchetto problematico successivo + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Salva il grafico come + + + Can't save in a file: Wrong length of captured packets. + Impossibile salvare in un file: lunghezza errata dei pacchetti catturati. + + + Can't save in a file: File I/O problem. + Impossibile salvare in un file: problemi di I/O sul file. + + + Save forward stream audio + Salva l'audio del flusso diretto + + + Save reverse stream audio + Salva l'audio del flusso inverso + + + Save audio + Salva audio + + + Sun Audio (*.au) + Sun Audio (*.au) + + + ;;Raw (*.raw) + ;;Raw (*.raw) + + + Warning + Avviso + + + Unable to save in that format + Impossibile salvare in quel formato + + + Unable to save %1 + Impossibile salvare %1 + + + Saving %1… + Salvataggio di %1... + + + Analyzing IAX2 + Analisi IAX2 + + + Save forward stream CSV + Salva CSV del flusso diretto + + + Save reverse stream CSV + Salva CSV del flusso inverso + + + Save CSV + Salva CSV + + + Comma-separated values (*.csv) + Valori separati da virgola (*.csv) + + + + ImportTextDialog + + File: + File: + + + Set name of text file to import + Seleziona il nome del file da importare + + + Browse for text file to import + Sfoglia il file di testo da importare + + + Browse… + Browse... + Sfoglia... + + + Hex Dump + Dump esadecimale + + + Import a standard hex dump as exported by Wireshark + Importa un dump esadecimale come esportato da Wireshark + + + Offsets in the text file are in octal notation + Gli offset nel file di testo sono in notazione ottale + + + Octal + Ottale + + + Offsets: + Offset: + + + Offsets in the text file are in hexadecimal notation + Gli offset nel file di testo sono in notazione esadecimale + + + Hexadecimal + Esadecimale + + + Offsets in the text file are in decimal notation + Gli offset nel file di testo sono in notazione decimale + + + Decimal + Decimale + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>Decidi se eseguire un'elaborazione aggiuntiva durante la rilevazione della rappresentazione ASCII alla fine di una riga hex+ASCII anche se sembrano byte esadecimali.</p><p>Non abilitarla se il dump esadecimale non contiene ASCII.</p></body></html> + + + ASCII identification: + Identificazione ASCII: + + + Regular Expression + Espressione regolare + + + Import a file formatted according to a custom regular expression + Importa un file formattato in base a un'espressione regolare personalizzata + + + Packet format regular expression + Espressione regolare di formato del pacchetto + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + <html><head/><body><p>Espressione regolare Perl che cattura un singolo pacchetto nel file con gruppi con nomi che identificano i dati da importare. Le ancore ^ e $ verificano anche prima/dopo le interruzioni di riga </p><p>È richiesto un solo gruppo di dati, sono supportati anche ora, dir e numseq.</p><p>Argomenti delle espressioni regolari: DUPNAMES, MULTILINE e NOEMPTY</p></body></html> + + + This is regexHintLabel, it will be set to default_regex_hint + Questa è etichettaSuggerimentoRegex, sarà impostata a default_regex_hint. + + + Data encoding: + Codifica dei dati: + + + How data is encoded + Come sono codificati i dati + + + encodingRegexExample + esempioEspressioneRegolareCodifica + + + List of characters indicating incoming packets + Elenco dei caratteri che indicano i pacchetti in ingresso + + + iI< + iI< + + + List of characters indicating outgoing packets + Elenco dei caratteri che indicano i pacchetti in uscita + + + oO> + oO> + + + Timestamp format: + Formato della marca temporale: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Il file contiene o meno le informazioni che indicano la direzione (ingresso o uscita) del pacchetto. + + + Direction indication: + Indicazione della direzione: + + + ExportPDU + EsportaPDU + + + IP version: + Versione IP: + + + Interface name: + Nome interfaccia: + + + The name of the interface to write to the import capture file + Il nome dell'interfaccia da scrivere su file di cattura da importare + + + Fake IF, Import from Hex Dump + IF finta, importazione da dump esadecimale + + + Maximum frame length: + Lunghezza massima del frame: + + + Encapsulation + Incapsulamento + + + The text file has no offset + Il file di testo non ha offset + + + None + Nessuno + + + <small><i>recommended regex:</small></i> + <small><i>espressione regolare consigliata:</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + Il formato in cui leggere le marche temporali nel file di testo (es. %H:%M:%S.). Gli specificatori di formato sono basati su strptime(3) + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + <html><head/><body><p>Il formato in cui elaborare le marche temporali nel file di testo (es. %H:%M:%S.%f).</p><p>Gli specificatori di formato sono basati su strptime(3) con l'aggiunta di %f per le frazioni di secondo. La precisione di %f è determinata dalla sua lunghezza.</p></body></html> + + + %H:%M:%S.%f + %H:%M:%S.%f + + + timestampExampleLabel + etichettaEsempioMarcaTemporale + + + Encapsulation Type: + Tipo di incapsulamento: + + + Encapsulation type of the frames in the import capture file + Tipo di incapsulamento dei frame nel file di importazione della cattura + + + Prefix each frame with an Ethernet and IP header + Anteponi a ogni frame un'intestazione Ethernet e IP + + + IP + IP + + + Prefix each frame with an Ethernet, IP and UDP header + Anteponi a ogni frame un'intestazione Ethernet, IP e UDP + + + Prefix each frame with an Ethernet, IP and TCP header + Anteponi a ogni frame un'intestazione Ethernet, IP e TCP + + + Prefix each frame with an Ethernet, IP and SCTP header + Anteponi a ogni frame un'intestazione Ethernet, IP e SCTP + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + Anteponi a ogni frame un'intestazione Ethernet, IP e SCTP (DATA) + + + Source address: + Indirizzo sorgente: + + + Destination address: + Indirizzo di destinazione: + + + Dissector + Decodificatore + + + The IP protocol ID for each frame + L'ID di protocollo IP per ogni frame + + + The IP source address for each frame + L'indirizzo IP sorgente per ogni frame + + + The IP destination address for each frame + L'indirizzo IP di destinazione per ogni frame + + + The UDP, TCP or SCTP source port for each frame + Porta sorgente UDP, TCP o SCTP per ogni frame + + + The SCTP DATA payload protocol identifier for each frame + Identificatore del protocollo del payload SCTP DATA per ogni frame + + + The UDP, TCP or SCTP destination port for each frame + Porta di destinazione UDP, TCP o SCTP per ogni frame + + + Prefix each frame with an Ethernet header + Anteponi un'intestazione ethernet a ogni frame + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + Protocollo (dec): + + + Leave frames unchanged + Lascia i frame inalterati + + + No dummy header + Nessuna intestazione fittizia + + + Tag: + Tag: + + + UDP + UDP + + + Source port: + Porta sorgente: + + + The Ethertype value of each frame + Il valore di Ethertype per ogni frame + + + TCP + TCP + + + The SCTP verification tag for each frame + Il tag di verifica SCTP per ogni frame + + + Destination port: + Porta di destinazione: + + + Ethertype (hex): + Ethertype (esa): + + + SCTP (Data) + SCTP (Dati) + + + The dissector to use for each frame + Il decodificatore da utilizzare per ogni frame + + + The IP Version to use for the dummy IP header + La versione IP da utilizzare con l'intestazione IP fittizia + + + The maximum size of the frames to write to the import capture file (max 256kiB) + La dimensione massima dei frame da scrivere sul file di importazione della cattura (max 256kiB) + + + Supported fields are data, dir, time, seqno + I campi supportati sono dati, dir, ora, numseq + + + Missing capturing group data (use (? + mancano i dati del gruppo di cattura (usa (? + + + Import From Hex Dump + Importa da dump esadecimale + + + Import + Importa + + + Import Text File + Importa da file di testo + + + + InterfaceFrame + + Frame + Frame + + + Wired + Cablata + + + AirPCAP + AirPCAP + + + Pipe + Pipe + + + STDIN + STDIN + + + Bluetooth + Bluetooth + + + Wireless + Wireless + + + Dial-Up + Dial-Up + + + USB + USB + + + External Capture + Cattura esterna + + + Virtual + Virtuale + + + Remote interfaces + Interfacce remote + + + Show hidden interfaces + Mostra interfacce nascoste + + + External capture interfaces disabled. + Interfacce di cattura esterne disabilitate. + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + <p>Le interfacce locali non sono disponibili poiché non è installato alcun driver di cattura.</p><p>Puoi correggere questo problema installando <a href="https://npcap.com/">Npcap</a>.</p> + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + <p>Le interfacce locali non sono disponibili poiché non è caricato alcun driver di cattura.</p><p>Puoi correggere questo problema eseguendo <pre>net start npcap</pre>, se ha installato Npcap o <pre>net start npf</pre>, se hai installato WinPcap. Entrambi i comandi devono essere eseguiti come amministratore.</p> + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + <p>Non hai i permessi per catturare sulle interfacce locali.</p><p>Puoi correggere questo problema <a href="file://%1">installando ChmodBPF</a>.</p> + + + You don't have permission to capture on local interfaces. + Non hai i permessi per catturare sulle interfacce locali. + + + No interfaces found. + Nessuna interfaccia trovata. + + + Interfaces not loaded (due to preference). Go to Capture + Interfacce non caricate (a causa delle preferenze). Vai in Cattura + + + Start capture + Avvia la cattura + + + Hide Interface + Nascondi interfaccia + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + Nessuna interfaccia da visualizzare. %1 interfacce nascoste. + + + + InterfaceToolbar + + Frame + Frame + + + Select interface + Seleziona interfaccia + + + Interface + Interfaccia + + + + InterfaceToolbarLineEdit + + Apply changes + Applica modifiche + + + + InterfaceTreeModel + + Show + Mostra + + + Friendly Name + Nome intuitivo + + + Interface Name + Nome dell'interfaccia + + + No interfaces found. + Nessuna interfaccia trovata. + + + This version of Wireshark was built without packet capture support. + Questa versione di Wireshark è stata compilata senza supporto per la cattura dei pacchetti. + + + Local Pipe Path + Percorso pipe locale + + + Comment + Commento + + + Link-Layer Header + Intestazione livello di collegamento + + + Promiscuous + Modalità promiscua + + + Snaplen (B) + Lunghezza di cattura (B) + + + Buffer (MB) + Buffer (MB) + + + Monitor Mode + Modalità di monitoraggio + + + Capture Filter + Filtro di cattura + + + Addresses + Indirizzi + + + Address + Indirizzo + + + Extcap interface: %1 + Interfaccia Extcap: %1 + + + No addresses + Nessun indirizzo + + + No capture filter + Nessun filtro di cattura + + + Capture filter + Filtro di cattura + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + Statistiche di trasporto LBT-RM + + + Sources + Sorgenti + + + Address/Transport + Indirizzo/Trasporto + + + Data frames + Frame di dati + + + Data bytes + Byte di dati + + + Data frames/bytes + Frame/byte di dati + + + Data rate + Tasso dei dati + + + RX data frames + Frame di dati in ricezione + + + RX data bytes + Byte di dati in ricezione + + + RX data frames/bytes + Frame/byte di dati in ricezione + + + RX data rate + Tasso dei dati in ricezione + + + NCF frames + Frame NCF + + + NCF count + Conteggio NCF + + + NCF bytes + Byte NCF + + + NCF frames/bytes + Frame/byte NCF + + + NCF count/bytes + Conteggio/byte NCF + + + NCF frames/count + Conteggio/Frame NCF + + + NCF frames/count/bytes + Frame/conteggio/byte NCF + + + NCF rate + Tasso NCF + + + SM frames + Frame SM + + + SM bytes + Byte SM + + + SM frames/bytes + Frame/byte SM + + + SM rate + Tasso SM + + + Show + Mostra + + + Data + Dati + + + RX Data + Dati in ricezione + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + Numeri di sequenza per il trasporto + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Conteggio + + + Frame + Frame + + + SQN/Reason + SQN/Motivo + + + Receivers + Ricevitori + + + NAK frames + Frame NAK + + + NAK count + Conteggio NAK + + + NAK bytes + Byte NAK + + + NAK rate + Tasso NAK + + + NAK sequence numbers for transport + Numeri di sequenza NAK per il trasporto + + + Display filter: + Filtro di visualizzazione: + + + Regenerate statistics using this display filter + Rigenera le statistiche usando questo filtro di visualizzazione + + + Apply + Applica + + + Copy as CSV + Copia come CSV + + + Copy the tree as CSV + Copia l'albero come CSV + + + Copy as YAML + Copia come YAML + + + Copy the tree as YAML + Copia l'albero come YAML + + + Show the data frames column + Mostra la colonna dei dati dei frame + + + Show the data bytes column + Mostra la colonna dei byte di dati + + + Show the data frames/bytes column + Mostra la colonna dei frame/byte di dati + + + Show the RX data frames column + Mostra la colonna dei frame in ricezione + + + Show the RX data bytes column + Mostra la colonna dei byte in ricezione + + + Show the RX data frames/bytes column + Mostra la colonna dei frame/byte di dati + + + Show the NCF frames column + Mostra la colonna dei frame NCF + + + Show the NCF bytes column + Mostra la colonna dei byte NCF + + + Show the NCF count column + Mostra la colonna del conteggio NCF + + + Show the data rate column + Mostra la colonna del tasso dei dati + + + Show the RX data rate column + Mostra la colonna del tasso dei dati in ricezione + + + Show the NCF frames/bytes column + Mostra la colonna dei frame/byte NCF + + + Show the NCF count/bytes column + Mostra la colonna del conteggio/byte NCF + + + Show the NCF frames/count column + Mostra la colonna dei frame/conteggio NCF + + + Show the NCF frames/count/bytes column + Mostra la colonna dei frame/conteggio/byte NCF + + + Show the NCF rate column + Mostra la colonna del tasso NCF + + + Show the SM frames column + Mostra la colonna dei frame SM + + + Show the SM bytes column + Mostra la colonna dei byte SM + + + Show the SM frames/bytes column + Mostra la colonna dei frame/byte SM + + + Show the SM rate column + Mostra la colonna del tasso SM + + + Auto-resize columns to content + Ridimensiona automaticamente le colonne in base al contenuto + + + Resize columns to content size + Ridimensiona le colonne in base alla dimensione del contenuto + + + LBT-RM Statistics failed to attach to tap + Le statistiche LBT-RM non sono riuscite ad agganciare il tap + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + Statistiche di trasporto LBT-RU + + + Sources + Sorgenti + + + Address/Transport/Client + Indirizzo/Trasporto/Client + + + Data frames + Frame di dati + + + Data bytes + Byte di dati + + + Data frames/bytes + Frame/byte di dati + + + Data rate + Tasso dei dati + + + RX data frames + Frame di dati in ricezione + + + RX data bytes + Byte di dati in ricezione + + + RX data frames/bytes + Frame/byte di dati in ricezione + + + RX data rate + Tasso dei dati in ricezione + + + NCF frames + Frame NCF + + + NCF count + Conteggio NCF + + + NCF bytes + Byte NCF + + + NCF frames/count + Conteggio/Frame NCF + + + NCF frames/bytes + Frame/byte NCF + + + NCF count/bytes + Conteggio/byte NCF + + + NCF frames/count/bytes + Frame/conteggio/byte NCF + + + NCF rate + Tasso NCF + + + SM frames + Frame SM + + + SM bytes + Byte SM + + + SM frames/bytes + Frame/byte SM + + + SM rate + Tasso SM + + + RST frames + Frame RST + + + RST bytes + Byte RST + + + RST frames/bytes + Frame/Byte RST + + + RST rate + Tasso RST + + + Show + Mostra + + + Data SQN + Dati SQN + + + RX Data SQN + Dati in ricezione SQN + + + NCF SQN + NCF SQN + + + SM SQN + SM SQN + + + RST reason + Motivo RST + + + details for transport + Dettagli per il trasporto + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Conteggio + + + Frame + Frame + + + Reason + Motivo + + + SQN/Reason + SQN/Motivo + + + Receivers + Ricevitori + + + Address/Transport + Indirizzo/Trasporto + + + NAK frames + Frame NAK + + + NAK count + Conteggio NAK + + + NAK bytes + Byte NAK + + + NAK frames/count + Frame/conteggio NAK + + + NAK count/bytes + Conteggio/Byte NAK + + + NAK frames/bytes + Frame/Byte NAK + + + NAK frames/count/bytes + Frame/conteggio/byte NAK + + + NAK rate + Tasso NAK + + + ACK frames + Frame ACK + + + ACK bytes + Byte ACK + + + ACK frames/bytes + Frame/byte ACK + + + ACK rate + Tasso ACK + + + CREQ frames + Frame CREQ + + + CREQ bytes + Byte CREQ + + + CREQ frames/bytes + Frame/byte CREQ + + + CREQ rate + Rate CREQ + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + Richiesta CREQ + + + Display filter: + Filtro di visualizzazione: + + + Regenerate statistics using this display filter + Rigenera le statistiche usando questo filtro di visualizzazione + + + Apply + Applica + + + Copy as CSV + Copia come CSV + + + Copy the tree as CSV + Copia l'albero come CSV + + + Copy as YAML + Copia come YAML + + + Copy the tree as YAML + Copia l'albero come YAML + + + Show the data frames column + Mostra la colonna dei dati dei frame + + + Show the data bytes column + Mostra la colonna dei byte di dati + + + Show the data frames/bytes column + Mostra la colonna dei frame/byte di dati + + + Show the data rate column + Mostra la colonna del tasso dei dati + + + Show the RX data frames column + Mostra la colonna dei frame in ricezione + + + Show the RX data bytes column + Mostra la colonna dei byte in ricezione + + + Show the RX data frames/bytes column + Mostra la colonna dei frame/byte di dati + + + Show the RX data rate column + Mostra la colonna del tasso dei dati in ricezione + + + Show the NCF frames column + Mostra la colonna dei frame NCF + + + Show the NCF count column + Mostra la colonna del conteggio NCF + + + Show the NCF bytes column + Mostra la colonna dei byte NCF + + + Show the NCF frames/bytes column + Mostra la colonna dei frame/byte NCF + + + Show the NCF count/bytes column + Mostra la colonna del conteggio/byte NCF + + + Show the NCF frames/count column + Mostra la colonna dei frame/conteggio NCF + + + Show the NCF frames/count/bytes column + Mostra la colonna dei frame/conteggio/byte NCF + + + Show the SM frames column + Mostra la colonna dei frame SM + + + Show the SM bytes column + Mostra la colonna dei byte SM + + + Show the SM frames/bytes column + Mostra la colonna dei frame/byte SM + + + Show the SM rate column + Mostra la colonna del tasso SM + + + Show the RST frames column + Mostra la colonna dei frame RST + + + Show the RST bytes column + Mostra la colonna dei byte RST + + + Show the RST frames/bytes column + Mostra la colonna dei frame/byte RST + + + Show the RST rate column + Mostra la colonna del tasso RST + + + Show the NAK frames column + Mostra la colonna dei frame NAK + + + Show the NAK count column + Mostra la colonna del conteggio NAK + + + Show the NAK bytes column + Mostra la colonna dei byte NAK + + + Show the NAK frames/count column + Mostra la colonna dei frame/conteggio NAK + + + Show the NAK count/bytes column + Mostra la colonna del conteggio/byte NAK + + + Show the NAK frames/bytes column + Mostra la colonna dei frame/byte NAK + + + Show the NAK frames/count/bytes column + Mostra la colonna dei frame/conteggio/byte NAK + + + Show the NAK rate column + Mostra la colonna del tasso NAK + + + Show the ACK frames column + Mostra la colonna dei frame ACK + + + Show the ACK bytes column + Mostra la colonna dei byte ACK + + + Show the ACK frames/bytes column + Mostra la colonna dei frame/byte ACK + + + Show the ACK rate column + Mostra la colonna del tasso ACK + + + Show the CREQ frames column + Mostra la colonna dei frame CREQ + + + Show the CREQ bytes column + Mostra la colonna dei byte CREQ + + + Show the CREQ frames/bytes column + Mostra la colonna dei frame/byte CREQ + + + Show the CREQ rate column + Mostra la colonna del tasso CREQ + + + Auto-resize columns to content + Ridimensiona automaticamente le colonne in base al contenuto + + + Resize columns to content size + Ridimensiona le colonne in base alla dimensione del contenuto + + + Show the NCF rate column + Mostra la colonna del tasso NCF + + + LBT-RU Statistics failed to attach to tap + Le statistiche LBT-RU non sono riuscite ad agganciare il tap + + + + LBMStreamDialog + + Dialog + Finestra + + + Stream + Flusso + + + Endpoint A + Terminatore A + + + Endpoint B + Terminatore B + + + Messages + Messaggi + + + Bytes + Byte + + + First Frame + Primo frame + + + Last Frame + Ultimo frame + + + Display filter: + Filtro di visualizzazione: + + + Regenerate statistics using this display filter + Rigenera le statistiche usando questo filtro di visualizzazione + + + Apply + Applica + + + Copy as CSV + Copia come CSV + + + Copy the tree as CSV + Copia l'albero come CSV + + + Copy as YAML + Copia come YAML + + + Copy the tree as YAML + Copia l'albero come YAML + + + LBM Stream failed to attach to tap + Il flusso LBM non è riuscito ad agganciare il tap + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + LayoutPreferencesFrame + + Frame + Frame + + + Pane 1: + Pannello 1: + + + Packet List + Elenco pacchetti + + + Packet Details + Dettagli del pacchetto + + + Packet Bytes + Byte pacchetto + + + Packet Diagram + Diagramma del pacchetto + + + None + Nessuno + + + Pane 2: + Pannello 2: + + + Pane 3: + Pannello 3: + + + Packet List settings: + Impostazioni elenco dei pacchetti: + + + Show packet separator + Mostra il separatore dei pacchetti + + + Show column definition in column context menu + Mostra la definizione della colonna nel menu contestuale delle colonne + + + Allow the list to be sorted + Consenti l'ordinamento dell'elenco + + + Maximum number of cached rows (affects sorting) + Numero massimo di righe memorizzate nella cache (influisce sull'ordinamento) + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + <html><head/><body><p>Se vengono visualizzate più righe, l'ordinamento per colonne che richiedono la dissezione dei pacchetti sarà disabilitato. L'aumento di questo numero aumenta il consumo di memoria archiviando nella cache i valori delle colonne.</p></body></html> + + + Enable mouse-over colorization + Abilita la colorazione al passaggio del puntatore + + + Status Bar settings: + Impostazioni barra di stato: + + + Show selected packet number + Mostra il numero dei pacchetti selezionati + + + Show file load time + Mostra il tempo di caricamento dei file + + + + LteMacStatisticsDialog + + LTE Mac Statistics + Statistiche Mac LTE + + + Include SR frames in filter + Includi i frame SR nel filtro + + + Include RACH frames in filter + Includi i frame RACH nel filtro + + + MAC Statistics + Statistiche MAC + + + + LteRlcGraphDialog + + Dialog + Finestra + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Scorciatoie di tastiera preziose e che risparmiano tempo</h3> +<table><tbody> + +<tr><th>+</th><td>Ingrandisci</td></th> +<tr><th>-</th><td>Rimpicciolisci</td></th> +<tr><th>0</th><td>Ripristina il grafico al suo stato iniziale</td></th> + +<tr><th>→</th><td>Sposta a destra di 10 pixel</td></th> +<tr><th>←</th><td>Sposta a sinistra di 10 pixel</td></th> +<tr><th>↑</th><td>Sposta in su di 10 pixel</td></th> +<tr><th>↓</th><td>Sposta in giù di 10 pixel</td></th> +<tr><th><i>Maiusc+</i>→</th><td>Sposta a destra di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>←</th><td>Sposta a sinistra di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>↑</th><td>Sposta in su di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>↓</th><td>Sposta in giù di 1 pixel</td></th> + +<tr><th>g</th><td>Vai al pacchetto sotto il cursore</td></th> + +<tr><th>z</th><td>Inverti il trascinamento / ingrandimento del mouse</td></th> +<tr><th>t</th><td>Commuta l'origine dell'orario della cattura / sessione</td></th> +<tr><th>Spazio</th><td>Commuta il reticolo</td></th> + +</tbody></table> +</body></html> + + + Mouse + Mouse + + + Drag using the mouse button. + Trascina utilizzando il pulsante del mouse. + + + drags + trascinamenti + + + Select using the mouse button. + Seleziona utilizzando il pulsante del mouse. + + + zooms + ingrandimenti + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Ripristina il grafico al suo stato iniziale.</p></body></html> + + + Reset + Ripristina + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Inverti la direzione della connessione (visualizza il flusso opposto).</p></body></html> + + + Switch Direction + Inverti direzione + + + Reset Graph + Ripristina grafico + + + Reset the graph to its initial state. + Ripristina il grafico al suo stato iniziale. + + + 0 + 0 + + + Zoom In + Ingrandisci + + + + + + + + + Zoom Out + Rimpicciolisci + + + - + - + + + Move Up 10 Pixels + Sposta in su di 10 pixel + + + Up + Su + + + Move Left 10 Pixels + Sposta a sinistra di 10 pixel + + + Left + Sinistra + + + Move Right 10 Pixels + Sposta a destra di 10 pixel + + + Right + Destra + + + Move Down 10 Pixels + Sposta in giù di 10 pixel + + + Down + Giù + + + Move Up 1 Pixel + Sposta in su di 1 pixel + + + Shift+Up + Maiusc+Su + + + Move Left 1 Pixel + Sposta a sinistra di 1 pixel + + + Shift+Left + Maiusc+Sinistra + + + Move Right 1 Pixel + Sposta a destra di 1 pixel + + + Shift+Right + Maiusc+Destra + + + Move Down 1 Pixel + Sposta in giù di 1 pixel + + + Move down 1 Pixel + Sposta in giù di 1 pixel + + + Shift+Down + Maiusc+Giù + + + Drag / Zoom + Trascina / Ingrandisci + + + Toggle mouse drag / zoom behavior + Commuta il comportamento di trascinamento/ingrandimento del mouse + + + Z + Z + + + Crosshairs + Reticolo + + + Toggle crosshairs + Inverti reticolo + + + Space + Spazio + + + Move Up 100 Pixels + Sposta in su di 100 pixel + + + PgUp + PgSu + + + PgDown + PgGiù + + + Go To Packet Under Cursor + Vai al pacchetto sotto il cursore + + + Go to packet currently under the cursor + Vai al pacchetto attualmente sotto il cursore + + + G + G + + + Zoom In X Axis + Ingrandisci asse X + + + X + X + + + Zoom Out Y Axis + Rimpicciolisci asse Y + + + Shift+Y + Maiusc+Y + + + Zoom In Y Axis + Ingrandisci asse Y + + + Y + Y + + + Zoom Out X Axis + Rimpicciolisci asse X + + + Shift+X + Maiusc+X + + + Switch direction (swap between UL and DL) + Inverti direzione (scambia tra invio e ricezione) + + + D + D + + + Time + Tempo + + + Sequence Number + Numero di sequenza + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + Grafico RLC LTE (UE=%1 can=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + Grafico RLC LTE - nessun canale selezionato + + + Save As… + Salva come... + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s seq %4 lun %5) + + + Click to select packet + Fai clic per selezionare il pacchetto + + + Packet + Pacchetto + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Rilascia per lo zoom, x = %1 a %2, y = %3 a %4 + + + Unable to select range. + Impossibile selezionare l'intervallo. + + + Click to select a portion of the graph. + Fai clic per selezionare una porzione del grafico. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Salva il grafico come... + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + Statistiche RLC LTE + + + Include SR frames in filter + Includi i frame SR nel filtro + + + Include RACH frames in filter + Includi i frame RACH nel filtro + + + Use RLC frames only from MAC frames + Usa i frame RLC solo da frame MAC + + + UL Frames + UL Frame + + + UL Bytes + UL Byte + + + UL MB/s + UL MB/s + + + UL ACKs + UL ACK + + + UL NACKs + UL NACK + + + UL Missing + UL mancanti + + + DL Frames + DL Frame + + + DL Bytes + DL Byte + + + DL MB/s + DL MB/s + + + DL ACKs + DL ACK + + + DL NACKs + DL NACK + + + DL Missing + DL Mancanti + + + RLC Statistics + Statistiche RLC + + + + MainStatusBar + + Ready to load or capture + Pronto per caricare o catturare + + + Ready to load file + Pronto per caricare file + + + Open the Capture File Properties dialog + Apri la finestra delle proprietà del file di cattura + + + Profile: %1 + Profilo: %1 + + + Manage Profiles… + Gestisci i profili... + + + New… + Nuovo... + + + Edit… + Modifica... + + + Import + Importa + + + Export + Esporta + + + Delete + Elimina + + + Switch to + Passa a + + + is the highest expert information level + is the highest expert info level + è il livello più alto di informazione per esperti + + + ERROR + ERRORE + + + WARNING + AVVISO + + + NOTE + NOTA + + + CHAT + CHAT + + + No expert information + No expert info + Nessuna informazione per esperti + + + %Ln byte(s) + , %1 bytes + + %Ln byte + %Ln byte + + + + Byte %1 + Byte %1 + + + Bytes %1-%2 + Byte %1-%2 + + + Selected Packet: %1 %2 + Pacchetto selezionato: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + Pacchetti: %1 %4 visualizzati: %2 (%3%) + + + %1 Selected: %2 (%3%) + %1 selezionati: %2 (%3%) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 marcati: %2 (%3%) + + + %1 Dropped: %2 (%3%) + %1 scartati: %2 (%3%) + + + %1 Ignored: %2 (%3%) + %1 ignorati: %2 (%3%) + + + %1 Comments: %2 + %1 commenti: %2 + + + %1 Load time: %2:%3.%4 + %1 Tempo di caricamento: %2:%3.%4 + + + No Packets + Nessun pacchetto + + + From Zip File... + Da file Zip... + + + From Directory... + Da cartella... + + + Selected Personal Profile... + Profilo personale selezionato... + + + All Personal Profiles... + Tutti i profili personali... + + + Packets: %1 + Pacchetti: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + Frame + + + Checking this will save the size, position, and maximized state of the main window. + Selezionando questa si salverà la dimensione, la posizione e lo stato massimizzato della finestra principale. + + + Remember main window size and placement + Ricorda la dimensione e il posizionamento della finestra principale + + + Open files in + Apri file in + + + This folder: + Questa cartella: + + + Browse… + Browse... + Sfoglia... + + + The most recently used folder + La cartella usata più di recente + + + Show up to + Mostra fino a + + + filter entries + voci del filtro + + + recent files + file recenti + + + Confirm unsaved capture files + Conferma file di cattura non salvati + + + Display autocompletion for filter text + Visualizza il completamento automatico per il testo del filtro + + + Main toolbar style: + Stile della barra degli strumenti principale: + + + Icons only + Solo icone + + + Text only + Solo testo + + + Icons & Text + Icone e testo + + + Window title + Titolo della finestra + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Titolo della finestra personalizzato da aggiungere al titolo esistente<br/>%F = percorso del file di cattura<br/>%P = nome del profilo<br/>%S = un separatore condizionale (&quot; - &quot;) che è mostrato solo quando è circondato da variabili con valori o testo statico<br/>%V = informazioni di versione</p></body></html> + + + Prepend window title + Anteponi il titolo della finestra + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Titolo della finestra personalizzato da anteporre al titolo esistente<br/>%F = percorso del file di cattura<br/>%P = nome del profilo<br/>%S = un separatore condizionale (&quot; - &quot;) che è mostrato solo quando è circondato da variabili con valori o testo statico<br/>%V = informazioni di versione</p></body></html> + + + Language: + Lingua: + + + Use system setting + Usa le impostazioni di sistema + + + Open Files In + Apri file in + + + + ManageInterfacesDialog + + Manage Interfaces + Gestisci interfacce + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Fai clic sulla casella per nascondere o mostrare un'interfaccia nascosta.</p></body></html> + + + Local Interfaces + Interfacce locali + + + Show + Mostra + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Aggiungi una pipe da cui catturare o rimuovi una pipe esistente dall'elenco.</p></body></html> + + + Pipes + Pipe + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Aggiungi una nuova pipe usando le impostazioni predefinite.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Rimuovi la pipe selezionata dall'elenco.</p></body></html> + + + Remote Interfaces + Interfacce remote + + + Host / Device URL + URL dell'host / dispositivo + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>Aggiungi un host remoto e le sue interfacce</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>Rimuovi l'host selezionato dall'elenco.</p></body></html> + + + Remote Settings + Impostazioni remote + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Questa versione di Wireshark non può salvare le impostazioni della pipe. + + + This version of Wireshark does not save remote settings. + Questa versione di Wireshark non può salvare le impostazioni remote. + + + This version of Wireshark does not support remote interfaces. + Questa versione di Wireshark non supporta le interfacce remote. + + + New Pipe + Nuova pipe + + + + ManufDialog + + MAC Address Blocks + Blocchi di indirizzi MAC + + + Search MAC address or address prefix. Special purpose bits are masked. + Cerca l'indirizzo MAC o il prefisso dell'indirizzo. I bit per scopi speciali sono mascherati. + + + MAC Address + Indirizzo MAC + + + Search vendor name using a case-insentitive regular expression. + Cerca il nome del fornitore utilizzando un'espressione regolare senza distinzione tra maiuscole e minuscole. + + + Vendor Name + Nome produttore + + + Show short name column. + Mostra la colonna dei nomi brevi. + + + Short name + Nome breve + + + Select all + Seleziona tutto + + + Copy + Copia + + + Find + Trova + + + Clear + Pulisci + + + + ManufTableModel + + Address Block + Blocco di indirizzi + + + Short Name + Nome breve + + + Vendor Name + Nome produttore + + + + ModulePreferencesScrollArea + + ScrollArea + AreaScorrimento + + + + Mtp3SummaryDialog + + Dialog + Finestra + + + MTP3 Summary + Riepilogo MTP3 + + + File + File + + + Name + Nome + + + Length + Lunghezza + + + Format + Formato + + + Snapshot length + Lunghezza istantanea + + + Data + Dati + + + First packet + Primo pacchetto + + + Last packet + Ultimo pacchetto + + + Elapsed + Trascorso + + + Packets + Pacchetti + + + Service Indicator (SI) Totals + Totali indicatori di servizio (SI) + + + SI + SI + + + MSUs + MSU + + + MSUs/s + MSU/s + + + Bytes + Byte + + + Bytes/MSU + Byte/MSU + + + Bytes/s + Byte/s + + + Totals + Totali + + + Total MSUs + MSU totali + + + Total Bytes + Byte totali + + + Average Bytes/MSU + Byte/MSU medi + + + Average Bytes/s + Byte/s medi + + + + MulticastStatisticsDialog + + UDP Multicast Streams + Flussi multicast UDP + + + Source Address + Indirizzo sorgente + + + Source Port + Porta sorgente + + + Destination Address + Indirizzo destinazione + + + Destination Port + Porta destinazione + + + Packets + Pacchetti + + + Packets/s + Pacchetti/s + + + Avg BW (bps) + Banda media (bps) + + + Max BW (bps) + Banda max (bps) + + + Max Burst + Burst max + + + Burst Alarms + Allarmi burst + + + Max Buffers (B) + Buffer max (B) + + + Buffer Alarms + Allarmi buffer + + + Burst measurement interval (ms): + Intervallo di misurazione burst (ms): + + + Burst alarm threshold (packets): + Soglia di allarme burst (pacchetti): + + + Buffer alarm threshold (B): + Soglia di allarme del buffer (B): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + Velocità di svuotamento del flusso (Kb/s): + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + Velocità di svuotamento totale (Kb/s): + + + The burst interval must be between 1 and 1000. + L'intervallo di burst deve essere compreso tra 1 e 1000. + + + The burst alarm threshold isn't valid. + La soglia di allarme di burst non è valida. + + + The buffer alarm threshold isn't valid. + La soglia di allarme di buffer non è valida. + + + The stream empty speed should be between 1 and 10000000. + La velocità di svuotamento del flusso dovrebbe essere compresa tra 1 e 10000000. + + + The total empty speed should be between 1 and 10000000. + La velocità di svuotamento totale dovrebbe essere compresa tra 1 e 10000000. + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + %1 flussi, banda media: %2bps, banda max: %3bps, burst max: %4 / %5ms, buffer max: %6B + + + + PacketCommentDialog + + Edit Packet Comment + Modifica commento del pacchetto + + + Add Packet Comment + Aggiungi commento del pacchetto + + + + PacketDiagram + + Packet diagram + Diagramma del pacchetto + + + Show Field Values + Mostra i valori dei campi + + + Save Diagram As… + Salva diagramma come... + + + Copy as Raster Image + Copia come immagine raster + + + …as SVG + …come SVG + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + Scalable Vector Graphics (*.svg) + + + Save Graph As… + Salva il grafico come... + + + + PacketDialog + + Dialog + Finestra + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + Mostra byte del pacchetto + + + Packet %1 + Pacchetto %1 + + + [%1 closed] + [%1 chiuso] + + + Byte %1 + Byte %1 + + + Bytes %1-%2 + Byte %1-%2 + + + %Ln byte(s) + + %Ln byte + %Ln byte + + + + + PacketFormatGroupBox + + GroupBox + CasellaGruppo + + + Packet Format + Formato del pacchetto + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>Linee del sommario del pacchetto simile all'elenco dei pacchetti</p></body></html> + + + Summary line + Linea di sommario + + + Include column headings + Includi le intestazioni di colonna + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>Dettagli del pacchetto simile all'albero dei protocolli</p></body></html> + + + Details: + Dettagli: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>Esporta solo le voci di dettaglio del pacchetto di massimo livello</p></body></html> + + + All co&llapsed + Tutti cont&ratti + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>Espandi e contrai i dettagli del pacchetto come sono visualizzati attualmente.</p></body></html> + + + As displa&yed + Come visual&izzati + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>Esporta le voci di dettaglio di tutti i pacchetti</p></body></html> + + + All e&xpanded + Tutti e&spansi + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>Esporta un dump esadecimale dei pacchetti simile alla vista dei byte del pacchetti</p></body></html> + + + Bytes + Byte + + + Include secondary data sources + Includi le fonti di dati secondarie + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + <html><head/><body><p>Genera dump esadecimali per le fonti di dati secondarie come buffer riassemblati o decifrati in aggiunta al frame</p></body></html> + + + + PacketList + + Protocol Preferences + Preferenze di protocollo + + + Summary as Text + Riepilogo come testo + + + …as CSV + …come CSV + + + …as YAML + …come YAML + + + Decode As… + Decodifica come... + + + Frame %1: %2 + + + Frame %1: %2 + + + + + [ Comment text exceeds %1. Stopping. ] + [ Il testo del commento supera %1. Interruzione. ] + + + + PacketListHeader + + Align Left + Allinea a sinistra + + + Align Center + Allinea al centro + + + Align Right + Allinea a destra + + + Edit Column + Modifica colonna + + + Resize to Contents + Ridimensiona al contenuto + + + Column Preferences… + Preferenze delle colonne... + + + Resize Column to Width… + Ridimensiona colonna alla larghezza... + + + Resolve Names + Risolvi nomi + + + Remove this Column + Rimuovi questa colonna + + + Column %1 + Colonna %1 + + + Width: + Larghezza: + + + + PacketListModel + + Column + Colonna + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + %1 può essere ordinato solo con %2 o meno righe visibili; aumenta la dimensione della cache nelle preferenze di disposizione + + + Sorting "%1"… + Ordinamento di "%1"... + + + Sorting … + Ordinamento... + + + + PacketRangeGroupBox + + Form + Modulo + + + Packet Range + Intervallo di pacchetti + + + - + - + + + Displayed + Visualizzati + + + &Marked packets only + Solo pacchetti &marcati + + + &Range: + &Intervallo: + + + Remove &ignored packets + Rimuovi i pacchetti &ignorati + + + Include &depended upon packets + Includi i pacchetti di &dipendenza + + + Also include packets depended upon, such as those used to reassemble displayed packets + Includi anche i pacchetti di dipendenza, come quelli utilizzati per riassemblare i pacchetti visualizzati + + + First &to last marked + Dal primo all'&ultimo marcati + + + &All packets + &Tutti i pacchetti + + + &Selected packets only + Solo i pacchetti &selezionati + + + Captured + Catturati + + + + PathSelectionDelegate + + Open a pipe + Apri una pipe + + + + PathSelectionEdit + + Browse + Sfoglia + + + Select a path + Seleziona un percorso + + + + PluginListModel + + Name + Nome + + + Version + Versione + + + Type + Tipo + + + Path + Percorso + + + + PortsModel + + All entries + Tutte le voci. + + + tcp + tcp + + + udp + udp + + + sctp + sctp + + + dccp + dccp + + + Name + Nome + + + Port + Porta + + + Type + Tipo + + + + PreferenceEditorFrame + + Frame + Frame + + + … + + + + a preference + una preferenza + + + Browse… + Sfoglia... + + + Open %1 preferences… + Apri le preferenze di %1... + + + Invalid value. + Valore non valido. + + + + PreferencesDialog + + Search: + Cerca: + + + Checking this will show only changed preferences. + Selezionandola saranno mostrate solo le preferenze modificate. + + + Show changed values + Mostra i valori modificati + + + Preferences + Preferenze + + + + PrefsModel + + Advanced + Avanzate + + + Appearance + Aspetto + + + Layout + Disposizione + + + Columns + Colonne + + + Font and Colors + Caratteri e colori + + + Capture + Cattura + + + Expert + Esperto + + + Filter Buttons + Pulsanti di filtro + + + RSA Keys + Chiavi RSA + + + + PrintDialog + + Packet Format + Formato del pacchetto + + + Print each packet on a new page + Stampa ogni pacchetto in una nuova pagina + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>Stampa il file di cattura su ogni pagina</p></body></html> + + + Capture information header + Intestazione delle informazioni di cattura + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>Usa i tasti &quot;+&quot; e &quot;-&quot; per fare lo zoom dell'anteprima. Usa il tasto &quot;0&quot; per ripristinare il livello di zoom.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ e - zoom, 0 ripristina</span></p></body></html> + + + Packet Range + Intervallo dei pacchetti + + + Print + Stampa + + + &Print… + Stam&pa... + + + Page &Setup… + Impostazioni &pagina... + + + %1 %2 total packets, %3 shown + %1 %2 pacchetti totali, %3 mostrati + + + Print Error + Errore di stampa + + + Unable to print to %1. + Impossibile stampare su %1. + + + + ProfileDialog + + Search for profile … + Cerca profilo... + + + Create a new profile using default settings. + Crea un nuovo profilo usando le impostazioni predefinite. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>Rimuovi questo profilo. I profili forniti dal sistema non possono essere rimossi. Il profilo predefinito sarà ripristinato dopo l'eliminazione.</p></body></html> + + + Copy this profile. + Copia questo profilo. + + + Configuration Profiles + Profili di configurazione + + + Import + noun + Importa + + + Export + noun + Esporta + + + From Zip File... + Da file Zip... + + + From Directory... + Da cartella... + + + %Ln Selected Personal Profile(s)... + + %Ln profilo personale selezionato... + %Ln profili personali selezionati + + + + All Personal Profiles... + Tutti i profili personali... + + + New profile + Nuovo profilo + + + Profile Error + Errore di profilo + + + Exporting profiles + Esportazione profili + + + No profiles found for export + Nessun profilo trovato per l'esportazione + + + Select zip file for export + Seleziona file zip per l'esportazione + + + … %Ln selected personal profile(s) + + … %Ln profilo personale selezionato + … %Ln profili personali selezionati + + + + %Ln selected personal profile(s) + + %Ln profilo personale selezionato + %Ln profili personali selezionati + + + + An import of profiles is not allowed, while changes are pending + Non è consentito importare profili, mentre ci sono modifiche in corso + + + An import is pending to be saved. Additional imports are not allowed + Un'importazione è in fase di salvataggio. Non sono consentite importazioni aggiuntive + + + An export of profiles is only allowed for personal profiles + Un'esportazione di profili è consentita solo per i profili personali + + + An export of profiles is not allowed, while changes are pending + Un'esportazione di profili non è consentita, mentre ci sono modifiche in corso + + + %Ln profile(s) exported + + %Ln profilo esportato + %Ln profili esportati + + + + Select zip file for import + Seleziona file zip per l'importazione + + + Select directory for import + Seleziona cartella per l'importazione + + + Zip File (*.zip) + File Zip (*.zip) + + + Error + Errore + + + An error has occurred while exporting profiles + Si è verificato un errore durante l'esportazione dei profili + + + No profiles found for import in %1 + Nessun profilo trovato per l'importazione in %1 + + + %Ln profile(s) imported + + %Ln profilo importato + %Ln profili importati + + + + , %Ln profile(s) skipped + + , %Ln profilo saltato + , %Ln profili saltati + + + + Importing profiles + Importazione profili + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + ProfileModel + + Resetting to default + Ripristino dei valori predefiniti + + + Imported profile + Profilo importato + + + This is a system provided profile + Questo è un profilo fornito dal sistema + + + A profile change for this name is pending + Una modifica al profilo per questo nome è in corso + + + (See: %1) + (Vedi: %1) + + + This is an invalid profile definition + Questa è una definizione non valida di un profilo + + + A profile already exists with this name + Un profilo con questo nome esiste già + + + A profile with this name is being deleted + Un profilo con questo nome è in fase di eliminazione + + + Created from default settings + Creato dalle impostazioni predefinite + + + system provided + fornito dal sistema + + + deleted + eliminato + + + copy + noun + copia + + + Exporting profiles while changes are pending is not allowed + Non è consentito esportare profili mentre ci sono modifiche in corso. + + + No profiles found to export + Nessun profilo trovato per l'esportazione + + + Can't delete profile directory + Impossibile eliminare la cartella del profilo + + + A profile name cannot contain the following characters: %1 + Il nome di un profilo non può contenere i seguenti caratteri: %1 + + + A profile name cannot contain the '/' character + Il nome di un profilo non può contenere il carattere '/' + + + A profile cannot start or end with a period (.) + Un profilo non può iniziare o finire con un punto (.) + + + Default + Predefinito + + + Global + Globale + + + Personal + Personale + + + Renamed from: %1 + Rinominato da: %1 + + + Copied from: %1 + Copiato da: %1 + + + renamed to %1 + rinominato in %1 + + + Profile + Profilo + + + Type + Tipo + + + + ProfileSortModel + + All profiles + Tutti i profili + + + Personal profiles + Profili personali + + + Global profiles + Profili globali + + + + ProgressFrame + + Frame + Frame + + + Loading + Caricamento + + + + ProtoTree + + Packet details + Dettagli del pacchetto + + + Not a field or protocol + Non un campo o protocollo + + + No field reference available for text labels. + Nessun riferimento di campo disponibile per le etichette di testo. + + + Expand Subtrees + Espandi sottoalberi + + + Collapse Subtrees + Contrai sottoalberi + + + Expand All + Espandi tutti + + + Collapse All + Contrai tutti + + + Copy + Copia + + + All Visible Items + Tutti gli elementi visibili + + + All Visible Selected Tree Items + Tutti gli elementi visibili dell'albero selezionato + + + Description + Descrizione + + + Field Name + Nome campo + + + Value + Valore + + + As Filter + Come filtro + + + Wiki Protocol Page + Pagina wiki del protocollo + + + Filter Field Reference + Riferimento campo di filtro + + + Copied + Copiato + + + Wiki Page for %1 + Pagina wiki per %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Il Wiki di Wireshark è gestito dalla comunità.</p><p>La pagina che stai per caricare potrebbe essere perfetta, incompleta, errata o inesistente.</p><p>Vuoi proseguire?</p> + + + Colorize with Filter + Colora con filtro + + + + ProtocolHierarchyDialog + + Dialog + Finestra + + + Protocol + Protocollo + + + Percent Packets + Percentuale pacchetti + + + Packets + Pacchetti + + + Percent Bytes + Percentuale byte + + + Bytes + Byte + + + Bits/s + Bit/s + + + End Packets + Pacchetti finali + + + End Bytes + Byte finali + + + End Bits/s + Bit/s finali + + + PDUs + PDU + + + <small><i>A hint.</i></small> + <small><i>Un suggerimento.</i></small> + + + Copy as CSV + Copia come CSV + + + Copy stream list as CSV. + Copia l'elenco dei flussi come CSV. + + + Copy as YAML + Copia come YAML + + + Copy stream list as YAML. + Copia l'elenco dei flussi come YAML. + + + Copy short names + Copia i nomi brevi + + + Copy short protocol names in use. + Copia i nomi brevi dei protocolli in uso. + + + Disable unused protocols + Disabilita i protocolli non utilizzati + + + Disable all protocols but those listed. + Disabilita tutti i protocolli tranne quelli elencati. + + + Re-enable unused protocols + Ripristina i protocolli non utilizzati + + + Re-enable protocols that were disabled in this dialog. + Ripristina i protocolli che sono stati disabilitati in questa finestra. + + + Protocol Hierarchy Statistics + Statistiche gerarchia di protocolli + + + Copy + Copia + + + as CSV + come CSV + + + as YAML + come YAML + + + protocol short names + nomi brevi dei protocolli + + + Protocols + Protocolli + + + Disable unused + Disabilita inutilizzati + + + Revert changes + Ripristina le modifiche + + + No display filter. + Nessun filtro di visualizzazione. + + + Display filter: %1 + Filtro di visualizzazione: %1 + + + Unused protocols have been disabled. + I protocolli non utilizzati sono stati disabilitati. + + + Protocol changes have been reverted. + Le modifiche al protocollo sono state annullate. + + + + ProtocolPreferencesMenu + + Protocol Preferences + Preferenze di protocollo + + + No protocol preferences available + Nessuna preferenza di protocollo disponibile + + + Disable %1 + Disabilita %1 + + + %1 has no preferences + %1 non ha preferenze + + + Open %1 preferences… + Apri le preferenze di %1... + + + + QObject + + Average Throughput (bits/s) + Capacità trasmissiva media (bit/s) + + + Round Trip Time (ms) + Round Trip Time (ms) + + + Segment Length (B) + Lunghezza del segmento (B) + + + Sequence Number (B) + Numero di sequenza (B) + + + Time (s) + Tempo (s) + + + Window Size (B) + Dimensione della finestra (B) + + + [no capture file] + [nessun file di cattura] + + + Conversation + Conversazione + + + Bars show the relative timeline for each conversation. + Le barre mostrano la linea temporale per ogni conversazione. + + + Endpoint + Terminatore + + + Apply as Filter + Applica come filtro + + + Prepare as Filter + Prepara come filtro + + + Find + Trova + + + Colorize + Colora + + + Look Up + Cerca + + + Copy + Copia + + + UNKNOWN + SCONOSCIUTO + + + Selected + Selezionati + + + Not Selected + Non selezionati + + + …and Selected + ...e selezionati + + + …or Selected + ...o selezionati + + + …and not Selected + ...e non selezionati + + + …or not Selected + ...o non selezionati + + + A + A + + + B + B + + + Any + Tutti + + + Don't show this message again. + Non mostrare più questo messaggio. + + + Multiple problems found + Rilevati diversi problemi + + + %1 (%L2%) + %1 (%L2%) + + + No entries. + Nessuna voce. + + + %1 entries. + %1 voci. + + + Base station + Stazione base + + + <Broadcast> + <Broadcast> + + + <Hidden> + <Hidden> + + + BSSID + BSSID + + + Beacons + Beacon + + + Data Pkts + Pacchetti di dati + + + Protection + Protezione + + + Address + Indirizzo + + + Pkts Sent + Pacchetti inviati + + + Pkts Received + Pacchetti ricevuti + + + Comment + Commento + + + Wrong sequence number + Numero di sequenza errato + + + Payload changed to PT=%1 + Payload cambiato in PT=%1 + + + Incorrect timestamp + Marca temporale non corretta + + + Marker missing? + Marcatore mancante? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + Tipo + + + UEId + UEId + + + UL Frames + UL Frame + + + UL Bytes + UL Byte + + + UL MB/s + UL MB/s + + + UL Padding % + UL Spaziatura % + + + UL Re TX + UL Re TX + + + DL Frames + DL Frame + + + DL Bytes + DL Byte + + + DL MB/s + DL MB/s + + + DL Padding % + DL Spaziatura % + + + DL CRC Failed + DL CRC non riuscito + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + Predef + + + Unknown (%1) + Sconosciuto (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + Sconosciuto + + + UE Id + UE Id + + + Name + Nome + + + Mode + Modalità + + + Priority + Priorità + + + default + predefinito + + + DLT %1 + DLT %1 + + + Invalid Display Filter + Filtro di visualizzazione non valido + + + The filter expression %1 isn't a valid display filter. (%2). + L'espressione %1 non è un filtro di visualizzazione valido. (%2). + + + Error + Errore + + + No remote interfaces found. + Non è stata trovata alcuna interfaccia remota. + + + PCAP not found + PCAP non trovate + + + Unknown error + Errore sconosciuto + + + Default + Predefinito + + + Changed + Modificato + + + Has this preference been changed? + Questa impostazione è stata cambiata? + + + Default value is empty + Il valore predefinito è vuoto + + + Gap in dissection + Divario nella decodifica + + + Edit… + Modifica... + + + Browse… + Sfoglia... + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + Interfaccia remota + + + Host: + Host: + + + Port: + Porta: + + + Authentication + Autenticazione + + + Null authentication + Autenticazione nulla + + + Password authentication + Autenticazione con password + + + Username: + Nome utente: + + + Password: + Password: + + + Clear list + Pulisci elenco + + + Error + Errore + + + No remote interfaces found. + Non è stata trovata alcuna interfaccia remota. + + + PCAP not found + PCAP non trovate + + + + RemoteSettingsDialog + + Remote Capture Settings + Impostazioni per la cattura remota + + + Capture Options + Opzioni di cattura + + + Do not capture own RPCAP traffic + Non catturare il proprio traffico RPCAP + + + Use UDP for data transfer + Usa UDP per il trasferimento dati + + + Sampling Options + Opzioni di campionamento + + + None + Nessuno + + + 1 of + 1 di + + + packets + pacchetti + + + 1 every + 1 ogni + + + milliseconds + millisecondi + + + + ResolvedAddressesDialog + + Dialog + Finestra + + + Hosts + Host: + + + Search for entry (min 3 characters) + Cerca voci (min 3 caratteri) + + + Ports + Porte + + + Search for port or name + Cerca porta o nome + + + Capture File Comments + Commenti file di cattura + + + Comment + Commento + + + Show the comment. + Mostra il commento. + + + IPv4 Hash Table + Tabella hash IPv4 + + + Show the IPv4 hash table entries. + Mostra le voci della tabella hash IPv4. + + + IPv6 Hash Table + Tabella hash IPv6 + + + Show the IPv6 hash table entries. + Mostra le voci della tabella hash IPv6. + + + Show All + Mostra tutto + + + Show all address types. + Mostra tutti i tipi di indirizzi. + + + Hide All + Nascondi tutto + + + Hide all address types. + Nascondi tutti i tipi di indirizzi. + + + IPv4 and IPv6 Addresses (hosts) + Indirizzi IPv4 e IPv6 (host) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + Mostra i nomi host IPv4 e IPv6 risolti nel formato "host". + + + Port names (services) + Nomi delle porte (servizi) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + Mostra i nomi delle porte risolti nel formato "servizi". + + + Ethernet Addresses + Indirizzi Ethernet + + + Show resolved Ethernet addresses in "ethers" format. + Mostra gli indirizzi Ethernet risolti nel formato "ether". + + + Ethernet Well-Known Addresses + Indirizzi Ethernet conosciuti + + + Show well-known Ethernet addresses in "ethers" format. + Mostra gli indirizzi Ethernet conosciuti nel formato "ether". + + + Ethernet Manufacturers + Produttori Ethernet + + + Show Ethernet manufacturers in "ethers" format. + Mostra i produttori Ethernet nel formato "ether". + + + [no file] + [nessun file] + + + Resolved Addresses + Indirizzi risolti + + + # Resolved addresses found in %1 + # Trovati indirizzi risolti in %1 + + + # Comments +# +# + # Commenti +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + Statistiche ritardo tempo di risposta %1 + + + Type + Tipo + + + Messages + Messaggi + + + Min SRT + SRT minimo + + + Max SRT + SRT massimo + + + Avg SRT + SRT medio + + + Min in Frame + Min in frame + + + Max in Frame + Max in frame + + + Open Requests + Richieste aperte + + + Discarded Responses + Risposte scartate + + + Repeated Requests + Richieste ripetute + + + Repeated Responses + Risposte ripetute + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>Seleziona un programma e la versione e digita un filtro se vuoi, poi premi Applica.</i></small> + + + Version: + Versione: + + + Program: + Programma: + + + DCE-RPC Service Response Times + Tempi di risposta del servizio DCE-RPC + + + ONC-RPC Service Response Times + Tempi di risposta del servizio ONC-RPC + + + + RsaKeysFrame + + RSA Keys + Chiavi RSA + + + RSA private keys are loaded from a file or PKCS #11 token. + Le chiavi private RSA sono caricate da un file o da un token PKCS #11. + + + Add new keyfile… + Aggiungi nuova chiave... + + + Add new token… + Aggiungi nuovo token... + + + Remove key + Rimuovi chiave + + + PKCS #11 provider libraries. + Librerie del fornitore PKCS #11. + + + Add new provider… + Aggiungi nuovo fornitore... + + + Remove provider + Rimuovi fornitore + + + Add PKCS #11 token or key + Aggiungi token o chiave PKCS #11 + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + Nessun nuovo token o chiave PKCS #11 trovato, considera di aggiungere un fornitore PKCS #11. + + + Select a new PKCS #11 token or key + Seleziona un nuovo token o chiave PKCS #11 + + + PKCS #11 token or key + Token o chiave PKCS #11 + + + Enter PIN or password for %1 (it will be stored unencrypted) + Digita il PIN o la password per %1 (sarà memorizzata senza cifratura) + + + Enter PIN or password for key + Digita il PIN o la password per la chiave + + + Key could not be added: %1 + La chiave non può essere aggiunta: %1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + Chiave privata RSA (*.pem *.p12 *.pfx *.key);;Tutti i file ( + + + Select RSA private key file + Seleziona file della chiave privata RSA + + + Libraries (*.dll) + Librerie (*.dll) + + + Libraries (*.so) + Librerie (*.so) + + + Select PKCS #11 Provider Library + Seleziona libreria del fornitore PKCS #11 + + + Changes will apply after a restart + Le modifiche saranno applicate dopo il riavvio + + + PKCS #11 provider %1 will be removed after the next restart. + Il fornitore PKCS #11 %1 sarà rimosso dopo il prossimo riavvio. + + + + RtpAnalysisDialog + + Dialog + Finestra + + + Packet + Pacchetto + + + Sequence + Sequenza + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter + Jitter (ms) + + + Skew + Skew + + + Bandwidth + Larghezza di banda + + + Marker + Marcatore + + + Status + Stato + + + Stream %1 + Flusso %1 + + + Stream %1 Jitter + Jitter del flusso %1 + + + Stream %1 Difference + Differenza del flusso %1 + + + Stream %1 Delta + Delta del flusso %1 + + + %1 streams, + %1 flussi, + + + Save one stream CSV + Salva CSV di un flusso + + + Save all stream's CSV + Salva CSV di tutti i flussi + + + &Analyze + &Analizza + + + Open the analysis window for the selected stream(s) + Apri la finestra di analisi per i flussi selezionati + + + &Set List + Elenco di in&siemi + + + &Add to List + &Aggiungi all'elenco + + + &Remove from List + &Rimuovi dall'elenco + + + Replace existing list in RTP Analysis Dialog with new one + Sostituisci l'elenco esistente nella finestra di analisi RTP con uno nuovo + + + Add new set to existing list in RTP Analysis Dialog + Aggiungi nuovo insieme all'elenco esistente nella finestra di analisi RTP + + + Remove selected streams from list in RTP Analysis Dialog + Rimuovi i flussi selezionati dall'elenco nella finestra di analisi RTP + + + Graph + Grafico + + + <small><i>A hint.</i></small> + <small><i>Un suggerimento.</i></small> + + + &Export + &Esporta + + + Open export menu + Apri menu di esportazione + + + CSV + CSV + + + Save tables as CSV. + Salva le tabelle come CSV. + + + Current Tab Stream CSV + CSV del flusso della scheda attuale + + + Save the table on the current tab as CSV. + Salva la tabella della scheda attuale come CSV. + + + All Tab Streams CSV + CSV dei flussi di tutte le schede + + + Save the table from all tabs as CSV. + Salva la tabella di tutte le schede come CSV. + + + Save Graph + Salva grafico + + + Save the graph image. + Salva l'immagine del grafico. + + + Go to Packet + Vai al pacchetto + + + Select the corresponding packet in the packet list. + Seleziona il pacchetto corrispondente nell'elenco dei pacchetti. + + + G + G + + + Next Problem Packet + Pacchetto problematico successivo + + + Go to the next problem packet + Vai al successivo pacchetto problematico + + + N + N + + + Prepare &Filter + Prepara &filtro + + + Prepare a filter matching the selected stream(s). + Prepara un filtro che corrisponde ai flussi selezionati. + + + &Current Tab + S&cheda attuale + + + Prepare a filter matching current tab. + Prepara un filtro che corrisponde alla scheda attuale. + + + &All Tabs + Tutte le s&chede + + + Prepare a filter matching all tabs. + Prepara un filtro che corrisponde a tutte le schede. + + + RTP Stream Analysis + Analisi flusso RTP + + + Save Graph As… + Salva grafico come... + + + G: Go to packet, N: Next problem packet + G: Vai al pacchetto, N: Pacchetto problematico successivo + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + Valori separati da virgola (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %1 non supporta PCM in %2. Il formato preferito è %3 + + + + RtpPlayerDialog + + RTP Player + Lettore RTP + + + Play + Riproduci + + + Source Address + Indirizzo sorgente + + + Source Port + Porta sorgente + + + Destination Address + Indirizzo destinazione + + + Destination Port + Porta destinazione + + + SSRC + SSRC + + + Setup Frame + Frame di configurazione + + + Packets + Pacchetti + + + Time Span (s) + Arco temporale (s) + + + Payloads + Payload + + + <small><i>No audio</i></small> + <small><i>Nessun audio</i></small> + + + Start playback of all unmuted streams + Avvia la riproduzione di tutti i flussi non silenziati + + + Pause/unpause playback + Sospendi/Riprendi la riproduzione + + + Stop playback + Ferma la riproduzione + + + Enable/disable skipping of silence during playback + Abilita/Disabilita il salto del silenzio durante la riproduzione + + + Min silence: + Silenzio minimo: + + + Minimum silence duration to skip in seconds + Durata minima del silenzio da saltare in secondi: + + + Output Device: + Dispositivo di uscita: + + + Output Audio Rate: + Velocità dell'uscita audio: + + + Jitter Buffer: + Buffer jitter: + + + The simulated jitter buffer in milliseconds. + Il buffer del jitter simulato in millisecondi. + + + Playback Timing: + Orario della riproduzione: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Buffer jitter</strong>: utilizza buffer jitter per simulare il flusso RTP come viene ascoltato dall'utente finale. +<br/> +<strong>Marca temporale RTP</strong>: utilizza Marca temporale RTP invece dell'ora di arrivo del pacchetto. Ciò non riprodurrà il flusso RTP come lo ha ascoltato l'utente finale, ma è utile quando il traffico RTP è instradato su tunnel e l'orario del pacchetto originale manca. +<br/> +<strong>Modalità senza interruzione</strong>: ignora la marca temporale RTP. Riproduce il flusso quando è completo. Ciò è utile quando manca la marca temporale RTP. + + + Jitter Buffer + Buffer jitter + + + RTP Timestamp + Marca temporale RTP + + + Uninterrupted Mode + Modalità senza interruzione + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>Visualizza le marche temporali come ora del giorno (marcata) o secondi dall'inizio della cattura (non marcata).</p></body></html> + + + Time of Day + Ora del giorno + + + &Export + &Esporta + + + Export audio of all unmuted selected channels or export payload of one channel. + Esporta l'audio di tutti i canali non silenziati selezionati o esporta il payload di un canale. + + + From &cursor + Dal &cursore + + + Save audio data started at the cursor + Salva i dati audio dalla posizione del cursore + + + &Stream Synchronized Audio + Audio &sincronizzato del flusso + + + Save audio data synchronized to start of the earliest stream. + Salva i dati dell'audio sincronizzati all'inizio del primo flusso. + + + &File Synchronized Audio + Audio sincronizzato del &file + + + Save audio data synchronized to start of the capture file. + Salva i dati audio sincronizzati all'inizio del file di cattura. + + + &Payload + &Payload + + + Save RTP payload of selected stream. + Salva il payload RTP del flusso selezionato. + + + Reset Graph + Ripristina grafico + + + Reset the graph to its initial state. + Ripristina il grafico al suo stato iniziale. + + + Go To Setup Packet + Vai alla configurazione del pacchetto + + + Go to setup packet of stream currently under the cursor + Vai alla configurazione del pacchetto del flusso attualmente sotto il cursore + + + Mute + Silenzia + + + Mute selected streams + Silenzia i flussi selezionati + + + Unmute + Rimuovi silenzio + + + Unmute selected streams + Rimuovi il silenzio per i flussi selezionati + + + Invert muting of selected streams + Inverti il silenziamento dei flussi selezionati + + + Route audio to left channel of selected streams + Instrada l'audio dei flussi selezionati al canale sinistro + + + Route audio to left and right channel of selected streams + Instrada l'audio dei flussi selezionati al canale sinistro e destro + + + Route audio to right channel of selected streams + Instrada l'audio dei flussi selezionati al canale destro + + + Remove Streams + Rimuovi flussi + + + Remove selected streams from the list + Rimuovi i flussi selezionati dall'elenco + + + All + Tutto + + + Select all + Seleziona tutto + + + None + Nessuno + + + Clear selection + Cancella la selezione + + + Invert + Inverti + + + Invert selection + Inverti la selezione + + + Play/Pause + Riproduci/Pausa + + + Start playing or pause playing + Avvia o sospendi la riproduzione + + + Stop + Ferma + + + Stop playing + Ferma la riproduzione + + + I&naudible streams + Flussi non udibili + + + Select/Deselect inaudible streams + Seleziona/Deseleziona i flussi non udibili + + + Inaudible streams + Flusso non udibile + + + &Select + &Seleziona + + + Select inaudible streams + Seleziona i flussi non udibili + + + &Deselect + &Deseleziona + + + Deselect inaudible streams + Deseleziona i flussi non udibili + + + Prepare &Filter + Prepara &filtro + + + Prepare a filter matching the selected stream(s). + Prepara un filtro che corrisponde ai flussi selezionati. + + + R&efresh streams + A&ggiorna flussi + + + Read captured packets from capture in progress to player + Leggi i pacchetti catturati dalla cattura in corso sul lettore + + + Zoom In + Ingrandisci + + + SR (Hz) + SR (Hz) + + + Sample rate of codec + Campionamento codificatore + + + PR (Hz) + PR (Hz) + + + Play rate of decoded audio (depends e. g. on selected sound card) + La velocità di riproduzione dell'audio decodificato (dipende ad es. dalla scheda audio selezionata) + + + Zoom Out + Rimpicciolisci + + + Move Left 10 Pixels + Sposta a sinistra di 10 pixel + + + Move Right 10 Pixels + Sposta a destra di 10 pixel + + + Move Left 1 Pixels + Sposta a sinistra di 1 pixel + + + Move Right 1 Pixels + Sposta a destra di 1 pixel + + + Go To Packet Under Cursor + Vai al pacchetto sotto il cursore + + + Go to packet currently under the cursor + Vai al pacchetto attualmente sotto il cursore + + + Play the stream + Riproduci il flusso + + + To Left + A sinistra + + + Left + Right + Sinistra + destra + + + To Right + A destra + + + Invert Muting + Inverti il silenziamento + + + No devices available + Nessun dispositivo disponibile + + + Select + Seleziona + + + Audio Routing + Instradamento audio + + + &Play Streams + Ri&produci flussi + + + Open RTP player dialog + Apri finestra del lettore RTP + + + &Set playlist + Imposta &scaletta + + + Replace existing playlist in RTP Player with new one + Sostituisci la scaletta esistente nel lettore RTP con una nuova + + + &Add to playlist + &Aggiungi a scaletta + + + Add new set to existing playlist in RTP Player + Aggiungi nuovo insieme alla scaletta nel lettore RTP + + + &Remove from playlist + &Rimuovi dalla scaletta + + + Remove selected streams from playlist in RTP Player + Rimuovi i flussi selezionati dalla scaletta nel lettore RTP + + + No Audio + Nessun audio + + + Decoding streams... + Decodifica dei flussi... + + + Out of Sequence + Fuori sequenza + + + Jitter Drops + Scarti jitter + + + Wrong Timestamps + Marche temporali errate + + + Inserted Silence + Silenzio inserito + + + Double click on cell to change audio routing + Doppio clic sulla cella per cambiare l'instradamento dell'audio + + + %1 streams + %1 flussi + + + , %1 selected + , %1 selezionati + + + , %1 not muted + , %1 non silenziati + + + , start: %1. Double click on graph to set start of playback. + , avvio: %1. Doppio clic sul grafico per impostare l'avvio della riproduzione. + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + , avvio: %1, cursore: %2. Premi "G" per portarti sul pacchetto %3. Doppio clic sul grafico per impostare l'avvio della riproduzione. + + + Playback of stream %1 failed! + Riproduzione del flusso %1 non riuscita! + + + Automatic + Automatico + + + WAV (*.wav) + WAV (*.wav) + + + Sun Audio (*.au) + Sun Audio (*.au) + + + Save audio + Salva audio + + + Raw (*.raw) + Raw (*.raw) + + + Save payload + Salva payload + + + Warning + Avviso + + + No stream selected or none of selected streams provide audio + Nessun flusso selezionato o nessuno dei flussi selezionati fornisce audio + + + Error + Errore + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + Tutti i flussi selezionati devono utilizzare la stessa velocità di riproduzione. L'impostazione manuale di velocità dell'uscita audio potrebbe aiutare. + + + No streams are suitable for save + Non ci sono flussi pronti per il salvataggio + + + Save failed! + Salvataggio non riuscito + + + Can't write header of AU file + Impossibile scrivere l'intestazione del file AU + + + Can't write header of WAV file + Impossibile scrivere l'intestazione del file WAV + + + Payload save works with just one audio stream. + Il salvataggio del payload funziona con un solo flusso audio. + + + Double click to change audio routing + Doppio clic per cambiare l'instradamento dell'audio + + + Preparing to play... + Preparazione della riproduzione… + + + Unknown + Sconosciuto + + + + RtpStreamDialog + + Dialog + Finestra + + + Source Address + Indirizzo sorgente + + + Source Port + Porta sorgente + + + Destination Address + Indirizzo destinazione + + + Destination Port + Porta destinazione + + + SSRC + SSRC + + + Start Time + Ora di inizio + + + Duration + Durata + + + Payload + Payload + + + Packets + Pacchetti + + + Lost + Persi + + + Max Delta (ms) + Delta massimo (ms) + + + Max Jitter + Jitter massimo + + + Mean Jitter + Jitter medio + + + Status + Stato + + + <small><i>A hint.</i></small> + <small><i>Un suggerimento.</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Mostra solo le conversazioni che corrispondono al filtro di visualizzazione attuale</p></body></html> + + + Limit to display filter + Limita al filtro di visualizzazione + + + Time of Day + Ora del giorno + + + Find &Reverse + Trova inve&rso + + + Prepare &Filter + Prepara &filtro + + + &Export + &Esporta + + + &Analyze + &Analizza + + + Open the analysis window for the selected stream(s) and add it to it + Apri la finestra di analisi per i flussi selezionati e aggiungili ad essa + + + Find the reverse stream matching the selected forward stream. + Trova il flusso inverso che corrisponde al flusso diretto selezionato. + + + Min Delta (ms) + Delta minimo (ms) + + + Mean Delta (ms) + Delta medio (ms) + + + Min Jitter + Jitter minimo + + + All forward/reverse stream actions + Tutte le azioni del flusso diretto/inverso + + + R + R + + + Find All &Pairs + Trova tutte le co&ppie + + + Select all streams which are paired in forward/reverse relation + Seleziona tutti i flussi accoppiati in relazione diretto/inverso + + + Shift+R + Maiusc+R + + + Find Only &Singles + Trova &solo i singoli + + + Find all streams which don't have paired reverse stream + Trova tutti i flussi che non hanno un flusso inverso associato + + + Ctrl+R + Ctrl+R + + + Mark Packets + Marca pacchetti + + + Mark the packets of the selected stream(s). + Marca i pacchetti dei flussi selezionati. + + + M + M + + + All + Tutto + + + Select all + Seleziona tutto + + + None + Nessuno + + + Clear selection + Cancella la selezione + + + Invert + Inverti + + + Invert selection + Inverti la selezione + + + Go To Setup + Vai alla configurazione + + + Go to the setup packet for this stream. + Vai al pacchetto di configurazione per questo flusso. + + + G + G + + + Prepare a filter matching the selected stream(s). + Prepara un filtro che corrisponde ai flussi selezionati. + + + P + P + + + Export the stream payload as rtpdump + Esporta il payload del flusso come rtpdump + + + E + E + + + A + A + + + Cop&y + Cop&ia + + + Open copy menu + Apri menu di copia + + + Copy as CSV + Copia come CSV + + + Copy stream list as CSV. + Copia l'elenco dei flussi come CSV. + + + Copy as YAML + Copia come YAML + + + Copy stream list as YAML. + Copia l'elenco dei flussi come YAML. + + + RTP Streams + Flussi RTP + + + Select + Seleziona + + + as CSV + come CSV + + + as YAML + come YAML + + + %1 streams + flussi %1 + + + , %1 selected, %2 total packets + , selezionati %1, pacchetti totali %2 + + + Save RTPDump As… + Salva RTPDump come... + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark - Associazioni SCTP + + + ID + ID + + + Port 1 + Porta 1 + + + Port 2 + Porta 2 + + + Number of Packets + Numero di pacchetti + + + Number of DATA Chunks + Numero dei frammenti di dati + + + Number of Bytes + Numero di byte + + + Filter Selected Association + Filtra l'associazione selezionata + + + Analyze + Analizza + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark - Analizza l'associazione + + + TabWidget + TabWidget + + + Statistics + Statistiche + + + Chunk Statistics + Statistiche sui blocchi + + + Filter Association + Associazione del filtro + + + Number of Data Chunks from EP2 to EP1: + Numero dei blocchi dati da EP2 a EP1: + + + Checksum Type: + Tipo di checksum: + + + Number of Data Chunks from EP1 to EP2: + Numero di blocchi dati da EP1 a EP2: + + + Number of Data Bytes from EP1 to EP2: + Numero di byte di dati da EP1 a EP2: + + + Number of Data Bytes from EP2 to EP1: + Numero di byte di dati da EP2 a EP1: + + + Endpoint 1 + Terminatore 1 + + + Graph TSN + Grafico TSN + + + Graph Bytes + Grafico Byte + + + Requested Number of Inbound Streams: + Numero di flussi in ingresso richiesti: + + + Port: + Porta: + + + Sent Verification Tag: + Tag di verifica inviato: + + + Minimum Number of Inbound Streams: + Numero minimo di flussi in ingresso: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + <small><i>Per un'analisi completa, seleziona la preferenza SCTP Abilita indicizzazione associazione</i></small> + + + Complete List of IP addresses from INIT Chunk: + Elenco completo degli indirizzi IP dal blocco INIT: + + + Minimum Number of Outbound Streams: + Numero minimo di flussi in uscita: + + + Graph Arwnd + Grafico Arwnd + + + Endpoint 2 + Terminatore 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + Elenco completo degli indirizzi IP dal blocco INIT_ACK: + + + Provided Number of Outbound Streams: + Numero fornito di flussi in uscito: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + Associazione dell'analisi SCTP: %1 Port1 %2 Port2 %3 + + + No Association found for this packet. + Nessuna associazione trovata per questo pacchetto. + + + Warning + Avviso + + + Could not find SCTP Association with id: %1 + Impossibile trovare l'associazione SCTP con id: %1 + + + Complete list of IP addresses from INIT Chunk: + Elenco completo degli indirizzi IP dal blocco INIT: + + + Complete list of IP addresses from INIT_ACK Chunk: + Elenco completo degli indirizzi IP dal blocco INIT_ACK: + + + List of Used IP Addresses + Elenco degli indirizzi IP utilizzati + + + Used Number of Inbound Streams: + Numero di flussi in ingresso utilizzati: + + + Used Number of Outbound Streams: + Numero di flussi in uscita utilizzati: + + + + SCTPChunkStatisticsDialog + + Dialog + Finestra + + + Association + Associazione + + + Endpoint 1 + Terminatore 1 + + + Endpoint 2 + Terminatore 2 + + + Save Chunk Type Order + Salva l'ordine del tipo di blocco + + + Hide Chunk Type + Nascondi il tipo di blocco + + + Remove the chunk type from the table + Rimuovi il tipo di blocco dalla tabella + + + Chunk Type Preferences + Configurazione del tipo di blocco + + + Go to the chunk type preferences dialog to show or hide other chunk types + Vai alla finestra della configurazione del tipo di blocco per mostrare o nascondere gli altri tipi di blocco + + + Show All Registered Chunk Types + Mostra tutti i tipi di blocco registrati + + + Show all chunk types with defined names + Mostra tutti i tipi di blocchi con nomi definiti + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + Statistiche sui blocchi SCTP: %1 Porta1 %2 Porta2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + Grafico SCTP + + + Reset to full size + Reimposta la dimensione massima + + + Save Graph + Salva il grafico + + + goToPacket + vaiAlPacchetto + + + Go to Packet + Vai al pacchetto + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + Dati SCTP e Finestra degli annunci ricevuti nel tempo: %1 Porta1 %2 Porta2 %3 + + + No Data Chunks sent + Nessun blocco dati inviato + + + Arwnd + Arwnd + + + time [secs] + tempo [sec] + + + Advertised Receiver Window [Bytes] + Finestra del ricevitore annunciata [Byte] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>Grafico %1: a_rwnd=%2 Tempo=%3 secondi </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + Grafico SCTP + + + Reset to full size + Reimposta la dimensione massima + + + Save Graph + Salva grafico + + + goToPacket + vaiAlPacchetto + + + Go to Packet + Vai al pacchetto + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + Dati SCTP e Finestra degli annunci ricevuti nel tempo: %1 Porta1 %2 Porta2 %3 + + + No Data Chunks sent + Nessun blocco dati inviato + + + Bytes + Byte + + + time [secs] + tempo [sec] + + + Received Bytes + Byte ricevuti + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>Grafico %1: Byte ricevuti=%2 Tempo=%3 secondi </i></small> + + + + SCTPGraphDialog + + SCTP Graph + Grafico SCTP + + + Relative TSNs + TSN relativi + + + Only SACKs + Solo SACK + + + Only TSNs + Solo TSN + + + Show both + Mostra entrambi + + + Reset to full size + Ripristina la dimensione massima + + + Save Graph + Salva grafico + + + goToPacket + vaiAlPacchetto + + + Go to Packet + Vai al pacchetto + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + SCTP TSN e SACK nel tempo: %1 Porta1 %2 Porta2 %3 + + + No Data Chunks sent + Nessun blocco dati inviato + + + CumTSNAck + CumTSNAck + + + Gap Ack + Gap Ack + + + NR Gap Ack + NR Gap Ack + + + Duplicate Ack + Ack duplicato + + + TSN + TSN + + + time [secs] + tempo [sec] + + + TSNs + TSN + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 Tempo: %3 secondi </i></small> + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Salva il grafico come... + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <small><i>Seleziona un comando e digita un filtro se vuoi, poi premi Applica.</i></small> + + + Command: + Comando: + + + SCSI Service Response Times + Tempi di risposta del servizio SCSI + + + + SearchFrame + + Frame + Frame + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>Cerca nella colonna delle informazioni dell'elenco dei pacchetti (pannello di riepilogo), le etichette dei pacchetti visualizzati (pannello con la vista ad albero) o i dati del pacchetto convertiti in ASCII (pannello con la vista in esadecimale).</p></body></html> + + + Packet list + Elenco dei pacchetti + + + Packet details + Dettagli del pacchetto + + + Packet bytes + Byte del pacchetto + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Cerca le stringhe che contengono i caratteri ridotti (UTF-8 e ASCII) o allargati (UTF-16).</p></body></html> + + + Narrow & Wide + Ridotti o allargati + + + Narrow (UTF-8 / ASCII) + Ridotti (UTF-8 / ASCII) + + + Wide (UTF-16) + Allargati (UTF-16) + + + Case sensitive + Distingui maiuscole + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>Cerca nei dati utilizzando la sintassi dei filtri di visualizzazione (ad es. ip.addr==10.1.1.1), una stringa esadecimale (ad es. fffffda5) o una stringa semplice (ad es. Mia Stringa) o un'espressione regolare (ad es. colou?r).</p></body></html> + + + Display filter + Filtro di visualizzazione + + + Hex value + Valore esadecimale + + + String + Stringa + + + Regular Expression + Espressione regolare + + + Find + Trova + + + Cancel + Annulla + + + No valid search type selected. Please report this to the development team. + Nessun tipo di ricerca valida selezionata. Segnala il problema alla squadra di sviluppo. + + + Invalid filter. + Filtro non valido. + + + That filter doesn't test anything. + Il filtro non verifica niente. + + + That's not a valid hex string. + Questa non è una stringa esadecimale valida. + + + You didn't specify any text for which to search. + Non hai specificato alcun testo da ricercare. + + + No valid character set selected. Please report this to the development team. + Nessun gruppo di caratteri valido selezionato. Segnala il problema alla squadra di sviluppo. + + + No valid search area selected. Please report this to the development team. + Nessuna area di ricerca valida selezionata. Segnala il problema alla squadra di sviluppo. + + + Searching for %1… + Ricerca di %1... + + + No packet contained those bytes. + Nessun pacchetto contiene questi byte. + + + No packet contained that string in its Info column. + Nessun pacchetto contiene questa stringa nella colonna delle informazioni. + + + No packet contained that string in its dissected display. + Nessun pacchetto contiene questa stringa nella sua visualizzazione decodificata. + + + No packet contained that string in its converted data. + Nessun pacchetto contiene questa stringa nei suoi dati convertiti. + + + No packet matched that filter. + Nessun pacchetto corrisponde a questo filtro. + + + + SequenceDialog + + Call Flow + Flusso della chiamata + + + Time + Tempo + + + Comment + Commento + + + No data + Nessun dato + + + %Ln node(s) + + %Ln nodo + %Ln nodi + + + + %Ln item(s) + + %Ln elemento + %Ln elementi + + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII (*.txt) + + + Save Graph As… + Salva il grafico come... + + + Flow + Flusso + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Scorciatoie per la tastiera di valore e meravigliose per risparmiare tempo</h3> +<table><tbody> + +<tr><th>+</th><td>Ingrandisci</td></th> +<tr><th>-</th><td>Rimpicciolisci</td></th> +<tr><th>0</th><td>Ripristina il grafico al suo stato iniziale</td></th> + +<tr><th>→</th><td>Sposta a destra 10 pixel</td></th> +<tr><th>←</th><td>Sposta a sinistra 10 pixel</td></th> +<tr><th>↑</th><td>Sposta in su 10 pixel</td></th> +<tr><th>↓</th><td>Sposta in giù 10 pixel</td></th> +<tr><th><i>Shift+</i>→</th><td>Sposta a destra 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Sposta a sinistra 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Sposta in su 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Muovi in giù 1 pixel</td></th> + +<tr><th>g</th><td>Vai al pacchetto sotto il cursore</td></th> +<tr><th>n</th><td>Vai al pacchetto successivo</td></th> +<tr><th>p</th><td>Vai al pacchetto precedente</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>Un suggerimento</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>Mostra solo i flussi che corrispondono al filtro di visualizzazione attuale</p></body></html> + + + Limit to display filter + Limita al filtro di visualizzazione + + + Flow type: + Tipo di flusso: + + + Addresses: + Indirizzi: + + + Any + Tutti + + + Network + Rete + + + Reset Diagram + Ripristina diagramma + + + Reset &Diagram + Ripristina &diagramma + + + Reset the diagram to its initial state. + Ripristina il diagramma al suo stato iniziale. + + + 0 + 0 + + + &Reset Diagram + &Ripristina diagramma + + + Reset the diagram to its initial state + Ripristina il diagramma al suo stato iniziale + + + &Export + &Esporta + + + Export diagram + Esporta diagramma + + + Zoom In + Ingrandisci + + + + + + + + + Zoom Out + Rimpicciolisci + + + - + - + + + Move Up 10 Pixels + Sposta in su di 10 pixel + + + Up + Su + + + Move Left 10 Pixels + Sposta a sinistra di 10 pixel + + + Left + Sinistra + + + Move Right 10 Pixels + Sposta a destra di 10 pixel + + + Right + Destra + + + Move Down 10 Pixels + Sposta in giù di 10 pixel + + + Down + Giù + + + Move Up 1 Pixel + Sposta in su di 1 pixel + + + Shift+Up + Shift+Su + + + Move Left 1 Pixel + Sposta a sinistra di 1 pixel + + + Shift+Left + Shift+Sinistra + + + Move Right 1 Pixel + Sposta a destra di 1 pixel + + + Shift+Right + Shift+Destra + + + Move Down 1 Pixel + Sposta in giù di 1 pixel + + + Shift+Down + Shift+Giù + + + Go To Packet Under Cursor + Vai al pacchetto sotto il cursore + + + Go to packet currently under the cursor + Vai al pacchetto attualmente sotto il cursore + + + G + G + + + All Flows + Tutti i flussi + + + Show flows for all packets + Mostra i flussi per tutti i pacchetti + + + 1 + 1 + + + TCP Flows + Flussi TCP + + + Show only TCP flow information + Mostra solo le informazioni sui flussi TCP + + + Go To Next Packet + Vai al pacchetto successivo + + + Go to the next packet + Vai al pacchetto successivo + + + N + N + + + Go To Previous Packet + Vai al pacchetto precedente + + + Go to the previous packet + Vai al pacchetto precedente + + + P + P + + + Select RTP Stream + Seleziona flusso RTP + + + Select RTP stream in RTP Streams dialog + Seleziona flusso RTP nella finestra Flussi RTP + + + S + S + + + Deselect RTP Stream + Deseleziona flusso RTP + + + Deselect RTP stream in RTP Streams dialog + Deseleziona flusso RTP nella finestra Flussi RTP + + + D + D + + + + ShortcutListModel + + Shortcut + Scorciatoia + + + Name + Nome + + + Description + Descrizione + + + + ShowPacketBytesDialog + + Show Packet Bytes + Mostra byte del pacchetto + + + Hint. + Suggerimento. + + + Decode as + Decodifica come + + + Show as + Mostra come + + + Start + Inizia + + + End + Finisce + + + Find: + Trova: + + + Find &Next + Trova &successivo + + + Frame %1, %2, %Ln byte(s). + + Frame %1: %2, %Ln byte. + Frame %1: %2, %Ln byte. + + + + None + Nessuno + + + Base64 + Base64 + + + Compressed + Compresso + + + Hex Digits + Cifre esadecimali + + + Percent-Encoding + Percentuale-Codifica + + + Quoted-Printable + Quoted-Printable + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII e Control + + + C Array + C Array + + + EBCDIC + EBCDIC + + + Hex Dump + Hex Dump + + + HTML + HTML + + + Image + Immagine + + + Raw + Grezzo + + + Rust Array + Array Rust + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Stampa + + + Copy + Copia + + + Save as… + Salva come... + + + Save Selected Packet Bytes As… + Salva byte del pacchetto selezionato come... + + + Displaying %Ln byte(s). + + Visualizzazione di %Ln byte. + Visualizzazione di %Ln byte. + + + + JSON + JSON + + + Regex Find: + Trova espressione regolare: + + + + ShowPacketBytesTextEdit + + Show Selected + Mostra selezionati + + + Show All + Mostra tutto + + + + SplashOverlay + + Initializing dissectors + Inizializzazione dei decodificatori in corso + + + Initializing tap listeners + Inizializzazione dei listener tap in corso + + + Initializing external capture plugins + Inizializzazione estensioni di cattura esterne + + + Registering dissectors + Registrazione dei decodificatori + + + Registering plugins + Registering dissector + Registrazione dei plugin in corso + + + Handing off dissectors + Trasferimento dei decodificatori in corso + + + Handing off plugins + Trasferimento dei plugin in corso + + + Loading Lua plugins + Caricamento dei plugin Lua in corso + + + Removing Lua plugins + Rimozione dei plugin Lua + + + Loading module preferences + Caricamento delle configurazioni dei moduli in corso + + + Finding local interfaces + Ricerca interfacce locali + + + Applying changed preferences + Applicazione delle preferenze modificate + + + (Unknown action) + (Azione sconosciuta) + + + + StatsTreeDialog + + Configuration not found + Configurazione non trovata + + + Unable to find configuration for %1. + Impossibile trovare la configurazione per %1. + + + + StripHeadersDialog + + Dialog + Finestra + + + Display filter: + Filtro di visualizzazione: + + + + SupportedProtocolsDialog + + Dialog + Finestra + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Cerca nell'elenco dei nomi di campo.</p></body></html> + + + Search: + Cerca: + + + <small><i>Gathering protocol information…</i></small> + <small><i>Raccolta delle informazioni di protocollo in corso...</i></small> + + + Supported Protocols + Protocolli supportati + + + %1 protocols, %2 fields. + %1 protocolli, %2 campi. + + + + SupportedProtocolsModel + + Name + Nome + + + Filter + Filtro + + + Type + Tipo + + + Description + Descrizione + + + + SyntaxLineEdit + + Invalid filter: %1 + Filtro non valido: %1 + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + "%1" è sconsigliato in favore di "%2". Vedi la sezione 6.4.8 della guida per i dettagli. + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + Finestra + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Scorciatoie di tastiera preziose e che risparmiano tempo</h3> +<table><tbody> + +<tr><th>+</th><td>Ingrandisci</td></th> +<tr><th>-</th><td>Rimpicciolisci</td></th> +<tr><th>x</th><td>Ingrandisci asse X</td></th> +<tr><th>X</th><td>Rimpicciolisci asse X</td></th> +<tr><th>y</th><td>Ingrandisci asse Y</td></th> +<tr><th>Y</th><td>Rimpicciolisci asse Y</td></th> +<tr><th>0</th><td>Reimposta il grafico al suo stato iniziale</td></th> + +<tr><th>→</th><td>Sposta a destra di 10 pixel</td></th> +<tr><th>←</th><td>Sposta a sinistra di 10 pixel</td></th> +<tr><th>↑</th><td>Sposta in su di 10 pixel</td></th> +<tr><th>↓</th><td>Sposta in giù di 10 pixel</td></th> +<tr><th><i>Maiusc+</i>→</th><td>Sposta a destra di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>←</th><td>Sposta a sinistra di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>↑</th><td>Sposta in su di 1 pixel</td></th> +<tr><th><i>Maiusc+</i>↓</th><td>Sposta in giù di 1 pixel</td></th> + +<tr><th>g</th><td>Vai al pacchetto sotto il cursore</td></th> + +<tr><th>z</th><td>Inverti il trascinamento / ingrandimento del mouse</td></th> +<tr><th>t</th><td>Commuta l'origine dell'orario della cattura / sessione</td></th> +<tr><th>Spazio</th><td>Commuta il reticolo</td></th> + +<tr><th>1</th><td>Grafico Round Trip Time</td></th> +<tr><th>2</th><td>Grafico della capacità trasmissiva</td></th> <tr><th>3</th><td>Grafico in stile Stevens di Tempo / Sequenza</td></th> +<tr><th>4</th><td>Grafico in stile tcptrace di Tempo / Sequenza</td></th> +<tr><th>5</th><td>Grafico del ridimensionamento della finestra</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>Passa il mouse sopra per le scorciatoie</i></small> + + + Type + Tipo + + + MA Window (s) + Finestra MA (s) + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + Consenti la selezione dei segmenti SACK così come dei pacchetti di dati tramite clic sul grafico + + + Select SACKs + select SACKs + Seleziona SACK + + + Stream + Flusso + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Inverti la direzione della connessione (guarda il flusso opposto).</p></body></html> + + + Switch Direction + Inverti direzione + + + Mouse + Mouse + + + Drag using the mouse button. + Trascina usando il pulsante del mouse. + + + drags + trascinamenti + + + Select using the mouse button. + Seleziona usando il pulsante del mouse. + + + zooms + zoom + + + Display Round Trip Time vs Sequence Number + Visualizza Round Trip Time e numero di sequenza + + + RTT By Sequence Number + RTT per numero di sequenza + + + Display graph of Segment Length vs Time + Visualizza grafico Lunghezza segmento e Tempo + + + Segment Length + Lunghezza del segmento + + + Display graph of Mean Transmitted Bytes vs Time + Visualizza grafico Media byte trasmessi e Tempo + + + Display graph of Mean ACKed Bytes vs Time + Visualizza grafico Media byte con ACK e Tempo + + + Goodput + Carico utile trasferito + + + Display graph of Receive Window Size vs Time + Visualizza grafico Finestra di ricezione e Tempo + + + Rcv Win + Fin RCV + + + Display graph of Outstanding Bytes vs Time + Visualizza grafico Outstanding byte e Tempo + + + Bytes Out + Out Byte + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Reimposta il grafico al suo stato iniziale.</p></body></html> + + + Reset + Ripristina + + + Reset Graph + Ripristina grafico + + + Reset the graph to its initial state. + Ripristina il grafico al suo stato iniziale. + + + 0 + 0 + + + Zoom In + Ingrandisci + + + + + + + + + Zoom Out + Rimpicciolisci + + + - + - + + + Move Up 10 Pixels + Sposta in su di 10 pixel + + + Up + Su + + + Move Left 10 Pixels + Sposta a sinistra di 10 pixel + + + Left + Sinistra + + + Move Right 10 Pixels + Sposta a destra di 10 pixel + + + Right + Destra + + + Move Down 10 Pixels + Sposta in giù di 10 pixel + + + Down + Giù + + + Move Up 1 Pixel + Sposta in su di 1 pixel + + + Shift+Up + Maiusc+Su + + + Move Left 1 Pixel + Sposta a sinistra di 1 pixel + + + Shift+Left + Maiusc+Sinistra + + + Move Right 1 Pixel + Sposta a destra di 1 pixel + + + Shift+Right + Maiusc+Destra + + + Move Down 1 Pixel + Sposta in giù di 1 pixel + + + Shift+Down + Maiusc+Giù + + + Next Stream + Flusso successivo + + + Go to the next stream in the capture + Vai al prossimo flusso nella cattura + + + PgUp + PgSu + + + Previous Stream + Flusso precedente + + + Go to the previous stream in the capture + Vai al flusso precedente nella cattura + + + PgDown + PgGiù + + + Switch direction (swap TCP endpoints) + Inverti direzione (scambia i terminatori TCP) + + + D + D + + + Go To Packet Under Cursor + Vai al pacchetto sotto il cursore + + + Go to packet currently under the cursor + Vai al pacchetto attualmente sotto il cursore + + + G + G + + + Drag / Zoom + Trascina/Zoom + + + Toggle mouse drag / zoom behavior + Inverti il comportamento di trascina/zoom del mouse + + + Z + Z + + + Relative / Absolute Sequence Numbers + Numeri di sequenza relativi/assoluti + + + Toggle relative / absolute sequence numbers + Inverti i numeri di sequenza relativi/assoluti + + + S + S + + + Capture / Session Time Origin + Orario di origine della cattura/sessione + + + Toggle capture / session time origin + Inverti il tempo di origine della cattura/sessione + + + T + T + + + Crosshairs + Reticolo + + + Toggle crosshairs + Inverti reticolo + + + Space + Spazio + + + Round Trip Time + Round Trip Time + + + Switch to the Round Trip Time graph + Passa al grafico Round Trip Time + + + 1 + 1 + + + Throughput + Capacità trasmissiva + + + Switch to the Throughput graph + Passa al grafico della capacità trasmissiva + + + 2 + 2 + + + Time / Sequence (Stevens) + Tempo / Sequenza (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + Passa allo stile Stevens per il grafico Tempo/Sequenza + + + 3 + 3 + + + Window Scaling + Ridimensionamento finestra + + + Switch to the Window Scaling graph + Passa al grafico del ridimensionamento finestra + + + 5 + 5 + + + Time / Sequence (tcptrace) + Tempo / Sequenza (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + Passa allo stile tcptrace per il grafico Tempo/Sequenza + + + 4 + 4 + + + Zoom In X Axis + Ingrandisci asse X + + + X + X + + + Zoom Out X Axis + Rimpicciolisci asse X + + + Shift+X + Maiusc+X + + + Zoom In Y Axis + Ingrandisci asse Y + + + Y + Y + + + Zoom Out Y Axis + Rimpicciolisci asse Y + + + Shift+Y + Maiusc+Y + + + Save As… + Salva come... + + + No Capture Data + Non ci sono dati di cattura + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 %2 pacchetti, %3 %4 %5 pacchetti, %6 + + + Sequence Numbers (Stevens) + Numeri di sequenza (Stevens) + + + Sequence Numbers (tcptrace) + Numeri di sequenza (tcptrace) + + + (MA) + (MA) + + + (%1 Segment MA) + (%1 segmento MA) + + + [not enough data] + [non ci sono abbastanza dati] + + + for %1:%2 %3 %4:%5 + per %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + Click to select packet + Fai clic per selezionare il pacchetto + + + Packet + Pacchetto + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Rilascia per lo zoom, x = %1 a %2, y = %3 a %4 + + + Unable to select range. + Impossibile selezionare l'intervallo. + + + Click to select a portion of the graph. + Fai clic per selezionare una porzione del grafico. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Salva il grafico come... + + + + TLSKeylogDialog + + Dialog + Finestra + + + Browse… + Sfoglia... + + + Command line + Riga di commando + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + Esegui un'applicazione con la variabile d'ambiente SSLKEYLOGFILE impostata sul file specificato dalla preferenza del nome file di log della chiave TLS. Ciò consente la decifratura TLS in Wireshark. Imposta il file di registro della chiave e avvia la cattura prima di avviare l'applicazione per garantire che le negoziazioni TLS iniziali vengano catturate. + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + <span style=" font-size:small;">È risaputo che Firefox e Chrome funzionino. Se il browser desiderato è attualmente in esecuzione, chiudilo prima di avviarlo di seguito. Sono supportate le opzioni della riga di comando.</span> + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + Percorso del file di log TLS (Pre)-Master-Secret (tls.keylog_file) + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + <span style=" font-size:small;">I segreti della sessione TLS saranno registrati in questo file. Se modifichi questo campo, premi il pulsante Salva per aggiornare le preferenze del protocollo TLS.</span> + + + Launch application with SSLKEYLOGFILE + Avvia applicazione con SSLKEYLOGFILE + + + Launch + Avvia + + + Save + Salva + + + TLS Keylog file + File Keylog TLS + + + Program to start with SSLKEYLOGFILE + Programma da avviare con SSLKEYLOGFILE + + + + TapParameterDialog + + Dialog + Finestra + + + Item + Elemento + + + <small><i>A hint.</i></small> + <small><i>Un suggerimento.</i></small> + + + Display filter: + Filtro di visualizzazione: + + + Regenerate statistics using this display filter + Rigenera le statistiche usando questo filtro di visualizzazione + + + Apply + Applica + + + Copy + Copia + + + Copy a text representation of the tree to the clipboard + Copia una rappresentazione testuale dell'albero negli appunti + + + Save as… + Save as... + Salva come... + + + Save the displayed data in various formats + Salva i dati visualizzati in vari formati + + + Collapse All + Contrai tutti + + + Expand All + Espandi tutti + + + Save Statistics As… + Salva statistiche come... + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + File di testo (*.txt);;Valori separati da virgole (*.csv);;Documento XML (*.xml);;Documento YAML (*.yaml) + + + Plain text file (*.txt) + File di testo (*.txt) + + + Error saving file %1 + Errore durante il salvataggio del file %1 + + + + TimeShiftDialog + + Shift all packets by + Sposta tutti i pacchetti di + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + Imposta l'ora per il pacchetto + + + to + a + + + …then set packet + ...then set packet + ... poi imposta il pacchetto + + + and extrapolate the time for all other packets + ed estrai l'ora di tutti gli altri pacchetti + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + Annulla tutte le modifiche + + + Time Shift + Spostamento temporale + + + Frame numbers must be between 1 and %1. + I numeri dei frame devono essere tra 1 e %1. + + + Invalid frame number. + Numero di frame non valido. + + + Time shifting is not available while capturing packets. + Il spostamento temporale non è disponibile durante l'acquisizione dei pacchetti. + + + + TrafficTab + + Map file error + Errore nel file di mappa + + + Could not open base file %1 for reading: %2 + Impossibile aprire il file di base %1 in lettura: %2 + + + No endpoints available to map + Nessun terminatore disponibile per la mappa + + + Unable to create temporary file + Impossibile creare il file temporaneo + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>Mostra gli indirizzi risolti e i nomi delle porte invece dei valori numerici. La corrispondente configurazione per la risoluzione dei nomi deve essere abilitata.</p></body></html> + + + Name resolution + Risoluzione dei nomi + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Mostra solo le conversazioni che corrispondono al filtro di visualizzazione attuale</p></body></html> + + + Limit to display filter + Limita al filtro di visualizzazione + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + <html><head/><body><p>Mostra solo i tipi che corrispondono al valore del filtro</p></body></html> + + + Filter list for specific type + Elenco di filtri per specifico tipo + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>Mostra gli orari assoluti nella colonna ora iniziale.</p></body></html> + + + GroupBox + CasellaGruppo + + + Absolute start time + Ora iniziale assoluta + + + Copy + Copia + + + Unknown + Sconosciuto + + + + TrafficTree + + Resize all columns to content + Ridimensiona tutte le colonne al contenuto + + + Filter on stream id + Filtro su ID flusso + + + Copy %1 table + Copia la tabella %1 + + + as CSV + come CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + Copia tutti i valori di questa pagina negli appunti nel formato CSV (valori separati da virgole). + + + as YAML + come YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + Copia tutti i valori di questa pagina negli appunti nel formato di serializzazione dati YAML. + + + as JSON + come JSON + + + Copy all values of this page to the clipboard in the JSON data serialization format. + Copia tutti i valori di questa pagina negli appunti nel formato di serializzazione dati JSON. + + + Save data as raw + Salva come dati grezzi + + + Disable data formatting for export/clipboard and save as raw data + Disabilita la formattazione dei dati per l'esportazione/appunti e salva come dati grezzi + + + + TrafficTreeHeaderView + + Less than + Minore di + + + Greater than + Maggiore di + + + Equal + Uguale + + + Columns to display + Colonne da visualizzare + + + Filter %1 by + Filtro %1 per + + + Enter filter value + Digita il valore del filtro + + + + TrafficTypesModel + + Protocol + Protocollo + + + + UatDialog + + Create a new entry. + Crea una nuova voce. + + + Remove this entry. + Remove this profile. + Rimuovi questa voce. + + + Copy this entry. + Copy this profile. + Copia questa voce. + + + Move entry up. + Sposta su la voce. + + + Move entry down. + Sposta giù la voce. + + + Clear all entries. + Cancella tutte le voci. + + + Unknown User Accessible Table + Tabella accessibile dall'utente sconosciuta + + + Open + Apri + + + + UatFrame + + Frame + Frame + + + Create a new entry. + Crea una nuova voce. + + + Remove this entry. + Rimuovi questa voce. + + + Copy this entry. + Copia questa voce. + + + Move entry up. + Sposta su la voce. + + + Move entry down. + Sposta giù la voce. + + + Clear all entries. + Cancella tutte le voci. + + + Copy entries from another profile. + Copia voci da un altro profilo. + + + Copy from + Copia da + + + Unknown User Accessible Table + Tabella accessibile dall'utente sconosciuta + + + Open + Apri + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Mostra solo le conversazioni che corrispondono al filtro di visualizzazione attuale</p></body></html> + + + Limit to display filter + Limita al filtro di visualizzazione + + + Time of Day + Ora del giorno + + + Flow &Sequence + &Sequenza del flusso + + + Show flow sequence for selected call(s). + Mostra la sequenza del flusso per le chiamate selezionate. + + + Prepare &Filter + Prepara &filtro + + + Prepare a filter matching the selected calls(s). + Prepara un filtro che corrisponde alle chiamate selezionate. + + + Cop&y + Cop&ia + + + Open copy menu + Apri menu di copia + + + All + Tutto + + + Select all + Seleziona tutto + + + None + Nessuno + + + Invert + Inverti + + + Invert selection + Inverti la selezione + + + Select related RTP streams + Seleziona i flussi RTP relativi + + + Select RTP streams related to selected calls in RTP Streams dialog + Seleziona i flussi RTP relativi alle chiamate selezionate nella finestra Flussi RTP + + + S + S + + + Deselect related RTP Streams + Deseleziona i flussi RTP relativi + + + D + D + + + Clear selection + Cancella la selezione + + + Display time as time of day + Visualizza l'ora come ora del giorno + + + Copy as CSV + Copia come CSV + + + Copy stream list as CSV. + Copia l'elenco dei flussi come CSV. + + + Copy as YAML + Copia come YAML + + + Copy stream list as YAML. + Copia l'elenco dei flussi come YAML. + + + SIP Flows + Flussi SIP + + + VoIP Calls + Chiamate VoIP + + + as CSV + come CSV + + + as YAML + come YAML + + + Select + Seleziona + + + + VoipCallsInfoModel + + On + Acceso + + + Off + Spento + + + Tunneling: %1 Fast Start: %2 + Tunnel: %1 Avvio veloce: %2 + + + Start Time + Ora di inizio + + + Stop Time + Ora di fine + + + Initial Speaker + Oratore iniziale + + + From + Da + + + To + A + + + Protocol + Protocollo + + + Duration + Durata + + + Packets + Pacchetti + + + State + Stato + + + Comments + Commenti + + + + WelcomePage + + Form + Modulo + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Benvenuto in Wireshark</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>Apri un file sul tuo file system</p></body></html> + + + <h2>Open</h2> + <h2>Apri</h2> + + + Recent capture files + File di cattura recenti + + + Capture files that have been opened previously + I file di cattura che sono stati aperti precedentemente + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>Cattura i pacchetti live dalla tua rete.</p></body></html> + + + <h2>Capture</h2> + <h2>Cattura</h2> + + + …using this filter: + ...usando questo filtro: + + + Interface list + Elenco delle interfacce + + + List of available capture interfaces + Elenco delle interfacce di cattura disponibili + + + <h2>Learn</h2> + <h2>Impara</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">Manuale utente</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Domande e risposte</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing list</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donazione</a></th> + +</tr></table> +</body></html> + + + Show in Finder + Mostra in Finder + + + Show in Folder + Mostra nella cartella + + + Welcome to %1 + Benvenuto in %1 + + + All interfaces shown + Tutte le interfacce mostrate + + + %n interface(s) shown, %1 hidden + + %n interfaccia mostrata, %1 nascoste + %n interfacce mostrate, %1 nascoste + + + + You are sniffing the glue that holds the Internet together using Wireshark + Stai annusando la colla che tiene insieme Internet con Wireshark + + + You are running Wireshark + Stai eseguendo Wireshark + + + You receive automatic updates. + Ricevi aggiornamenti automatici. + + + You have disabled automatic updates. + Hai disabilitato gli aggiornamenti automatici. + + + not found + non trovato + + + Copy file path + Copia percorso del file + + + Remove from list + Rimuovi dall'elenco + + + + WirelessFrame + + Frame + Frame + + + Interface + Interfaccia + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>Imposta il canale 802.11.</p></body></html> + + + Channel + Canale + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>Durante la cattura, mostra tutti i frame, quelli che hanno la sequenza di controllo dei frame (FCS), o quelli con una FCS non valida.</p></body></html> + + + FCS Filter + Filtro FCS + + + All Frames + Tutti i fotogrammi + + + Valid Frames + Frame validi + + + Invalid Frames + Frame non validi + + + Wireless controls are not supported in this version of Wireshark. + I controlli wireless non sono supportati in questa versione di Wireshark. + + + External Helper + Helper esterno + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>Mostra le preferenze IEEE 802.11, incluse le chiavi di cifratura.</p></body></html> + + + 802.11 Preferences + Preferenze 802.11 + + + AirPcap Control Panel + Pannello di controllo AirPcap + + + Open the AirPcap Control Panel + Apri il pannello di controllo AirPcap + + + Unable to set channel or offset. + Impossibile impostare il canale o l'offset. + + + Unable to set FCS validation behavior. + Impossibile impostare il comportamento di convalida FCS. + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + Il pacchetto numero %1 non include la marca temporale TSF, non sarà mostrata la linea temporale. + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + Il pacchetto numero %u presenta un grande salto negativo in TSF, che non mostra la sequenza temporale. Forse il punto di riferimento TSF è impostato in modo errato? + + + + WiresharkDialog + + Failed to attach to tap "%1" + Aggancio del tap "%1" non riuscito + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Vai al pacchetto + + + Cancel + Annulla + + + File Set + Gruppo di file + + + Export Packet Dissections + Esporta decodifiche di pacchetti + + + Export Objects + Esporta oggetti + + + &Zoom + &Zoom + + + &Time Display Format + Formato di visualizzazione del &tempo + + + Copy + Copia + + + Manual pages + Manuali + + + Apply as Filter + Applica come filtro + + + Prepare as Filter + Prepara come filtro + + + SCTP + SCTP + + + TCP Stream Graphs + Grafici dei flussi TCP + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &File + + + &Capture + &Cattura + + + &Help + &Aiuto + + + &Go + Va&i + + + &View + &Visualizza + + + &Analyze + &Analizza + + + Follow + Segui + + + &Statistics + &Statistiche + + + 29West + 29West + + + Topics + Argomenti + + + Queues + Code + + + UIM + UIM + + + Telephon&y + Telefon&ia + + + RTSP + RTSP + + + &Edit + &Modifica + + + Packet Comments + Commenti pacchetto + + + Main Toolbar + Barra degli strumenti principale + + + Display Filter Toolbar + Barra degli strumenti dei filtri di visualizzazione + + + Open a capture file + Apri un file di cattura + + + Quit Wireshark + Esci da Wireshark + + + &Start + &Avvia + + + Start capturing packets + Avvia la cattura dei pacchetti + + + S&top + &Ferma + + + Stop capturing packets + Ferma la cattura dei pacchetti + + + No files found + File non trovati + + + &Contents + &Contenuti + + + Wireshark Filter + Filtro di Wireshark + + + TShark + TShark + + + Rawshark + Rawshark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + Text2pcap + + + Website + Sito web + + + Downloads + Download + + + Wiki + Wiki + + + Sample Captures + Catture di esempio + + + &About Wireshark + &Informazioni su Wireshark + + + Ask (Q&&A) + Chiedi (Q&&A) + + + Next Packet + Pacchetto successivo + + + Go to the next packet + Vai al pacchetto successivo + + + Previous Packet + Pacchetto precedente + + + Go to the previous packet + Vai al pacchetto precedente + + + First Packet + Primo pacchetto + + + Go to the first packet + Vai al primo pacchetto + + + Last Packet + Ultimo pacchetto + + + Go to the last packet + Vai all'ultimo pacchetto + + + E&xpand Subtrees + E&spandi sottoalberi + + + Expand the current packet detail + Espandi i dettagli del pacchetto attuale + + + &Expand All + &Espandi tutti + + + Expand packet details + Espandi i dettagli del pacchetto + + + Collapse &All + Contrai &tutti + + + Collapse all packet details + Contrai tutti i dettagli del pacchetto + + + Go to specified packet + Vai al pacchetto specificato + + + Merge one or more files + Unisci uno o più file + + + Import a file + Importa un file + + + &Save + &Salva + + + Save as a different file + Salva come file diverso + + + Export specified packets + Esporta i pacchetti specificati + + + Export TLS Session Keys… + Esporta chiavi di sessione SSL... + + + List Files + Elenca file + + + Next File + File successivo + + + Previous File + File precedente + + + &Reload + &Ricarica + + + Options + Opzioni + + + Capture options + Opzioni di cattura + + + Capture filters + Filtri di cattura + + + Refresh Interfaces + Aggiorna interfacce + + + Refresh interfaces + Aggiorna le interfacce + + + &Restart + &Riavvia + + + Restart current capture + Riavvia la cattura attuale + + + As &CSV… + Come &CSV... + + + As "C" &Arrays… + Come &array "C"... + + + As P&SML XML… + Come P&SML XML... + + + As P&DML XML… + Come P&DML XML... + + + As &JSON… + Come &JSON... + + + Description + Descrizione + + + Field Name + Nome campo + + + Value + Valore + + + As Filter + Come filtro + + + Close this capture file + Chiudi questo file di cattura + + + Packet: + Pacchetto: + + + Interface Toolbars + Barre degli strumenti interfacce + + + Colorize Conversation + Colora conversazione + + + Internals + Interni + + + Additional Toolbars + Barre degli strumenti aggiuntive + + + Conversation Filter + Filtro di conversazione + + + Reliable Server Pooling (RSerPool) + Reliable Server Pooling (RSerPool) + + + SOME/IP + SOME/IP + + + &DTN + &DTN + + + Osmux + Osmux + + + &Tools + Tools + S&trumenti + + + Wireless Toolbar + Barra degli strumenti wireless + + + Help contents + Contenuti della guida + + + FAQs + FAQ + + + Next Packet in Conversation + Pacchetto successivo in conversazione + + + Go to the next packet in this conversation + Vai al pacchetto successivo in questa conversazione + + + Previous Packet in Conversation + Pacchetto precedente in conversazione + + + Go to the previous packet in this conversation + Vai al pacchetto precedente in questa conversazione + + + Next Packet In History + Pacchetto successivo in cronologia + + + Go to the next packet in your selection history + Vai al pacchetto successivo nella cronologia di selezione + + + Previous Packet In History + Pacchetto precedente in cronologia + + + Go to the previous packet in your selection history + Vai al pacchetto precedente nella cronologia di selezione + + + Collapse Subtrees + Contrai sottoalberi + + + Collapse the current packet detail + Contrai i dettagli del pacchetto attuale + + + Go to Packet… + Vai al pacchetto... + + + &Merge… + &Unisci... + + + &Import from Hex Dump… + &Importa da dump esadecimale... + + + Save this capture file + Salva questo file di cattura + + + Save &As… + S&alva come... + + + Export Specified Packets… + Esporta i pacchetti specificati... + + + Export Packet &Bytes… + Esporta &byte del pacchetto... + + + &Print… + Stam&pa... + + + Reload this file + Ricarica questo file + + + Reload as File Format/Capture + Ricarica come file di formato/cattura + + + Copy this item's description + Copia la descrizione di questo elemento + + + Copy this item's field name + Copia il nome del campo di questo elemento + + + Copy this item's value + Copia il valore di questo elemento + + + Copy this item as a display filter + Copia questo elemento come filtro di visualizzazione + + + Apply as Column + Applica come colonna + + + Create a packet list column from the selected field. + Crea una colonna dell'elenco dei pacchetti dal campo selezionato. + + + Find a packet + Trova un pacchetto + + + Find the next packet + Vai al pacchetto successivo + + + Find the previous packet + Vai al pacchetto precedente + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + &Marca/Deseleziona pacchetto + + + Mark All Displayed + Marca tutti i visualizzati + + + Mark all displayed packets + Marca tutti i pacchetti visualizzati + + + Unmark all displayed packets + Rimuovi la selezione da tutti i pacchetti visualizzati + + + Next Mark + Marchio successivo + + + Go to the next marked packet + Vai al prossimo pacchetto marcato + + + Previous Mark + Marchio precedente + + + Go to the previous marked packet + Val al prossimo pacchetto marchiato + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + &Ignora/Considera pacchetto + + + Ignore All Displayed + Ignora tutti i visualizzati + + + Ignore all displayed packets + Ignora tutti i pacchetti visualizzati + + + Set/Unset Time Reference + Imposta/Rimuovi il riferimento temporale + + + Set or unset a time reference for this packet + Imposta o rimuovi il riferimento temporale per questo pacchetto + + + Unset All Time References + Rimuovi tutti i riferimenti temporali + + + Remove all time references + Elimina tutti i riferimenti temporali + + + Next Time Reference + Riferimento temporale successivo + + + Go to the next time reference + Vai al riferimento temporale successivo + + + Previous Time Reference + Riferimento temporale precedente + + + Go to the previous time reference + Vai al riferimento temporale precedente + + + Shift or change packet timestamps + Scorri o cambia la marca temporale del pacchetto + + + Delete All Packet Comments + Elimina tutti i commenti dei pacchetti + + + Remove all packet comments in the capture file + Rimuovi tutti i commenti dei pacchetti nel file di cattura + + + &Configuration Profiles… + Profili di &configurazione... + + + Configuration profiles + Profili di configurazione + + + Manage your configuration profiles + Gestisci i tuoi profili di configurazione + + + Manage Wireshark's preferences + Gestisci le preferenze di Wireshark + + + Capture File Properties + Proprietà file di cattura + + + Capture file properties + Proprietà del file di cattura + + + &Protocol Hierarchy + Gerarchia di &protocolli + + + Show a summary of protocols present in the capture file. + Mostra un riepilogo dei protocolli presenti nel file di cattura. + + + Capinfos + Capinfos + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + Sequenza temporale (Stevens) + + + TCP time sequence graph (Stevens) + Grafico della sequenza temporale TCP (Stevens) + + + Throughput + Capacità trasmissiva + + + Round Trip Time + Round Trip Time + + + TCP round trip time + Round trip time TCP + + + Window Scaling + Ridimensionamento della finestra + + + TCP window scaling + Ridimensionamento della finestra TCP + + + HTTP/2 Stream + Flusso HTTP/2 + + + SIP Call + Chiamate SIP + + + Time Sequence (tcptrace) + Sequenza temporale (tcptrace) + + + TCP time sequence graph (tcptrace) + Grafico della sequenza temporale TCP (tcptrace) + + + Analyse this Association + Analizza questa associazione + + + Show All Associations + Mostra tutte le associazioni + + + Flow Graph + Grafico del flusso + + + Flow sequence diagram + Diagramma della sequenza del flusso + + + ANCP + ANCP + + + ANCP statistics + Statistiche ANCP + + + Packets sorted by Instance ID + Pacchetti ordinati per Instance ID + + + BACapp statistics sorted by instance ID + Statistiche BACapp ordinate per instance ID + + + Packets sorted by IP + Pacchetti ordinati per IP + + + BACapp statistics sorted by IP + Statistiche BACapp ordinate per IP + + + Packets sorted by object type + Pacchetti ordinati per tipo di oggetto + + + BACapp statistics sorted by object type + Statistiche BACapp ordinate per tipo di oggetto + + + Packets sorted by service + Pacchetti ordinati per servizio + + + BACapp statistics sorted by service + Statistiche BACapp ordinate per servizio + + + Collectd + Collectd + + + Collectd statistics + Statistiche Collectd + + + DNS + DNS + + + DNS statistics + Statistiche DNS + + + HART-IP + HART-IP + + + HART-IP statistics + Statistiche HART-IP + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + Statistiche hpfeeds + + + HTTP2 + HTTP2 + + + HTTP2 statistics + Statistiche HTTP2 + + + Packet Counter + Contatore di pacchetti + + + HTTP packet counter + Contatore di pacchetti HTTP + + + Requests + Richieste + + + HTTP requests + Richieste HTTP + + + Load Distribution + Distribuzione di carico + + + HTTP load distribution + Distribuzione di carico HTTP + + + Packet Lengths + Lunghezze dei pacchetti + + + Packet length statistics + Statistiche sulle lunghezza dei pacchetti + + + Sametime + Sametime + + + Sametime statistics + Statistiche sametime + + + SOME/IP Messages + SOME/Messaggi IP + + + SOME/IP Message statistics + Statistiche SOME/Messaggi IP + + + SOME/IP-SD Entries + Voci SOME/IP-SD + + + SOME/IP-SD Entries statistics + Statistiche voci SOME/Messaggi IP + + + &LTP + &LTP + + + LTP segment and block statistics + Statistiche segmenti e blocchi LTP + + + &ISUP Messages + Messaggi &ISUP + + + ISUP message statistics + Statistiche dei messaggi ISUP + + + Osmux packet counts + Conteggi dei pacchetti Osmux + + + RTSP packet counts + Conteggi dei pacchetti RTSP + + + SM&PP Operations + Operazioni SM&PP + + + SMPP operation statistics + Statistiche delle operazioni SMPP + + + &UCP Messages + Messaggi &UCP + + + UCP message statistics + Statistiche dei messaggi UCP + + + F1AP + F1AP + + + F1AP Messages + Messaggi F1AP + + + NGAP + NGAP + + + NGAP Messages + Messaggi NGAP + + + Change the way packets are dissected + Modifica come i pacchetti vengono decodificati + + + Reload Lua Plugins + Ricarica plugin Lua + + + Reload Lua plugins + Ricarica i plugin Lua + + + Advertisements by Topic + Annunci per argomento + + + Advertisements by Source + Annunci per sorgente + + + Advertisements by Transport + Annunci per trasporto + + + Queries by Topic + Interrogazioni per argomento + + + Queries by Receiver + Interrogazioni per ricevitore + + + Wildcard Queries by Pattern + Interrogazioni con metacarattere per schema + + + Wildcard Queries by Receiver + Interrogazioni con metacarattere per ricevitore + + + Advertisements by Queue + Interrogazioni con metacarattere per coda + + + Queries by Queue + Interrogazioni per coda + + + Streams + Flussi + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + Filtra questa associazione + + + Strip Headers… + Rimuovi intestazioni… + + + Strip headers and export higher level encapsulations to file + Rimuovi le intestazioni ed esporta gli incapsulamenti di livello più alto su file + + + &I/O Graphs + Grafici &I/O + + + &Conversations + &Conversazioni + + + &Endpoints + T&erminatori + + + Shrink the main window text + Riduci il testo della finestra principale + + + Return the main window text to its normal size + Riporta il testo della finestra principale alle sue dimensioni normali + + + Reset Layout + Ripristina disposizione + + + Reset appearance layout to default size + Ripristina la disposizione dell'aspetto alla dimensione predefinita + + + Seconds Since First Captured Packet + Secondi dal primo pacchetto catturato + + + Show packet times as the seconds since the first captured packet. + Mostra i tempi dei pacchetti come secondi trascorsi dal primo pacchetto catturato. + + + Tenths of a millisecond + Decimi di millisecondo + + + Hundredths of a millisecond + Centesimi di millisecondo + + + Tenths of a microsecond + Decimi di microsecondo + + + Hundredths of a microsecond + Centesimi di microsecondo + + + Packet &Diagram + &Diagramma del pacchetto + + + Show or hide the packet diagram + Mostra o nascondi il diagramma del pacchetto + + + Show each conversation hash table + Mostri ogni tabella degli hash di conversazione + + + Show each dissector table and its entries + Mostra ogni tabella dei decodificatori e le sue voci + + + Show the currently supported protocols and display filter fields + Mostra i protocolli attualmente supportato e i campi del filtro di visualizzazione + + + MAC Statistics + Statistiche MAC + + + LTE MAC statistics + Statistiche MAC LTE + + + RLC Statistics + Statistiche RLC + + + LTE RLC statistics + Statistiche RLC LTE + + + LTE RLC graph + Grafico RLC LTE + + + MTP3 Summary + Riepilogo MTP3 + + + MTP3 summary statistics + Statistiche riepilogo MTP3 + + + Bluetooth Devices + Dispositivi Bluetooth + + + Bluetooth HCI Summary + Riepilogo HCI Bluetooth + + + Display Filter &Expression… + &Espressione filtro di visualizzazione... + + + Display Filter Expression… + Espressione filtro di visualizzazione... + + + REGISTER_STAT_GROUP_RSERPOOL + REGISTER_STAT_GROUP_RSERPOOL + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + Avvio di "REGISTER_STAT_GROUP_RSERPOOL" + + + No GSM statistics registered + Nessuna statistica GSM registrata + + + No LTE statistics registered + Nessuna statistica LTE registrata + + + No MTP3 statistics registered + Nessuna statistica MTP3 registrata + + + IAX2 Stream Analysis + Analisi flusso IAX2 + + + Show Packet Bytes… + Mostra byte del pacchetto... + + + Go to &Linked Packet + Vai a&l pacchetto collegato + + + UDP Multicast Streams + Flusso multicast UDP + + + Show UTP multicast stream statistics. + Mostra le statistiche del flusso multicast UDP. + + + WLAN Traffic + Traffico WLAN + + + Show IEEE 802.11 wireless LAN statistics. + Mostra le statistiche wireless LAN IEEE 802.11. + + + Add a display filter button. + Aggiungi un pulsante del filtro di visualizzazione. + + + Firewall ACL Rules + Regole ACL firewall + + + Create firewall ACL rules + Crea regole ACL firewall + + + &Full Screen + Schermo &intero + + + Credentials + Credenziali + + + MAC Address Blocks + Blocchi di indirizzi MAC + + + TLS Keylog Launcher + Esecutore Keylog TLS + + + Release Notes + Note di rilascio + + + &Options… + &Opzioni... + + + &Wireless + &Wireless + + + Capture &Filters… + &Filtri di cattura... + + + As Plain &Text… + Come &testo semplice... + + + As Plain &Text + Come &testo semplice + + + As &CSV + Come &CSV + + + As &YAML + Come &YAML + + + All Visible Items + Tutti gli elementi visibili + + + All Visible Selected Tree Items + Tutti gli elementi visibili dell'albero selezionato + + + Display Filter &Macros… + Visualizza &macro dei filtri... + + + &Find Packet… + &Trova pacchetto... + + + Find Ne&xt + Trova &successivo + + + Find Pre&vious + Tro&va precedente + + + Mark or unmark each selected packet + Marca o rimuovi ogni pacchetto selezionato + + + Ignore or unignore each selected packet + Ignora o considera ogni pacchetto selezionato + + + U&nignore All Displayed + Co&nsidera tutti i visualizzati + + + Unignore all displayed packets + Considera tutti i pacchetti visualizzati + + + Time Shift… + Spostamento temporale... + + + Inject TLS Secrets + Immissione segreti TLS + + + Embed used TLS secrets in the capture file + Incorpora i segreti TLS utilizzati nel file di cattura + + + Discard All Secrets + Scarta tutti i segreti + + + Discard all decryption secrets in the capture file + Elimina tutti i segreti di decifratura nel file di cattura + + + &Preferences… + &Preferenze... + + + TCP throughput + Throughput TCP + + + Request Sequences + Sequenze richiesta + + + HTTP Request Sequences + Sequenze richiesta HTTP + + + Decode &As… + Decodific&a come... + + + Export PDUs to File… + Esporta PDU su file... + + + Create graphs based on display filter fields + Crea grafici sulla base dei campi del filtro di visualizzazione + + + &Main Toolbar + Barra degli strumenti &principale + + + Show or hide the main toolbar + Mostra o nascondi la barra degli strumenti principale + + + &Filter Toolbar + Barra degli strumenti del &filtro + + + Show or hide the display filter toolbar + Mostra o nascondi la barra degli strumenti del filtro di visualizzazione + + + Conversations at different protocol levels + Conversazioni a diversi livelli di protocollo + + + Endpoints at different protocol levels + Terminatori a diversi livelli di protocollo + + + Colorize Packet List + Colora l'elenco dei pacchetti + + + Draw packets using your coloring rules + Visualizza i pacchetti usando le tue regole di colorazione + + + &Zoom In + &Ingrandisci + + + Enlarge the main window text + Ingrandisci il testo della finestra principale + + + Zoom Out + Rimpicciolisci + + + Normal Size + Dimensione normale + + + Resize Columns + Ridimensiona colonne + + + Resize packet list columns to fit contents + Ridimensiona le colonne dell'elenco dei pacchetti per adattare il contenuto + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Data e ora del giorno (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + Mostra gli orari dei pacchetti come date e ora del giorno. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Anno, giorno dell'anno, e ora del giorno (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Mostra gli orari del pacchetto come anno, giorno dell'anno e ora del giorno. + + + Time of Day (01:02:03.123456) + Ora del giorno (01:02:03.123456) + + + Seconds Since 1970-01-01 + Secondi dal 1970-01-01 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Mostra gli orari del pacchetto come secondi dalla epoch UNIX/POSIX (1970-01-01). + + + Seconds Since Previous Captured Packet + Secondi dal precedente pacchetto catturato + + + Show packet times as the seconds since the previous captured packet. + Mostra gli orari del pacchetto come secondi dal precedente pacchetto catturato. + + + Seconds Since Previous Displayed Packet + Secondi dal precedente pacchetto visualizzato + + + Show packet times as the seconds since the previous displayed packet. + Mostra gli orari del pacchetto come secondi dal precedente pacchetto visualizzato. + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + Date e ora del giorno UTC (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Mostra gli orari del pacchetto come data e ora del giorno UTC. + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Anno, giorno dell'anno e ora del giorno UTC (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + Mostra l'orario dei pacchetti come anno, giorno dell'anno e ora del giorno UTC. + + + UTC Time of Day (01:02:03.123456) + Ora del giorno UTC (01:02:03.123456) + + + Show packet times as the UTC time of day. + Mostra l'orario dei pacchetti come ora del giorno UTC. + + + Automatic (from capture file) + Automatico (dal file di cattura) + + + Use the time precision indicated in the capture file. + Usa la precisione del tempo indicata nel file di cattura. + + + Seconds + Secondi + + + Tenths of a second + Decimi di secondo + + + Hundredths of a second + Centesimi di secondo + + + Milliseconds + Millisecondi + + + Microseconds + Microsecondi + + + Nanoseconds + Nanosecondi + + + Display Seconds With Hours and Minutes + Visualizza i secondi con ore e minuti + + + Display seconds with hours and minutes + Visualizza i secondi con ore e minuti + + + Resolve &Physical Addresses + Risolvi gli indirizzi &fisici + + + Show names for known MAC addresses. Lookups use a local database. + Mostra i nomi per gli indirizzi MAC conosciuti. Le ricerche usano un database locale. + + + Resolve &Network Addresses + Risolvi gli indirizzi di &rete + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + Mostra nomi per gli indirizzi conosciuti IPv4, IPv6 e IPX. Le ricerche possono generare traffico di rete. + + + Resolve &Transport Addresses + Risolvi gli indirizzi di &trasporto + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + Mostra i nomi per gli indirizzi conosciuti per i servizi TCP, UDP e SCTP. Le ricerche possono generare traffico di rete su alcuni sistemi. + + + Wire&less Toolbar + Barra degli strumenti wire&less + + + Show or hide the wireless toolbar + Mostra o nascondi la barra degli strumenti wireless + + + &Status Bar + Barra di &stato + + + Show or hide the status bar + Mostra o nascondi la barra di stato + + + Packet &List + E&lenco dei pacchetti + + + Show or hide the packet list + Mostra o nascondi l'elenco dei pacchetti + + + Packet &Details + &Dettagli del pacchetto + + + Show or hide the packet details + Mostra o nascondi i dettagli del pacchetto + + + Packet &Bytes + &Byte del pacchetto + + + Show or hide the packet bytes + Mostra o nascondi i byte del pacchetto + + + &Conversation Hash Tables + Tabelle hash di &conversazione + + + &Dissector Tables + Tabella dei &decodificatori + + + &Supported Protocols + Protocolli &supportati + + + MAP Summary + Riepilogo MAP + + + GSM MAP summary statistics + Statistiche riepilogo GSM MAP + + + RLC &Graph + &Grafico RLC + + + &Coloring Rules… + Regole di &colorazione... + + + Show Linked Packet in New Window + Mostra il pacchetto collegato in una nuova finestra + + + New Coloring Rule… + New Conversation Rule… + Nuova regola di colorazione... + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + Analisi flusso RTP per il flusso selezionato. Premi il tasto CTRL per aggiungere anche il flusso inverso. + + + RTP Player + Lettore RTP + + + Play selected stream. Press CTRL key for playing reverse stream too. + Riproduci il flusso selezionato. Premi il tasto CTRL riprodurre anche il flusso inverso. + + + IA&X2 Stream Analysis + Analisi flusso IA&X2 + + + Enabled Protocols… + Enable Protocols… + Abilita protocolli... + + + Wiki Protocol Page + Pagina wiki del protocollo + + + Open the Wireshark wiki page for this protocol. + Apri la pagina del wiki di Wireshark per questo protocollo. + + + Filter Field Reference + Riferimento campo di filtro + + + Open the display filter reference page for this filter field. + Apri la pagina di riferimento del filtro di visualizzazione per questo campo di filtro. + + + Go to the packet referenced by the selected field. + Vai al pacchetto referenziato dal campo selezionato. + + + &VoIP Calls + Chiamate &VoIP + + + Open &Recent + Apri &recenti + + + Name Resol&ution + Risol&uzione dei nomi + + + Service &Response Time + Tempo di &risposta del servizio + + + &RTP + &RTP + + + S&CTP + S&CTP + + + &ANSI + &ANSI + + + &GSM + &GSM + + + &LTE + &LTE + + + &MTP3 + &MTP3 + + + &Open + &Apri + + + &Quit + &Esci + + + &Close + Chiudi + + + Display &Filters… + &Filtri di visualizzazione... + + + &Unmark All Displayed + Rim&uovi la selezione da tutti i visualizzati + + + All VoIP Calls + Tutte le chiamate VoIP + + + SIP &Flows + &Flussi SIP + + + SIP Flows + Flussi SIP + + + RTP Streams + Flussi RTP + + + Edit the packet list coloring rules. + Modifica le regole di colorazione dell'elenco dei pacchetti. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Attributi server ATT Bluetooth + + + Show Packet in New &Window + M&ostra pacchetto in una nuova finestra + + + Show this packet in a separate window. + Mostra questo pacchetto in una finestra separata. + + + Show the linked packet in a separate window. + Mostra il pacchetto collegato in una finestra separata. + + + Auto Scroll in Li&ve Capture + Scorrimento a&utomatico durante la cattura + + + Automatically scroll to the last packet during a live capture. + Scorri automaticamente all'ultimo pacchetto durante una cattura. + + + Expert Information + Informazioni per esperti + + + Show expert notifications + Mostra notifiche per esperti + + + Add an expression to the display filter. + Aggiungi un'espressione per il filtro di visualizzazione. + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + Avvio di "REGISTER_STAT_GROUP_UNSORTED" + + + No ANSI statistics registered + No tools registered + Nessuna statistica ANSI registrata + + + Resolved Addresses + Indirizzi risolti + + + Show each table of resolved addresses as copyable text. + Mostra ogni tabella di indirizzi risolti come testo copiabile. + + + Color &1 + Colore &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + Marca la conversazione attuale con il suo colore. + + + Color &2 + Colore &2 + + + Color &3 + Colore &3 + + + Color &4 + Colore &4 + + + Color &5 + Colore &5 + + + Color &6 + Colore &6 + + + Color &7 + Colore &7 + + + Color &8 + Colore &8 + + + Color &9 + Colore &9 + + + Color 1&0 + Colore 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + Crea una nuova regola di colorazione basata su questo campo. + + + Reset Colorization + Ripristina colorazione + + + Reset colorized conversations. + Ripristina le conversazioni colorate. + + + RTP Stream Analysis + Analisi flusso RTP + + + Edit Resolved Name + Modifica nome risolto + + + Manually edit a name resolution entry. + Modifica manualmente la voce di risoluzione di un nome. + + + Enable and disable specific protocols + Abilita e disabilita protocolli specifici + + + before quitting + prima di uscire + + + Save packets before merging? + Salvare i pacchetti prima di unire? + + + A temporary capture file can't be merged. + Un file di cattura temporaneo non può essere unito. + + + Save changes in "%1" before merging? + Salvare le modifiche a "%1" prima di unire? + + + Changes must be saved before the files can be merged. + Le modifiche devono essere salvate prima di unire. + + + Invalid Display Filter + Filtro di visualizzazione non valido + + + Invalid Read Filter + Filtro di lettura non valido + + + The filter expression %1 isn't a valid read filter. (%2). + L'espressione %1 non è un filtro di lettura valido. (%2). + + + before importing a capture + before importing a new capture + prima di importare una cattura + + + Unable to export to "%1". + Impossibile esportare su "%1". + + + You cannot export packets to the current capture file. + Non puoi esportare pacchetti sul file di cattura attuale. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + Vuoi salvare le modifiche che hai apportato%1? + + + Your captured packets will be lost if you don't save them. + I pacchetti catturati saranno persi se non li salvi. + + + Do you want to save the changes you've made to the capture file "%1"%2? + Vuoi salvare le modifiche che hai apportato al file di cattura "%1"%2? + + + Your changes will be lost if you don't save them. + Le modifiche saranno perse se non le salvi. + + + Check for Updates… + Verifica aggiornamenti... + + + Unable to drop files during capture. + Impossibile scartare file durante la cattura. + + + Unknown file type returned by merge dialog. + Tipo di file sconosciuto restituito dalla finestra di unione. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Segnalalo come un problema di Wireshark su https://gitlab.com/wireshark/wireshark/-/issues. + + + Unknown file type returned by export dialog. + Tipo di file sconosciuto restituito dalla finestra di esportazione. + + + Do you want to stop the capture and save the captured packets%1? + Vuoi fermare la cattura e salvare i pacchetti catturati%1? + + + Do you want to save the captured packets%1? + Vuoi salvare i pacchetti catturati%1? + + + Save before Continue + Salva prima di continuare + + + Stop and Save + Ferma e salva + + + Stop and Quit &without Saving + Stop and Quit without Saving + Ferma ed esci senza sal&vare + + + Quit &without Saving + Quit without Saving + Esci senza sal&vare + + + There is no "rtp.ssrc" field in this version of Wireshark. + Non c'è alcun campo "rtp.ssrc" in questa versione di Wireshark. + + + Please select an RTPv2 packet with an SSRC value + Seleziona un pacchetto RTPv2 con un valore SSRC + + + SSRC value not found. + Valore SSRC non trovato. + + + Show or hide the toolbar + Mostra o nascondi la barra degli strumenti + + + Continue &without Saving + Continue without Saving + Continua senza sal&vare + + + Stop and Continue &without Saving + Stop and Continue without Saving + Ferma e continua senza sal&vare + + + The Wireshark Network Analyzer + Wireshark l'analizzatore di rete + + + Capturing from %1 + Cattura da %1 + + + before opening another file + errore durante l'apertura di un altro file + + + Merging files. + Unisci i file. + + + %1: %2 + %1: %2 + + + Clear Menu + Pulisci menu + + + before closing the file + prima della chiusura del file + + + Export Selected Packet Bytes + Esporta i byte del pacchetto selezionato + + + No Keys + Nessuna chiave + + + Export SSL Session Keys (%Ln key(s)) + Export SSL Session Keys (%1 key%2 + + Esporta chiavi di sessione SSL (%Ln chiave) + Esporta chiavi di sessione SSL (%Ln chiavi) + + + + Raw data (*.bin *.dat *.raw);;All Files ( + Dati grezzi (*.bin *.dat *.raw);;Tutti i file ( + + + Couldn't copy text. Try another item. + Non riesco a copiare il testo. Prova un'altra voce. + + + Are you sure you want to remove all packet comments? + Sei sicuro di voler rimuovere tutti i commenti dei pacchetti? + + + Unable to build conversation filter. + Impossibile creare il filtro di conversazione. + + + before reloading the file + prima di ricaricare il file + + + Error compiling filter for this conversation. + Errore durante la compilazione del filtro per questa conversazione. + + + No previous/next packet in conversation. + Nessun pacchetto precedente/successivo in conversazione. + + + No interface selected. + Nessuna interfaccia selezionata. + + + Saving %1… + Salvataggio di %1... + + + Configure all extcaps before start of capture. + Configura tutte le extcap prima di avviare la cattura. + + + Invalid capture filter. + Filtro di cattura invalido. + + + (empty comment) + placeholder for empty comment + (commento vuoto) + + + Add New Comment… + Aggiungi nuovo commento... + + + Edit "%1" + edit packet comment + Modifica "%1" + + + Delete "%1" + delete packet comment + Elimina "%1" + + + Delete packet comments + Elimina i commenti dei pacchetti + + + Delete comments from %n packet(s) + + Elimina i commenti da %n pacchetto + Elimina i commenti da %n pacchetti + + + + before starting a new capture + prima di iniziare una nuova cattura + + + before reloading Lua plugins + prima di ricaricare i plugin Lua + + + Please wait while Wireshark is initializing… + Attendi l'inizializzazione di Wireshark... + + + before updating + prima di aggiornare + + + There are no TLS Session Keys to save. + Non ci sono chiavi di sessione SSL da salvare. + + + Export TLS Session Keys (%Ln key(s)) + + Esporta chiavi di sessione TLS (%Ln chiave) + Esporta chiavi di sessione TLS (%Ln chiavi) + + + + TLS Session Keys (*.keys *.txt);;All Files ( + Chiavi di sessione TLS (*.keys *.txt);;Tutti i file ( + + + No TLS Secrets + Nessun segreto TLS + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + Non sono disponibili segreti utilizzati per decifrare il traffico TLS nel file di cattura. Desideri visualizzare informazioni su come decifrare il traffico TLS sul wiki? + + + Are you sure you want to discard all decryption secrets? + Sei sicuro di voler eliminare tutti i segreti di decifratura? + + + No filter available. Try another %1. + Nessun filtro disponibile. Prova un altro %1. + + + column + colonna + + + item + elemento + + + The "%1" column already exists. + La colonna "%1" esiste già. + + + The "%1" column already exists as "%2". + La colonna "%1" esiste già come "%2". + + + RTP packet search failed + Ricerca pacchetto RTP non riuscita + + + No Interface Selected. + Nessuna interfaccia selezionata. + + + before restarting the capture + prima di riavviare la cattura + + + Wiki Page for %1 + Pagina wiki per %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Il Wiki di Wireshark è gestito dalla comunità.</p><p>La pagina che stai per caricare potrebbe essere perfetta, incompleta, errata o inesistente.</p><p>Vuoi proseguire?</p> + + + Loading + Caricamento + + + Reloading + Ricaricamento + + + Rescanning + Nuova scansione in corso + + + + WlanStatisticsDialog + + Wireless LAN Statistics + Statistiche wireless LAN + + + Channel + Canale + + + SSID + SSID + + + Percent Packets + Percentuale pacchetti + + + Percent Retry + Percentuale retry + + + Probe Reqs + Richieste probe + + + Probe Resp + Risposte probe + + + Auths + Auth + + + Retry + Riprova + + + Deauths + Deauth + + + Other + Altro + + + diff --git a/ui/qt/wireshark_ja_JP.ts b/ui/qt/wireshark_ja_JP.ts new file mode 100644 index 00000000..a1073f5d --- /dev/null +++ b/ui/qt/wireshark_ja_JP.ts @@ -0,0 +1,14812 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + Wiresharkについて + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">ネットワークプロトコルアナライザ</span> + + + Copy the version information to the clipboard + バージョン情報をクリップボードにコピーします + + + Copy to Clipboard + クリップボードへコピー + + + Authors + 作者 + + + Search Authors + 著者を検索 + + + Folders + フォルダ + + + Filter by path + パスでフィルタ + + + Plugins + プラグイン + + + No plugins found. + プラグインが見つかりませんでした + + + Search Plugins + プラグインを検索 + + + Filter by type: + 種類でフィルタ: + + + Keyboard Shortcuts + キーボードショートカット + + + Search Shortcuts + ショートカットを検索 + + + Acknowledgments + 確認 + + + License + ライセンス + + + The directory does not exist + ディレクトリがありません + + + Should the directory %1 be created? + ディレクトリ %1 を作成されますか? + + + The directory could not be created + ディレクトリを作成できませんでした + + + The directory %1 could not be created. + ディレクトリ %1 を作成できませんでした + + + Show in Finder + ファインダーで表示 + + + Show in Folder + フォルダで表示 + + + Copy + コピー + + + Copy Row(s) + + 行をコピー + + + + + AddressEditorFrame + + Frame + フレーム + + + Name Resolution Preferences… + Name Resolution Preferences... + 名前解決設定… + + + Address: + アドレス: + + + Name: + 名前: + + + Can't assign %1 to %2. + %1 を %2 に割り当てることができません + + + + AdvancedPrefsModel + + Name + 名前 + + + Status + 状態 + + + Type + 種別 + + + Value + + + + + ApplyLineEdit + + Apply changes + 変更を適用 + + + + AuthorListModel + + Name + 名前 + + + Email + メール + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Bluetooth ATTサーバ属性 + + + Handle + ハンドル + + + UUID + UUID + + + UUID Name + UUID名 + + + All Interfaces + すべてのインターフェース + + + All Devices + すべてのデバイス + + + Remove duplicates + 重複を削除 + + + Copy Cell + セルをコピー + + + Copy Rows + 行をコピー + + + Copy All + すべてをコピー + + + Save as image + 画像として保存 + + + Mark/Unmark Row + 行をマーク/マーク解除 + + + Ctrl-M + Ctrl+M + + + Mark/Unmark Cell + セルをマーク/マーク解除 + + + Save Table Image + 表の画像として保存 + + + PNG Image (*.png) + PNG画像 (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + Bluetooth機器 + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + 名前 + + + Class of Device + 機器のクラス + + + LMP Version + LMP バージョン + + + LMP Subversion + LMP サブバージョン + + + Manufacturer + 製造者 + + + HCI Version + HCI バージョン + + + HCI Revision + HCI リビジョン + + + Scan + スキャン + + + Authentication + 認証 + + + Encryption + 暗号化 + + + ACL MTU + ACL MTU + + + ACL Total Packets + ACL 全パケット + + + SCO MTU + SCO MTU + + + SCO Total Packets + SCO 全パケット + + + LE ACL MTU + LE ACL MTU + + + LE ACL Total Packets + LE ACL 全パケット + + + LE ISO MTU + LE ISO MTU + + + LE ISO Total Packets + LE ISO 全パケット + + + Inquiry Mode + 問い合わせモード + + + Page Timeout + ページタイムアウト + + + Simple Pairing Mode + シンプルペアリングモード + + + Voice Setting + 音声設定 + + + Value + + + + Changes + 変更 + + + %1 changes + %1 変更 + + + Copy Cell + セルをコピー + + + Copy Rows + 行をコピー + + + Copy All + すべてをコピー + + + Save as image + 画像として保存 + + + Mark/Unmark Row + 行をマーク/マーク解除 + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + セルをマーク/マーク解除 + + + Unknown + 不明 + + + Bluetooth Device - %1%2 + Bluetooth機器 - %1%2 + + + enabled + 有効 + + + disabled + 無効 + + + %1 ms (%2 slots) + %1 ミリ秒 (%2 スロット) + + + Save Table Image + テーブル画像を保存 + + + PNG Image (*.png) + PNG画像 (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Bluetoothデバイス + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + 名前 + + + LMP Version + LMPバージョン + + + LMP Subversion + LMPサブバージョン + + + Manufacturer + 製造元 + + + HCI Version + HCIバージョン + + + HCI Revision + HCIリビジョン + + + Is Local Adapter + ローカルアダプタである + + + All Interfaces + すべてのインターフェース + + + Show information steps + 情報ステップを表示 + + + %1 items; Right click for more option; Double click for device details + %1 アイテム; 右クリックしてより多くのオプション; ダブルクリックしてデバイスの詳細 + + + Copy Cell + セルをコピー + + + Copy Rows + 行をコピー + + + Copy All + すべてをコピー + + + Save as image + 画像として保存 + + + Mark/Unmark Row + 行をマーク/マーク解除 + + + Ctrl-M + Ctrl+M + + + Mark/Unmark Cell + セルをマーク/マーク解除 + + + true + true + + + Save Table Image + 表の画像を保存 + + + PNG Image (*.png) + PNG画像 (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Bluetooth HCI概要 + + + Name + 名前 + + + OGF + OGF + + + OCF + OCF + + + Opcode + Opcode + + + Event + イベント + + + Subevent + サブイベント + + + Status + 状態 + + + Reason + 原因 + + + Hardware Error + ハードウエアエラー + + + Occurrence + 出現位置 + + + Link Control Commands + リンクコントロールコマンド + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + リンクポリシーコマンド + + + 0x02 + 0x02 + + + Controller & Baseband Commands + コントローラーとベースバンドコマンド + + + 0x03 + 0x03 + + + Informational Parameters + 情報パラメタ + + + 0x04 + 0x04 + + + Status Parameters + 状態パラメタ + + + 0x05 + 0x05 + + + Testing Commands + テストコマンド + + + 0x06 + 0x06 + + + LE Controller Commands + LEコントローラーコマンド + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + Bluetoothロゴテストコマンド + + + 0x3E + 0x3E + + + Vendor-Specific Commands + ベンダ固有コマンド + + + 0x3F + 0x3F + + + Unknown OGF + 不明なOGF + + + Events + イベント + + + Hardware Errors + ハードウエアエラー + + + Results filter: + 結果フィルタ: + + + Display filter: + 表示フィルタ: + + + All Interfaces + すべてのインターフェース + + + All Adapters + すべてのアダプタ + + + Copy Cell + セルをコピー + + + Copy Rows + 行をコピー + + + Copy All + すべてをコピー + + + Save as image + 画像として保存 + + + Mark/Unmark Row + 行をマーク/マーク解除 + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + セルをマーク/マーク解除 + + + Unknown + 不明 + + + Adapter %1 + アダプタ %1 + + + Frame %1 + フレーム %1 + + + Pending + 保留 + + + Save Table Image + 表の画像を保存 + + + PNG Image (*.png) + PNG画像 (*.png) + + + + ByteViewTab + + Packet bytes + パケットバイト列 + + + + ByteViewText + + Allow hover highlighting + ホーバーハイライトを許可 + + + Show bytes as hexadecimal + 16進数でバイト列を表示 + + + …as decimal + 10進数として... + + + …as octal + 8進数として... + + + …as bits + ビット列として… + + + Show text based on packet + パケットをテキストベースで表示 + + + …as ASCII + ASCII形式として… + + + …as EBCDIC + EBCDIC形式として… + + + + CaptureFile + + [closing] + [閉じています] + + + [closed] + [閉じました] + + + + CaptureFileDialog + + This capture file contains comments. + このキャプチャファイルにはコメントが含まれています。 + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + 選択したファイル形式はコメントに対応していません。コメントに対応した形式で保存しますか?もしくはコメントを破棄して選んだ形式で保存しますか? + + + Discard comments and save + コメントを破棄して保存 + + + Save in another format + 別の形式で保存 + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + コメントに対応したファイル形式ではありません。コメントを破棄して選んだ形式で保存しますか? + + + All Files ( + すべてのファイル ( + + + All Capture Files + すべてのキャプチャファイル + + + Format: + 形式: + + + Size: + サイズ: + + + Start / elapsed: + 開始 / 経過: + + + Automatically detect file type + ファイル種別を自動的に検出 + + + %1, error after %Ln packet(s) + %1, error after %2 packets + + %Ln パケットの後に %1, エラー + + + + %1, timed out at %Ln packet(s) + %1, timed out at %2 packets + + %Ln パケットにて %1, タイムアウト + + + + %1, %Ln packet(s) + + %1, %Ln パケット + + + + Prepend packets + 前にパケットを挿入 + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + 現在のファイルの前に選択したファイルからパケットを挿入します。パケットのタイムスタンプは無視されます + + + Merge chronologically + 時系列で結合 + + + Insert packets in chronological order. + 時系列順でパケットを挿入します + + + Append packets + 後にパケットを追加 + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + 現在のファイルの後に選択したファイルからパケットを挿入します パケットのタイムスタンプは無視されます + + + Read filter: + 読込フィルタ: + + + Compress with g&zip + gzip形式で圧縮(&z) + + + Open Capture File + Wireshark: Open Capture File + キャプチャファイルを開く + + + Save Capture File As + Wireshark: Save Capture File As + としてキャプチャファイルを保存 + + + Save as: + として保存: + + + Export Specified Packets + Wireshark: Export Specified Packets + 指定したパケットをエクスポート + + + Export as: + としてエクスポート: + + + Merge Capture File + Wireshark: Merge Capture File + キャプチャファイルを結合 + + + Unknown file type returned by save as dialog. + …として保存画面から返された未知のファイルタイプ + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + どうかこれをWiresharkの問題として以下に報告ください。https://gitlab.com/wireshark/wireshark/-/issues + + + directory + ディレクトリ + + + unknown file format + 不明なファイルフォーマット + + + error opening file + ファイルオープンエラー + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + %Ln データレコードの後に %1, エラー + + + + %1, timed out at %Ln data record(s) + + %Ln データレコードにて %1, タイムアウト + + + + %1, %Ln data record(s) + + %1, %Ln データレコード + + + + unknown + 不明 + + + + CaptureFilePropertiesDialog + + Details + 詳細 + + + Capture file comments + キャプチャファイルコメント + + + Refresh + 更新 + + + Copy To Clipboard + クリップボードにコピー + + + Save Comments + コメントを保存 + + + Capture File Properties + キャプチャファイルプロパティ + + + Unknown + 不明 + + + File + ファイル + + + Name + 名前 + + + Length + 長さ + + + Hash (SHA256) + ハッシュ(SHA256) + + + Hash (SHA1) + ハッシュ (SHA1) + + + Format + 形式 + + + Encapsulation + カプセル化 + + + Snapshot length + スナップショット長 + + + Time + 時間 + + + First packet + 最初のパケット + + + Last packet + 最後のパケット + + + Elapsed + 経過時間 + + + Section %1 + セクション %1 + + + Capture + キャプチャ + + + Hardware + ハードウエア + + + OS + OS + + + Application + アプリケーション + + + Interfaces + インターフェース + + + Interface + インターフェース + + + Dropped packets + 欠落したパケット + + + Capture filter + キャプチャフィルタ + + + Link type + リンク種別 + + + Packet size limit (snaplen) + パケットサイズ制限(snaplen) + + + none + なし + + + %1 bytes + %1 バイト + + + Statistics + 統計 + + + Measurement + 測定 + + + Captured + キャプチャ + + + Displayed + 表示 + + + Marked + マーク済 + + + Packets + パケット数 + + + Time span, s + 時間間隔,秒 + + + Average pps + 平均パケット毎秒 + + + Average packet size, B + 平均パケットサイズ,バイト + + + Bytes + バイト数 + + + Average bytes/s + 平均バイト数毎秒 + + + Average bits/s + 平均ビット数毎秒 + + + Section Comment + セクション コメント + + + Packet Comments + パケットコメント + + + <p>Frame %1: + <p>フレーム %1: + + + Created by Wireshark %1 + + + Wireshark %1 によって作成されました + + + + + + CaptureFilterCombo + + Capture filter selector + キャプチャフィルタ選択 + + + + CaptureFilterEdit + + Capture filter entry + キャプチャフィルタエントリ + + + Manage saved bookmarks. + 保存したブックマークを管理 + + + Apply this filter string to the display. + このフィルタ文字列を表示に適用 + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + 複数のフィルタが選択されています。選択を上書きするか空白のままにしてください + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>選択したインターフェースには異なるキャプチャフィルタがあります。ここにフィルタを入力すれば上書きされます。なにもしなければ保存します</p> + + + Enter a capture filter %1 + キャプチャフィルタ %1 を入力 + + + Save this filter + このフィルタを保存 + + + Remove this filter + このフィルタを削除 + + + Manage Capture Filters + キャプチャフィルタ管理 + + + + CaptureInfoDialog + + Capture Information + キャプチャ情報 + + + Stop Capture + キャプチャ停止 + + + %1 packets, %2:%3:%4 + %1 パケット, %2:%3:%4 + + + + CaptureInfoModel + + Other + その他 + + + + CaptureOptionsDialog + + Input + 入力 + + + Interface + インターフェース + + + Traffic + トラフィック + + + Link-layer Header + リンク層ヘッダ + + + Promiscuous + プロミスキャス + + + Snaplen (B) + キャプチャ長(バイト) + + + Buffer (MB) + バッファ(メガバイト) + + + Monitor Mode + モニタモード + + + Capture Filter + キャプチャフィルタ + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>これを有効にした方がよいでしょう。通常ネットワークカードは自身のネットワークアドレスに送られたトラフィックのみキャプチャします。もし、ネットワークカードが"見る"ことのできるすべてのトラフィックをキャプチャしたいのでしたら、このオプションをマークしてください。スイッチされたネットワークからパケットをキャプチャするより多くの詳細はFAQを参照してください。</p></body></html> + + + Enable promiscuous mode on all interfaces + すべてのインターフェースにおいてプロミスキャスモードを有効化します + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + インターフェースを表示/非表示、コメントを追加、およびパイプとリモートインターフェースを管理します + + + Manage Interfaces… + インターフェース管理… + + + Capture filter for selected interfaces: + 選択したインタフェースのキャプチャフィルタ: + + + Compile BPFs + BPF形式をコンパイル + + + Output + 出力 + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>キャプチャしたデータを記録するファイル名を入力してください。デフォルトでは一時ファイルが利用されます</p></body></html> + + + Capture to a permanent file + 保存ファイルにキャプチャ + + + File: + ファイル: + + + Browse… + 参照… + + + Output format: + 出力形式: + + + pcapng + pcap-ng形式 + + + pcap + pcap形式 + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>1つのキャプチャファイルを用いる代わりに複数のファイルが作成されます。</p><p>生成されたファイル名は連番とキャプチャ開始時間を含みます。</p><p>注意:有効にすると少なくとも1つの新規ファイルの基準が選択されなければなりません。</p></body></html> + + + Create a new file automatically… + 新規ファイルを自動的に作成します… + + + after + + + + Switch to the next file after the specified number of packets have been captured. + 指定したパケット数がキャプチャされた後に次のファイルに切り替えます + + + packets + パケット + + + Switch to the next file after the file size exceeds the specified file size. + ファイルサイズが指定したファイルサイズを超過した後に次のファイルに切り替えます + + + kilobytes + キロバイト + + + megabytes + メガバイト + + + gigabytes + ギガバイト + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + 現在のファイルをキャプチャしている時間が指定した時間を超過した際に次のファイルに切り替えます + + + seconds + + + + minutes + + + + hours + + + + when time is a multiple of + 時刻が複数の + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + (時計の)時刻が特定の間隔になったら次のファイルに切り替えます。例えば、1時間に設定すると毎時1時間ごとに新しいファイルが作成されます + + + compression + 圧縮 + + + None + なし + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>キャプチャが次のファイルに切り替えられた後、与えられたファイル数を超過したら、最も古いファイルが消されます</p></body></html> + + + Use a ring buffer with + リングバッファを用いる + + + files + ファイル + + + Options + オプション + + + Display Options + 表示オプション + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>このオプションを用いるとメイン画面ですぐにパケットキャプチャを表示します。注意:この操作はキャプチャを遅くするので、よりパケットをドロップするかもしれません。</p></body></html> + + + Update list of packets in real-time + 実時間でパケット一覧を更新 + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>実際の時間でパケット一覧を更新オプションを用いるとパケット一覧画面はスクロールして最新のパケットを表示します</p></body></html> + + + Automatically scroll during live capture + キャプチャ中に自動スクロール + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>キャプチャ中にキャプチャ情報画面を表示</p></body></html> + + + Show capture information during live capture + キャプチャ中にキャプチャ情報画面を表示 + + + Name Resolution + 名前解決 + + + Perform MAC layer name resolution while capturing. + キャプチャ中にMAC層の名前解決を行います + + + Resolve MAC addresses + MACアドレスを解決 + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>キャプチャ中にネットワーク層の名前解決を行います。</p></body></html> + + + Resolve network names + ネットワーク名を解決 + + + Perform transport layer name resolution while capturing. + キャプチャ中にトランスポート層の名前解決を行います + + + Resolve transport names + トランスポート名を解決 + + + Stop capture automatically after… + …後に自動的にキャプチャを停止 + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>指定した数のパケットがキャプチャされた後キャプチャを停止します</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + 指定したパケット数がキャプチャされた後にキャプチャを停止します + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>指定した数のファイルを作成した後キャプチャを停止します</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>指定した量のデータがキャプチャされたらキャプチャを停止します</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + 指定した量のデータがキャプチャされたらキャプチャを停止します + + + Stop capturing after the specified amount of time has passed. + 指定時間が経過した後キャプチャを停止します + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>オプションで名称未設定のキャプチャファイルの一時ディレクトリを指定します</p></body></html> + + + Directory for temporary files + 一時ファイルのディレクトリ + + + Capture Options + キャプチャオプション + + + Start + 開始 + + + Leave blank to use a temporary file + 一時ファイルを使うため空白にしておきます + + + Specify a Capture File + キャプチャファイルを指定します + + + Specify temporary directory + 一時ファイルのディレクトリを指定 + + + %1: %2 + %1: %2 + + + Addresses + アドレス + + + Address + アドレス + + + no addresses + アドレスがありません + + + Error + エラー + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + 複数ファイル:要求されたファイルサイズが大きすぎます。ファイルサイズは2GBより大きくできません + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + 複数ファイル:キャプチャファイル名がありません。複数のファイルを用いたいのならファイル名を指定しなければなりません + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + 複数ファイル:ファイル制限がありません。各ファイルのファイルサイズ、間隔、またはパケット数を指定しなければなりません。 + + + + CapturePreferencesFrame + + Frame + フレーム + + + Default interface + デフォルトインターフェース + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + これを有効にした方がよいでしょう。通常ネットワークカードは自身のアドレスに送られたトラフィックのみキャプチャします。もし、ネットワークカードが"見る"ことのできるすべてのトラフィックをキャプチャしたいのでしたら、このオプションをマークしてください。FAQにスイッチングハブのネットワークからパケットをキャプチャする詳細があります。</p></body></html> + + + Capture packets in promiscuous mode + プロミスキャスモードでパケットをキャプチャ + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>次世代のキャプチャファイル形式でパケットをキャプチャします</p></body></html> + + + Capture packets in pcapng format + pcapng形式でパケットをキャプチャします + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>キャプチャが進んでいる間パケット一覧を更新します。これにより、高速ネットワークにおいてはパケットが欠落することがありえます。</p></body></html> + + + Update list of packets in real time + 実時間でパケット一覧を更新 + + + Interval between updates (ms) + 更新間隔(ms) + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + <html><head/><body><p>どのくらいの頻度でキャプチャによって新しいパケットのGUIの通知があるか。どのくらいの頻度でGUIが更新されるのとタイマの精度に影響します。</p></body></html> + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + <html><head/><body><p>新しいパケットが更新される間隔。どのくらいの頻度でGUIが更新されるのとタイマの精度に影響します。</p></body></html> + + + Don't load interfaces on startup + 起動時にインタフェースを読み込みません + + + Disable external capture interfaces + 外部キャプチャインタフェースを無効にします + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + "@" シンボルは無視されます + + + + ColoringRulesDialog + + Dialog + ダイアログ + + + <small><i>A hint.</i></small> + <small><i>ヒント</i></small> + + + Add a new coloring rule. + 新しい色付けルールを追加します + + + Delete this coloring rule. + この色付けルールを削除します + + + Duplicate this coloring rule. + この色付けルールを複製します + + + Clear all coloring rules. + 全ての色付けルールをクリアします + + + Set the foreground color for this rule. + このルールに対して前景色を設定します + + + Foreground + 前景色 + + + Set the background color for this rule. + このルールに対して背景色を設定します + + + Background + 背景色 + + + Set the display filter using this rule. + このルールを用いる表示フィルタを設定します + + + Apply as filter + フィルタとして適用 + + + Select a file and add its filters to the end of the list. + ファイルを選択してリストの末尾にフィルタを追加します + + + Save filters in a file. + ファイルにフィルタを保存します + + + Coloring Rules %1 + 色付けルール %1 + + + Import… + インポート… + + + Export… + エクスポート… + + + Copy coloring rules from another profile. + 別のプロファイルから色付けルールをコピーします + + + Open + 開く + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + ダブルクリックして編集します。ドラッグして動かします。ルールは一致が見つかるまで順番に処理されます + + + Import Coloring Rules + 色付けルールをインポート + + + Export %1 Coloring Rules + %1 色付けルールをエクスポート + + + + ColoringRulesModel + + New coloring rule + 新規色付けルール + + + Unable to save coloring rules: %1 + 色付けルール %1 を保存できません + + + Name + 名前 + + + Filter + フィルタ + + + + ColumnEditorFrame + + Frame + フレーム + + + Title: + Title + 題名: + + + Type: + Type + 種別: + + + Fields: + Fields + フィールド: + + + Occurrence: + Occurrence + 出現位置: + + + Resolve Names: + 名前解決: + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + <html><head/><p>raw(無加工)のフィールド値の代わりに人が読むことのできる文字列を表示します。有効な文字列を含むフィールドを持つカスタム列へ適用する場合にのみ適用できます。</p></body></html> + + + Missing fields. + 見つからないフィールド + + + Invalid fields. + 無効なフィールド + + + Invalid occurrence value. + 無効なオカレンス値 + + + + ColumnListModel + + Displayed + 表示済 + + + Title + 題名 + + + Type + 種別 + + + Fields + フィールド + + + Field Occurrence + フィールド出現位置 + + + Resolved + 解決済 + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + <html>123raw(無加工)のフィールド値の代わりに人が読むことのできる文字列を表示します。有効な文字列を含むフィールドを持つカスタム列へ適用する場合にのみ適用できます。</html> + + + New Column + 新規列 + + + + ColumnPreferencesFrame + + Frame + フレーム + + + Add a new column + 新規列を追加します + + + Delete selected column + 選択した列を削除します + + + Show displayed columns only + 表示されている列のみ表示します + + + Reset all changes + すべての変更をリセットします + + + + CompiledFilterOutput + + Compiled Filter Output + コンパイル済フィルタ出力 + + + Copy + コピー + + + Copy filter text to the clipboard. + フィルタテキストをクリップボードにコピーします + + + + ConversationDataModel + + Address A + アドレス A + + + Port A + ポート A + + + Address B + アドレス B + + + Port B + ポート B + + + Packets + パケット + + + Bytes + バイト + + + Stream ID + ストリームID + + + Packets A + パケット A + + + Bytes A + バイト A + + + Packets B + パケット B + + + Bytes B + バイト B + + + Abs Start + 絶対的な開始 + + + Rel Start + 相対的な開始 + + + Duration + 時間 + + + Bits/s A + ビット毎秒 A + + + Bits/s B + ビット毎秒 B + + + Total Packets + 全パケット + + + Percent Filtered + フィルタされた割合 + + + + ConversationDialog + + Follow Stream… + ストリームを追跡… + + + Follow a TCP or UDP stream. + TCPかUDPストリームを追跡 + + + Graph… + グラフ… + + + Graph a TCP conversation. + TCP対話をグラフ化します + + + + ConversationHashTablesDialog + + Dialog + ダイアログ + + + Conversation Hash Tables + 対話ハッシュ表 + + + + CopyFromProfileButton + + Copy from + からコピー + + + Copy entries from another profile. + 別のプロファイルからエントリをコピーします + + + System default + システム デフォルト + + + + CredentialsDialog + + Wireshark - Credentials + ワイヤーシャーク - 証明書 + + + Credentials + 証明書 + + + + CredentialsModel + + Click to select the packet + クリックしてパケットを選びます + + + Click to select the packet with username + クリックしてパケットとユーザ名を選びます + + + Username not available + ユーザ名が利用できません + + + Packet No. + パケット番号 + + + Protocol + プロトコル + + + Username + ユーザ名 + + + Additional Info + 追加情報 + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + 16進数およびASCIIダンプ形式としてバイト列をコピー + + + Copy packet bytes as a hex and ASCII dump. + 16進数およびASCIIダンプ形式としてパケットバイト列をコピー + + + …as Hex Dump + 16進数ダンプ形式として… + + + Copy packet bytes as a hex dump. + 16進数ダンプとしてパケットバイト列をコピー + + + …as MIME Data + MIMEデータとして... + + + …as C String + C言語文字列として... + + + Copy packet bytes as printable ASCII characters and escape sequences. + 印刷可能なASCII文字とエスケープシーケンスとしてパケットバイト列をコピー + + + …as a Hex Stream + 16進数ストリームとして… + + + Copy packet bytes as a stream of hex. + 16進数ストリームとしてパケットバイト列をコピー + + + …as a Base64 String + BASE64文字列として... + + + Copy packet bytes as a base64 encoded string. + BASE64エンコードされた文字列としてパケットバイト列をコピー + + + Copy packet bytes as application/octet-stream MIME data. + MIME形式データ(application/octet-stream)としてパケットバイト列をコピー + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + プロトコルに対するダイセクターの振る舞いを変更します + + + Remove this dissection behavior. + このダイセクターの振る舞いを削除します + + + Copy this dissection behavior. + このダイセクターの振る舞いをコピーします + + + Clear all dissection behaviors. + すべてのダイセクタの振る舞いをクリア + + + Decode As… + …としてデコード + + + Open + 開く + + + + DecodeAsModel + + Match using this field + このフィールドを使って照合 + + + Change behavior when the field matches this value + フィールドがこの値に一致した場合に動作を変更します + + + Field value type (and base, if Integer) + フィールド値の型(整数の場合は基数) + + + Current"Decode As" behavior + 現在の "...としてデコード" の振る舞い + + + Default "Decode As" behavior + デフォルトの "...としてデコード " の振る舞い + + + String + 文字列 + + + Integer, base + 整数型, 基数 + + + unknown + 不明 + + + <none> + <none> + + + GUID + GUID + + + Field + フィールド + + + Value + + + + Type + 種別 + + + Default + デフォルト + + + Current + 現在 + + + + DisplayFilterCombo + + Display filter selector + 表示フィルタの選択 + + + Select from previously used filters. + 前に使ったフィルタから選択 + + + + DisplayFilterEdit + + Display filter entry + 表示フィルタエントリ + + + Manage saved bookmarks. + 保存したブックマークを管理 + + + Display Filter Expression… + 表示フィルタ式... + + + Apply a display filter %1 <%2/> + 表示フィルタ %1 <%2/> を適用 + + + Enter a display filter %1 + 表示フィルタ %1 を入力 + + + Clear display filter + 表示フィルタをクリア + + + Apply display filter + 表示フィルタを適用 + + + Left align buttons + ボタンを左揃え + + + Apply a read filter %1 + 読込フィルタ %1 を適用 + + + Current filter: %1 + 現在のフィルタ: %1 + + + Invalid filter: + 無効なフィルタ: + + + Save this filter + このフィルタを保存 + + + Remove this filter + このフィルタを削除 + + + Manage Display Filters + 表示フィルタを管理 + + + Filter Button Preferences... + フィルタボタン設定… + + + + DisplayFilterExpressionDialog + + Dialog + ダイアログ + + + Select a field to start building a display filter. + フィールドを選択して表示フィルタの構築を開始します。 + + + Field Name + フィールド名 + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>フィールド名の一覧を検索します</p></body></html> + + + Search: + 検索: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>リレーションはフィールドを指定した値に制限するために使うことができます。それぞれのリレーションは以下のように動きます:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">は 現在の値</span></p></td><td><p>このフィールドに含まれるいずれかのパケットに適合する</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, 等々</span></p></td><td><p>フィールドを指定した値と比較する</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>文字列に対してフィールドをチェックする (contains) または 正規表現 (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>フィールドを指定した一連の値と比較する</p></td></tr></table></body></html> + + + + + Relation + リレーション + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + デフォルトでは比較演算子と contains/matches/in 関係演算子はいずれかの値が一致した場合に真になります。数量詞 "all" はフレーム内の全ての値に適用するテストに使えます。 + + + Quantifier + 数量 + + + Any + いずれかの + + + All + すべて + + + Match against this value. + この値に合致します + + + Value + + + + If the field you have selected has a known set of valid values they will be listed here. + 選択したフィールドに有効な値があるとここに一覧表示されます + + + Predefined Values + 事前に定義された値 + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + 選択したフィールドがバイト範囲に収まる場合(プロトコルを選んだときなど)、ここで合致するバイトの範囲制限できます + + + Range (offset:length) + 範囲 (位置:長さ) + + + No display filter + 表示フィルタなし + + + <small><i>A hint.</i></small> + <small><i>ヒント</i></small> + + + Display Filter Expression + 表示フィルタ式 + + + Select a field name to get started + 開始するフィールド名を選択 + + + Click OK to insert this filter + OKをクリックしてこのフィルタを挿入 + + + + DissectorSyntaxLineEdit + + Dissector entry + ダイセクタ登録 + + + Enter a dissector %1 + ダイセクタ %1 を入力 + + + + DissectorTablesDialog + + Dialog + ダイアログ + + + Search: + 検索: + + + Dissector Tables + ダイセクター表 + + + + DissectorTablesProxyModel + + Table Type + 表の種別 + + + String + 文字列 + + + Dissector Description + ダイセクタの説明 + + + Integer + 整数型 + + + Protocol + プロトコル + + + Short Name + 短い名前 + + + Table Name + 表の名前 + + + Selector Name + セレクタ名 + + + + EnabledProtocolsDialog + + Dialog + ダイアログ + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>プロトコルを無効にすることで上位レイヤのプロトコルが表示されるのを防ぎます</i></small> + + + Search: + 検索: + + + in + in + + + Enable All + すべて有効 + + + Disable All + すべて無効 + + + Invert + 反転 + + + Enabled Protocols + 有効にしたプロトコル + + + Everywhere + すべて + + + Only Protocols + プロトコルのみ + + + Only Description + 記述のみ + + + Only enabled protocols + 有効にしたプロトコルのみ + + + Only disabled protocols + 無効にしたプロトコルのみ + + + any protocol + すべてのプロトコル + + + non-heuristic protocols + 非ヒューリスティックプロトコル + + + heuristic protocols + ヒューリスティックプロトコル + + + + EnabledProtocolsModel + + Protocol + プロトコル + + + Description + 記述 + + + + EndpointDataModel + + Address + アドレス + + + Port + ポート + + + Packets + パケット + + + Bytes + バイト + + + Tx Packets + 送信パケット + + + Tx Bytes + 送信バイト + + + Rx Packets + 受信パケット + + + Rx Bytes + 受信バイト + + + Country + + + + City + 都市 + + + Latitude + 経度 + + + Longitude + 緯度 + + + AS Number + AS番号 + + + AS Organization + AS 組織 + + + Total Packets + 全パケット + + + Percent Filtered + フィルタされた割合 + + + + EndpointDialog + + Map + 地図 + + + Draw IPv4 or IPv6 endpoints on a map. + IPv4またはIPv6の端末を地図に描画 + + + Open in browser + ブラウザで開く + + + Save As… + ...として保存 + + + Map file error + 地図ファイルエラー + + + Save Endpoints Map + 端末の地図を保存します + + + Failed to save map file %1. + 地図ファイル %1 を保存できませんでした + + + + EthernetAddressModel + + Type + 種別 + + + Name + 名前 + + + Address + アドレス + + + All entries + すべてのエントリ + + + Hosts + ホスト + + + Ethernet Addresses + Ethernetアドレス + + + Ethernet Manufacturers + Ethernet製造元 + + + Ethernet Well-Known Addresses + Ethernet既知のアドレス + + + + ExpertInfoDialog + + Dialog + ダイアログ + + + <small><i>A hint.</i></small> + <small><i>ヒント</i></small> + + + Limit to Display Filter + 表示フィルタに制限 + + + Group by summary + 概要ごとにグループ + + + Search expert summaries. + エキスパートサマリを検索します + + + Search: + 検索: + + + Show… + Show... + 表示… + + + Error + エラー + + + Show error packets. + エラーパケットを表示します + + + Warning + 警告 + + + Show warning packets. + 警告パケットを表示します + + + Note + 注意 + + + Show note packets. + 注意パケットを表示します + + + Chat + チャット + + + Show chat packets. + チャットパケットを表示します + + + Comment + コメント + + + Show comment packets. + コメントパケットを表示します + + + Expert Information + エキスパート情報 + + + Collapse All + すべて閉じる + + + Expand All + すべて展開 + + + Capture file closed. + キャプチャファイルを閉じました + + + No display filter + 表示フィルタなし + + + No display filter set. + 表示フィルタが設定されていません + + + Limit information to "%1". + 情報を "%1"に制限します + + + Display filter: "%1" + 表示フィルタ: "%1" + + + + ExpertInfoProxyModel + + Packet + パケット + + + Severity + 重要度 + + + Summary + 概要 + + + Group + グループ + + + Protocol + プロトコル + + + Count + カウント + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + エキスパートパケット解析 + + + Export As: + Export as: + としてエクスポート: + + + Plain text (*.txt) + プレインテキスト (*.txt) + + + Comma Separated Values - summary (*.csv) + カンマ区切りテキスト - 概要 (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML形式 - 概要 (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PSML形式 - 詳細 (*.psml, *.xml) + + + JSON (*.json) + JSON形式 (*.json) + + + C Arrays - bytes (*.c, *.h) + C言語配列 - バイト列 (*.c, *.h) + + + + ExportObjectDialog + + Dialog + ダイアログ + + + Content Type: + コンテントタイプ: + + + Searching for objects + オブジェクトを探索中 + + + Text Filter: + テキストフィルタ: + + + Only display entries containing this string + この文字列を含むエントリのみ表示します + + + Preview + プレビュー + + + All Content-Types + すべてのコンテントタイプ + + + Export + エクスポート + + + %1 object list + %1 オブジェクト一覧 + + + Save Object As… + …としてオブジェクトを保存 + + + Save All Objects In… + ...の中にすべてのオブジェクトを保存 + + + + ExportObjectModel + + Packet + パケット + + + Hostname + ホスト名 + + + Content Type + コンテントタイプ + + + Size + サイズ + + + Filename + ファイル名 + + + + ExportPDUDialog + + Dialog + ダイアログ + + + Display filter: + 表示フィルタ: + + + + ExtArgSelector + + Reload data + データを再読み込み + + + + ExtcapArgumentFileSelection + + Clear + クリア + + + All Files ( + すべてのファイル( + + + Open File + ファイルを開く + + + Select File + ファイルを選択 + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + インターフェース オプション + + + Start + 開始 + + + Save + 保存 + + + Default + デフォルト + + + Restore default value of the item + デフォルト値を復元します + + + Extcap Help cannot be found + Extcap ヘルプを見つけることができません + + + The help for the extcap interface %1 cannot be found. Given file: %2 + extcap インターフェース %1 のヘルプを見つけることができません。与えられたファイル: %2 + + + Save parameter(s) on capture start + キャプチャ開始時にパラメタを保存します + + + + FieldFilterEdit + + Display filter entry + 表示フィルタエントリ + + + Enter a field %1 + フィールド %1 を入力 + + + Invalid filter: + 無効なフィルタ: + + + + FileSetDialog + + Dialog + ダイアログ + + + Directory: + ディレクトリ: + + + No files in Set + セットにファイルがありません + + + No capture loaded + 何もキャプチャが読み込まれていません + + + %Ln File(s) in Set + %1 File%2 in Set + + セット内の %Ln ファイル + + + + + FilesetEntryModel + + Open this capture file + このキャプチャファイルを開く + + + Filename + ファイル名 + + + Created + 作成日 + + + Modified + 修正日 + + + Size + サイズ + + + + FilterAction + + Selected + 選択済 + + + Not Selected + 未選択 + + + …and Selected + …かつ選択内容と一致 + + + …or Selected + …または選択内容と一致 + + + …and not Selected + …かつ選択内容と不一致 + + + …or not Selected + …または選択内容と不一致 + + + + FilterDialog + + Dialog + ダイアログ + + + Create a new filter. + 新規フィルタを作成します + + + Remove this filter. + Remove this profile. + このプロファイルを削除します + + + Copy this filter. + Copy this profile. + このプロファイルをコピーします + + + Capture Filters + キャプチャフィルタ + + + Display Filters + 表示フィルタ + + + Open + 開く + + + New capture filter + This text is automatically filled in when a new filter is created + 新規キャプチャフィルタ + + + New display filter + This text is automatically filled in when a new filter is created + 新規表示フィルタ + + + + FilterExpressionFrame + + Frame + フレーム + + + Filter Buttons Preferences… + フィルタボタン設定… + + + Label: + ラベル: + + + Enter a description for the filter button + フィルタボタンの記述を入力します + + + Filter: + フィルタ: + + + Enter a filter expression to be applied + 適用されるフィルタ書式を入力します + + + Comment: + コメント: + + + Enter a comment for the filter button + フィルタボタンのコメントを入力します + + + Missing label. + 見つからないラベル + + + Missing filter expression. + 見つからないフィルタ書式 + + + Invalid filter expression. + 無効なフィルタ式 + + + + FilterExpressionToolBar + + Filter Button Preferences... + フィルタボタン設定… + + + Edit + 編集 + + + Disable + 無効 + + + Remove + 削除 + + + + FilterListModel + + Filter Name + フィルタ名 + + + Filter Expression + フィルタ式 + + + + FindLineEdit + + Textual Find + テキストで検索 + + + Regular Expression Find + 正規表現で検索 + + + + FirewallRulesDialog + + Create rules for + に対してルールを作成 + + + Inbound + 入力 + + + Deny + 否定 + + + Firewall ACL Rules + ファイアウォール ACL ルール + + + Copy + コピー + + + IPv4 source address. + IPv4 送信元アドレス + + + IPv4 destination address. + IPv4 宛先アドレス + + + Source port. + 送信元ポート + + + Destination port. + 宛先ポート + + + IPv4 source address and port. + IPv4 送信元アドレスとポート + + + IPv4 destination address and port. + IPv4 宛先アドレスとポート + + + MAC source address. + 送信元MACアドレス + + + MAC destination address. + 宛先MACアドレス + + + Text file (*.txt);;All Files ( + テキストファイル (*.txt);;すべてのファイル ( + + + Warning + 警告 + + + Unable to save %1 + %1を保存できません + + + + FolderListModel + + "File" dialogs + "ファイル" ダイアログ + + + capture files + キャプチャファイル + + + Temp + 一時的 + + + untitled capture files + 名称未設定キャプチャファイル + + + Personal configuration + 個人設定 + + + Global configuration + グローバル設定 + + + dfilters, preferences, ethers, … + dfilters, preferences, ethers, … + + + dfilters, preferences, manuf, … + dfilters, preferences, manuf, … + + + System + システム + + + ethers, ipxnets + ethers, ipxnets + + + Program + プログラム + + + program files + プログラムファイル + + + Personal Plugins + 個人プラグイン + + + binary plugins + バイナリプラグイン + + + Global Plugins + グローバルプラグイン + + + Personal Lua Plugins + 個人Luaプラグイン + + + Global Lua Plugins + グローバルLuaプラグイン + + + Lua scripts + Luaスクリプト + + + Personal Extcap path + 個人Extcapパス + + + external capture (extcap) plugins + 外部キャプチャ(extcap)プラグイン + + + Global Extcap path + グローバルExtcapパス + + + MaxMind DB path + MaxMind DB パス + + + MaxMind DB database search path + MaxMind DB データベース検索パス + + + MIB/PIB path + MIB/PIB パス + + + SMI MIB/PIB search path + SMI MIB/PIB 検索パス + + + macOS Extras + macOS エキストラ + + + Extra macOS packages + エキストラ macOS パッケージ + + + Name + 名前 + + + Location + 場所 + + + Typical Files + 典型的なファイル + + + + FollowStreamAction + + %1 Stream + %1 ストリーム + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + このストリームを除外します + + + Print + 印刷 + + + %Ln client pkt(s), + + %Ln クライアントパケット, + + + + %Ln server pkt(s), + + %Ln サーバパケット, + + + + ASCII + ASCII形式 + + + C Arrays + C言語配列形式 + + + EBCDIC + EBCDIC形式 + + + Hex Dump + 16進数ダンプ形式 + + + UTF-8 + UTF-8形式 + + + YAML + YAML形式 + + + Raw + Raw(無加工)形式 + + + Save as… + …として保存 + + + Back + 戻る + + + Packet %1. + パケット %1 + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">クライアント</span> パケット, + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">サーバ</span> パケット, + + + + %Ln turn(s). + + %Ln ターン + + + + Click to select. + クリックして選択します + + + Regex Find: + 正規表現検索: + + + No capture file. + キャプチャファイルがありません + + + Please make sure you have a capture file opened. + キャプチャファイルを開いていないか確認してください + + + Error following stream. + ストリーム追跡のエラー + + + Capture file invalid. + キャプチャファイルが無効です + + + Please make sure you have a %1 packet selected. + パケット %1 を選択したか確認してください + + + %1 stream not found on the selected packet. + 選択したパケットに %1 ストリームは見つかりませんでした + + + Entire conversation (%1) + 全体の対話 (%1) + + + Follow %1 Stream (%2) + %1 ストリーム (%2)を追跡 + + + Error creating filter for this stream. + このストリームのフィルタ作成エラー + + + Save Stream Content As… + …としてストリーム内容を保存 + + + [Stream output truncated] + [ストリーム出力が切り詰められました] + + + %Ln total stream(s). + + %Ln 全ストリーム + + + + Max sub stream ID for the selected stream: %Ln + + 選択したストリーム: %Ln の最大のサブストリームID + + + + File closed. + ファイルを閉じました。 + + + Follow Stream + ストリームを追跡 + + + Hint. + ヒント + + + Show data as + Show and save data as + としてデータを表示 + + + Stream + ストリーム + + + Substream + サブストリーム + + + Find: + 検索: + + + Find &Next + 次を検索(&N) + + + + FontColorPreferencesFrame + + Frame + フレーム + + + Main window font: + メインウインドウのフォント: + + + Select Font + フォントの選択 + + + Colors: + 色: + + + System Default + システム デフォルト + + + Solid + Solid + + + Sample ignored packet text + 無視されたパケットテキストのサンプル + + + Sample marked packet text + マークされたパケットテキストのサンプル + + + Sample active selected item + アクティブな選択済み項目サンプル + + + Style: + スタイル: + + + Gradient + 勾配 + + + Sample inactive selected item + 非アクティブな選択済み項目サンプル + + + Sample "Follow Stream" client text + "ストリーム追跡"クライアントテキストのサンプル + + + Sample "Follow Stream" server text + "ストリーム追跡"サーバテキストのサンプル + + + Sample valid filter + 有効フィルタのサンプル + + + Sample invalid filter + 無効フィルタのサンプル + + + Sample warning filter + Sample deprecated filter + 非推奨フィルタのサンプル + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + サンプルのGIF形式のクエリパケットは巨大なウインドウサイズを持ちます。 + + + Lazy badgers move unique waxy jellyfish packets + Geraldさんが作成したpangram すべての文字を1文字使う回文 Lazy badgers move unique waxy jellyfish packets. + + + Font + フォント + + + + FunnelStringDialog + + Dialog + ダイアログ + + + + FunnelTextDialog + + Dialog + ダイアログ + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>何かテキストや正規表現を入力してください。上に強調表示されます。</p></body></html> + + + Highlight: + 強調表示: + + + + GsmMapSummaryDialog + + Dialog + ダイアログ + + + GSM MAP Summary + GSM MAP概要 + + + File + ファイル + + + Name + 名前 + + + Length + 長さ + + + Format + フォーマット + + + Snapshot length + スナップショット長 + + + Data + データ + + + First packet + 最初のパケット + + + Last packet + 最後のパケット + + + Elapsed + 経過 + + + Packets + パケット + + + Invokes + インボーク + + + Total number of Invokes + インボークの総数 + + + Average number of Invokes per second + 平均インボーク数毎秒 + + + Total number of bytes for Invokes + インボークの全バイト数 + + + Average number of bytes per Invoke + 平均バイト数毎インボーク + + + Return Results + リターン結果 + + + Total number of Return Results + リターン結果の総数 + + + Average number of Return Results per second + 平均リターン結果数毎秒 + + + Total number of bytes for Return Results + リターン結果の全バイト数 + + + Average number of bytes per Return Result + 平均バイト数毎リターン結果 + + + Totals + 総数 + + + Total number of GSM MAP messages + GSM MAPメッセージの総数 + + + Average number of GSM MAP messages per second + 平均GSM MAPメッセージ数毎秒 + + + Total number of bytes for GSM MAP messages + GSM MAPメッセージの全バイト数 + + + Average number of bytes per GSM MAP message + GSM MAPメッセージの平均バイト数 + + + + IOConsoleDialog + + Dialog + ダイアログ + + + Enter code + コードを入力 + + + Evaluate + 評価 + + + Clear + クリア + + + Use %1 to evaluate. + %1 を用いて評価 + + + + IOGraphDialog + + Dialog + ダイアログ + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>価値のあり、すばらしく、時間を節約するキーボードショートカット</h3> +<table><tbody> + +<tr><th>+</th><td>ズームイン</td></th> +<tr><th>-</th><td>ズームアウト</td></th> +<tr><th>x</th><td>X軸のズーム</td></th> +<tr><th>X</th><td>X軸のズームアウト</td></th> +<tr><th>y</th><td>Y軸のズーム</td></th> +<tr><th>Y</th><td>Y軸のズームアウト</td></th> +<tr><th>0</th><td>初期状態にグラフをリセット</td></th> + +<tr><th>→</th><td>右に10ピクセル移動</td></th> +<tr><th>←</th><td>左に10ピクセル移動</td></th> +<tr><th>↑</th><td>上に10ピクセル移動</td></th> +<tr><th>↓</th><td>下に10ピクセル移動</td></th> +<tr><th><i>Shift+</i>→</th><td>右に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>←</th><td>左に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>↑</th><td>上に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>↓</th><td>下に1ピクセル移動</td></th> + +<tr><th>g</th><td>カーソル下のパケットへ移動</td></th> + +<tr><th>z</th><td>マウスドラッグ/ズーム(切替)</td></th> +<tr><th>t</th><td>キャプチャ/セッション(切替)</td></th> +<tr><th>Space</th><td>クロスヘア(切替)</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + このグラフを削除します + + + Add a new graph. + 新規グラフを追加します + + + Duplicate this graph. + このグラフを複製します + + + Clear all graphs. + すべてのグラフをクリア + + + Move this graph upwards. + このグラフを上に移動します + + + Move this graph downwards. + このグラフを下に移動します + + + Mouse + マウス + + + Drag using the mouse button. + マウスボタンを使ってドラッグします + + + drags + ドラッグ + + + Select using the mouse button. + マウスボタンを使って選択します + + + zooms + ズーム + + + Interval + インターバル + + + Time of day + 時刻 + + + Log scale + ログのスケール + + + Automatic update + 自動アップデート + + + Enable legend + 凡例を有効化 + + + Reset + リセット + + + Reset Graph + グラフをリセット + + + Reset the graph to its initial state. + グラフを初期状態にリセットします + + + 0 + 0 + + + Zoom In + 拡大 + + + + + + + + + Zoom Out + 縮小 + + + - + - + + + Move Up 10 Pixels + 上に10ピクセル移動 + + + Up + + + + Move Left 10 Pixels + 左に10ピクセル移動 + + + Left + + + + Move Right 10 Pixels + 右に10ピクセル移動 + + + Right + + + + Move Down 10 Pixels + 下に10ピクセル移動 + + + Down + + + + Move Up 1 Pixel + 上に1ピクセル移動 + + + Shift+Up + Shift+上 + + + Move Left 1 Pixel + 左に1ピクセル移動 + + + Shift+Left + Shift+左 + + + Move Right 1 Pixel + 1ピクセル右に移動 + + + Shift+Right + Shift+右 + + + Move Down 1 Pixel + 下に1ピクセル移動 + + + Move down 1 Pixel + Move down 1 pixel + 下に1ピクセル移動 + + + Shift+Down + Shift+下 + + + Go To Packet Under Cursor + カーソルの位置のパケットに移動 + + + Go to packet currently under the cursor + 現在カーソルの位置のパケットに移動 + + + G + G + + + Drag / Zoom + ドラッグ / ズーム + + + Toggle mouse drag / zoom behavior + マウスのドラッグ/ズームの動作を切り替えます + + + Z + Z + + + Capture / Session Time Origin + キャプチャ/セッション時間の起点 + + + Toggle capture / session time origin + キャプチャ/セッション時間の起点を切り替え + + + T + T + + + Crosshairs + 十字カーソル + + + Toggle crosshairs + 十字カーソルの表示を切り替えます + + + Space + スペース + + + Zoom In X Axis + X軸をズーム + + + X + X + + + Zoom Out X Axis + X軸をズームアウト + + + Shift+X + Shift+X + + + Zoom In Y Axis + Y軸をズーム + + + Y + Y + + + Zoom Out Y Axis + Y軸をズームアウト + + + Shift+Y + Shift+Y + + + 1 sec + 1秒 + + + 10 sec + 10秒 + + + 1 min + 1分 + + + 10 min + 10分 + + + Time (s) + 時間(秒) + + + I/O Graphs + 入出力グラフ + + + Save As… + ...として保存 + + + Copy + コピー + + + Copy graphs from another profile. + 別のプロファイルからグラフをコピーします + + + 1 ms + 1ミリ秒 + + + 2 ms + 2 ミリ秒 + + + 5 ms + 5ミリ秒 + + + 10 ms + 10ミリ秒 + + + 20 ms + 20 ミリ秒 + + + 50 ms + 50 ミリ秒 + + + 100 ms + 100ミリ秒 + + + 200 ms + 200 ミリ秒 + + + 500 ms + 500 ミリ秒 + + + 2 sec + 2 秒 + + + 5 sec + 5 秒 + + + Wireshark I/O Graphs: %1 + Wireshark入出力グラフ: %1 + + + Filtered packets + フィルタされたパケット + + + Filtered events + フィルタされたイベント + + + All Packets + すべてのパケット + + + TCP Errors + TCPエラー + + + All Events + すべてのイベント + + + Access Denied + アクセス拒否 + + + Hover over the graph for details. + 詳細はグラフの上にマウスをのせてください + + + No packets in interval + この間隔にパケットはありません + + + No events in interval + この間隔にイベントはありません + + + Click to select packet + クリックしてパケットを選んでください + + + Packet + パケット + + + Click to select event + クリックしてイベントを選んでください + + + Event + イベント + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 離してズーム, x = %1 から %2, y = %3 から %4 + + + Unable to select range. + 範囲を選択できません + + + Click to select a portion of the graph. + クリックしてグラフの割合を選びます + + + Portable Document Format (*.pdf) + PDF形式 (*.pdf) + + + Portable Network Graphics (*.png) + PNG形式 (*.png) + + + Windows Bitmap (*.bmp) + Windowsビットマップ形式 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG形式 (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + カンマ区切りテキスト形式 (*.csv) + + + Save Graph As… + ...としてグラフを保存 + + + + Iax2AnalysisDialog + + Dialog + ダイアログ + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + + + Forward + 進む + + + Packet + パケット + + + Delta (ms) + 間隔 (ms) + + + Jitter (ms) + ジッタ (ms) + + + Bandwidth + 帯域 + + + Status + 状態 + + + Length + 長さ + + + Reverse + 反転 + + + Graph + グラフ + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>順方向ジッタ値を表示または非表示</p></body></html> + + + Forward Jitter + 順方向ジッタ + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>順方向差異値を表示もしくは非表示にします</p></body></html> + + + Forward Difference + 順方向差値 + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>逆方向ジッタ値を表示もしくは非表示にします</p></body></html> + + + Reverse Jitter + 逆方向ジッタ + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>逆方向差値を表示もしくは非表示にします</p></body></html> + + + Reverse Difference + 逆方向差値 + + + <small><i>A hint.</i></small> + <small><i>ヒント</i></small> + + + Audio + 音声 + + + Save the audio data for both channels. + 両チャンネルの音声データを保存します + + + Forward Stream Audio + 順方向ストリーム音声 + + + Save the forward stream audio data. + 順方向ストリーム音声データを保存します + + + Reverse Stream Audio + 逆方向ストリーム音声 + + + Save the reverse stream audio data. + 逆方向ストリーム音声データを保存します + + + CSV + CSV形式 + + + Save both tables as CSV. + CSV形式として両方の表を保存します + + + Forward Stream CSV + 順方向ストリーム CSV + + + Save the forward table as CSV. + 順方向の表をCSVとして保存します + + + Reverse Stream CSV + 逆方向ストリームCSV + + + Save the reverse table as CSV. + 逆方向の表をCSVとして保存します + + + Save Graph + グラフを保存 + + + Save the graph image. + グラフ画像を保存します + + + Go to Packet + パケットに移動 + + + Select the corresponding packet in the packet list. + パケット一覧から関連するパケットを選択します + + + G + G + + + Next Problem Packet + 次の問題パケット + + + Go to the next problem packet + 次の問題パケットへ移動します + + + N + N + + + IAX2 Stream Analysis + IAX2ストリーム分析 + + + Unable to save RTP data. + RTPデータを保存できません。 + + + Please select an IAX2 packet. + IAX2パケットを選んでください + + + G: Go to packet, N: Next problem packet + G: パケットに移動, N: 次の問題パケット + + + Portable Document Format (*.pdf) + PDF形式 (*.pdf) + + + Portable Network Graphics (*.png) + PNG形式 (*.png) + + + Windows Bitmap (*.bmp) + Windowsビットマップ形式 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG形式 (*.jpeg *.jpg) + + + Save Graph As… + ...としてグラフを保存 + + + Can't save in a file: Wrong length of captured packets. + ファイルに保存できません:キャプチャされたパケットの長さが違います + + + Can't save in a file: File I/O problem. + ファイルに保存できません:ファイル入出力問題です + + + Save forward stream audio + 順方向ストリーム音声を保存 + + + Save reverse stream audio + 逆方向のストリーム音声を保存 + + + Save audio + 音声を保存 + + + Sun Audio (*.au) + Sunオーディオ形式 (*.au) + + + ;;Raw (*.raw) + ;;Raw(無加工)形式 (*.raw) + + + Warning + 警告 + + + Unable to save in that format + そのフォーマットで保存できません + + + Unable to save %1 + %1を保存できません + + + Saving %1… + %1 を保存中… + + + Analyzing IAX2 + IAX2を解析中 + + + Save forward stream CSV + 順方向ストリームCSVを保存 + + + Save reverse stream CSV + 逆方向ストリームCSVを保存 + + + Save CSV + CSVを保存 + + + Comma-separated values (*.csv) + カンマ区切りテキスト形式 (*.csv) + + + + ImportTextDialog + + File: + ファイル: + + + Set name of text file to import + インポートするテキストファイルの名前を指定します + + + Browse for text file to import + インポートするテキストファイルを参照します + + + Browse… + Browse... + 参照… + + + Hex Dump + 16進数ダンプ + + + Import a standard hex dump as exported by Wireshark + Wiresharkによってエクスポートされた標準16進数ダンプをインポート + + + Offsets in the text file are in octal notation + テキストファイルのオフセットは8進数表記です + + + Octal + 8進数 + + + Offsets: + オフセット: + + + Offsets in the text file are in hexadecimal notation + テキストファイルのオフセットは16進数表記です + + + Hexadecimal + 16進数 + + + Offsets in the text file are in decimal notation + テキストファイルのオフセットは10進数表記です + + + Decimal + 10進数 + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>たとえ16進数バイト列のように見えても16進数+ASCII行の末尾にてASCII表現の開始を検出する追加処理をするかどうか。</p><p>16進数ダンプにASCIIを含まない場合には有効にしません。</html> + + + ASCII identification: + ASCII 識別: + + + Regular Expression + 正規表現 + + + Import a file formatted according to a custom regular expression + カスタム正規表現によってフォーマットされたファイルをインポート + + + Packet format regular expression + パケット形式正規表現 + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + <html><head/><body><p>インポートするデータを識別する名前付きグループとともにファイルにある1つのパケットをキャプチャするPerl互換正規表現 アンカー、 ^ および $ 記号も新規の行の前後に合致します。 </p><p>必須はデータグループのみ、またtime,dirおよびseqnoもサポートされます。</p><p>Regex フラグ: DUPNAMES, MULTILINE および NOEMPTY</p></body></html> + + + This is regexHintLabel, it will be set to default_regex_hint + これは regexHintLabel です。default_regex_hint に設定されます + + + Data encoding: + データエンコーディング: + + + How data is encoded + どのようにデータがエンコードされたか + + + encodingRegexExample + エンコード正規表現例 + + + List of characters indicating incoming packets + 入力パケットを示す文字列 + + + iI< + iI< + + + List of characters indicating outgoing packets + 出力パケットを示す文字列 + + + oO> + oO> + + + Timestamp format: + タイムスタンプ形式: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + ファイルにパケットの向き(入力や出力)を示す情報が含まれていてもいなくても + + + Direction indication: + 方向指示: + + + ExportPDU + エキスポートPDU + + + IP version: + IP バージョン: + + + Interface name: + インターフェース名: + + + The name of the interface to write to the import capture file + インポートしたキャプチャファイルに書き込むインターフェースの名前 + + + Fake IF, Import from Hex Dump + フェイクインターフェース, 16進ダンプからインポート + + + Maximum frame length: + 最大フレーム長: + + + Encapsulation + カプセル化 + + + The text file has no offset + テキストファイルにはオフセットがありません + + + None + なし + + + <small><i>recommended regex:</small></i> + <small><i>推奨された正規表現:</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + テキストファイルのタイムスタンプを解析する形式(例 %H:%M:%S) 形式の仕様はstrptime(3)に基づきます + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + <html><head/><body><p>テキストファイルのタイムスタンプを解析する際の形式(例 %H:%M:%S.%f)</p><p> 形式の仕様はstrptime(3)に %fでコンマ秒を加えた仕様に基づきます %fの精度はその長さにより決まります。</p></body></html> + + + %H:%M:%S.%f + %H:%M:%S.%f + + + timestampExampleLabel + タイムスタンプラベル例 + + + Encapsulation Type: + カプセル化種別: + + + Encapsulation type of the frames in the import capture file + インポートしたキャプチャファイルのフレームのカプセル化の種別 + + + Prefix each frame with an Ethernet and IP header + EthernetとIPヘッダをもつ各々のフレームのプレフィックス + + + IP + IP + + + Prefix each frame with an Ethernet, IP and UDP header + EthernetとIPとUDPヘッダをもつ各々のフレームのプレフィックス + + + Prefix each frame with an Ethernet, IP and TCP header + EthernetとIPとTCPヘッダをもつ各々のフレームのプレフィックス + + + Prefix each frame with an Ethernet, IP and SCTP header + EthernetとIPとSCTPヘッダをもつ各々のフレームのプレフィックス + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + EthernetとIPとSCTP (DATA)ヘッダをもつ各々のフレームのプレフィックス + + + Source address: + 送信元アドレス: + + + Destination address: + 宛先アドレス: + + + Dissector + ダイセクタ + + + The IP protocol ID for each frame + 各々のフレームのIPプロトコルID + + + The IP source address for each frame + 各々のフレームの送信元IPアドレス + + + The IP destination address for each frame + 各々のフレームのIP宛先アドレス + + + The UDP, TCP or SCTP source port for each frame + フレーム毎のUDP/TCP/SCTP送信元ポート + + + The SCTP DATA payload protocol identifier for each frame + フレーム毎のSCTPデータペイロードのプロトコル識別子 + + + The UDP, TCP or SCTP destination port for each frame + フレーム毎のUDP/TCP/SCTP宛先ポート + + + Prefix each frame with an Ethernet header + 各フレームの前にEthernetヘッダを付加します + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + プロトコル(10進数): + + + Leave frames unchanged + フレームを変更しない + + + No dummy header + ダミーヘッダなし + + + Tag: + タグ: + + + UDP + UDP + + + Source port: + 送信元ポート: + + + The Ethertype value of each frame + 各フレームのイーサタイプの値 + + + TCP + TCP + + + The SCTP verification tag for each frame + 各フレームのSCTP検証タグ + + + Destination port: + 宛先ポート: + + + Ethertype (hex): + イーサタイプ(16進数): + + + SCTP (Data) + SCTP(データ) + + + The dissector to use for each frame + 各フレームに用いるダイセクタ + + + The IP Version to use for the dummy IP header + ダミーIPヘッダーに用いるIPバージョン + + + The maximum size of the frames to write to the import capture file (max 256kiB) + インポートするキャプチャファイルへ書き込む最大のフレームサイズ(最大256kB) + + + Supported fields are data, dir, time, seqno + サポートされているフィールドは data, dir, time, seqno です + + + Missing capturing group data (use (? + キャプチャグループデータが不明(使用中?) + + + Import From Hex Dump + 16進数ダンプからインポート + + + Import + インポート + + + Import Text File + テキストファイルをインポート + + + + InterfaceFrame + + Frame + フレーム + + + Wired + 有線 + + + AirPCAP + AirPCAP + + + Pipe + パイプ + + + STDIN + 標準入力 + + + Bluetooth + Bluetooth + + + Wireless + 無線 + + + Dial-Up + ダイヤルアップ + + + USB + USB + + + External Capture + 外部キャプチャ + + + Virtual + 仮想 + + + Remote interfaces + リモートインターフェース + + + Show hidden interfaces + 非表示のインターフェースを表示 + + + External capture interfaces disabled. + 外部のキャプチャインターフェース無効化 + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + <p>キャプチャドライバがインストールされていないのでローカルインターフェースは利用できません。<p>以下をインストールすることで修正できます。 <a href="https://npcap.com/">Npcap</a>。</p> + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + <p>キャプチャドライバが読み込まれていないためローカル印ーフェースは利用できません。</p><p>以下を実行することで修正することができます。 もしNpcapをインストールした場合、<pre>net start npcap</pre> もしWinPcapをインストールした場合、<pre>net start npf</pre> 両方のコマンドは管理者権限として実行する必要があります。</p> + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + <p>ローカルインターフェースでキャプチャする許可がありません</p><p> <a href="file://%1">ChmodBPFをインストールすることで</a>これを修正できます。</p> + + + You don't have permission to capture on local interfaces. + ローカルインターフェースでキャプチャする許可がありません。 + + + No interfaces found. + インターフェースが見つかりません + + + Interfaces not loaded (due to preference). Go to Capture + (設定により)インタフェースがロードされませんでした。キャプチャへ移動 + + + Start capture + キャプチャ開始 + + + Hide Interface + インターフェースを隠す + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + インターフェースは表示されません。 %1 インターフェース非表示 + + + + InterfaceToolbar + + Frame + フレーム + + + Select interface + インターフェースを選択 + + + Interface + インターフェース + + + + InterfaceToolbarLineEdit + + Apply changes + 変更を適用 + + + + InterfaceTreeModel + + Show + 表示 + + + Friendly Name + フレンドリ名 + + + Interface Name + インターフェース名 + + + No interfaces found. + インターフェースが見つかりません + + + This version of Wireshark was built without packet capture support. + このバージョンの Wireshark はパケットキャプチャをサポートせずビルドされました + + + Local Pipe Path + ローカルパイプパス + + + Comment + コメント + + + Link-Layer Header + リンク層ヘッダ + + + Promiscuous + プロミスキャス + + + Snaplen (B) + キャプチャ長(バイト) + + + Buffer (MB) + バッファ(メガバイト) + + + Monitor Mode + モニタモード + + + Capture Filter + キャプチャフィルタ + + + Addresses + アドレス + + + Address + アドレス + + + Extcap interface: %1 + Extcap インターフェース: %1 + + + No addresses + アドレスなし + + + No capture filter + キャプチャフィルタなし + + + Capture filter + キャプチャフィルタ + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + LBT-RMトランスポート統計 + + + Sources + 送信元 + + + Address/Transport + アドレス/転送 + + + Data frames + データフレーム数 + + + Data bytes + データバイト数 + + + Data frames/bytes + データフレーム数/バイト数 + + + Data rate + データ速度 + + + RX data frames + 受信データフレーム数 + + + RX data bytes + 受信データバイト数 + + + RX data frames/bytes + 受信データフレーム数/バイト数 + + + RX data rate + 受信データ速度 + + + NCF frames + NCFフレーム数 + + + NCF count + NCFカウント + + + NCF bytes + NCFバイト数 + + + NCF frames/bytes + NCFフレーム数/バイト数 + + + NCF count/bytes + NCFカウント/バイト数 + + + NCF frames/count + NCFフレーム数/カウント + + + NCF frames/count/bytes + NCFフレーム数/カウント/バイト数 + + + NCF rate + NCF速度 + + + SM frames + SMフレーム数 + + + SM bytes + SMバイト数 + + + SM frames/bytes + SMフレーム数/バイト数 + + + SM rate + SM速度 + + + Show + 表示 + + + Data + データ + + + RX Data + 受信データ + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + トランスポートシーケンス番号 + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + カウント + + + Frame + フレーム + + + SQN/Reason + SQN/理由 + + + Receivers + 受信元 + + + NAK frames + NAKフレーム数 + + + NAK count + NAKカウント + + + NAK bytes + NAKバイト数 + + + NAK rate + NAK速度 + + + NAK sequence numbers for transport + トランスポートNAKシーケンス番号 + + + Display filter: + 表示フィルタ: + + + Regenerate statistics using this display filter + この表示フィルタを用いて統計を再作成 + + + Apply + 適用 + + + Copy as CSV + CSVとしてコピー + + + Copy the tree as CSV + ツリーをCSVとしてコピー + + + Copy as YAML + YAMLとしてコピー + + + Copy the tree as YAML + ツリーをYAMLとしてコピー + + + Show the data frames column + データフレーム数の列を表示 + + + Show the data bytes column + データバイト数の列を表示 + + + Show the data frames/bytes column + データフレーム数/バイト数の列を表示 + + + Show the RX data frames column + 受信データフレーム数の列を表示 + + + Show the RX data bytes column + 受信データバイト数の列を表示 + + + Show the RX data frames/bytes column + 受信データフレーム数/バイト数の列を表示 + + + Show the NCF frames column + NCFフレーム数の列を表示 + + + Show the NCF bytes column + NCFバイト数の列を表示 + + + Show the NCF count column + NCFカウントの列を表示 + + + Show the data rate column + データ速度の列を表示 + + + Show the RX data rate column + 受信データ速度の列を表示 + + + Show the NCF frames/bytes column + NCFフレーム数/バイト数の列を表示 + + + Show the NCF count/bytes column + NCFカウント/バイト数の列を表示を表示 + + + Show the NCF frames/count column + NCFフレーム数/カウントの列を表示 + + + Show the NCF frames/count/bytes column + NCFフレーム数/カウント/バイト数の列を表示 + + + Show the NCF rate column + NCF速度の列を表示 + + + Show the SM frames column + SMフレーム数の列を表示 + + + Show the SM bytes column + SMバイト数の列を表示 + + + Show the SM frames/bytes column + SMフレーム数/バイト数の列を表示 + + + Show the SM rate column + SM速度の列を表示 + + + Auto-resize columns to content + 内容にあわせて列を自動調整 + + + Resize columns to content size + 内容の大きさにあわせて列を調整 + + + LBT-RM Statistics failed to attach to tap + LBT-RM統計はタップ割り当てに失敗しました + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + LBT-RUトランスポート統計 + + + Sources + 送信元 + + + Address/Transport/Client + アドレス/トランスポート/クライアント + + + Data frames + データフレーム数 + + + Data bytes + データバイト数 + + + Data frames/bytes + データフレーム数/バイト数 + + + Data rate + データ速度 + + + RX data frames + 受信データフレーム数 + + + RX data bytes + 受信データバイト数 + + + RX data frames/bytes + 受信データフレーム数/バイト数 + + + RX data rate + 受信データ速度 + + + NCF frames + NCFフレーム数 + + + NCF count + NCFカウント + + + NCF bytes + NCFバイト数 + + + NCF frames/count + NCFフレーム数/カウント + + + NCF frames/bytes + NCFフレーム数/バイト数 + + + NCF count/bytes + NCFカウント/バイト数 + + + NCF frames/count/bytes + NCFフレーム数/カウント/バイト数 + + + NCF rate + NCF速度 + + + SM frames + SMフレーム数 + + + SM bytes + SMバイト数 + + + SM frames/bytes + SMフレーム数/バイト数 + + + SM rate + SM速度 + + + RST frames + RSTフレーム数 + + + RST bytes + RSTバイト数 + + + RST frames/bytes + RSTフレーム数/バイト数 + + + RST rate + RST速度 + + + Show + 表示 + + + Data SQN + データSQN + + + RX Data SQN + 受信データSQN + + + NCF SQN + NCF SQN + + + SM SQN + SM SQN + + + RST reason + RST 原因 + + + details for transport + トランスポートの詳細 + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + カウント + + + Frame + フレーム + + + Reason + 原因 + + + SQN/Reason + SQN/原因 + + + Receivers + 受信元 + + + Address/Transport + アドレス/トランスポート + + + NAK frames + NAKフレーム数 + + + NAK count + NAKカウント + + + NAK bytes + NAKバイト数 + + + NAK frames/count + NAKフレーム数/カウント + + + NAK count/bytes + NAKカウント/バイト数 + + + NAK frames/bytes + NAKフレーム数/バイト数 + + + NAK frames/count/bytes + NAKフレーム数/カウント/バイト数 + + + NAK rate + NAK速度 + + + ACK frames + ACKフレーム数 + + + ACK bytes + ACKバイト数 + + + ACK frames/bytes + ACKフレーム数/バイト数 + + + ACK rate + ACK速度 + + + CREQ frames + CREQフレーム数 + + + CREQ bytes + CREQバイト数 + + + CREQ frames/bytes + CREQフレーム数/バイト数 + + + CREQ rate + CREQ速度 + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + CREQリクエスト + + + Display filter: + 表示フィルタ: + + + Regenerate statistics using this display filter + この表示フィルタを使って統計を再作成 + + + Apply + 適用 + + + Copy as CSV + CSVとしてコピー + + + Copy the tree as CSV + CSVとしてツリーをコピー + + + Copy as YAML + YAMLとしてコピー + + + Copy the tree as YAML + YAMLとしてツリーをコピー + + + Show the data frames column + データフレーム数を列に表示 + + + Show the data bytes column + データバイト数を列に表示 + + + Show the data frames/bytes column + データフレーム数/バイト数を列に表示 + + + Show the data rate column + データ速度を列に表示 + + + Show the RX data frames column + 受信データフレーム数を列に表示 + + + Show the RX data bytes column + 受信データバイト数を列に表示 + + + Show the RX data frames/bytes column + 受信データフレーム数/バイト数を列に表示 + + + Show the RX data rate column + 受信データ速度を列に表示 + + + Show the NCF frames column + NCFフレーム数を列に表示 + + + Show the NCF count column + NCFカウントを列に表示 + + + Show the NCF bytes column + NCFバイト数を列に表示 + + + Show the NCF frames/bytes column + NCFフレーム数/バイト数を列に表示 + + + Show the NCF count/bytes column + NCFカウント/バイト数を列に表示 + + + Show the NCF frames/count column + NCFフレーム数/カウントを列に表示 + + + Show the NCF frames/count/bytes column + NCFフレーム数/カウント/バイト数を列を表示 + + + Show the SM frames column + SMフレーム数を列に表示 + + + Show the SM bytes column + SMバイト数を列に表示 + + + Show the SM frames/bytes column + SMフレーム数/バイト数を列に表示 + + + Show the SM rate column + SM速度を列に表示 + + + Show the RST frames column + RSTフレーム数を列に表示 + + + Show the RST bytes column + RSTバイト数を列に表示 + + + Show the RST frames/bytes column + RSTフレーム数/バイト数を列に表示 + + + Show the RST rate column + RST速度を列に表示 + + + Show the NAK frames column + NAKフレーム数を列に表示 + + + Show the NAK count column + NAKカウントを列に表示 + + + Show the NAK bytes column + NAKバイト数を列に表示 + + + Show the NAK frames/count column + NAKフレーム数/カウントを列に表示 + + + Show the NAK count/bytes column + NAKカウント/バイト数を列に表示 + + + Show the NAK frames/bytes column + NAKフレーム数/バイト数を列に表示 + + + Show the NAK frames/count/bytes column + NAKフレーム数/カウント/バイト数を列に表示 + + + Show the NAK rate column + NAK速度を列に表示 + + + Show the ACK frames column + ACKフレーム数を列に表示 + + + Show the ACK bytes column + ACKバイト内容を列に表示 + + + Show the ACK frames/bytes column + ACKフレーム数/バイト数を列に表示 + + + Show the ACK rate column + ACK速度を列に表示 + + + Show the CREQ frames column + CREQフレーム数を列に表示 + + + Show the CREQ bytes column + CREQバイト数を列に表示 + + + Show the CREQ frames/bytes column + CREQフレーム数/バイト数を列に表示 + + + Show the CREQ rate column + CREQ速度を列に表示 + + + Auto-resize columns to content + 内容に合わせて列を自動調整 + + + Resize columns to content size + 内容の大きさに合わせて列を調整 + + + Show the NCF rate column + NCF速度を列に表示 + + + LBT-RU Statistics failed to attach to tap + LBT-RU統計はタップへの割り当てに失敗しました + + + + LBMStreamDialog + + Dialog + ダイアログ + + + Stream + ストリーム + + + Endpoint A + 終端 A + + + Endpoint B + 終端 B + + + Messages + メッセージ + + + Bytes + バイト + + + First Frame + 最初のフレーム + + + Last Frame + 最後のフレーム + + + Display filter: + 表示フィルタ: + + + Regenerate statistics using this display filter + 表示フィルタを用いて統計を再作成 + + + Apply + 適用 + + + Copy as CSV + CSVとしてコピー + + + Copy the tree as CSV + ツリーをCSVとしてコピー + + + Copy as YAML + YAMLとしてコピー + + + Copy the tree as YAML + ツリーをYAMLとしてコピー + + + LBM Stream failed to attach to tap + LVMストリームをタップに割り当てるのに失敗しました + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + + + + %Ln item(s) + + %Ln item + + + + + LayoutPreferencesFrame + + Frame + フレーム + + + Pane 1: + 表示部1: + + + Packet List + パケット一覧 + + + Packet Details + パケット詳細 + + + Packet Bytes + パケットバイト列 + + + Packet Diagram + パケットダイアグラム + + + None + なし + + + Pane 2: + 表示部2: + + + Pane 3: + 表示部3: + + + Packet List settings: + パケット一覧設定: + + + Show packet separator + パケットの仕切りを表示 + + + Show column definition in column context menu + 列コンテキストメニューで列定義を表示 + + + Allow the list to be sorted + ソートされるリストを許可 + + + Maximum number of cached rows (affects sorting) + キャッシュされた行の最大数(ソートに影響します) + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + <html><head/><body><p>もしこれ以上の行が表示されたらパケット解析に必要な列でソートは不可能になります。この数値を増やすことで、列の値をキャッシュするためのメモリ消費量が増加します。</p></body></html> + + + Enable mouse-over colorization + マウスオーバーの色付けを有効化 + + + Status Bar settings: + ステータスバー設定: + + + Show selected packet number + 選択したパケット番号を表示 + + + Show file load time + ファイル読込時間を表示 + + + + LteMacStatisticsDialog + + LTE Mac Statistics + LTE MAC統計 + + + Include SR frames in filter + フィルタにあるSRフレームを含める + + + Include RACH frames in filter + フィルタのRACHフレームを含める + + + MAC Statistics + MAC統計 + + + + LteRlcGraphDialog + + Dialog + ダイアログ + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>価値のあり、すばらしく、時間を節約するキーボードショートカット</h3> +<table><tbody> + +<tr><th>+</th><td>ズームイン</td></th> +<tr><th>-</th><td>ズームアウト</td></th> +<tr><th>x</th><td>X軸のズーム</td></th> +<tr><th>X</th><td>X軸のズームアウト</td></th> +<tr><th>y</th><td>Y軸のズーム</td></th> +<tr><th>Y</th><td>Y軸のズームアウト</td></th> +<tr><th>0</th><td>初期状態にグラフをリセット</td></th> + +<tr><th>→</th><td>右に10ピクセル移動</td></th> +<tr><th>←</th><td>左に10ピクセル移動</td></th> +<tr><th>↑</th><td>上に10ピクセル移動</td></th> +<tr><th>↓</th><td>下に10ピクセル移動</td></th> +<tr><th><i>Shift+</i>→</th><td>右に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>←</th><td>左に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>↑</th><td>上に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>↓</th><td>下に1ピクセル移動</td></th> + +<tr><th>g</th><td>カーソル下のパケットへ移動</td></th> + +<tr><th>z</th><td>マウスドラッグ/ズーム(切替)</td></th> +<tr><th>t</th><td>キャプチャ/セッション(切替)</td></th> +<tr><th>Space</th><td>クロスヘア(切替)</td></th> + +</tbody></table> +</body></html> + + + Mouse + マウス + + + Drag using the mouse button. + マウスボタンを使ってドラッグします + + + drags + ドラッグ + + + Select using the mouse button. + マウスボタンを使って選択します + + + zooms + ズーム + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>グラフを初期状態にリセットします</p></body></html> + + + Reset + リセット + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>接続の向きを切り替えます (反対側のフローを表示します)</p></body></html> + + + Switch Direction + 向きを切替 + + + Reset Graph + グラフをリセット + + + Reset the graph to its initial state. + グラフを初期状態にリセットします + + + 0 + 0 + + + Zoom In + 拡大 + + + + + + + + + Zoom Out + 縮小 + + + - + - + + + Move Up 10 Pixels + 上に10ピクセル移動 + + + Up + + + + Move Left 10 Pixels + 左に10ピクセル移動 + + + Left + + + + Move Right 10 Pixels + 右に10ピクセル移動 + + + Right + + + + Move Down 10 Pixels + 下に10ピクセル移動 + + + Down + + + + Move Up 1 Pixel + 上に1ピクセル移動 + + + Shift+Up + Shift+上 + + + Move Left 1 Pixel + 左に1ピクセル移動 + + + Shift+Left + Shift+左 + + + Move Right 1 Pixel + 1ピクセル右に移動 + + + Shift+Right + Shift+右 + + + Move Down 1 Pixel + 下に1ピクセル移動 + + + Move down 1 Pixel + 下に1ピクセル移動 + + + Shift+Down + Shift+下 + + + Drag / Zoom + ドラッグ / ズーム + + + Toggle mouse drag / zoom behavior + マウスのドラッグ/ズームの動作を切り替え + + + Z + Z + + + Crosshairs + 十字カーソル + + + Toggle crosshairs + 十字カーソルの表示を切り替え + + + Space + スペース + + + Move Up 100 Pixels + 上に100ピクセル移動 + + + PgUp + ページアップ + + + PgDown + ページダウン + + + Go To Packet Under Cursor + カーソル位置パケットに移動 + + + Go to packet currently under the cursor + 現在のカーソル位置のパケットに移動 + + + G + G + + + Zoom In X Axis + X軸をズーム + + + X + X + + + Zoom Out Y Axis + Y軸をズームアウト + + + Shift+Y + Shift+Y + + + Zoom In Y Axis + Y軸をズームイン + + + Y + Y + + + Zoom Out X Axis + X軸をズームアウト + + + Shift+X + Shift+X + + + Switch direction (swap between UL and DL) + 向きを切替(ULとDLを入替) + + + D + D + + + Time + 時間 + + + Sequence Number + シーケンス番号 + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + LTE RLCグラフ (UE=%1 chan=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + LTE RLCグラフ - チャンネルが選択されていません + + + Save As… + ...として保存 + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s シーケンス %4 長さ %5) + + + Click to select packet + クリックしてパケットを選びます + + + Packet + パケット + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 離してズーム, x = %1 から %2, y = %3 から %4 + + + Unable to select range. + 範囲を選択できません + + + Click to select a portion of the graph. + クリックしてグラフの割合を選びます + + + Portable Document Format (*.pdf) + PDF形式 (*.pdf) + + + Portable Network Graphics (*.png) + PNG形式 (*.png) + + + Windows Bitmap (*.bmp) + Windowsビットマップ形式 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG形式 (*.jpeg *.jpg) + + + Save Graph As… + ...としてグラフを保存 + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + LTE RLC統計 + + + Include SR frames in filter + フィルタにあるSRフレームを含める + + + Include RACH frames in filter + フィルタにあるRACHフレームを含める + + + Use RLC frames only from MAC frames + MACフレームからRLCフレームのみ + + + UL Frames + ULフレーム数 + + + UL Bytes + ULバイト数 + + + UL MB/s + ULメガバイト毎秒 + + + UL ACKs + UL ACK数 + + + UL NACKs + UL NACK数 + + + UL Missing + UL 欠落 + + + DL Frames + DL フレーム数 + + + DL Bytes + DL バイト数 + + + DL MB/s + DL メガバイト毎秒 + + + DL ACKs + DL ACK数 + + + DL NACKs + DL NACK数 + + + DL Missing + DL 欠落 + + + RLC Statistics + RLC統計 + + + + MainStatusBar + + Ready to load or capture + 読み込みもしくはキャプチャの準備 + + + Ready to load file + ファイル読み込み準備 + + + Open the Capture File Properties dialog + キャプチャファイルプロパティ画面を開く + + + Profile: %1 + プロファイル: %1 + + + Manage Profiles… + プロファイルの管理... + + + New… + 新規… + + + Edit… + 編集... + + + Import + インポート + + + Export + エクスポート + + + Delete + 削除 + + + Switch to + 切替 + + + is the highest expert information level + is the highest expert info level + は最高位のエキスパート情報レベルです + + + ERROR + エラー + + + WARNING + 警告 + + + NOTE + 注意 + + + CHAT + チャット + + + No expert information + No expert info + エキスパート情報はありません + + + %Ln byte(s) + , %1 bytes + + %Ln バイト + + + + Byte %1 + %1 バイト + + + Bytes %1-%2 + %1-%2 バイト + + + Selected Packet: %1 %2 + 選択されたパケット: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + パケット数: %1 %4 表示: %2 (%3%) + + + %1 Selected: %2 (%3%) + %1 選択済: %2 (%3%) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 マーク: %2 (%3%) + + + %1 Dropped: %2 (%3%) + %1 欠落: %2 (%3%) + + + %1 Ignored: %2 (%3%) + %1 無視: %2 (%3%) + + + %1 Comments: %2 + %1 コメント: %2 + + + %1 Load time: %2:%3.%4 + %1 読込時間: %2:%3.%4 + + + No Packets + パケットなし + + + From Zip File... + Zipファイルより... + + + From Directory... + ディレクトリより... + + + Selected Personal Profile... + 選択された個人プロファイル... + + + All Personal Profiles... + すべての個人プロファイル... + + + Packets: %1 + パケット: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + フレーム + + + Checking this will save the size, position, and maximized state of the main window. + これを選択することでメインウィンドウのサイズ、位置、および最大化した状態が保存されます + + + Remember main window size and placement + メインウィンドウのサイズと位置を記憶 + + + Open files in + ファイルを開く + + + This folder: + このフォルダ:(( + + + Browse… + Browse... + ブラウズ... + + + The most recently used folder + 一番最近に使ったフォルダ + + + Show up to + ここまで表示 + + + filter entries + フィルタエントリ + + + recent files + 最近のファイル + + + Confirm unsaved capture files + 保存していないキャプチャファイルの確認 + + + Display autocompletion for filter text + フィルタテキストの自動補完を表示 + + + Main toolbar style: + メニューツールバーのスタイル: + + + Icons only + アイコンのみ + + + Text only + テキストのみ + + + Icons & Text + アイコンとテキスト + + + Window title + ウインドウタイトル + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>既存のタイトルに追加されるカスタムウインドウタイトル<br/>%F = キャプチャファイルのファイルパス<br/>%P = プロファイル名<br/>%S = 条件つき区切り文字 (&quot; - &quot;) 値や静的テキストの変数に囲まれている際にのみ表示します<br/>%V = バージョン情報</p></body></html> + + + Prepend window title + ウィンドウタイトルに追加 + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>既存のタイトルに追加されるカスタムウインドウタイトル<br/>%F = キャプチャファイルのファイルパス<br/>%P = プロファイル名<br/>%S = 条件つき区切り文字 (&quot; - &quot;) 値や静的テキストの変数に囲まれている際にのみ表示します<br/>%V = バージョン情報</p></body></html> + + + Language: + 言語: + + + Use system setting + システム設定を利用 + + + Open Files In + ファイルを開く + + + + ManageInterfacesDialog + + Manage Interfaces + インターフェースの管理 + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + チェックボックスをクリックしてインターフェースは、表示したり非表示にできます + + + Local Interfaces + ローカルインターフェース + + + Show + 表示 + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>一覧からキャプチャするパイプを追加したり、既存のパイプを削除します</p></body></html> + + + Pipes + パイプ + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>デフォルトの設定で新規のパイプを追加します</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>一覧から選択したパイプを削除します</p></body></html> + + + Remote Interfaces + リモートインターフェース + + + Host / Device URL + ホスト/デバイスURL + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>リモートホストとそのインターフェースを追加します</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>一覧から選択したホストを削除します</p></body></html> + + + Remote Settings + リモート設定 + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + このバージョンの Wireshark はパイプの設定を保存しません + + + This version of Wireshark does not save remote settings. + このバージョンの Wireshark はリモートの設定を保存しません + + + This version of Wireshark does not support remote interfaces. + このバージョンの Wireshark はリモートインターフェイスをサポートしていません + + + New Pipe + 新規パイプ + + + + ManufDialog + + MAC Address Blocks + MACアドレスブロック + + + Search MAC address or address prefix. Special purpose bits are masked. + MACアドレスかアドレスプレフィックスを検索します。特別な目的のビットはマスクされます + + + MAC Address + MACアドレス + + + Search vendor name using a case-insentitive regular expression. + 大文字小文字を区別した正規表現を用いてベンダ名を検索します + + + Vendor Name + ベンダ名 + + + Show short name column. + 短い名前の列を表示 + + + Short name + 短い名前 + + + Select all + すべてを選択 + + + Copy + コピー + + + Find + 検索 + + + Clear + クリア + + + + ManufTableModel + + Address Block + アドレスブロック + + + Short Name + 短い名前 + + + Vendor Name + ベンダ名 + + + + ModulePreferencesScrollArea + + ScrollArea + スクロールエリア + + + + Mtp3SummaryDialog + + Dialog + ダイアログ + + + MTP3 Summary + MTP3概要 + + + File + ファイル + + + Name + 名前 + + + Length + 長さ + + + Format + フォーマット + + + Snapshot length + スナップショット長 + + + Data + データ + + + First packet + 最初のパケット + + + Last packet + 最後のパケット + + + Elapsed + 経過 + + + Packets + パケット + + + Service Indicator (SI) Totals + サービスインジケータ(SI)合計 + + + SI + SI + + + MSUs + MSUs + + + MSUs/s + MSUs/秒 + + + Bytes + バイト数 + + + Bytes/MSU + バイト/MSU + + + Bytes/s + バイト/秒 + + + Totals + 合計 + + + Total MSUs + MSU合計 + + + Total Bytes + 合計バイト数 + + + Average Bytes/MSU + 平均バイト/MSU + + + Average Bytes/s + 平均バイト/秒 + + + + MulticastStatisticsDialog + + UDP Multicast Streams + UDPマルチキャストストリーム + + + Source Address + 送信元アドレス + + + Source Port + 送信元ポート + + + Destination Address + 宛先アドレス + + + Destination Port + 宛先ポート + + + Packets + パケット + + + Packets/s + パケット/秒 + + + Avg BW (bps) + 平均帯域(ビット毎秒) + + + Max BW (bps) + 最大帯域(ビット毎秒) + + + Max Burst + 最大バースト + + + Burst Alarms + バースト警告 + + + Max Buffers (B) + 最大バッファ(B) + + + Buffer Alarms + バッファ警告 + + + Burst measurement interval (ms): + バースト計測間隔(ms): + + + Burst alarm threshold (packets): + バースト警告閾値(パケット数): + + + Buffer alarm threshold (B): + バッファ警告閾値(B): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + ストリーム排出速度 (キロバイト/秒): + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + 総排出速度 (キロバイト/秒): + + + The burst interval must be between 1 and 1000. + バースト間隔は1から1000の間でなくてはいけません + + + The burst alarm threshold isn't valid. + バースト警告閾値が有効ではありません + + + The buffer alarm threshold isn't valid. + バッファ警告閾値が有効ではありません + + + The stream empty speed should be between 1 and 10000000. + ストリーム空き速度は1から10000000の間でなくてはいけません + + + The total empty speed should be between 1 and 10000000. + 総排出速度は1から10000000の間であるべきです + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + %1 ストリーム, 平均帯域: %2bps, 最大帯域: %3bps, 最大バースト: %4 / %5ms, 最大バッファ: %6B + + + + PacketCommentDialog + + Edit Packet Comment + パケットコメントを編集 + + + Add Packet Comment + パケットコメントを追加 + + + + PacketDiagram + + Packet diagram + パケットダイアグラム + + + Show Field Values + フィールド値を表示 + + + Save Diagram As… + …としてダイアグラムを保存 + + + Copy as Raster Image + ラスタイメージとしてコピー + + + …as SVG + SVGとして… + + + Portable Network Graphics (*.png) + PNG形式 (*.png) + + + Windows Bitmap (*.bmp) + Winodwsビットマップ形式 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG形式 (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + SVG形式 (*.svg) + + + Save Graph As… + …としてグラフを保存 + + + + PacketDialog + + Dialog + ダイアログ + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + パケットバイト列を表示 + + + Packet %1 + パケット %1 + + + [%1 closed] + [%1 を綴じました] + + + Byte %1 + %1 バイト + + + Bytes %1-%2 + %1-%2 バイト + + + %Ln byte(s) + + %Ln バイト + + + + + PacketFormatGroupBox + + GroupBox + グループボックス + + + Packet Format + パケットフォーマット + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>パケット一覧のようなパケット概要行</p></body></html> + + + Summary line + 概要行 + + + Include column headings + ヘッダ列を含む + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>プロトコルツリーのようなパケット詳細</p></body></html> + + + Details: + 詳細: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>最上位のパケット詳細の項目のみエクスポート</p></body></html> + + + All co&llapsed + すべて閉じる(&l) + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>現在表示されてるようにパケット詳細画面を開いたり閉じたりします</p></body></html> + + + As displa&yed + 表示された通り(&y) + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>すべてのパケット項目をエクスポート</p></body></html> + + + All e&xpanded + すべて展開(&x) + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>パケットバイト列表示のようなパケットデータの16進数ダンプをエクスポート</p></body></html> + + + Bytes + バイト列 + + + Include secondary data sources + 第2データソースを含む + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + <html><head/><body><p>フレームに加えて第二データソースに対し再組立てや復号化したバッファなどの16進数ダンプを作成します。</html> + + + + PacketList + + Protocol Preferences + プロトコル設定 + + + Summary as Text + テキストとしての概要 + + + …as CSV + CSVとして… + + + …as YAML + YAMLとして… + + + Decode As… + …としてデコード + + + Frame %1: %2 + + + フレーム %1: %2 + + + + + [ Comment text exceeds %1. Stopping. ] + [ コメントテキスト超過 %1 停止中 ] + + + + PacketListHeader + + Align Left + 左揃え + + + Align Center + 中央揃え + + + Align Right + 右揃え + + + Edit Column + 列の編集 + + + Resize to Contents + 内容にあわせて揃える + + + Column Preferences… + 列の設定… + + + Resize Column to Width… + 内容に合わせて列幅を揃える… + + + Resolve Names + 名前を解決 + + + Remove this Column + この列を削除 + + + Column %1 + %1 列 + + + Width: + 幅: + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + %1 は %2 か、より少ない表示行数でのみソートできます。設定のレイアウトでキャッシュサイズを増やしてください。 + + + Sorting "%1"… + "%1" ソート中… + + + Sorting … + ソート中 … + + + + PacketRangeGroupBox + + Form + フォーム + + + Packet Range + パケットの範囲 + + + - + - + + + Displayed + 表示されたパケット + + + &Marked packets only + マークされたパケットのみ(&M) + + + &Range: + 範囲(&R): + + + Remove &ignored packets + 無視されたパケットを削除(&i) + + + Include &depended upon packets + 依存したパケットも含める(&d) + + + Also include packets depended upon, such as those used to reassemble displayed packets + 表示されたパケットを再組立するのに用いられたパケットなど、依存したパケットも含めます。 + + + First &to last marked + 最初のマークから最後にマークされたものまで(&t) + + + &All packets + すべてのパケット(&A) + + + &Selected packets only + 選択されたパケットのみ(&t) + + + Captured + キャプチャされたパケット + + + + PathSelectionDelegate + + Open a pipe + パイプを開く + + + + PathSelectionEdit + + Browse + 参照 + + + Select a path + パスを選択 + + + + PluginListModel + + Name + 名前 + + + Version + バージョン + + + Type + 種別 + + + Path + パス + + + + PortsModel + + All entries + すべてのエントリ + + + tcp + tcp + + + udp + udp + + + sctp + sctp + + + dccp + dccp + + + Name + 名前 + + + Port + ポート + + + Type + 種別 + + + + PreferenceEditorFrame + + Frame + フレーム + + + … + + + + a preference + 設定 + + + Browse… + 参照… + + + Open %1 preferences… + %1 設定を開く… + + + Invalid value. + 無効な値 + + + + PreferencesDialog + + Search: + 検索: + + + Checking this will show only changed preferences. + これをチェックすることで変更された設定のみ表示します + + + Show changed values + 変更した値を表示 + + + Preferences + 設定 + + + + PrefsModel + + Advanced + 高度 + + + Appearance + 外観 + + + Layout + レイアウト + + + Columns + + + + Font and Colors + フォントと色 + + + Capture + キャプチャ + + + Expert + エキスパート + + + Filter Buttons + フィルタボタン + + + RSA Keys + RSA 鍵 + + + + PrintDialog + + Packet Format + パケット形式 + + + Print each packet on a new page + パケットごとに新ページで印刷 + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>ページ毎にキャプチャファイル情報を印刷</p></body></html> + + + Capture information header + キャプチャ情報ヘッダー + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>"+"キーと "-"キーでプレビューをズームします "0"キーでズームをリセットします </p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ と - でズーム 0 でリセット</span></p></body></html> + + + Packet Range + パケット範囲 + + + Print + 印刷 + + + &Print… + 印刷(&P)… + + + Page &Setup… + ページ設定(&S)… + + + %1 %2 total packets, %3 shown + %1 %2 全パケット, %3 表示 + + + Print Error + 印刷エラー + + + Unable to print to %1. + %1 に印刷することができません + + + + ProfileDialog + + Search for profile … + プロファイルを探索… + + + Create a new profile using default settings. + デフォルトの設定を用いて新規プロファイルを作成します + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>このプロファイルを削除します。システムによって提供されたプロファイルは削除できません。デフォルトプロファイルは削除によってリセットされます。</p></body></html> + + + Copy this profile. + このプロファイルをコピーします + + + Configuration Profiles + 設定プロファイル + + + Import + noun + インポート + + + Export + noun + エクスポート + + + From Zip File... + Zipファイルより... + + + From Directory... + ディレクトリより... + + + %Ln Selected Personal Profile(s)... + + %Ln 選択された個人プロファイル... + + + + All Personal Profiles... + すべての個人プロファイル... + + + New profile + 新規プロファイル + + + Profile Error + プロファイルエラー + + + Exporting profiles + プロファイルをエキスポート中 + + + No profiles found for export + エキスポートするプロファイルが見つかりませんでした + + + Select zip file for export + エキスポートするzipファイルを選択します + + + … %Ln selected personal profile(s) + + … %Ln 選択された個人プロファイル + + + + %Ln selected personal profile(s) + + %Ln 選択された個人プロファイル + + + + An import of profiles is not allowed, while changes are pending + プロファイルのインポートは許可されません。この間の変更は保留されます。 + + + An import is pending to be saved. Additional imports are not allowed + 保存されるインポートは保留中です。追加のインポートは許可されません。 + + + An export of profiles is only allowed for personal profiles + プロファイルのエクスポートは個人プロファイルのみ許可されます。 + + + An export of profiles is not allowed, while changes are pending + プロファイルのエクスポートは許可されません。この間の変更は保留されます。 + + + %Ln profile(s) exported + + %Ln プロファイルがエキスポートされました + + + + Select zip file for import + インポートするzipファイルを選択します + + + Select directory for import + インポートするディレクトリを選択します + + + Zip File (*.zip) + Zipファイル (*.zip) + + + Error + エラー + + + An error has occurred while exporting profiles + プロファイルのエキスポート中にエラーが起きました。 + + + No profiles found for import in %1 + %1 のインポートにおいてプロファイルは見つかりませんでした。 + + + %Ln profile(s) imported + + %Ln プロファイルがインポートされました + + + + , %Ln profile(s) skipped + + , %Ln プロファイルがスキップされました。 + + + + Importing profiles + プロファイルをインポート中 + + + %Ln profile(s) selected + + %Ln profile selected + + + + + ProfileModel + + Resetting to default + デフォルトにリセット中 + + + Imported profile + インポートされたプロファイル + + + This is a system provided profile + これはシステム提供プロファイルです + + + A profile change for this name is pending + この名前のプロファイルの変更は保留中です。 + + + (See: %1) + (参照: %1) + + + This is an invalid profile definition + これは無効なプロファイル定義です。 + + + A profile already exists with this name + この名前のプロファイルは既に存在します。 + + + A profile with this name is being deleted + この名前のプロファイルは削除されました。 + + + Created from default settings + デフォルト設定から作成されました + + + system provided + システム提供 + + + deleted + 削除済 + + + copy + noun + コピー + + + Exporting profiles while changes are pending is not allowed + 変更が保留中のプロファイルのエキスポートはは許可されません。 + + + No profiles found to export + エキスポートするプロファイルが見つかりませんでした + + + Can't delete profile directory + プロファイルのディレクトリを削除できません + + + A profile name cannot contain the following characters: %1 + プロファイル名には次の文字を含むことができません: %1 + + + A profile name cannot contain the '/' character + プロファイル名には '/' の文字を含むことができません + + + A profile cannot start or end with a period (.) + プロファイルはピリオド (.) で開始や終了できません + + + Default + デフォルト + + + Global + グローバル + + + Personal + 個人 + + + Renamed from: %1 + %1 から名前を変更されました + + + Copied from: %1 + %1 からコピーされました + + + renamed to %1 + %1 へ名前が変更されました + + + Profile + プロファイル + + + Type + 種別 + + + + ProfileSortModel + + All profiles + すべてのプロファイル + + + Personal profiles + 個人プロファイル + + + Global profiles + グローバルプロファイル + + + + ProgressFrame + + Frame + フレーム + + + Loading + ロード中 + + + + ProtoTree + + Packet details + パケット詳細 + + + Not a field or protocol + フィールドやプロトコルではあります + + + No field reference available for text labels. + テキストラベルのフィールドリファレンスが利用できません + + + Expand Subtrees + サブツリーを展開 + + + Collapse Subtrees + サブツリーを閉じる + + + Expand All + すべて展開 + + + Collapse All + すべて閉じる + + + Copy + コピー + + + All Visible Items + すべての見えている項目 + + + All Visible Selected Tree Items + すべての見えている選択されたツリー項目 + + + Description + 記述 + + + Field Name + フィールド名 + + + Value + + + + As Filter + フィルタとして + + + Wiki Protocol Page + Wikiプロトコルページ + + + Filter Field Reference + フィルタフィールドリファレンス + + + Copied + コピー済 + + + Wiki Page for %1 + %1 のWikiページ + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>ワイヤーシャークWikiはコミュニティによって運営されています </p><p>いま見ているページはすばらしかったり、不完全だったり、間違っていたり、存在しないかもしれません。</p><p>Wikiに移動しますか?</p> + + + Colorize with Filter + フィルタで色付け + + + + ProtocolHierarchyDialog + + Dialog + ダイアログ + + + Protocol + プロトコル + + + Percent Packets + パケット数割合 + + + Packets + パケット数 + + + Percent Bytes + バイト数割合 + + + Bytes + バイト + + + Bits/s + ビット/秒 + + + End Packets + 終端パケット数 + + + End Bytes + 終端バイト数 + + + End Bits/s + 終端ビット毎秒 + + + PDUs + PDU + + + <small><i>A hint.</i></small> + <small><i>ヒント</i></small> + + + Copy as CSV + CSVとしてコピー + + + Copy stream list as CSV. + CSVとしてストリーム一覧をコピーします + + + Copy as YAML + YAMLとしてコピー + + + Copy stream list as YAML. + YAMLとしてストリーム一覧をコピーします + + + Copy short names + 短い名前をコピー + + + Copy short protocol names in use. + 使われている短いプロトコル名をコピーします + + + Disable unused protocols + 未使用のプロトコルを無効化 + + + Disable all protocols but those listed. + リストされている全てのプロトコルを無効化します + + + Re-enable unused protocols + 未使用のプロトコルを再度有効化 + + + Re-enable protocols that were disabled in this dialog. + このダイアログで無効化されているプロトコルを再度有効化します + + + Protocol Hierarchy Statistics + プロトコル階層統計 + + + Copy + コピー + + + as CSV + CSVとして + + + as YAML + YAMLとして + + + protocol short names + プロトコルの短い名前 + + + Protocols + プロトコル + + + Disable unused + 未使用を無効化 + + + Revert changes + 変更を元に戻す + + + No display filter. + 表示フィルタがありません + + + Display filter: %1 + 表示フィルタ: %1 + + + Unused protocols have been disabled. + 未使用のプロトコルを無効化しました + + + Protocol changes have been reverted. + プロトコルの変更を元に戻しました + + + + ProtocolPreferencesMenu + + Protocol Preferences + プロトコル設定 + + + No protocol preferences available + プロトコル設定が利用できません + + + Disable %1 + %1を無効化する + + + %1 has no preferences + %1には設定がありません + + + Open %1 preferences… + %1 設定を開く… + + + + QObject + + Average Throughput (bits/s) + 平均スループット(bits/s) + + + Round Trip Time (ms) + 往復遅延時間(ミリ秒) + + + Segment Length (B) + セグメント長(バイト) + + + Sequence Number (B) + シーケンス番号(バイト) + + + Time (s) + 時間(秒) + + + Window Size (B) + ウインドウサイズ(バイト) + + + [no capture file] + [キャプチャファイルなし] + + + Conversation + 対話 + + + Bars show the relative timeline for each conversation. + 棒によってそれぞれの対話の相対的な時間線が示されます + + + Endpoint + 終端 + + + Apply as Filter + フィルタとして適用 + + + Prepare as Filter + フィルタとして準備 + + + Find + 検索 + + + Colorize + 色をつける + + + Look Up + 探す + + + Copy + コピー + + + UNKNOWN + 不明 + + + Selected + 選択済み + + + Not Selected + 選択されていません + + + …and Selected + …かつ選択内容と一致 + + + …or Selected + …または選択内容と一致 + + + …and not Selected + …かつ選択内容と不一致 + + + …or not Selected + …または選択内容と不一致 + + + A + A + + + B + B + + + Any + いずれかの + + + Don't show this message again. + このメッセージは再度表示されません + + + Multiple problems found + 複数の問題が見つかりました + + + %1 (%L2%) + 1 (%L2%) + + + No entries. + エントリなし + + + %1 entries. + %1 エントリ + + + Base station + ベースステーション + + + <Broadcast> + <ブロードキャスト> + + + <Hidden> + <非表示> + + + BSSID + BSSID + + + Beacons + ビーコン + + + Data Pkts + データパケット + + + Protection + 保護 + + + Address + アドレス + + + Pkts Sent + 送信パケット + + + Pkts Received + 受信パケット + + + Comment + コメント + + + Wrong sequence number + 誤ったシーケンス番号 + + + Payload changed to PT=%1 + ペイロードがPT=%1に変更されました + + + Incorrect timestamp + 不正なタイムスタンプ + + + Marker missing? + マーカー欠如? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + 種別 + + + UEId + UEId + + + UL Frames + UL フレーム + + + UL Bytes + UL バイト + + + UL MB/s + ULメガバイト/秒 + + + UL Padding % + ULパディング% + + + UL Re TX + UL Re TX + + + DL Frames + DLフレーム + + + DL Bytes + DLバイト数 + + + DL MB/s + DLメガバイト/秒 + + + DL Padding % + DLパディング% + + + DL CRC Failed + DL CRC失敗 + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + 規定 + + + Unknown (%1) + 未知 (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + 不明 + + + UE Id + UE Id + + + Name + 名前 + + + Mode + モード + + + Priority + 優先度 + + + default + デフォルト + + + DLT %1 + DLT %1 + + + Invalid Display Filter + 無効な表示フィルタ + + + The filter expression %1 isn't a valid display filter. (%2). + %1 フィルターの書式は有効な表示フィルタではありません (%2) + + + Error + エラー + + + No remote interfaces found. + リモートインターフェースは見つかりませんでした + + + PCAP not found + PCAPは見つかりません + + + Unknown error + 不明なエラー + + + Default + デフォルト + + + Changed + 変更済 + + + Has this preference been changed? + この設定を変更されましたか? + + + Default value is empty + デフォルト値は空です + + + Gap in dissection + 解析のギャップ + + + Edit… + 編集… + + + Browse… + 参照… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + リモートインターフェース + + + Host: + ホスト: + + + Port: + ポート: + + + Authentication + 認証 + + + Null authentication + ヌル認証 + + + Password authentication + パスワード認証 + + + Username: + ユーザ名: + + + Password: + パスワード: + + + Clear list + 一覧消去 + + + Error + エラー + + + No remote interfaces found. + リモートインターフェースは見つかりませんでした + + + PCAP not found + PCAPは見つかりません + + + + RemoteSettingsDialog + + Remote Capture Settings + リモートキャプチャ設定 + + + Capture Options + キャプチャオプション + + + Do not capture own RPCAP traffic + 自分のRPCAPトラフィックはキャプチャしません + + + Use UDP for data transfer + データ伝送にUDPを使用 + + + Sampling Options + サンプリングオプション + + + None + なし + + + 1 of + 1 の + + + packets + パケット + + + 1 every + 1 毎 + + + milliseconds + ミリ秒 + + + + ResolvedAddressesDialog + + Dialog + ダイアログ + + + Hosts + ホスト + + + Search for entry (min 3 characters) + エントリを検索 (最小 3 文字) + + + Ports + ポート + + + Search for port or name + ポートか名前で検索 + + + Capture File Comments + キャプチャファイルコメント + + + Comment + コメント + + + Show the comment. + コメントを表示します + + + IPv4 Hash Table + IPv4ハッシュ表 + + + Show the IPv4 hash table entries. + IPv4ハッシュ表エントリを表示します + + + IPv6 Hash Table + IPv6ハッシュ表 + + + Show the IPv6 hash table entries. + IPv6ハッシュ表エントリを表示します + + + Show All + すべて表示 + + + Show all address types. + すべてのアドレス種別を表示します + + + Hide All + すべて隠す + + + Hide all address types. + すべてのアドレス種別を隠します + + + IPv4 and IPv6 Addresses (hosts) + IPv4とIPv6アドレス(hosts) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + 解決されたIPv4とIPv6ホスト名を"hosts"形式で表示します + + + Port names (services) + ポート名(services) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + 解決されたポート名を"services"形式で表示します + + + Ethernet Addresses + Ethernetアドレス + + + Show resolved Ethernet addresses in "ethers" format. + 解決されたEthernetアドレスを"ethers"形式で表示します + + + Ethernet Well-Known Addresses + Ethernet既知のアドレス + + + Show well-known Ethernet addresses in "ethers" format. + "ethers"形式で既知のEthernetアドレスを表示 + + + Ethernet Manufacturers + Ethernet製造元 + + + Show Ethernet manufacturers in "ethers" format. + "ethers"形式でEthernet製造元を表示します + + + [no file] + [ファイルがありません] + + + Resolved Addresses + 解決したアドレス + + + # Resolved addresses found in %1 + # %1 に解決したアドレスが見つかりました + + + # Comments +# +# + # コメント +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + %1 応答時間遅延統計 + + + Type + 種別 + + + Messages + メッセージ + + + Min SRT + 最小SRT + + + Max SRT + 最大SRT + + + Avg SRT + 平均SRT + + + Min in Frame + フレーム内の最小 + + + Max in Frame + フレーム内の最大 + + + Open Requests + オープン要求 + + + Discarded Responses + 破棄された応答 + + + Repeated Requests + 繰り返された要求 + + + Repeated Responses + 繰り返された応答 + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>プログラムとバージョンを選んで臨んだフィルタを入力して、適用をクリックしてください。</i></small> + + + Version: + バージョン: + + + Program: + プログラム: + + + DCE-RPC Service Response Times + DCE-RPCサービス応答時間 + + + ONC-RPC Service Response Times + ONC-RPCサービス応答時間 + + + + RsaKeysFrame + + RSA Keys + RSA 鍵 + + + RSA private keys are loaded from a file or PKCS #11 token. + RSA プライベート鍵がファイルかPKCS #11 トークンから読み込まれます + + + Add new keyfile… + 新規鍵ファイルを追加… + + + Add new token… + 新規トークンを追加… + + + Remove key + 鍵を削除 + + + PKCS #11 provider libraries. + PKCS #11 プロバイダライブラリ + + + Add new provider… + 新規プロバイダを追加… + + + Remove provider + プロバイダを削除 + + + Add PKCS #11 token or key + PKCS #11 トークンか鍵を追加 + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + 新規の PKCS #11 トークンか鍵が見つかりませんでした。PKCS #11 プロバイダを追加してください。 + + + Select a new PKCS #11 token or key + 新規 PKCS #11 トークンか鍵を選択 + + + PKCS #11 token or key + PKCS #11 トークンか鍵 + + + Enter PIN or password for %1 (it will be stored unencrypted) + %1 のPINかパスワードを入力 (暗号化されずに保存されます) + + + Enter PIN or password for key + 鍵のPINかパスワードを入力 + + + Key could not be added: %1 + 鍵は追加できませんでした: %1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + RSA プライベート鍵 (*.pem *.p12 *.pfx *.key);;すべてのファイル ( + + + Select RSA private key file + RSA プライベート鍵ファイルを選択 + + + Libraries (*.dll) + ライブラリ (*.dll) + + + Libraries (*.so) + ライブラリ (*.so) + + + Select PKCS #11 Provider Library + PKCS #11 プロバイダライブラリを選択 + + + Changes will apply after a restart + 再起動後に変更が適用されます + + + PKCS #11 provider %1 will be removed after the next restart. + PKCS #11 プロバイダ %1 は次回の再起動時後に削除されます + + + + RtpAnalysisDialog + + Dialog + ダイアログ + + + Packet + パケット + + + Sequence + シーケンス + + + Delta (ms) + 間隔 (ms) + + + Jitter (ms) + Jitter + ジッタ (ms) + + + Skew + Skew + + + Bandwidth + 帯域 + + + Marker + マーカー + + + Status + 状態 + + + Stream %1 + ストリーム %1 + + + Stream %1 Jitter + ストリーム %1 ジッタ + + + Stream %1 Difference + ストリーム %1 差異 + + + Stream %1 Delta + ストリーム %1 デルタ + + + %1 streams, + %1 ストリーム, + + + Save one stream CSV + 1つのストリームをCSVで保存 + + + Save all stream's CSV + すべてのストリームのCSVを保存 + + + &Analyze + 分析(&A) + + + Open the analysis window for the selected stream(s) +  選択したストリームの分析ウインドウを開きます + + + &Set List + リスト設定(&S) + + + &Add to List + リストに追加(&A) + + + &Remove from List + リストから削除(&R) + + + Replace existing list in RTP Analysis Dialog with new one + RTP分析画面の既存リストを新規に置換 + + + Add new set to existing list in RTP Analysis Dialog + RTP分析画面の既存のリストへ新規セットを追加 + + + Remove selected streams from list in RTP Analysis Dialog + RTP分析画面のリストから選択したストリームを削除します + + + Graph + グラフ + + + <small><i>A hint.</i></small> + lt;small><i>ヒント</i></small> + + + &Export + エクスポート(&E) + + + Open export menu + エキスポートメニューを開く + + + CSV + CSV + + + Save tables as CSV. + 表をCSVとして保存します + + + Current Tab Stream CSV + 現在のタブストリームCSV + + + Save the table on the current tab as CSV. + 現在のタブの表をCSVとして保存します + + + All Tab Streams CSV + すべてのタブストリームCSV + + + Save the table from all tabs as CSV. + すべてのタブから表をCSVとして保存します + + + Save Graph + グラフを保存 + + + Save the graph image. + グラフの画像を保存します + + + Go to Packet + パケットに移動 + + + Select the corresponding packet in the packet list. + パケット一覧から関係するパケットを選択します + + + G + G + + + Next Problem Packet + 次の問題パケット + + + Go to the next problem packet + 次の問題パケットに移動します + + + N + N + + + Prepare &Filter + フィルタを準備(&F) + + + Prepare a filter matching the selected stream(s). + 選択したストリームに合致するフィルタを準備します + + + &Current Tab + 現在のタブ(&C) + + + Prepare a filter matching current tab. + 現在のタブに合致するフィルタを準備します + + + &All Tabs + すべてのタブ(&A) + + + Prepare a filter matching all tabs. + すべてのタブに合致するフィルタを準備します + + + RTP Stream Analysis + RTPストリーム分析 + + + Save Graph As… + …としてグラフを保存 + + + G: Go to packet, N: Next problem packet + G: パケットに移動, N: 次の問題パケット + + + Portable Document Format (*.pdf) + PDF形式 (*.pdf) + + + Portable Network Graphics (*.png) + PNG形式 (*.png) + + + Windows Bitmap (*.bmp) + Windowsビットマップ形式 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG形式 (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + カンマ区切りテキスト形式 (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %2において %1 はPCMをサポートしません。推奨フォーマットは %3 です。 + + + + RtpPlayerDialog + + RTP Player + RTPプレイヤー + + + Play + 再生 + + + Source Address + 送信元アドレス + + + Source Port + 送信元ポート + + + Destination Address + 宛先アドレス + + + Destination Port + 宛先ポート + + + SSRC + SSRC + + + Setup Frame + セットアップフレーム + + + Packets + パケット + + + Time Span (s) + 時間間隔(s) + + + Payloads + ペイロード + + + <small><i>No audio</i></small> + <small><i>音声なし</i></small> + + + Start playback of all unmuted streams + すべてのミュートしていないストリームの再生を開始 + + + Pause/unpause playback + 再生を一時停止/一時停止解除 + + + Stop playback + 再生を停止 + + + Enable/disable skipping of silence during playback + 再生している間の無音スキップを有効化/無効化 + + + Min silence: + 最小の無音時間: + + + Minimum silence duration to skip in seconds + スキップするための最小の無音秒数 + + + Output Device: + 出力デバイス: + + + Output Audio Rate: + 出力音声レート: + + + Jitter Buffer: + ジッタバッファ: + + + The simulated jitter buffer in milliseconds. + ミリ秒で試算したジッタバッファ + + + Playback Timing: + 再生タイミング: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>ジッタバッファ</strong>: ジッタバッファを用いてエンドユーザに聞こえたようなRTPストリームをシミュレート試算できます +<br/> +<strong>RTP タイムスタンプ</strong>: やってくるパケットの時間の代わりにRTPタイムスタンプを利用します。これはRTPストリームをユーザが聞こえたようには再生しませんが、RTPがトンネルされていたり、元のパケットタイミングが失われている場合に有用です。 +<br/> +<strong>Uninterrupted Mode</strong>: RTPタイムスタンプを無視します。ストリームを完成させたかのようにして再生します。これはRTPタイムスタンプがない場合に有効です。 + + + Jitter Buffer + ジッタバッファ + + + RTP Timestamp + RTPタイムスタンプ + + + Uninterrupted Mode + 非割込みモード + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>時刻表示(チェックされています)でタイムスタンプを表示するかパケットキャプチャ開始してからの秒数(チェックされていません)で表示します</p></body></html> + + + Time of Day + 時刻 + + + &Export + エクスポート(&E) + + + Export audio of all unmuted selected channels or export payload of one channel. + すべてのミュートされていない選択したチャンネルのオーディオをエクスポート、または1チャンネルのペイロードをエクスポート + + + From &cursor + カーソル位置から(&c) + + + Save audio data started at the cursor + カーソル位置で開始した音声データを保存 + + + &Stream Synchronized Audio + 音声同期ストリーム(&S) + + + Save audio data synchronized to start of the earliest stream. + もっとも早いストリームの開始に同期した音声データを保存 + + + &File Synchronized Audio + 同期した音声のファイル(&F) + + + Save audio data synchronized to start of the capture file. + キャプチャファイルの開始部分に同期した音声データを保存 + + + &Payload + ペイロード(&P) + + + Save RTP payload of selected stream. + 選択したストリームのRTPペイロードを保存 + + + Reset Graph + グラフをリセット + + + Reset the graph to its initial state. + グラフを初期状態にリセットします + + + Go To Setup Packet + セットアップパケットに移動 + + + Go to setup packet of stream currently under the cursor + 現在カーソル位置のストリームのセットアップパケットに移動します + + + Mute + ミュート + + + Mute selected streams + 選択したストリームをミュート + + + Unmute + ミュート解除 + + + Unmute selected streams + 選択したストリームをミュート解除 + + + Invert muting of selected streams + 選択したストリームをミュート・ミュート解除 + + + Route audio to left channel of selected streams + 選択したストリームの左チャンネルへオーディオを接続 + + + Route audio to left and right channel of selected streams + 選択したストリームの左と右のチャンネルへオーディオを接続 + + + Route audio to right channel of selected streams + 選択したストリームの右チャンネルへオーディオを接続 + + + Remove Streams + ストリームを削除 + + + Remove selected streams from the list + 選択したストリームをリストから削除します + + + All + すべて + + + Select all + すべてを選択 + + + None + なし + + + Clear selection + 選択をクリア + + + Invert + 反転 + + + Invert selection + 選択を反転します + + + Play/Pause + 再生/一時停止 + + + Start playing or pause playing + 再生を開始または再生を一時停止します + + + Stop + 停止 + + + Stop playing + 再生を停止します + + + I&naudible streams + 音声のないストリーム(&n) + + + Select/Deselect inaudible streams + 音声のないストリームを選択/非選択 + + + Inaudible streams + 音声のないストリーム + + + &Select + 選択(&S) + + + Select inaudible streams + 音声のないストリームを選択します + + + &Deselect + 非選択(&D) + + + Deselect inaudible streams + 音声のないストリームを非選択します + + + Prepare &Filter + フィルタを準備(&F) + + + Prepare a filter matching the selected stream(s). + 選択したストリームに合致するフィルタを準備します + + + R&efresh streams + ストリームを更新(&e) + + + Read captured packets from capture in progress to player + 現在キャプチャ中のキャプチャしたパケットから読み込んでプレイヤーへ + + + Zoom In + 拡大 + + + SR (Hz) + SR (Hz) + + + Sample rate of codec + コーデックのサンプリングレート + + + PR (Hz) + PR (Hz) + + + Play rate of decoded audio (depends e. g. on selected sound card) + デコードされた音声の再生レート(選択されたサウンドカード等に依存します) + + + Zoom Out + 縮小 + + + Move Left 10 Pixels + 左に10ピクセル移動 + + + Move Right 10 Pixels + 右に10ピクセル移動 + + + Move Left 1 Pixels + 左に1ピクセル移動 + + + Move Right 1 Pixels + 右に1ピクセル移動 + + + Go To Packet Under Cursor + カーソル位置のパケットに移動 + + + Go to packet currently under the cursor + 現在カーソル位置のパケットに移動します + + + Play the stream + ストリームを再生 + + + To Left + 左へ + + + Left + Right + 左 + 右 + + + To Right + 右へ + + + Invert Muting + ミュートを反転します + + + No devices available + デバイスが利用できません + + + Select + 選択 + + + Audio Routing + オーディオ接続 + + + &Play Streams + ストリームを再生(&P) + + + Open RTP player dialog + RTPプレイヤー画面を開きます + + + &Set playlist + 再生リスト設定(&S) + + + Replace existing playlist in RTP Player with new one + RTPプレイヤーの既存の再生リストを新しいものに置換します + + + &Add to playlist + 再生リストに追加(&A) + + + Add new set to existing playlist in RTP Player + RTPプレイヤーの既存のプレイリストに新規セットを追加します + + + &Remove from playlist + 再生リストから削除(&R) + + + Remove selected streams from playlist in RTP Player + 選択したストリームをRTPプレイヤーの再生リストから削除します + + + No Audio + 音声なし + + + Decoding streams... + ストリームをデコード中 + + + Out of Sequence + シーケンス外 + + + Jitter Drops + ジッタ欠落 + + + Wrong Timestamps + タイムスタンプが誤っている + + + Inserted Silence + 無音が挿入されている + + + Double click on cell to change audio routing + セルをダブルクリックして音声ルーティングを変更します + + + %1 streams + %1 ストリーム + + + , %1 selected + , %1 選択済 + + + , %1 not muted + , %1 ミュートなし + + + , start: %1. Double click on graph to set start of playback. + , 開始: %1. グラフをダブルクリックして再生の開始点を設定 + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + , 開始: %1, カーソル: %2. "G" を押してパケット %3 へ移動します。ダブルクリックして再生開始を設定します。 + + + Playback of stream %1 failed! + ストリーム %1 の再生に失敗しました!! + + + Automatic + 自動 + + + WAV (*.wav) + WAV形式 (*.wav) + + + Sun Audio (*.au) + Sunオーディオ形式 (*.au) + + + Save audio + 音声を保存 + + + Raw (*.raw) + 無加工形式 (*.raw) + + + Save payload + ペイロードを保存 + + + Warning + 警告 + + + No stream selected or none of selected streams provide audio + ストリームが選択されてないかオーディオを提供する選択されたストリームがありません + + + Error + エラー + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + すべての選択されたストリームは同一の再生レートを使用しなければなりません。出力音声レートのマニュアル設定が助けになるかもしれません。 + + + No streams are suitable for save + 保存にて適したストリームはありません + + + Save failed! + 保存失敗!! + + + Can't write header of AU file + AUファイルのヘッダを書き込むことができません + + + Can't write header of WAV file + WAVファイルのヘッダを書き込むことができません + + + Payload save works with just one audio stream. + ペイロードの保存は1つのオーディオストリームのみ動作します + + + Double click to change audio routing + ダブルクリックして音声ルーティングを変更します + + + Preparing to play... + 再生準備中... + + + Unknown + 不明 + + + + RtpStreamDialog + + Dialog + ダイアログ + + + Source Address + 送信元アドレス + + + Source Port + 送信元ポート + + + Destination Address + 宛先アドレス + + + Destination Port + 宛先ポート + + + SSRC + SSRC + + + Start Time + 開始時間 + + + Duration + 時間 + + + Payload + ペイロード + + + Packets + パケット + + + Lost + 欠落 + + + Max Delta (ms) + 最大間隔(ms) + + + Max Jitter + 最大ジッタ + + + Mean Jitter + 平均ジッタ + + + Status + 状態 + + + <small><i>A hint.</i></small> + <small><i>ヒント</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>現在の表示フィルタに合致した対話のみ表示します</p></body></html> + + + Limit to display filter + 表示フィルタに制限 + + + Time of Day + 時刻 + + + Find &Reverse + 逆方向を検索(&R) + + + Prepare &Filter + フィルタを準備(&F) + + + &Export + エクスポート(&E) + + + &Analyze + 分析(&A) + + + Open the analysis window for the selected stream(s) and add it to it + 選択したストリームに対して分析ウインドウを開き追加します + + + Find the reverse stream matching the selected forward stream. + 選択した順方向のストリームに合致する逆方向のストリームを探します + + + Min Delta (ms) + 最小間隔(ms) + + + Mean Delta (ms) + 平均間隔(ms) + + + Min Jitter + 最小ジッタ + + + All forward/reverse stream actions + すべての順方向/逆方向ストリームの動作 + + + R + R + + + Find All &Pairs + すべてのペアを検索(&P) + + + Select all streams which are paired in forward/reverse relation + 順方向/逆方向関係の対をなすすべてのストリームを選択します + + + Shift+R + Shift+R + + + Find Only &Singles + 片方のみ検索(&S) + + + Find all streams which don't have paired reverse stream + 逆方向ストリームと対をなしていないすべてのストリームを検索します + + + Ctrl+R + Ctrl+R + + + Mark Packets + パケットをマーク + + + Mark the packets of the selected stream(s). + 選択したストリームのパケットをマークします + + + M + M + + + All + すべて + + + Select all + すべてを選択 + + + None + なし + + + Clear selection + 選択をクリア + + + Invert + 反転 + + + Invert selection + 選択を反転します + + + Go To Setup + セットアップに移動 + + + Go to the setup packet for this stream. + このストリームに対するセットアップパケットへ移動します + + + G + G + + + Prepare a filter matching the selected stream(s). + 選択したストリームに合致するフィルタを準備します + + + P + P + + + Export the stream payload as rtpdump + rtpdumpとしてストリームペイロードをエクスポート + + + E + E + + + A + A + + + Cop&y + コピー(&y) + + + Open copy menu + コピーメニューを開く + + + Copy as CSV + CSVとしてコピー + + + Copy stream list as CSV. + CSVとしてストリーム一覧をコピーします + + + Copy as YAML + YAMLとしてコピー + + + Copy stream list as YAML. + YAMLとしてストリーム一覧をコピーします + + + RTP Streams + RTPストリーム + + + Select + 選択 + + + as CSV + CSVとして + + + as YAML + YAMLとして + + + %1 streams + %1 ストリーム + + + , %1 selected, %2 total packets + , %1 選択, %2 全パケット + + + Save RTPDump As… + RRPDumpとして保存… + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark - SCTPアソシエーション + + + ID + ID + + + Port 1 + ポート 1 + + + Port 2 + ポート 2 + + + Number of Packets + パケット数 + + + Number of DATA Chunks + データのチャンク数 + + + Number of Bytes + バイト数 + + + Filter Selected Association + 選択したアソシエーションをフィルタ + + + Analyze + 分析 + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark - アソシエーションの分析 + + + TabWidget + TabWidget + + + Statistics + 統計 + + + Chunk Statistics + チャンク統計 + + + Filter Association + アソシエーションをフィルタ + + + Number of Data Chunks from EP2 to EP1: + EP2からEP1へのデータチャンク数: + + + Checksum Type: + チェックサム種別: + + + Number of Data Chunks from EP1 to EP2: + EP1からEP2へのデータチャンク数: + + + Number of Data Bytes from EP1 to EP2: + EP1からEP2へのデータバイト数: + + + Number of Data Bytes from EP2 to EP1: + EP2からEP1へのデータバイト数: + + + Endpoint 1 + 終端 1 + + + Graph TSN + TSNグラフ + + + Graph Bytes + バイト数グラフ + + + Requested Number of Inbound Streams: + 内向きストリームの要求数: + + + Port: + ポート: + + + Sent Verification Tag: + 送信された検証タグ: + + + Minimum Number of Inbound Streams: + 内向きストリームの最小数: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + <small><i>分析を完了するためにSCTPの設定のアソシエーションのインデックスが有効か確認してください。</i></small> + + + Complete List of IP addresses from INIT Chunk: + INITチャンクで提供されたIPアドレスの完全な一覧: + + + Minimum Number of Outbound Streams: + 外向きストリームの最小数: + + + Graph Arwnd + Arwndグラフ + + + Endpoint 2 + 終端 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + INIT_ACKチャンクで提供されたIPアドレスの完全な一覧: + + + Provided Number of Outbound Streams: + 外向きストリームの提供数: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + SCTP分析アソシエーション: %1 ポート1 %2 ポート2 %3 + + + No Association found for this packet. + このパケットに対するアソシエーションは見つかりませんでした + + + Warning + 警告 + + + Could not find SCTP Association with id: %1 + id: %1 とのSCTPアソシエーションを見つけることができませんでした + + + Complete list of IP addresses from INIT Chunk: + INITACKチャンクで提供されたIPアドレスの完全な一覧: + + + Complete list of IP addresses from INIT_ACK Chunk: + INIT_ACKチャンクで提供されたIPアドレスの完全な一覧: + + + List of Used IP Addresses + 使用されたIPアドレス一覧 + + + Used Number of Inbound Streams: + 内向きストリームの使用数: + + + Used Number of Outbound Streams: + 外向きストリームの使用数: + + + + SCTPChunkStatisticsDialog + + Dialog + ダイアログ + + + Association + アソシエーション + + + Endpoint 1 + 終端 1 + + + Endpoint 2 + 終端 2 + + + Save Chunk Type Order + チャンク種別の順序を保存 + + + Hide Chunk Type + チャンク種別を隠す + + + Remove the chunk type from the table + 表からチャンク種別を削除します + + + Chunk Type Preferences + チャンク種別設定 + + + Go to the chunk type preferences dialog to show or hide other chunk types + チャンク種別の設定画面に移動して表示もしくは他のチャンク種別を隠します + + + Show All Registered Chunk Types + すべての登録されたチャンク種別を表示 + + + Show all chunk types with defined names + 定義された名前ですべてのチャンク種別を表示します + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + SCTP断片統計: %1 ポート1 %2 ポート2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + SCTPグラフ + + + Reset to full size + 最大サイズにリセット + + + Save Graph + グラフを保存 + + + goToPacket + パケットに移動 + + + Go to Packet + パケットに移動 + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + 時間あたりのSCTPデータとアドバタイズされた受信ウインドウ: %1 ポート1 %2 ポート2 %3 + + + No Data Chunks sent + 何のデータチャンクも送られていません + + + Arwnd + Arwnd + + + time [secs] + 時間 [秒] + + + Advertised Receiver Window [Bytes] + アドバタイズされた受信ウインドウ [バイト数] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>グラフ %1: a_rwnd=%2 時間=%3 秒 </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + SCTPグラフ + + + Reset to full size + 最大サイズにリセット + + + Save Graph + グラフを保存 + + + goToPacket + パケットに移動 + + + Go to Packet + パケットに移動 + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + 時間あたりのSCTPデータとアドバタイズされた受信ウインドウ: %1 ポート1 %2 ポート2 %3 + + + No Data Chunks sent + 何のデータチャンクも送られていません + + + Bytes + バイト + + + time [secs] + 時間[秒数] + + + Received Bytes + 受信バイト数 + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>グラフ %1: 受信バイト数=%2 時間=%3 秒 </i></small> + + + + SCTPGraphDialog + + SCTP Graph + SCTPグラフ + + + Relative TSNs + 相対的 TSNs + + + Only SACKs + SACKのみ + + + Only TSNs + TSNのみ + + + Show both + 両方を表示 + + + Reset to full size + 最大サイズにリセット + + + Save Graph + グラフを保存 + + + goToPacket + パケットに移動 + + + Go to Packet + パケットに移動します + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + 時間当たりのSCTP TSN数とSACK数: %1 ポート1 %2 ポート2 %3 + + + No Data Chunks sent + 何のデータチャンクも送られていません + + + CumTSNAck + CumTSNAck + + + Gap Ack + Gap Ack + + + NR Gap Ack + NR Gap Ack + + + Duplicate Ack + 重複ACK + + + TSN + TSN + + + time [secs] + 時間[秒数] + + + TSNs + TSNs + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 時間: %3 秒 </i></small> + + + Portable Document Format (*.pdf) + PDF形式 (*.pdf) + + + Portable Network Graphics (*.png) + PNG形式 (*.png) + + + Windows Bitmap (*.bmp) + Windowsビットマップ形式 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG形式 (*.jpeg *.jpg) + + + Save Graph As… + …としてグラフを保存 + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <small><i>コマンドを選択して望んだフィルタを入力したら適用を押してください</i></small> + + + Command: + コマンド: + + + SCSI Service Response Times + SCSI サービス応答時間 + + + + SearchFrame + + Frame + フレーム + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>パケット一覧(概要部分)やデコードされたパケット表示ラベル(ツリー表示部分)やアスキー変換されたパケットデータ(16進数表示部分)を検索します</p></body></html> + + + Packet list + パケット一覧 + + + Packet details + パケット詳細 + + + Packet bytes + パケットバイト列 + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>ナロー文字列 (UTF-8とASCII)もしくはワイド(UTF-16)文字を検索</p></body></html> + + + Narrow & Wide + ナローとワイド + + + Narrow (UTF-8 / ASCII) + ナロー(UTF-8/ASCII) + + + Wide (UTF-16) + ワイド(UTF-16) + + + Case sensitive + 大文字小文字を区別 + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>表示フィルタ構文(例 ip.addr==10.1.1.1),16数文字列(例 ffffda5),平文文字列(例 My String)もしくは正規表現(例 colou?r)を用いてデータを検索しますt;/body></html> + + + Display filter + 表示フィルタ + + + Hex value + 16進数値 + + + String + 文字列 + + + Regular Expression + 正規表現 + + + Find + 検索 + + + Cancel + キャンセル + + + No valid search type selected. Please report this to the development team. + 有効な検索形式が選択されていません どうか開発チームにこれを報告してください + + + Invalid filter. + 無効なフィルタです + + + That filter doesn't test anything. + そのフィルタは何も検査しません + + + That's not a valid hex string. + 有効な16進数文字列ではありません + + + You didn't specify any text for which to search. + 検索するためのテキストを指定しませんでした + + + No valid character set selected. Please report this to the development team. + 有効な文字セットが選択されていません どうかこれを開発チームに報告してください + + + No valid search area selected. Please report this to the development team. + 有効な検索範囲が選択されていません。どうかこれを開発チームに報告してください + + + Searching for %1… + %1 検索中… + + + No packet contained those bytes. + そのようなバイト列を含むパケットはありませんでした + + + No packet contained that string in its Info column. + 情報列にその文字列を含むパケットはありませんでした + + + No packet contained that string in its dissected display. + 解析された表示にその文字列を含むパケットはありませんでした + + + No packet contained that string in its converted data. + コンバートされたデータにその文字列を含むパケットはありませんでした + + + No packet matched that filter. + フィルタに一致するパケットはありませんでした + + + + SequenceDialog + + Call Flow + Callフロー + + + Time + 時間 + + + Comment + コメント + + + No data + データなし + + + %Ln node(s) + + %Ln ノード + + + + %Ln item(s) + + %Ln 項目 + + + + Portable Document Format (*.pdf) + PDF形式 (*.pdf) + + + Portable Network Graphics (*.png) + PNG形式 (*.png) + + + Windows Bitmap (*.bmp) + Windowsビットマップ形式 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG形式 (*.jpeg *.jpg) + + + ASCII (*.txt) + アスキー形式 (*.txt) + + + Save Graph As… + ...としてグラフを保存 + + + Flow + フロー + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>価値のあり、素晴らしく時間を節約できるキーボードショートカット</h3> +<table><tbody> + +<tr><th>+</th><td>拡大</td></th> +<tr><th>-</th><td>縮小</td></th> +<tr><th>0</th><td>グラフを初期状態にリセット</td></th> + +<tr><th>→</th><td>右に10ピクセル移動</td></th> +<tr><th>←</th><td>左に10ピクセル移動</td></th> +<tr><th>↑</th><td>上に10ピクセル移動</td></th> +<tr><th>↓</th><td>下に10ピクセル移動</td></th> +<tr><th><i>Shift+</i>→</th><td>右に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>←</th><td>左に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>↑</th><td>上に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>↓</th><td>下に1ピクセル移動</td></th> + +<tr><th>g</th><td>カーソルのあるパケットへ移動</td></th> +<tr><th>n</th><td>次のパケットへ移動</td></th> +<tr><th>p</th><td>前のパケットへ移動</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>ヒント</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>現在の表示フィルタに合致した表示フィルタのみ表示します</p></body></html> + + + Limit to display filter + 表示フィルタに制限 + + + Flow type: + フロー種別: + + + Addresses: + アドレス: + + + Any + いずれかの + + + Network + ネットワーク + + + Reset Diagram + ダイアグラムをリセット + + + Reset &Diagram + ダイアグラムをリセット(&D) + + + Reset the diagram to its initial state. + ダイアグラムを初期状態にリセットします + + + 0 + 0 + + + &Reset Diagram + ダイアグラムをリセット(&R) + + + Reset the diagram to its initial state + ダイアグラムを初期状態にリセットします + + + &Export + エクスポート(&E) + + + Export diagram + ダイアグラムをエクスポート + + + Zoom In + 拡大 + + + + + + + + + Zoom Out + 縮小 + + + - + - + + + Move Up 10 Pixels + 上に10ピクセル移動 + + + Up + + + + Move Left 10 Pixels + 左に10ピクセル移動 + + + Left + + + + Move Right 10 Pixels + 右に10ピクセル移動 + + + Right + + + + Move Down 10 Pixels + 下に10ピクセル移動 + + + Down + + + + Move Up 1 Pixel + 上に1ピクセル移動 + + + Shift+Up + Shift+Up + + + Move Left 1 Pixel + 左に1ピクセル移動 + + + Shift+Left + Shift+Left + + + Move Right 1 Pixel + 右に1ピクセル移動 + + + Shift+Right + Shift+Right + + + Move Down 1 Pixel + 下に1ピクセル移動 + + + Shift+Down + Shift+Down + + + Go To Packet Under Cursor + カーソル位置のパケットに移動 + + + Go to packet currently under the cursor + 現在のカーソル位置にあるパケットに移動します + + + G + G + + + All Flows + すべてのフロー + + + Show flows for all packets + すべてのパケットのフローを表示します + + + 1 + 1 + + + TCP Flows + TCPフロー + + + Show only TCP flow information + TCPフロー情報のみ表示します + + + Go To Next Packet + 次のパケットに移動 + + + Go to the next packet + 次のパケットに移動します + + + N + N + + + Go To Previous Packet + 前のパケットに移動 + + + Go to the previous packet + 前のパケットに移動します + + + P + P + + + Select RTP Stream + RTPストリームを選択 + + + Select RTP stream in RTP Streams dialog + RTPストリーム画面のRTPストリームを選択します + + + S + S + + + Deselect RTP Stream + RTPストリームを選択解除 + + + Deselect RTP stream in RTP Streams dialog + RTPストリーム画面のRTPストリームを選択解除します + + + D + D + + + + ShortcutListModel + + Shortcut + ショートカット + + + Name + 名前 + + + Description + 記述 + + + + ShowPacketBytesDialog + + Show Packet Bytes + パケットバイト列を表示 + + + Hint. + ヒント + + + Decode as + としてでコード + + + Show as + として表示 + + + Start + 開始 + + + End + 終了 + + + Find: + 検索: + + + Find &Next + 次を検索 + + + Frame %1, %2, %Ln byte(s). + + フレーム %1, %2, %Ln バイト + + + + None + なし + + + Base64 + ベース64形式 + + + Compressed + 圧縮済 + + + Hex Digits + 16進数字 + + + Percent-Encoding + パーセント-エンコーディング + + + Quoted-Printable + 節に分けて印刷可能 + + + ROT13 + ROT13形式 + + + ASCII + アスキー文字形式 + + + ASCII & Control + アスキー文字と制御文字形式 + + + C Array + C言語配列形式 + + + EBCDIC + EBCDIC形式 + + + Hex Dump + 16進数ダンプ形式 + + + HTML + HTML形式 + + + Image + 画像形式 + + + Raw + Raw(無加工)形式 + + + Rust Array + Rust配列 + + + UTF-8 + UTF-8形式 + + + YAML + YAML形式 + + + Print + 印刷 + + + Copy + コピー + + + Save as… + …として保存 + + + Save Selected Packet Bytes As… + …として選択したパケットバイト列を保存 + + + Displaying %Ln byte(s). + + %Ln バイトを表示中 + + + + JSON + JSON + + + Regex Find: + 正規表現検索: + + + + ShowPacketBytesTextEdit + + Show Selected + 選択されたものを表示 + + + Show All + すべてを表示 + + + + SplashOverlay + + Initializing dissectors + ダイセクターを初期化中 + + + Initializing tap listeners + タップリスナを初期化中 + + + Initializing external capture plugins + 外部キャプチャプラグインを初期化 + + + Registering dissectors + ダイセクターを登録中 + + + Registering plugins + Registering dissector + プラグインを登録中 + + + Handing off dissectors + ダイセクターを除去中 + + + Handing off plugins + プラグインを除去中 + + + Loading Lua plugins + Luaプラグインを読込中 + + + Removing Lua plugins + Luaプラグインを削除中 + + + Loading module preferences + モジュール設定を読込中 + + + Finding local interfaces + ローカルインターフェースを見つけています + + + Applying changed preferences + 変更した設定を適用中 + + + (Unknown action) + (不明な動作) + + + + StatsTreeDialog + + Configuration not found + 設定が見つかりませんでした + + + Unable to find configuration for %1. + %1 に対する設定を見つけることができません + + + + StripHeadersDialog + + Dialog + ダイアログ + + + Display filter: + 表示フィルタ: + + + + SupportedProtocolsDialog + + Dialog + ダイアログ + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>フィールド名の一覧を検索します</p></body></html> + + + Search: + 検索: + + + <small><i>Gathering protocol information…</i></small> + <small><i>プロトコル情報を集めています…</i></small> + + + Supported Protocols + サポートされたプロトコル + + + %1 protocols, %2 fields. + %1 プロトコル, %2 フィールド + + + + SupportedProtocolsModel + + Name + 名前 + + + Filter + フィルタ + + + Type + 種別 + + + Description + 記述 + + + + SyntaxLineEdit + + Invalid filter: %1 + 無効なフィルタ: %1 + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + "%1" は非推奨で "%2" が推奨されます。詳細はヘルプ 6.4.8をご覧ください + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + ダイアログ + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>価値のありすばらしく時間を節約するキーボードショートカット</h3> +<table><tbody> + +<tr><th>+</th><td>ズームイン</td></th> +<tr><th>-</th><td>ズームアウト</td></th> +<tr><th>x</th><td>X軸にズームイン</td></th> +<tr><th>X</th><td>X軸にズームアウト</td></th> +<tr><th>y</th><td>Y軸にズームイン</td></th> +<tr><th>Y</th><td>Y軸にズームアウト</td></th> +<tr><th>0</th><td>初期状態にグラフをリセット</td></th> + +<tr><th>→</th><td>右に10ピクセル移動</td></th> +<tr><th>←</th><td>左に10ピクセル移動</td></th> +<tr><th>↑</th><td>上に10ピクセル移動</td></th> +<tr><th>↓</th><td>下に10ピクセル移動</td></th> +<tr><th><i>Shift+</i>→</th><td>右に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>←</th><td>左に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>↑</th><td>上に1ピクセル移動</td></th> +<tr><th><i>Shift+</i>↓</th><td>下に1ピクセル移動</td></th> + +<tr><th><i>Pg Up</i></th><td>新規ストリーム</td></th> +<tr><th><i>Pg Dn</i></th><td>前のストリーム</td></th> +<tr><th>d</th><td>方向を切替 (TCP端末を切替)</td></th> +<tr><th>g</th><td>カーソル下のパケットへ移動</td></th> + +<tr><th>z</th><td>マウスドラッグとズームを切替</td></th> +<tr><th>s</th><td>相対と絶対シーケンス番号を切替</td></th> +<tr><th>t</th><td>キャプチャとセッション時間基準を切替</td></th> +<tr><th>Space</th><td>十字を切替</td></th> + +<tr><th>1</th><td>往復遅延時間グラフ</td></th> +<tr><th>2</th><td>スループットグラフ</td></th> +<tr><th>3</th><td>スティーブン形式時間シーケンスグラフ</td></th> +<tr><th>4</th><td>tcptrace形式時間シーケンスグラフ</td></th> +<tr><th>5</th><td>ウインドウスケーリンググラフ</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>マウスをあわせるとショートカットが確認できます</i></small> + + + Type + 種別 + + + MA Window (s) + MA ウインドウ (s) + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + グラフをクリックすることでSACKセグメントと選択されたデータパケットを許可します + + + Select SACKs + select SACKs + SACKを選択 + + + Stream + ストリーム + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>接続の向きを切り替えます (反対側のフローを表示します)</p></body></html> + + + Switch Direction + 向きを切り替え + + + Mouse + マウス + + + Drag using the mouse button. + マウスボタンを使ってドラッグします + + + drags + ドラッグ + + + Select using the mouse button. + マウスボタンを使うことを選択します + + + zooms + ズーム + + + Display Round Trip Time vs Sequence Number + 往復遅延時間対シーケンス番号を表示 + + + RTT By Sequence Number + シーケンス番号毎の往復遅延時間 + + + Display graph of Segment Length vs Time + セグメント長対時間のグラフを表示 + + + Segment Length + セグメント長 + + + Display graph of Mean Transmitted Bytes vs Time + 平均送信バイト対時間のグラフを表示 + + + Display graph of Mean ACKed Bytes vs Time + 平均のACKされたバイト数対時間のグラフを表示 + + + Goodput + Goodput + + + Display graph of Receive Window Size vs Time + 受信ウインドウサイズ対時間のグラフを表示 + + + Rcv Win + 受信ウインドウ + + + Display graph of Outstanding Bytes vs Time + 未出力バイト対時間のグラフを表示 + + + Bytes Out + 出力バイト + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>グラフを初期状態にリセットします</p></body></html> + + + Reset + リセット + + + Reset Graph + グラフをリセット + + + Reset the graph to its initial state. + グラフを初期状態にリセットします + + + 0 + 0 + + + Zoom In + ズームイン + + + + + + + + + Zoom Out + ズームアウト + + + - + - + + + Move Up 10 Pixels + 上に10ピクセル移動 + + + Up + + + + Move Left 10 Pixels + 左に10ピクセル移動 + + + Left + + + + Move Right 10 Pixels + 右に10ピクセル移動 + + + Right + + + + Move Down 10 Pixels + 下に10ピクセル移動 + + + Down + + + + Move Up 1 Pixel + 上に1ピクセル移動 + + + Shift+Up + Shift+Up + + + Move Left 1 Pixel + 左に1ピクセル移動 + + + Shift+Left + Shift+Left + + + Move Right 1 Pixel + 右に1ピクセル移動 + + + Shift+Right + Shift+Right + + + Move Down 1 Pixel + 下に1ピクセル移動 + + + Shift+Down + Shift+Down + + + Next Stream + 次のストリーム + + + Go to the next stream in the capture + キャプチャにある次のストリームへ移動します + + + PgUp + ページアップ + + + Previous Stream + 前のストリーム + + + Go to the previous stream in the capture + キャプチャにある前のストリームへ移動します + + + PgDown + ページダウン + + + Switch direction (swap TCP endpoints) + 向きを切り替え(TCP終端を入替) + + + D + D + + + Go To Packet Under Cursor + カーソル位置のパケットへ移動 + + + Go to packet currently under the cursor + 現在のカーソル位置にあるパケットへ移動します + + + G + G + + + Drag / Zoom + ドラッグ/ズーム + + + Toggle mouse drag / zoom behavior + マウスのドラッグ/ズームの動作を切り替えます + + + Z + Z + + + Relative / Absolute Sequence Numbers + 相対的/絶対的シーケンス番号 + + + Toggle relative / absolute sequence numbers + 相対的/絶対的シーケンス番号を切り替えます + + + S + S + + + Capture / Session Time Origin + キャプチャ/セッション時間起点 + + + Toggle capture / session time origin + キャプチャ/セッション時間起点を切り替えます + + + T + T + + + Crosshairs + 十字カーソル + + + Toggle crosshairs + 十字カーソルの表示を切り替えます + + + Space + スペース + + + Round Trip Time + 往復遅延時間 + + + Switch to the Round Trip Time graph + 往復遅延グラフに切り替えます + + + 1 + 1 + + + Throughput + スループット + + + Switch to the Throughput graph + スループットグラフに切り替えます + + + 2 + 2 + + + Time / Sequence (Stevens) + 時間/シーケンス(Stevens) + + + Switch to the Stevens-style Time / Sequence graph + Stevens形式の時間/シーケンスグラフに切り替えます + + + 3 + 3 + + + Window Scaling + ウインドウスケーリング + + + Switch to the Window Scaling graph + ウインドウスケーリンググラフに切り替えます + + + 5 + 5 + + + Time / Sequence (tcptrace) + 時間/シーケンス(tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + tcptrace形式の時間/シーケンスグラフに切り替えます + + + 4 + 4 + + + Zoom In X Axis + X軸をズームイン + + + X + X + + + Zoom Out X Axis + X軸をズームアウト + + + Shift+X + Shift+X + + + Zoom In Y Axis + Y軸をズームイン + + + Y + Y + + + Zoom Out Y Axis + Y軸をズームアウト + + + Shift+Y + Shift+Y + + + Save As… + ...として保存 + + + No Capture Data + キャプチャデータがありません + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 %2 pkts, %3 %4 %5 pkts, %6 + + + Sequence Numbers (Stevens) + シーケンス番号 (Stevens) + + + Sequence Numbers (tcptrace) + シーケンス番号 (tcptrace) + + + (MA) + (MA) + + + (%1 Segment MA) + (%1 セグメント MA) + + + [not enough data] + [十分なデータがありません] + + + for %1:%2 %3 %4:%5 + for %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + Click to select packet + クリックしてパケットを選んでください + + + Packet + パケット + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 離してズーム, x = %1 から %2, y = %3 から %4 + + + Unable to select range. + 範囲を選択できません + + + Click to select a portion of the graph. + クリックしてグラフの割合を選びます + + + Portable Document Format (*.pdf) + PDF形式 (*.pdf) + + + Portable Network Graphics (*.png) + PNG形式 (*.png) + + + Windows Bitmap (*.bmp) + Winodwsビットマップ形式 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG形式 (*.jpeg *.jpg) + + + Save Graph As… + ...としてグラフを保存 + + + + TLSKeylogDialog + + Dialog + ダイアログ + + + Browse… + 参照… + + + Command line + コマンドライン + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + TLSキーログファイル名設定で指定したファイルにSSLKEYLOGFILE環境変数を設定してアプリケーションを起動します。これによってWiresharkでTLSの復号化が行えます。初回のTLSハンドシェイクがキャプチャされたことを確認するためにアプリケーションを起動する前にキーログファイルを設定してキャプチャを開始してください。 + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + <span style=" font-size:small;">FirefoxとChromeが動作することで知られています。望むブラウザが現在起動している場合は以下を起動する前に一度閉じてください。コマンドラインオプションもサポートされます。 + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + TLS (プリ)マスターシークレット ログファイルパス (tls.keylog_file) + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + <span style=" font-size:small;">TLS セッションシークレットはこのファイルに記録されます。このフィールドを変更したら保存ボタンを押してTLSプロトコル設定を更新してください。</span> + + + Launch application with SSLKEYLOGFILE + SSLKEYLOGFILEとともにアプリケーションを起動します + + + Launch + 起動 + + + Save + 保存 + + + TLS Keylog file + TLSキーログファイル + + + Program to start with SSLKEYLOGFILE + SSLKEYLOGFILEとともに開始するプログラム + + + + TapParameterDialog + + Dialog + ダイアログ + + + Item + 項目 + + + <small><i>A hint.</i></small> + <small><i>ヒント</i></small> + + + Display filter: + 表示フィルタ: + + + Regenerate statistics using this display filter + 表示フィルタを用いて統計を再生成します + + + Apply + 適用 + + + Copy + コピー + + + Copy a text representation of the tree to the clipboard + ツリーのテキスト表記をクリップボードにコピーします + + + Save as… + Save as... + …として保存 + + + Save the displayed data in various formats + 様々な形式で表示データを保存します + + + Collapse All + すべて閉じる + + + Expand All + すべて展開 + + + Save Statistics As… + …として統計を保存 + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + プレインテキストファイル(*.txt);;カンマ区切りテキスト(*.csv);;XMLドキュメント(*.xml);;YAMLドキュメント (*.yaml) + + + Plain text file (*.txt) + プレインテキストファイル(*.txt) + + + Error saving file %1 + ファイル保存エラー %1 + + + + TimeShiftDialog + + Shift all packets by + すべてのパケットを調整します + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + パケット番号 + + + to + の時間を設定 + + + …then set packet + ...then set packet + …そしてパケット番号 + + + and extrapolate the time for all other packets + そして、他のすべてのパケットの時間を推定します + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + すべての調整を元に戻す + + + Time Shift + 時間調整 + + + Frame numbers must be between 1 and %1. + フレーム番号は 1 から %1 の間でないといけません + + + Invalid frame number. + フレーム番号が無効です + + + Time shifting is not available while capturing packets. + 時間調整はパケットキャプチャ中に利用できません + + + + TrafficTab + + Map file error + マップファイルエラー + + + Could not open base file %1 for reading: %2 + %2 を読み込む際にベースファイル %1 を開くことができません。 + + + No endpoints available to map + マップに利用できる終端がありません + + + Unable to create temporary file + 一時ファイルを作成できません + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>そのままの値より解決したアドレスととポート名を表示します 対応する名前解決設定が有効にされている必要があります</p></body></html> + + + Name resolution + 名前解決 + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>現在の表示フィルタに合致した対話のみ表示します</p></body></html> + + + Limit to display filter + 表示フィルタに制限 + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + <html><head/><body><p>フィルタ値に適当する形式のみ表示します</p></body></html> + + + Filter list for specific type + 特定の種別でリストをフィルタ + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>開始時間列に絶対的な時間を表示します</p></body></html> + + + GroupBox + グループボックス + + + Absolute start time + 絶対的開始時間 + + + Copy + コピー + + + Unknown + 不明 + + + + TrafficTree + + Resize all columns to content + すべての列を内容にあわせてリサイズします + + + Filter on stream id + ストリームIDに基づいてフィルタ + + + Copy %1 table + %1 表をコピー + + + as CSV + CSVとして + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + このページの全ての値をCSV(カンマ区切りテキスト)形式でクリップボードへコピーします + + + as YAML + YAMLとして + + + Copy all values of this page to the clipboard in the YAML data serialization format. + このページの全ての値をシリアル化されたYAMLデータ形式でクリップボードへコピーします + + + as JSON + JSONとして + + + Copy all values of this page to the clipboard in the JSON data serialization format. + このページの全ての値をシリアル化されたJSONデータ形式でクリップボードへコピーします + + + Save data as raw + Raw(無加工)形式でデータを保存 + + + Disable data formatting for export/clipboard and save as raw data + エクスポートやクリップボードのためにデータフォーマットを無効にしてRaw(無加工)形式で保存します + + + + TrafficTreeHeaderView + + Less than + より小さい + + + Greater than + より大きい + + + Equal + イコール + + + Columns to display + 表示する列 + + + Filter %1 by + %1 でフィルタします + + + Enter filter value + フィルタ値を入力します + + + + TrafficTypesModel + + Protocol + プロトコル + + + + UatDialog + + Create a new entry. + 新規エントリを作成します + + + Remove this entry. + Remove this profile. + このエントリを削除します + + + Copy this entry. + Copy this profile. + このエントリをコピーします + + + Move entry up. + エントリを上に移動します + + + Move entry down. + エントリを下に移動します + + + Clear all entries. + すべてのエントリをクリアします + + + Unknown User Accessible Table + ユーザがアクセスできる表が不明 + + + Open + 開く + + + + UatFrame + + Frame + フレーム + + + Create a new entry. + 新規エントリを作成します + + + Remove this entry. + このエントリを削除します + + + Copy this entry. + このエントリをコピーします + + + Move entry up. + エントリを上に移動します + + + Move entry down. + エントリを下に移動します + + + Clear all entries. + すべてのエントリをクリアします + + + Copy entries from another profile. + 別のプロファイルからエントリをコピーします + + + Copy from + からコピー + + + Unknown User Accessible Table + 不明なユーザがアクセスできる表 + + + Open + 開く + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>現在の表示フィルタに合致した対話のみ表示します</p></body></html> + + + Limit to display filter + 表示フィルタに制限 + + + Time of Day + 時刻 + + + Flow &Sequence + フロー順序(&S) + + + Show flow sequence for selected call(s). + 選択した通話のフロー順序を表示 + + + Prepare &Filter + フィルタを準備(&F) + + + Prepare a filter matching the selected calls(s). + 選択した通話に合致するフィルタを準備します + + + Cop&y + コピー(&y) + + + Open copy menu + コピーメニューを開く + + + All + すべて + + + Select all + すべてを選択 + + + None + なし + + + Invert + 反転 + + + Invert selection + 選択を反転します + + + Select related RTP streams + 関連するRTPストリームを選択します + + + Select RTP streams related to selected calls in RTP Streams dialog + RTPストリーム画面の選択された通話に関するRTPストリームを選択します + + + S + S + + + Deselect related RTP Streams + 関連するRTPストリームを選択解除します + + + D + D + + + Clear selection + 選択をクリア + + + Display time as time of day + 時間を時刻として表示します + + + Copy as CSV + CSVとしてコピー + + + Copy stream list as CSV. + CSVとしてストリーム一覧をコピーします + + + Copy as YAML + YAMLとしてコピー + + + Copy stream list as YAML. + YAMLとしてストリーム一覧をコピーします + + + SIP Flows + SIPフロー + + + VoIP Calls + VoIP通話 + + + as CSV + CSVとして + + + as YAML + YAMLとして + + + Select + 選択 + + + + VoipCallsInfoModel + + On + オン + + + Off + オフ + + + Tunneling: %1 Fast Start: %2 + トンネリング: %1 高速開始: %2 + + + Start Time + 開始時間 + + + Stop Time + 停止時間 + + + Initial Speaker + 初期話者 + + + From + 送信元 + + + To + 宛先 + + + Protocol + プロトコル + + + Duration + 時間 + + + Packets + パケット数 + + + State + 状態 + + + Comments + コメント + + + + WelcomePage + + Form + フォーム + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Wiresharkへようこそ</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>ファイルシステムのファイルを開く</p></body></html> + + + <h2>Open</h2> + <h2>開く</h2> + + + Recent capture files + 最近のキャプチャファイル + + + Capture files that have been opened previously + 以前に開いたキャプチャファイル + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>ネットワークから現在のパケットをキャプチャします</p></body></html> + + + <h2>Capture</h2> + <h2>キャプチャ</h2> + + + …using this filter: + …このフィルタを利用: + + + Interface list + インターフェース一覧 + + + List of available capture interfaces + 利用可能なキャプチャインターフェース一覧 + + + <h2>Learn</h2> + <h2>学習</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">ユーザーズガイド</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">質問と回答</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">メーリングリスト</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest Wireshark開発者会議</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">寄付</a></th> + +</tr></table> +</body></html> + + + Show in Finder + ファインダーで表示 + + + Show in Folder + フォルダで表示 + + + Welcome to %1 + %1 へようこそ + + + All interfaces shown + すべての表示されたインターフェース + + + %n interface(s) shown, %1 hidden + + %n インターフェース表示, %1 非表示 + + + + You are sniffing the glue that holds the Internet together using Wireshark + あなたはWiresharkを使ってインターネットを互いにつなぐ膠をキャプチャしています + + + You are running Wireshark + Wiresharkを起動中 + + + You receive automatic updates. + 自動アップデートを受信します + + + You have disabled automatic updates. + 自動アップデートを無効にしました + + + not found + 見つかりません + + + Copy file path + ファイルパスをコピー + + + Remove from list + リストから削除 + + + + WirelessFrame + + Frame + フレーム + + + Interface + インターフェース + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>802.11チャンネルを設定します</p></body></html> + + + Channel + チャンネル + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>キャプチャ中にすべてのフレーム、フレームチェックシーケンス(FCS)が有効なフレーム、もしくはFCSが無効なフレームを表示します</p></body></html> + + + FCS Filter + FCSフィルタ + + + All Frames + すべてのフレーム + + + Valid Frames + 有効フレーム + + + Invalid Frames + 無効フレーム + + + Wireless controls are not supported in this version of Wireshark. + 無線の制御はこのバージョンのWiresharkではサポートされていません + + + External Helper + 外部ヘルパー + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>復号化鍵を含むIEEE802.11設定を表示します</p></body></html> + + + 802.11 Preferences + 802.11設定 + + + AirPcap Control Panel + AirPcapコントロールパネル + + + Open the AirPcap Control Panel + AirPcapコントロールパネルを開く + + + Unable to set channel or offset. + チャンネルもしくはオフセットを設定できません + + + Unable to set FCS validation behavior. + FCSの確認動作を設定できません + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + パケット番号 %1 にはTSF タイムスタンプが含まれないため、タイムラインを表示しません + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + パケット番号 %u のTSFには大きな負数ジャンプがあり、タイムラインに表示できません。おそらく、TSF参照点の設定が誤っているかもしれません。 + + + + WiresharkDialog + + Failed to attach to tap "%1" + タップ "%1" の紐づけに失敗しました + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + パケットに移動 + + + Cancel + キャンセル + + + File Set + ファイルセット + + + Export Packet Dissections + エキスパートパケット解析 + + + Export Objects + オブジェクトをエクスポート + + + &Zoom + &ズーム + + + &Time Display Format + &時刻表示形式 + + + Copy + コピー + + + Manual pages + マニュアルページ + + + Apply as Filter + フィルタとして適用 + + + Prepare as Filter + フィルタとして準備 + + + SCTP + SCTP + + + TCP Stream Graphs + TCPストリームグラフ + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + ファイル(&F) + + + &Capture + キャプチャ(&C) + + + &Help + ヘルプ(&H) + + + &Go + 移動(&G) + + + &View + 表示(&V) + + + &Analyze + 分析(&A) + + + Follow + 追跡 + + + &Statistics + 統計(&S) + + + 29West + 29West + + + Topics + トピック + + + Queues + キュー + + + UIM + UIM + + + Telephon&y + 電話(&y) + + + RTSP + RTSP + + + &Edit + 編集(&E) + + + Packet Comments + パケットコメント + + + Main Toolbar + メインツールバー + + + Display Filter Toolbar + 表示フィルタツールバー + + + Open a capture file + キャプチャファイルを開きます + + + Quit Wireshark + Wiresharkを終了します + + + &Start + 開始(&S) + + + Start capturing packets + パケットキャプチャを開始します + + + S&top + 停止(&t) + + + Stop capturing packets + パケットキャプチャを停止します + + + No files found + ファイルが見つかりませんでした + + + &Contents + 目次(&C) + + + Wireshark Filter + Wiresharkフィルタ + + + TShark + Tshark + + + Rawshark + RawShark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Mergecap + + + Text2pcap + Text2cap + + + Website + ウェブサイト + + + Downloads + ダウンロード + + + Wiki + Wiki + + + Sample Captures + サンプルキャプチャ + + + &About Wireshark + wiresharkについて + + + Ask (Q&&A) + 問い合わせ (Q&&A) + + + Next Packet + 次のパケット + + + Go to the next packet + 次のパケットに移動します + + + Previous Packet + 前のパケット + + + Go to the previous packet + 前のパケットに移動します + + + First Packet + 最初のパケット + + + Go to the first packet + 最初のパケットに移動します + + + Last Packet + 最後のパケット + + + Go to the last packet + 最後のパケットに移動します + + + E&xpand Subtrees + サブツリーを展開(&x) + + + Expand the current packet detail + 現在のパケット詳細を展開します + + + &Expand All + すべて展開(&E) + + + Expand packet details + パケット詳細を展開 + + + Collapse &All + すべて閉じる(&A) + + + Collapse all packet details + すべてのパケット詳細を閉じます + + + Go to specified packet + 指定したパケットへ移動します + + + Merge one or more files + 1つそれ以上のファイルを結合します + + + Import a file + ファイルをインポート + + + &Save + 保存(&S) + + + Save as a different file + 異なるファイルとして保存 + + + Export specified packets + 指定したパケットを出力 + + + Export TLS Session Keys… + TLSセッション鍵をエクスポート… + + + List Files + ファイル一覧 + + + Next File + 次のファイル + + + Previous File + 前のファイル + + + &Reload + 再読込(&R) + + + Options + オプション + + + Capture options + キャプチャオプション + + + Capture filters + キャプチャフィルタ + + + Refresh Interfaces + インターフェースを更新 + + + Refresh interfaces + インターフェースを更新 + + + &Restart + 再キャプチャ(&R) + + + Restart current capture + 現在のキャプチャを再スタートします + + + As &CSV… + CSVとして(&C) + + + As "C" &Arrays… + C言語配列として(&A) + + + As P&SML XML… + PSML XMLとして(&S) + + + As P&DML XML… + PSML XMLとして(&D) + + + As &JSON… + JSONとして(&J) + + + Description + 記述 + + + Field Name + フィールド名 + + + Value + + + + As Filter + フィルタとして + + + Close this capture file + このキャプチャファイルを閉じます + + + Packet: + パケット: + + + Interface Toolbars + インターフェースツールバー + + + Colorize Conversation + 対話に色をつける + + + Internals + 内部 + + + Additional Toolbars + 追加のツールバー + + + Conversation Filter + 対話フィルタ + + + Reliable Server Pooling (RSerPool) + 信頼できるサーバープール (RSerPool) + + + SOME/IP + SOME/IP + + + &DTN + &DTN + + + Osmux + Osmux + + + &Tools + Tools + ツール(&T) + + + Wireless Toolbar + 無線ツールバー + + + Help contents + ヘルプ目次 + + + FAQs + FAQ's + + + Next Packet in Conversation + 対話の次のパケット + + + Go to the next packet in this conversation + この対話の次のパケットに移動します + + + Previous Packet in Conversation + 対話の前のパケット + + + Go to the previous packet in this conversation + この対話の前のパケットに移動します + + + Next Packet In History + ヒストリの次のパケット + + + Go to the next packet in your selection history + 選択ヒストリの次のパケットへ移動します + + + Previous Packet In History + ヒストリの前のパケット + + + Go to the previous packet in your selection history + 選択したヒストリの前のパケットへ移動します + + + Collapse Subtrees + サブツリーを閉じる + + + Collapse the current packet detail + 現在のパケット詳細を閉じます + + + Go to Packet… + パケットに移動… + + + &Merge… + 結合…(&M) + + + &Import from Hex Dump… + 16進数ダンプからインポート…(&I) + + + Save this capture file + このキャプチャファイルを保存します + + + Save &As… + …として保存(&A) + + + Export Specified Packets… + 指定したパケットをエクスポート… + + + Export Packet &Bytes… + パケットバイト列をエクスポート…(&B) + + + &Print… + 印刷(&P)… + + + Reload this file + このファイルを再読み込み + + + Reload as File Format/Capture + ファイルフォーマット/キャプチャとして再読み込みします + + + Copy this item's description + この項目の記述をコピーします + + + Copy this item's field name + この項目のフィールド名をコピーします + + + Copy this item's value + この項目の値をコピーします + + + Copy this item as a display filter + この項目を表示フィルタとしてコピーします + + + Apply as Column + 列として適用 + + + Create a packet list column from the selected field. + 選択したフィールドからパケット一覧の列を作成します + + + Find a packet + パケットを検索します + + + Find the next packet + 次のパケットを検索します + + + Find the previous packet + 前のパケットを検索します + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + パケットをマーク/マーク解除(&M) + + + Mark All Displayed + 表示されているものをすべてマークします + + + Mark all displayed packets + すべての表示されているパケットをマークします + + + Unmark all displayed packets + すべての表示されているパケットをマーク解除します + + + Next Mark + 次のマーク + + + Go to the next marked packet + 次にマークされたパケットに移動します + + + Previous Mark + 前のマーク + + + Go to the previous marked packet + 前にマークされたパケットに移動します + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + パケットを無視/無視を解除(&I) + + + Ignore All Displayed + 表示されているものすべてを無視します + + + Ignore all displayed packets + すべての表示されたパケットを無視します + + + Set/Unset Time Reference + 時間参照を設定/設定解除します + + + Set or unset a time reference for this packet + このパケットの時間参照を設定または設定解除します + + + Unset All Time References + すべての時間参照を設定解除 + + + Remove all time references + すべての時間参照を削除 + + + Next Time Reference + 次の時間参照 + + + Go to the next time reference + 次の時間参照へ移動します + + + Previous Time Reference + 前の時間参照 + + + Go to the previous time reference + 前の時間参照へ移動 + + + Shift or change packet timestamps + パケットのタイムスタンプをずらすもしくは変更します + + + Delete All Packet Comments + すべてのパケットコメントを削除 + + + Remove all packet comments in the capture file + キャプチャファイルにあるすべてのパケットを削除します + + + &Configuration Profiles… + 設定プロファイル(&C)… + + + Configuration profiles + 設定プロファイル + + + Manage your configuration profiles + 設定プロファイルを管理します + + + Manage Wireshark's preferences + Wiresharkの設定を管理します + + + Capture File Properties + キャプチャファイルプロパティ + + + Capture file properties + キャプチャファイルプロパティ + + + &Protocol Hierarchy + プロトコル階層(&P) + + + Show a summary of protocols present in the capture file. + キャプチャファイルの現在のプロトコル概要を表示します + + + Capinfos + Capinfos + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + タイムシーケンス (Stevens) + + + TCP time sequence graph (Stevens) + TCP タイムシーケンスグラフ (Stevens) + + + Throughput + スループット + + + Round Trip Time + 往復遅延時間 + + + TCP round trip time + TCP往復遅延時間 + + + Window Scaling + ウインドウスケーリング + + + TCP window scaling + TCPウインドウスケーリング + + + HTTP/2 Stream + HTTP/2 ストリーム + + + SIP Call + SIP通話 + + + Time Sequence (tcptrace) + タイムシーケンス(tcptrace) + + + TCP time sequence graph (tcptrace) + TCPタイムシーケンスグラフ(tcptrace) + + + Analyse this Association + このアソシエーションを分析 + + + Show All Associations + すべてのアソシエーションを表示 + + + Flow Graph + フローグラフ + + + Flow sequence diagram + フローシーケンス図 + + + ANCP + ANCP + + + ANCP statistics + ANCP統計 + + + Packets sorted by Instance ID + インスタンスID順パケット + + + BACapp statistics sorted by instance ID + インスタンスID順に並べたBACapp統計 + + + Packets sorted by IP + IP順パケット + + + BACapp statistics sorted by IP + IP順に並べたBACapp統計 + + + Packets sorted by object type + オブジェクト種別順パケット + + + BACapp statistics sorted by object type + オブジェクト種別順に並べたBACapp統計 + + + Packets sorted by service + サービス順パケット + + + BACapp statistics sorted by service + サービス順に並べたBACapp統計 + + + Collectd + 収集 + + + Collectd statistics + 収集統計 + + + DNS + DNS + + + DNS statistics + DNS統計 + + + HART-IP + HART-IP + + + HART-IP statistics + HART-IP統計 + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + hpfeeds統計 + + + HTTP2 + HTTP2 + + + HTTP2 statistics + HTTP2統計 + + + Packet Counter + パケットカウンタ + + + HTTP packet counter + HTTPパケットカウンタ + + + Requests + 要求 + + + HTTP requests + HTTP要求 + + + Load Distribution + 負荷分散 + + + HTTP load distribution + HTTP負荷分散 + + + Packet Lengths + パケット長 + + + Packet length statistics + パケット長統計 + + + Sametime + Sametime + + + Sametime statistics + Sametime統計 + + + SOME/IP Messages + SOME/IP メッセージ + + + SOME/IP Message statistics + SOME/IP メッセージ統計 + + + SOME/IP-SD Entries + SOME/IP-SD エントリ + + + SOME/IP-SD Entries statistics + SOME/IP-SD エントリ統計 + + + &LTP + &LTP + + + LTP segment and block statistics + LTPセグメントとブロック統計 + + + &ISUP Messages + ISUPメッセージ(&I) + + + ISUP message statistics + ISUPメッセージ統計 + + + Osmux packet counts + Osmux パケット数 + + + RTSP packet counts + RTSPパケット数 + + + SM&PP Operations + SMPP操作(&P) + + + SMPP operation statistics + SMPP操作統計 + + + &UCP Messages + UCPメッセージ(&U) + + + UCP message statistics + UCPメッセージ統計 + + + F1AP + F1AP + + + F1AP Messages + F1AP メッセージ + + + NGAP + NGAP + + + NGAP Messages + NGAP メッセージ + + + Change the way packets are dissected + パケットが解析される方法を変更します + + + Reload Lua Plugins + Luaプラグイン再読込 + + + Reload Lua plugins + Luaプラグインを再読込します + + + Advertisements by Topic + トピック毎の通知 + + + Advertisements by Source + 送信元毎の通知 + + + Advertisements by Transport + トランスポート毎の通知 + + + Queries by Topic + トピック毎のクエリ + + + Queries by Receiver + 受信者毎のクエリ + + + Wildcard Queries by Pattern + パターン毎のワイルドカードクエリ + + + Wildcard Queries by Receiver + 受信者毎のワイルドカードクエリ + + + Advertisements by Queue + キュー毎の通知 + + + Queries by Queue + キュー毎のクエリ + + + Streams + ストリーム + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + このアソシエーションをフィルタします + + + Strip Headers… + ヘッダーを除去… + + + Strip headers and export higher level encapsulations to file + ヘッダを除去して上位のカプセル化をファイルにエクスポートします + + + &I/O Graphs + 入出力グラフ(&I) + + + &Conversations + 対話(&C) + + + &Endpoints + 終端(&E) + + + Shrink the main window text + メインウインドウのテキストを縮小します + + + Return the main window text to its normal size + メインウインドウのテキストを標準サイズに戻します + + + Reset Layout + レイアウトをリセット + + + Reset appearance layout to default size + 外観のレイアウトをデフォルトのサイズにリセットします + + + Seconds Since First Captured Packet + 最初にキャプチャしたパケットからの経過時間 + + + Show packet times as the seconds since the first captured packet. + パケット時間を最初にキャプチャされたパケットからの秒数で表示します + + + Tenths of a millisecond + ×10ミリ秒 + + + Hundredths of a millisecond + ×100ミリ秒 + + + Tenths of a microsecond + ×10マイクロ秒 + + + Hundredths of a microsecond + ×100マイクロ秒 + + + Packet &Diagram + パケット &ダイアグラム + + + Show or hide the packet diagram + パケットダイアグラムを表示または非表示にします + + + Show each conversation hash table + 各々の会話ハッシュ表を表示します + + + Show each dissector table and its entries + 各々のダイセクター表とそのエントリを表示します + + + Show the currently supported protocols and display filter fields + サポートされているプロトコルと表示フィルタフィールドを表示します + + + MAC Statistics + MAC統計 + + + LTE MAC statistics + LTE MAC統計 + + + RLC Statistics + RLC統計 + + + LTE RLC statistics + LTE RLC統計 + + + LTE RLC graph + LTE RLCグラフ + + + MTP3 Summary + MTP3概要 + + + MTP3 summary statistics + MTP3概要統計 + + + Bluetooth Devices + Bluetoothデバイス + + + Bluetooth HCI Summary + Bluetooth HCI概要 + + + Display Filter &Expression… + 表示フィルタ式(&E) + + + Display Filter Expression… + 表示フィルタ式... + + + REGISTER_STAT_GROUP_RSERPOOL + REGISTER_STAT_GROUP_RSERPOOL + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + "REGISTER_STAT_GROUP_RSERPOOL"の開始 + + + No GSM statistics registered + GSM統計が登録されていません + + + No LTE statistics registered + LTE統計が登録されていません + + + No MTP3 statistics registered + MTP3統計が登録されていません + + + IAX2 Stream Analysis + IAX2ストリーム分析 + + + Show Packet Bytes… + パケットバイト列を表示… + + + Go to &Linked Packet + リンクされたパケットへ移動(&L) + + + UDP Multicast Streams + UDPマルチキャストストリーム + + + Show UTP multicast stream statistics. + UTPマルチキャストストリーム統計を表示します + + + WLAN Traffic + 無線LANトラフィック + + + Show IEEE 802.11 wireless LAN statistics. + IEEE802.11無線LAN統計を表示します + + + Add a display filter button. + 表示フィルタボタンを追加します + + + Firewall ACL Rules + ファイアウォール ACL ルール + + + Create firewall ACL rules + ファイアウォール ACL ルールを作成します + + + &Full Screen + フルスクリーン(&F) + + + Credentials + 証明書 + + + MAC Address Blocks + MACアドレスブロック + + + TLS Keylog Launcher + TLSキーログランチャー + + + Release Notes + リリースノート + + + &Options… + オプション…(&O) + + + &Wireless + 無線(&W) + + + Capture &Filters… + キャプチャフィルタ…(&F) + + + As Plain &Text… + プレインテキストとして…(&T) + + + As Plain &Text + プレインテキストとして(&T) + + + As &CSV + CSVとして(&C) + + + As &YAML + YAMLとして(&Y) + + + All Visible Items + すべての見えている項目 + + + All Visible Selected Tree Items + すべての見えている選択されたツリー項目 + + + Display Filter &Macros… + 表示フィルタマクロ…(&M) + + + &Find Packet… + パケットの検索…(&F) + + + Find Ne&xt + 次を検索(&x) + + + Find Pre&vious + 前を検索(&v) + + + Mark or unmark each selected packet + 各々の選択されたパケットをマーク/マーク解除します + + + Ignore or unignore each selected packet + 各々の選択されたパケットを無視/無視を解除します + + + U&nignore All Displayed + すべての表示されているものの無視を解除します(&U) + + + Unignore all displayed packets + すべての表示されたパケットの無視を解除します + + + Time Shift… + 時間調整… + + + Inject TLS Secrets + TLSシークレットを注入 + + + Embed used TLS secrets in the capture file + キャプチャファイルで使用されたTLSシークレットを埋め込みます + + + Discard All Secrets + すべてのシークレットを破棄 + + + Discard all decryption secrets in the capture file + キャプチャファイルのすべての復号化シークレットを破棄します + + + &Preferences… + 設定…(&P) + + + TCP throughput + TCP スループット + + + Request Sequences + リクエストシーケンス + + + HTTP Request Sequences + HTTPリクエストシーケンス + + + Decode &As… + …としてデコード(&A) + + + Export PDUs to File… + PDUをファイルにエクスポート… + + + Create graphs based on display filter fields + 表示フィルタのフィールドに基づいてグラフを作成します + + + &Main Toolbar + メインツールバー(&M) + + + Show or hide the main toolbar + メインツールバーを表示もしくは非表示にします + + + &Filter Toolbar + フィルタツールバー(&F) + + + Show or hide the display filter toolbar + 表示フィルタツールバーを表示もしくは非表示にします + + + Conversations at different protocol levels + 異なるプロトコルレベルの対話 + + + Endpoints at different protocol levels + 異なるプロトコルレベルの終端 + + + Colorize Packet List + パケット一覧を色付けする + + + Draw packets using your coloring rules + 色付けルールを用いてパケットを描画します + + + &Zoom In + 拡大(&Z) + + + Enlarge the main window text + メインウインドウのテキストを拡大します + + + Zoom Out + 縮小 + + + Normal Size + 標準サイズ + + + Resize Columns + 列幅を再調整 + + + Resize packet list columns to fit contents + 列の内容に合わせてパケット一覧の列幅を調整します + + + Date and Time of Day (1970-01-01 01:02:03.123456) + 日時 (1973-06-14 01:02:03.123456) + + + Show packet times as the date and time of day. + パケット時間を日付と時刻で表示します。 + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + 年、通年日、時刻 (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + パケット時間を年、通年日、時刻で表示します + + + Time of Day (01:02:03.123456) + 時刻 (01:02:03.123456) + + + Seconds Since 1970-01-01 + 1970年1月1日からの秒数 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + パケットの時間をUNIX/POSIX起源(1970-01-01)以来の秒数で表示します + + + Seconds Since Previous Captured Packet + 前にキャプチャされたパケットからの秒数 + + + Show packet times as the seconds since the previous captured packet. + パケット時間を前にキャプチャされたパケットからの秒数で表示します + + + Seconds Since Previous Displayed Packet + 前に表示されたパケットからの秒数 + + + Show packet times as the seconds since the previous displayed packet. + パケット時間を前に表示されたパケットからの秒数で表示します + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + UTC日時 (1973-06-14 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + パケット時間をUTC日付および時刻で表示します + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + UTC年、通年日、時刻 (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + パケット時間をUTC年、通年日、時刻で表示します + + + UTC Time of Day (01:02:03.123456) + UTC時刻 (01:02:03.123456) + + + Show packet times as the UTC time of day. + パケット時間をUTC時刻として表示します + + + Automatic (from capture file) + 自動(キャプチャファイルから) + + + Use the time precision indicated in the capture file. + キャプチャファイルに記述された時間精度を利用します + + + Seconds + + + + Tenths of a second + ×10秒 + + + Hundredths of a second + ×100秒 + + + Milliseconds + ミリ秒 + + + Microseconds + マイクロ秒 + + + Nanoseconds + ナノ秒 + + + Display Seconds With Hours and Minutes + 時分秒表示 + + + Display seconds with hours and minutes + 時分秒で表示します + + + Resolve &Physical Addresses + 物理アドレスを解決(&P) + + + Show names for known MAC addresses. Lookups use a local database. + 既知のMACアドレスの名前を表示します ローカルデータベースを参照します + + + Resolve &Network Addresses + ネットワークアドレスを解決(&N) + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + 既知のIPv4、IPv6およびIPXアドレスの名前を表示します 解決にはネットワークトラフィックを生成します + + + Resolve &Transport Addresses + トランスポート層アドレスを解決(&T) + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + 既知のTCP UDPおよびSCTPサービスの名前を表示します システムによっては解決にトラフィックを生成します + + + Wire&less Toolbar + 無線ツールバー(&l) + + + Show or hide the wireless toolbar + 無線ツールバーを表示または非表示にします + + + &Status Bar + ステータスバー(&S) + + + Show or hide the status bar + ステータスバーを表示または非表示にします + + + Packet &List + パケット一覧(&L) + + + Show or hide the packet list + パケット一覧を表示または非表示にします + + + Packet &Details + パケット詳細(&D) + + + Show or hide the packet details + パケット詳細を表示または非表示にします + + + Packet &Bytes + パケットバイト列(&B) + + + Show or hide the packet bytes + パケットバイト列を表示または非表示にします + + + &Conversation Hash Tables + 対話ハッシュ表(&C) + + + &Dissector Tables + ダイセクター表(&D) + + + &Supported Protocols + サポートされているプロトコル(&S) + + + MAP Summary + MAP概要 + + + GSM MAP summary statistics + GSM MAP 概要統計 + + + RLC &Graph + RLCグラフ(&G) + + + &Coloring Rules… + 色付けルール…(&C) + + + Show Linked Packet in New Window + 新規ウインドウでリンクされたパケットを表示 + + + New Coloring Rule… + New Conversation Rule… + 新規対話ルール… + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + 選択したストリームのRTPストリーム分析。CTRLキーを押して逆方向のストリームも追加できます + + + RTP Player + RTPプレイヤー + + + Play selected stream. Press CTRL key for playing reverse stream too. + 選択したストリームを再生します。CTRLキーを押して逆方向のストリームも再生します。 + + + IA&X2 Stream Analysis + IAX2ストリーム分析(&X) + + + Enabled Protocols… + Enable Protocols… + 有効化したプロトコル… + + + Wiki Protocol Page + Wikiプロトコルページ + + + Open the Wireshark wiki page for this protocol. + このプロトコルについてのWireshark Wikiページを開きます + + + Filter Field Reference + フィルタフィールドリファレンス + + + Open the display filter reference page for this filter field. + このフィルタフィールドについての表示フィルタリファレンスを開きます + + + Go to the packet referenced by the selected field. + 選択されたフィールドによって参照されたパケットへ移動します + + + &VoIP Calls + VoIP通話(&V) + + + Open &Recent + 最近使ったファイルを開く(&R) + + + Name Resol&ution + 名前解決(&u) + + + Service &Response Time + サービス応答時間(&R) + + + &RTP + &RTP + + + S&CTP + S&CTP + + + &ANSI + &ANSI + + + &GSM + &GSM + + + &LTE + &LTE + + + &MTP3 + &MTP3 + + + &Open + 開く(&O) + + + &Quit + 終了(&Q) + + + &Close + 閉じる(&C) + + + Display &Filters… + 表示フィルタ(&F)… + + + &Unmark All Displayed + 表示されているものすべてをマーク解除(&U) + + + All VoIP Calls + すべてのVoIP通話 + + + SIP &Flows + SIPフロー(&F) + + + SIP Flows + SIPフロー + + + RTP Streams + RTPストリーム + + + Edit the packet list coloring rules. + パケット一覧の色付けルールを編集します + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Bluetooth ATTサーバ属性 + + + Show Packet in New &Window + 新規ウインドウでパケットを表示(&W) + + + Show this packet in a separate window. + 個別のウィンドウでこのパケットを表示 + + + Show the linked packet in a separate window. + 個別のウインドウでリンクされたパケットを表示します + + + Auto Scroll in Li&ve Capture + 生のキャプチャを自動スクロール(&v) + + + Automatically scroll to the last packet during a live capture. + 生キャプチャ中の最後のパケットを自動スクロールします + + + Expert Information + エキスパート情報 + + + Show expert notifications + エキスパート通知を表示します + + + Add an expression to the display filter. + 表示フィルタの書式を追加します + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + "RREGISTER_STAT_GROUP_UNSORTED"Rの開始 + + + No ANSI statistics registered + No tools registered + ANSI統計は登録されていません + + + Resolved Addresses + 解決したアドレス + + + Show each table of resolved addresses as copyable text. + コピー可能なテキストとして解決したアドレスの表を表示します + + + Color &1 + 色 &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + 独自の色で現在の対話をマークします + + + Color &2 + 色 &2 + + + Color &3 + 色 &3 + + + Color &4 + 色 &4 + + + Color &5 + 色 &5 + + + Color &6 + 色 &6 + + + Color &7 + 色 &7 + + + Color &8 + 色 &8 + + + Color &9 + 色 &9 + + + Color 1&0 + 色 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + この対話に基づいた新しい色付けルールを作成します + + + Reset Colorization + 色付けをリセット + + + Reset colorized conversations. + 対話の色付けをリセットします + + + RTP Stream Analysis + RTPストリーム分析 + + + Edit Resolved Name + 解決した名前を編集 + + + Manually edit a name resolution entry. + 名前解決エントリを手動で編集します + + + Enable and disable specific protocols + 指定プロトコルを有効および無効化します + + + before quitting + 終了前 + + + Save packets before merging? + 結合する前にパケットを保存しますか? + + + A temporary capture file can't be merged. + 一時的なキャプチャファイルは結合することができません + + + Save changes in "%1" before merging? + 結合する前に "%1" に変更を保存しますか? + + + Changes must be saved before the files can be merged. + ファイルを結合する前に変更を保存する必要があります + + + Invalid Display Filter + 無効な表示フィルタ + + + Invalid Read Filter + 無効な読込フィルタ + + + The filter expression %1 isn't a valid read filter. (%2). + フィルタ式 %1 は有効な読込みフィルタではありません(%2) + + + before importing a capture + before importing a new capture + 新しいキャプチャファイルをインポートする前に + + + Unable to export to "%1". + "%1" をエクスポートすることができません + + + You cannot export packets to the current capture file. + 現在のキャプチャファイルへパケットをエクスポートすることはできません + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + %1 に作成した変更を保存したいですか? + + + Your captured packets will be lost if you don't save them. + 保存しない場合キャプチャしたパケットは失われます + + + Do you want to save the changes you've made to the capture file "%1"%2? + キャプチャスタイルに行った変更を保存しますか + + + Your changes will be lost if you don't save them. + 保存しない場合変更は失われます + + + Check for Updates… + 更新を確認… + + + Unable to drop files during capture. + キャプチャ中にファイルをドロップできません + + + Unknown file type returned by merge dialog. + 結合画面から返された未知のファイルタイプ + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + どうかこれをWiresharkの問題として以下に報告ください。https://gitlab.com/wireshark/wireshark/-/issues + + + Unknown file type returned by export dialog. + エクスポート画面から返された未知のファイルタイプ + + + Do you want to stop the capture and save the captured packets%1? + キャプチャを停止してキャプチャしたパケットを保存しますか?%1 + + + Do you want to save the captured packets%1? + キャプチャしたパケットを保存しますか?%1 + + + Save before Continue + 続ける前に保存 + + + Stop and Save + 停止して保存 + + + Stop and Quit &without Saving + Stop and Quit without Saving + 保存しないで停止して終了(&w) + + + Quit &without Saving + Quit without Saving + 保存しないで終了(&w) + + + There is no "rtp.ssrc" field in this version of Wireshark. + このバージョンのWiresharkには "rtp.ssrc" フィールドはありません + + + Please select an RTPv2 packet with an SSRC value + SSRC値をもつRTPv2パケットを選んでください + + + SSRC value not found. + SSRC値が見つかりませんでした + + + Show or hide the toolbar + ツールバーを表示/非表示 + + + Continue &without Saving + Continue without Saving + 保存せずに続ける(&w) + + + Stop and Continue &without Saving + Stop and Continue without Saving + 保存せずに停止して続ける(&w) + + + The Wireshark Network Analyzer + ワイヤーシャークネットワークアナライザ + + + Capturing from %1 + %1 からキャプチャ中 + + + before opening another file + 別のファイルを開く前に + + + Merging files. + ファイルを結合 + + + %1: %2 + %1: %2 + + + Clear Menu + メニューをクリア + + + before closing the file + ファイルを閉じる前に + + + Export Selected Packet Bytes + 選択したパケットバイト列をエクスポート + + + No Keys + キーはありません + + + Export SSL Session Keys (%Ln key(s)) + Export SSL Session Keys (%1 key%2 + + SSLセッション鍵をエクスポート (%Ln 鍵) + + + + Raw data (*.bin *.dat *.raw);;All Files ( + Raw(無加工)データ形式 (*.bin *.dat *.raw);;すべてのファイル( + + + Couldn't copy text. Try another item. + テキストをコピーできません 別の項目を試してみてください + + + Are you sure you want to remove all packet comments? + 本当にすべてのパケットコメントを削除してよいですか? + + + Unable to build conversation filter. + 会話フィルタを作成できません + + + before reloading the file + ファイルを再読み込みする前に + + + Error compiling filter for this conversation. + この対話の間のフィルタ翻訳エラー + + + No previous/next packet in conversation. + 対話の前/次のパケットがありません + + + No interface selected. + インターフェースが選択されていません + + + Saving %1… + 保存中 %1… + + + Configure all extcaps before start of capture. + キャプチャ開始前にすべてのextcapを設定します + + + Invalid capture filter. + 無効なキャプチャフィルタ + + + (empty comment) + placeholder for empty comment + (コメント空) + + + Add New Comment… + 新規コメントを追加… + + + Edit "%1" + edit packet comment + 編集 "%1" + + + Delete "%1" + delete packet comment + 削除 "%1" + + + Delete packet comments + パケットコメントを削除 + + + Delete comments from %n packet(s) + + %n パケットよりコメントを削除 + + + + before starting a new capture + 新規キャプチャを開始する前に + + + before reloading Lua plugins + Lua プラグインの再読込前 + + + Please wait while Wireshark is initializing… + Wiresharkが初期化している間お待ちください… + + + before updating + 更新前 + + + There are no TLS Session Keys to save. + 保存するTLSセッション鍵はありません + + + Export TLS Session Keys (%Ln key(s)) + + TLSセッション鍵をエクスポート (%Ln keys) + + + + TLS Session Keys (*.keys *.txt);;All Files ( + TLSセッション鍵 (*.keys *.txt);;すべてのファイル ( + + + No TLS Secrets + TLSシークレットなし + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + キャプチャファイルにはTLSトラフィックを復号化するために用いられる利用可能なシークレットはありません。WikiでTLSトラフィックを復号する方法についての情報をご覧になりますか? + + + Are you sure you want to discard all decryption secrets? + 本当にすべての復号化シークレットを破棄されますか? + + + No filter available. Try another %1. + フィルタが利用できません 別の %1 を利用してください + + + column + + + + item + 項目 + + + The "%1" column already exists. + "%1" 列は既に存在します + + + The "%1" column already exists as "%2". + "%1" 列は "%2" として既に存在します + + + RTP packet search failed + RTPパケットの検索に失敗しました + + + No Interface Selected. + インターフェースが選択されていません + + + before restarting the capture + キャプチャを再スタートする前に + + + Wiki Page for %1 + %1 の Wikiページ + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>ワイヤーシャークWikiはコミュニティによって運営されています </p><p>いま見ているページはすばらしかったり、不完全だったり、間違っていたり、存在しないかもしれません。</p><p>Wikiに移動しますか?</p> + + + Loading + ロード中 + + + Reloading + 再読み込み中 + + + Rescanning + 再スキャニング中 + + + + WlanStatisticsDialog + + Wireless LAN Statistics + 無線LAN統計 + + + Channel + チャンネル + + + SSID + SSID + + + Percent Packets + パケット割合 + + + Percent Retry + 再送率 + + + Probe Reqs + プルーブ要求 + + + Probe Resp + プルーブ応答 + + + Auths + 認証 + + + Retry + 再送 + + + Deauths + 非認証 + + + Other + その他 + + + diff --git a/ui/qt/wireshark_ko.ts b/ui/qt/wireshark_ko.ts new file mode 100644 index 00000000..495b3d61 --- /dev/null +++ b/ui/qt/wireshark_ko.ts @@ -0,0 +1,14809 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + Wireshark 정보 + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">네트워크 프로토콜 분석기</span> + + + Copy the version information to the clipboard + 버전 정보를 클립보드에 복사 + + + Copy to Clipboard + 클립보드에 복사 + + + Authors + 제작자 + + + Search Authors + 제작자 검색 + + + Folders + 폴더 + + + Filter by path + 경로로 필터 + + + Plugins + 플러그인 + + + No plugins found. + 플러그인을 찾을 수 없습니다. + + + Search Plugins + 플러그인 검색 + + + Filter by type: + 유형별 필터: + + + Keyboard Shortcuts + 키보드 단축키 + + + Search Shortcuts + 단축키 검색 + + + Acknowledgments + 감사 표시 + + + License + 라이선스 + + + The directory does not exist + 디렉터리가 존재하지 않음 + + + Should the directory %1 be created? + %1 디렉터리를 생성하시겠습니까? + + + The directory could not be created + 디렉터리를 생성할 수 없음 + + + The directory %1 could not be created. + %1 디렉터리를 생성할 수 없습니다. + + + Show in Finder + Finder에 표시 + + + Show in Folder + 폴더 위치 표시 + + + Copy + 복사 + + + Copy Row(s) + + 행 복사 + + + + + AddressEditorFrame + + Frame + 프레임 + + + Name Resolution Preferences… + Name Resolution Preferences... + 이름 해석 설정… + + + Address: + 주소: + + + Name: + 이름: + + + Can't assign %1 to %2. + %1을(를) %2에 할당할 수 없습니다. + + + + AdvancedPrefsModel + + Name + 이름 + + + Status + 상태 + + + Type + 유형 + + + Value + + + + + ApplyLineEdit + + Apply changes + 변경 사항 적용 + + + + AuthorListModel + + Name + 이름 + + + Email + 이메일 + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + 블루투스 ATT 서버 속성 + + + Handle + 핸들 + + + UUID + UUID + + + UUID Name + UUID 이름 + + + All Interfaces + 모든 인터페이스 + + + All Devices + 모든 장치 + + + Remove duplicates + 중복된 내용 삭제 + + + Copy Cell + 셀 복사 + + + Copy Rows + 행 복사 + + + Copy All + 모두 복사 + + + Save as image + 그림으로 저장 + + + Mark/Unmark Row + 행 마크/해제 + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + 셀 마크/해제 + + + Save Table Image + 표를 그림으로 저장 + + + PNG Image (*.png) + PNG 그림 (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + 블루투스 장치 + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + 이름 + + + Class of Device + 장치 클래스 + + + LMP Version + LMP 버전 + + + LMP Subversion + LMP 하위 버전 + + + Manufacturer + 제조사 + + + HCI Version + HCI 버전 + + + HCI Revision + HCI 리비전 + + + Scan + 스캔 + + + Authentication + 인증 + + + Encryption + 암호화 + + + ACL MTU + ACL MTU + + + ACL Total Packets + ACL 전체 패킷 + + + SCO MTU + SCO MTU + + + SCO Total Packets + SCO 전체 패킷 + + + LE ACL MTU + LE ACL MTU + + + LE ACL Total Packets + LE ACL 전체 패킷 + + + LE ISO MTU + LE ISO MTU + + + LE ISO Total Packets + LE ISO 전체 패킷 + + + Inquiry Mode + 질의 모드 + + + Page Timeout + 페이지 시간 초과 + + + Simple Pairing Mode + 심플 페어링 모드 + + + Voice Setting + 음성 설정 + + + Value + + + + Changes + 변경 + + + %1 changes + 변경 %1회 + + + Copy Cell + 셀 복사 + + + Copy Rows + 행 복사 + + + Copy All + 모두 복사 + + + Save as image + 그림으로 저장 + + + Mark/Unmark Row + 행 마크/해제 + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + 셀 마크/해제 + + + Unknown + 알 수 없음 + + + Bluetooth Device - %1%2 + 블루투스 장치 - %1%2 + + + enabled + 활성화됨 + + + disabled + 비활성화됨 + + + %1 ms (%2 slots) + %1 밀리초 (%2 슬롯) + + + Save Table Image + 표를 그림으로 저장 + + + PNG Image (*.png) + PNG 그림 (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + 블루투스 장치 + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + 이름 + + + LMP Version + LMP 버전 + + + LMP Subversion + LMP 하위 버전 + + + Manufacturer + 제조사 + + + HCI Version + HCI 버전 + + + HCI Revision + HCI 리비전 + + + Is Local Adapter + 로컬 어댑터 여부 + + + All Interfaces + 모든 인터페이스 + + + Show information steps + 정보 단계 표시 + + + %1 items; Right click for more option; Double click for device details + 항목 %1개. 더 많은 옵션을 보려면 마우스 오른쪽 단추로 클릭, 장치의 자세한 정보를 보려면 두 번 클릭 + + + Copy Cell + 셀 복사 + + + Copy Rows + 행 복사 + + + Copy All + 모두 복사 + + + Save as image + 그림으로 저장 + + + Mark/Unmark Row + 행 마크/해제 + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + 셀 마크/해제 + + + true + true + + + Save Table Image + 표를 그림으로 저장 + + + PNG Image (*.png) + PNG 그림 (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + 블루투스 HCI 요약 + + + Name + 이름 + + + OGF + OGF + + + OCF + OCF + + + Opcode + Opcode + + + Event + 이벤트 + + + Subevent + 하위 이벤트 + + + Status + 상태 + + + Reason + 원인 + + + Hardware Error + 하드웨어 오류 + + + Occurrence + 출현 횟수 + + + Link Control Commands + 연결 제어 명령 + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + 연결 정책 명령 + + + 0x02 + 0x02 + + + Controller & Baseband Commands + 컨트롤러 및 기저 대역 명령 + + + 0x03 + 0x03 + + + Informational Parameters + 정보성 인자 + + + 0x04 + 0x04 + + + Status Parameters + 상태 인자 + + + 0x05 + 0x05 + + + Testing Commands + 테스트 명령 + + + 0x06 + 0x06 + + + LE Controller Commands + LE 컨트롤러 명령 + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + 블루투스 로고 테스트 명령 + + + 0x3E + 0x3E + + + Vendor-Specific Commands + 제조사 지정 명령 + + + 0x3F + 0x3F + + + Unknown OGF + 알 수 없는 OGF + + + Events + 이벤트 + + + Hardware Errors + 하드웨어 오류 + + + Results filter: + 결과 필터: + + + Display filter: + 표시 필터: + + + All Interfaces + 모든 인터페이스 + + + All Adapters + 모든 어댑터 + + + Copy Cell + 셀 복사 + + + Copy Rows + 행 복사 + + + Copy All + 모두 복사 + + + Save as image + 그림으로 저장 + + + Mark/Unmark Row + 행 마크/해제 + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + 셀 마크/해제 + + + Unknown + 알 수 없음 + + + Adapter %1 + 어댑터 %1 + + + Frame %1 + 프레임 %1 + + + Pending + 보류 중 + + + Save Table Image + 표를 그림으로 저장 + + + PNG Image (*.png) + PNG 그림 (*.png) + + + + ByteViewTab + + Packet bytes + 패킷 바이트 + + + + ByteViewText + + Allow hover highlighting + 마우스로 가리킬 때 강조 표시 허용 + + + Show bytes as hexadecimal + 16진수로 바이트 표시 + + + …as decimal + 10진수로 표시 + + + …as octal + 8진수로 표시 + + + …as bits + 비트 단위로 표시 + + + Show text based on packet + 패킷의 내용을 텍스트로 표시 + + + …as ASCII + ASCII로 표시 + + + …as EBCDIC + EBCDIC으로 표시 + + + + CaptureFile + + [closing] + [닫는 중] + + + [closed] + [닫힘] + + + + CaptureFileDialog + + This capture file contains comments. + 이 캡처 파일에 주석이 포함되어 있습니다. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + 선택한 파일 형식은 주석을 지원하지 않습니다. 주석을 지원하는 형식으로 캡처한 내용을 저장하시겠습니까? 아니면 주석을 제외하고 선택한 형식으로 저장하시겠습니까? + + + Discard comments and save + 주석 제외 후 저장 + + + Save in another format + 다른 형식으로 저장 + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + 저장할 수 있는 파일 형식이 주석을 지원하지 않습니다. 주석을 제외하고 선택한 형식으로 저장하시겠습니까? + + + All Files ( + 모든 파일 ( + + + All Capture Files + 모든 캡처 파일 + + + Format: + 형식: + + + Size: + 크기: + + + Start / elapsed: + 시작/경과: + + + Automatically detect file type + 파일 형식을 자동으로 감지 + + + %1, error after %Ln packet(s) + %1, error after %2 packets + + %1, 패킷 %Ln개 이후 오류 + + + + %1, timed out at %Ln packet(s) + %1, timed out at %2 packets + + %1, 패킷 %Ln개 이후 시간 초과됨 + + + + %1, %Ln packet(s) + + %1, 패킷 %Ln개 + + + + Prepend packets + 앞쪽에 패킷 넣기 + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + 선택한 파일의 패킷을 현재 파일 앞에 추가합니다. 패킷의 타임스탬프는 무시됩니다. + + + Merge chronologically + 시간순으로 병합 + + + Insert packets in chronological order. + 시간순으로 패킷을 추가합니다. + + + Append packets + 뒤쪽에 패킷 넣기 + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + 선택한 파일의 패킷을 현재 파일 뒤에 추가합니다. 패킷의 타임스탬프는 무시됩니다. + + + Read filter: + 읽기 필터: + + + Compress with g&zip + gzip 형식으로 압축(&Z) + + + Open Capture File + Wireshark: Open Capture File + 캡처 파일 열기 + + + Save Capture File As + Wireshark: Save Capture File As + 다른 이름으로 캡처 파일 저장 + + + Save as: + 다른 이름으로 저장: + + + Export Specified Packets + Wireshark: Export Specified Packets + 지정된 패킷 내보내기 + + + Export as: + 내보낼 형식: + + + Merge Capture File + Wireshark: Merge Capture File + 캡처 파일 병합 + + + Unknown file type returned by save as dialog. + 다른 이름으로 저장 대화 상자에서 알 수 없는 파일 형식이 반환되었습니다. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + 이 문제를 Wireshark 이슈로 보고해 주십시오. https://gitlab.com/wireshark/wireshark/-/issues + + + directory + 디렉터리 + + + unknown file format + 알 수 없는 파일 형식 + + + error opening file + 파일 열기 오류 + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + %1, 데이터 레코드 %Ln 이후 오류 + + + + %1, timed out at %Ln data record(s) + + %1, 데이터 레코드 %Ln 이후 시간 초과 + + + + %1, %Ln data record(s) + + %1, 데이터 레코드 %Ln개 + + + + unknown + 알 수 없음 + + + + CaptureFilePropertiesDialog + + Details + 자세한 정보 + + + Capture file comments + 캡처 파일 주석 + + + Refresh + 새로 고침 + + + Copy To Clipboard + 클립보드에 복사 + + + Save Comments + 주석 저장 + + + Capture File Properties + 캡처 파일 속성 + + + Unknown + 알 수 없음 + + + File + 파일 + + + Name + 이름 + + + Length + 길이 + + + Hash (SHA256) + 해시(SHA256) + + + Hash (SHA1) + 해시(SHA1) + + + Format + 형식 + + + Encapsulation + 캡슐화 + + + Snapshot length + 스냅샷 길이 + + + Time + 시간 + + + First packet + 처음 패킷 + + + Last packet + 마지막 패킷 + + + Elapsed + 경과 시간 + + + Section %1 + %1 부분 + + + Capture + 캡처 + + + Hardware + 하드웨어 + + + OS + 운영 체제 + + + Application + 응용 프로그램 + + + Interfaces + 인터페이스 + + + Interface + 인터페이스 + + + Dropped packets + 누락된 패킷 + + + Capture filter + 캡처 필터 + + + Link type + 연결 유형 + + + Packet size limit (snaplen) + 패킷 크기 제한(snaplen) + + + none + 없음 + + + %1 bytes + %1바이트 + + + Statistics + 통계 + + + Measurement + 측정 + + + Captured + 캡처됨 + + + Displayed + 표시됨 + + + Marked + 마크됨 + + + Packets + 패킷 수 + + + Time span, s + 시간 간격, 초 + + + Average pps + 초당 평균 패킷 + + + Average packet size, B + 평균 패킷 크기, 바이트 + + + Bytes + 바이트 + + + Average bytes/s + 초당 평균 바이트 + + + Average bits/s + 초당 평균 비트 수 + + + Section Comment + 부분 주석 + + + Packet Comments + 패킷 주석 + + + <p>Frame %1: + <p>프레임 %1: + + + Created by Wireshark %1 + + + Wireshark %1에서 생성됨 + + + + + + CaptureFilterCombo + + Capture filter selector + 캡처 필터 선택자 + + + + CaptureFilterEdit + + Capture filter entry + 캡처 필터 항목 + + + Manage saved bookmarks. + 저장한 북마크를 관리합니다. + + + Apply this filter string to the display. + 디스플레이에 이 필터 문자열을 적용합니다. + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + 여러 필터를 선택했습니다. 여기에서 필터를 덮어쓸 수 있으며, 비워 두면 유지합니다. + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>선택한 인터페이스에 다른 캡처 필터가 있습니다. 여기에서 필터를 덮어쓸 수 있으며, 비워 두면 필터를 유지합니다.</p> + + + Enter a capture filter %1 + 캡처 필터 입력 %1 + + + Save this filter + 이 필터 저장 + + + Remove this filter + 이 필터 삭제 + + + Manage Capture Filters + 캡처 필터 관리 + + + + CaptureInfoDialog + + Capture Information + 캡처 정보 + + + Stop Capture + 캡처 정지 + + + %1 packets, %2:%3:%4 + 패킷 %1개, %2:%3:%4 + + + + CaptureInfoModel + + Other + 기타 + + + + CaptureOptionsDialog + + Input + 입력 + + + Interface + 인터페이스 + + + Traffic + 트래픽 + + + Link-layer Header + 링크 레이어 헤더 + + + Promiscuous + 무작위 + + + Snaplen (B) + Snaplen(B) + + + Buffer (MB) + 버퍼(MB) + + + Monitor Mode + 모니터 모드 + + + Capture Filter + 캡처 필터 + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>이 옵션을 활성화하는 것을 추천합니다. 일반적으로 네트워크 카드는 자신의 네트워크 주소로 전송된 트래픽만 캡처합니다. 네트워크 카드가 &quot;볼 수 있는&quot; 모든 트래픽을 캡처하려면 이 옵션을 활성화하십시오. 스위치 네트워크에서 패킷을 캡처할 때에 대한 자세한 내용은 FAQ를 참조하십시오.</p></body></html> + + + Enable promiscuous mode on all interfaces + 모든 인터페이스에서 무작위 모드 사용 + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + 인터페이스를 표시하거나 숨기고, 설명을 추가하고, 파이프 및 원격 인터페이스를 관리합니다. + + + Manage Interfaces… + 인터페이스 관리… + + + Capture filter for selected interfaces: + 선택한 인터페이스에 대한 캡처 필터: + + + Compile BPFs + BPF 컴파일 + + + Output + 출력 + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>캡처 데이터를 저장할 별도의 파일 이름을 입력하십시오. 기본값으로 임시 파일을 사용합니다.</p></body></html> + + + Capture to a permanent file + 영구 파일로 캡처 + + + File: + 파일: + + + Browse… + 찾아보기... + + + Output format: + 출력 형식: + + + pcapng + pcapng + + + pcap + pcap + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>캡처 파일을 여러 개 생성합니다.</p><p>생성된 파일 이름은 일련 번호와 캡처 시작 시간을 포함합니다.</p><p>참고: 이 옵션을 활성화하면 새로운 파일 생성 기준 중 하나 이상을 선택해야 합니다.</p></body></html> + + + Create a new file automatically… + 자동으로 새 파일 생성하기… + + + after + 다음 양 이후: + + + Switch to the next file after the specified number of packets have been captured. + 지정한 개수의 패킷을 캡처한 후 다음 파일로 전환합니다. + + + packets + 개 패킷 + + + Switch to the next file after the file size exceeds the specified file size. + 지정한 파일 크기를 초과한 후 다음 파일로 전환합니다. + + + kilobytes + 킬로바이트 + + + megabytes + 메가바이트 + + + gigabytes + 기가바이트 + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + 지정한 시간 동안 캡처한 후 다음 파일로 전환합니다. + + + seconds + + + + minutes + + + + hours + 시간 + + + when time is a multiple of + 시간이 다음의 배수일 때: + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + 지정한 주기마다 다음 파일로 전환합니다. +예를 들어 1시간으로 설정하면 매시간마다 새로운 파일을 만듭니다. + + + compression + 압축 + + + None + 없음 + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>다음 파일로 캡처를 전환한 후 생성하기로 지정한 파일 개수를 초과하면 가장 오래된 파일을 지울 것입니다.</p></body></html> + + + Use a ring buffer with + 링 버퍼 파일 개수: + + + files + 개 파일 + + + Options + 옵션 + + + Display Options + 표시 옵션 + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>이 옵션을 활성화하면 캡처한 패킷을 주 화면에 즉시 표시합니다. 주의: 이 옵션을 활성화하면 캡처가 느려질 수도 있으며 표시된 패킷 외에 버려지는 패킷이 발생할 수 있습니다.</p></body></html> + + + Update list of packets in real-time + 실시간으로 패킷 목록 업데이트 + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>이 옵션을 활성화하면 &quot;실시간으로 패킷 목록 업데이트&quot; 옵션을 활성화했을 때 &quot;패킷 목록&quot;의 가장 마지막으로 캡처된 패킷으로 자동 스크롤됩니다.</p></body></html> + + + Automatically scroll during live capture + 캡처 중에 자동 스크롤 + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>패킷 캡처 중에 캡처 정보 대화 상자를 표시합니다.</p></body></html> + + + Show capture information during live capture + 라이브 캡처 중에 캡처 정보 화면 표시 + + + Name Resolution + 이름 해석 + + + Perform MAC layer name resolution while capturing. + 캡처 중에 MAC 레이어 주소를 호스트 이름으로 해석합니다. + + + Resolve MAC addresses + MAC 주소 해석 + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>캡처 중에 네트워크 계층의 이름을 해석합니다.</p></body></html> + + + Resolve network names + 네트워크 이름 해석 + + + Perform transport layer name resolution while capturing. + 캡처 중에 전송 계층 이름을 해석합니다. + + + Resolve transport names + 전송 계층 이름 해석 + + + Stop capture automatically after… + 다음 이후 자동으로 캡처 정지 + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>지정한 개수의 패킷을 캡처한 후 캡처를 정지합니다.</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + 지정한 개수의 패킷을 캡처한 후 캡처를 정지합니다. + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>지정한 개수의 파일을 생성한 후 캡처를 정지합니다.</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>지정한 분량의 데이터를 캡처한 후 캡처를 정지합니다.</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + 지정한 분량의 데이터를 캡처한 후 캡처를 정지합니다. + + + Stop capturing after the specified amount of time has passed. + 지정한 시간이 지난 뒤 캡처를 정지합니다. + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>이름이 없는 캡처 파일을 임시로 저장할 디렉터리를 지정합니다(선택 사항).</p></body></html> + + + Directory for temporary files + 임시 파일을 저장할 디렉터리 + + + Capture Options + 캡처 옵션 + + + Start + 시작 + + + Leave blank to use a temporary file + 비워 두면 임시 파일 사용 + + + Specify a Capture File + 캡처 파일 지정 + + + Specify temporary directory + 임시 디렉터리 지정 + + + %1: %2 + %1: %2 + + + Addresses + 주소 + + + Address + 주소 + + + no addresses + 주소 없음 + + + Error + 오류 + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + 여러 파일: 요청한 파일 크기가 너무 큽니다. 파일 최대 크기는 2 GiB입니다. + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + 여러 파일: 캡처 파일 이름이 없습니다. 여러 파일을 사용하려면 파일 이름을 지정해야 합니다. + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + 여러 파일: 파일 제한을 지정하지 않았습니다. 각 파일별 파일 제한 크기나, 생성 시간 주기, 패킷의 개수를 지정해야 합니다. + + + + CapturePreferencesFrame + + Frame + 프레임 + + + Default interface + 기본 인터페이스 + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>이 옵션을 활성화하는 것을 추천합니다. 일반적으로 네트워크 카드는 자신의 네트워크 주소로 전송된 트래픽만 캡처합니다. 네트워크 카드가 &quot;볼 수 있는&quot; 모든 트래픽을 캡처하려면 이 옵션을 활성화하십시오. 스위치 네트워크에서 패킷을 캡처할 때에 대한 자세한 내용은 FAQ를 참조하십시오.</p></body></html> + + + Capture packets in promiscuous mode + 무작위 모드로 패킷 캡처 + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>다음 세대(Next Generation)의 캡처 파일 형식으로 패킷을 캡처합니다.</p></body></html> + + + Capture packets in pcapng format + pcapng 형식으로 패킷 캡처 + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>캡처가 진행되는 동안 패킷 목록을 업데이트합니다. 고속 네트워크에서는 패킷이 손실될 수도 있습니다.</p></body></html> + + + Update list of packets in real time + 실시간으로 패킷 목록 업데이트 + + + Interval between updates (ms) + 업데이트 시간 간격(ms) + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + <html><head/><body><p>새 패킷이 GUI에 표시되는 시간 간격입니다. GUI 업데이트 주기와 타이머 정밀도에 영향을 줍니다.</p></body></html> + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + <html><head/><body><p>새 패킷 업데이트 시간 간격입니다. GUI 업데이트 주기와 타이머 정밀도에 영향을 줍니다.</p></body></html> + + + Don't load interfaces on startup + 시작할 때 인터페이스를 불러오지 않기 + + + Disable external capture interfaces + 외부 캡처 인터페이스 비활성화 + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + "@" 기호는 무시됩니다. + + + + ColoringRulesDialog + + Dialog + 대화 상자 + + + <small><i>A hint.</i></small> + <small><i>힌트입니다.</i></small> + + + Add a new coloring rule. + 새로운 색상 규칙을 추가합니다. + + + Delete this coloring rule. + 이 색상 규칙을 삭제합니다. + + + Duplicate this coloring rule. + 이 색상 규칙을 복제합니다. + + + Clear all coloring rules. + 모든 색상 규칙을 지웁니다. + + + Set the foreground color for this rule. + 이 규칙에 전경색을 설정합니다. + + + Foreground + 전경색 + + + Set the background color for this rule. + 이 규칙에 배경색을 설정합니다. + + + Background + 배경색 + + + Set the display filter using this rule. + 이 규칙을 사용할 표시 필터를 설정합니다. + + + Apply as filter + 필터로 적용 + + + Select a file and add its filters to the end of the list. + 필터 파일을 선택하여 목록 맨 뒤에 필터를 추가합니다. + + + Save filters in a file. + 필터를 파일로 저장합니다. + + + Coloring Rules %1 + 색상 규칙 %1 + + + Import… + 가져오기... + + + Export… + 내보내기... + + + Copy coloring rules from another profile. + 다른 프로필에서 색상 규칙을 복사합니다. + + + Open + 열기 + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + 두 번 클릭하여 편집합니다. 드래그하여 움직입니다. 각각 규칙은 일치하는 규칙을 찾을 때까지 순서대로 처리됩니다. + + + Import Coloring Rules + 색상 규칙 가져오기 + + + Export %1 Coloring Rules + %1 색상 규칙 내보내기 + + + + ColoringRulesModel + + New coloring rule + 새로운 색상 규칙 + + + Unable to save coloring rules: %1 + 색상 규칙을 저장할 수 없습니다: %1 + + + Name + 이름 + + + Filter + 필터 + + + + ColumnEditorFrame + + Frame + 프레임 + + + Title: + Title + 제목: + + + Type: + Type + 유형: + + + Fields: + Fields + 필드: + + + Occurrence: + Occurrence + 출현 횟수: + + + Resolve Names: + 해석된 이름: + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + <html><head/><p>필드의 원시값 대신 사람이 읽을 수 있는 문자열로 표시합니다. 문자열 값을 갖는 필드가 있는 사용자 지정 칼럼에만 적용할 수 있습니다.</p></body></html> + + + Missing fields. + 필드가 비어 있습니다. + + + Invalid fields. + 필드가 잘못되었습니다. + + + Invalid occurrence value. + 출현 횟수가 잘못되었습니다. + + + + ColumnListModel + + Displayed + 표시됨 + + + Title + 제목 + + + Type + 유형 + + + Fields + 필드 + + + Field Occurrence + 필드 출현 횟수 + + + Resolved + 해석됨 + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + <html>필드의 원시값 대신 사람이 읽을 수 있는 문자열로 보여 줍니다. 문자열 값을 갖는 필드를 가진 사용자 지정 칼럼에 대해서만 적용 가능합니다.</html> + + + New Column + 새로운 열 + + + + ColumnPreferencesFrame + + Frame + 프레임 + + + Add a new column + 새로운 열 추가 + + + Delete selected column + 선택한 열 삭제 + + + Show displayed columns only + 표시된 열만 보기 + + + Reset all changes + 모든 변경 사항 초기화 + + + + CompiledFilterOutput + + Compiled Filter Output + 컴파일된 필터 출력 + + + Copy + 복사 + + + Copy filter text to the clipboard. + 필터 텍스트를 클립보드에 복사합니다. + + + + ConversationDataModel + + Address A + 주소 A + + + Port A + 포트 A + + + Address B + 주소 B + + + Port B + 포트 B + + + Packets + 패킷 + + + Bytes + 바이트 + + + Stream ID + 스트림 ID + + + Packets A + 패킷 A + + + Bytes A + 바이트 A + + + Packets B + 패킷 B + + + Bytes B + 바이트 B + + + Abs Start + 절대 시작 + + + Rel Start + 상대 시작 + + + Duration + 지속 시간 + + + Bits/s A + 비트/초 A + + + Bits/s B + 비트/초 B + + + Total Packets + 전체 패킷 + + + Percent Filtered + 필터된 비율 + + + + ConversationDialog + + Follow Stream… + 스트림 따라가기… + + + Follow a TCP or UDP stream. + TCP 또는 UDP 스트림을 따라갑니다. + + + Graph… + 그래프... + + + Graph a TCP conversation. + TCP 대화를 그래프로 표시합니다. + + + + ConversationHashTablesDialog + + Dialog + 대화 상자 + + + Conversation Hash Tables + 대화 해시표 + + + + CopyFromProfileButton + + Copy from + 다음에서 복사 + + + Copy entries from another profile. + 다른 프로필에서 항목을 복사합니다. + + + System default + 시스템 기본값 + + + + CredentialsDialog + + Wireshark - Credentials + Wireshark - 자격 증명 + + + Credentials + 자격 증명 + + + + CredentialsModel + + Click to select the packet + 클릭하여 패킷 선택 + + + Click to select the packet with username + 클릭하여 사용자 이름이 포함된 패킷 선택 + + + Username not available + 사용자 이름을 사용할 수 없음 + + + Packet No. + 패킷 번호 + + + Protocol + 프로토콜 + + + Username + 사용자 이름 + + + Additional Info + 추가 정보 + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + 바이트를 16진수 + ASCII 덤프로 복사 + + + Copy packet bytes as a hex and ASCII dump. + 16진수 및 ASCII 덤프 형식으로 패킷 바이트를 복사합니다. + + + …as Hex Dump + 16진수 덤프로 복사 + + + Copy packet bytes as a hex dump. + 16진수 덤프 형식으로 패킷 바이트를 복사합니다. + + + …as MIME Data + MIME 데이터로 복사 + + + …as C String + C 문자열로 복사 + + + Copy packet bytes as printable ASCII characters and escape sequences. + 출력 가능한 ASCII 문자와 탈출 시퀀스로 패킷 바이트를 복사합니다. + + + …as a Hex Stream + 16진수 스트림으로 복사 + + + Copy packet bytes as a stream of hex. + 16진수 스트림으로 패킷 바이트를 복사합니다. + + + …as a Base64 String + Base64 문자열로 복사 + + + Copy packet bytes as a base64 encoded string. + Base64로 인코딩된 문자열로 패킷 바이트를 복사합니다. + + + Copy packet bytes as application/octet-stream MIME data. + MIME 데이터(application/octet-stream)로 패킷 바이트를 복사합니다. + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + 프로토콜에 대한 분해 행동을 변경합니다. + + + Remove this dissection behavior. + 이 분해 행동을 삭제합니다. + + + Copy this dissection behavior. + 이 분해 행동을 복사합니다. + + + Clear all dissection behaviors. + 모든 분해 행동을 지웁니다. + + + Decode As… + 다른 형식으로 디코드… + + + Open + 열기: + + + + DecodeAsModel + + Match using this field + 이 필드를 사용하여 일치 + + + Change behavior when the field matches this value + 필드가 이 값과 일치했을 때 동작 변경 + + + Field value type (and base, if Integer) + 필드 값 유형(정수인 경우에는 밑수 포함) + + + Current"Decode As" behavior + 현재 "다른 형식으로 디코드"의 동작 + + + Default "Decode As" behavior + 기본 "다른 형식으로 디코드"의 동작 + + + String + 문자열 + + + Integer, base + 정수형, 밑수: + + + unknown + 알 수 없음 + + + <none> + <없음> + + + GUID + GUID + + + Field + 필드 + + + Value + + + + Type + 유형 + + + Default + 기본값 + + + Current + 현재 + + + + DisplayFilterCombo + + Display filter selector + 표시 필터 선택기 + + + Select from previously used filters. + 이전에 사용한 사용한 필터에서 선택합니다. + + + + DisplayFilterEdit + + Display filter entry + 표시 필터 항목 + + + Manage saved bookmarks. + 저장한 책갈피를 관리합니다. + + + Display Filter Expression… + 표시 필터 표현식… + + + Apply a display filter %1 <%2/> + 표시 필터 적용 %1 <%2/> + + + Enter a display filter %1 + 표시 필터 입력 %1 + + + Clear display filter + 표시 필터 지우기 + + + Apply display filter + 표시 필터 적용 + + + Left align buttons + 왼쪽으로 버튼 정렬 + + + Apply a read filter %1 + 읽기 필터 적용 %1 + + + Current filter: %1 + 현재 필터: %1 + + + Invalid filter: + 잘못된 필터: + + + Save this filter + 이 필터 저장 + + + Remove this filter + 이 필터 삭제 + + + Manage Display Filters + 표시 필터 관리 + + + Filter Button Preferences... + 필터 버튼 속성... + + + + DisplayFilterExpressionDialog + + Dialog + 대화 상자 + + + Select a field to start building a display filter. + 표시 필터를 구축하려면 필드를 선택하여 시작하십시오. + + + Field Name + 필드 이름 + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>필드 이름의 목록에서 검색합니다.</p></body></html> + + + Search: + 검색: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>관계는 필드를 지정된 값으로 제한하는 데 사용할 수 있습니다. 각각 관계는 다음 동작을 수행합니다.</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>이 필드를 포함하는 모든 패킷과 일치</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, != 등등</span></p></td><td><p>지정된 값과 필드를 비교</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>필드의 값과 문자열을 포함하거나 정규 표현식과 일치하는지 확인</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>필드의 값이 지정한 값의 집합에 포함되는지 확인</p></td></tr></table></body></html> + + + + + Relation + 관계 + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + 기본값으로 임의의 값과 일치하면 순서 비교 및 contains/matches/in 관계가 참으로 간주됩니다. "all" 수량자를 사용하면 프레임 내의 모든 값에 시험을 적용합니다. + + + Quantifier + 수량자 + + + Any + Any + + + All + All + + + Match against this value. + 이 값과 일치합니다. + + + Value + + + + If the field you have selected has a known set of valid values they will be listed here. + 선택한 필드에 알려진 유효한 값의 모음이 있는 경우 여기에 나열됩니다. + + + Predefined Values + 사전 정의된 값 + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + 선택한 필드가 바이트 범위를 다루는 경우(예: 프로토콜을 선택한 경우) 바이트 범위 내부로 일치를 제한할 수 있습니다. + + + Range (offset:length) + 범위(오프셋:길이) + + + No display filter + 표시 필터 없음 + + + <small><i>A hint.</i></small> + <small><i>필터 힌트입니다.</i></small> + + + Display Filter Expression + 표시 필터 표현식 + + + Select a field name to get started + 필드 이름을 선택하여 시작하십시오 + + + Click OK to insert this filter + 이 필터를 추가하려면 확인을 클릭하십시오 + + + + DissectorSyntaxLineEdit + + Dissector entry + 분해기 항목 + + + Enter a dissector %1 + 분해기 입력 %1 + + + + DissectorTablesDialog + + Dialog + 대화 상자 + + + Search: + 검색: + + + Dissector Tables + 분해기 표 + + + + DissectorTablesProxyModel + + Table Type + 표 형식 + + + String + 문자열 + + + Dissector Description + 분해기 설명 + + + Integer + 정수형 + + + Protocol + 프로토콜 + + + Short Name + 짧은 이름 + + + Table Name + 표 이름 + + + Selector Name + 선택기 이름 + + + + EnabledProtocolsDialog + + Dialog + 대화 상자 + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>프로토콜을 비활성화하면 상위 계층 프로토콜이 표시되지 않습니다</i></small> + + + Search: + 검색: + + + in + in + + + Enable All + 모두 활성화 + + + Disable All + 모두 비활성화 + + + Invert + 반전 + + + Enabled Protocols + 활성화된 프로토콜 + + + Everywhere + 모든 곳 + + + Only Protocols + 프로토콜만 + + + Only Description + 설명만 + + + Only enabled protocols + 활성화된 프로토콜만 + + + Only disabled protocols + 비활성화된 프로토콜만 + + + any protocol + 모든 프로토콜 + + + non-heuristic protocols + 비 휴리스틱 프로토콜 + + + heuristic protocols + 휴리스틱 프로토콜 + + + + EnabledProtocolsModel + + Protocol + 프로토콜 + + + Description + 설명 + + + + EndpointDataModel + + Address + 주소 + + + Port + 포트 + + + Packets + 패킷 + + + Bytes + 바이트 + + + Tx Packets + Tx 패킷 + + + Tx Bytes + Tx 바이트 + + + Rx Packets + Rx 패킷 + + + Rx Bytes + Rx 바이트 + + + Country + 국가 + + + City + 도시 + + + Latitude + 위도 + + + Longitude + 경도 + + + AS Number + AS 번호 + + + AS Organization + AS 조직 + + + Total Packets + 전체 패킷 + + + Percent Filtered + 필터된 비율 + + + + EndpointDialog + + Map + 지도 + + + Draw IPv4 or IPv6 endpoints on a map. + IPv4 또는 IPv6의 종단점을 지도에 나타냅니다. + + + Open in browser + 브라우저에서 열기 + + + Save As… + 다른 이름으로 저장… + + + Map file error + 지도 파일 오류 + + + Save Endpoints Map + 종단점 지도 저장 + + + Failed to save map file %1. + %1 지도 파일 저장에 실패하였습니다. + + + + EthernetAddressModel + + Type + 유형 + + + Name + 이름 + + + Address + 주소 + + + All entries + 모든 항목 + + + Hosts + 호스트 + + + Ethernet Addresses + 이더넷 주소 + + + Ethernet Manufacturers + 이더넷 제조사 + + + Ethernet Well-Known Addresses + 잘 알려진 이더넷 주소 + + + + ExpertInfoDialog + + Dialog + 대화 상자 + + + <small><i>A hint.</i></small> + <small><i>필터 힌트입니다.</i></small> + + + Limit to Display Filter + 표시 필터로 제한 + + + Group by summary + 요약으로 묶음 + + + Search expert summaries. + 전문가 요약을 검색합니다. + + + Search: + 검색: + + + Show… + Show... + 표시… + + + Error + 오류 + + + Show error packets. + 오류 패킷을 표시합니다. + + + Warning + 경고 + + + Show warning packets. + 경고 패킷을 표시합니다. + + + Note + 주의 + + + Show note packets. + 주의 패킷을 표시합니다. + + + Chat + 대화 + + + Show chat packets. + 대화 패킷을 표시합니다. + + + Comment + 주석 + + + Show comment packets. + 주석 패킷을 표시합니다. + + + Expert Information + 전문가 정보 + + + Collapse All + 모두 접기 + + + Expand All + 모두 펴기 + + + Capture file closed. + 캡처 파일이 닫혔습니다. + + + No display filter + 표시 필터 없음 + + + No display filter set. + 표시 필터가 설정되어 있지 않습니다. + + + Limit information to "%1". + "%1"(으)로 정보를 제한합니다. + + + Display filter: "%1" + 표시 필터: "%1" + + + + ExpertInfoProxyModel + + Packet + 패킷 + + + Severity + 심각도 + + + Summary + 요약 + + + Group + 그룹 + + + Protocol + 프로토콜 + + + Count + 개수 + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + 패킷 분해 결과 내보내기 + + + Export As: + Export as: + 다른 이름으로 내보내기: + + + Plain text (*.txt) + 일반 텍스트 (*.txt) + + + Comma Separated Values - summary (*.csv) + 쉼표로 구분된 값 - 요약 (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML - 요약 (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PSML - 자세한 정보 (*.psml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + C 언어 배열 - 바이트 (*.c, *.h) + + + + ExportObjectDialog + + Dialog + 대화 상자 + + + Content Type: + 내용 유형: + + + Searching for objects + 객체 검색 중 + + + Text Filter: + 텍스트 필터: + + + Only display entries containing this string + 이 문자열을 포함하는 항목만 표시 + + + Preview + 미리 보기 + + + All Content-Types + 모든 Content-Type + + + Export + 내보내기 + + + %1 object list + %1 객체 목록 + + + Save Object As… + 다른 이름으로 객체 저장... + + + Save All Objects In… + 모든 객체 저장... + + + + ExportObjectModel + + Packet + 패킷 + + + Hostname + 호스트 이름 + + + Content Type + 내용 유형 + + + Size + 크기 + + + Filename + 파일 이름 + + + + ExportPDUDialog + + Dialog + 대화 상자 + + + Display filter: + 표시 필터: + + + + ExtArgSelector + + Reload data + 데이터 다시 불러오기 + + + + ExtcapArgumentFileSelection + + Clear + 지우기 + + + All Files ( + 모든 파일 ( + + + Open File + 파일 열기 + + + Select File + 파일 선택 + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + 인터페이스 옵션 + + + Start + 시작 + + + Save + 저장 + + + Default + 기본값 + + + Restore default value of the item + 항목의 기본값 복원 + + + Extcap Help cannot be found + Extcap 도움말을 찾을 수 없습니다 + + + The help for the extcap interface %1 cannot be found. Given file: %2 + %1 extcap 인터페이스에 대한 도움말을 찾을 수 없습니다. 주어진 파일: %2 + + + Save parameter(s) on capture start + 캡처를 시작할 때 인자 저장 + + + + FieldFilterEdit + + Display filter entry + 표시 필터 항목 + + + Enter a field %1 + 필드 입력 %1 + + + Invalid filter: + 잘못된 필터: + + + + FileSetDialog + + Dialog + 대화 상자 + + + Directory: + 디렉터리: + + + No files in Set + 집합에 파일 없음 + + + No capture loaded + 캡처를 불러오지 않았음 + + + %Ln File(s) in Set + %1 File%2 in Set + + 집합에 파일 %Ln개 있음 + + + + + FilesetEntryModel + + Open this capture file + 이 캡처 파일 열기 + + + Filename + 파일 이름 + + + Created + 생성됨 + + + Modified + 수정됨 + + + Size + 크기 + + + + FilterAction + + Selected + 선택됨 + + + Not Selected + 선택 안 됨 + + + …and Selected + …그리고 선택됨 + + + …or Selected + …또는 선택됨 + + + …and not Selected + …그리고 선택 안 됨 + + + …or not Selected + …또는 선택 안 됨 + + + + FilterDialog + + Dialog + 대화 상자 + + + Create a new filter. + 새 필터를 만듭니다. + + + Remove this filter. + Remove this profile. + 이 필터를 삭제합니다. + + + Copy this filter. + Copy this profile. + 이 필터를 복사합니다. + + + Capture Filters + 캡처 필터 + + + Display Filters + 표시 필터 + + + Open + 열기: + + + New capture filter + This text is automatically filled in when a new filter is created + 새 캡처 필터 + + + New display filter + This text is automatically filled in when a new filter is created + 새 표시 필터 + + + + FilterExpressionFrame + + Frame + 프레임 + + + Filter Buttons Preferences… + 필터 버튼 설정… + + + Label: + 레이블: + + + Enter a description for the filter button + 필터 버튼에 대한 설명을 입력하십시오 + + + Filter: + 필터: + + + Enter a filter expression to be applied + 적용할 필터 표현식을 입력하십시오 + + + Comment: + 주석 + + + Enter a comment for the filter button + 필터 버튼에 대한 주석을 입력하십시오 + + + Missing label. + 레이블이 누락되었습니다. + + + Missing filter expression. + 필터 표현식이 누락되었습니다. + + + Invalid filter expression. + 잘못된 필터 표현식입니다. + + + + FilterExpressionToolBar + + Filter Button Preferences... + 필터 버튼 속성... + + + Edit + 편집 + + + Disable + 비활성화 + + + Remove + 삭제 + + + + FilterListModel + + Filter Name + 필터 이름 + + + Filter Expression + 필터 표현식 + + + + FindLineEdit + + Textual Find + 검색어로 찾기 + + + Regular Expression Find + 정규 표현식으로 찾기 + + + + FirewallRulesDialog + + Create rules for + 다음에서 사용 가능한 규칙 생성 + + + Inbound + 인바운드 + + + Deny + 거부 + + + Firewall ACL Rules + 방화벽 ACL 규칙 + + + Copy + 복사 + + + IPv4 source address. + IPv4 발신지 주소. + + + IPv4 destination address. + IPv4 목적지 주소. + + + Source port. + 발신지 포트. + + + Destination port. + 목적지 포트. + + + IPv4 source address and port. + IPv4 발신지 주소와 포트. + + + IPv4 destination address and port. + IPv4 목적지 주소와 포트. + + + MAC source address. + MAC 발신지 주소. + + + MAC destination address. + MAC 목적지 주소. + + + Text file (*.txt);;All Files ( + 텍스트 파일 (*.txt);;모든 파일 ( + + + Warning + 경고 + + + Unable to save %1 + %1을(를) 저장할 수 없음 + + + + FolderListModel + + "File" dialogs + "파일" 대화 상자 + + + capture files + 캡처 파일 + + + Temp + 임시 + + + untitled capture files + 이름 없는 캡처 파일 + + + Personal configuration + 개인 환경 설정 + + + Global configuration + 전역 설정 + + + dfilters, preferences, ethers, … + dfilters, 설정, ethers, … + + + dfilters, preferences, manuf, … + dfilters, 설정, manuf, … + + + System + 시스템 + + + ethers, ipxnets + ethers, ipxnets + + + Program + 프로그램 + + + program files + 프로그램 파일 + + + Personal Plugins + 사용자 플러그인 + + + binary plugins + 바이너리 플러그인 + + + Global Plugins + 전역 플러그인 + + + Personal Lua Plugins + 사용자 Lua 플러그인 + + + Global Lua Plugins + 전역 Lua 플러그인 + + + Lua scripts + Lua 스크립트 + + + Personal Extcap path + 사용자 Extcap 경로 + + + external capture (extcap) plugins + 외부 캡처(Extcap) 플러그인 + + + Global Extcap path + 전역 Extcap 경로 + + + MaxMind DB path + MaxMind DB 경로 + + + MaxMind DB database search path + MaxMind DB 데이터베이스 검색 경로 + + + MIB/PIB path + MIB/PIB 경로 + + + SMI MIB/PIB search path + SMI MIB/PIB 검색 경로 + + + macOS Extras + macOS 추가판 + + + Extra macOS packages + 추가 macOS 패키지 + + + Name + 이름 + + + Location + 위치 + + + Typical Files + 일반적인 파일 + + + + FollowStreamAction + + %1 Stream + %1 스트림 + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + 필터로 이 스트림 제외 + + + Print + 인쇄 + + + %Ln client pkt(s), + + 클라이언트 패킷 %Ln개, + + + + %Ln server pkt(s), + + 서버 패킷 %Ln개, + + + + ASCII + ASCII + + + C Arrays + C 언어 배열 형식 + + + EBCDIC + EBCDIC 형식 + + + Hex Dump + 16진수 덤프 형식 + + + UTF-8 + UTF-8 형식 + + + YAML + YAML 형식 + + + Raw + Raw(무편집) 형식 + + + Save as… + 다른 이름으로 저장… + + + Back + 뒤로 + + + Packet %1. + 패킷 %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + <span style="color: %1; background-color:%2">클라이언트</span> 패킷 %Ln개, + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + <span style="color: %1; background-color:%2">서버</span> 패킷 %Ln개, + + + + %Ln turn(s). + + 턴 %Ln회. + + + + Click to select. + 클릭하면 선택합니다. + + + Regex Find: + 정규식 검색: + + + No capture file. + 캡처 파일이 없습니다. + + + Please make sure you have a capture file opened. + 캡처 파일이 열려 있는지 확인하십시오. + + + Error following stream. + 스트림을 따라가는 중 오류가 발생했습니다. + + + Capture file invalid. + 캡처 파일이 잘못되었습니다. + + + Please make sure you have a %1 packet selected. + %1 패킷을 선택했는지 확인하십시오. + + + %1 stream not found on the selected packet. + 선택한 패킷에서 %1 스트림을 찾을 수 없습니다. + + + Entire conversation (%1) + 전체 대화(%1) + + + Follow %1 Stream (%2) + %1 스트림 따라가기(%2) + + + Error creating filter for this stream. + 이 스트림에 대한 필터를 생성하는 중 오류가 발생했습니다. + + + Save Stream Content As… + 다른 이름으로 스트림 내용 저장… + + + [Stream output truncated] + [스트림 출력이 잘림] + + + %Ln total stream(s). + + 전체 스트림 %Ln개. + + + + Max sub stream ID for the selected stream: %Ln + + 선택한 스트림에 대한 최대 하위 스트림 ID: %Ln + + + + File closed. + 파일이 닫혔습니다. + + + Follow Stream + 스트림 따라가기 + + + Hint. + 필터 힌트. + + + Show data as + Show and save data as + 다음 형식으로 표시: + + + Stream + 스트림 + + + Substream + 하위 스트림 + + + Find: + 찾기: + + + Find &Next + 다음 찾기(&N) + + + + FontColorPreferencesFrame + + Frame + 프레임 + + + Main window font: + 기본 창 글꼴: + + + Select Font + 글꼴 선택 + + + Colors: + 색상: + + + System Default + 시스템 기본값 + + + Solid + 단색 + + + Sample ignored packet text + 무시된 패킷 텍스트 예제 + + + Sample marked packet text + 표시된 패킷 텍스트 예제 + + + Sample active selected item + 활성 선택된 항목 예제 + + + Style: + 스타일: + + + Gradient + 그라디언트 + + + Sample inactive selected item + 비활성 선택된 항목 예제 + + + Sample "Follow Stream" client text + "스트림 따라라기" 클라이언트 텍스트 예제 + + + Sample "Follow Stream" server text + "스트림 따라가기" 서버 텍스트 예제 + + + Sample valid filter + 올바른 필터 예제 + + + Sample invalid filter + 잘못된 필터 예제 + + + Sample warning filter + Sample deprecated filter + 경고 필터 예제 + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + 콩고물과 우유가 들어간 빙수는 차게 먹어야 특별한 맛이 잘 표현된다 + + + Lazy badgers move unique waxy jellyfish packets + 겉표지보다 큰 몇 향수류 + + + Font + 글꼴 + + + + FunnelStringDialog + + Dialog + 대화 상자 + + + + FunnelTextDialog + + Dialog + 대화 상자 + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>검색 대상 문자열이나 정규 표현식을 입력하십시오. 일치하는 부분이 강조 표시됩니다.</p></body></html> + + + Highlight: + 강조 표시: + + + + GsmMapSummaryDialog + + Dialog + 대화 상자 + + + GSM MAP Summary + GSM MAP 요약 + + + File + 파일 + + + Name + 이름 + + + Length + 길이 + + + Format + 형식 + + + Snapshot length + 스냅샷 길이 + + + Data + 데이터 + + + First packet + 첫 패킷 + + + Last packet + 마지막 패킷 + + + Elapsed + 경과 + + + Packets + 패킷 + + + Invokes + Invokes + + + Total number of Invokes + 총 Invoke 수 + + + Average number of Invokes per second + 초당 평균 Invoke 수 + + + Total number of bytes for Invokes + 총 Invoke 바이트 + + + Average number of bytes per Invoke + Invoke당 평균 바이트 + + + Return Results + 반환 결과 + + + Total number of Return Results + 총 반환 결과 수 + + + Average number of Return Results per second + 초당 평균 반환 결과 수 + + + Total number of bytes for Return Results + 총 반환 결과 바이트 + + + Average number of bytes per Return Result + 반환 결과당 평균 바이트 + + + Totals + 전체 + + + Total number of GSM MAP messages + 전체 GSM MAP 메시지 수 + + + Average number of GSM MAP messages per second + 초당 평균 GSM MAP 메시지 수 + + + Total number of bytes for GSM MAP messages + 총 GSM MAP 메시지 바이트 + + + Average number of bytes per GSM MAP message + GSM MAP 메시지당 평균 바이트 + + + + IOConsoleDialog + + Dialog + 대화 상자 + + + Enter code + 코드 입력 + + + Evaluate + 평가 + + + Clear + 지우기 + + + Use %1 to evaluate. + %1 키를 누르면 평가합니다. + + + + IOGraphDialog + + Dialog + 대화 상자 + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>가치있고 놀라운 시간 절약형 키보드 단축키</h3> +<table><tbody> + +<tr><th>+</th><td>확대</td></th> +<tr><th>-</th><td>축소</td></th> +<tr><th>x</th><td>X축 확대</td></th> +<tr><th>X</th><td>X축 축소</td></th> +<tr><th>y</th><td>Y축 확대</td></th> +<tr><th>Y</th><td>Y축 축소</td></th> +<tr><th>0</th><td>초기 상태로 그래프 다시 설정</td></th> + +<tr><th>→</th><td>오른쪽으로 10픽셀 이동</td></th> +<tr><th>←</th><td>왼쪽으로 10픽셀 이동</td></th> +<tr><th>↑</th><td>위쪽으로 10픽셀 이동</td></th> +<tr><th>↓</th><td>아래쪽으로 10픽셀 이동</td></th> +<tr><th><i>Shift+</i>→</th><td>오른쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>←</th><td>왼쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>↑</th><td>위쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>↓</th><td>아래쪽으로 1픽셀 이동</td></th> + +<tr><th>g</th><td>커서 위치의 패킷으로 이동</td></th> + +<tr><th>z</th><td>마우스 드래그/크기 조절 전환</td></th> +<tr><th>t</th><td>캡처/세션 시간 기점 전환</td></th> +<tr><th>Space</th><td>십자선 전환</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + 이 그래프를 삭제합니다. + + + Add a new graph. + 새로운 그래프를 추가합니다. + + + Duplicate this graph. + 이 그래프를 복제합니다. + + + Clear all graphs. + 모든 그래프를 지웁니다. + + + Move this graph upwards. + 이 그래프를 위로 이동합니다. + + + Move this graph downwards. + 이 그래프를 아래로 이동합니다. + + + Mouse + 마우스 + + + Drag using the mouse button. + 마우스 단추를 사용하여 드래그합니다. + + + drags + 드래그 + + + Select using the mouse button. + 마우스 단추를 사용하여 선택합니다. + + + zooms + 크기 조정 + + + Interval + 주기 + + + Time of day + 시간 + + + Log scale + 로그 표기 + + + Automatic update + 자동 업데이트 + + + Enable legend + 범례 활성화 + + + Reset + 초기화 + + + Reset Graph + 그래프 초기화 + + + Reset the graph to its initial state. + 그래프를 초기 상태로 설정합니다. + + + 0 + 0 + + + Zoom In + 확대 + + + + + + + + + Zoom Out + 축소 + + + - + - + + + Move Up 10 Pixels + 위쪽으로 10픽셀 이동 + + + Up + 위쪽 + + + Move Left 10 Pixels + 왼쪽으로 10픽셀 이동 + + + Left + 왼쪽 + + + Move Right 10 Pixels + 오른쪽으로 10픽셀 이동 + + + Right + 오른쪽 + + + Move Down 10 Pixels + 아래쪽으로 10픽셀 이동 + + + Down + 아래쪽 + + + Move Up 1 Pixel + 위쪽으로 1픽셀 이동 + + + Shift+Up + Shift+위쪽 + + + Move Left 1 Pixel + 왼쪽으로 1픽셀 이동 + + + Shift+Left + Shift+왼쪽 + + + Move Right 1 Pixel + 오른쪽으로 1픽셀 이동 + + + Shift+Right + Shift+오른쪽 + + + Move Down 1 Pixel + 아래쪽으로 1픽셀 이동 + + + Move down 1 Pixel + Move down 1 pixel + 아래쪽으로 1픽셀 이동 + + + Shift+Down + Shift+아래쪽 + + + Go To Packet Under Cursor + 커서 위치의 패킷으로 이동 + + + Go to packet currently under the cursor + 현재 커서 위치의 패킷으로 이동 + + + G + G + + + Drag / Zoom + 드래그/크기 조정 + + + Toggle mouse drag / zoom behavior + 마우스 드래그/크기 조정 동작 전환 + + + Z + Z + + + Capture / Session Time Origin + 캡처/세션 시간 기점 + + + Toggle capture / session time origin + 캡처/세션 시간 기점 전환 + + + T + T + + + Crosshairs + 십자선 + + + Toggle crosshairs + 십자선 표시 전환 + + + Space + Space + + + Zoom In X Axis + X축 확대 + + + X + X + + + Zoom Out X Axis + X축 축소 + + + Shift+X + Shift+X + + + Zoom In Y Axis + Y축 확대 + + + Y + Y + + + Zoom Out Y Axis + Y축 축소 + + + Shift+Y + Shift+Y + + + 1 sec + 1초 + + + 10 sec + 10초 + + + 1 min + 1분 + + + 10 min + 10분 + + + Time (s) + 시간(초) + + + I/O Graphs + I/O 그래프 + + + Save As… + 다른 이름으로 저장… + + + Copy + 복사 + + + Copy graphs from another profile. + 다른 프로필에서 그래프를 복사합니다. + + + 1 ms + 1 ms + + + 2 ms + 2 ms + + + 5 ms + 5 ms + + + 10 ms + 10 ms + + + 20 ms + 20 ms + + + 50 ms + 50 ms + + + 100 ms + 100 ms + + + 200 ms + 200 ms + + + 500 ms + 500 ms + + + 2 sec + 2초 + + + 5 sec + 5초 + + + Wireshark I/O Graphs: %1 + Wireshark I/O 그래프: %1 + + + Filtered packets + 필터된 패킷 + + + Filtered events + 필터된 이벤트 + + + All Packets + 모든 패킷 + + + TCP Errors + TCP 오류 + + + All Events + 모든 이벤트 + + + Access Denied + 접근 거부됨 + + + Hover over the graph for details. + 자세한 정보를 보려면 그래프 위에 마우스를 올리십시오. + + + No packets in interval + 이 주기 안에 패킷 없음 + + + No events in interval + 이 주기 안에 이벤트 없음 + + + Click to select packet + 클릭하여 패킷 선택 + + + Packet + 패킷 + + + Click to select event + 클릭하여 이벤트 선택 + + + Event + 이벤트 + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 커서를 놓으면 크기 조정, x = %1 - %2, y = %3 - %4 + + + Unable to select range. + 범위를 선택할 수 없습니다. + + + Click to select a portion of the graph. + 클릭하여 그래프의 부분을 선택하십시오. + + + Portable Document Format (*.pdf) + PDF 형식 (*.pdf) + + + Portable Network Graphics (*.png) + PNG 형식 (*.png) + + + Windows Bitmap (*.bmp) + Windows 비트맵 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 형식 (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + 쉼표로 구분된 값 형식 (*.csv) + + + Save Graph As… + 다른 이름으로 그래프 저장… + + + + Iax2AnalysisDialog + + Dialog + 대화 상자 + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">정방향</span></p><p><span style=" font-size:medium; font-weight:600;">역방향</span></p></body></html> + + + Forward + 정방향 + + + Packet + 패킷 + + + Delta (ms) + 델타 (ms) + + + Jitter (ms) + 지터 (ms) + + + Bandwidth + 대역폭 + + + Status + 상태 + + + Length + 길이 + + + Reverse + 역방향 + + + Graph + 그래프 + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>정방향 지터 값을 표시하거나 숨깁니다.</p></body></html> + + + Forward Jitter + 정방향 지터 + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>정방향 차이 값을 표시하거나 숨깁니다.</p></body></html> + + + Forward Difference + 정방향 차이 값 + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>역방향 지터 값을 표시하거나 숨깁니다.</p></body></html> + + + Reverse Jitter + 역방향 지터 + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>역방향 차이 값을 표시하거나 숨깁니다.</p></body></html> + + + Reverse Difference + 역방향 차이 값 + + + <small><i>A hint.</i></small> + <small><i>힌트입니다.</i></small> + + + Audio + 오디오 + + + Save the audio data for both channels. + 두 채널의 오디오 데이터를 저장합니다. + + + Forward Stream Audio + 정방향 스트림 음성 + + + Save the forward stream audio data. + 정방향 스트림 오디오 데이터를 저장합니다. + + + Reverse Stream Audio + 역방향 스트림 오디오 + + + Save the reverse stream audio data. + 역방향 스트림 오디오 데이터를 저장합니다. + + + CSV + CSV 형식 + + + Save both tables as CSV. + CSV 형식으로 두 표를 저장합니다. + + + Forward Stream CSV + 정방향 스트림 CSV + + + Save the forward table as CSV. + 정방향 표를 CSV로 저장합니다. + + + Reverse Stream CSV + 역방향 스트림 CSV + + + Save the reverse table as CSV. + 역방향 표를 CSV로 저장합니다. + + + Save Graph + 그래프 저장 + + + Save the graph image. + 그래프 그림을 저장합니다. + + + Go to Packet + 다음 패킷으로 이동 + + + Select the corresponding packet in the packet list. + 패킷 목록에서 해당 패킷을 선택합니다. + + + G + G + + + Next Problem Packet + 다음 문제 패킷 + + + Go to the next problem packet + 다음 문제가 발생한 패킷으로 이동 + + + N + N + + + IAX2 Stream Analysis + IAX2 스트림 분석 + + + Unable to save RTP data. + RTP 데이터를 저장할 수 없습니다. + + + Please select an IAX2 packet. + IAX2 패킷을 선택하십시오. + + + G: Go to packet, N: Next problem packet + G: 패킷으로 이동, N: 다음 문제 패킷 + + + Portable Document Format (*.pdf) + PDF 형식 (*.pdf) + + + Portable Network Graphics (*.png) + PNG 형식 (*.png) + + + Windows Bitmap (*.bmp) + Windows 비트맵 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 형식 (*.jpeg *.jpg) + + + Save Graph As… + 다른 이름으로 그래프 저장… + + + Can't save in a file: Wrong length of captured packets. + 파일에 저장할 수 없습니다: 캡처된 패킷의 길이가 다릅니다. + + + Can't save in a file: File I/O problem. + 파일에 저장할 수 없습니다: 파일 입출력 문제입니다. + + + Save forward stream audio + 정방향 스트림 오디오 저장 + + + Save reverse stream audio + 역방향 스트림 오디오 저장 + + + Save audio + 음성 저장 + + + Sun Audio (*.au) + Sun 오디오 형식 (*.au) + + + ;;Raw (*.raw) + ;;Raw(무편집) 형식 (*.raw) + + + Warning + 경고 + + + Unable to save in that format + 해당 형식으로 저장할 수 없음 + + + Unable to save %1 + %1을(를) 저장할 수 없음 + + + Saving %1… + %1 저장 중… + + + Analyzing IAX2 + IAX2 분석 중 + + + Save forward stream CSV + 정방향 스트림을 CSV로 저장 + + + Save reverse stream CSV + 역방향 스트림을 CSV로 저장 + + + Save CSV + CSV로 저장 + + + Comma-separated values (*.csv) + 쉼표로 구분된 값 (*.csv) + + + + ImportTextDialog + + File: + 파일: + + + Set name of text file to import + 가져올 텍스트 파일 이름을 지정 + + + Browse for text file to import + 가져올 텍스트 파일을 찾아보기 + + + Browse… + Browse... + 찾아보기… + + + Hex Dump + 16진수 덤프 형식 + + + Import a standard hex dump as exported by Wireshark + Wireshark에서 내보낸 표준 16진수 덤프를 가져오기 + + + Offsets in the text file are in octal notation + 텍스트 파일의 오프셋을 8진수로 표시 + + + Octal + 8진수 + + + Offsets: + 오프셋: + + + Offsets in the text file are in hexadecimal notation + 텍스트 파일의 오프셋을 16진수로 표시 + + + Hexadecimal + 16진수 + + + Offsets in the text file are in decimal notation + 텍스트 파일의 오프셋을 10진수로 표시 + + + Decimal + 10진수 + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>16진수 바이트 표기로 보이더라도 Hex+ASCII 줄 끝에 ASCII 표현의 시작점을 감지했을 때 추가 처리를 할지 여부입니다.</p><p>16진수 덤프가 ASCII를 포함하지 않는다면 사용하지 마십시오.</p></body></html> + + + ASCII identification: + ASCII 식별: + + + Regular Expression + 정규 표현식 + + + Import a file formatted according to a custom regular expression + 사용자가 지정한 정규 표현식으로 형식화된 파일 가져오기 + + + Packet format regular expression + 패킷 형식 정규 표현식 + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + <html><head/><body><p>파일에서 각각 패킷을 가져올 때 이름 있는 그룹을 통해서 가져올 데이터를 나타내는 Perl 호환 정규 표현식입니다. 앵커 문자 ^, $는 각각 줄의 맨 처음과 맨 끝과 일치합니다.</p><p>필수 항목은 데이터 그룹이며, 선택적 항목은 시간, 방향, 순서 번호입니다.</p><p>정규 표현식 플래그: DUPNAMES, MULTILINE, NOEMPTY</p></body></html> + + + This is regexHintLabel, it will be set to default_regex_hint + 정규 표현식 힌트 레이블이며 default_regex_hint로 지정됨 + + + Data encoding: + 데이터 인코딩: + + + How data is encoded + 데이터가 인코딩되는 방법 + + + encodingRegexExample + 인코딩 정규 표현식 예제 + + + List of characters indicating incoming packets + 들어오는 패킷을 나타내는 문자 목록 + + + iI< + iI< + + + List of characters indicating outgoing packets + 나가는 패킷을 나타내는 문자 목록 + + + oO> + oO> + + + Timestamp format: + 타임스탬프 형식: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + 파일에 패킷의 방향(수신 또는 발신)을 나타내는 정보가 포함되어 있는지 여부입니다. + + + Direction indication: + 방향 지시: + + + ExportPDU + PDU 내보내기 + + + IP version: + IP 버전: + + + Interface name: + 인터페이스 이름: + + + The name of the interface to write to the import capture file + 가져온 캡처 파일에 기록할 인터페이스 이름 + + + Fake IF, Import from Hex Dump + 가짜 IF, 16진수 덤프에서 가져옴 + + + Maximum frame length: + 최대 프레임 길이: + + + Encapsulation + 캡슐화 + + + The text file has no offset + 텍스트 파일에 오프셋이 없음 + + + None + 없음 + + + <small><i>recommended regex:</small></i> + <small><i>추천 정규 표현식:</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + 텍스트 파일에 있는 타임스탬프를 해석할 형식입니다(예: %H:%M:%S). 형식 지정자는 strptime(3)을 기반으로 합니다 + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + <html><head/><body><p>텍스트 파일에 있는 타임스탬프를 해석할 형식입니다(예: %H:%M:%S.%f).</p><p>형식 지정자는 strptime(3)을 기반으로 하며, 추가로 초 미만의 시간을 지원하는 %f를 지원합니다. %f의 정확도는 길이에 따라 결정됩니다.</p></body></html> + + + %H:%M:%S.%f + %H:%M:%S.%f + + + timestampExampleLabel + 타임스탬프 예제 레이블 + + + Encapsulation Type: + 캡슐화 유형: + + + Encapsulation type of the frames in the import capture file + 가져온 캡처 파일 프레임의 캡슐화 유형 + + + Prefix each frame with an Ethernet and IP header + 각각 프레임 맨 앞에 이더넷과 IP 헤더 덧붙이기 + + + IP + IP + + + Prefix each frame with an Ethernet, IP and UDP header + 각각 프레임 맨 앞에 이더넷, IP, UDP 헤더 덧붙이기 + + + Prefix each frame with an Ethernet, IP and TCP header + 각각 프레임 맨 앞에 이더넷, IP, TCP 헤더 덧붙이기 + + + Prefix each frame with an Ethernet, IP and SCTP header + 각각 프레임 맨 앞에 이더넷, IP, SCTP 헤더 덧붙이기 + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + 각각 프레임 맨 앞에 이더넷, IP, SCTP(DATA) 헤더 덧붙이기 + + + Source address: + 발신 주소: + + + Destination address: + 목적지 주소: + + + Dissector + 분해기 + + + The IP protocol ID for each frame + 각 프레임별 IP 프로토콜 ID + + + The IP source address for each frame + 각 프레임별 IP 발신 주소 + + + The IP destination address for each frame + 각 프레임별 IP 목적지 주소 + + + The UDP, TCP or SCTP source port for each frame + 각 프레임별 UDP, TCP, SCTP 발신 포트 + + + The SCTP DATA payload protocol identifier for each frame + 각 프레임별 SCTP DATA 페이로드 프로토콜 식별자 + + + The UDP, TCP or SCTP destination port for each frame + 각 프레임별 UDP, TCP, SCTP 목적지 포트 + + + Prefix each frame with an Ethernet header + 각각 프레임 맨 앞에 이더넷 헤더 덧붙이기 + + + Ethernet + 이더넷 + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + 프로토콜(10진수): + + + Leave frames unchanged + 프레임을 그대로 유지 + + + No dummy header + 더미 헤더 없음 + + + Tag: + 태그: + + + UDP + UDP + + + Source port: + 발신 포트: + + + The Ethertype value of each frame + 각 프레임별 Ethertype 값 + + + TCP + TCP + + + The SCTP verification tag for each frame + 각 프레임의 SCTP 검증 태그 + + + Destination port: + 목적지 포트: + + + Ethertype (hex): + Ethertype(16진수): + + + SCTP (Data) + SCTP(데이터) + + + The dissector to use for each frame + 각 프레임별 사용할 분해기 + + + The IP Version to use for the dummy IP header + 더미 IP 헤더에 사용할 IP 버전 + + + The maximum size of the frames to write to the import capture file (max 256kiB) + 가져올 캡처 파일에 기록할 최대 프레임 크기(최대 256kiB) + + + Supported fields are data, dir, time, seqno + 지원되는 필드는 data, dir, time, seqno + + + Missing capturing group data (use (? + 그룹 데이터를 캡처하는 표현식 없음(사용: (? + + + Import From Hex Dump + 16진수 덤프에서 가져오기 + + + Import + 가져오기 + + + Import Text File + 텍스트 파일 가져오기 + + + + InterfaceFrame + + Frame + 프레임 + + + Wired + 유선 + + + AirPCAP + AirPCAP + + + Pipe + 파이프 + + + STDIN + STDIN(표준 입력) + + + Bluetooth + 블루투스 + + + Wireless + 무선 + + + Dial-Up + 전화 접속 + + + USB + USB + + + External Capture + 외부 캡처 + + + Virtual + 가상 + + + Remote interfaces + 원격 인터페이스 + + + Show hidden interfaces + 숨겨진 인터페이스 표시 + + + External capture interfaces disabled. + 외부 캡처 인터페이스가 비활성화되었습니다. + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + <p>설치된 패킷 캡처 드라이버가 없어서 사용 가능한 로컬 인터페이스가 없습니다.</p><p>문제를 해결하려면 <a href="https://npcap.com/">Npcap</a>을 설치하십시오.</p> + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + <p>패킷 캡처 드라이버를 불러오지 않았기 때문에 로컬 인터페이스를 사용할 수 없습니다.</p><p>Npcap이 설치된 경우 <pre>net start npcap</pre>을 실행하거나 WinPcap이 설치된 경우 <pre>net start npf</pre>를 실행하여 이 문제를 해결할 수 있습니다. 두 명령 모두 관리자 권한으로 실행해야 합니다.</p> + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + <p>로컬 인터페이스에서 캡처할 수 있는 권한이 없습니다.</p><p><a href="file://%1">ChmodBPF를 설치</a>하여 이 문제를 해결할 수 있습니다.</p> + + + You don't have permission to capture on local interfaces. + 로컬 인터페이스에서 캡처할 수 있는 권한이 없습니다. + + + No interfaces found. + 인터페이스를 찾을 수 없습니다. + + + Interfaces not loaded (due to preference). Go to Capture + (환경 설정으로 인해) 인터페이스를 불러올수 없습니다. 캡처 + + + Start capture + 캡처 시작 + + + Hide Interface + 인터페이스 숨기기 + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + 표시할 인터페이스가 없습니다. 인터페이스 %1개는 숨겨졌습니다. + + + + InterfaceToolbar + + Frame + 프레임 + + + Select interface + 인터페이스 선택 + + + Interface + 인터페이스 + + + + InterfaceToolbarLineEdit + + Apply changes + 변경 사항 적용 + + + + InterfaceTreeModel + + Show + 표시 + + + Friendly Name + 친근한 이름 + + + Interface Name + 인터페이스 이름 + + + No interfaces found. + 인터페이스를 찾을 수 없습니다. + + + This version of Wireshark was built without packet capture support. + 이 버전의 Wireshark는 패킷 캡처 지원 없이 빌드되었습니다. + + + Local Pipe Path + 로컬 파이프 경로 + + + Comment + 주석 + + + Link-Layer Header + 링크 레이어 헤더 + + + Promiscuous + 무작위 + + + Snaplen (B) + Snaplen(바이트) + + + Buffer (MB) + 버퍼(MB) + + + Monitor Mode + 모니터 모드 + + + Capture Filter + 캡처 필터 + + + Addresses + 주소 + + + Address + 주소 + + + Extcap interface: %1 + Extcap 인터페이스: %1 + + + No addresses + 주소 없음 + + + No capture filter + 캡처 필터 없음 + + + Capture filter + 캡처 필터 + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + LBT-RM 전송 통계 + + + Sources + 발신지 + + + Address/Transport + 주소/전송 + + + Data frames + 데이터 프레임 + + + Data bytes + 데이터 바이트 + + + Data frames/bytes + 데이터 프레임/바이트 + + + Data rate + 데이터 전송률 + + + RX data frames + 수신 데이터 프레임 + + + RX data bytes + 수신 데이터 바이트 + + + RX data frames/bytes + 수신 데이터 프레임/바이트 + + + RX data rate + 수신 데이터 전송률 + + + NCF frames + NCF 프레임 + + + NCF count + NCF 카운트 + + + NCF bytes + NCF 바이트 + + + NCF frames/bytes + NCF 프레임/바이트 + + + NCF count/bytes + NCF 카운트/바이트 + + + NCF frames/count + NCF 프레임/카운트 + + + NCF frames/count/bytes + NCF 프레임/카운트/바이트 + + + NCF rate + NCF 속도 + + + SM frames + SM 프레임 + + + SM bytes + SM 바이트 + + + SM frames/bytes + SM 프레임/바이트 + + + SM rate + SM 속도 + + + Show + 표시 + + + Data + 데이터 + + + RX Data + RX 데이터 + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + 전송 시퀀스 번호 + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + 카운트 + + + Frame + 프레임 + + + SQN/Reason + SQN/원인 + + + Receivers + 수신자 + + + NAK frames + NAK 프레임 + + + NAK count + NAK 카운트 + + + NAK bytes + NAK 바이트 + + + NAK rate + NAK 속도 + + + NAK sequence numbers for transport + 전송 NAK 시퀀스 번호 + + + Display filter: + 표시 필터: + + + Regenerate statistics using this display filter + 이 표시 필터를 사용하여 통계를 재생성 + + + Apply + 적용 + + + Copy as CSV + CSV로 복사 + + + Copy the tree as CSV + 트리를 CSV로 복사 + + + Copy as YAML + YAML로 복사 + + + Copy the tree as YAML + 트리를 YAML로 복사 + + + Show the data frames column + 데이터 프레임 열 표시 + + + Show the data bytes column + 데이터 바이트 열 표시 + + + Show the data frames/bytes column + 데이터 프레임/바이트 열 표시 + + + Show the RX data frames column + RX 데이터 프레임 열 표시 + + + Show the RX data bytes column + RX 데이터 바이트의 열 표시 + + + Show the RX data frames/bytes column + RX 데이터 프레임/바이트의 열 표시 + + + Show the NCF frames column + NCF 프레임 열 표시 + + + Show the NCF bytes column + NCF 바이트의 열 표시 + + + Show the NCF count column + NCF 카운트 열 표시 + + + Show the data rate column + 데이터 전송률 열 표시 + + + Show the RX data rate column + RX 데이터 전송률 열 표시 + + + Show the NCF frames/bytes column + NCF 프레임/바이트 열 표시 + + + Show the NCF count/bytes column + NCF 카운트/바이트 열 표시 + + + Show the NCF frames/count column + NCF 프레임/카운트 열 표시 + + + Show the NCF frames/count/bytes column + NCF 프레임/카운트/바이트의 열 표시 + + + Show the NCF rate column + NCF 전송률 열 표시 + + + Show the SM frames column + SM 프레임 열 표시 + + + Show the SM bytes column + SM 바이트 열 표시 + + + Show the SM frames/bytes column + SM 프레임/바이트 열 표시 + + + Show the SM rate column + SM 전송율 열 표시 + + + Auto-resize columns to content + 내용에 맞추어 열을 자동 조정 + + + Resize columns to content size + 내용 크기에 맞춰 열을 조정 + + + LBT-RM Statistics failed to attach to tap + LBT-RM 통계 탭 할당에 실패했습니다 + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + LBT-RU 전송 통계 + + + Sources + 발신지 + + + Address/Transport/Client + 주소/전송/클라이언트 + + + Data frames + 데이터 프레임 + + + Data bytes + 데이터 바이트 + + + Data frames/bytes + 데이터 프레임/바이트 + + + Data rate + 데이터 전송률 + + + RX data frames + RX 데이터 프레임 + + + RX data bytes + RX 데이터 바이트 + + + RX data frames/bytes + RX 데이터 프레임/바이트 + + + RX data rate + RX 데이터 전송률 + + + NCF frames + NCF 프레임 + + + NCF count + NCF 카운트 + + + NCF bytes + NCF 바이트 + + + NCF frames/count + NCF 프레임/카운트 + + + NCF frames/bytes + NCF 프레임/바이트 + + + NCF count/bytes + NCF 카운트/바이트 + + + NCF frames/count/bytes + NCF 프레임/카운트/바이트 + + + NCF rate + NCF 속도 + + + SM frames + SM 프레임 + + + SM bytes + SM 바이트 + + + SM frames/bytes + SM 프레임/바이트 + + + SM rate + SM 전송률 + + + RST frames + RST 프레임 + + + RST bytes + RST 바이트 + + + RST frames/bytes + RST 프레임/바이트 + + + RST rate + RST 전송률 + + + Show + 표시 + + + Data SQN + 데이터 SQN + + + RX Data SQN + RX 데이터 SQN + + + NCF SQN + NCF SQN + + + SM SQN + SM SQN + + + RST reason + RST 원인 + + + details for transport + 전송 상세 정보 + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + 카운트 + + + Frame + 프레임 + + + Reason + 원인 + + + SQN/Reason + SQN/원인 + + + Receivers + 수신지 + + + Address/Transport + 주소/전송 + + + NAK frames + NAK 프레임 + + + NAK count + NAK 카운트 + + + NAK bytes + NAK 바이트 + + + NAK frames/count + NAK 프레임/카운트 + + + NAK count/bytes + NAK 카운트/바이트 + + + NAK frames/bytes + NAK 프레임/바이트 + + + NAK frames/count/bytes + NAK 프레임/카운트/바이트 + + + NAK rate + NAK 비율 + + + ACK frames + ACK 프레임 + + + ACK bytes + ACK 바이트 + + + ACK frames/bytes + ACK 프레임/바이트 + + + ACK rate + ACK 비율 + + + CREQ frames + CREQ 프레임 + + + CREQ bytes + CREQ 바이트 + + + CREQ frames/bytes + CREQ 프레임/바이트 + + + CREQ rate + CREQ 비율 + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + CREQ 요청 + + + Display filter: + 표시 필터: + + + Regenerate statistics using this display filter + 이 표시 필터를 사용하여 통계를 다시 생성 + + + Apply + 적용 + + + Copy as CSV + CSV로 복사 + + + Copy the tree as CSV + CSV로 트리를 복사 + + + Copy as YAML + YAML로 복사 + + + Copy the tree as YAML + YAML로 트리를 복사 + + + Show the data frames column + 데이터 프레임 열 표시 + + + Show the data bytes column + 데이터 바이트 열 표시 + + + Show the data frames/bytes column + 데이터 프레임/바이트 열 표시 + + + Show the data rate column + 데이터 전송률 열 표시 + + + Show the RX data frames column + 수신 데이터 프레임 열 표시 + + + Show the RX data bytes column + 수신 데이터 바이트 열 표시 + + + Show the RX data frames/bytes column + 수신 데이터 프레임/바이트 열 표시 + + + Show the RX data rate column + 수신 데이터 전송률 열 표시 + + + Show the NCF frames column + NCF 프레임 열 표시 + + + Show the NCF count column + NCF 카운트 열 표시 + + + Show the NCF bytes column + NCF 바이트 열 표시 + + + Show the NCF frames/bytes column + NCF 프레임/바이트 열 표시 + + + Show the NCF count/bytes column + NCF 카운트/바이트 열 표시 + + + Show the NCF frames/count column + NCF 프레임/카운트 열 표시 + + + Show the NCF frames/count/bytes column + NCF 프레임/카운트/바이트 열 표시 + + + Show the SM frames column + SM 프레임 열 표시 + + + Show the SM bytes column + SM 바이트 열 표시 + + + Show the SM frames/bytes column + SM 프레임/바이트 열 표시 + + + Show the SM rate column + SM 전송률 열 표시 + + + Show the RST frames column + RST 프레임 열 표시 + + + Show the RST bytes column + RST 바이트 열 표시 + + + Show the RST frames/bytes column + RST 프레임/바이트 열 표시 + + + Show the RST rate column + RST 전송률 열 표시 + + + Show the NAK frames column + NAK 프레임 열 표시 + + + Show the NAK count column + NAK 카운트 열 표시 + + + Show the NAK bytes column + NAK 바이트 열 표시 + + + Show the NAK frames/count column + NAK 프레임/카운트 열 표시 + + + Show the NAK count/bytes column + NAK 카운트/바이트 열 표시 + + + Show the NAK frames/bytes column + NAK 프레임/바이트 열 표시 + + + Show the NAK frames/count/bytes column + NAK 프레임/카운트/바이트 열 표시 + + + Show the NAK rate column + NAK 전송률 열 표시 + + + Show the ACK frames column + ACK 프레임 열 표시 + + + Show the ACK bytes column + ACK 바이트 내용을 열 표시 + + + Show the ACK frames/bytes column + ACK 프레임/바이트를 열 표시 + + + Show the ACK rate column + ACK 전송률 열 표시 + + + Show the CREQ frames column + CREQ 프레임 열 표시 + + + Show the CREQ bytes column + CREQ 바이트 열 표시 + + + Show the CREQ frames/bytes column + CREQ 프레임/바이트 열 표시 + + + Show the CREQ rate column + CREQ 전송률 열 표시 + + + Auto-resize columns to content + 내용에 따라 열을 자동 조정 + + + Resize columns to content size + 내용의 크기에 맞게 열을 조정 + + + Show the NCF rate column + NCF 전송률 열 표시 + + + LBT-RU Statistics failed to attach to tap + LBT-RU 통계 탭에 할당에 실패했습니다 + + + + LBMStreamDialog + + Dialog + 대화 상자 + + + Stream + 스트림 + + + Endpoint A + 종단점 A + + + Endpoint B + 종단점 B + + + Messages + 메시지 + + + Bytes + 바이트 + + + First Frame + 첫 프레임 + + + Last Frame + 마지막 프레임 + + + Display filter: + 표시 필터: + + + Regenerate statistics using this display filter + 이 표시 필터를 사용하여 통계를 다시 생성 + + + Apply + 적용 + + + Copy as CSV + CSV로 복사 + + + Copy the tree as CSV + 트리를 CSV로 복사 + + + Copy as YAML + YAML로 복사 + + + Copy the tree as YAML + 트리를 YAML로 복사 + + + LBM Stream failed to attach to tap + LBM 스트림을 탭에 할당하는 데 실패했습니다 + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + + + + %Ln item(s) + + %Ln item + + + + + LayoutPreferencesFrame + + Frame + 프레임 + + + Pane 1: + 평면 1: + + + Packet List + 패킷 목록 + + + Packet Details + 패킷 상세 정보 + + + Packet Bytes + 패킷 바이트 + + + Packet Diagram + 패킷 다이어그램 + + + None + 없음 + + + Pane 2: + 평면 2: + + + Pane 3: + 평면 3: + + + Packet List settings: + 패킷 목록 설정: + + + Show packet separator + 패킷 구분자 표시 + + + Show column definition in column context menu + 열의 상황에 맞는 메뉴에 열 정의 표시 + + + Allow the list to be sorted + 목록 정렬 허용 + + + Maximum number of cached rows (affects sorting) + 캐시될 행의 최대 개수(정렬 속도에 영향을 미침) + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + <html><head/><body><p>이 개수보다 더 많은 행이 표시되면 패킷 분해가 필요한 열 기준 정렬이 비활성화됩니다. 이 값을 증가시키면 캐시할 열 개수가 증가하며 메모리 사용량도 증가합니다.</p></body></html> + + + Enable mouse-over colorization + 마우스가 지나갈 때 색상 변경 활성화 + + + Status Bar settings: + 상태 표시줄 설정: + + + Show selected packet number + 선택한 패킷 번호 표시 + + + Show file load time + 파일 불러오기 시간 표시 + + + + LteMacStatisticsDialog + + LTE Mac Statistics + LTE MAC 통계 + + + Include SR frames in filter + 필터의 SR 프레임 포함 + + + Include RACH frames in filter + 필터의 RACH 프레임 포함 + + + MAC Statistics + MAC 통계 + + + + LteRlcGraphDialog + + Dialog + 대화 상자 + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>가치있고 놀라운 시간 절약형 키보드 단축키</h3> +<table><tbody> + +<tr><th>+</th><td>확대</td></th> +<tr><th>-</th><td>축소</td></th> +<tr><th>0</th><td>초기 상태로 그래프 다시 설정</td></th> + +<tr><th>→</th><td>오른쪽으로 10픽셀 이동</td></th> +<tr><th>←</th><td>왼쪽으로 10픽셀 이동</td></th> +<tr><th>↑</th><td>위쪽으로 10픽셀 이동</td></th> +<tr><th>↓</th><td>아래쪽으로 10픽셀 이동</td></th> +<tr><th><i>Shift+</i>→</th><td>오른쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>←</th><td>왼쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>↑</th><td>위쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>↓</th><td>아래쪽으로 1픽셀 이동</td></th> + +<tr><th>g</th><td>커서 위치의 패킷으로 이동</td></th> + +<tr><th>z</th><td>마우스 드래그/크기 조절 전환</td></th> +<tr><th>t</th><td>캡처/세션 시간 기점 전환</td></th> +<tr><th>Space</th><td>십자선 전환</td></th> + +</tbody></table> +</body></html> + + + Mouse + 마우스 + + + Drag using the mouse button. + 마우스 단추를 사용하여 드래그합니다. + + + drags + 드래그 + + + Select using the mouse button. + 마우스 단추를 사용하여 선택합니다. + + + zooms + 크기 조정 + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>그래프를 초기 상태로 다시 설정합니다.</p></body></html> + + + Reset + 초기화 + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>연결 방향을 전환합니다(역방향 플로 표시).</p></body></html> + + + Switch Direction + 방향 전환 + + + Reset Graph + 그래프 초기화 + + + Reset the graph to its initial state. + 그래프를 초기 상태로 다시 설정합니다. + + + 0 + 0 + + + Zoom In + 확대 + + + + + + + + + Zoom Out + 축소 + + + - + - + + + Move Up 10 Pixels + 위쪽으로 10픽셀 이동 + + + Up + 위쪽 + + + Move Left 10 Pixels + 왼쪽으로 10픽셀 이동 + + + Left + 왼쪽 + + + Move Right 10 Pixels + 오른쪽으로 10픽셀 이동 + + + Right + 오른쪽 + + + Move Down 10 Pixels + 아래쪽으로 10픽셀 이동 + + + Down + 아래쪽 + + + Move Up 1 Pixel + 위쪽으로 1픽셀 이동 + + + Shift+Up + Shift+위쪽 + + + Move Left 1 Pixel + 왼쪽으로 1픽셀 이동 + + + Shift+Left + Shift+왼쪽 + + + Move Right 1 Pixel + 오른쪽으로 1픽셀 이동 + + + Shift+Right + Shift+오른쪽 + + + Move Down 1 Pixel + 아래쪽으로 1픽셀 이동 + + + Move down 1 Pixel + 아래쪽으로 1픽셀 이동 + + + Shift+Down + Shift+아래쪽 + + + Drag / Zoom + 드래그/크기 조정 + + + Toggle mouse drag / zoom behavior + 마우스 드래그/크기 조정 동작 전환 + + + Z + Z + + + Crosshairs + 십자선 + + + Toggle crosshairs + 십자선 표시 전환 + + + Space + 스페이스 + + + Move Up 100 Pixels + 위쪽으로 100픽셀 이동 + + + PgUp + PgUp + + + PgDown + PgDown + + + Go To Packet Under Cursor + 커서 아래 패킷으로 이동 + + + Go to packet currently under the cursor + 현재 커서 아래 패킷으로 이동 + + + G + G + + + Zoom In X Axis + X축 확대 + + + X + X + + + Zoom Out Y Axis + Y축 축소 + + + Shift+Y + Shift+Y + + + Zoom In Y Axis + Y축 확대 + + + Y + Y + + + Zoom Out X Axis + X축 축소 + + + Shift+X + Shift+X + + + Switch direction (swap between UL and DL) + 방향 전환(UL과 DL 전환) + + + D + D + + + Time + 시간 + + + Sequence Number + 시퀀스 번호 + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + LTE RLC 그래프(UE=%1 chan=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + LTE RLC 그래프 - 선택한 채널 없음 + + + Save As… + 다른 이름으로 저장… + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s seq %4 len %5) + + + Click to select packet + 클릭하여 패킷 선택 + + + Packet + 패킷 + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 커서를 놓으면 크기 조정, x = %1 - %2, y = %3 - %4 + + + Unable to select range. + 범위를 선택할 수 없습니다. + + + Click to select a portion of the graph. + 클릭하여 그래프의 부분을 선택하십시오. + + + Portable Document Format (*.pdf) + PDF 형식 (*.pdf) + + + Portable Network Graphics (*.png) + PNG 형식 (*.png) + + + Windows Bitmap (*.bmp) + Windows 비트맵 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 형식 (*.jpeg *.jpg) + + + Save Graph As… + 다른 이름으로 그래프 저장… + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + LTE RLC 통계 + + + Include SR frames in filter + 필터의 SR 프레임 포함 + + + Include RACH frames in filter + 필터의 RACH 프레임 포함 + + + Use RLC frames only from MAC frames + MAC 프레임에 있는 RLC 프레임만 사용 + + + UL Frames + UL 프레임 + + + UL Bytes + UL 바이트 + + + UL MB/s + UL MB/s + + + UL ACKs + UL ACK 개수 + + + UL NACKs + UL NACK 개수 + + + UL Missing + UL 누락 + + + DL Frames + DL 프레임 + + + DL Bytes + DL 바이트 + + + DL MB/s + DL MB/s + + + DL ACKs + DL ACK 개수 + + + DL NACKs + DL NACK 개수 + + + DL Missing + DL 누락 + + + RLC Statistics + RLC 통계 + + + + MainStatusBar + + Ready to load or capture + 불러오거나 캡처할 준비됨 + + + Ready to load file + 파일 읽기 준비됨 + + + Open the Capture File Properties dialog + 캡처 파일 속성 대화 상자 열기 + + + Profile: %1 + 프로필: %1 + + + Manage Profiles… + 프로필 관리… + + + New… + 새로 만들기… + + + Edit… + 편집… + + + Import + 가져오기 + + + Export + 내보내기 + + + Delete + 삭제 + + + Switch to + 전환 + + + is the highest expert information level + is the highest expert info level + : 현재 최고 정보 수준 + + + ERROR + 오류 + + + WARNING + 경고 + + + NOTE + 주의 + + + CHAT + 대화 + + + No expert information + No expert info + 전문가 정보 없음 + + + %Ln byte(s) + , %1 bytes + + %Ln바이트 + + + + Byte %1 + 바이트 %1 + + + Bytes %1-%2 + 바이트 %1-%2 + + + Selected Packet: %1 %2 + 선택된 패킷: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + 패킷 수: %1 %4 표시됨: %2(%3%) + + + %1 Selected: %2 (%3%) + %1 선택됨: %2(%3%) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 표시됨: %2(%3%) + + + %1 Dropped: %2 (%3%) + %1 누락됨: %2(%3%) + + + %1 Ignored: %2 (%3%) + %1 무시됨: %2(%3%) + + + %1 Comments: %2 + %1 주석: %2 + + + %1 Load time: %2:%3.%4 + %1 불러오기 시간: %2: %3.%4 + + + No Packets + 패킷 없음 + + + From Zip File... + ZIP 파일에서... + + + From Directory... + 디렉터리에서... + + + Selected Personal Profile... + 선택한 개인 프로필... + + + All Personal Profiles... + 모든 개인 프로필... + + + Packets: %1 + 패킷: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + 프레임 + + + Checking this will save the size, position, and maximized state of the main window. + 이 옵션을 사용하면 주 창의 크기, 위치, 최대화 상태를 저장합니다. + + + Remember main window size and placement + 주 창의 크기와 위치 기억 + + + Open files in + 파일 열기 위치 + + + This folder: + 이 폴더: + + + Browse… + Browse... + 찾아보기... + + + The most recently used folder + 가장 최근에 사용한 폴더 + + + Show up to + 최대 표시 개수 + + + filter entries + 개의 필터 항목 + + + recent files + 개의 최근 파일 + + + Confirm unsaved capture files + 캡처 파일을 저장하지 않았을 때 확인 + + + Display autocompletion for filter text + 필터 텍스트 자동 완성 표시 + + + Main toolbar style: + 주 도구 모음 스타일: + + + Icons only + 아이콘만 + + + Text only + 텍스트만 + + + Icons & Text + 아이콘과 텍스트 + + + Window title + 창 제목 + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>기존 제목 뒤쪽에 덧붙일 사용자 정의 창 제목입니다.<br/>%F = 캡처 파일 경로<br/>%P = 프로필 이름<br/>%S = 값 또는 정적 텍스트가 있는 변수에 둘러싸인 경우에만 표시되는 조건부 구분 기호 (&quot; - &quot;)<br/>%V = 버전 정보</p></body></html> + + + Prepend window title + 창 제목 접두어 + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>기존 제목 앞쪽에 덧붙일 사용자 정의 창 제목입니다.<br/>%F = 캡처 파일 경로<br/>%P = 프로필 이름<br/>%S = 값 또는 정적 텍스트가 있는 변수에 둘러싸인 경우에만 표시되는 조건부 구분 기호 (&quot; - &quot;)<br/>%V = 버전 정보</p></body></html> + + + Language: + 언어: + + + Use system setting + 시스템 설정 사용 + + + Open Files In + 파일 열기 위치 + + + + ManageInterfacesDialog + + Manage Interfaces + 인터페이스 관리 + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>체크 상자를 클릭하여 인터페이스를 표시하거나 숨길 수 있습니다.</p></body></html> + + + Local Interfaces + 로컬 인터페이스 + + + Show + 표시 + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>목록에 캡처할 파이프를 추가하거나 기존의 파이프를 삭제합니다.</p></body></html> + + + Pipes + 파이프 + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>기본 설정을 사용하여 새 파이프를 추가합니다.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>목록에서 선택한 파이프를 삭제합니다.</p></body></html> + + + Remote Interfaces + 원격 인터페이스 + + + Host / Device URL + 호스트/장치 URL + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>원격 호스트와 인터페이스를 추가합니다</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>목록에서 선택한 호스트를 삭제합니다.</p></body></html> + + + Remote Settings + 원격 설정 + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + 이 버전의 Wireshark는 파이프의 설정을 저장하지 않습니다. + + + This version of Wireshark does not save remote settings. + 이 버전의 Wireshark는 원격 설정을 저장하지 않습니다. + + + This version of Wireshark does not support remote interfaces. + 이 버전의 Wireshark는 원격 인터페이스를 지원하지 않습니다. + + + New Pipe + 새 파이프 + + + + ManufDialog + + MAC Address Blocks + MAC 주소 블록 + + + Search MAC address or address prefix. Special purpose bits are masked. + MAC 주소나 주소 접두사를 검색합니다. 특수 목적 비트는 마스크됩니다. + + + MAC Address + MAC 주소 + + + Search vendor name using a case-insentitive regular expression. + 대소문자를 구분하지 않는 정규 표현식으로 제조사 이름을 검색합니다. + + + Vendor Name + 제조사 이름 + + + Show short name column. + 짧은 이름 열을 표시합니다. + + + Short name + 짧은 이름 + + + Select all + 모두 선택 + + + Copy + 복사 + + + Find + 찾기 + + + Clear + 지우기 + + + + ManufTableModel + + Address Block + 주소 블록 + + + Short Name + 짧은 이름 + + + Vendor Name + 제조사 이름 + + + + ModulePreferencesScrollArea + + ScrollArea + 스크롤 영역 + + + + Mtp3SummaryDialog + + Dialog + 대화 상자 + + + MTP3 Summary + MTP3 요약 + + + File + 파일 + + + Name + 이름 + + + Length + 길이 + + + Format + 형식 + + + Snapshot length + 스냅샷 길이 + + + Data + 데이터 + + + First packet + 첫 패킷 + + + Last packet + 마지막 패킷 + + + Elapsed + 경과 + + + Packets + 패킷 + + + Service Indicator (SI) Totals + 서비스 표시기 (SI) 합계 + + + SI + SI + + + MSUs + MSU + + + MSUs/s + MSU/초 + + + Bytes + 바이트 + + + Bytes/MSU + 바이트/MSU + + + Bytes/s + 바이트/초 + + + Totals + 합계 + + + Total MSUs + 총 MSU + + + Total Bytes + 총 바이트 + + + Average Bytes/MSU + 평균 바이트/MSU + + + Average Bytes/s + 평균 바이트/초 + + + + MulticastStatisticsDialog + + UDP Multicast Streams + UDP 멀티캐스트 스트림 + + + Source Address + 발신지 주소 + + + Source Port + 발신지 포트 + + + Destination Address + 목적지 주소 + + + Destination Port + 목적지 포트 + + + Packets + 패킷 + + + Packets/s + 패킷/초 + + + Avg BW (bps) + 평균 대역폭(bps) + + + Max BW (bps) + 최대 대역폭(bps) + + + Max Burst + 최대 버스트 + + + Burst Alarms + 버스트 경고 + + + Max Buffers (B) + 최대 버퍼(바이트) + + + Buffer Alarms + 버퍼 경고 + + + Burst measurement interval (ms): + 버스트 측정 간격(밀리초): + + + Burst alarm threshold (packets): + 버스트 경고 한계 값(패킷 수): + + + Buffer alarm threshold (B): + 버퍼 경고 한계 값(바이트): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + 스트림 비우기 속도(Kb/s): + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + 총 비우기 속도(Kb/s): + + + The burst interval must be between 1 and 1000. + 버스트 간격은 1 이상 1000 이하여야 합니다. + + + The burst alarm threshold isn't valid. + 버스트 경고 경계값이 잘못되었습니다. + + + The buffer alarm threshold isn't valid. + 버퍼 경고 경계값이 잘못되었습니다. + + + The stream empty speed should be between 1 and 10000000. + 스트림 비우기 속도는 1에서 10000000 사이여야 합니다. + + + The total empty speed should be between 1 and 10000000. + 총 비우기 속도는 1에서 10000000 사이여야 합니다. + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + 스트림 %1개, 평균 대역폭: %2 bps, 최대 대역폭: %3 bps, 최대 버스트: %4 / %5 ms, 최대 버퍼: %6바이트 + + + + PacketCommentDialog + + Edit Packet Comment + 패킷 주석 편집 + + + Add Packet Comment + 패킷 주석 추가 + + + + PacketDiagram + + Packet diagram + 패킷 다이어그램 + + + Show Field Values + 필드 값 표시 + + + Save Diagram As… + 다른 이름으로 다이어그램 저장… + + + Copy as Raster Image + 래스터 이미지로 복사 + + + …as SVG + …SVG로 + + + Portable Network Graphics (*.png) + PNG 형식 (*.png) + + + Windows Bitmap (*.bmp) + Windows 비트맵 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 형식 (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + SVG 형식 (*.png) + + + Save Graph As… + 다른 이름으로 그래프 저장… + + + + PacketDialog + + Dialog + 대화 상자 + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + 패킷 바이트 표시 + + + Packet %1 + 패킷 %1 + + + [%1 closed] + [%1 닫음] + + + Byte %1 + 바이트 %1 + + + Bytes %1-%2 + 바이트 %1-%2 + + + %Ln byte(s) + + %Ln바이트 + + + + + PacketFormatGroupBox + + GroupBox + 그룹 상자 + + + Packet Format + 패킷 형식 + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>패킷 목록과 유사한 패킷 요약 행</p></body></html> + + + Summary line + 요약 행 + + + Include column headings + 열 머리글 포함 + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>프로토콜 트리와 유사한 패킷 자세한 정보</p></body></html> + + + Details: + 자세한 정보: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>최상위 패킷 자세한 정보 항목만 내보내기</p></body></html> + + + All co&llapsed + 모두 접기(&L) + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>현재 표시된 대로 패킷 자세한 정보를 확장 및 축소합니다.</p></body></html> + + + As displa&yed + 표시된 대로(&Y) + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>모든 패킷 항목을 내보내기</p></body></html> + + + All e&xpanded + 모두 펴기(&X) + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>패킷 바이트 보기와 유사하게 패킷의 16진수 데이터 내보내기</p></body></html> + + + Bytes + 바이트 + + + Include secondary data sources + 2차 데이터 원본 포함 + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + <html><head/><body><p>프레임 외에도 재조합하거나 복호화한 것 등의 2차 데이터 원본의 16진수 덤프를 생성합니다</p></body></html> + + + + PacketList + + Protocol Preferences + 프로토콜 설정 + + + Summary as Text + 텍스트로 요약 + + + …as CSV + …CSV로 + + + …as YAML + …YAML로 + + + Decode As… + 다른 형식으로 디코드… + + + Frame %1: %2 + + + 프레임 %1: %2 + + + + + [ Comment text exceeds %1. Stopping. ] + [ 주석 텍스트가 %1를 초과합니다. 중지합니다. ] + + + + PacketListHeader + + Align Left + 왼쪽 정렬 + + + Align Center + 가운데 정렬 + + + Align Right + 오른쪽 정렬 + + + Edit Column + 열 편집 + + + Resize to Contents + 내용에 맞게 조정 + + + Column Preferences… + 열 설정… + + + Resize Column to Width… + 열 너비 조정… + + + Resolve Names + 이름 해석 + + + Remove this Column + 이 열 삭제 + + + Column %1 + 열 %1 + + + Width: + 폭: + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + %1(으)로는 표시된 행이 %2개 이하일 때만 정렬할 수 있습니다. 레이아웃 설정에서 캐시 크기를 증가시키십시오 + + + Sorting "%1"… + "%1" 정렬 중… + + + Sorting … + 정렬 중 … + + + + PacketRangeGroupBox + + Form + + + + Packet Range + 패킷 범위 + + + - + - + + + Displayed + 표시된 패킷 + + + &Marked packets only + 마크한 패킷만(&M) + + + &Range: + 범위(&R): + + + Remove &ignored packets + 무시된 패킷 삭제(&I) + + + Include &depended upon packets + 의존하는 패킷 포함(&I) + + + Also include packets depended upon, such as those used to reassemble displayed packets + 표시된 패킷을 재조합하는 데 사용된 패킷 등 의존하는 패킷 포함 + + + First &to last marked + 마크한 처음부터 마지막까지(&T) + + + &All packets + 모든 패킷(&A) + + + &Selected packets only + 선택한 패킷만(&S) + + + Captured + 캡처된 패킷 + + + + PathSelectionDelegate + + Open a pipe + 파이프 열기 + + + + PathSelectionEdit + + Browse + 탐색 + + + Select a path + 경로 선택 + + + + PluginListModel + + Name + 이름 + + + Version + 버전 + + + Type + 유형 + + + Path + 경로 + + + + PortsModel + + All entries + 모든 항목 + + + tcp + tcp + + + udp + udp + + + sctp + sctp + + + dccp + dccp + + + Name + 이름 + + + Port + 포트 + + + Type + 유형 + + + + PreferenceEditorFrame + + Frame + 프레임 + + + … + + + + a preference + 설정 + + + Browse… + 찾아보기… + + + Open %1 preferences… + %1 설정 열기… + + + Invalid value. + 값이 잘못되었습니다. + + + + PreferencesDialog + + Search: + 검색: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + 설정 + + + + PrefsModel + + Advanced + 고급 + + + Appearance + 모양 + + + Layout + 레이아웃 + + + Columns + + + + Font and Colors + 글꼴 및 색상 + + + Capture + 캡처 + + + Expert + 전문가 + + + Filter Buttons + 필터 버튼 + + + RSA Keys + RSA 키 + + + + PrintDialog + + Packet Format + 패킷 형식 + + + Print each packet on a new page + 각 패킷마다 새로운 페이지에 인쇄 + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>각 페이지에 캡처 파일 정보 인쇄</p></body></html> + + + Capture information header + 캡처 정보 헤더 + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>&quot;+&quot; 키와 &quot;-&quot; 키로 미리 보기 크기를 조정할 수 있습니다. &quot;0&quot; 키로 원래 크기로 복원합니다.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">확대/축소: +, -, 초기화: 0</span></p></body></html> + + + Packet Range + 패킷 범위 + + + Print + 인쇄 + + + &Print… + 인쇄(&P)… + + + Page &Setup… + 페이지 설정(&S)… + + + %1 %2 total packets, %3 shown + %1 전체 패킷 %2개, %3개 표시됨 + + + Print Error + 인쇄 오류 + + + Unable to print to %1. + %1(으)로 인쇄할 수 없습니다. + + + + ProfileDialog + + Search for profile … + 프로필 찾기 … + + + Create a new profile using default settings. + 기본 설정을 사용하여 새로운 프로필을 만듭니다. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>이 프로필을 삭제하십시오. 시스템에서 제공한 프로필은 삭제할 수 없습니다. 삭제 후에 기본 프로필이 초기화됩니다.</p></body></html> + + + Copy this profile. + 이 프로필을 복사합니다. + + + Configuration Profiles + 설정 프로필 + + + Import + noun + 가져오기 + + + Export + noun + 내보내기 + + + From Zip File... + ZIP 파일에서... + + + From Directory... + 디렉터리에서... + + + %Ln Selected Personal Profile(s)... + + 선택한 개인 프로필 %Ln개... + + + + All Personal Profiles... + 모든 개인 프로필... + + + New profile + 새 프로필 + + + Profile Error + 프로필 오류 + + + Exporting profiles + 프로필 내보내기 + + + No profiles found for export + 내보낼 프로필을 찾을 수 없음 + + + Select zip file for export + 내보낼 ZIP 파일 선택 + + + … %Ln selected personal profile(s) + + … 선택한 개인 프로필 %Ln개 + + + + %Ln selected personal profile(s) + + 선택한 개인 프로필 %Ln개 + + + + An import of profiles is not allowed, while changes are pending + 변경 사항이 보류 중일 때 프로필 가져오기는 허용되지 않습니다 + + + An import is pending to be saved. Additional imports are not allowed + 가져온 항목 저장이 보류 중입니다. 추가 가져오기는 허용되지 않습니다 + + + An export of profiles is only allowed for personal profiles + 프로필 내보내기는 개인 프로필에만 허용됩니다 + + + An export of profiles is not allowed, while changes are pending + 변경 사항이 보류 중일 때 프로필 내보내기는 허용되지 않습니다 + + + %Ln profile(s) exported + + 프로필 %Ln개 내보냄 + + + + Select zip file for import + 가져올 ZIP 파일 선택 + + + Select directory for import + 가져올 디렉터리 선택 + + + Zip File (*.zip) + ZIP 파일 (*.zip) + + + Error + 오류 + + + An error has occurred while exporting profiles + 프로필을 내보내는 중 오류가 발생했습니다 + + + No profiles found for import in %1 + %1에서 가져올 프로필을 찾을 수 없음 + + + %Ln profile(s) imported + + 프로필 %Ln개 가져옴 + + + + , %Ln profile(s) skipped + + , 프로필 %Ln개 건너뜀 + + + + Importing profiles + 프로필 가져오기 + + + %Ln profile(s) selected + + %Ln profile selected + + + + + ProfileModel + + Resetting to default + 기본값으로 초기화 중 + + + Imported profile + 가져온 프로필 + + + This is a system provided profile + 이것은 시스템에서 제공한 프로필입니다 + + + A profile change for this name is pending + 이 이름의 프로필 변경을 보류하고 있습니다 + + + (See: %1) + (참조: %1) + + + This is an invalid profile definition + 잘못된 프로필 정의입니다 + + + A profile already exists with this name + 이 이름의 프로필이 이미 있습니다 + + + A profile with this name is being deleted + 이 이름의 프로필이 삭제되고 있습니다 + + + Created from default settings + 기본 설정에서 생성됨 + + + system provided + 시스템 제공 + + + deleted + 삭제됨 + + + copy + noun + 복사 + + + Exporting profiles while changes are pending is not allowed + 변경 사항이 보류 중일 때 프로필 내보내기는 허용되지 않습니다 + + + No profiles found to export + 내보낼 프로필을 찾지 못했습니다 + + + Can't delete profile directory + 프로필 디렉터리를 삭제할 수 없습니다 + + + A profile name cannot contain the following characters: %1 + 프로필 이름에는 다음 문자를 사용할 수 없습니다: %1 + + + A profile name cannot contain the '/' character + 프로필 이름에는 '/' 문자를 사용할 수 없습니다 + + + A profile cannot start or end with a period (.) + 프로필은 마침표(.)로 시작하거나 끝낼 수 없습니다 + + + Default + 기본값 + + + Global + 전역 + + + Personal + 개인 + + + Renamed from: %1 + 다음으로부터 이름 변경: %1 + + + Copied from: %1 + 다음으로부터 복사됨: %1 + + + renamed to %1 + %1로 이름 변경 + + + Profile + 프로필 + + + Type + 유형 + + + + ProfileSortModel + + All profiles + 모든 프로필 + + + Personal profiles + 개인 프로필 + + + Global profiles + 전역 프로필 + + + + ProgressFrame + + Frame + 프레임 + + + Loading + 불러오는 중 + + + + ProtoTree + + Packet details + 패킷 자세한 정보 + + + Not a field or protocol + 필드나 프로토콜이 아님 + + + No field reference available for text labels. + 텍스트 레이블에 사용 가능한 필드 참조가 없습니다. + + + Expand Subtrees + 하위 트리 펴기 + + + Collapse Subtrees + 하위 트리 접기 + + + Expand All + 모두 펴기 + + + Collapse All + 모두 접기 + + + Copy + 복사 + + + All Visible Items + 모든 표시된 항목 + + + All Visible Selected Tree Items + 모든 표시된 선택한 트리 항목 + + + Description + 설명 + + + Field Name + 필드 이름 + + + Value + + + + As Filter + 필터로 + + + Wiki Protocol Page + 위키 프로토콜 페이지 + + + Filter Field Reference + 필터 필드 참조 + + + Copied + 복사됨 + + + Wiki Page for %1 + %1 위키 페이지 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Wireshark 위키는 커뮤니티에 의해 운영되고 있습니다.</p><p>불러올 페이지는 훌륭하거나 불완전하거나 잘못되었거나 존재하지 않을지도 모릅니다.</p><p>위키로 이동하시겠습니까?</p> + + + Colorize with Filter + 필터로 색상화 + + + + ProtocolHierarchyDialog + + Dialog + 대화 상자 + + + Protocol + 프로토콜 + + + Percent Packets + 패킷 비율 + + + Packets + 패킷 + + + Percent Bytes + 바이트 비율 + + + Bytes + 바이트 + + + Bits/s + 비트/초 + + + End Packets + 최종 패킷 + + + End Bytes + 최종 바이트 + + + End Bits/s + 최종 초당 비트 수 + + + PDUs + PDU + + + <small><i>A hint.</i></small> + <small><i>힌트입니다.</i></small> + + + Copy as CSV + CSV로 복사 + + + Copy stream list as CSV. + 스트림 목록을 CSV로 복사합니다. + + + Copy as YAML + YAML로 복사 + + + Copy stream list as YAML. + 스트림 목록을 YAML로 복사합니다. + + + Copy short names + 짧은 이름 복사 + + + Copy short protocol names in use. + 짧은 프로토콜 이름을 복사합니다. + + + Disable unused protocols + 사용되지 않는 프로토콜 비활성화 + + + Disable all protocols but those listed. + 목록에 없는 프로토콜을 비활성화합니다. + + + Re-enable unused protocols + 사용되지 않는 프로토콜 다시 활성화 + + + Re-enable protocols that were disabled in this dialog. + 이 대화 상자에서 비활성화된 프로토콜을 다시 활성화합니다. + + + Protocol Hierarchy Statistics + 프로토콜 계층 통계 + + + Copy + 복사 + + + as CSV + CSV로 + + + as YAML + YAML로 + + + protocol short names + 프로토콜 짧은 이름 + + + Protocols + 프로토콜 + + + Disable unused + 미사용 비활성화 + + + Revert changes + 변경 사항 되돌리기 + + + No display filter. + 표시 필터가 없습니다. + + + Display filter: %1 + 표시 필터: %1 + + + Unused protocols have been disabled. + 사용되지 않는 프로토콜이 비활성화되었습니다. + + + Protocol changes have been reverted. + 프로토콜 변경 사항이 되돌려졌습니다. + + + + ProtocolPreferencesMenu + + Protocol Preferences + 프로토콜 설정 + + + No protocol preferences available + 프로토콜 설정을 사용할 수 없음 + + + Disable %1 + %1 비활성화 + + + %1 has no preferences + %1에는 설정이 없습니다 + + + Open %1 preferences… + %1 설정 열기… + + + + QObject + + Average Throughput (bits/s) + 평균 처리량(비트/초) + + + Round Trip Time (ms) + 왕복 시간(ms) + + + Segment Length (B) + 세그먼트 길이(바이트) + + + Sequence Number (B) + 시퀀스 번호(바이트) + + + Time (s) + 시간(초) + + + Window Size (B) + 윈도 크기(바이트) + + + [no capture file] + [캡처 파일 없음] + + + Conversation + 대화 상자 + + + Bars show the relative timeline for each conversation. + 막대는 각 대화에 대한 상대적인 시간 표시열(타임라인)을 표시합니다. + + + Endpoint + 종단점 + + + Apply as Filter + 필터로 적용 + + + Prepare as Filter + 필터로 준비 + + + Find + 찾기 + + + Colorize + 색상화 + + + Look Up + 검색 + + + Copy + 복사 + + + UNKNOWN + 알 수 없음 + + + Selected + 선택됨 + + + Not Selected + 선택 안 됨 + + + …and Selected + …그리고 선택됨 + + + …or Selected + …또는 선택됨 + + + …and not Selected + …그리고 선택 안 됨 + + + …or not Selected + …또는 선택 안 됨 + + + A + A + + + B + B + + + Any + 모두 + + + Don't show this message again. + 이 메시지를 다시 표시하지 않습니다. + + + Multiple problems found + 여러 가지 문제가 발견됨 + + + %1 (%L2%) + %1(%L2%) + + + No entries. + 항목이 없습니다. + + + %1 entries. + 항목 %1개. + + + Base station + 기지국 + + + <Broadcast> + <브로드캐스트> + + + <Hidden> + <숨김> + + + BSSID + BSSID + + + Beacons + 비콘 + + + Data Pkts + 데이터 패킷 + + + Protection + 보호 + + + Address + 주소 + + + Pkts Sent + 발신 패킷 + + + Pkts Received + 수신 패킷 + + + Comment + 주석 + + + Wrong sequence number + 잘못된 시퀀스 번호 + + + Payload changed to PT=%1 + 페이로드 변경: PT=%1 + + + Incorrect timestamp + 잘못된 타임스탬프 + + + Marker missing? + 마커 없음? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + 유형 + + + UEId + UEId + + + UL Frames + UL 프레임 + + + UL Bytes + UL 바이트 + + + UL MB/s + UL MB/s + + + UL Padding % + UL 패딩 % + + + UL Re TX + UL Re TX + + + DL Frames + DL 프레임 + + + DL Bytes + DL 바이트 + + + DL MB/s + DL MB/s + + + DL Padding % + DL 패딩 % + + + DL CRC Failed + DL CRC 실패 + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + 사전 정의 + + + Unknown (%1) + 알 수 없음(%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + 알 수 없음 + + + UE Id + UE Id + + + Name + 이름 + + + Mode + 모드 + + + Priority + 우선 순위 + + + default + 기본값 + + + DLT %1 + DLT %1 + + + Invalid Display Filter + 잘못된 표시 필터 + + + The filter expression %1 isn't a valid display filter. (%2). + 필터 표현식 %1이(가) 올바른 표시 필터가 아닙니다. (%2). + + + Error + 오류 + + + No remote interfaces found. + 원격 인터페이스를 찾을 수 없습니다. + + + PCAP not found + PCAP을 찾을 수 없음 + + + Unknown error + 알 수 없는 오류 + + + Default + 기본값 + + + Changed + 변경됨 + + + Has this preference been changed? + 이 환경 설정이 변경되었습니까? + + + Default value is empty + 기본값이 비어 있음 + + + Gap in dissection + 분해 공백 + + + Edit… + 편집… + + + Browse… + 찾아보기... + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + 원격 인터페이스 + + + Host: + 호스트: + + + Port: + 포트: + + + Authentication + 인증 + + + Null authentication + Null 인증 + + + Password authentication + 암호 인증 + + + Username: + 사용자 이름: + + + Password: + 암호: + + + Clear list + 목록 지우기 + + + Error + 오류 + + + No remote interfaces found. + 원격 인터페이스를 찾을 수 없습니다. + + + PCAP not found + PCAP을 찾을 수 없음 + + + + RemoteSettingsDialog + + Remote Capture Settings + 원격 캡처 설정 + + + Capture Options + 캡처 옵션 + + + Do not capture own RPCAP traffic + 내 RPCAP 트래픽은 캡처하지 않기 + + + Use UDP for data transfer + 데이터 전송에 UDP 사용 + + + Sampling Options + 샘플링 옵션 + + + None + 없음 + + + 1 of + 다음 중 하나: + + + packets + 개 패킷 + + + 1 every + 다음 시간마다 하나: + + + milliseconds + 밀리초 + + + + ResolvedAddressesDialog + + Dialog + 대화 상자 + + + Hosts + 호스트 + + + Search for entry (min 3 characters) + 항목 검색(최소 3글자) + + + Ports + 포트 + + + Search for port or name + 포트 또는 이름을 검색 + + + Capture File Comments + 캡처 파일 주석 + + + Comment + 주석 + + + Show the comment. + 주석을 표시합니다. + + + IPv4 Hash Table + IPv4 해시 테이블 + + + Show the IPv4 hash table entries. + IPv4 해시 테이블 항목을 표시합니다. + + + IPv6 Hash Table + IPv6 해시 테이블 + + + Show the IPv6 hash table entries. + IPv6 해시 테이블 항목을 표시합니다. + + + Show All + 모두 표시 + + + Show all address types. + 모든 주소 유형을 표시합니다. + + + Hide All + 모두 숨기기 + + + Hide all address types. + 모든 주소 유형을 숨깁니다. + + + IPv4 and IPv6 Addresses (hosts) + IPv4와 IPv6 주소(hosts) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + 해석된 IPv4와 IPv6 호스트 이름을 "hosts" 형식으로 표시합니다. + + + Port names (services) + 포트 이름(services) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + 해석된 포트 이름을 "services" 형식으로 표시합니다. + + + Ethernet Addresses + 이더넷 주소 + + + Show resolved Ethernet addresses in "ethers" format. + 해석된 이더넷 주소를 "ethers" 형식으로 표시합니다. + + + Ethernet Well-Known Addresses + 이더넷 알려진 주소 + + + Show well-known Ethernet addresses in "ethers" format. + 알려진 이더넷 주소를 "ethers" 형식으로 표시합니다. + + + Ethernet Manufacturers + 이더넷 제조사 + + + Show Ethernet manufacturers in "ethers" format. + 이더넷 제조사를 "ethers" 형식으로 표시합니다. + + + [no file] + [파일 없음] + + + Resolved Addresses + 해석된 주소 + + + # Resolved addresses found in %1 + # %1에서 발견된 해석된 주소 + + + # Comments +# +# + # 주석 +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + %1 응답 시간 지연 통계 + + + Type + 유형 + + + Messages + 메시지 + + + Min SRT + 최소 SRT + + + Max SRT + 최대 SRT + + + Avg SRT + 평균 SRT + + + Min in Frame + 프레임의 최소 + + + Max in Frame + 프레임의 최대 + + + Open Requests + 열기 요청 + + + Discarded Responses + 취소된 응답 + + + Repeated Requests + 반복된 요청 + + + Repeated Responses + 반복된 응답 + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>프로그램과 버전을 선택하고, 필요한 경우 필터를 입력하고 적용을 클릭하십시오.</i></small> + + + Version: + 버전: + + + Program: + 프로그램: + + + DCE-RPC Service Response Times + DCE-RPC 서비스 응답 시간 + + + ONC-RPC Service Response Times + ONC-RPC 서비스 응답 시간 + + + + RsaKeysFrame + + RSA Keys + RSA 키 + + + RSA private keys are loaded from a file or PKCS #11 token. + RSA 개인 키는 파일 또는 PKCS #11 토큰에서 불러옵니다. + + + Add new keyfile… + 새 키 파일 추가… + + + Add new token… + 새 토큰 추가… + + + Remove key + 키 삭제 + + + PKCS #11 provider libraries. + PKCS #11 공급자 라이브러리입니다. + + + Add new provider… + 새 공급자 추가… + + + Remove provider + 공급자 삭제 + + + Add PKCS #11 token or key + PKCS #11 토큰 또는 키 추가 + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + 새 PKCS #11 토큰 또는 키를 찾을 수 없습니다. PKCS #11 공급자 추가를 고려해 보십시오. + + + Select a new PKCS #11 token or key + 새 PKCS #11 토큰 또는 키 선택 + + + PKCS #11 token or key + PKCS #11 토큰 또는 키 + + + Enter PIN or password for %1 (it will be stored unencrypted) + %1의 PIN이나 암호를 입력하십시오(암호화되지 않은 상태로 저장됨) + + + Enter PIN or password for key + 키의 PIN이나 암호를 입력하십시오 + + + Key could not be added: %1 + 키를 추가할 수 없음: %1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + RSA 개인 키 (*.pem *.p12 *.pfx *.key);;모든 파일 ( + + + Select RSA private key file + RSA 개인 키 파일 선택 + + + Libraries (*.dll) + 라이브러리 (*.dll) + + + Libraries (*.so) + 라이브러리 (*.so) + + + Select PKCS #11 Provider Library + PKCS #11 공급자 라이브러리 선택 + + + Changes will apply after a restart + 다시 시작한 후 변경 사항이 적용됩니다 + + + PKCS #11 provider %1 will be removed after the next restart. + 다음에 다시 시작할 때 PKCS #11 공급자 %1이(가) 삭제됩니다. + + + + RtpAnalysisDialog + + Dialog + 대화 상자 + + + Packet + 패킷 + + + Sequence + 순서 + + + Delta (ms) + 델타(밀리초) + + + Jitter (ms) + Jitter + 지터(밀리초) + + + Skew + Skew + + + Bandwidth + 대역폭 + + + Marker + 마커 + + + Status + 상태 + + + Stream %1 + 스트림 %1 + + + Stream %1 Jitter + 스트림 %1 지터 + + + Stream %1 Difference + 스트림 %1 차이 + + + Stream %1 Delta + 스트림 %1 델타 + + + %1 streams, + 스트림 %1개, + + + Save one stream CSV + 스트림 한 개를 CSV로 저장 + + + Save all stream's CSV + 모든 스트림을 CSV로 저장 + + + &Analyze + 분석(&A) + + + Open the analysis window for the selected stream(s) + 선택한 스트림에 대한 분석 창 열기 + + + &Set List + 목록 설정(&S) + + + &Add to List + 목록에 추가(&A) + + + &Remove from List + 목록에서 삭제(&R) + + + Replace existing list in RTP Analysis Dialog with new one + RTP 분석 대화 상자의 기존 목록을 새로운 목록으로 교체 + + + Add new set to existing list in RTP Analysis Dialog + RTP 분석 대화 상자의 기존 목록에 새로운 집합 추가 + + + Remove selected streams from list in RTP Analysis Dialog + RTP 분석 대화 상자의 기존 목록에서 선택한 스트림 삭제 + + + Graph + 그래프 + + + <small><i>A hint.</i></small> + <small><i>힌트입니다.</i></small> + + + &Export + 내보내기(&E) + + + Open export menu + 내보내기 메뉴 열기 + + + CSV + CSV + + + Save tables as CSV. + CSV 형식으로 표를 저장합니다. + + + Current Tab Stream CSV + 현재 탭 스트림 CSV + + + Save the table on the current tab as CSV. + 현재 탭의 표를 CSV 형식으로 저장합니다. + + + All Tab Streams CSV + 모든 탭 스트림 CSV + + + Save the table from all tabs as CSV. + 모든 탭의 표를 CSV 형식으로 저장합니다. + + + Save Graph + 그래프 저장 + + + Save the graph image. + 그래프의 그림을 저장합니다. + + + Go to Packet + 패킷으로 이동 + + + Select the corresponding packet in the packet list. + 패킷 목록에서 해당하는 패킷을 선택합니다. + + + G + G + + + Next Problem Packet + 다음 문제 패킷 + + + Go to the next problem packet + 다음 문제가 발생한 패킷으로 이동 + + + N + N + + + Prepare &Filter + 필터 준비(&F) + + + Prepare a filter matching the selected stream(s). + 선택한 스트림과 일치하는 필터를 준비합니다. + + + &Current Tab + 현재 탭(&C) + + + Prepare a filter matching current tab. + 현재 탭과 일치하는 필터를 준비합니다. + + + &All Tabs + 모든 탭(&A) + + + Prepare a filter matching all tabs. + 모든 탭과 일치하는 필터를 준비합니다. + + + RTP Stream Analysis + RTP 스트림 분석 + + + Save Graph As… + 다른 이름으로 그래프 저장… + + + G: Go to packet, N: Next problem packet + G: 패킷으로 이동, N: 다음 문제가 발생한 패킷 + + + Portable Document Format (*.pdf) + PDF 형식 (*.pdf) + + + Portable Network Graphics (*.png) + PNG 형식 (*.png) + + + Windows Bitmap (*.bmp) + Windows 비트맵 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 형식 (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + 쉼표로 구분된 값 형식 (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %1은(는) %2의 PCM을 지원하지 않습니다. 선호하는 형식은 %3입니다 + + + + RtpPlayerDialog + + RTP Player + RTP 재생기 + + + Play + 재생 + + + Source Address + 발신지 주소 + + + Source Port + 발신지 포트 + + + Destination Address + 목적지 주소 + + + Destination Port + 목적지 포트 + + + SSRC + SSRC + + + Setup Frame + 설정 프레임 + + + Packets + 패킷 + + + Time Span (s) + 시간 간격(초) + + + Payloads + 페이로드 + + + <small><i>No audio</i></small> + <small><i>오디오 없음</i></small> + + + Start playback of all unmuted streams + 모든 음소거 해제된 스트림 재생 시작 + + + Pause/unpause playback + 재생 일시 정지/해제 + + + Stop playback + 재생 정지 + + + Enable/disable skipping of silence during playback + 재생 중 무음 구간 건너뛰기/건너뛰지 않기 + + + Min silence: + 최소 무음: + + + Minimum silence duration to skip in seconds + 건너뛸 초 단위의 최소 묵음 지속 시간 + + + Output Device: + 출력 장치: + + + Output Audio Rate: + 출력 오디오 레이트: + + + Jitter Buffer: + 지터 버퍼: + + + The simulated jitter buffer in milliseconds. + 밀리초 단위의 시뮬레이션된 지터 버퍼 크기입니다. + + + Playback Timing: + 재생 시간: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>지터 버퍼</strong>: 사용자가 듣는 RTP 스트림을 시뮬레이션하려면 지터 버퍼를 사용하십시오. +<br/> +<strong>RTP 타임스탬프</strong>: 패킷 도착 시간 대신 RTP 타임스탬프를 사용합니다. 사용자가 듣는 대로 RTP 스트림을 재구축하지는 않지만, RTP가 터널링되어 있고 원래 패킷 타이밍과 일치하지 않을 때 유용합니다. +<br/> +<strong>무중단 모드</strong>: RTP 타임스탬프를 무시합니다. 스트림이 완성된 것처럼 재생합니다. RTP 타임스탬프가 없는 경우에 유용합니다. + + + Jitter Buffer + 지터 버퍼 + + + RTP Timestamp + RTP 타임스탬프 + + + Uninterrupted Mode + 무중단 모드 + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>타임스탬프를 시간으로 표시(체크됨)하거나 캡처 시작 시점 기준 경과된 초 단위 시간(체크 해제됨)으로 표시합니다.</p></body></html> + + + Time of Day + 시간 + + + &Export + 내보내기(&E) + + + Export audio of all unmuted selected channels or export payload of one channel. + 모든 음소거 해제된 선택된 채널이나 한 채널의 페이로드를 내보냅니다. + + + From &cursor + 커서에서부터(&C) + + + Save audio data started at the cursor + 커서부터 시작하여 오디오 데이터 저장 + + + &Stream Synchronized Audio + 스트림에 동기화된 오디오(&S) + + + Save audio data synchronized to start of the earliest stream. + 가장 첫 스트림의 시작점에 동기화된 오디오 데이터를 저장합니다. + + + &File Synchronized Audio + 파일에 동기화된 오디오(&F) + + + Save audio data synchronized to start of the capture file. + 캡처 파일의 시작점에 동기화된 오디오 데이터를 저장합니다. + + + &Payload + 페이로드(&P) + + + Save RTP payload of selected stream. + 선택한 스트림의 RTP 페이로드를 저장합니다. + + + Reset Graph + 그래프 초기화 + + + Reset the graph to its initial state. + 그래프를 초기 상태로 초기화합니다. + + + Go To Setup Packet + 설정 패킷으로 이동 + + + Go to setup packet of stream currently under the cursor + 현재 커서 아래의 스트림을 설정하는 패킷으로 이동 + + + Mute + 음소거 + + + Mute selected streams + 선택한 스트림 음소거 + + + Unmute + 음소거 해제 + + + Unmute selected streams + 선택한 스트림 음소거 해제 + + + Invert muting of selected streams + 선택한 스트림의 음소거 상태 반전 + + + Route audio to left channel of selected streams + 선택한 스트림의 왼쪽 채널로 오디오 라우트 + + + Route audio to left and right channel of selected streams + 선택한 스트림의 왼쪽과 오른쪽 채널로 오디오 라우트 + + + Route audio to right channel of selected streams + 선택한 스트림의 오른쪽 채널로 오디오 라우트 + + + Remove Streams + 스트림 삭제 + + + Remove selected streams from the list + 목록에서 선택한 스트림 삭제 + + + All + 전체 + + + Select all + 모두 선택 + + + None + 없음 + + + Clear selection + 선택 해제 + + + Invert + 반전 + + + Invert selection + 선택 반전 + + + Play/Pause + 재생/일시 정지 + + + Start playing or pause playing + 재생 시작 또는 일시 정지 + + + Stop + 정지 + + + Stop playing + 재생 정지 + + + I&naudible streams + 비가청 음역 스트림(&I) + + + Select/Deselect inaudible streams + 비가청 음역 스트림 선택/해제 + + + Inaudible streams + 비가청 음역 스트림 + + + &Select + 선택(&S) + + + Select inaudible streams + 비가청 음역 스트림 선택 + + + &Deselect + 선택 해제(&D) + + + Deselect inaudible streams + 비가청 음역 스트림 선택 해제 + + + Prepare &Filter + 필터 준비(&F) + + + Prepare a filter matching the selected stream(s). + 선택한 스트림과 일치하는 필터를 준비합니다. + + + R&efresh streams + 스트림 새로 고침(&E) + + + Read captured packets from capture in progress to player + 진행 중인 캡처에서 캡처된 패킷을 재생기로 전송 + + + Zoom In + 확대 + + + SR (Hz) + SR(Hz) + + + Sample rate of codec + 코덱 샘플 레이트 + + + PR (Hz) + PR(Hz) + + + Play rate of decoded audio (depends e. g. on selected sound card) + 디코드한 오디오의 재생 샘플 레이트(선택한 사운드 카드 등에 의존) + + + Zoom Out + 축소 + + + Move Left 10 Pixels + 왼쪽으로 10픽셀 이동 + + + Move Right 10 Pixels + 오른쪽으로 10픽셀 이동 + + + Move Left 1 Pixels + 왼쪽으로 1픽셀 이동 + + + Move Right 1 Pixels + 오른쪽으로 1픽셀 이동 + + + Go To Packet Under Cursor + 커서 아래 패킷으로 이동 + + + Go to packet currently under the cursor + 현재 커서 아래 패킷으로 이동 + + + Play the stream + 스트림 재생 + + + To Left + 왼쪽으로 + + + Left + Right + 왼쪽 + 오른쪽 + + + To Right + 오른쪽으로 + + + Invert Muting + 음소거 반전 + + + No devices available + 사용 가능한 장치 없음 + + + Select + 선택 + + + Audio Routing + 오디오 라우팅 + + + &Play Streams + 스트림 재생(&P) + + + Open RTP player dialog + RTP 재생기 대화 상자 열기 + + + &Set playlist + 재생 목록 설정(&S) + + + Replace existing playlist in RTP Player with new one + RTP 재생기의 기존 재생 목록을 새로운 목록으로 교체 + + + &Add to playlist + 재생 목록에 추가(&A) + + + Add new set to existing playlist in RTP Player + RTP 재생기의 기존 재생 목록에 새 집합 추가 + + + &Remove from playlist + 재생 목록에서 삭제(&R) + + + Remove selected streams from playlist in RTP Player + RTP 재생기의 재생 목록에서 선택한 재생 목록 삭제 + + + No Audio + 오디오 없음 + + + Decoding streams... + 스트림 디코드 중... + + + Out of Sequence + 순서 외 + + + Jitter Drops + 지터 누락 + + + Wrong Timestamps + 잘못된 타임스탬프 + + + Inserted Silence + 무음 추가됨 + + + Double click on cell to change audio routing + 오디오 라우팅을 변경하려면 셀을 두 번 클릭 + + + %1 streams + 스트림 %1개 + + + , %1 selected + , %1개 선택됨 + + + , %1 not muted + , %1개 음소거되지 않음 + + + , start: %1. Double click on graph to set start of playback. + , 시작: %1. 그래프에서 두 번 클릭하면 재생 시작점을 설정합니다. + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + , 시작: %1, 커서: %2, "G" 키를 누르면 패킷 %3(으)로 이동합니다. 그래프에서 두 번 클릭하면 재생 시작점을 설정합니다. + + + Playback of stream %1 failed! + 스트림 %1 재생이 실패했습니다! + + + Automatic + 자동 + + + WAV (*.wav) + WAV (*.wav) + + + Sun Audio (*.au) + Sun 오디오 (*.au) + + + Save audio + 오디오 저장 + + + Raw (*.raw) + Raw (*.raw) + + + Save payload + 페이로드 저장 + + + Warning + 경고 + + + No stream selected or none of selected streams provide audio + 스트림을 선택하지 않았거나 선택한 모든 스트림에 오디오가 없음 + + + Error + 오류 + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + 모든 선택한 스트림의 재생 레이트는 같아야 합니다. 수동으로 출력 오디오 레이트를 지정해 보십시오. + + + No streams are suitable for save + 저장하기에 적합한 스트림 없음 + + + Save failed! + 저장이 실패했습니다! + + + Can't write header of AU file + AU 파일 헤더를 기록할 수 없음 + + + Can't write header of WAV file + WAV 파일 헤더를 기록할 수 없음 + + + Payload save works with just one audio stream. + 페이로드 저장 시에는 오디오 스트림 한 개만 사용할 수 있습니다. + + + Double click to change audio routing + 오디오 라우팅을 변경하려면 두 번 클릭하십시오 + + + Preparing to play... + 재생 준비 중... + + + Unknown + 알 수 없음 + + + + RtpStreamDialog + + Dialog + 대화 상자 + + + Source Address + 발신지 주소 + + + Source Port + 발신지 포트 + + + Destination Address + 목적지 주소 + + + Destination Port + 목적지 포트 + + + SSRC + SSRC + + + Start Time + 시작 시간 + + + Duration + 지속 시간 + + + Payload + 페이로드 + + + Packets + 패킷 + + + Lost + 누락 + + + Max Delta (ms) + 최대 간격(ms) + + + Max Jitter + 최대 지터 + + + Mean Jitter + 평균 지터 + + + Status + 상태 + + + <small><i>A hint.</i></small> + <small><i>힌트입니다.</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>현재 표시 필터와 일치하는 대화만 표시</p></body></html> + + + Limit to display filter + 표시 필터로 제한 + + + Time of Day + 시간 표기 + + + Find &Reverse + 역방향 찾기(&R) + + + Prepare &Filter + 필터 준비(&F) + + + &Export + 내보내기(&E) + + + &Analyze + 분석(&A) + + + Open the analysis window for the selected stream(s) and add it to it + 선택한 스트림에 대한 분석 창을 열고 추가합니다 + + + Find the reverse stream matching the selected forward stream. + 선택한 정방향 스트림에 부합하는 역방향 스트림을 찾습니다. + + + Min Delta (ms) + 최소 델타(ms) + + + Mean Delta (ms) + 평균 델타(ms) + + + Min Jitter + 최소 지터 + + + All forward/reverse stream actions + 모든 정방향/역방향 스트림 동작 + + + R + R + + + Find All &Pairs + 모든 쌍 찾기(&P) + + + Select all streams which are paired in forward/reverse relation + 정방향/역방향 쌍으로 연결된 모든 스트림 선택 + + + Shift+R + Shift+R + + + Find Only &Singles + 단독만 찾기(&S) + + + Find all streams which don't have paired reverse stream + 쌍이 되는 역방향 스트림이 없는 모든 스트림 찾기 + + + Ctrl+R + Ctrl+R + + + Mark Packets + 패킷 마크 + + + Mark the packets of the selected stream(s). + 선택한 스트림의 패킷을 마크합니다. + + + M + M + + + All + 모두 + + + Select all + 모두 선택 + + + None + 없음 + + + Clear selection + 선택 해제 + + + Invert + 반전 + + + Invert selection + 선택 반전 + + + Go To Setup + 설정으로 이동 + + + Go to the setup packet for this stream. + 이 스트림에 대한 설정 패킷으로 이동합니다. + + + G + G + + + Prepare a filter matching the selected stream(s). + 선택한 스트림과 일치하는 필터를 준비합니다. + + + P + P + + + Export the stream payload as rtpdump + rtpdump로 스트림 페이로드를 내보내기 + + + E + E + + + A + A + + + Cop&y + 복사(&Y) + + + Open copy menu + 복사 메뉴 열기 + + + Copy as CSV + CSV로 복사 + + + Copy stream list as CSV. + CSV로 스트림 목록을 복사합니다. + + + Copy as YAML + YAML로 복사 + + + Copy stream list as YAML. + YAML로 스트림 목록을 복사합니다. + + + RTP Streams + RTP 스트림 + + + Select + 선택 + + + as CSV + CSV로 + + + as YAML + YAML로 + + + %1 streams + 스트림 %1개 + + + , %1 selected, %2 total packets + , %1개 선택됨, 총 패킷 %2개 + + + Save RTPDump As… + 다른 이름으로 RTP 덤프 저장… + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark - SCTP 연결 + + + ID + ID + + + Port 1 + 포트 1 + + + Port 2 + 포트 2 + + + Number of Packets + 패킷 수 + + + Number of DATA Chunks + DATA 청크 개수 + + + Number of Bytes + 바이트 + + + Filter Selected Association + 선택한 연결 필터 + + + Analyze + 분석 + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark - 연결 분석 + + + TabWidget + 탭위젯 + + + Statistics + 통계 + + + Chunk Statistics + 청크 통계 + + + Filter Association + 연결 필터 + + + Number of Data Chunks from EP2 to EP1: + EP2에서 EP1로 전송된 데이터 청크 개수: + + + Checksum Type: + 체크섬 유형: + + + Number of Data Chunks from EP1 to EP2: + EP1에서 EP2로 전송된 데이터 청크 개수: + + + Number of Data Bytes from EP1 to EP2: + EP1에서 EP2로 전송된 데이터 바이트: + + + Number of Data Bytes from EP2 to EP1: + EP2에서 EP1로 전송된 데이터 바이트: + + + Endpoint 1 + 종단점 1 + + + Graph TSN + TSN 그래프 + + + Graph Bytes + 바이트 그래프 + + + Requested Number of Inbound Streams: + 요청된 인바운드 스트림 개수: + + + Port: + 포트: + + + Sent Verification Tag: + 발신 검증 태그: + + + Minimum Number of Inbound Streams: + 최소 인바운드 스트림 개수: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + <small><i>완전한 분석을 보려면 SCTP 설정에서 연결 색인을 활성화하십시오</i></small> + + + Complete List of IP addresses from INIT Chunk: + INIT 청크의 전체 IP 주소 목록: + + + Minimum Number of Outbound Streams: + 최소 아웃바운드 스트림 개수: + + + Graph Arwnd + Arwnd 그래프 + + + Endpoint 2 + 종단점 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + INIT_ACK 청크의 전체 IP 주소 목록: + + + Provided Number of Outbound Streams: + 제공된 아웃바운드 스트림 개수: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + SCTP 연결 분석: %1 포트1 %2 포트2 %3 + + + No Association found for this packet. + 이 패킷에 대한 연결을 찾을 수 없습니다. + + + Warning + 경고 + + + Could not find SCTP Association with id: %1 + ID: %1인 SCTP 연결을 찾을 수 없음 + + + Complete list of IP addresses from INIT Chunk: + INIT 청크의 전체 IP 주소 목록: + + + Complete list of IP addresses from INIT_ACK Chunk: + INIT_ACK 청크의 전체 IP 주소 목록: + + + List of Used IP Addresses + 사용된 IP 주소 목록 + + + Used Number of Inbound Streams: + 사용된 인바운드 스트림 개수: + + + Used Number of Outbound Streams: + 사용된 아웃바운드 스트림 개수: + + + + SCTPChunkStatisticsDialog + + Dialog + 대화 상자 + + + Association + 연결 + + + Endpoint 1 + 종단점 1 + + + Endpoint 2 + 종단점 2 + + + Save Chunk Type Order + 청크 유형 순서 저장 + + + Hide Chunk Type + 청크 유형 숨기기 + + + Remove the chunk type from the table + 표에서 청크 유형 삭제 + + + Chunk Type Preferences + 청크 유형 설정 + + + Go to the chunk type preferences dialog to show or hide other chunk types + 다른 청크 유형을 표시하거나 숨기려면 청크 유형 설정으로 이동하십시오 + + + Show All Registered Chunk Types + 등록된 모든 청크 유형 표시 + + + Show all chunk types with defined names + 정의된 이름이 있는 모든 청크 유형 표시 + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + SCTP 청크 통계: %1 포트1 %2 포트2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + SCTP 그래프 + + + Reset to full size + 전체 크기로 초기화 + + + Save Graph + 그래프 저장 + + + goToPacket + 패킷으로 이동 + + + Go to Packet + 패킷으로 이동 + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + 시간별 SCTP 데이터와 고급 수신 윈도: %1 포트1 %2 포트2 %3 + + + No Data Chunks sent + 발신된 데이터 청크 없음 + + + Arwnd + Arwnd + + + time [secs] + 시간 [초] + + + Advertised Receiver Window [Bytes] + 알려진 수신지 윈도 [바이트] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>그래프 %1: a_rwnd=%2 시간=%3초 </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + SCTP 그래프 + + + Reset to full size + 전체 크기로 초기화 + + + Save Graph + 그래프 저장 + + + goToPacket + 패킷으로 이동 + + + Go to Packet + 패킷으로 이동 + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + 시간별 SCTP 데이터와 고급 수신 윈도: %1 포트1 %2 포트2 %3 + + + No Data Chunks sent + 발신된 데이터 청크 없음 + + + Bytes + 바이트 + + + time [secs] + 시간 [초] + + + Received Bytes + 수신 바이트 + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>그래프 %1: 수신 바이트=%2 시간=%3초 </i></small> + + + + SCTPGraphDialog + + SCTP Graph + SCTP 그래프 + + + Relative TSNs + 상대적 TSN + + + Only SACKs + SACK만 + + + Only TSNs + TSN만 + + + Show both + 모두 표시 + + + Reset to full size + 전체 크기로 초기화 + + + Save Graph + 그래프 저장 + + + goToPacket + 패킷으로 이동 + + + Go to Packet + 패킷으로 이동 + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + 시간별 SCTP TSN 개수와 SACK 개수: %1 포트1 %2 포트2 %3 + + + No Data Chunks sent + 발신된 데이터 청크 없음 + + + CumTSNAck + CumTSNAck + + + Gap Ack + Gap Ack + + + NR Gap Ack + NR Gap Ack + + + Duplicate Ack + 중복 ACK + + + TSN + TSN + + + time [secs] + 시간 [초] + + + TSNs + TSN + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 시간: %3초 </i></small> + + + Portable Document Format (*.pdf) + PDF 형식 (*.pdf) + + + Portable Network Graphics (*.png) + PNG 형식 (*.png) + + + Windows Bitmap (*.bmp) + Windows 비트맵 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 형식 (*.jpeg *.jpg) + + + Save Graph As… + 다른 이름으로 그래프 저장… + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <small><i>명령을 선택하고 필요한 경우 필터를 입력한 다음 적용을 누르십시오.</i></small> + + + Command: + 설명: + + + SCSI Service Response Times + SCSI 서비스 응답 시간 + + + + SearchFrame + + Frame + 프레임 + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>패킷 목록의 정보 열(요약 패널), 디코드된 패킷 표시 레이블(트리 보기 패널), ASCII 변환된 패킷 데이터(16진수 표시 패널)에서 검색합니다.</p></body></html> + + + Packet list + 패킷 목록 + + + Packet details + 패킷 자세한 정보 + + + Packet bytes + 패킷 바이트 + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>좁은 문자(UTF-8과 ASCII) 또는 넓은 문자(UTF-16)를 포함하는 문자열을 검색합니다.</p></body></html> + + + Narrow & Wide + 좁음 및 넓음 + + + Narrow (UTF-8 / ASCII) + 좁음(UTF-8/ASCII) + + + Wide (UTF-16) + 넓음(UTF-16) + + + Case sensitive + 대소문자 구분 + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>표시 필터 문법(예: ip.addr==10.1.1.1), 16진수 문자열(예: ffffda5), 일반 문자열(예: My String) 또는 정규 표현식(예: colou?r)을 사용하여 데이터를 검색합니다.</p></body></html> + + + Display filter + 표시 필터 + + + Hex value + 16진수 값 + + + String + 문자열 + + + Regular Expression + 정규 표현식 + + + Find + 찾기 + + + Cancel + 취소 + + + No valid search type selected. Please report this to the development team. + 올바른 검색 유형이 선택되지 않았습니다. 이 문제를 개발 팀에 보고해 주십시오. + + + Invalid filter. + 잘못된 필터입니다. + + + That filter doesn't test anything. + 필터에서 아무것도 시험하지 않습니다. + + + That's not a valid hex string. + 올바른 16진수 문자열이 아닙니다. + + + You didn't specify any text for which to search. + 검색할 문자열을 지정하지 않았습니다. + + + No valid character set selected. Please report this to the development team. + 올바른 문자 집합이 선택되지 않았습니다. 이 문제를 개발 팀에 보고해 주십시오. + + + No valid search area selected. Please report this to the development team. + 올바른 검색 영역이 선택되지 않았습니다. 이 문제를 개발 팀에 보고해 주십시오. + + + Searching for %1… + %1 검색 중… + + + No packet contained those bytes. + 해당 바이트를 포함하는 패킷이 없습니다. + + + No packet contained that string in its Info column. + 정보 열에 해당 문자열을 포함하는 패킷이 없습니다. + + + No packet contained that string in its dissected display. + 분석된 표시에 해당 문자열을 포함한 패킷이 없습니다. + + + No packet contained that string in its converted data. + 변환된 데이터에 해당 문자열을 포함한 패킷이 없습니다. + + + No packet matched that filter. + 필터와 일치하는 패킷이 없습니다. + + + + SequenceDialog + + Call Flow + 호 진행 + + + Time + 시간 + + + Comment + 주석 + + + No data + 데이터 없음 + + + %Ln node(s) + + 노드 %Ln개 + + + + %Ln item(s) + + 항목 %Ln개 + + + + Portable Document Format (*.pdf) + PDF 형식 (*.pdf) + + + Portable Network Graphics (*.png) + PNG 형식 (*.png) + + + Windows Bitmap (*.bmp) + Windows 비트맵 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 형식 (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII 형식 (*.txt) + + + Save Graph As… + 다른 이름으로 그래프 저장… + + + Flow + 플로 + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>가치있고 놀라운 시간 절약형 키보드 단축키</h3> +<table><tbody> + +<tr><th>+</th><td>확대</td></th> +<tr><th>-</th><td>축소</td></th> +<tr><th>0</th><td>초기 상태로 그래프 다시 설정</td></th> + +<tr><th>→</th><td>오른쪽으로 10픽셀 이동</td></th> +<tr><th>←</th><td>왼쪽으로 10픽셀 이동</td></th> +<tr><th>↑</th><td>위쪽으로 10픽셀 이동</td></th> +<tr><th>↓</th><td>아래쪽으로 10픽셀 이동</td></th> +<tr><th><i>Shift+</i>→</th><td>오른쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>←</th><td>왼쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>↑</th><td>위쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>↓</th><td>아래쪽으로 1픽셀 이동</td></th> + +<tr><th>g</th><td>커서 위치의 패킷으로 이동</td></th> +<tr><th>n</th><td>다음 패킷으로 이동</td></th> +<tr><th>p</th><td>이전 패킷으로 이동</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>힌트입니다</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>현재 디스플레이 필터와 일치하는 플로만 표시</p></body></html> + + + Limit to display filter + 표시 필터로 제한 + + + Flow type: + 플로 유형: + + + Addresses: + 주소: + + + Any + 임의 + + + Network + 네트워크 + + + Reset Diagram + 다이어그램 초기화 + + + Reset &Diagram + 다이어그램 초기화(&D) + + + Reset the diagram to its initial state. + 다이어그램을 초기 상태로 다시 설정합니다. + + + 0 + 0 + + + &Reset Diagram + 다이어그램 초기화(&R) + + + Reset the diagram to its initial state + 초기 상태로 다이어그램 초기화 + + + &Export + 내보내기(&E) + + + Export diagram + 다이어그램 내보내기 + + + Zoom In + 확대 + + + + + + + + + Zoom Out + 축소 + + + - + - + + + Move Up 10 Pixels + 위쪽으로 10픽셀 이동 + + + Up + 위쪽 + + + Move Left 10 Pixels + 왼쪽으로 10픽셀 이동 + + + Left + 왼쪽 + + + Move Right 10 Pixels + 오른쪽으로 10픽셀 이동 + + + Right + 오른쪽 + + + Move Down 10 Pixels + 아래쪽으로 10픽셀 이동 + + + Down + 아래쪽 + + + Move Up 1 Pixel + 위쪽으로 1픽셀 이동 + + + Shift+Up + Shift+위쪽 + + + Move Left 1 Pixel + 왼쪽으로 1픽셀 이동 + + + Shift+Left + Shift+왼쪽 + + + Move Right 1 Pixel + 오른쪽으로 1픽셀 이동 + + + Shift+Right + Shift+오른쪽 + + + Move Down 1 Pixel + 아래쪽으로 1픽셀 이동 + + + Shift+Down + Shift+아래쪽 + + + Go To Packet Under Cursor + 커서 아래 패킷으로 이동 + + + Go to packet currently under the cursor + 현재 커서 아래 패킷으로 이동 + + + G + G + + + All Flows + 모든 플로 + + + Show flows for all packets + 모든 패킷의 플로 표시 + + + 1 + 1 + + + TCP Flows + TCP 플로 + + + Show only TCP flow information + TCP 플로 정보만 표시 + + + Go To Next Packet + 다음 패킷으로 이동 + + + Go to the next packet + 다음 패킷으로 이동 + + + N + N + + + Go To Previous Packet + 이전 패킷으로 이동 + + + Go to the previous packet + 이전 패킷으로 이동 + + + P + P + + + Select RTP Stream + RTP 스트림 선택 + + + Select RTP stream in RTP Streams dialog + RTP 스트림 대화 상자에서 RTP 스트림 선택 + + + S + S + + + Deselect RTP Stream + RTP 스트림 선택 해제 + + + Deselect RTP stream in RTP Streams dialog + RTP 스트림 대화 상자에서 RTP 스트림 선택 해제 + + + D + D + + + + ShortcutListModel + + Shortcut + 단축키 + + + Name + 이름 + + + Description + 설명 + + + + ShowPacketBytesDialog + + Show Packet Bytes + 패킷 바이트 표시 + + + Hint. + 필터 힌트. + + + Decode as + 다른 형식으로 디코드 + + + Show as + 다른 형식으로 표시 + + + Start + 시작 + + + End + 종료 + + + Find: + 찾기: + + + Find &Next + 다음 찾기(&N) + + + Frame %1, %2, %Ln byte(s). + + 프레임 %1, %2, %Ln바이트. + + + + None + 없음 + + + Base64 + Base64 + + + Compressed + 압축됨 + + + Hex Digits + 16진 자릿수 + + + Percent-Encoding + 퍼센트 인코딩 + + + Quoted-Printable + Quoted-Printable + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII와 제어 문자 + + + C Array + C 언어 배열 + + + EBCDIC + EBCDIC + + + Hex Dump + 16진수 덤프 + + + HTML + HTML + + + Image + 그림 + + + Raw + Raw(원본) + + + Rust Array + 러스트 배열 + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + 출력 + + + Copy + 복사 + + + Save as… + 다른 이름으로 저장… + + + Save Selected Packet Bytes As… + 선택한 패킷 바이트를 다른 이름으로 저장… + + + Displaying %Ln byte(s). + + %Ln바이트를 표시하고 있습니다. + + + + JSON + JSON + + + Regex Find: + 정규식 찾기: + + + + ShowPacketBytesTextEdit + + Show Selected + 선택한 항목 표시 + + + Show All + 모두 표시 + + + + SplashOverlay + + Initializing dissectors + 분해기 초기화 중 + + + Initializing tap listeners + tap 리스너 초기화 중 + + + Initializing external capture plugins + 외부 캡처 플러그인 초기화 중 + + + Registering dissectors + 분해기 등록 중 + + + Registering plugins + Registering dissector + 플러그인 등록 중 + + + Handing off dissectors + 분해기 제거 중 + + + Handing off plugins + 플러그인 제거 중 + + + Loading Lua plugins + Lua 플러그인 불러오는 중 + + + Removing Lua plugins + Lua 플러그인 삭제 중 + + + Loading module preferences + 모듈 설정 불러오는 중 + + + Finding local interfaces + 로컬 인터페이스 찾는 중 + + + Applying changed preferences + 변경된 설정 적용 중 + + + (Unknown action) + (알 수 없는 동작) + + + + StatsTreeDialog + + Configuration not found + 설정을 찾을 수 없음 + + + Unable to find configuration for %1. + %1에 대한 설정을 찾을 수 없습니다. + + + + StripHeadersDialog + + Dialog + 대화 상자 + + + Display filter: + 표시 필터: + + + + SupportedProtocolsDialog + + Dialog + 대화 상자 + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>필드 이름의 목록에서 검색합니다.</p></body></html> + + + Search: + 검색: + + + <small><i>Gathering protocol information…</i></small> + <small><i>프로토콜 정보 수집 중…</i></small> + + + Supported Protocols + 지원하는 프로토콜 + + + %1 protocols, %2 fields. + 프로토콜 %1개, 필드 %2개. + + + + SupportedProtocolsModel + + Name + 이름 + + + Filter + 필터 + + + Type + 유형 + + + Description + 설명 + + + + SyntaxLineEdit + + Invalid filter: %1 + 잘못된 필터: %1 + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + "%1"은(는) "%2"(으)로 대체될 예정입니다. 도움말의 6.4.8절을 참조하십시오. + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + 대화 상자 + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>가치있고 놀라운 시간 절약형 키보드 단축키</h3> +<table><tbody> + +<tr><th>+</th><td>확대</td></th> +<tr><th>-</th><td>축소</td></th> +<tr><th>x</th><td>X축 확대</td></th> +<tr><th>X</th><td>X축 축소</td></th> +<tr><th>y</th><td>Y축 확대</td></th> +<tr><th>Y</th><td>Y축 축소</td></th> +<tr><th>0</th><td>초기 상태로 그래프 다시 설정</td></th> + +<tr><th>→</th><td>오른쪽으로 10픽셀 이동</td></th> +<tr><th>←</th><td>왼쪽으로 10픽셀 이동</td></th> +<tr><th>↑</th><td>위쪽으로 10픽셀 이동</td></th> +<tr><th>↓</th><td>아래쪽으로 10픽셀 이동</td></th> +<tr><th><i>Shift+</i>→</th><td>오른쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>←</th><td>왼쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>↑</th><td>위쪽으로 1픽셀 이동</td></th> +<tr><th><i>Shift+</i>↓</th><td>아래쪽으로 1픽셀 이동</td></th> + +<tr><th><i>Pg Up</i></th><td>다음 스트림</td></th> +<tr><th><i>Pg Dn</i></th><td>이전 스트림</td></th> +<tr><th>d</th><td>방향 전환(TCP 종단점 변경)</td></th> +<tr><th>g</th><td>커서 아래 패킷으로 이동</td></th> + +<tr><th>z</th><td>마우스 드래그/크기 조정 전환</td></th> +<tr><th>s</th><td>절대/상대 시퀀스 번호 전환</td></th> +<tr><th>t</th><td>캡처/세션 시간 원본 전환</td></th> +<tr><th>Space</th><td>십자선 전환</td></th> + +<tr><th>1</th><td>왕복 시간 그래프</td></th> +<tr><th>2</th><td>처리량 그래프</td></th> +<tr><th>3</th><td>스티븐스 스타일 시간/시퀀스 그래프</td></th> +<tr><th>4</th><td>tcptrace 스타일 시간/시퀀스 그래프</td></th> +<tr><th>5</th><td>윈도 스케일링 그래프</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>마우스를 올려 두면 단축키 표시</i></small> + + + Type + 유형 + + + MA Window (s) + MA 윈도(초) + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + 그래프를 클릭하여 데이터 패킷과 SACK 세그먼트 선택 허용 + + + Select SACKs + select SACKs + SACK 선택 + + + Stream + 스트림 + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>연결 방향을 전환합니다(역방향 플로 표시).</p></body></html> + + + Switch Direction + 방향 전환 + + + Mouse + 마우스 + + + Drag using the mouse button. + 마우스 단추를 사용하여 드래그합니다. + + + drags + 드래그 + + + Select using the mouse button. + 마우스 단추를 사용하여 선택합니다. + + + zooms + 확대 + + + Display Round Trip Time vs Sequence Number + 왕복 시간 대 시퀀스 번호 표시 + + + RTT By Sequence Number + 시퀀스 번호별 RTT + + + Display graph of Segment Length vs Time + 세그먼트 길이 대 시간의 그래프 표시 + + + Segment Length + 세그먼트 길이 + + + Display graph of Mean Transmitted Bytes vs Time + 평균 전송된 바이트 대 시간의 그래프 표시 + + + Display graph of Mean ACKed Bytes vs Time + 평균 ACK된 바이트 대 시간의 그래프 표시 + + + Goodput + 굿풋(응용 프로그램 수준의 처리량) + + + Display graph of Receive Window Size vs Time + 수신 윈도 크기 대 시간의 그래프 표시 + + + Rcv Win + 수신 윈도 + + + Display graph of Outstanding Bytes vs Time + 눈에 띄는 바이트 대 시간의 그래프 표시 + + + Bytes Out + 바이트 송신 + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>그래프를 초기 상태로 초기화합니다.</p></body></html> + + + Reset + 초기화 + + + Reset Graph + 그래프 초기화 + + + Reset the graph to its initial state. + 그래프를 초기 상태로 초기화합니다. + + + 0 + 0 + + + Zoom In + 확대 + + + + + + + + + Zoom Out + 축소 + + + - + - + + + Move Up 10 Pixels + 위쪽으로 10픽셀 이동 + + + Up + 위쪽 + + + Move Left 10 Pixels + 왼쪽으로 10픽셀 이동 + + + Left + 왼쪽 + + + Move Right 10 Pixels + 오른쪽으로 10픽셀 이동 + + + Right + 오른쪽 + + + Move Down 10 Pixels + 아래쪽으로 10픽셀 이동 + + + Down + 아래쪽 + + + Move Up 1 Pixel + 위쪽으로 1픽셀 이동 + + + Shift+Up + Shift+위쪽 + + + Move Left 1 Pixel + 왼쪽으로 1픽셀 이동 + + + Shift+Left + Shift+왼쪽 + + + Move Right 1 Pixel + 오른쪽으로 1픽셀 이동 + + + Shift+Right + Shift+오른쪽 + + + Move Down 1 Pixel + 아래쪽으로 1픽셀 이동 + + + Shift+Down + Shift+아래쪽 + + + Next Stream + 다음 스트림 + + + Go to the next stream in the capture + 캡처의 다음 스트림으로 이동합니다 + + + PgUp + PgUp + + + Previous Stream + 이전 스트림 + + + Go to the previous stream in the capture + 캡처의 이전 스트림으로 이동합니다 + + + PgDown + PgDown + + + Switch direction (swap TCP endpoints) + 방향 전환(TCP 종단점 바꾸기) + + + D + D + + + Go To Packet Under Cursor + 커서 아래 패킷으로 이동 + + + Go to packet currently under the cursor + 현재 커서 아래 패킷으로 이동 + + + G + G + + + Drag / Zoom + 드래그/크기 조정 + + + Toggle mouse drag / zoom behavior + 마우스 드래그/크기 조정 동작 전환 + + + Z + Z + + + Relative / Absolute Sequence Numbers + 상대적/절대적 시퀀스 번호 + + + Toggle relative / absolute sequence numbers + 상대적/절대적 시퀀스 번호를 전환합니다 + + + S + S + + + Capture / Session Time Origin + 캡처/세션 시간 기점 + + + Toggle capture / session time origin + 캡처/세션 시간 기점 전환 + + + T + T + + + Crosshairs + 십자선 + + + Toggle crosshairs + 십자선 표시 전환 + + + Space + Space + + + Round Trip Time + 왕복 시간 + + + Switch to the Round Trip Time graph + 왕복 시간 그래프로 전환합니다 + + + 1 + 1 + + + Throughput + 처리량 + + + Switch to the Throughput graph + 처리량 그래프로 전환합니다 + + + 2 + 2 + + + Time / Sequence (Stevens) + 시간/시퀀스(스티븐스) + + + Switch to the Stevens-style Time / Sequence graph + 스티븐스 스타일 시간/시퀀스 그래프로 전환합니다 + + + 3 + 3 + + + Window Scaling + 윈도 크기 조정 + + + Switch to the Window Scaling graph + 윈도 크기 조정 그래프로 전환합니다 + + + 5 + 5 + + + Time / Sequence (tcptrace) + 시간/시퀀스(tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + tcptrace 스타일 시간/시퀀스 그래프로 전환합니다 + + + 4 + 4 + + + Zoom In X Axis + X축 확대 + + + X + X + + + Zoom Out X Axis + X축 축소 + + + Shift+X + Shift+X + + + Zoom In Y Axis + Y축 확대 + + + Y + Y + + + Zoom Out Y Axis + Y축 축소 + + + Shift+Y + Shift+Y + + + Save As… + 다른 이름으로 저장… + + + No Capture Data + 캡처 데이터가 없습니다 + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 패킷 %2개, %3 %4 패킷 %5개, %6 + + + Sequence Numbers (Stevens) + 시퀀스 번호(스티븐스) + + + Sequence Numbers (tcptrace) + 시퀀스 번호(tcptrace) + + + (MA) + (MA) + + + (%1 Segment MA) + (%1 세그먼트 MA) + + + [not enough data] + [충분한 데이터 없음] + + + for %1:%2 %3 %4:%5 + 대상: %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3초 길이 %4 seq %5 ack %6 win %7) + + + Click to select packet + 클릭하여 패킷 선택 + + + Packet + 패킷 + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 커서를 놓으면 크기 조정, x = %1 - %2, y = %3 - %4 + + + Unable to select range. + 범위를 선택할 수 없습니다. + + + Click to select a portion of the graph. + 클릭하여 그래프의 부분을 선택하십시오. + + + Portable Document Format (*.pdf) + PDF 형식 (*.pdf) + + + Portable Network Graphics (*.png) + PNG 형식 (*.png) + + + Windows Bitmap (*.bmp) + Windows 비트맵 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 형식 (*.jpeg *.jpg) + + + Save Graph As… + 다른 이름으로 그래프 저장… + + + + TLSKeylogDialog + + Dialog + 대화 상자 + + + Browse… + 찾아보기... + + + Command line + 명령행 + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + SSLKEYLOG 환경 변수를 키 로그 파일 이름 설정에서 지정한 것과 동일하게 설정한 후 프로그램을 실행하십시오. 이렇게 하면 Wireshark에서 TLS 복호화를 활성화합니다. 초기 TLS 핸드셰이크를 캡처할 수 있도록 프로그램을 실행하기 전에 키 로그 파일을 설정하고 캡처를 시작하십시오. + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + <span style=" font-size:small;">Firefox와 Chrome 브라우저에서는 이 설정이 작동합니다. 사용하고자 하는 브라우저가 현재 실행 중이라면 아래에서 실행하기 전에 먼저 종료하십시오. 명령행 옵션도 지원합니다.</span> + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + TLS (Pre)-Master-Secret 로그 파일 경로(tls.keylog_file) + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + <span style=" font-size:small;">이 파일에 TLS 세션 비밀 키가 기록됩니다. 이 필드를 변경하면 아래의 저장 단추를 클릭하여 TLS 프로토콜 설정을 업데이트하십시오.</span> + + + Launch application with SSLKEYLOGFILE + SSLKEYLOGFILE을 지정하여 프로그램 실행 + + + Launch + 실행 + + + Save + 저장 + + + TLS Keylog file + TLS 키로그 파일 + + + Program to start with SSLKEYLOGFILE + SSLKEYLOGFILE을 지정하여 시작할 프로그램 + + + + TapParameterDialog + + Dialog + 대화 상자 + + + Item + 항목 + + + <small><i>A hint.</i></small> + <small><i>힌트입니다.</i></small> + + + Display filter: + 표시 필터: + + + Regenerate statistics using this display filter + 이 표시 필터를 사용하여 통계를 다시 생성 + + + Apply + 적용 + + + Copy + 복사 + + + Copy a text representation of the tree to the clipboard + 트리의 텍스트 표현을 클립 보드에 복사 + + + Save as… + Save as... + 다른 이름으로 저장… + + + Save the displayed data in various formats + 다양한 형식으로 표시 데이터를 저장합니다 + + + Collapse All + 모두 접기 + + + Expand All + 모두 펴기 + + + Save Statistics As… + 다른 이름으로 통계 저장… + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + 일반 텍스트 파일 (*.txt);;쉼표로 구분된 값 (*.csv);;XML 문서 (*.xml);;YAML 문서 (*.yaml) + + + Plain text file (*.txt) + 일반 텍스트 파일 (*.txt) + + + Error saving file %1 + 파일 저장 오류 %1 + + + + TimeShiftDialog + + Shift all packets by + 다음만큼 모든 패킷 조정 + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + 다음 패킷에 대한 설정: + + + to + 값: + + + …then set packet + ...then set packet + … 그리고 다음 패킷: + + + and extrapolate the time for all other packets + 그리고 나머지 모든 패킷의 시간을 외삽 + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + 모든 조정 되돌리기 + + + Time Shift + 타임시프트 + + + Frame numbers must be between 1 and %1. + 프레임 번호는 1에서 %1 사이여야 합니다. + + + Invalid frame number. + 프레임 번호가 잘못되었습니다. + + + Time shifting is not available while capturing packets. + 패킷 캡처 중에는 타임시프트를 사용할 수 없습니다. + + + + TrafficTab + + Map file error + 맵 파일 오류 + + + Could not open base file %1 for reading: %2 + 기반 파일 %1에서 읽기 위해 열 수 없음: %2 + + + No endpoints available to map + 맵에 사용 가능한 종단점이 없음 + + + Unable to create temporary file + 임시 파일을 만들 수 없음 + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>플레인 값이 아닌 해석된 주소와 포트 이름을 표시합니다. 관련 이름 해석 설정이 활성화되어 있어야 합니다.</p></body></html> + + + Name resolution + 이름 해석 + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>현재 표시 필터와 일치하는 대화만 표시</p></body></html> + + + Limit to display filter + 표시 필터로 제한 + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + <html><head/><body><p>필터 값과 일치하는 유형만 표시합니다</p></body></html> + + + Filter list for specific type + 특정 유형에 대한 필터 목록 + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>시작 시간 열에 절대 시간을 표시합니다.</p></body></html> + + + GroupBox + 그룹 상자 + + + Absolute start time + 절대 시작 시간 + + + Copy + 복사 + + + Unknown + 알 수 없음 + + + + TrafficTree + + Resize all columns to content + 내용에 맞게 모든 열 크기 조정 + + + Filter on stream id + 스트림 ID에 필터 + + + Copy %1 table + %1 표 복사 + + + as CSV + CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + 이 페이지의 모든 값을 CSV(쉼표로 구분된 값) 형식으로 클립보드에 복사합니다. + + + as YAML + YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + 이 페이지의 모든 값을 YAML 데이터 직렬화 형식으로 클립보드에 복사합니다. + + + as JSON + JSON + + + Copy all values of this page to the clipboard in the JSON data serialization format. + 이 페이지의 모든 값을 JSON 데이터 직렬화 형식으로 클립보드에 복사합니다. + + + Save data as raw + RAW 형식으로 데이터 저장 + + + Disable data formatting for export/clipboard and save as raw data + 내보내기/클립보드에 데이터 형식 지정 없이 RAW 데이터로 저장 + + + + TrafficTreeHeaderView + + Less than + 미만 + + + Greater than + 초과 + + + Equal + 같음 + + + Columns to display + 표시할 열 + + + Filter %1 by + 다음으로 %1 필터 + + + Enter filter value + 필터 값 입력 + + + + TrafficTypesModel + + Protocol + 프로토콜 + + + + UatDialog + + Create a new entry. + 새 항목을 만듭니다. + + + Remove this entry. + Remove this profile. + 이 항목을 삭제합니다. + + + Copy this entry. + Copy this profile. + 이 항목을 복사합니다. + + + Move entry up. + 항목을 위로 이동합니다. + + + Move entry down. + 항목을 아래로 이동합니다. + + + Clear all entries. + 모든 항목을 삭제합니다. + + + Unknown User Accessible Table + 알 수 없는 사용자가 접근 가능한 표 + + + Open + 열기 + + + + UatFrame + + Frame + 프레임 + + + Create a new entry. + 새 항목을 만듭니다. + + + Remove this entry. + 이 항목을 삭제합니다. + + + Copy this entry. + 이 항목을 복사합니다. + + + Move entry up. + 항목을 위로 이동합니다. + + + Move entry down. + 항목을 아래로 이동합니다. + + + Clear all entries. + 모든 항목을 삭제합니다. + + + Copy entries from another profile. + 다른 프로필에서 항목을 복사합니다. + + + Copy from + 복사 위치 + + + Unknown User Accessible Table + 알 수 없는 사용자가 접근할 수 있는 표 + + + Open + 열기 + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>현재 표시 필터와 일치하는 대화만 표시</p></body></html> + + + Limit to display filter + 표시 필터로 제한 + + + Time of Day + 시간 표기 + + + Flow &Sequence + 플로 시퀀스(&S) + + + Show flow sequence for selected call(s). + 선택한 호에 대한 플로 시퀀스를 표시합니다. + + + Prepare &Filter + 필터 준비(&F) + + + Prepare a filter matching the selected calls(s). + 선택한 호와 일치하는 필터를 준비합니다. + + + Cop&y + 복사(&Y) + + + Open copy menu + 복사 메뉴 열기 + + + All + 전체 + + + Select all + 모두 선택 + + + None + 없음 + + + Invert + 반전 + + + Invert selection + 선택 반전 + + + Select related RTP streams + 연관된 RTP 스트림 선택 + + + Select RTP streams related to selected calls in RTP Streams dialog + RTP 스트림 대화 상자에서 선택한 호와 연관된 RTP 스트림 선택 + + + S + S + + + Deselect related RTP Streams + 연관된 RTP 스트림 선택 해제 + + + D + D + + + Clear selection + 선택 해제 + + + Display time as time of day + 하루 기준으로 시간 표시 + + + Copy as CSV + CSV로 복사 + + + Copy stream list as CSV. + 스트림 목록을 CSV로 복사합니다. + + + Copy as YAML + YAML로 복사 + + + Copy stream list as YAML. + 스트림 목록을 YAML로 복사합니다. + + + SIP Flows + SIP 플로 + + + VoIP Calls + VoIP 호 + + + as CSV + CSV로 + + + as YAML + YAML로 + + + Select + 선택 + + + + VoipCallsInfoModel + + On + 켜기 + + + Off + 끄기 + + + Tunneling: %1 Fast Start: %2 + 터널링: %1 빠른 시작: %2 + + + Start Time + 시작 시간 + + + Stop Time + 종료 시간 + + + Initial Speaker + 초기 스피커 + + + From + 시점 + + + To + 종점 + + + Protocol + 프로토콜 + + + Duration + 지속 시간 + + + Packets + 패킷 + + + State + 상태 + + + Comments + 주석 + + + + WelcomePage + + Form + + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Wireshark에 오신 것을 환영합니다</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>파일 시스템 상의 파일 열기</p></body></html> + + + <h2>Open</h2> + <h2>열기</h2> + + + Recent capture files + 최근 캡처 파일 + + + Capture files that have been opened previously + 이전에 열었던 캡처 파일 + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>여러분의 네트워크에서 패킷을 바로 캡처하십시오.</p></body></html> + + + <h2>Capture</h2> + <h2>캡처</h2> + + + …using this filter: + …다음 필터 사용: + + + Interface list + 인터페이스 목록 + + + List of available capture interfaces + 사용 가능한 캡처 인터페이스 목록 + + + <h2>Learn</h2> + <h2>배우기</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">사용자 설명서</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">위키</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">질문과 답변</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">메일링 리스트</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">기부하기</a></th> + +</tr></table> +</body></html> + + + Show in Finder + Finder에 표시 + + + Show in Folder + 폴더 위치 표시 + + + Welcome to %1 + %1에 오신 것을 환영합니다 + + + All interfaces shown + 모든 인터페이스가 표시됨 + + + %n interface(s) shown, %1 hidden + + 인터페이스 %n개 표시됨, %1개 숨겨짐 + + + + You are sniffing the glue that holds the Internet together using Wireshark + Wireshark를 사용하여 인터넷을 고정하는 접착제의 냄새를 맡고(스니핑) 있습니다 + + + You are running Wireshark + 실행 중인 Wireshark 버전: + + + You receive automatic updates. + 자동 업데이트를 받고 있습니다. + + + You have disabled automatic updates. + 자동 업데이트를 사용하지 않도록 설정했습니다. + + + not found + 찾을 수 없음 + + + Copy file path + 파일 경로 복사 + + + Remove from list + 목록에서 삭제 + + + + WirelessFrame + + Frame + 프레임 + + + Interface + 인터페이스 + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>802.11 채널을 설정합니다.</p></body></html> + + + Channel + 채널 + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>캡처할 때 모든 프레임, 프레임 확인 순서(FCS, Frame Check Sequence)가 올바른 프레임, 또는 FCS가 잘못된 프레임을 표시합니다.</p></body></html> + + + FCS Filter + FCS 필터 + + + All Frames + 모든 프레임 + + + Valid Frames + 유효한 프레임 + + + Invalid Frames + 유효하지 않은 프레임 + + + Wireless controls are not supported in this version of Wireshark. + 이 버전의 Wireshark에서는 무선 제어가 지원되지 않습니다. + + + External Helper + 외부 도우미 + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>복호화 키를 포함한 IEEE 802.11 설정을 표시합니다.</p></body></html> + + + 802.11 Preferences + 802.11 설정 + + + AirPcap Control Panel + AirPcap 제어판 + + + Open the AirPcap Control Panel + AirPcap 제어판 열기 + + + Unable to set channel or offset. + 채널 또는 오프셋을 설정할 수 없습니다. + + + Unable to set FCS validation behavior. + FCS 확인 행동을 설정할 수 없습니다. + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + 패킷 번호 %1에 TSF 타임스탬프가 포함되어 있지 않아서 타임라인이 표시되지 않습니다. + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + 패킷 번호 %u의 TSF가 역방향으로 크게 건너뛰기 때문에 타임라인을 표시하지 않습니다. TSF 기준점이 잘못 설정되었습니까? + + + + WiresharkDialog + + Failed to attach to tap "%1" + "%1" 탭 부착 실패 + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + 패킷으로 이동 + + + Cancel + 취소 + + + File Set + 파일 설정 + + + Export Packet Dissections + 패킷 분해 결과 내보내기 + + + Export Objects + 객체 내보내기 + + + &Zoom + 크기 조정(&Z) + + + &Time Display Format + 시간 표시 형식(&T) + + + Copy + 복사 + + + Manual pages + 매뉴얼 페이지 + + + Apply as Filter + 필터로 적용 + + + Prepare as Filter + 필터로 준비 + + + SCTP + SCTP + + + TCP Stream Graphs + TCP 스트림 그래프 + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + 파일(&F) + + + &Capture + 캡처(&C) + + + &Help + 도움말(&H) + + + &Go + 이동(&G) + + + &View + 보기(&V) + + + &Analyze + 분석(&A) + + + Follow + 따라가기 + + + &Statistics + 통계(&S) + + + 29West + 29West + + + Topics + 주제 + + + Queues + 대기열 + + + UIM + UIM + + + Telephon&y + 전화(&Y) + + + RTSP + RTSP + + + &Edit + 편집(&E) + + + Packet Comments + 패킷 주석 + + + Main Toolbar + 주 도구 모음 + + + Display Filter Toolbar + 표시 필터 도구 모음 + + + Open a capture file + 캡처 파일 열기 + + + Quit Wireshark + Wireshark 끝내기 + + + &Start + 시작(&S) + + + Start capturing packets + 패킷 캡처 시작 + + + S&top + 정지(&S) + + + Stop capturing packets + 패킷 캡처 정지 + + + No files found + 찾은 파일 없음 + + + &Contents + 내용(&C) + + + Wireshark Filter + Wireshark 필터 + + + TShark + TShark + + + Rawshark + Rawshark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + Text2pacp + + + Website + 웹사이트 + + + Downloads + 다운로드 + + + Wiki + 위키 + + + Sample Captures + 예제 캡처 + + + &About Wireshark + Wireshark 정보(&A) + + + Ask (Q&&A) + Ask(질문과 답변)(&A) + + + Next Packet + 다음 패킷 + + + Go to the next packet + 다음 패킷으로 이동 + + + Previous Packet + 이전 패킷 + + + Go to the previous packet + 이전 패킷으로 이동 + + + First Packet + 첫 패킷 + + + Go to the first packet + 첫 패킷으로 이동 + + + Last Packet + 마지막 패킷 + + + Go to the last packet + 마지막 패킷으로 이동 + + + E&xpand Subtrees + 하위 트리 펴기(&X) + + + Expand the current packet detail + 현재 패킷의 자세한 정보 확장 + + + &Expand All + 모두 펴기(&E) + + + Expand packet details + 패킷 자세한 정보 펴기 + + + Collapse &All + 모두 접기(&A) + + + Collapse all packet details + 모든 패킷의 자세한 정보 접기 + + + Go to specified packet + 지정한 패킷으로 이동 + + + Merge one or more files + 하나 이상의 파일 합치기 + + + Import a file + 파일 가져오기 + + + &Save + 저장(&S) + + + Save as a different file + 다른 파일로 저장 + + + Export specified packets + 지정한 패킷 내보내기 + + + Export TLS Session Keys… + TLS 세션 키 내보내기… + + + List Files + 파일 나열하기 + + + Next File + 다음 파일 + + + Previous File + 이전 파일 + + + &Reload + 다시 읽기(&R) + + + Options + 옵션 + + + Capture options + 캡처 옵션 + + + Capture filters + 캡처 필터 + + + Refresh Interfaces + 인터페이스 갱신 + + + Refresh interfaces + 인터페이스 갱신 + + + &Restart + 다시 시작(&R) + + + Restart current capture + 현재 캡처 다시 시작 + + + As &CSV… + CSV로(&C)… + + + As "C" &Arrays… + "C" 배열로(&A)… + + + As P&SML XML… + PSML XML로(&S)… + + + As P&DML XML… + PDML XML로(&D)… + + + As &JSON… + JSON으로(&J)… + + + Description + 설명 + + + Field Name + 필드 이름 + + + Value + + + + As Filter + 필터로 + + + Close this capture file + 이 캡처 파일 닫기 + + + Packet: + 패킷: + + + Interface Toolbars + 인터페이스 도구 모음 + + + Colorize Conversation + 대화 색상화 + + + Internals + 내부 + + + Additional Toolbars + 추가 도구 모음 + + + Conversation Filter + 대화 필터 + + + Reliable Server Pooling (RSerPool) + 신뢰할 수 있는 서버 풀링(RSerPool) + + + SOME/IP + SOME/IP + + + &DTN + DTN(&D) + + + Osmux + Dsmux + + + &Tools + Tools + 도구(&T) + + + Wireless Toolbar + 무선 도구 모음 + + + Help contents + 도움말 내용 + + + FAQs + 자주 묻는 질문 + + + Next Packet in Conversation + 대화 내의 다음 패킷 + + + Go to the next packet in this conversation + 이 대화에서 다음 패킷으로 이동 + + + Previous Packet in Conversation + 대화 내의 이전 패킷 + + + Go to the previous packet in this conversation + 이 대화 내의 이전 패킷으로 이동 + + + Next Packet In History + 과거 기록 내의 다음 패킷 + + + Go to the next packet in your selection history + 선택 기록의 다음 패킷으로 이동 + + + Previous Packet In History + 과거 기록 내의 이전 패킷 + + + Go to the previous packet in your selection history + 선택 기록의 이전 패킷으로 이동 + + + Collapse Subtrees + 하위 트리 접기 + + + Collapse the current packet detail + 현재 패킷의 자세한 정보 접기 + + + Go to Packet… + 패킷으로 이동… + + + &Merge… + 병합(&M)… + + + &Import from Hex Dump… + 16진수 덤프에서 가져오기(&I)… + + + Save this capture file + 이 캡처 파일 저장 + + + Save &As… + 다른 이름으로 저장(&A)… + + + Export Specified Packets… + 지정한 패킷 내보내기… + + + Export Packet &Bytes… + 패킷 바이트 내보내기(&B)… + + + &Print… + 인쇄(&P)… + + + Reload this file + 이 파일 다시 읽기 + + + Reload as File Format/Capture + 파일 형식/캡처로서 다시 불러오기 + + + Copy this item's description + 이 항목의 설명 복사 + + + Copy this item's field name + 이 항목의 필드 이름 복사 + + + Copy this item's value + 이 항목의 값 복사 + + + Copy this item as a display filter + 이 항목을 표시 필터로 복사 + + + Apply as Column + 열로서 적용 + + + Create a packet list column from the selected field. + 선택한 필드에서 패킷 목록 열을 생성합니다. + + + Find a packet + 패킷 찾기 + + + Find the next packet + 다음 패킷 찾기 + + + Find the previous packet + 이전 패킷 찾기 + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + 패킷 마크/해제(&M) + + + Mark All Displayed + 표시된 모든 패킷 마크 + + + Mark all displayed packets + 표시된 모든 패킷 마크 + + + Unmark all displayed packets + 표시된 모든 패킷 마크 해제 + + + Next Mark + 다음 마크 + + + Go to the next marked packet + 다음 마크한 패킷으로 이동 + + + Previous Mark + 이전 마크 + + + Go to the previous marked packet + 이전 마크한 패킷으로 이동 + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + 패킷 무시/해제(&I) + + + Ignore All Displayed + 표시된 모든 패킷 무시 + + + Ignore all displayed packets + 표시된 모든 패킷 무시 + + + Set/Unset Time Reference + 시간 참조 설정/해제 + + + Set or unset a time reference for this packet + 이 패킷을 시간 참조로 설정/해제 + + + Unset All Time References + 모든 시간 참조 해제 + + + Remove all time references + 모든 시간 참조 삭제 + + + Next Time Reference + 다음 시간 참조 + + + Go to the next time reference + 다음 시간 참조로 이동 + + + Previous Time Reference + 이전 시간 참조 + + + Go to the previous time reference + 이전 시간 참조로 이동 + + + Shift or change packet timestamps + 패킷 타임스탬프를 이동하거나 변경 + + + Delete All Packet Comments + 모든 패킷 주석 삭제 + + + Remove all packet comments in the capture file + 캡처 파일 내의 모든 패킷 주석 삭제 + + + &Configuration Profiles… + 설정 프로필(&C)… + + + Configuration profiles + 설정 프로필 + + + Manage your configuration profiles + 내 설정 프로필 관리 + + + Manage Wireshark's preferences + Wireshark의 설정 관리 + + + Capture File Properties + 캡처 파일 속성 + + + Capture file properties + 캡처 파일 속성 + + + &Protocol Hierarchy + 프로토콜 계층 구조(&P) + + + Show a summary of protocols present in the capture file. + 캡처 파일에 존재하는 프로토콜 요약을 표시합니다. + + + Capinfos + Capinfos + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + 시간 시퀀스(스티븐스) + + + TCP time sequence graph (Stevens) + TCP 시간 시퀀스 그래프(스티븐스) + + + Throughput + 처리량 + + + Round Trip Time + 왕복 시간 + + + TCP round trip time + TCP 왕복 시간 + + + Window Scaling + 윈도 크기 조정 + + + TCP window scaling + TCP 윈도 크기 조정 + + + HTTP/2 Stream + HTTP/2 스트림 + + + SIP Call + SIP 호 + + + Time Sequence (tcptrace) + 시간 시퀀스(tcptrace) + + + TCP time sequence graph (tcptrace) + TCP 시간 시퀀스 그래프(tcptrace) + + + Analyse this Association + 이 연결 분석 + + + Show All Associations + 모든 연결 보기 + + + Flow Graph + 플로 그래프 + + + Flow sequence diagram + 플로 시퀀스 다이어그램 + + + ANCP + ANCP + + + ANCP statistics + ANCP 통계 + + + Packets sorted by Instance ID + 인스턴스 ID로 정렬한 패킷 + + + BACapp statistics sorted by instance ID + 인스턴스 ID로 정렬한 BACapp 통계 + + + Packets sorted by IP + IP로 정렬한 패킷 + + + BACapp statistics sorted by IP + IP로 정렬한 BACapp 통계 + + + Packets sorted by object type + 객체 유형으로 정렬한 패킷 + + + BACapp statistics sorted by object type + 객체 유형으로 정렬한 BACapp 통계 + + + Packets sorted by service + 서비스로 정렬한 패킷 + + + BACapp statistics sorted by service + 서비스로 정렬한 BACapp 통계 + + + Collectd + Collectd + + + Collectd statistics + Collectd 통계 + + + DNS + DNS + + + DNS statistics + DNS 통계 + + + HART-IP + HART-IP + + + HART-IP statistics + HART-IP 통계 + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + hpfeeds 통계 + + + HTTP2 + HTTP2 + + + HTTP2 statistics + HTTP2 통계 + + + Packet Counter + 패킷 카운터 + + + HTTP packet counter + HTTP 패킷 카운터 + + + Requests + 요청 + + + HTTP requests + HTTP 요청 + + + Load Distribution + 로드 분산 + + + HTTP load distribution + HTTP 로드 분산 + + + Packet Lengths + 패킷 길이 + + + Packet length statistics + 패킷 길이 통계 + + + Sametime + Sametime + + + Sametime statistics + Sametime 통계 + + + SOME/IP Messages + SOME/IP 메시지 + + + SOME/IP Message statistics + SOME/IP 메시지 통계 + + + SOME/IP-SD Entries + SOME/IP-SD 엔트리 + + + SOME/IP-SD Entries statistics + SOME/IP-SD 엔트리 통계 + + + &LTP + LTP(&L) + + + LTP segment and block statistics + LTP 세그먼트와 블록 통계 + + + &ISUP Messages + ISUP 메시지(&I) + + + ISUP message statistics + ISUP 메시지 통계 + + + Osmux packet counts + Osmux 패킷 카운트 + + + RTSP packet counts + RTSP 패킷 카운트 + + + SM&PP Operations + SMPP 동작(&P) + + + SMPP operation statistics + SMPP 동작 통계 + + + &UCP Messages + UCP 메시지(&U) + + + UCP message statistics + UCP 메시지 통계 + + + F1AP + F1AP + + + F1AP Messages + F1AP 메시지 + + + NGAP + NGAP + + + NGAP Messages + NGAP 메시지 + + + Change the way packets are dissected + 패킷 분해 방식 변경 + + + Reload Lua Plugins + Lua 플러그인 다시 로드 + + + Reload Lua plugins + Lua 플러그인 다시 로드 + + + Advertisements by Topic + 주제별 광고 + + + Advertisements by Source + 소스별 광고 + + + Advertisements by Transport + 전송별 광고 + + + Queries by Topic + 주제별 질의 + + + Queries by Receiver + 수신자별 질의 + + + Wildcard Queries by Pattern + 패턴별 와일드카드 질의 + + + Wildcard Queries by Receiver + 수신자별 와일드카드 질의 + + + Advertisements by Queue + 대기열별 광고 + + + Queries by Queue + 대기열별 질의 + + + Streams + 스트림 + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + 이 연결을 필터 + + + Strip Headers… + 헤더 잘라내기… + + + Strip headers and export higher level encapsulations to file + 헤더를 잘라내고 더 높은 레벨의 캡슐화를 파일로 내보내기 + + + &I/O Graphs + I/O 그래프(&I) + + + &Conversations + 대화(&C) + + + &Endpoints + 종단점(&E) + + + Shrink the main window text + 주 창 텍스트 줄이기 + + + Return the main window text to its normal size + 주 창 텍스트를 원래 크기로 되돌림 + + + Reset Layout + 레이아웃 초기화 + + + Reset appearance layout to default size + 외관 레이아웃을 기본 크기로 초기화 + + + Seconds Since First Captured Packet + 최초로 캡처된 패킷부터 지난 초 단위 시간 + + + Show packet times as the seconds since the first captured packet. + 최초로 캡처된 패킷으로부터 초 단위의 지난 시간으로 패킷 시간을 표시합니다. + + + Tenths of a millisecond + 밀리초의 1/10 + + + Hundredths of a millisecond + 밀리초의 1/100 + + + Tenths of a microsecond + 마이크로초의 1/10 + + + Hundredths of a microsecond + 마이크로초의 1/100 + + + Packet &Diagram + 패킷 다이어그램(&D) + + + Show or hide the packet diagram + 패킷 다이어그램 표시/숨기기 + + + Show each conversation hash table + 각 대화의 해시 테이블 표시 + + + Show each dissector table and its entries + 각 분해자 테이블과 항목 표시 + + + Show the currently supported protocols and display filter fields + 현재 지원하는 프로토콜과 표시 필터 필드 표시 + + + MAC Statistics + MAC 통계 + + + LTE MAC statistics + LTE MAC 통계 + + + RLC Statistics + RLC 통계 + + + LTE RLC statistics + LTE RLC 통계 + + + LTE RLC graph + LTE RLC 그래프 + + + MTP3 Summary + MTP3 요약 + + + MTP3 summary statistics + MTP3 요약 통계 + + + Bluetooth Devices + 블루투스 장치 + + + Bluetooth HCI Summary + 블루투스 HCI 요약 + + + Display Filter &Expression… + 표시 필터 표현식(&E)… + + + Display Filter Expression… + 표시 필터 표현식… + + + REGISTER_STAT_GROUP_RSERPOOL + REGISTER_STAT_GROUP_RSERPOOL + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + "REGISTER_STAT_GROUP_RSERPOOL"의 시작 + + + No GSM statistics registered + 등록된 GSM 통계 없음 + + + No LTE statistics registered + 등록된 LTE 통계 없음 + + + No MTP3 statistics registered + 등록된 MTP3 통계 없음 + + + IAX2 Stream Analysis + IAX2 스트림 분석 + + + Show Packet Bytes… + 패킷 바이트 표시… + + + Go to &Linked Packet + 연결된 패킷으로 이동(&L) + + + UDP Multicast Streams + UDP 멀티캐스트 스트림 + + + Show UTP multicast stream statistics. + UTP 멀티캐스트 스트림 통계를 표시합니다. + + + WLAN Traffic + WLAN 트래픽 + + + Show IEEE 802.11 wireless LAN statistics. + IEEE 802.11 무선 LAN 통계를 표시합니다. + + + Add a display filter button. + 표시 필터 단추를 추가합니다. + + + Firewall ACL Rules + 방화벽 ACL 규칙 + + + Create firewall ACL rules + 방화벽 ACL 규칙 생성 + + + &Full Screen + 전체 화면(&F) + + + Credentials + 자격 증명 + + + MAC Address Blocks + MAC 주소 블록 + + + TLS Keylog Launcher + TLS 키로그 실행기 + + + Release Notes + 릴리스 노트 + + + &Options… + 옵션(&O)… + + + &Wireless + 무선(&W) + + + Capture &Filters… + 캡처 필터(&F)… + + + As Plain &Text… + 일반 텍스트로(&T)… + + + As Plain &Text + 일반 텍스트로(&T) + + + As &CSV + CSV로(&C) + + + As &YAML + YAML로(&Y) + + + All Visible Items + 모든 표시된 항목 + + + All Visible Selected Tree Items + 모든 표시된 선택한 트리 항목 + + + Display Filter &Macros… + 표시 필터 매크로(&M)… + + + &Find Packet… + 패킷 찾기(&F)… + + + Find Ne&xt + 다음 찾기(&X) + + + Find Pre&vious + 이전 찾기(&V) + + + Mark or unmark each selected packet + 각 선택된 패킷 마크/해제 + + + Ignore or unignore each selected packet + 각 선택된 패킷 무시/해제 + + + U&nignore All Displayed + 모든 표시된 항목 무시 해제(&N) + + + Unignore all displayed packets + 표시된 모든 패킷을 무시 해제 + + + Time Shift… + 타임시프트… + + + Inject TLS Secrets + TLS 비밀 정보 주입 + + + Embed used TLS secrets in the capture file + 캡처 파일에 사용된 TLS 비밀 정보 주입 + + + Discard All Secrets + 모든 비밀 정보 버리기 + + + Discard all decryption secrets in the capture file + 캡처 파일의 모든 복호화 비밀 정보 버리기 + + + &Preferences… + 설정(&P)… + + + TCP throughput + TCP 처리량 + + + Request Sequences + 요청 시퀀스 + + + HTTP Request Sequences + HTTP 요청 시퀀스 + + + Decode &As… + 다른 형식으로 디코드(&A)… + + + Export PDUs to File… + PDU를 파일로 내보내기… + + + Create graphs based on display filter fields + 표시 필터 필드를 기반으로 그래프 생성 + + + &Main Toolbar + 주 도구 모음(&M) + + + Show or hide the main toolbar + 주 도구 모음 표시 또는 숨기기 + + + &Filter Toolbar + 필터 도구 모음(&F) + + + Show or hide the display filter toolbar + 표시 필터 도구 모음 표시 또는 숨기기 + + + Conversations at different protocol levels + 다른 프로토콜 수준의 대화 + + + Endpoints at different protocol levels + 다른 프로토콜 수준의 종단점 + + + Colorize Packet List + 패킷 목록 색상화 + + + Draw packets using your coloring rules + 내 색상화 규칙을 사용하여 패킷 표시 + + + &Zoom In + 확대(&Z) + + + Enlarge the main window text + 주 창 텍스트 확대 + + + Zoom Out + 축소 + + + Normal Size + 보통 크기 + + + Resize Columns + 열 크기 조정 + + + Resize packet list columns to fit contents + 내용에 맞게 패킷 목록 열 크기 조정 + + + Date and Time of Day (1970-01-01 01:02:03.123456) + 날짜와 시간(1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + 패킷 시간을 날짜와 시간으로 표현합니다. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + 년, 연중일, 시간(1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + 연도, 연도 단위 일수, 시간으로 패킷 시간을 표현합니다. + + + Time of Day (01:02:03.123456) + 시간(01:02:03.123456) + + + Seconds Since 1970-01-01 + 1970-01-01로부터 초 단위 시간 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + UNIX/POSIX 기원(1970-01-01)으로부터의 초 단위 시간으로 패킷 시간을 표현합니다. + + + Seconds Since Previous Captured Packet + 이전에 캡처된 패킷부터 초 단위 시간 + + + Show packet times as the seconds since the previous captured packet. + 이전에 캡처된 패킷부터 초 단위의 지난 시간으로 패킷 시간을 표시합니다. + + + Seconds Since Previous Displayed Packet + 이전에 표시된 패킷부터 초 단위 시간 + + + Show packet times as the seconds since the previous displayed packet. + 이전에 표시된 패킷부터 초 단위의 지난 시간으로 패킷 시간을 표시합니다. + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + UTC 날짜와 시간(1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + UTC 날짜와 시간으로 패킷 시간을 표현합니다. + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + UTC 년, 연중일, 시간(1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + UTC 년, 연중일, 시간으로 패킷 시간을 표시합니다. + + + UTC Time of Day (01:02:03.123456) + UTC 시간(01:02:03.123456) + + + Show packet times as the UTC time of day. + UTC 시간으로 패킷 시간을 표시합니다. + + + Automatic (from capture file) + 자동(캡처 파일에서) + + + Use the time precision indicated in the capture file. + 캡처 파일에서 지정한 시간 정밀도를 사용합니다. + + + Seconds + + + + Tenths of a second + 1/10초 + + + Hundredths of a second + 1/100초 + + + Milliseconds + 밀리초 + + + Microseconds + 마이크로초 + + + Nanoseconds + 나노초 + + + Display Seconds With Hours and Minutes + 시간과 분으로 초 표시 + + + Display seconds with hours and minutes + 시간과 분으로 초 표시 + + + Resolve &Physical Addresses + 물리 주소 해석(&P) + + + Show names for known MAC addresses. Lookups use a local database. + 잘 알려진 MAC 주소에 대한 이름을 표시합니다. 검색에 로컬 데이터베이스를 사용합니다. + + + Resolve &Network Addresses + 네트워크 주소 해석 + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + 알려진 IPv4, IPv6, IPX 주소를 이름으로 표시합니다. 검색 시 네트워크 트래픽을 발생할 수 있습니다. + + + Resolve &Transport Addresses + 전송 주소 해석(&T) + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + 알려진 TCP, UDP, SCTP 서비스에 대한 이름을 표시합니다. 검색 시 몇몇 시스템 상에 트래픽을 발생할 수 있습니다. + + + Wire&less Toolbar + 무선 도구 모음(&L) + + + Show or hide the wireless toolbar + 무선 도구 모음 표시 또는 숨기기 + + + &Status Bar + 상태 표시줄(&S) + + + Show or hide the status bar + 상태 표시줄 표시/숨기기 + + + Packet &List + 패킷 목록(&L) + + + Show or hide the packet list + 패킷 목록 표시/숨기기 + + + Packet &Details + 패킷 자세한 정보(&D) + + + Show or hide the packet details + 패킷 자세한 정보 표시/숨기기 + + + Packet &Bytes + 패킷 바이트(&B) + + + Show or hide the packet bytes + 패킷 바이트 표시/숨기기 + + + &Conversation Hash Tables + 대화 해시 테이블(&C) + + + &Dissector Tables + 분해기 표(&D) + + + &Supported Protocols + 지원하는 프로토콜(&S) + + + MAP Summary + MAP 요약 + + + GSM MAP summary statistics + GSM MAP 요약 통계 + + + RLC &Graph + RLC 그래프(&G) + + + &Coloring Rules… + 색상화 규칙(&C)… + + + Show Linked Packet in New Window + 새 창에 연결된 패킷 표시 + + + New Coloring Rule… + New Conversation Rule… + 새 색상화 규칙… + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + 선택한 스트림의 RTP 스트림을 분석합니다. 역방향 스트림을 추가하려면 Ctrl 키를 누르십시오. + + + RTP Player + RTP 재생기 + + + Play selected stream. Press CTRL key for playing reverse stream too. + 선택한 스트림을 재생합니다. 역방향 스트림을 재생하려면 Ctrl 키를 누르십시오. + + + IA&X2 Stream Analysis + IAX2 스트림 분석(&X) + + + Enabled Protocols… + Enable Protocols… + 활성화된 프로토콜… + + + Wiki Protocol Page + 위키 프로토콜 페이지 + + + Open the Wireshark wiki page for this protocol. + 이 프로토콜에 대한 Wireshark 위키 페이지 열기 + + + Filter Field Reference + 필터 필드 참조 + + + Open the display filter reference page for this filter field. + 이 필터 필드에 대한 표시 필터 참조 페이지를 엽니다. + + + Go to the packet referenced by the selected field. + 선택한 필드에서 참조하는 패킷으로 이동합니다. + + + &VoIP Calls + VoIP 호(&V) + + + Open &Recent + 최근에 연 파일(&R) + + + Name Resol&ution + 이름 해석(&U) + + + Service &Response Time + 서비스 응답 시간(&R) + + + &RTP + RTP(&R) + + + S&CTP + SCTP(&C) + + + &ANSI + ANSI(&A) + + + &GSM + GSM(&G) + + + &LTE + LTE(&L) + + + &MTP3 + MTP3(&M) + + + &Open + 열기(&O) + + + &Quit + 끝내기(&Q) + + + &Close + 닫기(&C) + + + Display &Filters… + 표시 필터(&F)… + + + &Unmark All Displayed + 표시된 모든 항목 마크 해제(&U) + + + All VoIP Calls + 모든 VoIP 호 + + + SIP &Flows + SIP 플로(&F) + + + SIP Flows + SIP 플로 + + + RTP Streams + RTP 스트림 + + + Edit the packet list coloring rules. + 패킷 목록 색상화 규칙을 편집합니다. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + ATT 서버 속성 + + + Show Packet in New &Window + 새 창에 패킷 표시(&W) + + + Show this packet in a separate window. + 분리된 창으로 패킷을 표시합니다. + + + Show the linked packet in a separate window. + 분리된 창에 연결된 패킷을 표시합니다. + + + Auto Scroll in Li&ve Capture + 라이브 캡처 자동 스크롤(&V) + + + Automatically scroll to the last packet during a live capture. + 라이브 캡처를 진행하는 동안 최신 패킷으로 자동으로 스크롤합니다. + + + Expert Information + 전문가 정보 + + + Show expert notifications + 전문가 알림 표시 + + + Add an expression to the display filter. + 표시 필터에 표현식을 추가합니다. + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + "REGISTER_STAT_GROUP_UNSORTED"의 시작 + + + No ANSI statistics registered + No tools registered + 등록된 ANSI 통계 없음 + + + Resolved Addresses + 해석된 주소 + + + Show each table of resolved addresses as copyable text. + 해석된 주소의 각 표를 복사 가능한 문자열로 표시합니다. + + + Color &1 + 색상 &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + 현재 대화를 고유 색상으로 표시합니다. + + + Color &2 + 색상 &2 + + + Color &3 + 색상 &3 + + + Color &4 + 색상 &4 + + + Color &5 + 색상 &5 + + + Color &6 + 색상 &6 + + + Color &7 + 색상 &7 + + + Color &8 + 색상 &8 + + + Color &9 + 색상 &9 + + + Color 1&0 + 색상 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + 이 필드를 기반으로 새로운 색상화 규칙을 만듭니다. + + + Reset Colorization + 색상화 초기화 + + + Reset colorized conversations. + 색상 처리된 대화를 초기화합니다. + + + RTP Stream Analysis + RTP 스트림 분석 + + + Edit Resolved Name + 해석된 이름 편집 + + + Manually edit a name resolution entry. + 수동으로 이름 해석 항목을 편집합니다. + + + Enable and disable specific protocols + 특정 프로토콜 활성화/비활성화 + + + before quitting + 종료 전 + + + Save packets before merging? + 병합 이전에 패킷을 저장하시겠습니까? + + + A temporary capture file can't be merged. + 임시 캡처 파일은 병합할 수 없습니다. + + + Save changes in "%1" before merging? + 병합하기 전에 "%1"에서 수정된 내용을 저장하시겠습니까? + + + Changes must be saved before the files can be merged. + 파일을 병합하기 전에 변경된 내용을 저장해야 합니다. + + + Invalid Display Filter + 잘못된 표시 필터 + + + Invalid Read Filter + 잘못된 읽기 필터 + + + The filter expression %1 isn't a valid read filter. (%2). + 필터 표현식 %1은(는) 잘못된 읽기 필터입니다. (%2). + + + before importing a capture + before importing a new capture + 새로운 캡처를 가져오기 전에 + + + Unable to export to "%1". + "%1"(으)로 내보낼 수 없습니다. + + + You cannot export packets to the current capture file. + 현재 캡처 파일로 패킷을 내보낼 수 없습니다. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + 변경 사항을 저장하시겠습니까%1? + + + Your captured packets will be lost if you don't save them. + 저장하지 않으면 캡처한 패킷이 손실됩니다. + + + Do you want to save the changes you've made to the capture file "%1"%2? + 캡처 파일 "%1"에 대한 변경 사항을 저장하시겠습니까%2? + + + Your changes will be lost if you don't save them. + 저장하지 않으면 변경 사항이 손실됩니다. + + + Check for Updates… + 업데이트 확인… + + + Unable to drop files during capture. + 캡처 중에는 파일을 버릴 수 없습니다. + + + Unknown file type returned by merge dialog. + 병합 대화 상자에서 알 수 없는 파일 형식을 반환했습니다. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + 이 문제를 Wireshark 이슈로 보고해 주십시오. https://gitlab.com/wireshark/wireshark/-/issues + + + Unknown file type returned by export dialog. + 내보내기 대화 상자에서 알 수 없는 파일 형식을 반환했습니다. + + + Do you want to stop the capture and save the captured packets%1? + 캡처를 정지하고 캡처된 패킷을 저장하시겠습니까%1? + + + Do you want to save the captured packets%1? + 캡처한 패킷을 저장하시겠습니까%1? + + + Save before Continue + 계속하기 전에 저장 + + + Stop and Save + 정지하고 저장 + + + Stop and Quit &without Saving + Stop and Quit without Saving + 정지하고 저장하지 않고 끝내기(&W) + + + Quit &without Saving + Quit without Saving + 저장하지 않고 끝내기(&W) + + + There is no "rtp.ssrc" field in this version of Wireshark. + 이 버전의 Wireshark에는 "rtp.ssrc" 필드가 없습니다. + + + Please select an RTPv2 packet with an SSRC value + SSRC 값이 있는 RTPv2 패킷을 선택하십시오 + + + SSRC value not found. + SSRC 값을 찾을 수 없습니다. + + + Show or hide the toolbar + 도구 모음 표시 또는 숨기기 + + + Continue &without Saving + Continue without Saving + 저장하지 않고 계속(&W) + + + Stop and Continue &without Saving + Stop and Continue without Saving + 정지하고 저장하지 않고 계속(&W) + + + The Wireshark Network Analyzer + Wireshark 네트워크 분석기 + + + Capturing from %1 + %1에서 캡처 중 + + + before opening another file + 다른 파일을 열기 전 + + + Merging files. + 파일을 병합 중입니다. + + + %1: %2 + %1: %2 + + + Clear Menu + 메뉴 비우기 + + + before closing the file + 파일을 닫기 전 + + + Export Selected Packet Bytes + 선택한 패킷 바이트 내보내기 + + + No Keys + 키 없음 + + + Export SSL Session Keys (%Ln key(s)) + Export SSL Session Keys (%1 key%2 + + SSL 세션 키 내보내기(키 %Ln개) + + + + Raw data (*.bin *.dat *.raw);;All Files ( + Raw 데이터 (*.bin *.dat *.raw);;모든 파일 ( + + + Couldn't copy text. Try another item. + 텍스트를 복사할 수 없습니다. 다른 항목으로 시도해 보십시오. + + + Are you sure you want to remove all packet comments? + 모든 패킷 주석을 삭제하시겠습니까? + + + Unable to build conversation filter. + 대화 필터를 구축할 수 없습니다. + + + before reloading the file + 파일을 다시 불러오기 전에 + + + Error compiling filter for this conversation. + 이 대화에 대한 필터를 컴파일하는 중 오류가 발생했습니다. + + + No previous/next packet in conversation. + 이 대화에 이전/다음 패킷이 없습니다. + + + No interface selected. + 선택한 인터페이스가 없습니다. + + + Saving %1… + %1 저장 중… + + + Configure all extcaps before start of capture. + 캡처를 시작하기 전에 모든 extcap을 설정하십시오. + + + Invalid capture filter. + 잘못된 캡처 필터입니다. + + + (empty comment) + placeholder for empty comment + (빈 주석) + + + Add New Comment… + 새 주석 추가… + + + Edit "%1" + edit packet comment + "%1" 편집 + + + Delete "%1" + delete packet comment + "%1" 삭제 + + + Delete packet comments + 패킷 주석 삭제 + + + Delete comments from %n packet(s) + + 패킷 %n개에서 주석 삭제 + + + + before starting a new capture + 새로운 캡처를 시작하기 전 + + + before reloading Lua plugins + Lua 플러그인을 다시 불러오기 전 + + + Please wait while Wireshark is initializing… + Wireshark를 초기화하는 동안 기다려 주십시오… + + + before updating + 업데이트 전 + + + There are no TLS Session Keys to save. + 저장할 TLS 세션 키가 없습니다. + + + Export TLS Session Keys (%Ln key(s)) + + TLS 세션 키 내보내기(키 %Ln개) + + + + TLS Session Keys (*.keys *.txt);;All Files ( + TLS 세션 키 (*.keys *.txt);;모든 파일 ( + + + No TLS Secrets + TLS 비밀 정보 없음 + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + 캡처 파일의 TLS 트래픽을 복호화하는 데 필요한 비밀 정보가 없습니다. 위키에서 TLS 트래픽 복호화에 대한 정보를 보시겠습니까? + + + Are you sure you want to discard all decryption secrets? + 모든 복호화 비밀 정보를 버리시겠습니까? + + + No filter available. Try another %1. + 사용 가능한 필터가 없습니다. 다른 %1을(를) 시도하십시오. + + + column + + + + item + 항목 + + + The "%1" column already exists. + "%1" 열이 이미 존재합니다. + + + The "%1" column already exists as "%2". + "%1" 열은 "%2"(으)로 이미 존재합니다. + + + RTP packet search failed + RTP 패킷 검색 실패 + + + No Interface Selected. + 선택한 인터페이스가 없습니다. + + + before restarting the capture + 캡처를 다시 시작하기 전 + + + Wiki Page for %1 + %1에 대한 위키 페이지 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Wireshark 위키는 커뮤니티에서 운영하고 있습니다</p><p>불러올 페이지는 뛰어날 수도, 불완전할 수도, 잘못될 수도, 혹은 존재하지 않을 수도 있습니다.</p><p>위키 페이지로 진행하시겠습니까?</p> + + + Loading + 불러오는 중 + + + Reloading + 다시 불러오는 중 + + + Rescanning + 재탐색 중 + + + + WlanStatisticsDialog + + Wireless LAN Statistics + 무선 LAN 통계 + + + Channel + 채널 + + + SSID + SSID + + + Percent Packets + 패킷 비율 + + + Percent Retry + 재시도 비율 + + + Probe Reqs + Probe 요청 + + + Probe Resp + Probe 응답 + + + Auths + 인증 + + + Retry + 재시도 + + + Deauths + 인증 해제 + + + Other + 기타 + + + diff --git a/ui/qt/wireshark_main_window.cpp b/ui/qt/wireshark_main_window.cpp new file mode 100644 index 00000000..040c45ba --- /dev/null +++ b/ui/qt/wireshark_main_window.cpp @@ -0,0 +1,3250 @@ +/* main_window.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "main_application.h" +#include "wireshark_main_window.h" + +/* + * The generated Ui_WiresharkMainWindow::setupUi() can grow larger than our configured limit, + * so turn off -Wframe-larger-than= for ui_main_window.h. + */ +DIAG_OFF(frame-larger-than=) +#include +DIAG_ON(frame-larger-than=) + +#include +#include "epan/conversation_filter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ui/iface_toolbar.h" + +#ifdef HAVE_LIBPCAP +#include "ui/capture.h" +#include +#endif + +#include "ui/alert_box.h" +#ifdef HAVE_LIBPCAP +#include "ui/capture_ui_utils.h" +#endif +#include "ui/capture_globals.h" +#include "ui/main_statusbar.h" +#include "ui/recent.h" +#include "ui/recent_utils.h" +#include "ui/util.h" +#include "ui/preference_utils.h" + +#include "byte_view_tab.h" +#ifdef HAVE_LIBPCAP +#include "capture_options_dialog.h" +#endif +#include "conversation_colorize_action.h" +#include "export_dissection_dialog.h" +#include "export_object_action.h" +#include "file_set_dialog.h" +#include "filter_dialog.h" +#include "follow_stream_action.h" +#include "funnel_statistics.h" +#include "import_text_dialog.h" +#include "interface_toolbar.h" +#include "packet_diagram.h" +#include "packet_list.h" +#include "proto_tree.h" +#include "simple_dialog.h" +#include "tap_parameter_dialog.h" +#include "wireless_frame.h" +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//menu_recent_file_write_all + +// If we ever add support for multiple windows this will need to be replaced. +static WiresharkMainWindow *gbl_cur_main_window_ = NULL; + +static void plugin_if_mainwindow_apply_filter(GHashTable * data_set) +{ + if (!gbl_cur_main_window_ || !data_set) + return; + + if (g_hash_table_lookup_extended(data_set, "filter_string", NULL, NULL)) { + QString filter((const char *)g_hash_table_lookup(data_set, "filter_string")); + gbl_cur_main_window_->filterPackets(filter); + } +} + +static void plugin_if_mainwindow_preference(GHashTable * data_set) +{ + if (!gbl_cur_main_window_ || !data_set) + return; + + const char * module_name; + const char * pref_name; + const char * pref_value; + +DIAG_OFF_CAST_AWAY_CONST + if (g_hash_table_lookup_extended(data_set, "pref_module", NULL, (gpointer *)&module_name) && + g_hash_table_lookup_extended(data_set, "pref_key", NULL, (gpointer *)&pref_name) && + g_hash_table_lookup_extended(data_set, "pref_value", NULL, (gpointer *)&pref_value)) + { + unsigned int changed_flags = prefs_store_ext(module_name, pref_name, pref_value); + if (changed_flags) { + mainApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged); + mainApp->emitAppSignal(WiresharkApplication::PreferencesChanged); + } + } +DIAG_ON_CAST_AWAY_CONST +} + +static void plugin_if_mainwindow_gotoframe(GHashTable * data_set) +{ + if (!gbl_cur_main_window_ || !data_set) + return; + + gpointer framenr; + + if (g_hash_table_lookup_extended(data_set, "frame_nr", NULL, &framenr)) { + if (GPOINTER_TO_UINT(framenr) != 0) + gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr)); + } +} + +#ifdef HAVE_LIBPCAP + +static void plugin_if_mainwindow_get_ws_info(GHashTable * data_set) +{ + if (!gbl_cur_main_window_ || !data_set) + return; + + ws_info_t *ws_info = NULL; + + if (!g_hash_table_lookup_extended(data_set, "ws_info", NULL, (void**)&ws_info)) + return; + + CaptureFile *cfWrap = gbl_cur_main_window_->captureFile(); + capture_file *cf = cfWrap->capFile(); + + ws_info->ws_info_supported = true; + + /* If we have a filename attached to ws_info clear it */ + if (ws_info->cf_filename != NULL) + { + g_free(ws_info->cf_filename); + ws_info->cf_filename = NULL; + } + + /* Determine the true state of the capture file. We return the true state in + the ws_info structure and DON'T CHANGE the cf->state as we don't want to cause problems + with code that follows this. */ + if (cf) + { + if (cf->filename) + { + /* As we have a cf->filename we'll use the name and the state */ + ws_info->cf_filename = g_strdup(cf->filename); + ws_info->cf_state = cf->state; + } + else + { + /* When we come through here the cf->state can show FILE_READ_DONE even though the + file is actually closed (no filename). A better fix would be to have a + FILE_CLOSE_PENDING state but that involves a lot of code change elsewhere. */ + ws_info->cf_state = FILE_CLOSED; + } + } + + if (!ws_info->cf_filename) + { + /* We may have a filename associated with the main window so let's use it */ + QString fileNameString = gbl_cur_main_window_->getMwFileName(); + if (fileNameString.length()) + { + QByteArray ba = fileNameString.toLatin1(); + const char *c_file_name = ba.data(); + ws_info->cf_filename = g_strdup(c_file_name); + } + } + + if (cf) { + ws_info->cf_count = cf->count; + + QList rows = gbl_cur_main_window_->selectedRows(); + frame_data * fdata = NULL; + if (rows.count() > 0) + fdata = gbl_cur_main_window_->frameDataForRow(rows.at(0)); + + if (cf->state == FILE_READ_DONE && fdata) { + ws_info->cf_framenr = fdata->num; + ws_info->frame_passed_dfilter = (fdata->passed_dfilter == 1); + } + else { + ws_info->cf_framenr = 0; + ws_info->frame_passed_dfilter = FALSE; + } + } + else + { + /* Initialise the other ws_info structure values */ + ws_info->cf_count = 0; + ws_info->cf_framenr = 0; + ws_info->frame_passed_dfilter = FALSE; + } +} + +#endif /* HAVE_LIBPCAP */ + +static void plugin_if_mainwindow_get_frame_data(GHashTable* data_set) +{ + if (!gbl_cur_main_window_ || !data_set) + return; + + plugin_if_frame_data_cb extract_cb; + void* user_data; + void** ret_value_ptr; + + if (g_hash_table_lookup_extended(data_set, "extract_cb", NULL, (void**)&extract_cb) && + g_hash_table_lookup_extended(data_set, "user_data", NULL, (void**)&user_data) && + g_hash_table_lookup_extended(data_set, "ret_value_ptr", NULL, (void**)&ret_value_ptr)) + { + QList rows = gbl_cur_main_window_->selectedRows(); + if (rows.count() > 0) { + frame_data* fdata = gbl_cur_main_window_->frameDataForRow(rows.at(0)); + if (fdata) { + *ret_value_ptr = extract_cb(fdata, user_data); + } + } + } +} + +static void plugin_if_mainwindow_get_capture_file(GHashTable* data_set) +{ + if (!gbl_cur_main_window_ || !data_set) + return; + + plugin_if_capture_file_cb extract_cb; + void* user_data; + void** ret_value_ptr; + + if (g_hash_table_lookup_extended(data_set, "extract_cb", NULL, (void**)&extract_cb) && + g_hash_table_lookup_extended(data_set, "user_data", NULL, (void**)&user_data) && + g_hash_table_lookup_extended(data_set, "ret_value_ptr", NULL, (void**)&ret_value_ptr)) + { + CaptureFile* cfWrap = gbl_cur_main_window_->captureFile(); + capture_file* cf = cfWrap->capFile(); + if (cf) { + *ret_value_ptr = extract_cb(cf, user_data); + } + } +} + +static void plugin_if_mainwindow_update_toolbars(GHashTable * data_set) +{ + if (!gbl_cur_main_window_ || !data_set) + return; + + if (g_hash_table_lookup_extended(data_set, "toolbar_name", NULL, NULL)) { + QString toolbarName((const char *)g_hash_table_lookup(data_set, "toolbar_name")); + gbl_cur_main_window_->removeAdditionalToolbar(toolbarName); + + } +} + +static void mainwindow_add_toolbar(const iface_toolbar *toolbar_entry) +{ + if (gbl_cur_main_window_ && toolbar_entry) + { + gbl_cur_main_window_->addInterfaceToolbar(toolbar_entry); + } +} + +static void mainwindow_remove_toolbar(const gchar *menu_title) +{ + if (gbl_cur_main_window_ && menu_title) + { + gbl_cur_main_window_->removeInterfaceToolbar(menu_title); + } +} + +QMenu* WiresharkMainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) { + QList actions = parent_menu->actions(); + QList::const_iterator i; + for (i = actions.constBegin(); i != actions.constEnd(); ++i) { + if ((*i)->text()==menu_text) { + return (*i)->menu(); + } + } + // If we get here there menu entry was not found, add a sub menu + return parent_menu->addMenu(menu_text); +} + +WiresharkMainWindow::WiresharkMainWindow(QWidget *parent) : + MainWindow(parent), + main_ui_(new Ui::WiresharkMainWindow), + previous_focus_(NULL), + file_set_dialog_(NULL), + show_hide_actions_(NULL), + time_display_actions_(NULL), + time_precision_actions_(NULL), + funnel_statistics_(NULL), + freeze_focus_(NULL), + was_maximized_(false), + capture_stopping_(false), + capture_filter_valid_(false) +#ifdef HAVE_LIBPCAP + , capture_options_dialog_(NULL) + , info_data_() +#endif +#if defined(Q_OS_MAC) + , dock_menu_(NULL) +#endif +{ + if (!gbl_cur_main_window_) { + connect(mainApp, SIGNAL(openStatCommandDialog(QString, const char*, void*)), + this, SLOT(openStatCommandDialog(QString, const char*, void*))); + connect(mainApp, SIGNAL(openTapParameterDialog(QString, const QString, void*)), + this, SLOT(openTapParameterDialog(QString, const QString, void*))); + } + gbl_cur_main_window_ = this; +#ifdef HAVE_LIBPCAP + capture_input_init(&cap_session_, CaptureFile::globalCapFile()); +#endif + + findTextCodecs(); + // setpUi calls QMetaObject::connectSlotsByName(this). connectSlotsByName + // iterates over *all* of our children, looking for matching "on_" slots. + // The fewer children we have at this point the better. + main_ui_->setupUi(this); +#ifdef HAVE_SOFTWARE_UPDATE + update_action_ = new QAction(tr("Check for Updates…"), main_ui_->menuHelp); +#endif +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) + wireless_frame_ = new WirelessFrame(this); + main_ui_->wirelessToolBar->addWidget(wireless_frame_); +#else + removeToolBar(main_ui_->wirelessToolBar); + main_ui_->menuView->removeAction(main_ui_->actionViewWirelessToolbar); +#endif + + menu_groups_ = QList() + << REGISTER_PACKET_ANALYZE_GROUP_UNSORTED + << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER + << REGISTER_PACKET_STAT_GROUP_UNSORTED + << REGISTER_STAT_GROUP_GENERIC + << REGISTER_STAT_GROUP_CONVERSATION_LIST + << REGISTER_STAT_GROUP_ENDPOINT_LIST + << REGISTER_STAT_GROUP_RESPONSE_TIME + << REGISTER_STAT_GROUP_RSERPOOL + << REGISTER_STAT_GROUP_TELEPHONY + << REGISTER_STAT_GROUP_TELEPHONY_ANSI + << REGISTER_STAT_GROUP_TELEPHONY_GSM + << REGISTER_STAT_GROUP_TELEPHONY_LTE + << REGISTER_STAT_GROUP_TELEPHONY_MTP3 + << REGISTER_STAT_GROUP_TELEPHONY_SCTP + << REGISTER_TOOLS_GROUP_UNSORTED; + + setWindowIcon(mainApp->normalIcon()); + setTitlebarForCaptureFile(); + setMenusForCaptureFile(); + setForCapturedPackets(false); + setMenusForFileSet(false); + interfaceSelectionChanged(); + loadWindowGeometry(); + +#ifndef HAVE_LUA + main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false); +#endif + + qRegisterMetaType("FilterAction::Action"); + qRegisterMetaType("FilterAction::ActionType"); + connect(this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SLOT(queuedFilterAction(QString, FilterAction::Action, FilterAction::ActionType)), + Qt::QueuedConnection); + + //To prevent users use features before initialization complete + //Otherwise unexpected problems may occur + setFeaturesEnabled(false); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(applyGlobalCommandLineOptions())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(zoomText())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(addPluginIFStructures())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(initConversationMenus())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(initExportObjectsMenus())); + connect(mainApp, SIGNAL(appInitialized()), this, SLOT(initFollowStreamMenus())); + + connect(mainApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry())); + connect(mainApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes())); + connect(mainApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars())); + connect(mainApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions())); + connect(mainApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText())); + connect(mainApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile())); + + connect(mainApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures())); + updateRecentCaptures(); + +#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) + connect(mainApp, SIGNAL(softwareUpdateRequested()), this, SLOT(softwareUpdateRequested()), + Qt::BlockingQueuedConnection); +#endif + + df_combo_box_ = new DisplayFilterCombo(this); + + funnel_statistics_ = new FunnelStatistics(this, capture_file_); + connect(df_combo_box_, &QComboBox::editTextChanged, funnel_statistics_, &FunnelStatistics::displayFilterTextChanged); + connect(funnel_statistics_, &FunnelStatistics::setDisplayFilter, this, &WiresharkMainWindow::setDisplayFilter); + connect(funnel_statistics_, SIGNAL(openCaptureFile(QString, QString)), + this, SLOT(openCaptureFile(QString, QString))); + + file_set_dialog_ = new FileSetDialog(this); + connect(file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)), + this, SLOT(openCaptureFile(QString))); + + initMainToolbarIcons(); + + main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionNewDisplayFilterExpression, df_combo_box_); + + // Make sure filter expressions overflow into a menu instead of a + // larger toolbar. We do this by adding them to a child toolbar. + // https://bugreports.qt.io/browse/QTBUG-2472 + FilterExpressionToolBar *filter_expression_toolbar_ = new FilterExpressionToolBar(this); + connect(filter_expression_toolbar_, &FilterExpressionToolBar::filterPreferences, this, &WiresharkMainWindow::onFilterPreferences); + connect(filter_expression_toolbar_, &FilterExpressionToolBar::filterSelected, this, &WiresharkMainWindow::onFilterSelected); + connect(filter_expression_toolbar_, &FilterExpressionToolBar::filterEdit, this, &WiresharkMainWindow::onFilterEdit); + + main_ui_->displayFilterToolBar->addWidget(filter_expression_toolbar_); + +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) + connect(wireless_frame_, SIGNAL(showWirelessPreferences(QString)), + this, SLOT(showPreferencesDialog(QString))); +#endif + + main_ui_->goToFrame->hide(); + connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)), + main_ui_->actionGoGoToPacket, SLOT(setChecked(bool))); + + // XXX For some reason the cursor is drawn funny with an input mask set + // https://bugreports.qt-project.org/browse/QTBUG-7174 + + main_ui_->searchFrame->hide(); + connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)), + main_ui_->actionEditFindPacket, SLOT(setChecked(bool))); + + main_ui_->addressEditorFrame->hide(); + main_ui_->columnEditorFrame->hide(); + main_ui_->preferenceEditorFrame->hide(); + main_ui_->filterExpressionFrame->hide(); + +#ifndef HAVE_LIBPCAP + main_ui_->menuCapture->setEnabled(false); + main_ui_->actionCaptureStart->setEnabled(false); + main_ui_->actionCaptureStop->setEnabled(false); + main_ui_->actionCaptureRestart->setEnabled(false); + main_ui_->actionCaptureOptions->setEnabled(false); + main_ui_->actionCaptureRefreshInterfaces->setEnabled(false); +#endif + + // Set OS specific shortcuts for fullscreen mode +#if defined(Q_OS_MAC) + main_ui_->actionViewFullScreen->setShortcut(QKeySequence::FullScreen); +#else + main_ui_->actionViewFullScreen->setShortcut(QKeySequence(Qt::Key_F11)); +#endif + +#if defined(Q_OS_MAC) + + main_ui_->goToPacketLabel->setAttribute(Qt::WA_MacSmallSize, true); + main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true); + main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true); + main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true); + + main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole); + +#endif // Q_OS_MAC + + connect(main_ui_->goToGo, &QPushButton::pressed, this, &WiresharkMainWindow::goToGoClicked); + connect(main_ui_->goToCancel, &QPushButton::pressed, this, &WiresharkMainWindow::goToCancelClicked); + +// A billion-1 is equivalent to the inputMask 900000000 previously used +// Avoid QValidator::Intermediate values by using a top value of all 9's +#define MAX_GOTO_LINE 999999999 + +QIntValidator *goToLineQiv = new QIntValidator(0,MAX_GOTO_LINE,this); +main_ui_->goToLineEdit->setValidator(goToLineQiv); + +#ifdef HAVE_SOFTWARE_UPDATE + QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout); + main_ui_->menuHelp->insertAction(update_sep, update_action_); + connect(update_action_, SIGNAL(triggered()), this, SLOT(checkForUpdates())); +#endif + master_split_.setObjectName("splitterMaster"); + extra_split_.setObjectName("splitterExtra"); + master_split_.setChildrenCollapsible(false); + extra_split_.setChildrenCollapsible(false); + main_ui_->mainStack->addWidget(&master_split_); + + empty_pane_.setObjectName("emptyPane"); + empty_pane_.setVisible(false); + + packet_list_ = new PacketList(&master_split_); + main_ui_->wirelessTimelineWidget->setPacketList(packet_list_); + connect(packet_list_, SIGNAL(framesSelected(QList)), this, SLOT(setMenusForSelectedPacket())); + connect(packet_list_, SIGNAL(framesSelected(QList)), this, SIGNAL(framesSelected(QList))); + + QAction *action = main_ui_->menuPacketComment->addAction(tr("Add New Comment…")); + connect(action, &QAction::triggered, this, &WiresharkMainWindow::addPacketComment); + action->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_C)); + connect(main_ui_->menuPacketComment, SIGNAL(aboutToShow()), this, SLOT(setEditCommentsMenu())); + + proto_tree_ = new ProtoTree(&master_split_); + proto_tree_->installEventFilter(this); + + packet_list_->setProtoTree(proto_tree_); + packet_list_->installEventFilter(this); + + packet_diagram_ = new PacketDiagram(&master_split_); + + main_stack_ = main_ui_->mainStack; + welcome_page_ = main_ui_->welcomePage; + main_status_bar_ = main_ui_->statusBar; + + connect(proto_tree_, &ProtoTree::fieldSelected, + this, &WiresharkMainWindow::fieldSelected); + connect(packet_list_, &PacketList::fieldSelected, + this, &WiresharkMainWindow::fieldSelected); + connect(this, &WiresharkMainWindow::fieldSelected, + this, &WiresharkMainWindow::setMenusForSelectedTreeRow); + connect(this, &WiresharkMainWindow::fieldSelected, + main_ui_->statusBar, &MainStatusBar::selectedFieldChanged); + + connect(this, &WiresharkMainWindow::fieldHighlight, + main_ui_->statusBar, &MainStatusBar::highlightedFieldChanged); + connect(mainApp, &WiresharkApplication::captureActive, + this, &WiresharkMainWindow::captureActive); + + byte_view_tab_ = new ByteViewTab(&master_split_); + + // Packet list and proto tree must exist before these are called. + setMenusForSelectedPacket(); + setMenusForSelectedTreeRow(); + + initShowHideMainWidgets(); + initTimeDisplayFormatMenu(); + initTimePrecisionFormatMenu(); + initFreezeActions(); + updatePreferenceActions(); + updateRecentActions(); + setForCaptureInProgress(false); + + setTabOrder(df_combo_box_->lineEdit(), packet_list_); + setTabOrder(packet_list_, proto_tree_); + + connect(&capture_file_, SIGNAL(captureEvent(CaptureEvent)), + this, SLOT(captureEventHandler(CaptureEvent))); + connect(&capture_file_, SIGNAL(captureEvent(CaptureEvent)), + mainApp, SLOT(captureEventHandler(CaptureEvent))); + connect(&capture_file_, SIGNAL(captureEvent(CaptureEvent)), + main_ui_->statusBar, SLOT(captureEventHandler(CaptureEvent))); + + connect(mainApp, SIGNAL(freezePacketList(bool)), + packet_list_, SLOT(freezePacketList(bool))); + connect(mainApp, SIGNAL(columnsChanged()), + packet_list_, SLOT(columnsChanged())); + connect(mainApp, SIGNAL(preferencesChanged()), + packet_list_, SLOT(preferencesChanged())); + connect(mainApp, SIGNAL(recentPreferencesRead()), + this, SLOT(applyRecentPaneGeometry())); + connect(mainApp, SIGNAL(recentPreferencesRead()), + this, SLOT(updateRecentActions())); + connect(mainApp, SIGNAL(packetDissectionChanged()), + this, SLOT(redissectPackets()), Qt::QueuedConnection); + + connect(mainApp, SIGNAL(checkDisplayFilter()), + this, SLOT(checkDisplayFilter())); + connect(mainApp, SIGNAL(fieldsChanged()), + this, SLOT(fieldsChanged())); + connect(mainApp, SIGNAL(reloadLuaPlugins()), + this, SLOT(reloadLuaPlugins())); + + connect(main_ui_->mainStack, SIGNAL(currentChanged(int)), + this, SLOT(mainStackChanged(int))); + + connect(welcome_page_, SIGNAL(startCapture(QStringList)), + this, SLOT(startCapture(QStringList))); + connect(welcome_page_, SIGNAL(recentFileActivated(QString)), + this, SLOT(openCaptureFile(QString))); + + connect(main_ui_->addressEditorFrame, &AddressEditorFrame::redissectPackets, + this, &WiresharkMainWindow::redissectPackets); + connect(main_ui_->addressEditorFrame, &AddressEditorFrame::showNameResolutionPreferences, + this, &WiresharkMainWindow::showPreferencesDialog); + connect(main_ui_->preferenceEditorFrame, &PreferenceEditorFrame::showProtocolPreferences, + this, &WiresharkMainWindow::showPreferencesDialog); + connect(main_ui_->filterExpressionFrame, &FilterExpressionFrame::showPreferencesDialog, + this, &WiresharkMainWindow::showPreferencesDialog); + connect(main_ui_->filterExpressionFrame, &FilterExpressionFrame::filterExpressionsChanged, + filter_expression_toolbar_, &FilterExpressionToolBar::filterExpressionsChanged); + + /* Connect change of capture file */ + connect(this, &WiresharkMainWindow::setCaptureFile, + main_ui_->searchFrame, &SearchFrame::setCaptureFile); + connect(this, &WiresharkMainWindow::setCaptureFile, + main_ui_->statusBar, &MainStatusBar::setCaptureFile); + connect(this, &WiresharkMainWindow::setCaptureFile, + packet_list_, &PacketList::setCaptureFile); + connect(this, &WiresharkMainWindow::setCaptureFile, + proto_tree_, &ProtoTree::setCaptureFile); + + connect(mainApp, SIGNAL(zoomMonospaceFont(QFont)), + packet_list_, SLOT(setMonospaceFont(QFont))); + connect(mainApp, SIGNAL(zoomMonospaceFont(QFont)), + proto_tree_, SLOT(setMonospaceFont(QFont))); + + connectFileMenuActions(); + connectEditMenuActions(); + connectViewMenuActions(); + connectGoMenuActions(); + connectCaptureMenuActions(); + connectAnalyzeMenuActions(); + connectStatisticsMenuActions(); + connectTelephonyMenuActions(); + connectWirelessMenuActions(); + connectToolsMenuActions(); + connectHelpMenuActions(); + + connect(packet_list_, SIGNAL(packetDissectionChanged()), + this, SLOT(redissectPackets())); + connect(packet_list_, SIGNAL(showColumnPreferences(QString)), + this, SLOT(showPreferencesDialog(QString))); + connect(packet_list_, SIGNAL(showProtocolPreferences(QString)), + this, SLOT(showPreferencesDialog(QString))); + connect(packet_list_, SIGNAL(editProtocolPreference(preference*, pref_module*)), + main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*, pref_module*))); + connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int))); + connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()), + packet_list_, SLOT(columnsChanged())); + connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)), + this, SLOT(openPacketDialog())); + connect(packet_list_, SIGNAL(packetListScrolled(bool)), + main_ui_->actionGoAutoScroll, SLOT(setChecked(bool))); + + connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)), + this, SLOT(openPacketDialog(bool))); + connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)), + this, SLOT(showPreferencesDialog(QString))); + connect(proto_tree_, SIGNAL(editProtocolPreference(preference*, pref_module*)), + main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*, pref_module*))); + + connect(main_ui_->statusBar, &MainStatusBar::showExpertInfo, this, [=]() { + statCommandExpertInfo(NULL, NULL); + }); + + connect(main_ui_->statusBar, &MainStatusBar::stopLoading, + &capture_file_, &CaptureFile::stopLoading); + + connect(main_ui_->statusBar, &MainStatusBar::editCaptureComment, + main_ui_->actionStatisticsCaptureFileProperties, &QAction::trigger); + + connect(main_ui_->menuApplyAsFilter, &QMenu::aboutToShow, + this, &WiresharkMainWindow::filterMenuAboutToShow); + connect(main_ui_->menuPrepareAFilter, &QMenu::aboutToShow, + this, &WiresharkMainWindow::filterMenuAboutToShow); + +#ifdef HAVE_LIBPCAP + QTreeWidget *iface_tree = findChild("interfaceTree"); + if (iface_tree) { + connect(iface_tree, SIGNAL(itemSelectionChanged()), + this, SLOT(interfaceSelectionChanged())); + } + connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)), + this, SLOT(captureFilterSyntaxChanged(bool))); + + connect(this, SIGNAL(showExtcapOptions(QString&, bool)), + this, SLOT(showExtcapOptionsDialog(QString&, bool))); + connect(this->welcome_page_, SIGNAL(showExtcapOptions(QString&, bool)), + this, SLOT(showExtcapOptionsDialog(QString&, bool))); + +#endif // HAVE_LIBPCAP + + /* Create plugin_if hooks */ + plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter); + plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter); + plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference); + plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe); +#ifdef HAVE_LIBPCAP + plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info); +#endif + plugin_if_register_gui_cb(PLUGIN_IF_GET_FRAME_DATA, plugin_if_mainwindow_get_frame_data); + plugin_if_register_gui_cb(PLUGIN_IF_GET_CAPTURE_FILE, plugin_if_mainwindow_get_capture_file); + plugin_if_register_gui_cb(PLUGIN_IF_REMOVE_TOOLBAR, plugin_if_mainwindow_update_toolbars); + + /* Register Interface Toolbar callbacks */ + iface_toolbar_register_cb(mainwindow_add_toolbar, mainwindow_remove_toolbar); + + /* Show tooltips on menu items that go to websites */ + main_ui_->actionHelpMPWireshark->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_WIRESHARK))); + main_ui_->actionHelpMPWireshark_Filter->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_WIRESHARK_FILTER))); + main_ui_->actionHelpMPCapinfos->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_CAPINFOS))); + main_ui_->actionHelpMPDumpcap->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_DUMPCAP))); + main_ui_->actionHelpMPEditcap->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_EDITCAP))); + main_ui_->actionHelpMPMergecap->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_MERGECAP))); + main_ui_->actionHelpMPRawshark->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_RAWSHARK))); + main_ui_->actionHelpMPReordercap->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_REORDERCAP))); + main_ui_->actionHelpMPText2pcap->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_TEXT2PCAP))); + main_ui_->actionHelpMPTShark->setToolTip(gchar_free_to_qstring(topic_action_url(LOCALPAGE_MAN_TSHARK))); + + main_ui_->actionHelpContents->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_USERGUIDE))); + main_ui_->actionHelpWebsite->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_HOME))); + main_ui_->actionHelpFAQ->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_FAQ))); + main_ui_->actionHelpAsk->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_ASK))); + main_ui_->actionHelpDownloads->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_DOWNLOAD))); + main_ui_->actionHelpWiki->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_WIKI))); + main_ui_->actionHelpSampleCaptures->setToolTip(gchar_free_to_qstring(topic_action_url(ONLINEPAGE_SAMPLE_CAPTURES))); + + showWelcome(); +} + +WiresharkMainWindow::~WiresharkMainWindow() +{ + disconnect(main_ui_->mainStack, 0, 0, 0); + +#ifndef Q_OS_MAC + // Below dialogs inherit GeometryStateDialog + // For reasons described in geometry_state_dialog.h no parent is set when + // instantiating the dialogs and as a resul objects are not automatically + // freed by its parent. Free then here explicitly to avoid leak and numerous + // Valgrind complaints. + delete file_set_dialog_; +#ifdef HAVE_LIBPCAP + delete capture_options_dialog_; +#endif + +#endif + delete main_ui_; +} + +QMenu *WiresharkMainWindow::createPopupMenu() +{ + QMenu *menu = new QMenu(); + menu->addAction(main_ui_->actionViewMainToolbar); + menu->addAction(main_ui_->actionViewFilterToolbar); +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) + menu->addAction(main_ui_->actionViewWirelessToolbar); +#endif + + if (!main_ui_->menuInterfaceToolbars->actions().isEmpty()) { + QMenu *submenu = menu->addMenu(main_ui_->menuInterfaceToolbars->title()); + foreach(QAction *action, main_ui_->menuInterfaceToolbars->actions()) { + submenu->addAction(action); + } + } + + if (!main_ui_->menuAdditionalToolbars->actions().isEmpty()) { + QMenu *subMenu = menu->addMenu(main_ui_->menuAdditionalToolbars->title()); + foreach(QAction *action, main_ui_->menuAdditionalToolbars->actions()) { + subMenu->addAction(action); + } + } + + menu->addAction(main_ui_->actionViewStatusBar); + + menu->addSeparator(); + menu->addAction(main_ui_->actionViewPacketList); + menu->addAction(main_ui_->actionViewPacketDetails); + menu->addAction(main_ui_->actionViewPacketBytes); + menu->addAction(main_ui_->actionViewPacketDiagram); + return menu; +} + +void WiresharkMainWindow::addInterfaceToolbar(const iface_toolbar *toolbar_entry) +{ + QMenu *menu = main_ui_->menuInterfaceToolbars; + bool visible = g_list_find_custom(recent.interface_toolbars, toolbar_entry->menu_title, (GCompareFunc)strcmp) ? true : false; + + QString title = QString().fromUtf8(toolbar_entry->menu_title); + QAction *action = new QAction(title, menu); + action->setEnabled(true); + action->setCheckable(true); + action->setChecked(visible); + action->setToolTip(tr("Show or hide the toolbar")); + + QAction *before = NULL; + foreach(QAction *action, menu->actions()) { + // Ensure we add the menu entries in sorted order + if (action->text().compare(title, Qt::CaseInsensitive) > 0) { + before = action; + break; + } + } + menu->insertAction(before, action); + + InterfaceToolbar *interface_toolbar = new InterfaceToolbar(this, toolbar_entry); + connect(mainApp, SIGNAL(appInitialized()), interface_toolbar, SLOT(interfaceListChanged())); + connect(mainApp, SIGNAL(localInterfaceListChanged()), interface_toolbar, SLOT(interfaceListChanged())); + + QToolBar *toolbar = new QToolBar(this); + toolbar->addWidget(interface_toolbar); + toolbar->setMovable(false); + toolbar->setVisible(visible); + + action->setData(QVariant::fromValue(toolbar)); + + addToolBar(Qt::TopToolBarArea, toolbar); + insertToolBarBreak(toolbar); + + if (show_hide_actions_) { + show_hide_actions_->addAction(action); + } + + menu->menuAction()->setVisible(true); +} + +void WiresharkMainWindow::removeInterfaceToolbar(const gchar *menu_title) +{ + QMenu *menu = main_ui_->menuInterfaceToolbars; + QAction *action = NULL; + QMap::iterator i; + + QString title = QString().fromUtf8(menu_title); + foreach(action, menu->actions()) { + if (title.compare(action->text()) == 0) { + break; + } + } + + if (action) { + if (show_hide_actions_) { + show_hide_actions_->removeAction(action); + } + menu->removeAction(action); + + QToolBar *toolbar = action->data().value(); + removeToolBar(toolbar); + + delete action; + delete toolbar; + } + + menu->menuAction()->setVisible(!menu->actions().isEmpty()); +} + +void WiresharkMainWindow::updateStyleSheet() +{ +#ifdef Q_OS_MAC + // TODO: The event type QEvent::ApplicationPaletteChange is not sent to all child widgets. + // Workaround this by doing it manually for all AccordionFrame. + main_ui_->addressEditorFrame->updateStyleSheet(); + main_ui_->columnEditorFrame->updateStyleSheet(); + main_ui_->filterExpressionFrame->updateStyleSheet(); + main_ui_->goToFrame->updateStyleSheet(); + main_ui_->preferenceEditorFrame->updateStyleSheet(); + main_ui_->searchFrame->updateStyleSheet(); + + df_combo_box_->updateStyleSheet(); + welcome_page_->updateStyleSheets(); +#endif +} + +bool WiresharkMainWindow::eventFilter(QObject *obj, QEvent *event) { + + // The user typed some text. Start filling in a filter. + // We may need to be more choosy here. We just need to catch events for the packet list, + // proto tree, and main welcome widgets. + if (event->type() == QEvent::KeyPress) { + QKeyEvent *kevt = static_cast(event); + if (kevt->text().length() > 0 && kevt->text()[0].isPrint() && + !(kevt->modifiers() & Qt::ControlModifier)) { + df_combo_box_->lineEdit()->insert(kevt->text()); + df_combo_box_->lineEdit()->setFocus(); + return true; + } + } + + return QMainWindow::eventFilter(obj, event); +} + +bool WiresharkMainWindow::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + initMainToolbarIcons(); + updateStyleSheet(); + break; + default: + break; + + } + return QMainWindow::event(event); +} + +void WiresharkMainWindow::keyPressEvent(QKeyEvent *event) { + + // Explicitly focus on the display filter combo. + if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) { + df_combo_box_->setFocus(Qt::ShortcutFocusReason); + return; + } + + if (mainApp->focusWidget() == main_ui_->goToLineEdit) { + if (event->modifiers() == Qt::NoModifier) { + if (event->key() == Qt::Key_Escape) { + goToCancelClicked(); + } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + goToGoClicked(); + } + } + return; // goToLineEdit didn't want it and we don't either. + } + + // Move up & down the packet list. + if (event->key() == Qt::Key_F7) { + packet_list_->goPreviousPacket(); + } else if (event->key() == Qt::Key_F8) { + packet_list_->goNextPacket(); + } + + // Move along, citizen. + QMainWindow::keyPressEvent(event); +} + +void WiresharkMainWindow::closeEvent(QCloseEvent *event) { + if (main_ui_->actionCaptureStop->isEnabled()) { + // Capture is running, we should stop it before close and ignore the event + stopCapture(); + event->ignore(); + return; + } + + saveWindowGeometry(); + + /* If we're in the middle of stopping a capture, don't do anything; + the user can try deleting the window after the capture stops. */ + if (capture_stopping_) { + event->ignore(); + return; + } + + QString before_what(tr(" before quitting")); + if (!testCaptureFileClose(before_what, Quit)) { + event->ignore(); + return; + } + +#ifdef HAVE_LIBPCAP + if (capture_options_dialog_) capture_options_dialog_->close(); +#endif + // Make sure we kill any open dumpcap processes. + delete welcome_page_; + + // One of the many places we assume one main window. + if (!mainApp->isInitialized()) { + // If we're still initializing, QCoreApplication::quit() won't + // exit properly because we are not in the event loop. This + // means that the application won't clean up after itself. We + // might want to call mainApp->processEvents() during startup + // instead so that we can do a normal exit here. + exit(0); + } + mainApp->quit(); + // When the main loop is not yet running (i.e. when openCaptureFile is + // executing in main.cpp), the above quit action has no effect. + // Schedule a quit action for the next execution of the main loop. + QMetaObject::invokeMethod(mainApp, "quit", Qt::QueuedConnection); +} + +// XXX On windows the drag description is "Copy". It should be "Open" or +// "Merge" as appropriate. It looks like we need access to IDataObject in +// order to set DROPDESCRIPTION. +void WiresharkMainWindow::dragEnterEvent(QDragEnterEvent *event) +{ + if (!event->mimeData()->hasUrls()) + { + event->ignore(); + return; + } + + if (!main_ui_->actionFileOpen->isEnabled()) { + // We could alternatively call setAcceptDrops(!capture_in_progress) + // in setMenusForCaptureInProgress but that wouldn't provide feedback. + + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, tr("Unable to drop files during capture.")); + event->setDropAction(Qt::IgnoreAction); + event->ignore(); + return; + } + + bool have_files = false; + foreach(QUrl drag_url, event->mimeData()->urls()) { + if (!drag_url.toLocalFile().isEmpty()) { + have_files = true; + break; + } + } + + if (have_files) { + event->acceptProposedAction(); + } +} + +void WiresharkMainWindow::dropEvent(QDropEvent *event) +{ + if (!event->mimeData()->hasUrls()) + { + event->ignore(); + return; + } + + QList local_files; + int max_dropped_files = 100; // Arbitrary + + foreach(QUrl drop_url, event->mimeData()->urls()) { + QString drop_file = drop_url.toLocalFile(); + if (!drop_file.isEmpty()) { + local_files << drop_file.toUtf8(); + if (local_files.size() >= max_dropped_files) { + break; + } + } + } + + event->acceptProposedAction(); + + if (local_files.size() < 1) { + event->ignore(); + return; + } + + event->accept(); + + if (local_files.size() == 1) { + openCaptureFile(local_files.at(0)); + return; + } + + const char **in_filenames = g_new(const char *, local_files.size()); + char *tmpname = NULL; + + for (int i = 0; i < local_files.size(); i++) { + in_filenames[i] = local_files.at(i).constData(); + } + + /* merge the files in chronological order */ + if (cf_merge_files_to_tempfile(this, global_capture_opts.temp_dir, &tmpname, static_cast(local_files.size()), + in_filenames, + wtap_pcapng_file_type_subtype(), + FALSE) == CF_OK) { + /* Merge succeeded; close the currently-open file and try + to open the merged capture file. */ + openCaptureFile(tmpname, QString(), WTAP_TYPE_AUTO, TRUE); + } + + g_free(tmpname); + g_free(in_filenames); +} + +// Apply recent settings to the main window geometry. +// We haven't loaded the preferences at this point so we assume that the +// position and size preference are enabled. +// Note we might end up with unexpected screen geometries if the user +// unplugs or plugs in a monitor: +// https://bugreports.qt.io/browse/QTBUG-44213 +void WiresharkMainWindow::loadWindowGeometry() +{ + int min_sensible_dimension = 200; + +#ifndef Q_OS_MAC + if (recent.gui_geometry_main_maximized) { + setWindowState(Qt::WindowMaximized); + } else +#endif + { + QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y, + recent.gui_geometry_main_width, recent.gui_geometry_main_height); + if (!rect_on_screen(recent_geom)) { + // We're not visible on any screens. See if we can move onscreen + // without resizing. + recent_geom.moveTo(50, 50); // recent.c defaults to 20. + } + + if (!rect_on_screen(recent_geom)) { + // Give up and use the default geometry. + return; + } + +// if (prefs.gui_geometry_save_position) { + move(recent_geom.topLeft()); +// } + + if (// prefs.gui_geometry_save_size && + recent_geom.width() > min_sensible_dimension && + recent_geom.height() > min_sensible_dimension) { + resize(recent_geom.size()); + } + } +} + +void WiresharkMainWindow::saveWindowGeometry() +{ + if (prefs.gui_geometry_save_position) { + recent.gui_geometry_main_x = pos().x(); + recent.gui_geometry_main_y = pos().y(); + } + + if (prefs.gui_geometry_save_size) { + recent.gui_geometry_main_width = size().width(); + recent.gui_geometry_main_height = size().height(); + } + + if (prefs.gui_geometry_save_maximized) { + // On macOS this is false when it shouldn't be + recent.gui_geometry_main_maximized = isMaximized(); + } + + if (master_split_.sizes().length() > 0) { + recent.gui_geometry_main_upper_pane = master_split_.sizes()[0]; + } + + if (master_split_.sizes().length() > 2) { + recent.gui_geometry_main_lower_pane = master_split_.sizes()[1]; + } else if (extra_split_.sizes().length() > 0) { + recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0]; + } +} + +// Our event loop becomes nested whenever we call update_progress_dlg, which +// includes several places in file.c. The GTK+ UI stays out of trouble by +// showing a modal progress dialog. We attempt to do the equivalent below by +// disabling parts of the main window. At a minumum the ProgressFrame in the +// main status bar must remain accessible. +// +// We might want to do this any time the main status bar progress frame is +// shown and hidden. +void WiresharkMainWindow::freeze() +{ + freeze_focus_ = mainApp->focusWidget(); + + // XXX Alternatively we could just disable and enable the main menu. + for (int i = 0; i < freeze_actions_.size(); i++) { + QAction *action = freeze_actions_[i].first; + freeze_actions_[i].second = action->isEnabled(); + action->setEnabled(false); + } + main_ui_->centralWidget->setEnabled(false); +} + +void WiresharkMainWindow::thaw() +{ + main_ui_->centralWidget->setEnabled(true); + for (int i = 0; i < freeze_actions_.size(); i++) { + freeze_actions_[i].first->setEnabled(freeze_actions_[i].second); + } + + if (freeze_focus_) freeze_focus_->setFocus(); + freeze_focus_ = NULL; +} + +void WiresharkMainWindow::mergeCaptureFile() +{ + QString file_name = ""; + QString read_filter = ""; + dfilter_t *rfcode = NULL; + int err; + + if (!capture_file_.capFile()) + return; + + if (prefs.gui_ask_unsaved) { + if (cf_has_unsaved_data(capture_file_.capFile())) { + QMessageBox msg_dialog; + gchar *display_basename; + int response; + + msg_dialog.setIcon(QMessageBox::Question); + /* This file has unsaved data; ask the user whether to save + the capture. */ + if (capture_file_.capFile()->is_tempfile) { + msg_dialog.setText(tr("Save packets before merging?")); + msg_dialog.setInformativeText(tr("A temporary capture file can't be merged.")); + } else { + /* + * Format the message. + */ + display_basename = g_filename_display_basename(capture_file_.capFile()->filename); + msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename)); + g_free(display_basename); + msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged.")); + } + + msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel); + msg_dialog.setDefaultButton(QMessageBox::Save); + + response = msg_dialog.exec(); + + switch (response) { + + case QMessageBox::Save: + /* Save the file but don't close it */ + saveCaptureFile(capture_file_.capFile(), false); + break; + + case QMessageBox::Cancel: + default: + /* Don't do the merge. */ + return; + } + } + } + + for (;;) { + CaptureFileDialog merge_dlg(this, capture_file_.capFile()); + int file_type; + cf_status_t merge_status; + char *in_filenames[2]; + char *tmpname; + + if (merge_dlg.merge(file_name, read_filter)) { + df_error_t *df_err = NULL; + + if (!dfilter_compile(qUtf8Printable(read_filter), &rfcode, &df_err)) { + /* Not valid. Tell the user, and go back and run the file + selection box again once they dismiss the alert. */ + // Similar to commandline_info.jfilter section in main(). + QMessageBox::warning(this, tr("Invalid Read Filter"), + QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, df_err->msg)), + QMessageBox::Ok); + df_error_free(&df_err); + continue; + } + } else { + return; + } + + file_type = capture_file_.capFile()->cd_t; + + /* Try to merge or append the two files */ + if (merge_dlg.mergeType() == 0) { + /* chronological order */ + in_filenames[0] = g_strdup(capture_file_.capFile()->filename); + in_filenames[1] = qstring_strdup(file_name); + merge_status = cf_merge_files_to_tempfile(this, global_capture_opts.temp_dir, &tmpname, 2, in_filenames, file_type, FALSE); + } else if (merge_dlg.mergeType() <= 0) { + /* prepend file */ + in_filenames[0] = qstring_strdup(file_name); + in_filenames[1] = g_strdup(capture_file_.capFile()->filename); + merge_status = cf_merge_files_to_tempfile(this, global_capture_opts.temp_dir, &tmpname, 2, in_filenames, file_type, TRUE); + } else { + /* append file */ + in_filenames[0] = g_strdup(capture_file_.capFile()->filename); + in_filenames[1] = qstring_strdup(file_name); + merge_status = cf_merge_files_to_tempfile(this, global_capture_opts.temp_dir, &tmpname, 2, in_filenames, file_type, TRUE); + } + + g_free(in_filenames[0]); + g_free(in_filenames[1]); + + if (merge_status != CF_OK) { + dfilter_free(rfcode); + g_free(tmpname); + continue; + } + + cf_close(capture_file_.capFile()); + + /* Try to open the merged capture file. */ + CaptureFile::globalCapFile()->window = this; + if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) { + /* We couldn't open it; fail. */ + CaptureFile::globalCapFile()->window = NULL; + dfilter_free(rfcode); + g_free(tmpname); + return; + } + + /* Attach the new read filter to "cf" ("cf_open()" succeeded, so + it closed the previous capture file, and thus destroyed any + previous read filter attached to "cf"). */ + cf_set_rfcode(CaptureFile::globalCapFile(), rfcode); + + switch (cf_read(CaptureFile::globalCapFile(), /*reloading=*/FALSE)) { + + case CF_READ_OK: + case CF_READ_ERROR: + /* Just because we got an error, that doesn't mean we were unable + to read any of the file; we handle what we could get from the + file. */ + break; + + case CF_READ_ABORTED: + /* The user bailed out of re-reading the capture file; the + capture file has been closed - just free the capture file name + string and return (without changing the last containing + directory). */ + g_free(tmpname); + return; + } + + /* Save the name of the containing directory specified in the path name. */ + mainApp->setLastOpenDirFromFilename(tmpname); + g_free(tmpname); + main_ui_->statusBar->showExpert(); + return; + } + +} + +void WiresharkMainWindow::importCaptureFile() { + ImportTextDialog import_dlg; + + QString before_what(tr(" before importing a capture")); + if (!testCaptureFileClose(before_what)) + return; + + import_dlg.exec(); + + if (import_dlg.result() != QDialog::Accepted) { + showWelcome(); + return; + } + + openCaptureFile(import_dlg.capfileName()); +} + +bool WiresharkMainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) { + QString file_name; + gboolean discard_comments; + + if (cf->is_tempfile) { + /* This is a temporary capture file, so saving it means saving + it to a permanent file. Prompt the user for a location + to which to save it. Don't require that the file format + support comments - if it's a temporary capture file, it's + probably pcapng, which supports comments and, if it's + not pcapng, let the user decide what they want to do + if they've added comments. */ + return saveAsCaptureFile(cf, FALSE, dont_reopen); + } else { + if (cf->unsaved_changes) { + cf_write_status_t status; + + /* This is not a temporary capture file, but it has unsaved + changes, so saving it means doing a "safe save" on top + of the existing file, in the same format - no UI needed + unless the file has comments and the file's format doesn't + support them. + + If the file has comments, does the file's format support them? + If not, ask the user whether they want to discard the comments + or choose a different format. */ + switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) { + + case SAVE: + /* The file can be saved in the specified format as is; + just drive on and save in the format they selected. */ + discard_comments = FALSE; + break; + + case SAVE_WITHOUT_COMMENTS: + /* The file can't be saved in the specified format as is, + but it can be saved without the comments, and the user + said "OK, discard the comments", so save it in the + format they specified without the comments. */ + discard_comments = TRUE; + break; + + case SAVE_IN_ANOTHER_FORMAT: + /* There are file formats in which we can save this that + support comments, and the user said not to delete the + comments. Do a "Save As" so the user can select + one of those formats and choose a file name. */ + return saveAsCaptureFile(cf, TRUE, dont_reopen); + + case CANCELLED: + /* The user said "forget it". Just return. */ + return false; + + default: + /* Squelch warnings that discard_comments is being used + uninitialized. */ + ws_assert_not_reached(); + return false; + } + + /* XXX - cf->filename might get freed out from under us, because + the code path through which cf_save_records() goes currently + closes the current file and then opens and reloads the saved file, + so make a copy and free it later. */ + file_name = cf->filename; + status = cf_save_records(cf, qUtf8Printable(file_name), cf->cd_t, cf->compression_type, + discard_comments, dont_reopen); + switch (status) { + + case CF_WRITE_OK: + /* The save succeeded; we're done. + If we discarded comments, redraw the packet list to reflect + any packets that no longer have comments. If we had unsaved + changes, redraw the packet list, because saving a time + shift zeroes out the frame.offset_shift field. + If we had a color filter based on frame data, recolor. */ + /* XXX: If there is a filter based on those, we want to force + a rescan with the current filter (we don't actually + need to redissect.) + */ + if (discard_comments || cf->unsaved_changes) { + if (color_filters_use_proto(proto_get_id_by_filter_name("frame"))) { + packet_list_->recolorPackets(); + } else { + packet_list_->redrawVisiblePackets(); + } + } + + cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes + updateForUnsavedChanges(); // we update the title bar to remove the * + break; + + case CF_WRITE_ERROR: + /* The write failed. + XXX - OK, what do we do now? Let them try a + "Save As", in case they want to try to save to a + different directory or file system? */ + break; + + case CF_WRITE_ABORTED: + /* The write was aborted; just drive on. */ + return false; + } + } + /* Otherwise just do nothing. */ + } + + return true; +} + +bool WiresharkMainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) { + QString file_name = ""; + int file_type; + wtap_compression_type compression_type; + cf_write_status_t status; + gchar *dirname; + gboolean discard_comments = FALSE; + + if (!cf) { + return false; + } + + for (;;) { + CaptureFileDialog save_as_dlg(this, cf); + + /* If the file has comments, does the format the user selected + support them? If not, ask the user whether they want to + discard the comments or choose a different format. */ + switch (save_as_dlg.saveAs(file_name, must_support_comments)) { + + case SAVE: + /* The file can be saved in the specified format as is; + just drive on and save in the format they selected. */ + discard_comments = FALSE; + break; + + case SAVE_WITHOUT_COMMENTS: + /* The file can't be saved in the specified format as is, + but it can be saved without the comments, and the user + said "OK, discard the comments", so save it in the + format they specified without the comments. */ + discard_comments = TRUE; + break; + + case SAVE_IN_ANOTHER_FORMAT: + /* There are file formats in which we can save this that + support comments, and the user said not to delete the + comments. The combo box of file formats has had the + formats that don't support comments trimmed from it, + so run the dialog again, to let the user decide + whether to save in one of those formats or give up. */ + must_support_comments = TRUE; + continue; + + case CANCELLED: + /* The user said "forget it". Just get rid of the dialog box + and return. */ + return false; + } + file_type = save_as_dlg.selectedFileType(); + if (file_type == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN) { + /* This "should not happen". */ + QMessageBox msg_dialog; + + msg_dialog.setIcon(QMessageBox::Critical); + msg_dialog.setText(tr("Unknown file type returned by merge dialog.")); + msg_dialog.setInformativeText(tr("Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues.")); + msg_dialog.exec(); + return false; + } + compression_type = save_as_dlg.compressionType(); + +#ifdef Q_OS_WIN + // the Windows dialog does not fixup extensions, do it manually here. + fileAddExtension(file_name, file_type, compression_type); +#endif // Q_OS_WIN + +//#ifndef _WIN32 +// /* If the file exists and it's user-immutable or not writable, +// ask the user whether they want to override that. */ +// if (!file_target_unwritable_ui(top_level, qUtf8Printable(file_name))) { +// /* They don't. Let them try another file name or cancel. */ +// continue; +// } +//#endif + + /* Attempt to save the file */ + status = cf_save_records(cf, qUtf8Printable(file_name), file_type, compression_type, + discard_comments, dont_reopen); + switch (status) { + + case CF_WRITE_OK: + /* The save succeeded; we're done. */ + /* Save the directory name for future file dialogs. */ + dirname = qstring_strdup(file_name); /* Overwrites cf_name */ + set_last_open_dir(get_dirname(dirname)); + g_free(dirname); + /* The save succeeded; we're done. + If we discarded comments, redraw the packet list to reflect + any packets that no longer have comments. If we had unsaved + changes, redraw the packet list, because saving a time + shift zeroes out the frame.offset_shift field. + If we had a color filter based on frame data, recolor. */ + /* XXX: If there is a filter based on those, we want to force + a rescan with the current filter (we don't actually + need to redissect.) + */ + if (discard_comments || cf->unsaved_changes) { + if (color_filters_use_proto(proto_get_id_by_filter_name("frame"))) { + packet_list_->recolorPackets(); + } else { + packet_list_->redrawVisiblePackets(); + } + } + + cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes + updateForUnsavedChanges(); // we update the title bar to remove the * + /* Add this filename to the list of recent files in the "Recent Files" submenu */ + add_menu_recent_capture_file(qUtf8Printable(file_name)); + return true; + + case CF_WRITE_ERROR: + /* The save failed; let the user try again. */ + continue; + + case CF_WRITE_ABORTED: + /* The user aborted the save; just return. */ + return false; + } + } + return true; +} + +void WiresharkMainWindow::exportSelectedPackets() { + QString file_name = ""; + int file_type; + wtap_compression_type compression_type; + packet_range_t range; + cf_write_status_t status; + gchar *dirname; + bool discard_comments = false; + + if (!capture_file_.capFile()) + return; + + /* Init the packet range */ + packet_range_init(&range, capture_file_.capFile()); + range.process_filtered = TRUE; + range.include_dependents = TRUE; + + QList rows = packet_list_->selectedRows(true); + + QStringList entries; + foreach (int row, rows) + entries << QString::number(row); + QString selRange = entries.join(","); + + for (;;) { + CaptureFileDialog esp_dlg(this, capture_file_.capFile()); + + /* If the file has comments, does the format the user selected + support them? If not, ask the user whether they want to + discard the comments or choose a different format. */ + switch (esp_dlg.exportSelectedPackets(file_name, &range, selRange)) { + + case SAVE: + /* The file can be saved in the specified format as is; + just drive on and save in the format they selected. */ + discard_comments = FALSE; + break; + + case SAVE_WITHOUT_COMMENTS: + /* The file can't be saved in the specified format as is, + but it can be saved without the comments, and the user + said "OK, discard the comments", so save it in the + format they specified without the comments. */ + discard_comments = TRUE; + break; + + case SAVE_IN_ANOTHER_FORMAT: + /* There are file formats in which we can save this that + support comments, and the user said not to delete the + comments. The combo box of file formats has had the + formats that don't support comments trimmed from it, + so run the dialog again, to let the user decide + whether to save in one of those formats or give up. */ + continue; + + case CANCELLED: + /* The user said "forget it". Just get rid of the dialog box + and return. */ + goto cleanup; + } + + /* + * Check that we're not going to save on top of the current + * capture file. + * We do it here so we catch all cases ... + * Unfortunately, the file requester gives us an absolute file + * name and the read file name may be relative (if supplied on + * the command line). From Joerg Mayer. + */ + if (files_identical(capture_file_.capFile()->filename, qUtf8Printable(file_name))) { + QMessageBox msg_box; + gchar *display_basename = g_filename_display_basename(qUtf8Printable(file_name)); + + msg_box.setIcon(QMessageBox::Critical); + msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename))); + msg_box.setInformativeText(tr("You cannot export packets to the current capture file.")); + msg_box.setStandardButtons(QMessageBox::Ok); + msg_box.setDefaultButton(QMessageBox::Ok); + msg_box.exec(); + g_free(display_basename); + continue; + } + + file_type = esp_dlg.selectedFileType(); + if (file_type == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN) { + /* This "should not happen". */ + QMessageBox msg_box; + + msg_box.setIcon(QMessageBox::Critical); + msg_box.setText(tr("Unknown file type returned by export dialog.")); + msg_box.setInformativeText(tr("Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues.")); + msg_box.exec(); + goto cleanup; + } + compression_type = esp_dlg.compressionType(); +#ifdef Q_OS_WIN + // the Windows dialog does not fixup extensions, do it manually here. + fileAddExtension(file_name, file_type, compression_type); +#endif // Q_OS_WIN + +//#ifndef _WIN32 +// /* If the file exists and it's user-immutable or not writable, +// ask the user whether they want to override that. */ +// if (!file_target_unwritable_ui(top_level, qUtf8Printable(file_name))) { +// /* They don't. Let them try another file name or cancel. */ +// continue; +// } +//#endif + + /* Attempt to save the file */ + status = cf_export_specified_packets(capture_file_.capFile(), qUtf8Printable(file_name), &range, file_type, compression_type); + switch (status) { + + case CF_WRITE_OK: + /* The save succeeded; we're done. */ + /* Save the directory name for future file dialogs. */ + dirname = qstring_strdup(file_name); /* Overwrites cf_name */ + set_last_open_dir(get_dirname(dirname)); + g_free(dirname); + /* If we discarded comments, redraw the packet list to reflect + any packets that no longer have comments. */ + /* XXX: Why? We're exporting some packets to a new file but not + changing our current capture file, that shouldn't change the + current packet list. */ + if (discard_comments) + packet_list_->redrawVisiblePackets(); + /* Add this filename to the list of recent files in the "Recent Files" submenu */ + add_menu_recent_capture_file(qUtf8Printable(file_name)); + goto cleanup; + + case CF_WRITE_ERROR: + /* The save failed; let the user try again. */ + continue; + + case CF_WRITE_ABORTED: + /* The user aborted the save; just return. */ + goto cleanup; + } + } + +cleanup: + packet_range_cleanup(&range); +} + +void WiresharkMainWindow::exportDissections(export_type_e export_type) { + capture_file *cf = capture_file_.capFile(); + g_return_if_fail(cf); + + QList rows = packet_list_->selectedRows(true); + + QStringList entries; + foreach (int row, rows) + entries << QString::number(row); + QString selRange = entries.join(","); + + ExportDissectionDialog *ed_dlg = new ExportDissectionDialog(this, cf, export_type, selRange); + ed_dlg->setWindowModality(Qt::ApplicationModal); + ed_dlg->setAttribute(Qt::WA_DeleteOnClose); + ed_dlg->show(); +} + +#ifdef Q_OS_WIN +/* + * Ensure that: + * + * If the file is to be compressed: + * + * if there is a set of extensions used by the file type to be used, + * the file name has one of those extensions followed by the extension + * for the compression type to be used; + * + * otherwise, the file name has the extension for the compression type + * to be used; + * + * otherwise: + * + * if there is a set of extensions used by the file type to be used, + * the file name has one of those extensions. + */ +void WiresharkMainWindow::fileAddExtension(QString &file_name, int file_type, wtap_compression_type compression_type) { + QString file_name_lower; + GSList *extensions_list; + const char *compressed_file_extension; + gboolean add_extension_for_file_type; + + /* Lower-case the file name, so the extension matching is case-insensitive. */ + file_name_lower = file_name.toLower(); + + /* Get a list of all extensions used for this file type; don't + include the ones with compression type extensions, as we + only want to check for the extension for the compression + type we'll be using. */ + extensions_list = wtap_get_file_extensions_list(file_type, FALSE); + + /* Get the extension for the compression type we'll be using; + NULL is returned if the type isn't supported or compression + is not being done. */ + compressed_file_extension = wtap_compression_type_extension(compression_type); + + if (extensions_list != NULL) { + GSList *extension; + + /* This file type has one or more extensions. + Start out assuming we need to add the default one. */ + add_extension_for_file_type = TRUE; + + /* OK, see if the file has one of those extensions, followed + by the appropriate compression type extension if it's to be + compressed. */ + for (extension = extensions_list; extension != NULL; + extension = g_slist_next(extension)) { + QString file_suffix = QString(".") + (char *)extension->data; + if (compressed_file_extension != NULL) + file_suffix += QString(".") + compressed_file_extension; + if (file_name_lower.endsWith(file_suffix)) { + /* + * The file name has one of the extensions for this file + * type, followed by a compression type extension if + * appropriate, so we don't need to add an extension for + * the file type or the compression type. + */ + add_extension_for_file_type = FALSE; + break; + } + } + } else { + /* We have no extensions for this file type. Just check + to see if we need to add an extension for the compressed + file type. + + Start out assuming we do. */ + add_extension_for_file_type = TRUE; + if (compressed_file_extension != NULL) { + QString file_suffix = QString(".") + compressed_file_extension; + if (file_name_lower.endsWith(file_suffix)) { + /* + * The file name has the appropriate compressed file extension, + * so we don't need to add an extension for the compression + * type. + */ + add_extension_for_file_type = FALSE; + } + } + } + + /* + * If we need to add an extension for the file type or compressed + * file type, do so. + */ + if (add_extension_for_file_type) { + if (wtap_default_file_extension(file_type) != NULL) { + /* This file type has a default extension; append it. */ + file_name += QString(".") + wtap_default_file_extension(file_type); + } + if (compression_type != WTAP_UNCOMPRESSED) { + /* + * The file is to be compressed, so append the extension for + * its compression type. + */ + file_name += QString(".") + compressed_file_extension; + } + } +} +#endif // Q_OS_WIN + +bool WiresharkMainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) { + bool capture_in_progress = false; + bool do_close_file = false; + + if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED) + return true; /* Already closed, nothing to do */ + + if (capture_file_.capFile()->read_lock) { + /* + * If the file is being redissected, we cannot stop the capture since + * that would crash and burn "cf_read", so stop early. Ideally all + * callers should be modified to check this condition and act + * accordingly (ignore action or queue it up), so print a warning. + */ + ws_warning("Refusing to close \"%s\" which is being read.", capture_file_.capFile()->filename); + return false; + } + +#ifdef HAVE_LIBPCAP + if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS || + capture_file_.capFile()->state == FILE_READ_PENDING) { + /* + * FILE_READ_IN_PROGRESS is true if we're reading a capture file + * *or* if we're doing a live capture. From the capture file itself we + * cannot differentiate the cases, so check the current capture session. + * FILE_READ_PENDING is only used for a live capture, but it doesn't + * hurt to check it here. + */ + capture_in_progress = captureSession()->state != CAPTURE_STOPPED; + } +#endif + + if (prefs.gui_ask_unsaved) { + if (cf_has_unsaved_data(capture_file_.capFile())) { + if (context == Update) { + // We're being called from the software update window; + // don't spawn yet another dialog. Just try again later. + // XXX: The WinSparkle dialogs *aren't* modal, and a user + // can bring Wireshark to the foreground, close/save the + // file, and then click "Install Update" again, but it + // seems like many users don't expect that (and also don't + // know that Help->Check for Updates... exist, only knowing + // about the automatic check.) See #17658 and duplicates. + // Maybe we *should* spawn the dialog? + return false; + } + + QMessageBox msg_dialog; + QString question; + QString infotext; + QPushButton *save_button; + QPushButton *discard_button; + + msg_dialog.setIcon(QMessageBox::Question); + msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS); + + /* This file has unsaved data or there's a capture in + progress; ask the user whether to save the data. */ + if (capture_in_progress && context != Restart) { + question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what); + infotext = tr("Your captured packets will be lost if you don't save them."); + } else if (capture_file_.capFile()->is_tempfile) { + if (context == Reload) { + // Reloading a tempfile will keep the packets, so this is not unsaved packets + question = tr("Do you want to save the changes you've made%1?").arg(before_what); + infotext = tr("Your changes will be lost if you don't save them."); + } else { + question = tr("Do you want to save the captured packets%1?").arg(before_what); + infotext = tr("Your captured packets will be lost if you don't save them."); + } + } else { + // No capture in progress and not a tempfile, so this is not unsaved packets + gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename); + question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what); + infotext = tr("Your changes will be lost if you don't save them."); + g_free(display_basename); + } + + msg_dialog.setText(question); + msg_dialog.setInformativeText(infotext); + + // XXX Text comes from ui/gtk/stock_icons.[ch] + // Note that the button roles differ from the GTK+ version. + // Cancel = RejectRole + // Save = AcceptRole + // Don't Save = DestructiveRole + msg_dialog.addButton(QMessageBox::Cancel); + + if (capture_in_progress) { + QString save_button_text; + if (context == Restart) { + save_button_text = tr("Save before Continue"); + } else { + save_button_text = tr("Stop and Save"); + } + save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole); + } else { + save_button = msg_dialog.addButton(QMessageBox::Save); + } + msg_dialog.setDefaultButton(save_button); + + QString discard_button_text; + if (capture_in_progress) { + switch (context) { + case Quit: + discard_button_text = tr("Stop and Quit &without Saving"); + break; + case Restart: + discard_button_text = tr("Continue &without Saving"); + break; + default: + discard_button_text = tr("Stop and Continue &without Saving"); + break; + } + } else { + switch (context) { + case Quit: + discard_button_text = tr("Quit &without Saving"); + break; + case Restart: + default: + discard_button_text = tr("Continue &without Saving"); + break; + } + } + discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole); + +#if defined(Q_OS_MAC) + /* + * In macOS, the "default button" is not necessarily the + * button that has the input focus; Enter/Return activates + * the default button, and the spacebar activates the button + * that has the input focus, and they might be different + * buttons. + * + * In a "do you want to save" dialog, for example, the + * "save" button is the default button, and the "don't + * save" button has the input focus, so you can press + * Enter/Return to save or space not to save (or Escape + * to dismiss the dialog). + * + * In Qt terms, this means "no auto-default", as auto-default + * makes the button with the input focus the default button, + * so that Enter/Return will activate it. + */ + QList buttons = msg_dialog.buttons(); + for (int i = 0; i < buttons.size(); ++i) { + QPushButton *button = static_cast(buttons.at(i));; + button->setAutoDefault(false); + } + + /* + * It also means that the "don't save" button should be the one + * initially given the focus. + */ + discard_button->setFocus(); +#endif + + msg_dialog.exec(); + /* According to the Qt doc: + * when using QMessageBox with custom buttons, exec() function returns an opaque value. + * + * Therefore we should use clickedButton() to determine which button was clicked. */ + + if (msg_dialog.clickedButton() == save_button) { +#ifdef HAVE_LIBPCAP + /* If there's a capture in progress, we have to stop the capture + and then do the save. */ + if (capture_in_progress) + captureStop(); +#endif + /* Save the file and close it */ + // XXX if no packets were captured, any unsaved comments set by + // the user are silently discarded because capFile() is null. + if (capture_file_.capFile() && saveCaptureFile(capture_file_.capFile(), true) == false) + return false; + do_close_file = true; + } else if (msg_dialog.clickedButton() == discard_button) { + /* Just close the file, discarding changes */ + do_close_file = true; + } else { + // cancelButton or some other unspecified button + return false; + } + } else { + /* Unchanged file or capturing with no packets */ + do_close_file = true; + } + } else { + /* User asked not to be bothered by those prompts, just close it. + XXX - should that apply only to saving temporary files? */ + do_close_file = true; + } + + /* + * Are we done with this file and should we close the file? + */ + if (do_close_file) { +#ifdef HAVE_LIBPCAP + /* If there's a capture in progress, we have to stop the capture + and then do the close. */ + if (capture_in_progress) + captureStop(); + else if (capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) { + /* + * When an offline capture is being read, mark it as aborted. + * cf_read will be responsible for actually closing the capture. + * + * We cannot just invoke cf_close here since cf_read is up in the + * call chain. (update_progress_dlg can end up processing the Quit + * event from the user which then ends up here.) + * See also the above "read_lock" check. + */ + capture_file_.capFile()->state = FILE_READ_ABORTED; + return true; + } +#endif + /* Clear MainWindow file name details */ + gbl_cur_main_window_->setMwFileName(""); + + /* captureStop() will close the file if not having any packets */ + if (capture_file_.capFile() && context != Restart && context != Reload) + // Don't really close if Restart or Reload + cf_close(capture_file_.capFile()); + } + + return true; /* File closed */ +} + +void WiresharkMainWindow::captureStop() { + stopCapture(); + + while (capture_file_.capFile() && (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS || + capture_file_.capFile()->state == FILE_READ_PENDING)) { + WiresharkApplication::processEvents(); + } +} + +void WiresharkMainWindow::findTextCodecs() { + const QList mibs = QTextCodec::availableMibs(); + QRegularExpression ibmRegExp("^IBM([0-9]+).*$"); + QRegularExpression iso8859RegExp("^ISO-8859-([0-9]+).*$"); + QRegularExpression windowsRegExp("^WINDOWS-([0-9]+).*$"); + QRegularExpressionMatch match; + for (int mib : mibs) { + QTextCodec *codec = QTextCodec::codecForMib(mib); + // QTextCodec::availableMibs() returns a list of hard-coded MIB + // numbers, it doesn't check if they are really available. ICU data may + // not have been compiled with support for all encodings. + if (!codec) { + continue; + } + + QString key = codec->name().toUpper(); + char rank; + + if (key.localeAwareCompare("IBM") < 0) { + rank = 1; + } else if ((match = ibmRegExp.match(key)).hasMatch()) { + rank = match.captured(1).size(); // Up to 5 + } else if (key.localeAwareCompare("ISO-8859-") < 0) { + rank = 6; + } else if ((match = iso8859RegExp.match(key)).hasMatch()) { + rank = 6 + match.captured(1).size(); // Up to 6 + 2 + } else if (key.localeAwareCompare("WINDOWS-") < 0) { + rank = 9; + } else if ((match = windowsRegExp.match(key)).hasMatch()) { + rank = 9 + match.captured(1).size(); // Up to 9 + 4 + } else { + rank = 14; + } + // This doesn't perfectly well order the IBM codecs because it's + // annoying to properly place IBM00858 and IBM00924 in the middle of + // code page numbers not zero padded to 5 digits. + // We could manipulate the key further to have more commonly used + // charsets earlier. IANA MIB ordering would be unxpected: + // https://www.iana.org/assignments/character-sets/character-sets.xml + // For data about use in HTTP (other protocols can be quite different): + // https://w3techs.com/technologies/overview/character_encoding + + key.prepend(char('0' + rank)); + // We use a map here because, due to backwards compatibility, + // the same QTextCodec may be returned for multiple MIBs, which + // happens for GBK/GB2312, EUC-KR/windows-949/UHC, and others. + text_codec_map_.insert(key, codec); + } +} + +void WiresharkMainWindow::initMainToolbarIcons() +{ + // Normally 16 px. Reflects current GTK+ behavior and other Windows apps. + int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); +#if !defined(Q_OS_WIN) + // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky. + // The macOS HIG specifies 32-pixel icons but they're a little too + // large IMHO. + icon_size = icon_size * 3 / 2; +#endif + main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size)); + + // Toolbar actions. The GNOME HIG says that we should have a menu icon for each + // toolbar item but that clutters up our menu. Set menu icons sparingly. + + main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start")); + main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop")); + main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart")); + main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options")); + + // Menu icons are disabled in main_window.ui for these items. + main_ui_->actionFileOpen->setIcon(StockIcon("document-open")); + main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save")); + main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close")); + main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload")); + + main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find")); + main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous")); + main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next")); + main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump")); + main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first")); + main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last")); + main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous")); + main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next")); +#if defined(Q_OS_MAC) + main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma)); + main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period)); +#endif + main_ui_->actionGoPreviousHistoryPacket->setIcon(StockIcon("go-previous")); + main_ui_->actionGoNextHistoryPacket->setIcon(StockIcon("go-next")); + main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last")); + + main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets")); + + QList zi_seq = main_ui_->actionViewZoomIn->shortcuts(); + zi_seq << QKeySequence(Qt::CTRL | Qt::Key_Equal); + main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in")); + main_ui_->actionViewZoomIn->setShortcuts(zi_seq); + main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out")); + main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original")); + main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns")); + + main_ui_->actionNewDisplayFilterExpression->setIcon(StockIcon("list-add")); +} + +void WiresharkMainWindow::initShowHideMainWidgets() +{ + if (show_hide_actions_) { + return; + } + + show_hide_actions_ = new QActionGroup(this); + QMap shmw_actions; + + show_hide_actions_->setExclusive(false); + shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar; + shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar; +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) + shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar; +#endif + shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar; + shmw_actions[main_ui_->actionViewPacketList] = packet_list_; + shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_; + shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_; + shmw_actions[main_ui_->actionViewPacketDiagram] = packet_diagram_; + + foreach(QAction *shmwa, shmw_actions.keys()) { + shmwa->setData(QVariant::fromValue(shmw_actions[shmwa])); + show_hide_actions_->addAction(shmwa); + } + + // Initial hide the Interface Toolbar submenu + main_ui_->menuInterfaceToolbars->menuAction()->setVisible(false); + + /* Initially hide the additional toolbars menus */ + main_ui_->menuAdditionalToolbars->menuAction()->setVisible(false); + + connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*))); +} + +void WiresharkMainWindow::initTimeDisplayFormatMenu() +{ + if (time_display_actions_) { + return; + } + + time_display_actions_ = new QActionGroup(this); + + td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD; + td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY; + td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE; + td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH; + td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceFirstCapturedPacket] = TS_RELATIVE; + td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA; + td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS; + td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD; + td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY; + td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC; + + foreach(QAction* tda, td_actions.keys()) { + tda->setData(QVariant::fromValue(td_actions[tda])); + time_display_actions_->addAction(tda); + } + + connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*))); +} + +void WiresharkMainWindow::initTimePrecisionFormatMenu() +{ + if (time_precision_actions_) { + return; + } + + time_precision_actions_ = new QActionGroup(this); + + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecision100Milliseconds] = TS_PREC_FIXED_100_MSEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecision10Milliseconds] = TS_PREC_FIXED_10_MSEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecision100Microseconds] = TS_PREC_FIXED_100_USEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecision10Microseconds] = TS_PREC_FIXED_10_USEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecision100Nanoseconds] = TS_PREC_FIXED_100_NSEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecision10Nanoseconds] = TS_PREC_FIXED_10_NSEC; + tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC; + + foreach(QAction* tpa, tp_actions.keys()) { + tpa->setData(QVariant::fromValue(tp_actions[tpa])); + time_precision_actions_->addAction(tpa); + } + + connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*))); +} + +// Menu items which will be disabled when we freeze() and whose state will +// be restored when we thaw(). Add to the list as needed. +void WiresharkMainWindow::initFreezeActions() +{ + QList freeze_actions = QList() + << main_ui_->actionFileClose + << main_ui_->actionViewReload + << main_ui_->actionEditMarkPacket + << main_ui_->actionEditMarkAllDisplayed + << main_ui_->actionEditUnmarkAllDisplayed + << main_ui_->actionEditIgnorePacket + << main_ui_->actionEditIgnoreAllDisplayed + << main_ui_->actionEditUnignoreAllDisplayed + << main_ui_->actionEditSetTimeReference + << main_ui_->actionEditUnsetAllTimeReferences; + + foreach(QAction *action, freeze_actions) { + freeze_actions_ << QPair(action, false); + } +} + +void WiresharkMainWindow::initConversationMenus() +{ + int i; + + QList cc_actions = QList() + << main_ui_->actionViewColorizeConversation1 << main_ui_->actionViewColorizeConversation2 + << main_ui_->actionViewColorizeConversation3 << main_ui_->actionViewColorizeConversation4 + << main_ui_->actionViewColorizeConversation5 << main_ui_->actionViewColorizeConversation6 + << main_ui_->actionViewColorizeConversation7 << main_ui_->actionViewColorizeConversation8 + << main_ui_->actionViewColorizeConversation9 << main_ui_->actionViewColorizeConversation10; + + for (GList *conv_filter_list_entry = packet_conv_filter_list; conv_filter_list_entry; conv_filter_list_entry = gxx_list_next(conv_filter_list_entry)) { + // Main menu items + conversation_filter_t* conv_filter = gxx_list_data(conversation_filter_t *, conv_filter_list_entry); + ConversationAction *conv_action = new ConversationAction(main_ui_->menuConversationFilter, conv_filter); + main_ui_->menuConversationFilter->addAction(conv_action); + + connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*))); + connect(conv_action, SIGNAL(triggered()), this, SLOT(applyConversationFilter()), Qt::QueuedConnection); + + // Packet list context menu items + packet_list_->conversationMenu()->addAction(conv_action); + + QMenu *submenu = packet_list_->colorizeMenu()->addMenu(conv_action->text()); + i = 1; + + foreach(QAction *cc_action, cc_actions) { + conv_action = new ConversationAction(submenu, conv_filter); + conv_action->setText(cc_action->text()); + conv_action->setIcon(cc_action->icon()); + conv_action->setColorNumber(i++); + submenu->addAction(conv_action); + connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*))); + connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered())); + } + + conv_action = new ConversationAction(submenu, conv_filter); + conv_action->setText(main_ui_->actionViewColorizeNewColoringRule->text()); + submenu->addAction(conv_action); + connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*))); + connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered())); + + // Proto tree conversation menu is filled in in ProtoTree::contextMenuEvent. + // We should probably do that here. + } + + // Proto tree colorization items + i = 1; + ColorizeAction *colorize_action; + foreach(QAction *cc_action, cc_actions) { + colorize_action = new ColorizeAction(proto_tree_->colorizeMenu()); + colorize_action->setText(cc_action->text()); + colorize_action->setIcon(cc_action->icon()); + colorize_action->setColorNumber(i++); + proto_tree_->colorizeMenu()->addAction(colorize_action); + connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray))); + connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered())); + } + + colorize_action = new ColorizeAction(proto_tree_->colorizeMenu()); + colorize_action->setText(main_ui_->actionViewColorizeNewColoringRule->text()); + proto_tree_->colorizeMenu()->addAction(colorize_action); + connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray))); + connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered())); +} + +bool WiresharkMainWindow::addExportObjectsMenuItem(const void *, void *value, void *userdata) +{ + register_eo_t *eo = (register_eo_t*)value; + WiresharkMainWindow *window = (WiresharkMainWindow*)userdata; + + ExportObjectAction *export_action = new ExportObjectAction(window->main_ui_->menuFileExportObjects, eo); + window->main_ui_->menuFileExportObjects->addAction(export_action); + + //initially disable until a file is loaded (then file signals will take over) + export_action->setEnabled(false); + + connect(&window->capture_file_, SIGNAL(captureEvent(CaptureEvent)), export_action, SLOT(captureFileEvent(CaptureEvent))); + connect(export_action, SIGNAL(triggered()), window, SLOT(applyExportObject())); + return FALSE; +} + +void WiresharkMainWindow::initExportObjectsMenus() +{ + eo_iterate_tables(addExportObjectsMenuItem, this); +} + +bool WiresharkMainWindow::addFollowStreamMenuItem(const void *key, void *value, void *userdata) +{ + const char *short_name = (const char*)key; + register_follow_t *follow = (register_follow_t*)value; + WiresharkMainWindow *window = (WiresharkMainWindow*)userdata; + + FollowStreamAction *follow_action = new FollowStreamAction(window->main_ui_->menuFollow, follow); + window->main_ui_->menuFollow->addAction(follow_action); + + follow_action->setEnabled(false); + + /* Special features for some of the built in follow types, like + * shortcuts and overriding the name. XXX: Should these go in + * FollowStreamAction, or should some of these (e.g. TCP and UDP) + * be registered in initFollowStreamMenus so that they can be + * on the top of the menu list too? + */ + if (g_strcmp0(short_name, "TCP") == 0) { + follow_action->setShortcut(Qt::CTRL | Qt::ALT | Qt::SHIFT | Qt::Key_T); + } else if (g_strcmp0(short_name, "UDP") == 0) { + follow_action->setShortcut(Qt::CTRL | Qt::ALT | Qt::SHIFT | Qt::Key_U); + } else if (g_strcmp0(short_name, "DCCP") == 0) { + /* XXX: Not sure this one is widely enough used to need a shortcut. */ + follow_action->setShortcut(Qt::CTRL | Qt::ALT | Qt::SHIFT | Qt::Key_E); + } else if (g_strcmp0(short_name, "TLS") == 0) { + follow_action->setShortcut(Qt::CTRL | Qt::ALT | Qt::SHIFT | Qt::Key_S); + } else if (g_strcmp0(short_name, "HTTP") == 0) { + follow_action->setShortcut(Qt::CTRL | Qt::ALT | Qt::SHIFT | Qt::Key_H); + } else if (g_strcmp0(short_name, "HTTP2") == 0) { + follow_action->setText(tr("HTTP/2 Stream")); + } else if (g_strcmp0(short_name, "SIP") == 0) { + follow_action->setText(tr("SIP Call")); + } + + connect(follow_action, &QAction::triggered, window, + [window, follow]() { window->openFollowStreamDialog(get_follow_proto_id(follow)); }, + Qt::QueuedConnection); + return FALSE; +} + +void WiresharkMainWindow::initFollowStreamMenus() +{ + /* This puts them all in the menus in alphabetical order. */ + follow_iterate_followers(addFollowStreamMenuItem, this); +} + +// Titlebar +void WiresharkMainWindow::setTitlebarForCaptureFile() +{ + if (capture_file_.capFile() && capture_file_.capFile()->filename) { + setWSWindowTitle(QString("[*]%1").arg(capture_file_.fileDisplayName())); + // + // XXX - on non-Mac platforms, put in the application + // name? Or do so only for temporary files? + // + if (!capture_file_.capFile()->is_tempfile) { + // + // Set the file path; that way, for macOS, it'll set the + // "proxy icon". + // + setWindowFilePath(capture_file_.filePath()); + } + setWindowModified(cf_has_unsaved_data(capture_file_.capFile())); + } else { + /* We have no capture file. */ + setWSWindowTitle(); + } +} + +QString WiresharkMainWindow::replaceWindowTitleVariables(QString title) +{ + title.replace("%P", get_profile_name()); + title.replace("%V", get_ws_vcs_version_info()); + + if (title.contains("%F")) { + // %F is file path of the capture file. + if (capture_file_.capFile()) { + // get_dirname() will overwrite the argument so make a copy first + char *filename = g_strdup(capture_file_.capFile()->filename); + QString file(get_dirname(filename)); + g_free(filename); +#ifndef _WIN32 + // Substitute HOME with ~ + QString homedir(g_getenv("HOME")); + if (!homedir.isEmpty()) { + homedir.remove(QRegularExpression("[/]+$")); + file.replace(homedir, "~"); + } +#endif + title.replace("%F", file); + } else { + // No file loaded, no folder name + title.remove("%F"); + } + } + + if (title.contains("%S")) { + // %S is a conditional separator (" - ") that only shows when surrounded by variables + // with values or static text. Remove repeating, leading and trailing separators. + title.replace(QRegularExpression("(%S)+"), "%S"); + title.remove(QRegularExpression("^%S|%S$")); +#ifdef __APPLE__ + // On macOS we separate with a unicode em dash + title.replace("%S", " " UTF8_EM_DASH " "); +#else + title.replace("%S", " - "); +#endif + } + + return title; +} + +void WiresharkMainWindow::setWSWindowTitle(QString title) +{ + if (title.isEmpty()) { + title = tr("The Wireshark Network Analyzer"); + } + + if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) { + QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title); + if (custom_title.length() > 0) { + title.prepend(QString("[%1] ").arg(custom_title)); + } + } + + if (prefs.gui_window_title && prefs.gui_window_title[0]) { + QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title); + if (custom_title.length() > 0) { +#ifdef __APPLE__ + // On macOS we separate the titles with a unicode em dash + title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title)); +#else + title.append(QString(" [%1]").arg(custom_title)); +#endif + } + } + + setWindowTitle(title); + setWindowFilePath(NULL); +} + +void WiresharkMainWindow::setTitlebarForCaptureInProgress() +{ + if (capture_file_.capFile()) { + setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile()))); + } else { + /* We have no capture in progress. */ + setWSWindowTitle(); + } +} + +// Menu state + +/* Enable or disable menu items based on whether you have a capture file + you've finished reading and, if you have one, whether it's been saved + and whether it could be saved except by copying the raw packet data. */ +void WiresharkMainWindow::setMenusForCaptureFile(bool force_disable) +{ + bool enable = true; + bool can_write = false; + bool can_save = false; + bool can_save_as = false; + + if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS || capture_file_.capFile()->state == FILE_READ_PENDING) { + /* We have no capture file or we're currently reading a file */ + enable = false; + } else { + /* We have a capture file. Can we write or save? */ + can_write = cf_can_write_with_wiretap(capture_file_.capFile()); + can_save = cf_can_save(capture_file_.capFile()); + can_save_as = cf_can_save_as(capture_file_.capFile()); + } + + main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(enable); + main_ui_->actionFileMerge->setEnabled(can_write); + main_ui_->actionFileClose->setEnabled(enable); + main_ui_->actionFileSave->setEnabled(can_save); + main_ui_->actionFileSaveAs->setEnabled(can_save_as); + main_ui_->actionStatisticsCaptureFileProperties->setEnabled(enable); + /* The Protocol Hierarchy statistics run on all the packets that + * pass the current filter, don't enable if a read or rescan is + * still in progress. + */ + main_ui_->actionStatisticsProtocolHierarchy->setEnabled(enable); + /* + * "Export Specified Packets..." should be available only if + * we can write the file out in at least one format. + */ + main_ui_->actionFileExportPackets->setEnabled(can_write); + + main_ui_->actionFileExportAsCArrays->setEnabled(enable); + main_ui_->actionFileExportAsCSV->setEnabled(enable); + main_ui_->actionFileExportAsPDML->setEnabled(enable); + main_ui_->actionFileExportAsPlainText->setEnabled(enable); + main_ui_->actionFileExportAsPSML->setEnabled(enable); + main_ui_->actionFileExportAsJSON->setEnabled(enable); + + main_ui_->actionFileExportPDU->setEnabled(enable); + main_ui_->actionFileStripHeaders->setEnabled(enable); + /* XXX: "Export TLS Session Keys..." should be enabled only if + * ssl_session_key_count() > 0. + */ + main_ui_->actionFileExportTLSSessionKeys->setEnabled(enable); + + foreach(QAction *eo_action, main_ui_->menuFileExportObjects->actions()) { + eo_action->setEnabled(enable); + } + + main_ui_->actionViewReload->setEnabled(enable); + +#ifdef HAVE_SOFTWARE_UPDATE + // We might want to enable or disable automatic checks here as well. + update_action_->setEnabled(!can_save); +#endif +} + +void WiresharkMainWindow::setMenusForCaptureInProgress(bool capture_in_progress) { + /* Either a capture was started or stopped; in either case, it's not + in the process of stopping, so allow quitting. */ + + main_ui_->actionFileOpen->setEnabled(!capture_in_progress); + main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress); + + main_ui_->actionFileExportAsCArrays->setEnabled(capture_in_progress); + main_ui_->actionFileExportAsCSV->setEnabled(capture_in_progress); + main_ui_->actionFileExportAsPDML->setEnabled(capture_in_progress); + main_ui_->actionFileExportAsPlainText->setEnabled(capture_in_progress); + main_ui_->actionFileExportAsPSML->setEnabled(capture_in_progress); + main_ui_->actionFileExportAsJSON->setEnabled(capture_in_progress); + + main_ui_->actionFileExportPDU->setEnabled(!capture_in_progress); + main_ui_->actionFileStripHeaders->setEnabled(!capture_in_progress); + main_ui_->actionFileExportTLSSessionKeys->setEnabled(capture_in_progress); + + foreach(QAction *eo_action, main_ui_->menuFileExportObjects->actions()) { + eo_action->setEnabled(capture_in_progress); + } + + main_ui_->menuFileSet->setEnabled(!capture_in_progress); + main_ui_->actionFileQuit->setEnabled(true); +#ifdef HAVE_SOFTWARE_UPDATE + // We might want to enable or disable automatic checks here as well. + update_action_->setEnabled(!capture_in_progress); +#endif + + main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress); + + // XXX Fix packet list heading menu sensitivity + // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending", + // !capture_in_progress); + // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending", + // !capture_in_progress); + // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting", + // !capture_in_progress); + +#ifdef HAVE_LIBPCAP + main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress); + main_ui_->actionCaptureStart->setEnabled(!capture_in_progress); + main_ui_->actionCaptureStart->setChecked(capture_in_progress); + main_ui_->actionCaptureStop->setEnabled(capture_in_progress); + main_ui_->actionCaptureRestart->setEnabled(capture_in_progress); + main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress); +#endif /* HAVE_LIBPCAP */ + +} + +void WiresharkMainWindow::setMenusForCaptureStopping() { + main_ui_->actionFileQuit->setEnabled(false); +#ifdef HAVE_SOFTWARE_UPDATE + update_action_->setEnabled(false); +#endif + main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false); +#ifdef HAVE_LIBPCAP + main_ui_->actionCaptureStart->setChecked(false); + main_ui_->actionCaptureStop->setEnabled(false); + main_ui_->actionCaptureRestart->setEnabled(false); +#endif /* HAVE_LIBPCAP */ +} + +void WiresharkMainWindow::setForCapturedPackets(bool have_captured_packets) +{ + main_ui_->actionFilePrint->setEnabled(have_captured_packets); + +// set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print", +// have_captured_packets); + + main_ui_->actionEditFindPacket->setEnabled(have_captured_packets); + main_ui_->actionEditFindNext->setEnabled(have_captured_packets); + main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets); + + main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets); + main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets); + main_ui_->actionGoNextPacket->setEnabled(have_captured_packets); + main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets); + main_ui_->actionGoLastPacket->setEnabled(have_captured_packets); + main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets); + main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets); + + main_ui_->actionViewZoomIn->setEnabled(have_captured_packets); + main_ui_->actionViewZoomOut->setEnabled(have_captured_packets); + main_ui_->actionViewNormalSize->setEnabled(have_captured_packets); + main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets); + + main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets); + main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets); + main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets); +} + +void WiresharkMainWindow::setMenusForFileSet(bool enable_list_files) { + bool enable_next = fileset_get_next() != NULL && enable_list_files; + bool enable_prev = fileset_get_previous() != NULL && enable_list_files; + + main_ui_->actionFileSetListFiles->setEnabled(enable_list_files); + main_ui_->actionFileSetNextFile->setEnabled(enable_next); + main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev); +} + +void WiresharkMainWindow::setWindowIcon(const QIcon &icon) { + mainApp->setWindowIcon(icon); + QMainWindow::setWindowIcon(icon); +} + +void WiresharkMainWindow::updateForUnsavedChanges() { + setTitlebarForCaptureFile(); + setMenusForCaptureFile(); +} + +void WiresharkMainWindow::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + main_ui_->retranslateUi(this); + // make sure that the "Clear Menu" item is retranslated + mainApp->emitAppSignal(WiresharkApplication::RecentCapturesChanged); + setTitlebarForCaptureFile(); + break; + case QEvent::LocaleChange: { + QString locale = QLocale::system().name(); + locale.truncate(locale.lastIndexOf('_')); + mainApp->loadLanguage(locale); + } + break; + case QEvent::WindowStateChange: + main_ui_->actionViewFullScreen->setChecked(this->isFullScreen()); + break; + default: + break; + } + } + QMainWindow::changeEvent(event); +} + +/* Update main window items based on whether there's a capture in progress. */ +void WiresharkMainWindow::setForCaptureInProgress(bool capture_in_progress, bool handle_toolbars, GArray *ifaces) +{ + setMenusForCaptureInProgress(capture_in_progress); + +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) + wireless_frame_->setCaptureInProgress(capture_in_progress); +#endif + +#ifdef HAVE_LIBPCAP + packet_list_->setCaptureInProgress(capture_in_progress, main_ui_->actionGoAutoScroll->isChecked()); + +// set_capture_if_dialog_for_capture_in_progress(capture_in_progress); +#endif + + if (handle_toolbars) { + QList toolbars = findChildren(); + foreach(InterfaceToolbar *toolbar, toolbars) { + if (capture_in_progress) { + toolbar->startCapture(ifaces); + } else { + toolbar->stopCapture(); + } + } + } +} + +void WiresharkMainWindow::addMenuActions(QList &actions, int menu_group) +{ + foreach(QAction *action, actions) { + switch (menu_group) { + case REGISTER_PACKET_ANALYZE_GROUP_UNSORTED: + case REGISTER_PACKET_STAT_GROUP_UNSORTED: + main_ui_->menuStatistics->insertAction( + main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED, + action); + break; + case REGISTER_STAT_GROUP_RESPONSE_TIME: + main_ui_->menuServiceResponseTime->addAction(action); + break; + case REGISTER_STAT_GROUP_RSERPOOL: + main_ui_->menuRSerPool->addAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY: + main_ui_->menuTelephony->addAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY_ANSI: + main_ui_->menuANSI->addAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY_GSM: + main_ui_->menuGSM->addAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY_LTE: + main_ui_->menuLTE->addAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY_MTP3: + main_ui_->menuMTP3->addAction(action); + break; + case REGISTER_TOOLS_GROUP_UNSORTED: + { + // Allow the creation of submenus. Mimics the behavor of + // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar + // and GtkUIManager. + // + // For now we limit the insanity to the "Tools" menu. + QStringList menu_path = action->text().split('/'); + QMenu *cur_menu = main_ui_->menuTools; + while (menu_path.length() > 1) { + QString menu_title = menu_path.takeFirst(); + QMenu *submenu = cur_menu->findChild(menu_title.toLower(), Qt::FindDirectChildrenOnly); + if (!submenu) { + submenu = cur_menu->addMenu(menu_title); + submenu->setObjectName(menu_title.toLower()); + } + cur_menu = submenu; + } + action->setText(menu_path.last()); + cur_menu->addAction(action); + break; + } + default: + // Skip log items. + return; + } + + // Connect each action type to its corresponding slot. We to + // distinguish various types of actions. Setting their objectName + // seems to work OK. + if (action->objectName() == TapParameterDialog::actionName()) { + connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog())); + } else if (action->objectName() == FunnelStatistics::actionName()) { + connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered())); + } + } +} +void WiresharkMainWindow::removeMenuActions(QList &actions, int menu_group) +{ + foreach(QAction *action, actions) { + switch (menu_group) { + case REGISTER_PACKET_ANALYZE_GROUP_UNSORTED: + case REGISTER_PACKET_STAT_GROUP_UNSORTED: + main_ui_->menuStatistics->removeAction(action); + break; + case REGISTER_STAT_GROUP_RESPONSE_TIME: + main_ui_->menuServiceResponseTime->removeAction(action); + break; + case REGISTER_STAT_GROUP_RSERPOOL: + main_ui_->menuRSerPool->removeAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY: + main_ui_->menuTelephony->removeAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY_ANSI: + main_ui_->menuANSI->removeAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY_GSM: + main_ui_->menuGSM->removeAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY_LTE: + main_ui_->menuLTE->removeAction(action); + break; + case REGISTER_STAT_GROUP_TELEPHONY_MTP3: + main_ui_->menuMTP3->removeAction(action); + break; + case REGISTER_TOOLS_GROUP_UNSORTED: + { + // Allow removal of submenus. + // For now we limit the insanity to the "Tools" menu. + QStringList menu_path = action->text().split('/'); + QMenu *cur_menu = main_ui_->menuTools; + while (menu_path.length() > 1) { + QString menu_title = menu_path.takeFirst(); + QMenu *submenu = cur_menu->findChild(menu_title.toLower(), Qt::FindDirectChildrenOnly); + cur_menu = submenu; + } + cur_menu->removeAction(action); + // Remove empty submenus. + while (cur_menu != main_ui_->menuTools) { + QMenu *empty_menu = (cur_menu->isEmpty() ? cur_menu : NULL); + cur_menu = dynamic_cast(cur_menu->parent()); + delete empty_menu; + } + break; + } + default: +// qDebug() << "FIX: Remove" << action->text() << "from the menu"; + break; + } + } +} + +void WiresharkMainWindow::addDynamicMenus() +{ + // Manual additions + mainApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary); + mainApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics); + mainApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics); + mainApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph); + mainApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary); + mainApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows); + + // Fill in each menu + foreach(register_stat_group_t menu_group, menu_groups_) { + QListactions = mainApp->dynamicMenuGroupItems(menu_group); + addMenuActions(actions, menu_group); + } + + // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728 + // We've added a placeholder in order to make sure some menus are visible. + // Hide them as needed. + if (mainApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) { + main_ui_->actionTelephonyANSIPlaceholder->setVisible(false); + } + if (mainApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) { + main_ui_->actionTelephonyGSMPlaceholder->setVisible(false); + } + if (mainApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) { + main_ui_->actionTelephonyLTEPlaceholder->setVisible(false); + } + if (mainApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) { + main_ui_->actionTelephonyMTP3Placeholder->setVisible(false); + } +} + +void WiresharkMainWindow::reloadDynamicMenus() +{ + foreach(register_stat_group_t menu_group, menu_groups_) { + QListactions = mainApp->removedMenuGroupItems(menu_group); + removeMenuActions(actions, menu_group); + + actions = mainApp->addedMenuGroupItems(menu_group); + addMenuActions(actions, menu_group); + } + + mainApp->clearAddedMenuGroupItems(); + mainApp->clearRemovedMenuGroupItems(); +} + +void WiresharkMainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth) +{ + QAction * itemAction = Q_NULLPTR; + ext_menubar_t * item = Q_NULLPTR; + GList * children = Q_NULLPTR; + + /* There must exists an xpath parent */ + Q_ASSERT(subMenu != NULL); + + /* If the depth counter exceeds, something must have gone wrong */ + Q_ASSERT(depth < EXT_MENUBAR_MAX_DEPTH); + + children = menu->children; + /* Iterate the child entries */ + while (children && children->data) { + item = gxx_list_data(ext_menubar_t *, children); + + if (item->type == EXT_MENUBAR_MENU) { + /* Handle Submenu entry */ + this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++); + } else if (item->type == EXT_MENUBAR_SEPARATOR) { + subMenu->addSeparator(); + } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) { + itemAction = subMenu->addAction(item->name); + itemAction->setData(QVariant::fromValue(static_cast(item))); + itemAction->setText(item->label); + connect(itemAction, &QAction::triggered, this, &WiresharkMainWindow::externalMenuItemTriggered); + } + + /* Iterate Loop */ + children = gxx_list_next(children); + } +} + +QMenu * WiresharkMainWindow::searchSubMenu(QString objectName) +{ + QList lst; + + if (objectName.length() > 0) { + QString searchName = QString("menu") + objectName; + + lst = main_ui_->menuBar->findChildren(); + foreach(QMenu* m, lst) { + if (QString::compare(m->objectName(), searchName) == 0) + return m; + } + } + + return 0; +} + +void WiresharkMainWindow::addPluginIFStructures() +{ + GList *user_menu = ext_menubar_get_entries(); + + while (user_menu && user_menu->data) { + QMenu *subMenu = Q_NULLPTR; + ext_menu_t *menu = gxx_list_data(ext_menu_t *, user_menu); + + /* On this level only menu items should exist. Not doing an assert here, + * as it could be an honest mistake */ + if (menu->type != EXT_MENUBAR_MENU) { + user_menu = gxx_list_next(user_menu); + continue; + } + + /* Create main submenu and add it to the menubar */ + if (menu->parent_menu) { + QMenu *sortUnderneath = searchSubMenu(QString(menu->parent_menu)); + if (sortUnderneath) + subMenu = sortUnderneath->addMenu(menu->label); + } + + if (!subMenu) + subMenu = main_ui_->menuBar->addMenu(menu->label); + + /* This will generate the action structure for each menu. It is recursive, + * therefore a sub-routine, and we have a depth counter to prevent endless loops. */ + this->externalMenuHelper(menu, subMenu, 0); + + /* Iterate Loop */ + user_menu = gxx_list_next(user_menu); + } + + int cntToolbars = 0; + + QMenu *tbMenu = main_ui_->menuAdditionalToolbars; + GList *if_toolbars = ext_toolbar_get_entries(); + while (if_toolbars && if_toolbars->data) { + ext_toolbar_t *toolbar = gxx_list_data(ext_toolbar_t*, if_toolbars); + + if (toolbar->type != EXT_TOOLBAR_BAR) { + if_toolbars = gxx_list_next(if_toolbars); + continue; + } + + bool visible = g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, reinterpret_cast(strcmp)) ? true : false; + + AdditionalToolBar *ifToolBar = AdditionalToolBar::create(this, toolbar); + + if (ifToolBar) { + ifToolBar->setVisible(visible); + + QAction *iftbAction = new QAction(QString(toolbar->name), this); + iftbAction->setToolTip(toolbar->tooltip); + iftbAction->setEnabled(true); + iftbAction->setCheckable(true); + iftbAction->setChecked(visible); + iftbAction->setToolTip(tr("Show or hide the toolbar")); + iftbAction->setData(VariantPointer::asQVariant(toolbar)); + + QAction *before = Q_NULLPTR; + + foreach(QAction *action, tbMenu->actions()) { + /* Ensure we add the menu entries in sorted order */ + if (action->text().compare(toolbar->name, Qt::CaseInsensitive) > 0) { + before = action; + break; + } + } + + tbMenu->insertAction(before, iftbAction); + + addToolBar(Qt::TopToolBarArea, ifToolBar); + insertToolBarBreak(ifToolBar); + + if (show_hide_actions_) + show_hide_actions_->addAction(iftbAction); + + cntToolbars++; + } + + if_toolbars = gxx_list_next(if_toolbars); + } + + if (cntToolbars) + tbMenu->menuAction()->setVisible(true); +} + +void WiresharkMainWindow::removeAdditionalToolbar(QString toolbarName) +{ + if (toolbarName.length() == 0) + return; + + QList toolbars = findChildren(); + foreach(QToolBar *tb, toolbars) { + AdditionalToolBar *ifToolBar = dynamic_cast(tb); + + if (ifToolBar && ifToolBar->menuName().compare(toolbarName)) { + GList *entry = g_list_find_custom(recent.gui_additional_toolbars, qUtf8Printable(ifToolBar->menuName()), reinterpret_cast(strcmp)); + if (entry) { + recent.gui_additional_toolbars = g_list_remove(recent.gui_additional_toolbars, entry->data); + } + QList actions = main_ui_->menuAdditionalToolbars->actions(); + foreach(QAction *action, actions) { + ext_toolbar_t *item = VariantPointer::asPtr(action->data()); + if (item && ifToolBar->menuName().compare(item->name)) { + if (show_hide_actions_) + show_hide_actions_->removeAction(action); + main_ui_->menuAdditionalToolbars->removeAction(action); + } + } + break; + } + } + +} + +QString WiresharkMainWindow::getMwFileName() +{ + return mwFileName_; +} + +void WiresharkMainWindow::setMwFileName(QString fileName) +{ + mwFileName_ = fileName; + return; +} + +// Finds rtp id for selected stream and adds it to stream_ids +// If reverse is set, tries to find reverse stream and other streams +// bundled on the same RTP session too +// Return error string if error happens +// +// Note: Caller must free each returned rtpstream_info_t +QString WiresharkMainWindow::findRtpStreams(QVector *stream_ids, bool reverse) +{ + rtpstream_tapinfo_t tapinfo; + rtpstream_id_t *new_id; + const gchar filter_text[] = "rtp && rtp.version == 2 && rtp.ssrc && (ip || ipv6)"; + dfilter_t *sfcode; + df_error_t *df_err = NULL; + + /* Try to get the hfid for "rtp.ssrc". */ + int hfid_rtp_ssrc = proto_registrar_get_id_byname("rtp.ssrc"); + if (hfid_rtp_ssrc == -1) { + return tr("There is no \"rtp.ssrc\" field in this version of Wireshark."); + } + + /* Try to compile the filter. */ + if (!dfilter_compile(filter_text, &sfcode, &df_err)) { + QString err = QString(df_err->msg); + df_error_free(&df_err); + return err; + } + + if (!capture_file_.capFile() || !capture_file_.capFile()->current_frame) close(); + + if (!cf_read_current_record(capture_file_.capFile())) close(); + + frame_data *fdata = capture_file_.capFile()->current_frame; + + epan_dissect_t edt; + + epan_dissect_init(&edt, capture_file_.capFile()->epan, true, false); + epan_dissect_prime_with_dfilter(&edt, sfcode); + epan_dissect_prime_with_hfid(&edt, hfid_rtp_ssrc); + epan_dissect_run(&edt, capture_file_.capFile()->cd_t, + &capture_file_.capFile()->rec, + frame_tvbuff_new_buffer( + &capture_file_.capFile()->provider, fdata, + &capture_file_.capFile()->buf), + fdata, NULL); + + /* + * Packet must be an RTPv2 packet with an SSRC; we use the filter to + * check. + */ + if (!dfilter_apply_edt(sfcode, &edt)) { + epan_dissect_cleanup(&edt); + dfilter_free(sfcode); + return tr("Please select an RTPv2 packet with an SSRC value"); + } + + dfilter_free(sfcode); + + if (!reverse) { + // If we only want streams that match the SSRC in this frame, we + // can just allocate an RTP stream ID directly instead of having + // to redissect all the other packets. + + /* We need the SSRC value of the current frame; try to get it. */ + GPtrArray *gp = proto_get_finfo_ptr_array(edt.tree, hfid_rtp_ssrc); + if (gp == NULL || gp->len == 0) { + /* XXX - should not happen, as the filter includes rtp.ssrc */ + epan_dissect_cleanup(&edt); + return tr("SSRC value not found."); + } + + /* + * OK, we have the SSRC value(s), so we can proceed. + * (Try to handle the unlikely case of a frame with more than one + * SSRC; perhaps a DVD-S2 Baseband frame? Does that even work + * properly?) + */ + for (unsigned i = 0; i < gp->len; i++) { + new_id = g_new0(rtpstream_id_t, 1); + rtpstream_id_copy_pinfo(&(edt.pi), new_id, false); + new_id->ssrc = fvalue_get_uinteger(((field_info *)gp->pdata[i])->value); + *stream_ids << new_id; + } + } else { + // If we want to find all SSRCs with the same RTP session as this + // frame, then we have to redissect all packets. + + /* Register the tap listener */ + memset(&tapinfo, 0, sizeof(rtpstream_tapinfo_t)); + tapinfo.tap_data = this; + tapinfo.mode = TAP_ANALYSE; + + /* Scan for RTP streams (redissect all packets) */ + rtpstream_scan(&tapinfo, capture_file_.capFile(), Q_NULLPTR); + + for (GList *strinfo_list = g_list_first(tapinfo.strinfo_list); strinfo_list; strinfo_list = gxx_list_next(strinfo_list)) { + rtpstream_info_t * strinfo = gxx_list_data(rtpstream_info_t*, strinfo_list); + // We want any RTP stream ID that matches the address and ports. + // This could mean more than one in the forward direction, if + // e.g., BUNDLE is used (RFC 9143). + if (rtpstream_id_equal_pinfo(&(strinfo->id), &(edt.pi), false) || + rtpstream_id_equal_pinfo(&(strinfo->id), &(edt.pi), true)) { + new_id = g_new0(rtpstream_id_t, 1); + rtpstream_id_copy(&(strinfo->id), new_id); + *stream_ids << new_id; + } + } + rtpstream_reset_cb(&tapinfo); + } + + epan_dissect_cleanup(&edt); + + return NULL; +} + +void WiresharkMainWindow::openTLSKeylogDialog() +{ + // Have a single instance of the dialog at any one time. + if (!tlskeylog_dialog_) { + tlskeylog_dialog_ = new TLSKeylogDialog(*this); + tlskeylog_dialog_->setAttribute(Qt::WA_DeleteOnClose); + } + + if (tlskeylog_dialog_->isMinimized()) { + tlskeylog_dialog_->showNormal(); + } + else { + tlskeylog_dialog_->show(); + } + tlskeylog_dialog_->raise(); + tlskeylog_dialog_->activateWindow(); +} diff --git a/ui/qt/wireshark_main_window.h b/ui/qt/wireshark_main_window.h new file mode 100644 index 00000000..781a1b28 --- /dev/null +++ b/ui/qt/wireshark_main_window.h @@ -0,0 +1,529 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WIRESHARK_MAIN_WINDOW_H +#define WIRESHARK_MAIN_WINDOW_H + +/** @defgroup main_window_group Main window + * The main window has the following submodules: + @dot + digraph main_dependencies { + node [shape=record, fontname=Helvetica, fontsize=10]; + main [ label="main window" URL="\ref main.h"]; + menu [ label="menubar" URL="\ref menus.h"]; + toolbar [ label="toolbar" URL="\ref main_toolbar.h"]; + packet_list [ label="packet list pane" URL="\ref packet_list.h"]; + proto_draw [ label="packet details & bytes panes" URL="\ref main_proto_draw.h"]; + recent [ label="recent user settings" URL="\ref recent.h"]; + main -> menu [ arrowhead="open", style="solid" ]; + main -> toolbar [ arrowhead="open", style="solid" ]; + main -> packet_list [ arrowhead="open", style="solid" ]; + main -> proto_draw [ arrowhead="open", style="solid" ]; + main -> recent [ arrowhead="open", style="solid" ]; + } + @enddot + */ + +/** @file + * The Wireshark main window + * @ingroup main_window_group + * @ingroup windows_group + */ + +#include + +#include + +#include + +#include "file.h" + +#include "ui/ws_ui_util.h" +#include "ui/iface_toolbar.h" + +#include +#include + +#ifdef HAVE_LIBPCAP +#include "capture_opts.h" +#endif +#include + +#include +#include +#include + +#ifdef _WIN32 +# include +#else +# include +#endif + +#include "capture_file.h" +#include "capture_file_dialog.h" +#include "capture_file_properties_dialog.h" +#include +#include +#include "main_window.h" +#include "rtp_stream_dialog.h" +#include "rtp_analysis_dialog.h" +#include "tlskeylog_launcher_dialog.h" + +class AccordionFrame; +class ByteViewTab; +class CaptureOptionsDialog; +class PrintDialog; +class FileSetDialog; +class FilterDialog; +class FunnelStatistics; +class WelcomePage; +class PacketCommentDialog; +class PacketDiagram; +class PacketList; +class ProtoTree; +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) +class WirelessFrame; +#endif +class FilterExpressionToolBar; +class WiresharkApplication; + +class QAction; +class QActionGroup; + +namespace Ui { + class WiresharkMainWindow; +} + +Q_DECLARE_METATYPE(ts_type) +Q_DECLARE_METATYPE(ts_precision) + +class WiresharkMainWindow : public MainWindow +{ + Q_OBJECT + +public: + explicit WiresharkMainWindow(QWidget *parent = nullptr); + ~WiresharkMainWindow(); + +#ifdef HAVE_LIBPCAP + capture_session *captureSession() { return &cap_session_; } + info_data_t *captureInfoData() { return &info_data_; } +#endif + + virtual QMenu *createPopupMenu(); + + CaptureFile *captureFile() { return &capture_file_; } + + void removeAdditionalToolbar(QString toolbarName); + + void addInterfaceToolbar(const iface_toolbar *toolbar_entry); + void removeInterfaceToolbar(const gchar *menu_title); + + QString getMwFileName(); + void setMwFileName(QString fileName); + +protected: + virtual bool eventFilter(QObject *obj, QEvent *event); + virtual bool event(QEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void closeEvent(QCloseEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void changeEvent(QEvent* event); + +private: + // XXX Move to FilterUtils + enum MatchSelected { + MatchSelectedReplace, + MatchSelectedAnd, + MatchSelectedOr, + MatchSelectedNot, + MatchSelectedAndNot, + MatchSelectedOrNot + }; + + enum FileCloseContext { + Default, + Quit, + Restart, + Reload, + Update + }; + + Ui::WiresharkMainWindow *main_ui_; + CaptureFile capture_file_; + QFont mono_font_; + QMap text_codec_map_; +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) + WirelessFrame *wireless_frame_; +#endif + QWidget *previous_focus_; + FileSetDialog *file_set_dialog_; + QActionGroup *show_hide_actions_; + QActionGroup *time_display_actions_; + QActionGroup *time_precision_actions_; + FunnelStatistics *funnel_statistics_; + QList > freeze_actions_; + QPointer freeze_focus_; + QMap td_actions; + QMap tp_actions; + bool was_maximized_; + + /* the following values are maintained so that the capture file name and status + is available when there is no cf structure available */ + QString mwFileName_; + + bool capture_stopping_; + bool capture_filter_valid_; +#ifdef HAVE_LIBPCAP + capture_session cap_session_; + CaptureOptionsDialog *capture_options_dialog_; + info_data_t info_data_; +#endif + +#if defined(Q_OS_MAC) + QMenu *dock_menu_; +#endif + +#ifdef HAVE_SOFTWARE_UPDATE + QAction *update_action_; +#endif + + QPoint dragStartPosition; + + QPointer tlskeylog_dialog_; + + void freeze(); + void thaw(); + + void mergeCaptureFile(); + void importCaptureFile(); + bool saveCaptureFile(capture_file *cf, bool dont_reopen); + bool saveAsCaptureFile(capture_file *cf, bool must_support_comments = false, bool dont_reopen = false); + void exportSelectedPackets(); + void exportDissections(export_type_e export_type); + +#ifdef Q_OS_WIN + void fileAddExtension(QString &file_name, int file_type, wtap_compression_type compression_type); +#endif // Q_OS_WIN + bool testCaptureFileClose(QString before_what, FileCloseContext context = Default); + void captureStop(); + + void findTextCodecs(); + + void initMainToolbarIcons(); + void initShowHideMainWidgets(); + void initTimeDisplayFormatMenu(); + void initTimePrecisionFormatMenu(); + void initFreezeActions(); + + void setTitlebarForCaptureInProgress(); + void setMenusForCaptureFile(bool force_disable = false); + void setMenusForCaptureInProgress(bool capture_in_progress = false); + void setMenusForCaptureStopping(); + void setForCapturedPackets(bool have_captured_packets); + void setMenusForFileSet(bool enable_list_files); + void setWindowIcon(const QIcon &icon); + QString replaceWindowTitleVariables(QString title); + void updateStyleSheet(); + + void externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth); + + void setForCaptureInProgress(bool capture_in_progress = false, bool handle_toolbars = false, GArray *ifaces = NULL); + QMenu* findOrAddMenu(QMenu *parent_menu, QString& menu_text); + + void captureFileReadStarted(const QString &action); + + void addMenuActions(QList &actions, int menu_group); + void removeMenuActions(QList &actions, int menu_group); + void goToConversationFrame(bool go_next); + void colorizeWithFilter(QByteArray filter, int color_number = -1); + +signals: + void setDissectedCaptureFile(capture_file *cf); + void closePacketDialogs(); + void reloadFields(); + void packetInfoChanged(struct _packet_info *pinfo); + void fieldFilterChanged(const QByteArray field_filter); + + void fieldHighlight(FieldInformation *); + + void captureActive(int); + void selectRtpStream(rtpstream_id_t *id); + void deselectRtpStream(rtpstream_id_t *id); + +#ifdef HAVE_LIBPCAP + void showExtcapOptions(QString &device_name, bool startCaptureOnClose); +#endif + +public slots: + // in main_window_slots.cpp + /** + * Open a capture file. + * @param cf_path Path to the file. + * @param display_filter Display filter to apply. May be empty. + * @param type File type. + * @param is_tempfile TRUE/FALSE. + * @return True on success, false on failure. + */ + // XXX We might want to return a cf_read_status_t or a CaptureFile. + bool openCaptureFile(QString cf_path, QString display_filter, unsigned int type, gboolean is_tempfile = FALSE); + bool openCaptureFile(QString cf_path = QString(), QString display_filter = QString()) { return openCaptureFile(cf_path, display_filter, WTAP_TYPE_AUTO); } + void filterPackets(QString new_filter = QString(), bool force = false); + void updateForUnsavedChanges(); + void layoutToolbars(); + void updatePreferenceActions(); + void updateRecentActions(); + + void setTitlebarForCaptureFile(); + void setWSWindowTitle(QString title = QString()); + + void showCaptureOptionsDialog(); + +#ifdef HAVE_LIBPCAP + void captureCapturePrepared(capture_session *); + void captureCaptureUpdateStarted(capture_session *); + void captureCaptureUpdateFinished(capture_session *); + void captureCaptureFixedFinished(capture_session *cap_session); + void captureCaptureFailed(capture_session *); +#endif + + void captureFileOpened(); + void captureFileReadFinished(); + void captureFileClosing(); + void captureFileClosed(); + + void launchRLCGraph(bool channelKnown, guint16 ueid, guint8 rlcMode, + guint16 channelType, guint16 channelId, guint8 direction); + + void rtpPlayerDialogReplaceRtpStreams(QVector stream_ids); + void rtpPlayerDialogAddRtpStreams(QVector stream_ids); + void rtpPlayerDialogRemoveRtpStreams(QVector stream_ids); + void rtpAnalysisDialogReplaceRtpStreams(QVector stream_ids); + void rtpAnalysisDialogAddRtpStreams(QVector stream_ids); + void rtpAnalysisDialogRemoveRtpStreams(QVector stream_ids); + void rtpStreamsDialogSelectRtpStreams(QVector stream_ids); + void rtpStreamsDialogDeselectRtpStreams(QVector stream_ids); + +private slots: + + void captureEventHandler(CaptureEvent ev); + + // Manually connected slots (no "on__"). + + void initViewColorizeMenu(); + void initConversationMenus(); + static bool addExportObjectsMenuItem(const void *key, void *value, void *userdata); + void initExportObjectsMenus(); + static bool addFollowStreamMenuItem(const void *key, void *value, void *userdata); + void initFollowStreamMenus(); + + // in main_window_slots.cpp + /** + * @brief startCapture + * Start capturing from the selected interfaces using the capture filter + * shown in the main welcome screen. + */ + void startCapture(QStringList); + void startCapture(); + void popLiveCaptureInProgress(); + void stopCapture(); + + void loadWindowGeometry(); + void saveWindowGeometry(); + void mainStackChanged(int); + void updateRecentCaptures(); + void recentActionTriggered(); + void addPacketComment(); + void editPacketComment(); + void deletePacketComment(); + void deleteCommentsFromPackets(); + QString commentToMenuText(QString text, int max_len = 40); + void setEditCommentsMenu(); + void setMenusForSelectedPacket(); + void setMenusForSelectedTreeRow(FieldInformation *fi = NULL); + void interfaceSelectionChanged(); + void captureFilterSyntaxChanged(bool valid); + void redissectPackets(); + void checkDisplayFilter(); + void fieldsChanged(); + void reloadLuaPlugins(); + void showAccordionFrame(AccordionFrame *show_frame, bool toggle = false); + void showColumnEditor(int column); + void showPreferenceEditor(); // module_t *, pref * + void addStatsPluginsToMenu(); + void addDynamicMenus(); + void reloadDynamicMenus(); + void addPluginIFStructures(); + QMenu * searchSubMenu(QString objectName); + void activatePluginIFToolbar(bool); + + void startInterfaceCapture(bool valid, const QString capture_filter); + + void applyGlobalCommandLineOptions(); + void setFeaturesEnabled(bool enabled = true); + + void on_actionNewDisplayFilterExpression_triggered(); + void onFilterSelected(QString, bool); + void onFilterPreferences(); + void onFilterEdit(int uatIndex); + + // Handle FilterAction signals + void queuedFilterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + + /** Pass stat cmd arguments to a slot. + * @param menu_path slot Partial slot name, e.g. "StatisticsIOGraph". + * @param arg "-z" argument, e.g. "io,stat". + * @param userdata Optional user data. + */ + void openStatCommandDialog(const QString &menu_path, const char *arg, void *userdata); + + /** Pass tap parameter arguments to a slot. + * @param cfg_str slot Partial slot name, e.g. "StatisticsAFPSrt". + * @param arg "-z" argument, e.g. "afp,srt". + * @param userdata Optional user data. + */ + void openTapParameterDialog(const QString cfg_str, const QString arg, void *userdata); + void openTapParameterDialog(); + +#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) + void softwareUpdateRequested(); +#endif + + // Automatically connected slots ("on__"). + // + // The slots below follow the naming conventaion described in + // https://doc.qt.io/archives/qt-4.8/qmetaobject.html#connectSlotsByName + // and are automatically connected at initialization time via + // main_ui_->setupUi, which in turn calls connectSlotsByName. + // + // If you're manually connecting a signal to a slot, don't prefix its name + // with "on_". Otherwise you'll get runtime warnings. + + // We might want move these to main_window_actions.cpp similar to + // gtk/main_menubar.c + + void connectFileMenuActions(); + void exportPacketBytes(); + void exportPDU(); + void stripPacketHeaders(); + void exportTLSSessionKeys(); + void printFile(); + + void connectEditMenuActions(); + void copySelectedItems(WiresharkMainWindow::CopySelected selection_type); + void findPacket(); + void editTimeShift(); + void editConfigurationProfiles(); + void editTimeShiftFinished(int); + void addPacketCommentFinished(PacketCommentDialog* pc_dialog, int result); + void editPacketCommentFinished(PacketCommentDialog* pc_dialog, int result, guint nComment); + void deleteAllPacketComments(); + void deleteAllPacketCommentsFinished(int result); + void injectSecrets(); + void discardAllSecrets(); + void discardAllSecretsFinished(int result); + void showPreferencesDialog(QString module_name); + + void connectViewMenuActions(); + void showHideMainWidgets(QAction *action); + void setTimestampFormat(QAction *action); + void setTimestampPrecision(QAction *action); + void setTimeDisplaySecondsWithHoursAndMinutes(bool checked); + void editResolvedName(); + void setNameResolution(); + void zoomText(); + void showColoringRulesDialog(); + void colorizeConversation(bool create_rule = false); + void colorizeActionTriggered(); + void openPacketDialog(bool from_reference = false); + void reloadCaptureFileAsFormatOrCapture(); + void reloadCaptureFile(); + + void connectGoMenuActions(); + + void setPreviousFocus(); + void resetPreviousFocus(); + + void connectCaptureMenuActions(); + void startCaptureTriggered(); + + void connectAnalyzeMenuActions(); + + void matchFieldFilter(FilterAction::Action action, FilterAction::ActionType filter_type); + void applyFieldAsColumn(); + + void filterMenuAboutToShow(); + + void applyConversationFilter(); + void applyExportObject(); + + void openFollowStreamDialog(int proto_id, guint stream_num, guint sub_stream_num, bool use_stream_index = true); + void openFollowStreamDialog(int proto_id); + + void statCommandExpertInfo(const char *, void *); + + void connectHelpMenuActions(); + +#ifdef HAVE_SOFTWARE_UPDATE + void checkForUpdates(); +#endif + + void goToCancelClicked(); + void goToGoClicked(); + void goToLineEditReturnPressed(); + + void connectStatisticsMenuActions(); + + void showResolvedAddressesDialog(); + void showConversationsDialog(); + void showEndpointsDialog(); + + void openTcpStreamDialog(int graph_type); + void openSCTPAllAssocsDialog(); + void on_actionSCTPShowAllAssociations_triggered(); + void on_actionSCTPAnalyseThisAssociation_triggered(); + void on_actionSCTPFilterThisAssociation_triggered(); + void statCommandMulticastStatistics(const char *arg, void *); + + void statCommandWlanStatistics(const char *arg, void *); + + void openStatisticsTreeDialog(const gchar *abbr); + void statCommandIOGraph(const char *, void *); + + void connectTelephonyMenuActions(); + + RtpStreamDialog *openTelephonyRtpStreamsDialog(); + RtpPlayerDialog *openTelephonyRtpPlayerDialog(); + RtpAnalysisDialog *openTelephonyRtpAnalysisDialog(); + void statCommandLteMacStatistics(const char *arg, void *); + void statCommandLteRlcStatistics(const char *arg, void *); + void openRtpStreamAnalysisDialog(); + void openRtpPlayerDialog(); + + void connectWirelessMenuActions(); + + void connectToolsMenuActions(); + + void externalMenuItemTriggered(); + + void on_actionContextWikiProtocolPage_triggered(); + void on_actionContextFilterFieldReference_triggered(); + + void extcap_options_finished(int result); + void showExtcapOptionsDialog(QString & device_name, bool startCaptureOnClose); + + QString findRtpStreams(QVector *stream_ids, bool reverse); + + void openTLSKeylogDialog(); + + friend class MainApplication; +}; + +#endif // WIRESHARK_MAIN_WINDOW_H diff --git a/ui/qt/wireshark_main_window.ui b/ui/qt/wireshark_main_window.ui new file mode 100644 index 00000000..cb01e642 --- /dev/null +++ b/ui/qt/wireshark_main_window.ui @@ -0,0 +1,3108 @@ + + + WiresharkMainWindow + + + + 0 + 0 + 960 + 768 + + + + true + + + Wireshark + + + true + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 10 + + + + + + + + Packet: + + + + + + + + + + + 16777215 + 27 + + + + Go to packet + + + true + + + + + + + + 16777215 + 27 + + + + Cancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + 0 + 0 + 960 + 23 + + + + + &File + + + + Open &Recent + + + + + + File Set + + + + + + + + Export Packet Dissections + + + + + + + + + + + + Export Objects + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Capture + + + + + + + + + + + + &Help + + + true + + + + Manual pages + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Go + + + + + + + + + + + + + + + + + + &View + + + + Interface Toolbars + + + + + &Zoom + + + + + + + + &Time Display Format + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name Resol&ution + + + + + + + + + + Colorize Conversation + + + + + + + + + + + + + + + + + + Internals + + + + + + + + Additional Toolbars + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Analyze + + + + Apply as Filter + + + + + Prepare as Filter + + + + + SCTP + + + + + + + + Follow + + + + + Conversation Filter + + + + + + + + + + + + + + + + + + + + + + + + true + + + &Statistics + + + + TCP Stream Graphs + + + + + + + + + + BACnet + + + + + + + + + HTTP + + + + + + + + + 29West + + + + Topics + + + + + + + + + + + + Queues + + + + + + + + + UIM + + + + + + + + + + + + Service &Response Time + + + + + Reliable Server Pooling (RSerPool) + + + + + SOME/IP + + + + + + + &DTN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Telephon&y + + + + RTSP + + + + + + &RTP + + + + + + + + S&CTP + + + + + + + + &ANSI + + + + + + &GSM + + + + + + &LTE + + + + + + &MTP3 + + + + + + Osmux + + + + + + + + + + + + + + + + + + + + + + &Edit + + + + Copy + + + + + + + + + + + + + + + + Packet Comments + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Wireless + + + + + + + + + + &Tools + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Main Toolbar + + + false + + + + 32 + 32 + + + + Qt::ToolButtonIconOnly + + + TopToolBarArea + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Display Filter Toolbar + + + false + + + + 14 + 14 + + + + TopToolBarArea + + + true + + + + + + Wireless Toolbar + + + false + + + TopToolBarArea + + + true + + + + + &Open + + + Open a capture file + + + Ctrl+O + + + false + + + + + &Quit + + + Quit Wireshark + + + Ctrl+Q + + + QAction::QuitRole + + + + + true + + + &Start + + + Start capturing packets + + + Ctrl+E + + + + + S&top + + + Stop capturing packets + + + Ctrl+E + + + + + &Close + + + Close this capture file + + + Ctrl+W + + + false + + + + + false + + + No files found + + + + + &Contents + + + Help contents + + + F1 + + + true + + + + + Wireshark + + + + + Wireshark Filter + + + + + TShark + + + + + Rawshark + + + + + Dumpcap + + + + + Mergecap + + + + + Editcap + + + + + Text2pcap + + + + + + :/menu/help/wsicon16.png:/menu/help/wsicon16.png + + + Website + + + + + FAQs + + + + + Downloads + + + + + + :/menu/help/wsicon16.png:/menu/help/wsicon16.png + + + Wiki + + + true + + + + + Sample Captures + + + + + &About Wireshark + + + QAction::AboutRole + + + + + + :/menu/help/wsicon-ask.png:/menu/help/wsicon-ask.png + + + Ask (Q&&A) + + + true + + + + + Next Packet + + + Go to the next packet + + + Ctrl+Down + + + + + Previous Packet + + + Go to the previous packet + + + Ctrl+Up + + + + + Next Packet in Conversation + + + Go to the next packet in this conversation + + + Ctrl+. + + + + + Previous Packet in Conversation + + + Go to the previous packet in this conversation + + + Ctrl+, + + + + + Next Packet In History + + + Go to the next packet in your selection history + + + Alt+Right + + + + + Previous Packet In History + + + Go to the previous packet in your selection history + + + Alt+Left + + + + + First Packet + + + Go to the first packet + + + Ctrl+Home + + + + + Last Packet + + + Go to the last packet + + + Ctrl+End + + + + + false + + + E&xpand Subtrees + + + Expand the current packet detail + + + Shift+Right + + + + + false + + + Collapse Subtrees + + + Collapse the current packet detail + + + Shift+Left + + + + + &Expand All + + + Expand packet details + + + Ctrl+Right + + + + + Collapse &All + + + Collapse all packet details + + + Ctrl+Left + + + + + true + + + Go to Packet… + + + Go to specified packet + + + Ctrl+G + + + + + &Merge… + + + Merge one or more files + + + + + &Import from Hex Dump… + + + Import a file + + + + + &Save + + + Save this capture file + + + Ctrl+S + + + false + + + + + Save &As… + + + Save as a different file + + + Ctrl+Shift+S + + + + + Export Specified Packets… + + + Export specified packets + + + + + Export Packet &Bytes… + + + Ctrl+Shift+X + + + + + Export TLS Session Keys… + + + + + &Print… + + + Ctrl+P + + + + + List Files + + + + + Next File + + + + + Previous File + + + + + &Reload + + + Reload this file + + + Ctrl+R + + + true + + + + + Reload as File Format/Capture + + + Ctrl+Shift+F + + + + + &Options… + + + Options + + + Capture options + + + Ctrl+K + + + QAction::NoRole + + + + + Capture &Filters… + + + Capture filters + + + + + Refresh Interfaces + + + Refresh interfaces + + + F5 + + + + + &Restart + + + Restart current capture + + + Ctrl+R + + + + + As Plain &Text… + + + + + As &CSV… + + + + + As "C" &Arrays… + + + + + As P&SML XML… + + + + + As P&DML XML… + + + + + As &JSON… + + + + + Description + + + Copy this item's description + + + Ctrl+Alt+Shift+D + + + + + As Plain &Text + + + + + As &CSV + + + + + As &YAML + + + + + All Visible Items + + + Ctrl+Alt+Shift+A + + + + + All Visible Selected Tree Items + + + + + Field Name + + + Copy this item's field name + + + Ctrl+Alt+Shift+F + + + + + Value + + + Copy this item's value + + + Ctrl+Alt+Shift+V + + + + + As Filter + + + Copy this item as a display filter + + + Ctrl+Shift+C + + + + + Display &Filters… + + + + + Display Filter &Macros… + + + + + Apply as Column + + + Create a packet list column from the selected field. + + + Ctrl+Shift+I + + + + + &Find Packet… + + + Find a packet + + + Ctrl+F + + + + + Find Ne&xt + + + Find the next packet + + + Ctrl+N + + + + + Find Pre&vious + + + Find the previous packet + + + Ctrl+B + + + + + &Mark/Unmark Packet(s) + + + Mark or unmark each selected packet + + + Ctrl+M + + + + + Mark All Displayed + + + Mark all displayed packets + + + Ctrl+Shift+M + + + + + &Unmark All Displayed + + + Unmark all displayed packets + + + Ctrl+Alt+M + + + + + Next Mark + + + Go to the next marked packet + + + Ctrl+Shift+N + + + + + Previous Mark + + + Go to the previous marked packet + + + Ctrl+Shift+B + + + + + &Ignore/Unignore Packet(s) + + + Ignore or unignore each selected packet + + + Ctrl+D + + + + + Ignore All Displayed + + + Ignore all displayed packets + + + Ctrl+Shift+D + + + + + U&nignore All Displayed + + + Unignore all displayed packets + + + Ctrl+Alt+D + + + + + Set/Unset Time Reference + + + Set or unset a time reference for this packet + + + Ctrl+T + + + + + Unset All Time References + + + Remove all time references + + + Ctrl+Alt+T + + + + + Next Time Reference + + + Go to the next time reference + + + Ctrl+Alt+N + + + + + Previous Time Reference + + + Go to the previous time reference + + + Ctrl+Alt+B + + + + + Time Shift… + + + Shift or change packet timestamps + + + Ctrl+Shift+T + + + + + Delete All Packet Comments + + + Remove all packet comments in the capture file + + + + + Inject TLS Secrets + + + Embed used TLS secrets in the capture file + + + + + Discard All Secrets + + + Discard all decryption secrets in the capture file + + + + + false + + + &Configuration Profiles… + + + Configuration profiles + + + Manage your configuration profiles + + + Ctrl+Shift+A + + + QAction::NoRole + + + + + &Preferences… + + + Manage Wireshark's preferences + + + Ctrl+Shift+P + + + QAction::PreferencesRole + + + + + false + + + Capture File Properties + + + Capture file properties + + + Ctrl+Alt+Shift+C + + + + + false + + + &Protocol Hierarchy + + + Show a summary of protocols present in the capture file. + + + + + Capinfos + + + + + Reordercap + + + + + Time Sequence (Stevens) + + + TCP time sequence graph (Stevens) + + + + + Throughput + + + TCP throughput + + + + + Round Trip Time + + + TCP round trip time + + + + + Window Scaling + + + TCP window scaling + + + + + Time Sequence (tcptrace) + + + TCP time sequence graph (tcptrace) + + + + + Analyse this Association + + + + + Show All Associations + + + + + Filter this Association + + + + + Flow Graph + + + Flow sequence diagram + + + + + ANCP + + + ANCP statistics + + + + + Packets sorted by Instance ID + + + BACapp statistics sorted by instance ID + + + + + Packets sorted by IP + + + BACapp statistics sorted by IP + + + + + Packets sorted by object type + + + BACapp statistics sorted by object type + + + + + Packets sorted by service + + + BACapp statistics sorted by service + + + + + Collectd + + + Collectd statistics + + + + + DNS + + + DNS statistics + + + + + HART-IP + + + HART-IP statistics + + + + + HPFEEDS + + + hpfeeds statistics + + + + + HTTP2 + + + HTTP2 statistics + + + + + Packet Counter + + + HTTP packet counter + + + + + Requests + + + HTTP requests + + + + + Load Distribution + + + HTTP load distribution + + + + + Request Sequences + + + HTTP Request Sequences + + + + + Packet Lengths + + + Packet length statistics + + + + + Sametime + + + Sametime statistics + + + + + SOME/IP Messages + + + SOME/IP Message statistics + + + + + SOME/IP-SD Entries + + + SOME/IP-SD Entries statistics + + + + + &LTP + + + LTP segment and block statistics + + + + + &ISUP Messages + + + ISUP message statistics + + + + + Packet Counter + + + Osmux packet counts + + + + + Packet Counter + + + RTSP packet counts + + + + + SM&PP Operations + + + SMPP operation statistics + + + + + &UCP Messages + + + UCP message statistics + + + + + F1AP + + + F1AP Messages + + + + + NGAP + + + NGAP Messages + + + + + Decode &As… + + + Change the way packets are dissected + + + Ctrl+Shift+U + + + + + Reload Lua Plugins + + + Reload Lua plugins + + + Ctrl+Shift+L + + + + + 29West + + + + + Advertisements by Topic + + + + + Advertisements by Source + + + + + Advertisements by Transport + + + + + Queries by Topic + + + + + Queries by Receiver + + + + + Wildcard Queries by Pattern + + + + + Wildcard Queries by Receiver + + + + + Advertisements by Queue + + + + + Advertisements by Source + + + + + Queries by Queue + + + + + Queries by Receiver + + + + + Streams + + + + + LBT-RM + + + + + LBT-RU + + + + + Export PDUs to File… + + + + + Strip Headers… + + + Strip headers and export higher level encapsulations to file + + + + + &I/O Graphs + + + Create graphs based on display filter fields + + + + + true + + + true + + + &Main Toolbar + + + Show or hide the main toolbar + + + + + true + + + true + + + &Filter Toolbar + + + Show or hide the display filter toolbar + + + + + &Conversations + + + Conversations at different protocol levels + + + + + &Endpoints + + + Endpoints at different protocol levels + + + + + true + + + Colorize Packet List + + + Draw packets using your coloring rules + + + + + &Zoom In + + + Enlarge the main window text + + + Ctrl++ + + + + + Zoom Out + + + Shrink the main window text + + + Ctrl+- + + + + + Normal Size + + + Return the main window text to its normal size + + + Ctrl+0 + + + + + Reset Layout + + + Reset appearance layout to default size + + + Ctrl+Shift+W + + + + + Resize Columns + + + Resize packet list columns to fit contents + + + Ctrl+Shift+R + + + + + true + + + Date and Time of Day (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + + + Ctrl+Alt+1 + + + + + true + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + + + + + true + + + Time of Day (01:02:03.123456) + + + Show packet times as the date and time of day. + + + Ctrl+Alt+2 + + + + + true + + + Seconds Since 1970-01-01 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + + + Ctrl+Alt+3 + + + + + true + + + Seconds Since First Captured Packet + + + Show packet times as the seconds since the first captured packet. + + + Ctrl+Alt+4 + + + + + true + + + Seconds Since Previous Captured Packet + + + Show packet times as the seconds since the previous captured packet. + + + Ctrl+Alt+5 + + + + + true + + + Seconds Since Previous Displayed Packet + + + Show packet times as the seconds since the previous displayed packet. + + + Ctrl+Alt+6 + + + + + true + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + + + Ctrl+Alt+7 + + + + + true + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + + + + + true + + + UTC Time of Day (01:02:03.123456) + + + Show packet times as the UTC time of day. + + + Ctrl+Alt+8 + + + + + true + + + Automatic (from capture file) + + + Use the time precision indicated in the capture file. + + + + + true + + + Seconds + + + + + true + + + Tenths of a second + + + + + true + + + Hundredths of a second + + + + + true + + + Milliseconds + + + + + true + + + Tenths of a millisecond + + + + + true + + + Hundredths of a millisecond + + + + + true + + + Microseconds + + + + + true + + + Tenths of a microsecond + + + + + true + + + Hundredths of a microsecond + + + + + true + + + Nanoseconds + + + + + true + + + Display Seconds With Hours and Minutes + + + Display seconds with hours and minutes + + + + + true + + + Resolve &Physical Addresses + + + Show names for known MAC addresses. Lookups use a local database. + + + + + true + + + Resolve &Network Addresses + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + + + + + true + + + Resolve &Transport Addresses + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + + + + + true + + + Wire&less Toolbar + + + Show or hide the wireless toolbar + + + + + true + + + true + + + &Status Bar + + + Show or hide the status bar + + + + + true + + + true + + + Packet &List + + + Show or hide the packet list + + + + + true + + + true + + + Packet &Details + + + Show or hide the packet details + + + + + true + + + true + + + Packet &Bytes + + + Show or hide the packet bytes + + + + + true + + + true + + + Packet &Diagram + + + Show or hide the packet diagram + + + + + &Conversation Hash Tables + + + Show each conversation hash table + + + + + &Dissector Tables + + + Show each dissector table and its entries + + + + + &Supported Protocols + + + Show the currently supported protocols and display filter fields + + + + + MAP Summary + + + GSM MAP summary statistics + + + + + MAC Statistics + + + LTE MAC statistics + + + + + RLC Statistics + + + LTE RLC statistics + + + + + RLC &Graph + + + LTE RLC graph + + + + + MTP3 Summary + + + MTP3 summary statistics + + + + + &VoIP Calls + + + All VoIP Calls + + + + + SIP &Flows + + + SIP Flows + + + + + RTP Streams + + + + + &Coloring Rules… + + + Edit the packet list coloring rules. + + + + + Bluetooth ATT Server Attributes + + + + + Bluetooth Devices + + + + + Bluetooth HCI Summary + + + + + Show Packet in New &Window + + + Show this packet in a separate window. + + + + + Show Linked Packet in New Window + + + Show the linked packet in a separate window. + + + + + true + + + Auto Scroll in Li&ve Capture + + + Automatically scroll to the last packet during a live capture. + + + + + Expert Information + + + Show expert notifications + + + + + Display Filter &Expression… + + + Display Filter Expression… + + + Add an expression to the display filter. + + + + + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + + + false + + + QAction::NoRole + + + + + REGISTER_STAT_GROUP_RSERPOOL + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + + + false + + + QAction::NoRole + + + + + false + + + No ANSI statistics registered + + + + + false + + + No GSM statistics registered + + + + + false + + + No LTE statistics registered + + + + + false + + + No MTP3 statistics registered + + + + + Resolved Addresses + + + Show each table of resolved addresses as copyable text. + + + + + Color &1 + + + Mark the current conversation with its own color. + + + Ctrl+1 + + + + + Color &2 + + + Mark the current conversation with its own color. + + + Ctrl+2 + + + + + Color &3 + + + Mark the current conversation with its own color. + + + Ctrl+3 + + + + + Color &4 + + + Mark the current conversation with its own color. + + + Ctrl+4 + + + + + Color &5 + + + Mark the current conversation with its own color. + + + Ctrl+5 + + + + + Color &6 + + + Mark the current conversation with its own color. + + + Ctrl+6 + + + + + Color &7 + + + Mark the current conversation with its own color. + + + Ctrl+7 + + + + + Color &8 + + + Mark the current conversation with its own color. + + + Ctrl+8 + + + + + Color &9 + + + Mark the current conversation with its own color. + + + Ctrl+9 + + + + + Color 1&0 + + + Mark the current conversation with its own color. + + + + + New Coloring Rule… + + + Create a new coloring rule based on this field. + + + + + Reset Colorization + + + Reset colorized conversations. + + + Ctrl+Space + + + + + RTP Stream Analysis + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + + + + + RTP Player + + + Play selected stream. Press CTRL key for playing reverse stream too. + + + + + IA&X2 Stream Analysis + + + IAX2 Stream Analysis + + + + + Edit Resolved Name + + + Manually edit a name resolution entry. + + + + + Enabled Protocols… + + + Enable and disable specific protocols + + + Ctrl+Shift+E + + + + + Show Packet Bytes… + + + Ctrl+Shift+O + + + + + Wiki Protocol Page + + + Open the Wireshark wiki page for this protocol. + + + + + Filter Field Reference + + + Open the display filter reference page for this filter field. + + + + + Go to &Linked Packet + + + Go to the packet referenced by the selected field. + + + + + UDP Multicast Streams + + + Show UTP multicast stream statistics. + + + + + WLAN Traffic + + + Show IEEE 802.11 wireless LAN statistics. + + + + + Add a display filter button. + + + + + Firewall ACL Rules + + + Create firewall ACL rules + + + + + true + + + &Full Screen + + + + + Credentials + + + + + MAC Address Blocks + + + + + TLS Keylog Launcher + + + + + Release Notes + + + + + + + AccordionFrame + QFrame +
accordion_frame.h
+ 1 +
+ + MainStatusBar + QStatusBar +
main_status_bar.h
+
+ + WelcomePage + QFrame +
welcome_page.h
+ 1 +
+ + SearchFrame + QWidget +
search_frame.h
+
+ + ColumnEditorFrame + QFrame +
column_editor_frame.h
+ 1 +
+ + PreferenceEditorFrame + QFrame +
preference_editor_frame.h
+ 1 +
+ + AddressEditorFrame + QFrame +
address_editor_frame.h
+ 1 +
+ + FilterExpressionFrame + QFrame +
filter_expression_frame.h
+ 1 +
+ + WirelessTimeline + QWidget +
widgets/wireless_timeline.h
+ 1 +
+
+ + + + + + actionFileQuit + triggered() + WiresharkMainWindow + close() + + + -1 + -1 + + + 408 + 258 + + + + +
diff --git a/ui/qt/wireshark_main_window_slots.cpp b/ui/qt/wireshark_main_window_slots.cpp new file mode 100644 index 00000000..74943adc --- /dev/null +++ b/ui/qt/wireshark_main_window_slots.cpp @@ -0,0 +1,4062 @@ +/* main_window_slots.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +// Qt 5.5.0 + Visual C++ 2013 +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) +#endif + +#include "wireshark_main_window.h" + +/* + * The generated Ui_WiresharkMainWindow::setupUi() can grow larger than our configured limit, + * so turn off -Wframe-larger-than= for ui_main_window.h. + */ +DIAG_OFF(frame-larger-than=) +#include +DIAG_ON(frame-larger-than=) + +#ifdef _WIN32 +#include +#endif + +#include "ui/dissect_opts.h" + +#ifdef HAVE_LIBPCAP +#include "ui/capture.h" +#endif + +#include "ui/commandline.h" + +#include "ui/urls.h" + +#include "epan/color_filters.h" +#include "epan/export_object.h" + +#include "wsutil/file_util.h" +#include "wsutil/filesystem.h" +#include +#include + +#include "epan/addr_resolv.h" +#include "epan/column.h" +#include "epan/dfilter/dfilter-macro.h" +#include "epan/conversation_filter.h" +#include "epan/epan_dissect.h" +#include "epan/filter_expressions.h" +#include "epan/prefs.h" +#include "epan/plugin_if.h" +#include "epan/uat.h" +#include "epan/uat-int.h" +#include "epan/value_string.h" + +#ifdef HAVE_LUA +#include +#endif + +#include "ui/alert_box.h" +#ifdef HAVE_LIBPCAP +#include "ui/capture_ui_utils.h" +#endif + +#include "ui/capture_globals.h" +#include "ui/help_url.h" +#include "ui/main_statusbar.h" +#include "ui/preference_utils.h" +#include "ui/recent.h" +#include "ui/recent_utils.h" +#include "ui/ssl_key_export.h" +#include "ui/ws_ui_util.h" +#include "ui/all_files_wildcard.h" +#include "ui/qt/simple_dialog.h" + +#include +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#ifdef HAVE_SOFTWARE_UPDATE +#include "ui/software_update.h" +#endif + +#include "about_dialog.h" +#include "bluetooth_att_server_attributes_dialog.h" +#include "bluetooth_devices_dialog.h" +#include "bluetooth_hci_summary_dialog.h" +#include "capture_file_dialog.h" +#include "capture_file_properties_dialog.h" +#ifdef HAVE_LIBPCAP +#include "capture_options_dialog.h" +#endif +#include +#include "coloring_rules_dialog.h" +#include "conversation_dialog.h" +#include "conversation_colorize_action.h" +#include "conversation_hash_tables_dialog.h" +#include "enabled_protocols_dialog.h" +#include "decode_as_dialog.h" +#include +#include "display_filter_expression_dialog.h" +#include "dissector_tables_dialog.h" +#include "endpoint_dialog.h" +#include "expert_info_dialog.h" +#include "export_object_action.h" +#include "export_object_dialog.h" +#include "export_pdu_dialog.h" +#include "extcap_options_dialog.h" +#include "file_set_dialog.h" +#include "filter_action.h" +#include "filter_dialog.h" +#include "firewall_rules_dialog.h" +#include "follow_stream_action.h" +#include "follow_stream_dialog.h" +#include "funnel_statistics.h" +#include "gsm_map_summary_dialog.h" +#include "iax2_analysis_dialog.h" +#include "interface_toolbar.h" +#include "io_graph_dialog.h" +#include +#include "lbm_stream_dialog.h" +#include "lbm_lbtrm_transport_dialog.h" +#include "lbm_lbtru_transport_dialog.h" +#include "lte_mac_statistics_dialog.h" +#include "lte_rlc_statistics_dialog.h" +#include "lte_rlc_graph_dialog.h" +#include "main_application.h" +#include "manuf_dialog.h" +#include "mtp3_summary_dialog.h" +#include "multicast_statistics_dialog.h" +#include "packet_comment_dialog.h" +#include "packet_diagram.h" +#include "packet_dialog.h" +#include "packet_list.h" +#include "credentials_dialog.h" +#include "preferences_dialog.h" +#include "print_dialog.h" +#include "profile_dialog.h" +#include "protocol_hierarchy_dialog.h" +#include +#include "resolved_addresses_dialog.h" +#include "rpc_service_response_time_dialog.h" +#include "rtp_stream_dialog.h" +#include "rtp_analysis_dialog.h" +#include "sctp_all_assocs_dialog.h" +#include "sctp_assoc_analyse_dialog.h" +#include "sctp_graph_dialog.h" +#include "sequence_dialog.h" +#include "show_packet_bytes_dialog.h" +#include "tlskeylog_launcher_dialog.h" +#include "stats_tree_dialog.h" +#include "strip_headers_dialog.h" +#include +#include "supported_protocols_dialog.h" +#include "tap_parameter_dialog.h" +#include "tcp_stream_dialog.h" +#include "time_shift_dialog.h" +#include "uat_dialog.h" +#include "voip_calls_dialog.h" +#include "wlan_statistics_dialog.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// XXX You must uncomment QT_WINEXTRAS_LIB lines in CMakeList.txt and +// cmakeconfig.h.in. +// #if defined(QT_WINEXTRAS_LIB) +// #include +// #include +// #include +// #endif + +// +// Public slots +// + +bool WiresharkMainWindow::openCaptureFile(QString cf_path, QString read_filter, unsigned int type, gboolean is_tempfile) +{ + QString file_name = ""; + dfilter_t *rfcode = NULL; + df_error_t *df_err = NULL; + int err; + gboolean name_param; + gboolean ret = true; + + // was a file name given as function parameter? + name_param = !cf_path.isEmpty(); + + for (;;) { + + if (cf_path.isEmpty()) { + CaptureFileDialog open_dlg(this, capture_file_.capFile()); + + if (open_dlg.open(file_name, type, read_filter)) { + cf_path = file_name; + } else { + ret = false; + goto finish; + } + } else { + this->welcome_page_->getInterfaceFrame()->showRunOnFile(); + } + + // TODO detect call from "cf_read" -> "update_progress_dlg" + // ("capture_file_.capFile()->read_lock"), possibly queue opening the + // file and return early to avoid the warning in testCaptureFileClose. + + QString before_what(tr(" before opening another file")); + if (!testCaptureFileClose(before_what)) { + ret = false; + goto finish; + } + + if (dfilter_compile(qUtf8Printable(read_filter), &rfcode, &df_err)) { + cf_set_rfcode(CaptureFile::globalCapFile(), rfcode); + } else { + /* Not valid. Tell the user, and go back and run the file + selection box again once they dismiss the alert. */ + //bad_dfilter_alert_box(top_level, read_filter->str); + QMessageBox::warning(this, tr("Invalid Display Filter"), + QString("The filter expression ") + + read_filter + + QString(" isn't a valid display filter. (") + + df_err->msg + QString(")."), + QMessageBox::Ok); + df_error_free(&df_err); + if (!name_param) { + // go back to the selection dialogue only if the file + // was selected from this dialogue + cf_path.clear(); + continue; + } + } + + /* Make the file name available via MainWindow */ + setMwFileName(cf_path); + + /* Try to open the capture file. This closes the current file if it succeeds. */ + CaptureFile::globalCapFile()->window = this; + if (cf_open(CaptureFile::globalCapFile(), qUtf8Printable(cf_path), type, is_tempfile, &err) != CF_OK) { + /* We couldn't open it; don't dismiss the open dialog box, + just leave it around so that the user can, after they + dismiss the alert box popped up for the open error, + try again. */ + CaptureFile::globalCapFile()->window = NULL; + dfilter_free(rfcode); + cf_path.clear(); + continue; + } + + switch (cf_read(CaptureFile::globalCapFile(), /*reloading=*/FALSE)) { + case CF_READ_OK: + case CF_READ_ERROR: + /* Just because we got an error, that doesn't mean we were unable + to read any of the file; we handle what we could get from the + file. */ + break; + + case CF_READ_ABORTED: + /* The user bailed out of re-reading the capture file; the + capture file has been closed - just free the capture file name + string and return (without changing the last containing + directory). */ + capture_file_.setCapFile(NULL); + ret = false; + goto finish; + } + break; + } + + mainApp->setLastOpenDirFromFilename(cf_path); + + main_ui_->statusBar->showExpert(); + +finish: +#ifdef HAVE_LIBPCAP + if (global_commandline_info.quit_after_cap) + exit(0); +#endif + return ret; +} + +void WiresharkMainWindow::filterPackets(QString new_filter, bool force) +{ + cf_status_t cf_status; + + cf_status = cf_filter_packets(CaptureFile::globalCapFile(), new_filter.toUtf8().data(), force); + + if (cf_status == CF_OK) { + if (new_filter.length() > 0) { + int index = df_combo_box_->findText(new_filter); + if (index == -1) { + df_combo_box_->insertItem(0, new_filter); + df_combo_box_->setCurrentIndex(0); + } else { + df_combo_box_->setCurrentIndex(index); + } + } else { + df_combo_box_->lineEdit()->clear(); + } + // Only after the display filter has been updated, + // disable the arrow button + emit displayFilterSuccess(true); + } else { + emit displayFilterSuccess(false); + } +} + +void WiresharkMainWindow::layoutToolbars() +{ + Qt::ToolButtonStyle tbstyle = Qt::ToolButtonIconOnly; + switch (prefs.gui_toolbar_main_style) { + case TB_STYLE_TEXT: + tbstyle = Qt::ToolButtonTextOnly; + break; + case TB_STYLE_BOTH: + tbstyle = Qt::ToolButtonTextUnderIcon; + break; + } + + main_ui_->mainToolBar->setToolButtonStyle(tbstyle); + + main_ui_->mainToolBar->setVisible(recent.main_toolbar_show); + main_ui_->displayFilterToolBar->setVisible(recent.filter_toolbar_show); +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) + main_ui_->wirelessToolBar->setVisible(recent.wireless_toolbar_show); +#endif + main_ui_->statusBar->setVisible(recent.statusbar_show); + + foreach(QAction *action, main_ui_->menuInterfaceToolbars->actions()) { + QToolBar *toolbar = action->data().value(); + if (g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc)strcmp)) { + toolbar->setVisible(true); + } else { + toolbar->setVisible(false); + } + } + + QList toolbars = findChildren(); + foreach(QToolBar *bar, toolbars) { + AdditionalToolBar *iftoolbar = dynamic_cast(bar); + if (iftoolbar) { + bool visible = false; + if (g_list_find_custom(recent.gui_additional_toolbars, qUtf8Printable(iftoolbar->menuName()), (GCompareFunc)strcmp)) + visible = true; + + iftoolbar->setVisible(visible); + + } + } +} + +void WiresharkMainWindow::updatePreferenceActions() +{ + 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_->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_->actionViewNameResolutionNetwork->setChecked(gbl_resolv_flags.network_name); + main_ui_->actionViewNameResolutionTransport->setChecked(gbl_resolv_flags.transport_name); +} + +void WiresharkMainWindow::updateRecentActions() +{ + main_ui_->actionViewMainToolbar->setChecked(recent.main_toolbar_show); + main_ui_->actionViewFilterToolbar->setChecked(recent.filter_toolbar_show); + main_ui_->actionViewWirelessToolbar->setChecked(recent.wireless_toolbar_show); + main_ui_->actionViewStatusBar->setChecked(recent.statusbar_show); + 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_->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()) { + if (g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc)strcmp)) { + action->setChecked(true); + } else { + action->setChecked(false); + } + } + + foreach(QAction * action, main_ui_->menuAdditionalToolbars->actions()) { + ext_toolbar_t * toolbar = VariantPointer::asPtr(action->data()); + bool checked = false; + if (toolbar && g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, (GCompareFunc)strcmp)) + checked = true; + + action->setChecked(checked); + } + + foreach(QAction* tda, td_actions.keys()) { + if (recent.gui_time_format == td_actions[tda]) { + tda->setChecked(true); + } + } + foreach(QAction* tpa, tp_actions.keys()) { + if (recent.gui_time_precision == tp_actions[tpa]) { + tpa->setChecked(true); + break; + } + } + main_ui_->actionViewTimeDisplaySecondsWithHoursAndMinutes->setChecked(recent.gui_seconds_format == TS_SECONDS_HOUR_MIN_SEC); + + main_ui_->actionViewColorizePacketList->setChecked(recent.packet_list_colorize); + + main_ui_->actionGoAutoScroll->setChecked(recent.capture_auto_scroll); +} + +// Don't connect to this directly. Connect to or emit fiterAction(...) instead. +void WiresharkMainWindow::queuedFilterAction(QString action_filter, FilterAction::Action action, FilterAction::ActionType type) +{ + QString cur_filter, new_filter; + + if (!df_combo_box_) return; + cur_filter = df_combo_box_->lineEdit()->text(); + + switch (type) { + case FilterAction::ActionTypePlain: + new_filter = action_filter; + break; + case FilterAction::ActionTypeAnd: + if (cur_filter.length()) { + new_filter = "(" + cur_filter + ") && (" + action_filter + ")"; + } + else { + new_filter = action_filter; + } + break; + case FilterAction::ActionTypeOr: + if (cur_filter.length()) { + new_filter = "(" + cur_filter + ") || (" + action_filter + ")"; + } else { + new_filter = action_filter; + } + break; + case FilterAction::ActionTypeNot: + new_filter = "!(" + action_filter + ")"; + break; + case FilterAction::ActionTypeAndNot: + if (cur_filter.length()) { + new_filter = "(" + cur_filter + ") && !(" + action_filter + ")"; + } else { + new_filter = "!(" + action_filter + ")"; + } + break; + case FilterAction::ActionTypeOrNot: + if (cur_filter.length()) { + new_filter = "(" + cur_filter + ") || !(" + action_filter + ")"; + } else { + new_filter = "!(" + action_filter + ")"; + } + break; + default: + ws_assert_not_reached(); + break; + } + + switch (action) { + case FilterAction::ActionApply: + df_combo_box_->lineEdit()->setText(new_filter); + df_combo_box_->applyDisplayFilter(); + break; + case FilterAction::ActionColorize: + colorizeWithFilter(new_filter.toUtf8()); + break; + case FilterAction::ActionCopy: + mainApp->clipboard()->setText(new_filter); + break; + case FilterAction::ActionFind: + main_ui_->searchFrame->findFrameWithFilter(new_filter); + break; + case FilterAction::ActionPrepare: + df_combo_box_->lineEdit()->setText(new_filter); + df_combo_box_->lineEdit()->setFocus(); + break; + case FilterAction::ActionWebLookup: + { + QString url = QString("https://www.google.com/search?q=") + new_filter; + QDesktopServices::openUrl(QUrl(url)); + break; + } + default: + ws_assert_not_reached(); + break; + } +} + +// Capture callbacks + +#ifdef HAVE_LIBPCAP +void WiresharkMainWindow::captureCapturePrepared(capture_session *session) { + setTitlebarForCaptureInProgress(); + + setWindowIcon(mainApp->captureIcon()); + + /* Disable menu items that make no sense if you're currently running + a capture. */ + bool handle_toolbars = (session->session_will_restart ? false : true); + setForCaptureInProgress(true, handle_toolbars, session->capture_opts->ifaces); +// set_capture_if_dialog_for_capture_in_progress(TRUE); + +// /* Don't set up main window for a capture file. */ +// main_set_for_capture_file(FALSE); + showCapture(); +} + +void WiresharkMainWindow::captureCaptureUpdateStarted(capture_session *session) { + + /* We've done this in "prepared" above, but it will be cleared while + switching to the next multiple file. */ + setTitlebarForCaptureInProgress(); + setWindowIcon(mainApp->captureIcon()); + + bool handle_toolbars = (session->session_will_restart ? false : true); + setForCaptureInProgress(true, handle_toolbars, session->capture_opts->ifaces); + + setForCapturedPackets(true); +} + +void WiresharkMainWindow::captureCaptureUpdateFinished(capture_session *session) { + + /* The capture isn't stopping any more - it's stopped. */ + capture_stopping_ = false; + + /* Update the main window as appropriate */ + updateForUnsavedChanges(); + + /* Enable menu items that make sense if you're not currently running + a capture. */ + bool handle_toolbars = (session->session_will_restart ? false : true); + setForCaptureInProgress(false, handle_toolbars); + setMenusForCaptureFile(); + + setWindowIcon(mainApp->normalIcon()); + popLiveCaptureInProgress(); + + if (global_commandline_info.quit_after_cap) { + // Command line asked us to quit after capturing. + // Don't pop up a dialog to ask for unsaved files etc. + exit(0); + } +} + +void WiresharkMainWindow::captureCaptureFixedFinished(capture_session *) { + + /* The capture isn't stopping any more - it's stopped. */ + capture_stopping_ = false; + + /* Enable menu items that make sense if you're not currently running + a capture. */ + setForCaptureInProgress(false); + /* There isn't a real capture_file structure yet, so just force disabling + menu options. They will "refresh" when the capture file is reloaded to + display packets */ + setMenusForCaptureFile(true); + + setWindowIcon(mainApp->normalIcon()); + popLiveCaptureInProgress(); + + if (global_commandline_info.quit_after_cap) { + // Command line asked us to quit after capturing. + // Don't pop up a dialog to ask for unsaved files etc. + exit(0); + } +} + +void WiresharkMainWindow::captureCaptureFailed(capture_session *) { + /* Capture isn't stopping any more. */ + capture_stopping_ = false; + + setForCaptureInProgress(false); + showWelcome(); + + // Reset expert information indicator + main_ui_->statusBar->captureFileClosing(); + mainApp->popStatus(WiresharkApplication::FileStatus); + + setWindowIcon(mainApp->normalIcon()); + popLiveCaptureInProgress(); + + if (global_commandline_info.quit_after_cap) { + // Command line asked us to quit after capturing. + // Don't pop up a dialog to ask for unsaved files etc. + exit(0); + } +} +#endif // HAVE_LIBPCAP + +// Callbacks from cfile.c and file.c via CaptureFile::captureFileCallback + +void WiresharkMainWindow::captureEventHandler(CaptureEvent ev) +{ + switch (ev.captureContext()) { + + case CaptureEvent::File: + switch (ev.eventType()) { + case CaptureEvent::Opened: + captureFileOpened(); + break; + case CaptureEvent::Closing: + captureFileClosing(); + break; + case CaptureEvent::Closed: + captureFileClosed(); + break; + case CaptureEvent::Started: + captureFileReadStarted(tr("Loading")); + break; + case CaptureEvent::Finished: + captureFileReadFinished(); + break; + default: + break; + } + break; + + case CaptureEvent::Reload: + switch (ev.eventType()) { + case CaptureEvent::Started: + captureFileReadStarted(tr("Reloading")); + break; + case CaptureEvent::Finished: + captureFileReadFinished(); + break; + default: + break; + } + break; + + case CaptureEvent::Rescan: + switch (ev.eventType()) { + case CaptureEvent::Started: + setMenusForCaptureFile(true); + captureFileReadStarted(tr("Rescanning")); + break; + case CaptureEvent::Finished: + captureFileReadFinished(); + break; + default: + break; + } + break; + + case CaptureEvent::Retap: + switch (ev.eventType()) { + case CaptureEvent::Started: + freeze(); + break; + case CaptureEvent::Finished: + thaw(); + break; + case CaptureEvent::Flushed: + draw_tap_listeners(FALSE); + break; + default: + break; + } + break; + + case CaptureEvent::Merge: + switch (ev.eventType()) { + case CaptureEvent::Started: + mainApp->popStatus(WiresharkApplication::FileStatus); + mainApp->pushStatus(WiresharkApplication::FileStatus, tr("Merging files."), QString()); + break; + case CaptureEvent::Finished: + mainApp->popStatus(WiresharkApplication::FileStatus); + break; + default: + break; + } + break; + + case CaptureEvent::Save: + switch (ev.eventType()) { + case CaptureEvent::Started: + { + QFileInfo file_info(ev.filePath()); + mainApp->popStatus(WiresharkApplication::FileStatus); + mainApp->pushStatus(WiresharkApplication::FileStatus, tr("Saving %1…").arg(file_info.fileName())); + break; + } + default: + break; + } + break; + +#ifdef HAVE_LIBPCAP + case CaptureEvent::Capture: + switch (ev.eventType()) { + case CaptureEvent::Prepared: + captureCapturePrepared(ev.capSession()); + break; + case CaptureEvent::Stopping: + capture_stopping_ = true; + setMenusForCaptureStopping(); + break; + case CaptureEvent::Failed: + captureCaptureFailed(ev.capSession()); + default: + break; + } + break; + + case CaptureEvent::Update: + switch (ev.eventType()) { + case CaptureEvent::Started: + captureCaptureUpdateStarted(ev.capSession()); + break; + case CaptureEvent::Finished: + captureCaptureUpdateFinished(ev.capSession()); + break; + default: + break; + } + break; + + case CaptureEvent::Fixed: + switch (ev.eventType()) { + case CaptureEvent::Finished: + captureCaptureFixedFinished(ev.capSession()); + break; + default: + break; + } + break; +#endif + } +} + +void WiresharkMainWindow::captureFileOpened() { + if (capture_file_.window() != this) return; + + file_set_dialog_->fileOpened(capture_file_.capFile()); + setMenusForFileSet(true); + emit setCaptureFile(capture_file_.capFile()); +} + +void WiresharkMainWindow::captureFileReadStarted(const QString &action) { +// tap_param_dlg_update(); + + /* Set up main window for a capture file. */ +// main_set_for_capture_file(TRUE); + + mainApp->popStatus(WiresharkApplication::FileStatus); + QString msg = QString(tr("%1: %2")).arg(action).arg(capture_file_.fileName()); + QString msgtip = QString(); + mainApp->pushStatus(WiresharkApplication::FileStatus, msg, msgtip); + showCapture(); + main_ui_->actionAnalyzeReloadLuaPlugins->setEnabled(false); + main_ui_->wirelessTimelineWidget->captureFileReadStarted(capture_file_.capFile()); +} + +void WiresharkMainWindow::captureFileReadFinished() { + if (!capture_file_.capFile()->is_tempfile && capture_file_.capFile()->filename) { + /* Add this filename to the list of recent files in the "Recent Files" submenu */ + add_menu_recent_capture_file(capture_file_.capFile()->filename); + + /* Remember folder for next Open dialog and save it in recent */ + mainApp->setLastOpenDirFromFilename(capture_file_.capFile()->filename); + } + + /* Update the appropriate parts of the main window. */ + updateForUnsavedChanges(); + + /* enable wireless timeline if capture allows it */ + main_ui_->wirelessTimelineWidget->captureFileReadFinished(); + + /* Enable menu items that make sense if you have some captured packets. */ + setForCapturedPackets(true); + + main_ui_->statusBar->setFileName(capture_file_); + main_ui_->actionAnalyzeReloadLuaPlugins->setEnabled(true); + + packet_list_->captureFileReadFinished(); + + emit setDissectedCaptureFile(capture_file_.capFile()); +} + +void WiresharkMainWindow::captureFileClosing() { + setMenusForCaptureFile(true); + setForCapturedPackets(false); + setForCaptureInProgress(false); + + // Reset expert information indicator + main_ui_->statusBar->captureFileClosing(); + main_ui_->searchFrame->animatedHide(); + main_ui_->goToFrame->animatedHide(); + // gtk_widget_show(expert_info_none); + emit setCaptureFile(NULL); + emit setDissectedCaptureFile(NULL); +} + +void WiresharkMainWindow::captureFileClosed() { + packets_bar_update(); + + file_set_dialog_->fileClosed(); + setMenusForFileSet(false); + setWindowModified(false); + + // Reset expert information indicator + main_ui_->statusBar->captureFileClosing(); + mainApp->popStatus(WiresharkApplication::FileStatus); + + setWSWindowTitle(); + setWindowIcon(mainApp->normalIcon()); + setMenusForSelectedPacket(); + setMenusForSelectedTreeRow(); + +#ifdef HAVE_LIBPCAP + if (!global_capture_opts.multi_files_on) + showWelcome(); +#endif +} + +// +// Private slots +// + +// ui/gtk/capture_dlg.c:start_capture_confirmed + +void WiresharkMainWindow::startCapture() { + startCapture(QStringList()); +} + +void WiresharkMainWindow::startCapture(QStringList interfaces _U_) { +#ifdef HAVE_LIBPCAP + interface_options *interface_opts; + guint i; + interface_t *device; + gboolean can_start_capture = TRUE; + + if (interfaces.count() > 0) { + global_capture_opts.num_selected = 0; + for (i = 0; i < global_capture_opts.all_ifaces->len; i++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + + if (interfaces.contains(device->name)) { + device->selected = TRUE; + global_capture_opts.num_selected++; + } + else { + device->selected = FALSE; + } + } + } + + /* did the user ever select a capture interface before? */ + if (global_capture_opts.num_selected == 0) { + QString msg = QString(tr("No interface selected.")); + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, msg); + main_ui_->actionCaptureStart->setChecked(false); + return; + } + + for (i = 0; i < global_capture_opts.all_ifaces->len; i++) { + device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i); + if (device->selected && (device->if_info.type == IF_EXTCAP)) { + /* device is EXTCAP and is selected. Check if all mandatory + * settings are set. + */ + if (extcap_requires_configuration(device->name)) + { + /* Request openning of extcap options dialog */ + QString device_name(device->name); + emit showExtcapOptions(device_name, false); + /* Cancel start of capture */ + can_start_capture = FALSE; + } + } + } + + /* If some of extcap was not configured, do not start with the capture */ + if (!can_start_capture) { + QString msg = QString(tr("Configure all extcaps before start of capture.")); + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, msg); + main_ui_->actionCaptureStart->setChecked(false); + return; + } + + // Ideally we should have disabled the start capture + // toolbar buttons and menu items. This may not be the + // case, e.g. with QtMacExtras. + if (!capture_filter_valid_) { + QString msg = QString(tr("Invalid capture filter.")); + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, msg); + main_ui_->actionCaptureStart->setChecked(false); + return; + } + + showCapture(); + + /* XXX - we might need to init other pref data as well... */ + main_ui_->actionGoAutoScroll->setChecked(recent.capture_auto_scroll); + + /* XXX - can this ever happen? */ + if (cap_session_.state != CAPTURE_STOPPED) + return; + + /* close the currently loaded capture file */ + cf_close((capture_file *)cap_session_.cf); + + /* Copy the selected interfaces to the set of interfaces to use for + this capture. */ + collect_ifaces(&global_capture_opts); + + CaptureFile::globalCapFile()->window = this; + info_data_.ui.ui = this; + if (capture_start(&global_capture_opts, NULL, &cap_session_, &info_data_, + main_window_update)) { + capture_options *capture_opts = cap_session_.capture_opts; + GString *interface_names; + + /* Add "interface name" on main status bar */ + interface_names = get_iface_list_string(capture_opts, 0); + if (strlen(interface_names->str) > 0) { + g_string_append(interface_names, ":"); + } + g_string_append(interface_names, " "); + + mainApp->popStatus(WiresharkApplication::FileStatus); + QString msg = QString("%1").arg(interface_names->str); + QString msgtip = QString("to file: "); + if (capture_opts->save_file) + msgtip += capture_opts->save_file; + mainApp->pushStatus(WiresharkApplication::FileStatus, msg, msgtip); + g_string_free(interface_names, TRUE); + + /* The capture succeeded, which means the capture filter syntax is + valid; add this capture filter to the recent capture filter list. */ + QByteArray filter_ba; + for (i = 0; i < global_capture_opts.ifaces->len; i++) { + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, i); + if (interface_opts->cfilter) { + recent_add_cfilter(interface_opts->name, interface_opts->cfilter); + if (filter_ba.isEmpty()) { + filter_ba = interface_opts->cfilter; + } else { + /* Not the first selected interface; is its capture filter + the same as the one the other interfaces we've looked + at have? */ + /* XXX: GCC 12.1 has a bogus warning at -O2 and higher + * even though the isEmpty() check guarantees that + * filter_ba.constData() is never NULL or empty. + */ +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_OFF(stringop-overread) +#endif + if (strcmp(interface_opts->cfilter, filter_ba.constData()) != 0) { +#if WS_IS_AT_LEAST_GNUC_VERSION(12,1) +DIAG_ON(stringop-overread) +#endif + /* No, so not all selected interfaces have the same capture + filter. */ + filter_ba.clear(); + } + } + } + } + if (!filter_ba.isEmpty()) { + recent_add_cfilter(NULL, filter_ba.constData()); + } + } else { + CaptureFile::globalCapFile()->window = NULL; + } +#endif // HAVE_LIBPCAP +} + +void WiresharkMainWindow::popLiveCaptureInProgress() { + /* Pop the "" message off the status bar. */ + main_ui_->statusBar->setFileName(capture_file_); +} + +void WiresharkMainWindow::stopCapture() { +//#ifdef HAVE_AIRPCAP +// if (airpcap_if_active) +// airpcap_set_toolbar_stop_capture(airpcap_if_active); +//#endif + +#ifdef HAVE_LIBPCAP + capture_stop(&cap_session_); +#endif // HAVE_LIBPCAP + +} + +// Keep focus rects from showing through the welcome screen. Primarily for +// macOS. +void WiresharkMainWindow::mainStackChanged(int) +{ + for (int i = 0; i < main_ui_->mainStack->count(); i++) { + main_ui_->mainStack->widget(i)->setEnabled(i == main_ui_->mainStack->currentIndex()); + } +} + +// XXX - Copied from ui/gtk/menus.c + +/** + * Add the capture filename (with an absolute path) to the "Recent Files" menu. + */ +// XXX - We should probably create a RecentFile class. +void WiresharkMainWindow::updateRecentCaptures() { + QAction *ra; + QMenu *recentMenu = main_ui_->menuOpenRecentCaptureFile; + QString action_cf_name; + + if (!recentMenu) { + return; + } + recentMenu->clear(); + +#if 0 +#if defined(QT_WINEXTRAS_LIB) + QWinJumpList recent_jl(this); + QWinJumpListCategory *recent_jlc = recent_jl.recent(); + if (recent_jlc) { + recent_jlc->clear(); + recent_jlc->setVisible(true); + } +#endif +#endif +#if defined(Q_OS_MAC) + if (!dock_menu_) { + dock_menu_ = new QMenu(); + dock_menu_->setAsDockMenu(); + } + dock_menu_->clear(); +#endif + + /* Iterate through the actions in menuOpenRecentCaptureFile, + * removing special items, a maybe duplicate entry and every item above count_max */ + int shortcut = Qt::Key_0; + foreach(recent_item_status *ri, mainApp->recentItems()) { + // Add the new item + ra = new QAction(recentMenu); + ra->setData(ri->filename); + // XXX - Needs get_recent_item_status or equivalent + ra->setEnabled(ri->accessible); + recentMenu->insertAction(NULL, ra); + action_cf_name = ra->data().toString(); + if (shortcut <= Qt::Key_9) { + ra->setShortcut(Qt::META | (Qt::Key)shortcut); + shortcut++; + } + ra->setText(action_cf_name); + connect(ra, SIGNAL(triggered()), this, SLOT(recentActionTriggered())); + +/* This is slow, at least on my VM here. The added links also open Wireshark + * in a new window. It might make more sense to add a recent item when we + * open a capture file. */ +#if 0 +#if defined(QT_WINEXTRAS_LIB) + if (recent_jlc) { + QFileInfo fi(ri->filename); + QWinJumpListItem *jli = recent_jlc->addLink( + fi.fileName(), + QApplication::applicationFilePath(), + QStringList() << "-r" << ri->filename + ); + // XXX set icon + jli->setWorkingDirectory(QDir::toNativeSeparators(QApplication::applicationDirPath())); + } +#endif +#endif +#if defined(Q_OS_MAC) + QAction *rda = new QAction(dock_menu_); + QFileInfo fi(ri->filename); + rda->setText(fi.fileName()); + dock_menu_->insertAction(NULL, rda); + connect(rda, SIGNAL(triggered()), ra, SLOT(trigger())); +#endif + } + + if (recentMenu->actions().count() > 0) { + // Separator + "Clear" + // XXX - Do we really need this? + ra = new QAction(recentMenu); + ra->setSeparator(true); + recentMenu->insertAction(NULL, ra); + + ra = new QAction(recentMenu); + ra->setText(tr("Clear Menu")); + recentMenu->insertAction(NULL, ra); + connect(ra, SIGNAL(triggered()), mainApp, SLOT(clearRecentCaptures())); + } else { + if (main_ui_->actionDummyNoFilesFound) { + recentMenu->addAction(main_ui_->actionDummyNoFilesFound); + } + } +} + +void WiresharkMainWindow::recentActionTriggered() { + QAction *ra = qobject_cast(sender()); + + if (ra) { + QString cfPath = ra->data().toString(); + openCaptureFile(cfPath); + } +} + +QString WiresharkMainWindow::commentToMenuText(QString text, int max_len) +{ + text = text.trimmed().replace(QRegularExpression("(\\r?\\n|\\r\\n?)+"), " "); + if (text.size() > 0) { + if (text.size() > max_len) { + text.truncate(max_len); + text += "…"; + } + } + else { + text = tr("(empty comment)", "placeholder for empty comment"); + } + return text; +} + +void WiresharkMainWindow::setEditCommentsMenu() +{ + main_ui_->menuPacketComment->clear(); + QAction *action = main_ui_->menuPacketComment->addAction(tr("Add New Comment…")); + connect(action, &QAction::triggered, this, &WiresharkMainWindow::addPacketComment); + action->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_C)); + if (selectedRows().count() == 1) { + const int thisRow = selectedRows().first(); + frame_data * current_frame = frameDataForRow(thisRow); + wtap_block_t pkt_block = cf_get_packet_block(capture_file_.capFile(), current_frame); + guint nComments = wtap_block_count_option(pkt_block, OPT_COMMENT); + if (nComments > 0) { + main_ui_->menuPacketComment->addSeparator(); + for (guint i = 0; i < nComments; i++) { + QString comment = packet_list_->getPacketComment(i); + comment = this->commentToMenuText(comment); + action = main_ui_->menuPacketComment->addAction(tr("Edit \"%1\"", "edit packet comment").arg(comment)); + connect(action, &QAction::triggered, this, &WiresharkMainWindow::editPacketComment); + action->setData(i); + } + + main_ui_->menuPacketComment->addSeparator(); + for (guint i = 0; i < nComments; i++) { + QString comment = packet_list_->getPacketComment(i); + comment = this->commentToMenuText(comment); + action = main_ui_->menuPacketComment->addAction(tr("Delete \"%1\"", "delete packet comment").arg(comment)); + connect(action, &QAction::triggered, this, &WiresharkMainWindow::deletePacketComment); + action->setData(i); + } + main_ui_->menuPacketComment->addSeparator(); + action = main_ui_->menuPacketComment->addAction(tr("Delete packet comments")); + connect(action, &QAction::triggered, this, &WiresharkMainWindow::deleteCommentsFromPackets); + } + wtap_block_unref(pkt_block); + } + if (selectedRows().count() > 1) { + main_ui_->menuPacketComment->addSeparator(); + action = main_ui_->menuPacketComment->addAction(tr("Delete comments from %n packet(s)", nullptr, static_cast(selectedRows().count()))); + connect(action, &QAction::triggered, this, &WiresharkMainWindow::deleteCommentsFromPackets); + } +} + +void WiresharkMainWindow::setMenusForSelectedPacket() +{ + gboolean is_ip = FALSE, is_tcp = FALSE, is_udp = FALSE, is_sctp = FALSE, is_tls = FALSE, is_rtp = FALSE, is_lte_rlc = FALSE, + is_quic = FALSE, is_exported_pdu = FALSE; + + /* Making the menu context-sensitive allows for easier selection of the + desired item and has the added benefit, with large captures, of + avoiding needless looping through huge lists for marked, ignored, + or time-referenced packets. */ + + /* We have one or more items in the packet list */ + bool have_frames = false; + /* A frame is selected */ + bool frame_selected = false; + bool multi_selection = false; + /* A visible packet comes after this one in the selection history */ + bool next_selection_history = false; + /* A visible packet comes before this one in the selection history */ + bool previous_selection_history = false; + /* We have marked frames. (XXX - why check frame_selected?) */ + bool have_marked = false; + /* We have a marked frame other than the current frame (i.e., + we have at least one marked frame, and either there's more + than one marked frame or the current frame isn't marked). */ + bool another_is_marked = false; + /* One or more frames are hidden by a display filter */ + bool have_filtered = false; + /* One or more frames have been ignored */ + bool have_ignored = false; + bool have_time_ref = false; + /* We have a time reference frame other than the current frame (i.e., + we have at least one time reference frame, and either there's more + than one time reference frame or the current frame isn't a + time reference frame). (XXX - why check frame_selected?) */ + bool another_is_time_ref = false; + + QList cc_actions = QList() + << main_ui_->actionViewColorizeConversation1 << main_ui_->actionViewColorizeConversation2 + << main_ui_->actionViewColorizeConversation3 << main_ui_->actionViewColorizeConversation4 + << main_ui_->actionViewColorizeConversation5 << main_ui_->actionViewColorizeConversation6 + << main_ui_->actionViewColorizeConversation7 << main_ui_->actionViewColorizeConversation8 + << main_ui_->actionViewColorizeConversation9 << main_ui_->actionViewColorizeConversation10; + + if (capture_file_.capFile()) { + QList rows = selectedRows(); + frame_data * current_frame = 0; + if (rows.count() > 0) + current_frame = frameDataForRow(rows.at(0)); + + frame_selected = rows.count() == 1; + if (packet_list_->multiSelectActive()) + { + frame_selected = false; + multi_selection = true; + } + next_selection_history = packet_list_->haveNextHistory(); + previous_selection_history = packet_list_->havePreviousHistory(); + have_frames = capture_file_.capFile()->count > 0; + have_marked = capture_file_.capFile()->marked_count > 0; + another_is_marked = have_marked && rows.count() <= 1 && + !(capture_file_.capFile()->marked_count == 1 && frame_selected && current_frame->marked); + have_filtered = capture_file_.capFile()->displayed_count > 0 && capture_file_.capFile()->displayed_count != capture_file_.capFile()->count; + have_ignored = capture_file_.capFile()->ignored_count > 0; + have_time_ref = capture_file_.capFile()->ref_time_count > 0; + another_is_time_ref = have_time_ref && rows.count() <= 1 && + !(capture_file_.capFile()->ref_time_count == 1 && frame_selected && current_frame->ref_time); + + if (capture_file_.capFile()->edt && ! multi_selection) + { + proto_get_frame_protocols(capture_file_.capFile()->edt->pi.layers, + &is_ip, &is_tcp, &is_udp, &is_sctp, + &is_tls, &is_rtp, &is_lte_rlc); + /* TODO: to follow a QUIC stream we need a *decrypted* QUIC connection, i.e. checking for "quic" in the protocol stack is not enough */ + is_quic = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "quic"); + is_exported_pdu = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "exported_pdu"); + /* For Exported PDU there is a tag inserting IP addresses into the SRC and DST columns */ + if (is_exported_pdu && + (capture_file_.capFile()->edt->pi.net_src.type == AT_IPv4 || capture_file_.capFile()->edt->pi.net_src.type == AT_IPv6) && + (capture_file_.capFile()->edt->pi.net_dst.type == AT_IPv4 || capture_file_.capFile()->edt->pi.net_dst.type == AT_IPv6)) { + is_ip = TRUE; + } + foreach (FollowStreamAction *follow_action, main_ui_->menuFollow->findChildren()) { + /* QUIC has TLS handshakes; don't enabled Follow TLS Stream if + * there's QUIC. + */ + gboolean is_frame = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, follow_action->filterName()); + if (g_strcmp0(follow_action->filterName(), "tls") == 0) { + follow_action->setEnabled(is_frame && !is_quic); + } else { + follow_action->setEnabled(is_frame); + } + } + } + } + + main_ui_->actionEditMarkPacket->setText(tr("&Mark/Unmark Packet(s)", "", static_cast(selectedRows().count()))); + main_ui_->actionEditIgnorePacket->setText(tr("&Ignore/Unignore Packet(s)", "", static_cast(selectedRows().count()))); + + main_ui_->actionCopyListAsText->setEnabled(selectedRows().count() > 0); + main_ui_->actionCopyListAsCSV->setEnabled(selectedRows().count() > 0); + main_ui_->actionCopyListAsYAML->setEnabled(selectedRows().count() > 0); + + main_ui_->actionEditMarkPacket->setEnabled(frame_selected || multi_selection); + main_ui_->actionEditMarkAllDisplayed->setEnabled(have_frames); + /* Unlike un-ignore, do not allow unmark of all frames when no frames are displayed */ + main_ui_->actionEditUnmarkAllDisplayed->setEnabled(have_marked); + main_ui_->actionEditNextMark->setEnabled(another_is_marked); + main_ui_->actionEditPreviousMark->setEnabled(another_is_marked); + + GArray * linkTypes = Q_NULLPTR; + if (capture_file_.capFile() && capture_file_.capFile()->linktypes) + linkTypes = capture_file_.capFile()->linktypes; + + bool enableEditComments = linkTypes && wtap_dump_can_write(capture_file_.capFile()->linktypes, WTAP_COMMENT_PER_PACKET); + main_ui_->menuPacketComment->setEnabled(enableEditComments && selectedRows().count() > 0); + main_ui_->actionDeleteAllPacketComments->setEnabled(enableEditComments); + + main_ui_->actionEditIgnorePacket->setEnabled(frame_selected || multi_selection); + main_ui_->actionEditIgnoreAllDisplayed->setEnabled(have_filtered); + /* Allow un-ignore of all frames even with no frames currently displayed */ + main_ui_->actionEditUnignoreAllDisplayed->setEnabled(have_ignored); + + // XXX: Should we allow frames that don't have a time stamp to be + // set as time references? "Time" references are also used to reset + // the "Cumulative Bytes", so it's not entirely useless. + main_ui_->actionEditSetTimeReference->setEnabled(frame_selected); + main_ui_->actionEditUnsetAllTimeReferences->setEnabled(have_time_ref); + main_ui_->actionEditNextTimeReference->setEnabled(another_is_time_ref); + main_ui_->actionEditPreviousTimeReference->setEnabled(another_is_time_ref); + main_ui_->actionEditTimeShift->setEnabled(have_frames); + + main_ui_->actionGoGoToLinkedPacket->setEnabled(false); + main_ui_->actionGoNextHistoryPacket->setEnabled(next_selection_history); + main_ui_->actionGoPreviousHistoryPacket->setEnabled(previous_selection_history); + + foreach(QAction *cc_action, cc_actions) { + cc_action->setEnabled(frame_selected); + } + main_ui_->actionViewColorizeNewColoringRule->setEnabled(frame_selected); + + main_ui_->actionViewColorizeResetColorization->setEnabled(tmp_color_filters_used()); + + main_ui_->actionViewShowPacketInNewWindow->setEnabled(frame_selected); + main_ui_->actionViewEditResolvedName->setEnabled(frame_selected && is_ip); + + emit packetInfoChanged(capture_file_.packetInfo()); + +// set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/ResolveName", +// frame_selected && (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name || +// gbl_resolv_flags.transport_name)); + + main_ui_->actionToolsFirewallAclRules->setEnabled(frame_selected); + + main_ui_->actionStatisticsTcpStreamRoundTripTime->setEnabled(is_tcp); + main_ui_->actionStatisticsTcpStreamStevens->setEnabled(is_tcp); + main_ui_->actionStatisticsTcpStreamTcptrace->setEnabled(is_tcp); + main_ui_->actionStatisticsTcpStreamThroughput->setEnabled(is_tcp); + main_ui_->actionStatisticsTcpStreamWindowScaling->setEnabled(is_tcp); + + main_ui_->actionSCTPAnalyseThisAssociation->setEnabled(is_sctp); + main_ui_->actionSCTPShowAllAssociations->setEnabled(is_sctp); + main_ui_->actionSCTPFilterThisAssociation->setEnabled(is_sctp); + main_ui_->actionTelephonyRtpStreamAnalysis->setEnabled(is_rtp); + main_ui_->actionTelephonyRtpPlayer->setEnabled(is_rtp); + main_ui_->actionTelephonyLteRlcGraph->setEnabled(is_lte_rlc); +} + +void WiresharkMainWindow::setMenusForSelectedTreeRow(FieldInformation *finfo) { + + bool can_match_selected = false; + bool is_framenum = false; + bool have_subtree = false; + bool can_open_url = false; + bool have_packet_bytes = false; + QByteArray field_filter; + int field_id = -1; + + field_info * fi = 0; + if (finfo) + fi = finfo->fieldInfo(); + + if (capture_file_.capFile()) { + capture_file_.capFile()->finfo_selected = fi; + + if (fi && fi->tree_type != -1) { + have_subtree = true; + } + + if (fi && fi->ds_tvb && (fi->length > 0)) { + have_packet_bytes = true; + } + } + + if (capture_file_.capFile() != NULL && fi != NULL) { + header_field_info *hfinfo = fi->hfinfo; + int linked_frame = -1; + + can_match_selected = proto_can_match_selected(capture_file_.capFile()->finfo_selected, capture_file_.capFile()->edt); + if (hfinfo && hfinfo->type == FT_FRAMENUM) { + is_framenum = true; + linked_frame = fvalue_get_uinteger(fi->value); + } + + char *tmp_field = proto_construct_match_selected_string(fi, capture_file_.capFile()->edt); + field_filter = tmp_field; + wmem_free(NULL, tmp_field); + emit fieldFilterChanged(field_filter); + + field_id = fi->hfinfo->id; + /* if the selected field isn't a protocol, get its parent */ + if (!proto_registrar_is_protocol(field_id)) { + field_id = proto_registrar_get_parent(fi->hfinfo->id); + } + + if (field_id >= 0) { + can_open_url = true; + main_ui_->actionContextWikiProtocolPage->setData(field_id); + main_ui_->actionContextFilterFieldReference->setData(field_id); + } else { + main_ui_->actionContextWikiProtocolPage->setData(QVariant()); + main_ui_->actionContextFilterFieldReference->setData(QVariant()); + } + + if (linked_frame > 0) { + main_ui_->actionGoGoToLinkedPacket->setData(linked_frame); + } else { + main_ui_->actionGoGoToLinkedPacket->setData(QVariant()); + } + } + + // Always enable / disable the following items. + main_ui_->actionCopyAllVisibleItems->setEnabled(capture_file_.capFile() != NULL && ! packet_list_->multiSelectActive()); + main_ui_->actionCopyAllVisibleSelectedTreeItems->setEnabled(can_match_selected); + main_ui_->actionEditCopyDescription->setEnabled(can_match_selected); + main_ui_->actionEditCopyFieldName->setEnabled(can_match_selected); + main_ui_->actionEditCopyValue->setEnabled(can_match_selected); + main_ui_->actionEditCopyAsFilter->setEnabled(can_match_selected); + + main_ui_->actionAnalyzeShowPacketBytes->setEnabled(have_packet_bytes); + main_ui_->actionFileExportPacketBytes->setEnabled(have_packet_bytes); + + main_ui_->actionViewExpandSubtrees->setEnabled(have_subtree); + main_ui_->actionViewCollapseSubtrees->setEnabled(have_subtree); + + main_ui_->actionGoGoToLinkedPacket->setEnabled(is_framenum); + + main_ui_->actionAnalyzeApplyAsColumn->setEnabled(can_match_selected); + + main_ui_->actionContextShowLinkedPacketInNewWindow->setEnabled(is_framenum); + + main_ui_->actionContextWikiProtocolPage->setEnabled(can_open_url); + main_ui_->actionContextFilterFieldReference->setEnabled(can_open_url); + + +// Only enable / disable the following items if we have focus so that we +// don't clobber anything we may have set in setMenusForSelectedPacket. + if (!proto_tree_ || !proto_tree_->hasFocus()) return; + + emit packetInfoChanged(capture_file_.packetInfo()); + + // set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ResolveName", + // frame_selected && (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name || + // gbl_resolv_flags.transport_name)); + +} + +void WiresharkMainWindow::interfaceSelectionChanged() +{ +#ifdef HAVE_LIBPCAP + // XXX This doesn't disable the toolbar button when using + // QtMacExtras. + if (global_capture_opts.num_selected > 0 && capture_filter_valid_) { + main_ui_->actionCaptureStart->setEnabled(true); + } else { + main_ui_->actionCaptureStart->setEnabled(false); + } +#endif // HAVE_LIBPCAP +} + +void WiresharkMainWindow::captureFilterSyntaxChanged(bool valid) +{ + capture_filter_valid_ = valid; + interfaceSelectionChanged(); +} + +void WiresharkMainWindow::startInterfaceCapture(bool valid, const QString capture_filter) +{ + capture_filter_valid_ = valid; + welcome_page_->setCaptureFilter(capture_filter); + QString before_what(tr(" before starting a new capture")); + if (testCaptureFileClose(before_what)) { + // The interface tree will update the selected interfaces via its timer + // so no need to do anything here. + startCapture(QStringList()); + } +} + +void WiresharkMainWindow::applyGlobalCommandLineOptions() +{ + if (global_dissect_options.time_format != TS_NOT_SET) { + foreach(QAction* tda, td_actions.keys()) { + if (global_dissect_options.time_format == td_actions[tda]) { + tda->setChecked(true); + // XXX - this means that if the user sets the + // time stamp format with the -t flag, that + // setting will persist and will be used as + // the default the next time Wireshark is run. + recent.gui_time_format = global_dissect_options.time_format; + timestamp_set_type(global_dissect_options.time_format); + break; + } + } + } + if (global_dissect_options.time_precision != TS_PREC_NOT_SET) { + foreach(QAction* tpa, tp_actions.keys()) { + if (global_dissect_options.time_precision == tp_actions[tpa]) { + tpa->setChecked(true); + // XXX - this means that if the user sets the + // time stamp precision with the -t flag, that + // setting will persist and will be used as + // the default the next time Wireshark is run. + recent.gui_time_precision = global_dissect_options.time_precision; + timestamp_set_precision(global_dissect_options.time_precision); + break; + } + } + } + if (global_commandline_info.full_screen) { + this->showFullScreen(); + } +} + +void WiresharkMainWindow::redissectPackets() +{ + if (capture_file_.capFile()) { + cf_redissect_packets(capture_file_.capFile()); + main_ui_->statusBar->expertUpdate(); + } + + proto_free_deregistered_fields(); +} + +void WiresharkMainWindow::checkDisplayFilter() +{ + if (!df_combo_box_->checkDisplayFilter()) { + g_free(CaptureFile::globalCapFile()->dfilter); + CaptureFile::globalCapFile()->dfilter = NULL; + } +} + +void WiresharkMainWindow::fieldsChanged() +{ + gchar *err_msg = NULL; + if (!color_filters_reload(&err_msg, color_filter_add_cb)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); + g_free(err_msg); + } + tap_listeners_dfilter_recompile(); + + emit checkDisplayFilter(); + + if (have_custom_cols(&CaptureFile::globalCapFile()->cinfo)) { + // Recreate packet list columns according to new/changed/deleted fields + packet_list_->fieldsChanged(CaptureFile::globalCapFile()); + } + + emit reloadFields(); +} + +void WiresharkMainWindow::reloadLuaPlugins() +{ +#ifdef HAVE_LUA + if (mainApp->isReloadingLua()) + return; + + gboolean uses_lua_filehandler = FALSE; + + if (capture_file_.capFile()) { + // Check if the current capture file is opened with a Lua FileHandler + capture_file *cf = capture_file_.capFile(); + uses_lua_filehandler = wtap_uses_lua_filehandler(cf->provider.wth); + + if (uses_lua_filehandler && cf->unsaved_changes) { + // Prompt to save the file before reloading, in case the FileHandler has changed + QString before_what(tr(" before reloading Lua plugins")); + if (!testCaptureFileClose(before_what, Reload)) { + return; + } + } + } + + mainApp->setReloadingLua(true); + + wslua_reload_plugins(NULL, NULL); + this->clearAddedPacketMenus(); + funnel_statistics_reload_menus(); + reloadDynamicMenus(); + closePacketDialogs(); + + // Preferences may have been deleted so close all widgets using prefs + main_ui_->preferenceEditorFrame->animatedHide(); + + mainApp->readConfigurationFiles(true); + commandline_options_reapply(); + + fieldsChanged(); + prefs_apply_all(); + + if (uses_lua_filehandler) { + // Reload the file in case the FileHandler has changed + if (cf_reload(capture_file_.capFile()) != CF_OK) { + cf_close(capture_file_.capFile()); + } + proto_free_deregistered_fields(); + } else { + redissectPackets(); + } + + mainApp->setReloadingLua(false); + SimpleDialog::displayQueuedMessages(); +#endif +} + +void WiresharkMainWindow::showAccordionFrame(AccordionFrame *show_frame, bool toggle) +{ + QListframe_list = QList() + << main_ui_->goToFrame << main_ui_->searchFrame + << main_ui_->addressEditorFrame << main_ui_->columnEditorFrame + << main_ui_->preferenceEditorFrame << main_ui_->filterExpressionFrame; + + frame_list.removeAll(show_frame); + foreach(AccordionFrame *af, frame_list) af->animatedHide(); + + if (toggle) { + if (show_frame->isVisible()) { + show_frame->animatedHide(); + return; + } + } + show_frame->animatedShow(); +} + +void WiresharkMainWindow::showColumnEditor(int column) +{ + setPreviousFocus(); + main_ui_->columnEditorFrame->editColumn(column); + showAccordionFrame(main_ui_->columnEditorFrame); +} + +void WiresharkMainWindow::showPreferenceEditor() +{ + showAccordionFrame(main_ui_->preferenceEditorFrame); +} + +void WiresharkMainWindow::initViewColorizeMenu() +{ + QList cc_actions = QList() + << main_ui_->actionViewColorizeConversation1 << main_ui_->actionViewColorizeConversation2 + << main_ui_->actionViewColorizeConversation3 << main_ui_->actionViewColorizeConversation4 + << main_ui_->actionViewColorizeConversation5 << main_ui_->actionViewColorizeConversation6 + << main_ui_->actionViewColorizeConversation7 << main_ui_->actionViewColorizeConversation8 + << main_ui_->actionViewColorizeConversation9 << main_ui_->actionViewColorizeConversation10; + + guint8 color_num = 1; + + foreach(QAction *cc_action, cc_actions) { + cc_action->setData(color_num); + connect(cc_action, SIGNAL(triggered()), this, SLOT(colorizeConversation())); + + const color_filter_t *colorf = color_filters_tmp_color(color_num); + if (colorf) { + QColor bg = ColorUtils::fromColorT(colorf->bg_color); + QColor fg = ColorUtils::fromColorT(colorf->fg_color); + cc_action->setIcon(StockIcon::colorIcon(bg.rgb(), fg.rgb(), QString::number(color_num))); + } + color_num++; + } + +#ifdef Q_OS_MAC + // Spotlight uses Cmd+Space + main_ui_->actionViewColorizeResetColorization->setShortcut(QKeySequence("Meta+Space")); +#endif +} + +void WiresharkMainWindow::addStatsPluginsToMenu() { + GList *cfg_list = stats_tree_get_cfg_list(); + QAction *stats_tree_action; + QMenu *parent_menu; + bool first_item = true; + + for (GList *iter = g_list_first(cfg_list); iter; iter = gxx_list_next(iter)) { + stats_tree_cfg *cfg = gxx_list_data(stats_tree_cfg *, iter); + if (!menu_groups_.contains(cfg->stat_group)) { + continue; + } + if (cfg->plugin) { + if (first_item) { + main_ui_->menuStatistics->addSeparator(); + first_item = false; + } + + parent_menu = main_ui_->menuStatistics; + // gtk/main_menubar.c compresses double slashes, hence SkipEmptyParts +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList cfg_name_parts = QString(cfg->name).split("/", Qt::SkipEmptyParts); +#else + QStringList cfg_name_parts = QString(cfg->name).split("/", QString::SkipEmptyParts); +#endif + if (cfg_name_parts.isEmpty()) continue; + + QString stat_name = cfg_name_parts.takeLast(); + if (!cfg_name_parts.isEmpty()) { + QString menu_name = cfg_name_parts.join("/"); + parent_menu = findOrAddMenu(parent_menu, menu_name); + } + + stats_tree_action = new QAction(stat_name, this); + stats_tree_action->setData(QString::fromUtf8(cfg->abbr)); + parent_menu->addAction(stats_tree_action); + connect(stats_tree_action, &QAction::triggered, this, [this]() { + QAction* action = qobject_cast(sender()); + if (action) { + openStatisticsTreeDialog(action->data().toString().toUtf8()); + } + }); + } + } + g_list_free(cfg_list); +} + +void WiresharkMainWindow::setFeaturesEnabled(bool enabled) +{ + main_ui_->menuBar->setEnabled(enabled); + main_ui_->mainToolBar->setEnabled(enabled); + main_ui_->displayFilterToolBar->setEnabled(enabled); + if (enabled) + { + main_ui_->statusBar->clearMessage(); +#ifdef HAVE_LIBPCAP + main_ui_->actionGoAutoScroll->setChecked(recent.capture_auto_scroll); +#endif + } + else + { + main_ui_->statusBar->showMessage(tr("Please wait while Wireshark is initializing…")); + } +} + +// Display Filter Toolbar + +void WiresharkMainWindow::on_actionNewDisplayFilterExpression_triggered() +{ + main_ui_->filterExpressionFrame->addExpression(df_combo_box_->lineEdit()->text()); +} + +void WiresharkMainWindow::onFilterSelected(QString filterText, bool prepare) +{ + if (filterText.length() <= 0) + return; + + df_combo_box_->setDisplayFilter(filterText); + // Holding down the Shift key will only prepare filter. + if (!prepare) + df_combo_box_->applyDisplayFilter(); +} + +void WiresharkMainWindow::onFilterPreferences() +{ + emit showPreferencesDialog(PrefsModel::typeToString(PrefsModel::FilterButtons)); +} + +void WiresharkMainWindow::onFilterEdit(int uatIndex) +{ + main_ui_->filterExpressionFrame->editExpression(uatIndex); +} + +void WiresharkMainWindow::openStatCommandDialog(const QString &menu_path, const char *arg, void *userdata) +{ + QString slot = QString("statCommand%1").arg(menu_path); + QMetaObject::invokeMethod(this, slot.toLatin1().constData(), Q_ARG(const char *, arg), Q_ARG(void *, userdata)); +} + +void WiresharkMainWindow::openTapParameterDialog(const QString cfg_str, const QString arg, void *userdata) +{ + TapParameterDialog *tp_dialog = TapParameterDialog::showTapParameterStatistics(*this, capture_file_, cfg_str, arg, userdata); + if (!tp_dialog) return; + + connect(tp_dialog, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + connect(tp_dialog, SIGNAL(updateFilter(QString)), + df_combo_box_->lineEdit(), SLOT(setText(QString))); + tp_dialog->show(); +} + +void WiresharkMainWindow::openTapParameterDialog() +{ + QAction *tpa = qobject_cast(QObject::sender()); + if (!tpa) return; + + const QString cfg_str = tpa->data().toString(); + openTapParameterDialog(cfg_str, NULL, NULL); +} + +#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) +void WiresharkMainWindow::softwareUpdateRequested() { + // testCaptureFileClose doesn't use this string because we aren't + // going to launch another dialog, but maybe we'll change that. + QString before_what(tr(" before updating")); + if (!testCaptureFileClose(before_what, Update)) { + mainApp->rejectSoftwareUpdate(); + } +} +#endif + +// File Menu + +void WiresharkMainWindow::connectFileMenuActions() +{ + connect(main_ui_->actionFileOpen, &QAction::triggered, this, + [this]() { openCaptureFile(); }); + + connect(main_ui_->actionFileMerge, &QAction::triggered, this, + [this]() { mergeCaptureFile(); }); + + connect(main_ui_->actionFileImportFromHexDump, &QAction::triggered, this, + [this]() { importCaptureFile(); }); + + connect(main_ui_->actionFileClose, &QAction::triggered, this, [this]() { + QString before_what(tr(" before closing the file")); + if (testCaptureFileClose(before_what)) { + showWelcome(); + } + }); + + connect(main_ui_->actionFileSave, &QAction::triggered, this, + [this]() { saveCaptureFile(capture_file_.capFile(), false); }); + + connect(main_ui_->actionFileSaveAs, &QAction::triggered, this, + [this]() { saveAsCaptureFile(capture_file_.capFile()); }); + + connect(main_ui_->actionFileSetListFiles, &QAction::triggered, this, + [this]() { file_set_dialog_->show(); }); + + connect(main_ui_->actionFileSetNextFile, &QAction::triggered, this, [this]() { + fileset_entry *entry = fileset_get_next(); + + if (entry) { + QString new_cf_path = entry->fullname; + openCaptureFile(new_cf_path); + } + }); + + connect(main_ui_->actionFileSetPreviousFile, &QAction::triggered, this, [this]() { + fileset_entry *entry = fileset_get_previous(); + + if (entry) { + QString new_cf_path = entry->fullname; + openCaptureFile(new_cf_path); + } + }); + + connect(main_ui_->actionFileExportPackets, &QAction::triggered, this, + [this]() { exportSelectedPackets(); }); + + connect(main_ui_->actionFileExportAsPlainText, &QAction::triggered, this, + [this]() { exportDissections(export_type_text); }); + + connect(main_ui_->actionFileExportAsCSV, &QAction::triggered, this, + [this]() { exportDissections(export_type_csv); }); + + connect(main_ui_->actionFileExportAsCArrays, &QAction::triggered, this, + [this]() { exportDissections(export_type_carrays); }); + + connect(main_ui_->actionFileExportAsPSML, &QAction::triggered, this, + [this]() { exportDissections(export_type_psml); }); + + connect(main_ui_->actionFileExportAsPDML, &QAction::triggered, this, + [this]() { exportDissections(export_type_pdml); }); + + connect(main_ui_->actionFileExportAsJSON, &QAction::triggered, this, + [this]() { exportDissections(export_type_json); }); + + connect(main_ui_->actionFileExportPacketBytes, &QAction::triggered, this, + [this]() { exportPacketBytes(); }, Qt::QueuedConnection); + + connect(main_ui_->actionFileExportPDU, &QAction::triggered, this, + [this]() { exportPDU(); }); + + connect(main_ui_->actionFileStripHeaders, &QAction::triggered, this, + [this]() { stripPacketHeaders(); }); + + connect(main_ui_->actionFileExportTLSSessionKeys, &QAction::triggered, this, + [this]() { exportTLSSessionKeys(); }); + + connect(main_ui_->actionFilePrint, &QAction::triggered, this, + [this]() { printFile(); }); +} + +void WiresharkMainWindow::exportPacketBytes() +{ + QString file_name; + + if (!capture_file_.capFile() || !capture_file_.capFile()->finfo_selected) return; + + file_name = WiresharkFileDialog::getSaveFileName(this, + mainApp->windowTitleString(tr("Export Selected Packet Bytes")), + mainApp->openDialogInitialDir().canonicalPath(), + tr("Raw data (*.bin *.dat *.raw);;All Files (" ALL_FILES_WILDCARD ")") + ); + + if (file_name.length() > 0) { + const guint8 *data_p; + + data_p = tvb_get_ptr(capture_file_.capFile()->finfo_selected->ds_tvb, 0, -1) + + capture_file_.capFile()->finfo_selected->start; + write_file_binary_mode(qUtf8Printable(file_name), data_p, capture_file_.capFile()->finfo_selected->length); + + /* Save the directory name for future file dialogs. */ + mainApp->setLastOpenDirFromFilename(file_name); + } +} + +void WiresharkMainWindow::exportPDU() +{ + ExportPDUDialog *exportpdu_dialog = new ExportPDUDialog(this); + + if (exportpdu_dialog->isMinimized() == true) + { + exportpdu_dialog->showNormal(); + } + else + { + exportpdu_dialog->show(); + } + + exportpdu_dialog->raise(); + exportpdu_dialog->activateWindow(); +} + +void WiresharkMainWindow::stripPacketHeaders() +{ + StripHeadersDialog *stripheaders_dialog = new StripHeadersDialog(this); + + if (stripheaders_dialog->isMinimized() == true) + { + stripheaders_dialog->showNormal(); + } + else + { + stripheaders_dialog->show(); + } + + stripheaders_dialog->raise(); + stripheaders_dialog->activateWindow(); +} + + +void WiresharkMainWindow::exportTLSSessionKeys() +{ + QString file_name; + QString save_title; + int keylist_len; + + keylist_len = ssl_session_key_count(); + /* don't show up the dialog, if no data has to be saved */ + if (keylist_len < 1) { + /* shouldn't happen as the menu item should have been greyed out */ + QMessageBox::warning( + this, + tr("No Keys"), + tr("There are no TLS Session Keys to save."), + QMessageBox::Ok + ); + return; + } + + save_title.append(mainApp->windowTitleString(tr("Export TLS Session Keys (%Ln key(s))", "", keylist_len))); + file_name = WiresharkFileDialog::getSaveFileName(this, + save_title, + mainApp->openDialogInitialDir().canonicalPath(), + tr("TLS Session Keys (*.keys *.txt);;All Files (" ALL_FILES_WILDCARD ")") + ); + if (file_name.length() > 0) { + gsize keylist_length; + gchar *keylist = ssl_export_sessions(&keylist_length); + write_file_binary_mode(qUtf8Printable(file_name), keylist, keylist_length); + + /* Save the directory name for future file dialogs. */ + mainApp->setLastOpenDirFromFilename(file_name); + g_free(keylist); + } +} + +void WiresharkMainWindow::printFile() +{ + capture_file *cf = capture_file_.capFile(); + g_return_if_fail(cf); + + QList rows = packet_list_->selectedRows(true); + + QStringList entries; + foreach (int row, rows) + entries << QString::number(row); + QString selRange = entries.join(","); + + PrintDialog * pdlg_ = new PrintDialog(this, cf, selRange); + pdlg_->setWindowModality(Qt::ApplicationModal); + pdlg_->show(); +} + +// Edit Menu + +void WiresharkMainWindow::connectEditMenuActions() +{ + connect(main_ui_->actionCopyAllVisibleItems, &QAction::triggered, this, + [this]() { copySelectedItems(CopyAllVisibleItems); }); + + connect(main_ui_->actionCopyListAsText, &QAction::triggered, this, + [this]() { copySelectedItems(CopyListAsText); }); + + connect(main_ui_->actionCopyListAsCSV, &QAction::triggered, this, + [this]() { copySelectedItems(CopyListAsCSV); }); + + connect(main_ui_->actionCopyListAsYAML, &QAction::triggered, this, + [this]() { copySelectedItems(CopyListAsYAML); }); + + connect(main_ui_->actionCopyAllVisibleSelectedTreeItems, &QAction::triggered, this, + [this]() { copySelectedItems(CopyAllVisibleSelectedTreeItems); }); + + connect(main_ui_->actionEditCopyDescription, &QAction::triggered, this, + [this]() { copySelectedItems(CopySelectedDescription); }); + + connect(main_ui_->actionEditCopyFieldName, &QAction::triggered, this, + [this]() { copySelectedItems(CopySelectedFieldName); }); + + connect(main_ui_->actionEditCopyValue, &QAction::triggered, this, + [this]() { copySelectedItems(CopySelectedValue); }); + + connect(main_ui_->actionEditCopyAsFilter, &QAction::triggered, this, + [this]() { matchFieldFilter(FilterAction::ActionCopy, FilterAction::ActionTypePlain); }); + + connect(main_ui_->actionEditFindPacket, &QAction::triggered, this, + [this]() { findPacket(); }); + + connect(main_ui_->actionEditFindNext, &QAction::triggered, this, + [this]() { main_ui_->searchFrame->findNext(); }); + + connect(main_ui_->actionEditFindPrevious, &QAction::triggered, this, + [this]() { main_ui_->searchFrame->findPrevious(); }); + + // The items below are used in the packet list and detail context menus. + // Use QueuedConnections so that the context menus aren't destroyed + // prematurely. + connect(main_ui_->actionEditMarkPacket, &QAction::triggered, this, [this]() { + freeze(); + packet_list_->markFrame(); + thaw(); + setMenusForSelectedPacket(); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditMarkAllDisplayed, &QAction::triggered, this, [this]() { + freeze(); + packet_list_->markAllDisplayedFrames(true); + thaw(); + setMenusForSelectedPacket(); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditUnmarkAllDisplayed, &QAction::triggered, this, [this]() { + freeze(); + packet_list_->markAllDisplayedFrames(false); + thaw(); + setMenusForSelectedPacket(); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditNextMark, &QAction::triggered, this, [this]() { + if (capture_file_.capFile()) { + cf_find_packet_marked(capture_file_.capFile(), SD_FORWARD); + } + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditPreviousMark, &QAction::triggered, this, [this]() { + if (capture_file_.capFile()) { + cf_find_packet_marked(capture_file_.capFile(), SD_BACKWARD); + } + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditIgnorePacket, &QAction::triggered, this, [this]() { + freeze(); + packet_list_->ignoreFrame(); + thaw(); + setMenusForSelectedPacket(); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditIgnoreAllDisplayed, &QAction::triggered, this, [this]() { + freeze(); + packet_list_->ignoreAllDisplayedFrames(true); + thaw(); + setMenusForSelectedPacket(); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditUnignoreAllDisplayed, &QAction::triggered, this, [this]() { + freeze(); + packet_list_->ignoreAllDisplayedFrames(false); + thaw(); + setMenusForSelectedPacket(); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditSetTimeReference, &QAction::triggered, this, [this]() { + packet_list_->setTimeReference(); + setMenusForSelectedPacket(); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditUnsetAllTimeReferences, &QAction::triggered, this, [this]() { + packet_list_->unsetAllTimeReferences(); + setMenusForSelectedPacket(); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditNextTimeReference, &QAction::triggered, this, [this]() { + if (!capture_file_.capFile()) return; + cf_find_packet_time_reference(capture_file_.capFile(), SD_FORWARD); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditPreviousTimeReference, &QAction::triggered, this, [this]() { + if (!capture_file_.capFile()) return; + cf_find_packet_time_reference(capture_file_.capFile(), SD_BACKWARD); + }, Qt::QueuedConnection); + + connect(main_ui_->actionEditTimeShift, &QAction::triggered, this, + [this]() { editTimeShift(); }, Qt::QueuedConnection); + + connect(main_ui_->actionDeleteAllPacketComments, &QAction::triggered, this, + [this]() { deleteAllPacketComments(); }, Qt::QueuedConnection); + + connect(main_ui_->actionEditInjectSecrets, &QAction::triggered, this, + [this]() { injectSecrets(); }, Qt::QueuedConnection); + + connect(main_ui_->actionEditDiscardAllSecrets, &QAction::triggered, this, + [this]() { discardAllSecrets(); }, Qt::QueuedConnection); + + connect(main_ui_->actionEditConfigurationProfiles, &QAction::triggered, this, + [this]() { editConfigurationProfiles(); }, Qt::QueuedConnection); + + connect(main_ui_->actionEditPreferences, &QAction::triggered, this, + [this]() { showPreferencesDialog(PrefsModel::typeToString(PrefsModel::Appearance)); }, Qt::QueuedConnection); +} + +// XXX This should probably be somewhere else. +void WiresharkMainWindow::copySelectedItems(WiresharkMainWindow::CopySelected selection_type) +{ + char label_str[ITEM_LABEL_LENGTH]; + QString clip; + + if (!capture_file_.capFile()) return; + + field_info *finfo_selected = capture_file_.capFile()->finfo_selected; + + switch (selection_type) { + case CopySelectedDescription: + if (proto_tree_->selectionModel()->hasSelection()) { + QModelIndex idx = proto_tree_->selectionModel()->selectedIndexes().first(); + clip = idx.data(Qt::DisplayRole).toString(); + } + break; + case CopySelectedFieldName: + if (finfo_selected && finfo_selected->hfinfo->abbrev != 0) { + clip.append(finfo_selected->hfinfo->abbrev); + } + break; + case CopySelectedValue: + if (finfo_selected && capture_file_.capFile()->edt != 0) { + gchar* field_str = get_node_field_value(finfo_selected, capture_file_.capFile()->edt); + clip.append(field_str); + g_free(field_str); + } + break; + case CopyAllVisibleItems: + clip = proto_tree_->toString(); + break; + case CopyAllVisibleSelectedTreeItems: + if (proto_tree_->selectionModel()->hasSelection()) { + clip = proto_tree_->toString(proto_tree_->selectionModel()->selectedIndexes().first()); + } + break; + case CopyListAsText: + case CopyListAsCSV: + case CopyListAsYAML: + if (packet_list_->selectedRows().count() > 0) + { + QList rows = packet_list_->selectedRows(); + QStringList content; + + PacketList::SummaryCopyType copyType = PacketList::CopyAsText; + if (selection_type == CopyListAsCSV) + copyType = PacketList::CopyAsCSV; + else if (selection_type == CopyListAsYAML) + copyType = PacketList::CopyAsYAML; + + if ((copyType == PacketList::CopyAsText) || + (copyType == PacketList::CopyAsCSV)) { + QString headerEntry = packet_list_->createHeaderSummaryText(copyType); + content << headerEntry; + } + foreach (int row, rows) + { + QModelIndex idx = packet_list_->model()->index(row, 0); + if (! idx.isValid()) + continue; + + QString entry = packet_list_->createSummaryText(idx, copyType); + content << entry; + } + + if (content.count() > 0) { + clip = content.join("\n"); + // + // Each YAML item ends with a newline, so the string + // ends with a newline already if it's CopyListAsYAML. + // If we add a newline, there'd be an extra blank + // line. + // + // Otherwise, we've used newlines as separators, not + // terminators, so there's no final newline. Add it. + // + if (selection_type != CopyListAsYAML) + clip += "\n"; + } + } + break; + } + + if (clip.length() == 0) { + /* If no representation then... Try to read the value */ + proto_item_fill_label(capture_file_.capFile()->finfo_selected, label_str); + clip.append(label_str); + } + + if (clip.length()) { + mainApp->clipboard()->setText(clip); + } else { + QString err = tr("Couldn't copy text. Try another item."); + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, err); + } +} + +void WiresharkMainWindow::findPacket() +{ + if (! packet_list_->model() || packet_list_->model()->rowCount() < 1) { + return; + } + setPreviousFocus(); + if (!main_ui_->searchFrame->isVisible()) { + showAccordionFrame(main_ui_->searchFrame, true); + } else { + main_ui_->searchFrame->animatedHide(); + } + main_ui_->searchFrame->setFocus(); +} + +void WiresharkMainWindow::editTimeShift() +{ + TimeShiftDialog *ts_dialog = new TimeShiftDialog(this, capture_file_.capFile()); + connect(ts_dialog, SIGNAL(finished(int)), this, SLOT(editTimeShiftFinished(int))); + + connect(this, SIGNAL(setCaptureFile(capture_file*)), + ts_dialog, SLOT(setCaptureFile(capture_file*))); + connect(ts_dialog, SIGNAL(timeShifted()), packet_list_, SLOT(applyTimeShift())); + + ts_dialog->setWindowModality(Qt::ApplicationModal); + ts_dialog->setAttribute(Qt::WA_DeleteOnClose); + ts_dialog->show(); +} + +void WiresharkMainWindow::editTimeShiftFinished(int) +{ + if (capture_file_.capFile()->unsaved_changes) { + updateForUnsavedChanges(); + } +} + +void WiresharkMainWindow::addPacketComment() +{ + QList rows = selectedRows(); + if (rows.count() == 0) + return; + + frame_data * fdata = frameDataForRow(rows.at(0)); + if (! fdata) + return; + + PacketCommentDialog* pc_dialog; + pc_dialog = new PacketCommentDialog(false, this, NULL); + connect(pc_dialog, &QDialog::finished, std::bind(&WiresharkMainWindow::addPacketCommentFinished, this, pc_dialog, std::placeholders::_1)); + pc_dialog->setWindowModality(Qt::ApplicationModal); + pc_dialog->setAttribute(Qt::WA_DeleteOnClose); + pc_dialog->show(); +} + +void WiresharkMainWindow::addPacketCommentFinished(PacketCommentDialog* pc_dialog _U_, int result _U_) +{ + if (result == QDialog::Accepted) { + packet_list_->addPacketComment(pc_dialog->text()); + updateForUnsavedChanges(); + } +} + +void WiresharkMainWindow::editPacketComment() +{ + QList rows = selectedRows(); + if (rows.count() != 1) + return; + + QAction *ra = qobject_cast(sender()); + guint nComment = ra->data().toUInt(); + PacketCommentDialog* pc_dialog; + pc_dialog = new PacketCommentDialog(true, this, packet_list_->getPacketComment(nComment)); + connect(pc_dialog, &QDialog::finished, std::bind(&WiresharkMainWindow::editPacketCommentFinished, this, pc_dialog, std::placeholders::_1, nComment)); + pc_dialog->setWindowModality(Qt::ApplicationModal); + pc_dialog->setAttribute(Qt::WA_DeleteOnClose); + pc_dialog->show(); +} + +void WiresharkMainWindow::editPacketCommentFinished(PacketCommentDialog* pc_dialog _U_, int result _U_, guint nComment) +{ + if (result == QDialog::Accepted) { + packet_list_->setPacketComment(nComment, pc_dialog->text()); + updateForUnsavedChanges(); + } +} + +void WiresharkMainWindow::deletePacketComment() +{ + QAction *ra = qobject_cast(sender()); + guint nComment = ra->data().toUInt(); + packet_list_->setPacketComment(nComment, QString("")); + updateForUnsavedChanges(); +} + +void WiresharkMainWindow::deleteCommentsFromPackets() +{ + packet_list_->deleteCommentsFromPackets(); + updateForUnsavedChanges(); +} + +void WiresharkMainWindow::deleteAllPacketComments() +{ + QMessageBox *msg_dialog = new QMessageBox(); + connect(msg_dialog, SIGNAL(finished(int)), this, SLOT(deleteAllPacketCommentsFinished(int))); + + msg_dialog->setIcon(QMessageBox::Question); + msg_dialog->setText(tr("Are you sure you want to remove all packet comments?")); + + msg_dialog->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + msg_dialog->setDefaultButton(QMessageBox::Ok); + + msg_dialog->setWindowModality(Qt::ApplicationModal); + msg_dialog->setAttribute(Qt::WA_DeleteOnClose); + msg_dialog->show(); +} + +void WiresharkMainWindow::deleteAllPacketCommentsFinished(int result) +{ + if (result == QMessageBox::Ok) { + /* XXX Do we need a wait/hourglass for large files? */ + packet_list_->deleteAllPacketComments(); + updateForUnsavedChanges(); + } +} + +void WiresharkMainWindow::injectSecrets() +{ + int keylist_len; + + keylist_len = ssl_session_key_count(); + /* don't do anything if no data has to be saved */ + if (keylist_len < 1) { + QMessageBox::Button ret = QMessageBox::warning( + this, + tr("No TLS Secrets"), + tr("There are no available secrets used to decrypt TLS traffic in the capture file.\ + Would you like to view information about how to decrypt TLS traffic on the wiki?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (ret != QMessageBox::Yes) return; + + QUrl wiki_url = QString(WS_WIKI_URL("TLS/#tls-decryption")); + QDesktopServices::openUrl(wiki_url); + return; + } + + if (!capture_file_.isValid()) + return; + + /* XXX: It would be nice to handle other types of secrets that + * can be written to a DSB, maybe have a proper dialog. + */ + capture_file *cf = capture_file_.capFile(); + tls_export_dsb(cf); + updateForUnsavedChanges(); +} + +void WiresharkMainWindow::discardAllSecrets() +{ + if (!capture_file_.isValid()) + return; + + QMessageBox* msg_dialog = new QMessageBox(); + connect(msg_dialog, SIGNAL(finished(int)), this, SLOT(discardAllSecretsFinished(int))); + + msg_dialog->setIcon(QMessageBox::Question); + msg_dialog->setText(tr("Are you sure you want to discard all decryption secrets?")); + + msg_dialog->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + msg_dialog->setDefaultButton(QMessageBox::Ok); + + msg_dialog->setWindowModality(Qt::ApplicationModal); + msg_dialog->setAttribute(Qt::WA_DeleteOnClose); + msg_dialog->show(); +} + +void WiresharkMainWindow::discardAllSecretsFinished(int result) +{ + if (result == QMessageBox::Ok) { + /* XXX: It would be nice to handle other types of secrets that + * can be written to a DSB, maybe have a proper dialog. + */ + capture_file* cf = capture_file_.capFile(); + if (wtap_file_discard_decryption_secrets(cf->provider.wth)) { + cf->unsaved_changes = TRUE; + updateForUnsavedChanges(); + } + } +} + +void WiresharkMainWindow::editConfigurationProfiles() +{ + ProfileDialog *cp_dialog = new ProfileDialog(); + cp_dialog->setWindowModality(Qt::ApplicationModal); + cp_dialog->setAttribute(Qt::WA_DeleteOnClose); + cp_dialog->show(); +} + +void WiresharkMainWindow::showPreferencesDialog(QString module_name) +{ + PreferencesDialog *pref_dialog = new PreferencesDialog(this); + connect(pref_dialog, SIGNAL(destroyed(QObject*)), mainApp, SLOT(flushAppSignals())); + saveWindowGeometry(); // Save in case the layout panes are rearranged + + pref_dialog->setPane(module_name); + pref_dialog->setWindowModality(Qt::ApplicationModal); + pref_dialog->setAttribute(Qt::WA_DeleteOnClose); + pref_dialog->show(); +} + +// View Menu + +void WiresharkMainWindow::connectViewMenuActions() +{ + connect(main_ui_->actionViewFullScreen, &QAction::triggered, this, [this](bool checked) { + if (checked) { + // Save the state for future restore + was_maximized_ = this->isMaximized(); + this->showFullScreen(); + } else { + // Restore the previous state + if (was_maximized_) { + this->showMaximized(); + } else { + this->showNormal(); + } + } + }); + + connect(main_ui_->actionViewTimeDisplaySecondsWithHoursAndMinutes, &QAction::triggered, this, + [this](bool checked) { setTimeDisplaySecondsWithHoursAndMinutes(checked); }); + + connect(main_ui_->actionViewEditResolvedName, &QAction::triggered, this, + [this]() { editResolvedName(); }); + + connect(main_ui_->actionViewNameResolutionPhysical, &QAction::triggered, this, + [this]() { setNameResolution(); }); + + connect(main_ui_->actionViewNameResolutionNetwork, &QAction::triggered, this, + [this]() { setNameResolution(); }); + + connect(main_ui_->actionViewNameResolutionTransport, &QAction::triggered, this, + [this]() { setNameResolution(); }); + + connect(main_ui_->actionViewZoomIn, &QAction::triggered, this, [this]() { + recent.gui_zoom_level++; + zoomText(); + }); + + connect(main_ui_->actionViewZoomOut, &QAction::triggered, this, [this]() { + recent.gui_zoom_level--; + zoomText(); + }); + + connect(main_ui_->actionViewNormalSize, &QAction::triggered, this, [this]() { + recent.gui_zoom_level = 0; + zoomText(); + }); + + connect(main_ui_->actionViewExpandSubtrees, &QAction::triggered, + proto_tree_, &ProtoTree::expandSubtrees); + + connect(main_ui_->actionViewCollapseSubtrees, &QAction::triggered, + proto_tree_, &ProtoTree::collapseSubtrees); + + connect(main_ui_->actionViewExpandAll, &QAction::triggered, + proto_tree_, &ProtoTree::expandAll); + + connect(main_ui_->actionViewCollapseAll, &QAction::triggered, + proto_tree_, &ProtoTree::collapseAll); + + connect(main_ui_->actionViewColorizePacketList, &QAction::triggered, this, [this](bool checked) { + recent.packet_list_colorize = checked; + packet_list_->recolorPackets(); + }); + + connect(main_ui_->actionViewColoringRules, &QAction::triggered, this, + [this]() { showColoringRulesDialog(); }); + + connect(main_ui_->actionViewColorizeResetColorization, &QAction::triggered, this, [this]() { + gchar *err_msg = NULL; + if (!color_filters_reset_tmp(&err_msg)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); + g_free(err_msg); + } + packet_list_->recolorPackets(); + setMenusForSelectedPacket(); + }); + + connect(main_ui_->actionViewColorizeNewColoringRule, &QAction::triggered, this, + [this]() { colorizeConversation(true); }); + + connect(main_ui_->actionViewResetLayout, &QAction::triggered, this, [this]() { + recent.gui_geometry_main_upper_pane = 0; + recent.gui_geometry_main_lower_pane = 0; + applyRecentPaneGeometry(); + }); + + connect(main_ui_->actionViewResizeColumns, &QAction::triggered, this, [this]() { + if (! packet_list_->model()) + return; + for (int col = 0; col < packet_list_->model()->columnCount(); col++) { + packet_list_->resizeColumnToContents(col); + recent_set_column_width(col, packet_list_->columnWidth(col)); + } + }); + + connect(main_ui_->actionViewInternalsConversationHashTables, &QAction::triggered, this, [this]() { + ConversationHashTablesDialog *conversation_hash_tables_dlg = new ConversationHashTablesDialog(this); + conversation_hash_tables_dlg->show(); + }); + + connect(main_ui_->actionViewInternalsDissectorTables, &QAction::triggered, this, [this]() { + DissectorTablesDialog *dissector_tables_dlg = new DissectorTablesDialog(this); + dissector_tables_dlg->show(); + }); + + connect(main_ui_->actionViewInternalsSupportedProtocols, &QAction::triggered, this, [this]() { + SupportedProtocolsDialog *supported_protocols_dlg = new SupportedProtocolsDialog(this); + supported_protocols_dlg->show(); + }); + + connect(main_ui_->actionViewShowPacketInNewWindow, &QAction::triggered, this, + [this]() { openPacketDialog(); }); + + // This is only used in ProtoTree. Defining it here makes more sense. + connect(main_ui_->actionContextShowLinkedPacketInNewWindow, &QAction::triggered, this, + [this]() { openPacketDialog(true); }); + + connect(main_ui_->actionViewReload_as_File_Format_or_Capture, &QAction::triggered, this, + [this]() { reloadCaptureFileAsFormatOrCapture(); }); + + connect(main_ui_->actionViewReload, &QAction::triggered, this, + [this]() { reloadCaptureFile(); }); +} + +void WiresharkMainWindow::showHideMainWidgets(QAction *action) +{ + if (!action) { + return; + } + bool show = action->isChecked(); + QWidget *widget = action->data().value(); + + // We may have come from the toolbar context menu, so check/uncheck each + // action as well. + if (widget == main_ui_->mainToolBar) { + recent.main_toolbar_show = show; + main_ui_->actionViewMainToolbar->setChecked(show); + } else if (widget == main_ui_->displayFilterToolBar) { + recent.filter_toolbar_show = show; + main_ui_->actionViewFilterToolbar->setChecked(show); +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) + } else if (widget == main_ui_->wirelessToolBar) { + recent.wireless_toolbar_show = show; + main_ui_->actionViewWirelessToolbar->setChecked(show); +#endif + } else if (widget == main_ui_->statusBar) { + recent.statusbar_show = show; + main_ui_->actionViewStatusBar->setChecked(show); + } else if (widget == packet_list_) { + recent.packet_list_show = show; + main_ui_->actionViewPacketList->setChecked(show); + } else if (widget == proto_tree_) { + recent.tree_view_show = show; + main_ui_->actionViewPacketDetails->setChecked(show); + } else if (widget == byte_view_tab_) { + recent.byte_view_show = show; + main_ui_->actionViewPacketBytes->setChecked(show); + } else if (widget == packet_diagram_) { + recent.packet_diagram_show = show; + main_ui_->actionViewPacketDiagram->setChecked(show); + } else { + foreach(QAction *action, main_ui_->menuInterfaceToolbars->actions()) { + QToolBar *toolbar = action->data().value(); + if (widget == toolbar) { + GList *entry = g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc)strcmp); + if (show && !entry) { + recent.interface_toolbars = g_list_append(recent.interface_toolbars, g_strdup(action->text().toUtf8().constData())); + } else if (!show && entry) { + recent.interface_toolbars = g_list_remove(recent.interface_toolbars, entry->data); + } + action->setChecked(show); + } + } + + ext_toolbar_t * toolbar = VariantPointer::asPtr(action->data()); + if (toolbar) { + GList *entry = g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, (GCompareFunc)strcmp); + if (show && !entry) { + recent.gui_additional_toolbars = g_list_append(recent.gui_additional_toolbars, g_strdup(toolbar->name)); + } else if (!show && entry) { + recent.gui_additional_toolbars = g_list_remove(recent.gui_additional_toolbars, entry->data); + } + action->setChecked(show); + + QList toolbars = findChildren(); + foreach(QToolBar *bar, toolbars) { + AdditionalToolBar *iftoolbar = dynamic_cast(bar); + if (iftoolbar && iftoolbar->menuName().compare(toolbar->name) == 0) { + iftoolbar->setVisible(show); + } + } + } + } + + if (widget) { + widget->setVisible(show); + } +} + +void WiresharkMainWindow::setTimestampFormat(QAction *action) +{ + if (!action) { + return; + } + ts_type tsf = action->data().value(); + if (recent.gui_time_format != tsf) { + timestamp_set_type(tsf); + recent.gui_time_format = tsf; + + if (packet_list_) { + packet_list_->resetColumns(); + packet_list_->resizeAllColumns(true); + } + } +} + +void WiresharkMainWindow::setTimestampPrecision(QAction *action) +{ + if (!action) { + return; + } + ts_precision tsp = action->data().value(); + if (recent.gui_time_precision != tsp) { + timestamp_set_precision(tsp); + recent.gui_time_precision = tsp; + + if (packet_list_) { + packet_list_->resetColumns(); + packet_list_->resizeAllColumns(true); + } + } +} + +void WiresharkMainWindow::setTimeDisplaySecondsWithHoursAndMinutes(bool checked) +{ + if (checked) { + recent.gui_seconds_format = TS_SECONDS_HOUR_MIN_SEC; + } else { + recent.gui_seconds_format = TS_SECONDS_DEFAULT; + } + timestamp_set_seconds_type(recent.gui_seconds_format); + + if (packet_list_) { + packet_list_->resetColumns(); + packet_list_->resizeAllColumns(true); + } +} + +void WiresharkMainWindow::editResolvedName() +{ + //int column = packet_list_->selectedColumn(); + int column = -1; + + if (packet_list_->contextMenuActive() || packet_list_->hasFocus()) { + if (packet_list_->currentIndex().isValid()) { + column = packet_list_->currentIndex().column(); + } + } + + main_ui_->addressEditorFrame->editAddresses(capture_file_, column); + showAccordionFrame(main_ui_->addressEditorFrame); +} + +void WiresharkMainWindow::setNameResolution() +{ + gbl_resolv_flags.mac_name = main_ui_->actionViewNameResolutionPhysical->isChecked() ? TRUE : FALSE; + gbl_resolv_flags.network_name = main_ui_->actionViewNameResolutionNetwork->isChecked() ? TRUE : FALSE; + gbl_resolv_flags.transport_name = main_ui_->actionViewNameResolutionTransport->isChecked() ? TRUE : FALSE; + + if (packet_list_) { + packet_list_->resetColumns(); + } + mainApp->emitAppSignal(WiresharkApplication::NameResolutionChanged); +} + +void WiresharkMainWindow::zoomText() +{ + mainApp->zoomTextFont(recent.gui_zoom_level); +} + +void WiresharkMainWindow::showColoringRulesDialog() +{ + ColoringRulesDialog *coloring_rules_dialog = new ColoringRulesDialog(this); + connect(coloring_rules_dialog, &ColoringRulesDialog::accepted, + packet_list_, &PacketList::recolorPackets); + connect(coloring_rules_dialog, &ColoringRulesDialog::filterAction, + this, &WiresharkMainWindow::filterAction); + + coloring_rules_dialog->setWindowModality(Qt::ApplicationModal); + coloring_rules_dialog->setAttribute(Qt::WA_DeleteOnClose); + coloring_rules_dialog->show(); +} + +// actionViewColorizeConversation1 - 10 +void WiresharkMainWindow::colorizeConversation(bool create_rule) +{ + QAction *colorize_action = qobject_cast(sender()); + if (!colorize_action) return; + + if (capture_file_.capFile() && selectedRows().count() > 0) { + packet_info *pi = capture_file_.packetInfo(); + guint8 cc_num = colorize_action->data().toUInt(); + gchar *filter = conversation_filter_from_packet(pi); + if (filter == NULL) { + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, tr("Unable to build conversation filter.")); + return; + } + + if (create_rule) { + ColoringRulesDialog coloring_rules_dialog(this, filter); + connect(&coloring_rules_dialog, &ColoringRulesDialog::accepted, + packet_list_, &PacketList::recolorPackets); + connect(&coloring_rules_dialog, &ColoringRulesDialog::filterAction, + this, &WiresharkMainWindow::filterAction); + coloring_rules_dialog.exec(); + } else { + gchar *err_msg = NULL; + if (!color_filters_set_tmp(cc_num, filter, FALSE, &err_msg)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); + g_free(err_msg); + } + packet_list_->recolorPackets(); + } + } + setMenusForSelectedPacket(); +} + +void WiresharkMainWindow::colorizeActionTriggered() +{ + QByteArray filter; + int color_number = -1; + + ConversationAction *conv_action = qobject_cast(sender()); + if (conv_action) { + filter = conv_action->filter(); + color_number = conv_action->colorNumber(); + } else { + ColorizeAction *colorize_action = qobject_cast(sender()); + if (colorize_action) { + filter = colorize_action->filter(); + color_number = colorize_action->colorNumber(); + } + } + + colorizeWithFilter(filter, color_number); +} + +void WiresharkMainWindow::colorizeWithFilter(QByteArray filter, int color_number) +{ + if (filter.isEmpty()) return; + + if (color_number > 0) { + // Assume "Color X" + gchar *err_msg = NULL; + if (!color_filters_set_tmp(color_number, filter.constData(), FALSE, &err_msg)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); + g_free(err_msg); + } + packet_list_->recolorPackets(); + } else { + // New coloring rule + ColoringRulesDialog coloring_rules_dialog(window(), filter); + connect(&coloring_rules_dialog, &ColoringRulesDialog::accepted, + packet_list_, &PacketList::recolorPackets); + connect(&coloring_rules_dialog, &ColoringRulesDialog::filterAction, + this, &WiresharkMainWindow::filterAction); + coloring_rules_dialog.exec(); + } + main_ui_->actionViewColorizeResetColorization->setEnabled(tmp_color_filters_used()); +} + +void WiresharkMainWindow::openPacketDialog(bool from_reference) +{ + frame_data * fdata = Q_NULLPTR; + + /* Find the frame for which we're popping up a dialog */ + if (from_reference) { + guint32 framenum = fvalue_get_uinteger(capture_file_.capFile()->finfo_selected->value); + if (framenum == 0) + return; + + fdata = frame_data_sequence_find(capture_file_.capFile()->provider.frames, framenum); + } else if (selectedRows().count() == 1) { + fdata = frameDataForRow(selectedRows().at(0)); + } else if (selectedRows().count() > 1) + return; + + /* If we have a frame, pop up the dialog */ + if (fdata) { + PacketDialog *packet_dialog = new PacketDialog(*this, capture_file_, fdata); + + connect(packet_dialog, &PacketDialog::showProtocolPreferences, + this, &WiresharkMainWindow::showPreferencesDialog); + connect(packet_dialog, SIGNAL(editProtocolPreference(preference*, pref_module*)), + main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*, pref_module*))); + + connect(this, &WiresharkMainWindow::closePacketDialogs, packet_dialog, &PacketDialog::close); + zoomText(); // Emits mainApp->zoomMonospaceFont(QFont) + + packet_dialog->show(); + } +} + +void WiresharkMainWindow::reloadCaptureFileAsFormatOrCapture() +{ + capture_file *cf = CaptureFile::globalCapFile(); + + if (cf->unsaved_changes) { + QString before_what(tr(" before reloading the file")); + if (!testCaptureFileClose(before_what, Reload)) + return; + } + + if (cf->open_type == WTAP_TYPE_AUTO) + cf->open_type = open_info_name_to_type("MIME Files Format"); + else /* TODO: This should be latest format chosen by user */ + cf->open_type = WTAP_TYPE_AUTO; + + cf_reload(cf); +} + +void WiresharkMainWindow::reloadCaptureFile() +{ + capture_file *cf = CaptureFile::globalCapFile(); + + if (cf->unsaved_changes) { + QString before_what(tr(" before reloading the file")); + if (!testCaptureFileClose(before_what, Reload)) + return; + } + + cf_reload(cf); +} + + +// Expand / collapse slots in proto_tree + +// Go Menu + +void WiresharkMainWindow::connectGoMenuActions() +{ + connect(main_ui_->actionGoGoToPacket, &QAction::triggered, this, [this]() { + if (! packet_list_->model() || packet_list_->model()->rowCount() < 1) { + return; + } + setPreviousFocus(); + + showAccordionFrame(main_ui_->goToFrame, true); + if (main_ui_->goToFrame->isVisible()) { + main_ui_->goToLineEdit->clear(); + main_ui_->goToLineEdit->setFocus(); + } + }); + + connect(main_ui_->actionGoGoToLinkedPacket, &QAction::triggered, this, [this]() { + QAction *gta = qobject_cast(sender()); + if (!gta) return; + + bool ok = false; + int packet_num = gta->data().toInt(&ok); + if (!ok) return; + + packet_list_->goToPacket(packet_num); + }); + + connect(main_ui_->actionGoNextPacket, &QAction::triggered, + packet_list_, &PacketList::goNextPacket); + + connect(main_ui_->actionGoPreviousPacket, &QAction::triggered, + packet_list_, &PacketList::goPreviousPacket); + + connect(main_ui_->actionGoFirstPacket, &QAction::triggered, + packet_list_, &PacketList::goFirstPacket); + + connect(main_ui_->actionGoLastPacket, &QAction::triggered, + packet_list_, &PacketList::goLastPacket); + + connect(main_ui_->actionGoNextConversationPacket, &QAction::triggered, this, + [this]() { goToConversationFrame(true); }); + + connect(main_ui_->actionGoPreviousConversationPacket, &QAction::triggered, this, + [this]() { goToConversationFrame(false); }); + + connect(main_ui_->actionGoNextHistoryPacket, &QAction::triggered, + packet_list_, &PacketList::goNextHistoryPacket); + + connect(main_ui_->actionGoPreviousHistoryPacket, &QAction::triggered, + packet_list_, &PacketList::goPreviousHistoryPacket); + + // triggered is whenever the user clicks the button; save that as + // the new recent value + connect(main_ui_->actionGoAutoScroll, &QAction::triggered, this, + [](bool checked) { recent.capture_auto_scroll = checked; }); + + // toggled is whenever the value changes; if it changes programmatically + // (e.g., the user scrolls upwards so we stop auto scrolling) change + // whether the button is checked but don't save value to recent (it's + // a temporary change) + connect(main_ui_->actionGoAutoScroll, &QAction::toggled, this, + [this](bool checked) { packet_list_->setVerticalAutoScroll(checked); }); +} + +void WiresharkMainWindow::goToConversationFrame(bool go_next) { + gchar *filter = NULL; + dfilter_t *dfcode = NULL; + gboolean found_packet = FALSE; + packet_info *pi = capture_file_.packetInfo(); + + if (!pi) { + // No packet was selected, or multiple packets were selected. + return; + } + + /* Try to build a conversation + * filter in the order TCP, UDP, IP, Ethernet and apply the + * coloring */ + filter = conversation_filter_from_packet(pi); + if (filter == NULL) { + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, tr("Unable to build conversation filter.")); + g_free(filter); + return; + } + + if (!dfilter_compile(filter, &dfcode, NULL)) { + /* The attempt failed; report an error. */ + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, tr("Error compiling filter for this conversation.")); + g_free(filter); + return; + } + + found_packet = cf_find_packet_dfilter(capture_file_.capFile(), dfcode, go_next ? SD_FORWARD : SD_BACKWARD); + + if (!found_packet) { + /* We didn't find a packet */ + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, tr("No previous/next packet in conversation.")); + } + + dfilter_free(dfcode); + g_free(filter); +} + +// Capture Menu + +void WiresharkMainWindow::connectCaptureMenuActions() +{ +#ifdef HAVE_LIBPCAP + connect(main_ui_->actionCaptureOptions, &QAction::triggered, this, + [this]() { showCaptureOptionsDialog(); }); +#endif + + connect(main_ui_->actionCaptureStart, &QAction::triggered, this, + [this]() { startCaptureTriggered(); }); + + connect(main_ui_->actionCaptureStop, &QAction::triggered, this, + [this]() { stopCapture(); }); + +#ifdef HAVE_LIBPCAP + connect(main_ui_->actionCaptureRestart, &QAction::triggered, this, [this]() { + QString before_what(tr(" before restarting the capture")); + cap_session_.capture_opts->restart = TRUE; + if (!testCaptureFileClose(before_what, Restart)) { + return; + } + startCapture(QStringList()); + }); +#endif // HAVE_LIBPCAP + + connect(main_ui_->actionCaptureCaptureFilters, &QAction::triggered, this, [this]() { + FilterDialog *capture_filter_dlg = new FilterDialog(window(), FilterDialog::CaptureFilter); + capture_filter_dlg->setWindowModality(Qt::ApplicationModal); + capture_filter_dlg->setAttribute(Qt::WA_DeleteOnClose); + capture_filter_dlg->show(); + }); + +#ifdef HAVE_LIBPCAP + connect(main_ui_->actionCaptureRefreshInterfaces, &QAction::triggered, this, [this]() { + main_ui_->actionCaptureRefreshInterfaces->setEnabled(false); + mainApp->refreshLocalInterfaces(); + main_ui_->actionCaptureRefreshInterfaces->setEnabled(true); + }); +#endif +} + +void WiresharkMainWindow::showCaptureOptionsDialog() +{ +#ifdef HAVE_LIBPCAP + if (!capture_options_dialog_) { + capture_options_dialog_ = new CaptureOptionsDialog(this); + + connect(capture_options_dialog_, &CaptureOptionsDialog::startCapture, this, [this]() { startCapture(); }); + connect(capture_options_dialog_, &CaptureOptionsDialog::stopCapture, this, &WiresharkMainWindow::stopCapture); + + connect(capture_options_dialog_, &CaptureOptionsDialog::interfacesChanged, + this->welcome_page_, &WelcomePage::interfaceSelected); + connect(capture_options_dialog_, &CaptureOptionsDialog::interfacesChanged, + this->welcome_page_->getInterfaceFrame(), &InterfaceFrame::updateSelectedInterfaces); + connect(capture_options_dialog_, &CaptureOptionsDialog::interfaceListChanged, + this->welcome_page_->getInterfaceFrame(), &InterfaceFrame::interfaceListChanged); + connect(capture_options_dialog_, &CaptureOptionsDialog::captureFilterTextEdited, + this->welcome_page_, &WelcomePage::setCaptureFilterText); + // Propagate selection changes from main UI to dialog. + connect(this->welcome_page_, &WelcomePage::interfacesChanged, + capture_options_dialog_, &CaptureOptionsDialog::interfaceSelected); + + connect(capture_options_dialog_, &CaptureOptionsDialog::setFilterValid, + this, &WiresharkMainWindow::startInterfaceCapture); + + connect(capture_options_dialog_, &CaptureOptionsDialog::showExtcapOptions, + this, &WiresharkMainWindow::showExtcapOptionsDialog); + } + capture_options_dialog_->updateInterfaces(); + + if (capture_options_dialog_->isMinimized()) { + capture_options_dialog_->showNormal(); + } else { + capture_options_dialog_->show(); + } + + capture_options_dialog_->raise(); + capture_options_dialog_->activateWindow(); +#endif +} + +void WiresharkMainWindow::startCaptureTriggered() +{ +//#ifdef HAVE_AIRPCAP +// airpcap_if_active = airpcap_if_selected; +// if (airpcap_if_active) +// airpcap_set_toolbar_start_capture(airpcap_if_active); +//#endif + +// if (cap_open_w) { +// /* +// * There's an options dialog; get the values from it and close it. +// */ +// gboolean success; + +// /* Determine if "capture start" while building of the "capture options" window */ +// /* is in progress. If so, ignore the "capture start. */ +// /* XXX: Would it be better/cleaner for the "capture options" window code to */ +// /* disable the capture start button temporarily ? */ +// if (cap_open_complete == FALSE) { +// return; /* Building options window: ignore "capture start" */ +// } +// success = capture_dlg_prep(cap_open_w); +// window_destroy(GTK_WIDGET(cap_open_w)); +// if (!success) +// return; /* error in options dialog */ +// } + +#ifdef HAVE_LIBPCAP + if (global_capture_opts.num_selected == 0) { + QString err_msg = tr("No Interface Selected."); + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, err_msg); + main_ui_->actionCaptureStart->setChecked(false); + return; + } + + /* XXX - will closing this remove a temporary file? */ + QString before_what(tr(" before starting a new capture")); + if (testCaptureFileClose(before_what)) { + startCapture(QStringList()); + } else { + // simply clicking the button sets it to 'checked' even though we've + // decided to do nothing, so undo that + main_ui_->actionCaptureStart->setChecked(false); + } +#endif // HAVE_LIBPCAP +} + +// Analyze Menu + +struct epan_uat; + +void WiresharkMainWindow::connectAnalyzeMenuActions() +{ + connect(main_ui_->actionAnalyzeDisplayFilters, &QAction::triggered, this, [=]() { + FilterDialog *display_filter_dlg = new FilterDialog(window(), FilterDialog::DisplayFilter); + display_filter_dlg->setWindowModality(Qt::ApplicationModal); + display_filter_dlg->setAttribute(Qt::WA_DeleteOnClose); + display_filter_dlg->show(); + }); + + connect(main_ui_->actionAnalyzeDisplayFilterMacros, &QAction::triggered, this, [=]() { + struct epan_uat* dfm_uat; + dfilter_macro_get_uat(&dfm_uat); + UatDialog *uat_dlg = new UatDialog(parentWidget(), dfm_uat); + connect(uat_dlg, SIGNAL(destroyed(QObject*)), mainApp, SLOT(flushAppSignals())); + + uat_dlg->setWindowModality(Qt::ApplicationModal); + uat_dlg->setAttribute(Qt::WA_DeleteOnClose); + uat_dlg->show(); + }); + + connect(main_ui_->actionDisplayFilterExpression, &QAction::triggered, this, [=]() { + DisplayFilterExpressionDialog *dfe_dialog = new DisplayFilterExpressionDialog(this); + + connect(dfe_dialog, &DisplayFilterExpressionDialog::insertDisplayFilter, + qobject_cast(df_combo_box_->lineEdit()), &SyntaxLineEdit::insertFilter); + + dfe_dialog->show(); + }); + + connect(main_ui_->actionAnalyzeApplyAsColumn, &QAction::triggered, this, &WiresharkMainWindow::applyFieldAsColumn); + + connect(main_ui_->actionAnalyzeEnabledProtocols, &QAction::triggered, this, [=]() { + EnabledProtocolsDialog *enable_proto_dialog = new EnabledProtocolsDialog(this); + connect(enable_proto_dialog, SIGNAL(destroyed(QObject*)), mainApp, SLOT(flushAppSignals())); + + enable_proto_dialog->setWindowModality(Qt::ApplicationModal); + enable_proto_dialog->setAttribute(Qt::WA_DeleteOnClose); + enable_proto_dialog->show(); + }); + + connect(main_ui_->actionAnalyzeDecodeAs, &QAction::triggered, this, [=]() { + QAction *da_action = qobject_cast(sender()); + bool create_new = da_action && da_action->property("create_new").toBool(); + + DecodeAsDialog *da_dialog = new DecodeAsDialog(this, capture_file_.capFile(), create_new); + connect(da_dialog, SIGNAL(destroyed(QObject*)), mainApp, SLOT(flushAppSignals())); + + da_dialog->setWindowModality(Qt::ApplicationModal); + da_dialog->setAttribute(Qt::WA_DeleteOnClose); + da_dialog->show(); + }); + + connect(main_ui_->actionAnalyzeReloadLuaPlugins, &QAction::triggered, this, &WiresharkMainWindow::reloadLuaPlugins); + + connect(main_ui_->actionAnalyzeShowPacketBytes, &QAction::triggered, this, [=]() { + ShowPacketBytesDialog *spbd = new ShowPacketBytesDialog(*this, capture_file_); + spbd->addCodecs(text_codec_map_); + spbd->show(); + }); + + connect(main_ui_->actionAnalyzeExpertInfo, &QAction::triggered, this, [=]() { + statCommandExpertInfo(NULL, NULL); + }); +} + +void WiresharkMainWindow::filterMenuAboutToShow() +{ + QMenu * menu = qobject_cast(sender()); + QString field_filter; + + if (capture_file_.capFile() && capture_file_.capFile()->finfo_selected) { + char *tmp_field = proto_construct_match_selected_string(capture_file_.capFile()->finfo_selected, + capture_file_.capFile()->edt); + field_filter = QString(tmp_field); + wmem_free(NULL, tmp_field); + } + bool enable = ! field_filter.isEmpty(); + bool prepare = menu->objectName().compare("menuPrepareAFilter") == 0; + + menu->clear(); + QActionGroup * group = FilterAction::createFilterGroup(field_filter, prepare, enable, menu); + menu->addActions(group->actions()); +} + +void WiresharkMainWindow::matchFieldFilter(FilterAction::Action action, FilterAction::ActionType filter_type) +{ + QString field_filter; + + if (packet_list_->contextMenuActive() || packet_list_->hasFocus()) { + field_filter = packet_list_->getFilterFromRowAndColumn(packet_list_->currentIndex()); + } else if (capture_file_.capFile() && capture_file_.capFile()->finfo_selected) { + char *tmp_field = proto_construct_match_selected_string(capture_file_.capFile()->finfo_selected, + capture_file_.capFile()->edt); + field_filter = QString(tmp_field); + wmem_free(NULL, tmp_field); + } + + if (field_filter.isEmpty()) { + QString err = tr("No filter available. Try another %1.").arg(packet_list_->contextMenuActive() ? tr("column") : tr("item")); + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, err); + return; + } + + setDisplayFilter(field_filter, action, filter_type); +} + +void WiresharkMainWindow::applyFieldAsColumn() +{ + if (capture_file_.capFile() != 0 && capture_file_.capFile()->finfo_selected != 0) { + header_field_info *hfinfo = capture_file_.capFile()->finfo_selected->hfinfo; + int col = column_prefs_has_custom(hfinfo->abbrev); + if (col == -1) { + insertColumn(hfinfo->name, hfinfo->abbrev); + } else { + QString status; + if (QString(hfinfo->name) == get_column_title(col)) { + status = tr("The \"%1\" column already exists.").arg(hfinfo->name); + } else { + status = tr("The \"%1\" column already exists as \"%2\".").arg(hfinfo->name).arg(get_column_title(col)); + } + mainApp->pushStatus(WiresharkApplication::TemporaryStatus, status); + + if (!get_column_visible(col)) { + packet_list_->setColumnHidden(col, false); + set_column_visible(col, TRUE); + prefs_main_write(); + } + } + } +} + +void WiresharkMainWindow::applyConversationFilter() +{ + ConversationAction *conv_action = qobject_cast(sender()); + if (!conv_action) return; + + packet_info *pinfo = capture_file_.packetInfo(); + if (!pinfo) return; + + QByteArray conv_filter = conv_action->filter(); + if (conv_filter.isEmpty()) return; + + if (conv_action->isFilterValid(pinfo)) { + + df_combo_box_->lineEdit()->setText(conv_filter); + df_combo_box_->applyDisplayFilter(); + } +} + +void WiresharkMainWindow::applyExportObject() +{ + ExportObjectAction *export_action = qobject_cast(sender()); + if (!export_action) + return; + + ExportObjectDialog* export_dialog = new ExportObjectDialog(*this, capture_file_, export_action->exportObject()); + export_dialog->setWindowModality(Qt::ApplicationModal); + export_dialog->setAttribute(Qt::WA_DeleteOnClose); + export_dialog->show(); +} + +void WiresharkMainWindow::openFollowStreamDialog(int proto_id, guint stream_num, guint sub_stream_num, bool use_stream_index) { + FollowStreamDialog *fsd = new FollowStreamDialog(*this, capture_file_, proto_id); + connect(fsd, SIGNAL(updateFilter(QString, bool)), this, SLOT(filterPackets(QString, bool))); + connect(fsd, SIGNAL(goToPacket(int)), packet_list_, SLOT(goToPacket(int))); + fsd->addCodecs(text_codec_map_); + fsd->show(); + if (use_stream_index) { + // If a specific conversation was requested, then ignore any previous + // display filters and display all related packets. + fsd->follow("", true, stream_num, sub_stream_num); + } else { + fsd->follow(getFilter()); + } +} + +void WiresharkMainWindow::openFollowStreamDialog(int proto_id) { + openFollowStreamDialog(proto_id, 0, 0, false); +} + +void WiresharkMainWindow::openSCTPAllAssocsDialog() +{ + SCTPAllAssocsDialog *sctp_dialog = new SCTPAllAssocsDialog(this, capture_file_.capFile()); + connect(sctp_dialog, SIGNAL(filterPackets(QString, bool)), + this, SLOT(filterPackets(QString, bool))); + connect(this, SIGNAL(setCaptureFile(capture_file*)), + sctp_dialog, SLOT(setCaptureFile(capture_file*))); + sctp_dialog->fillTable(); + + if (sctp_dialog->isMinimized() == true) + { + sctp_dialog->showNormal(); + } + else + { + sctp_dialog->show(); + } + + sctp_dialog->raise(); + sctp_dialog->activateWindow(); +} + +void WiresharkMainWindow::on_actionSCTPShowAllAssociations_triggered() +{ + openSCTPAllAssocsDialog(); +} + +void WiresharkMainWindow::on_actionSCTPAnalyseThisAssociation_triggered() +{ + const sctp_assoc_info_t* assoc = SCTPAssocAnalyseDialog::findAssocForPacket(capture_file_.capFile()); + if (!assoc) { + return; + } + SCTPAssocAnalyseDialog *sctp_analyse = new SCTPAssocAnalyseDialog(this, assoc, capture_file_.capFile()); + connect(sctp_analyse, SIGNAL(filterPackets(QString, bool)), + this, SLOT(filterPackets(QString, bool))); + + if (sctp_analyse->isMinimized() == true) + { + sctp_analyse->showNormal(); + } + else + { + sctp_analyse->show(); + } + + sctp_analyse->raise(); + sctp_analyse->activateWindow(); +} + +void WiresharkMainWindow::on_actionSCTPFilterThisAssociation_triggered() +{ + const sctp_assoc_info_t* assoc = SCTPAssocAnalyseDialog::findAssocForPacket(capture_file_.capFile()); + if (assoc) { + QString newFilter = QString("sctp.assoc_index==%1").arg(assoc->assoc_id); + assoc = NULL; + emit filterPackets(newFilter, false); + } +} + +// -z wlan,stat +void WiresharkMainWindow::statCommandWlanStatistics(const char *arg, void *) +{ + WlanStatisticsDialog *wlan_stats_dlg = new WlanStatisticsDialog(*this, capture_file_, arg); + connect(wlan_stats_dlg, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + wlan_stats_dlg->show(); +} + +// -z expert +void WiresharkMainWindow::statCommandExpertInfo(const char *, void *) +{ + const DisplayFilterEdit *df_edit = dynamic_cast(df_combo_box_->lineEdit()); + ExpertInfoDialog *expert_dialog = new ExpertInfoDialog(*this, capture_file_, df_edit->text()); + + connect(expert_dialog->getExpertInfoView(), SIGNAL(goToPacket(int, int)), + packet_list_, SLOT(goToPacket(int, int))); + connect(expert_dialog, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + + expert_dialog->show(); +} + + +// Next / previous / first / last slots in packet_list + +// Statistics Menu + +void WiresharkMainWindow::connectStatisticsMenuActions() +{ + connect(main_ui_->actionStatisticsCaptureFileProperties, &QAction::triggered, this, [=]() { + CaptureFilePropertiesDialog *capture_file_properties_dialog = new CaptureFilePropertiesDialog(*this, capture_file_); + connect(capture_file_properties_dialog, SIGNAL(captureCommentChanged()), + this, SLOT(updateForUnsavedChanges())); + capture_file_properties_dialog->show(); + }); + + connect(main_ui_->actionStatisticsResolvedAddresses, &QAction::triggered, this, &WiresharkMainWindow::showResolvedAddressesDialog); + + connect(main_ui_->actionStatisticsProtocolHierarchy, &QAction::triggered, this, [=]() { + ProtocolHierarchyDialog *phd = new ProtocolHierarchyDialog(*this, capture_file_); + connect(phd, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + phd->show(); + }); + + connect(main_ui_->actionStatisticsConversations, &QAction::triggered, this, &WiresharkMainWindow::showConversationsDialog); + connect(main_ui_->actionStatisticsEndpoints, &QAction::triggered, this, &WiresharkMainWindow::showEndpointsDialog); + + connect(main_ui_->actionStatisticsPacketLengths, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("plen"); }); + + connect(main_ui_->actionStatisticsIOGraph, &QAction::triggered, this, [=]() { statCommandIOGraph(NULL, NULL); }); + + connect(main_ui_->actionStatisticsFlowGraph, &QAction::triggered, this, [=]() { + SequenceDialog *sequence_dialog = new SequenceDialog(*this, capture_file_); + sequence_dialog->show(); + }); + + connect(main_ui_->actionStatisticsCollectd, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("collectd"); }); + connect(main_ui_->actionStatisticsDNS, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("dns"); }); + connect(main_ui_->actionStatisticsHART_IP, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("hart_ip"); }); + connect(main_ui_->actionStatisticsHpfeeds, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("hpfeeds"); }); + connect(main_ui_->actionStatisticsHTTP2, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("http2"); }); + connect(main_ui_->actionStatisticsUdpMulticastStreams, &QAction::triggered, this, [=]() { statCommandMulticastStatistics(NULL, NULL); }); + + connect(main_ui_->actionStatistics29WestTopics_Advertisements_by_Topic, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_topic_ads_topic"); + }); + + connect(main_ui_->actionStatistics29WestTopics_Advertisements_by_Source, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_topic_ads_source"); + }); + + connect(main_ui_->actionStatistics29WestTopics_Advertisements_by_Transport, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_topic_ads_transport"); + }); + + connect(main_ui_->actionStatistics29WestTopics_Queries_by_Topic, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_topic_queries_topic"); + }); + + connect(main_ui_->actionStatistics29WestTopics_Queries_by_Receiver, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_topic_queries_receiver"); + }); + + connect(main_ui_->actionStatistics29WestTopics_Wildcard_Queries_by_Pattern, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_topic_queries_pattern"); + }); + + connect(main_ui_->actionStatistics29WestTopics_Wildcard_Queries_by_Receiver, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_topic_queries_pattern_receiver"); + }); + + connect(main_ui_->actionStatistics29WestQueues_Advertisements_by_Queue, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_queue_ads_queue"); + }); + + connect(main_ui_->actionStatistics29WestQueues_Advertisements_by_Source, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_queue_ads_source"); + }); + + connect(main_ui_->actionStatistics29WestQueues_Queries_by_Queue, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_queue_queries_queue"); + }); + + connect(main_ui_->actionStatistics29WestQueues_Queries_by_Receiver, &QAction::triggered, this, [=]() { + openStatisticsTreeDialog("lbmr_queue_queries_receiver"); + }); + + connect(main_ui_->actionStatistics29WestUIM_Streams, &QAction::triggered, this, [=]() { + LBMStreamDialog *stream_dialog = new LBMStreamDialog(this, capture_file_.capFile()); + // connect(stream_dialog, SIGNAL(goToPacket(int)), + // packet_list_, SLOT(goToPacket(int))); + connect(this, SIGNAL(setCaptureFile(capture_file*)), + stream_dialog, SLOT(setCaptureFile(capture_file*))); + stream_dialog->show(); + }); + + connect(main_ui_->actionStatistics29WestLBTRM, &QAction::triggered, this, [=]() { + LBMLBTRMTransportDialog * lbtrm_dialog = new LBMLBTRMTransportDialog(this, capture_file_.capFile()); + connect(lbtrm_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); + connect(this, SIGNAL(setCaptureFile(capture_file*)), + lbtrm_dialog, SLOT(setCaptureFile(capture_file*))); + lbtrm_dialog->show(); + }); + + connect(main_ui_->actionStatistics29WestLBTRU, &QAction::triggered, this, [=]() { + LBMLBTRUTransportDialog * lbtru_dialog = new LBMLBTRUTransportDialog(this, capture_file_.capFile()); + connect(lbtru_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); + connect(this, SIGNAL(setCaptureFile(capture_file*)), + lbtru_dialog, SLOT(setCaptureFile(capture_file*))); + lbtru_dialog->show(); + }); + + connect(main_ui_->actionStatisticsTcpStreamStevens, &QAction::triggered, this, [=]() { openTcpStreamDialog(GRAPH_TSEQ_STEVENS); }); + connect(main_ui_->actionStatisticsTcpStreamTcptrace, &QAction::triggered, this, [=]() { openTcpStreamDialog(GRAPH_TSEQ_TCPTRACE); }); + connect(main_ui_->actionStatisticsTcpStreamThroughput, &QAction::triggered, this, [=]() { openTcpStreamDialog(GRAPH_THROUGHPUT); }); + connect(main_ui_->actionStatisticsTcpStreamRoundTripTime, &QAction::triggered, this, [=]() { openTcpStreamDialog(GRAPH_RTT); }); + connect(main_ui_->actionStatisticsTcpStreamWindowScaling, &QAction::triggered, this, [=]() { openTcpStreamDialog(GRAPH_WSCALE); }); + + connect(main_ui_->actionStatisticsANCP, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("ancp"); }); + + connect(main_ui_->actionStatisticsBACappInstanceId, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("bacapp_instanceid"); }); + connect(main_ui_->actionStatisticsBACappIP, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("bacapp_ip"); }); + connect(main_ui_->actionStatisticsBACappObjectId, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("bacapp_objectid"); }); + connect(main_ui_->actionStatisticsBACappService, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("bacapp_service"); }); + + connect(main_ui_->actionStatisticsHTTPPacketCounter, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("http"); }); + connect(main_ui_->actionStatisticsHTTPRequests, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("http_req"); }); + connect(main_ui_->actionStatisticsHTTPLoadDistribution, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("http_srv"); }); + connect(main_ui_->actionStatisticsHTTPRequestSequences, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("http_seq"); }); + + connect(main_ui_->actionStatisticsSametime, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("sametime"); }); + + connect(main_ui_->actionStatisticsSOMEIPmessages, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("someip_messages"); }); + connect(main_ui_->actionStatisticsSOMEIPSDentries, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("someipsd_entries"); }); + + connect(main_ui_->actionStatisticsLTP, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("ltp"); }); +} + +void WiresharkMainWindow::openTcpStreamDialog(int graph_type) +{ + TCPStreamDialog *stream_dialog = new TCPStreamDialog(this, capture_file_.capFile(), (tcp_graph_type)graph_type); + connect(stream_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); + connect(this, SIGNAL(setCaptureFile(capture_file*)), + stream_dialog, SLOT(setCaptureFile(capture_file*))); + if (stream_dialog->result() == QDialog::Accepted) { + stream_dialog->show(); + } +} + +// -z mcast,stat +void WiresharkMainWindow::statCommandMulticastStatistics(const char *arg, void *) +{ + MulticastStatisticsDialog *mcast_stats_dlg = new MulticastStatisticsDialog(*this, capture_file_, arg); + connect(mcast_stats_dlg, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + mcast_stats_dlg->show(); +} + +void WiresharkMainWindow::openStatisticsTreeDialog(const gchar *abbr) +{ + StatsTreeDialog *st_dialog = new StatsTreeDialog(*this, capture_file_, abbr); +// connect(st_dialog, SIGNAL(goToPacket(int)), +// packet_list_, SLOT(goToPacket(int))); + st_dialog->show(); +} + +// -z io,stat +void WiresharkMainWindow::statCommandIOGraph(const char *, void *) +{ + const DisplayFilterEdit *df_edit = qobject_cast(df_combo_box_->lineEdit()); + QString displayFilter; + if (df_edit) + displayFilter = df_edit->text(); + + IOGraphDialog *iog_dialog = new IOGraphDialog(*this, capture_file_, displayFilter); + connect(iog_dialog, SIGNAL(goToPacket(int)), packet_list_, SLOT(goToPacket(int))); + connect(this, SIGNAL(reloadFields()), iog_dialog, SLOT(reloadFields())); + iog_dialog->show(); +} + +// Telephony Menu + +void WiresharkMainWindow::connectTelephonyMenuActions() +{ + connect(main_ui_->actionTelephonyVoipCalls, &QAction::triggered, this, [=]() { + VoipCallsDialog *dialog = VoipCallsDialog::openVoipCallsDialogVoip(*this, capture_file_, packet_list_); + dialog->show(); + }); + + connect(main_ui_->actionTelephonyIax2StreamAnalysis, &QAction::triggered, this, [=]() { + Iax2AnalysisDialog *iax2_analysis_dialog = new Iax2AnalysisDialog(*this, capture_file_); + connect(iax2_analysis_dialog, &Iax2AnalysisDialog::goToPacket, this, [=](int packet) { packet_list_->goToPacket(packet); }); + iax2_analysis_dialog->show(); + }); + + connect(main_ui_->actionTelephonyISUPMessages, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("isup_msg"); }); + + connect(main_ui_->actionTelephonyGsmMapSummary, &QAction::triggered, this, [=]() { + GsmMapSummaryDialog *gms_dialog = new GsmMapSummaryDialog(*this, capture_file_); + gms_dialog->show(); + }); + + connect(main_ui_->actionTelephonyLteMacStatistics, &QAction::triggered, this, [=]() { statCommandLteMacStatistics(NULL, NULL); }); + connect(main_ui_->actionTelephonyLteRlcGraph, &QAction::triggered, this, [=]() { + // We don't yet know the channel. + launchRLCGraph(false, 0, 0, 0, 0, 0); + }); + connect(main_ui_->actionTelephonyLteRlcStatistics, &QAction::triggered, this, [=]() { statCommandLteRlcStatistics(NULL, NULL); }); + + connect(main_ui_->actionTelephonyMtp3Summary, &QAction::triggered, this, [=]() { + Mtp3SummaryDialog *mtp3s_dialog = new Mtp3SummaryDialog(*this, capture_file_); + mtp3s_dialog->show(); + }); + + connect(main_ui_->actionTelephonyOsmuxPacketCounter, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("osmux"); }); + + connect(main_ui_->actionTelephonyRtpStreams, &QAction::triggered, this, &WiresharkMainWindow::openTelephonyRtpStreamsDialog); + connect(main_ui_->actionTelephonyRtpStreamAnalysis, &QAction::triggered, this, &WiresharkMainWindow::openRtpStreamAnalysisDialog); + connect(main_ui_->actionTelephonyRtpPlayer, &QAction::triggered, this, &WiresharkMainWindow::openRtpPlayerDialog); + + connect(main_ui_->actionTelephonyRTSPPacketCounter, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("rtsp"); }); + + connect(main_ui_->actionTelephonySMPPOperations, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("smpp_commands"); }); + + connect(main_ui_->actionTelephonyUCPMessages, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("ucp_messages"); }); + + connect(main_ui_->actionTelephonyF1APMessages, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("f1ap"); }); + + connect(main_ui_->actionTelephonyNGAPMessages, &QAction::triggered, this, [=]() { openStatisticsTreeDialog("ngap"); }); + + connect(main_ui_->actionTelephonySipFlows, &QAction::triggered, this, [=]() { + VoipCallsDialog *dialog = VoipCallsDialog::openVoipCallsDialogSip(*this, capture_file_, packet_list_); + dialog->show(); + }); +} + +RtpPlayerDialog *WiresharkMainWindow::openTelephonyRtpPlayerDialog() +{ + RtpPlayerDialog *dialog; + +#ifdef HAVE_LIBPCAP + dialog = RtpPlayerDialog::openRtpPlayerDialog(*this, capture_file_, packet_list_, captureSession()->state != CAPTURE_STOPPED); +#else + dialog = RtpPlayerDialog::openRtpPlayerDialog(*this, capture_file_, packet_list_, false); +#endif + + dialog->show(); + + return dialog; +} + +RtpAnalysisDialog *WiresharkMainWindow::openTelephonyRtpAnalysisDialog() +{ + RtpAnalysisDialog *dialog; + + dialog = RtpAnalysisDialog::openRtpAnalysisDialog(*this, capture_file_, packet_list_); + dialog->show(); + + return dialog; +} + +// -z mac-lte,stat +void WiresharkMainWindow::statCommandLteMacStatistics(const char *arg, void *) +{ + LteMacStatisticsDialog *lte_mac_stats_dlg = new LteMacStatisticsDialog(*this, capture_file_, arg); + connect(lte_mac_stats_dlg, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + lte_mac_stats_dlg->show(); +} + +void WiresharkMainWindow::statCommandLteRlcStatistics(const char *arg, void *) +{ + LteRlcStatisticsDialog *lte_rlc_stats_dlg = new LteRlcStatisticsDialog(*this, capture_file_, arg); + connect(lte_rlc_stats_dlg, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + // N.B. It is necessary for the RLC Statistics window to launch the RLC graph in this way, to ensure + // that the goToPacket() signal/slot connection gets set up... + connect(lte_rlc_stats_dlg, SIGNAL(launchRLCGraph(bool, guint16, guint8, guint16, guint16, guint8)), + this, SLOT(launchRLCGraph(bool, guint16, guint8, guint16, guint16, guint8))); + + lte_rlc_stats_dlg->show(); +} + +void WiresharkMainWindow::launchRLCGraph(bool channelKnown, + guint16 ueid, guint8 rlcMode, + guint16 channelType, guint16 channelId, guint8 direction) +{ + LteRlcGraphDialog *lrg_dialog = new LteRlcGraphDialog(*this, capture_file_, channelKnown); + connect(lrg_dialog, SIGNAL(goToPacket(int)), packet_list_, SLOT(goToPacket(int))); + // This is a bit messy, but wanted to hide these parameters from users of + // on_actionTelephonyLteRlcGraph_triggered(). + if (channelKnown) { + lrg_dialog->setChannelInfo(ueid, rlcMode, channelType, channelId, direction); + } + lrg_dialog->show(); +} + +RtpStreamDialog *WiresharkMainWindow::openTelephonyRtpStreamsDialog() +{ + RtpStreamDialog *dialog = RtpStreamDialog::openRtpStreamDialog(*this, capture_file_, packet_list_); + dialog->show(); + + return dialog; +} + +void WiresharkMainWindow::openRtpStreamAnalysisDialog() +{ + QVector stream_ids; + QString err; + + if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) { + err = findRtpStreams(&stream_ids, true); + } else { + err = findRtpStreams(&stream_ids, false); + } + if (!err.isNull()) { + QMessageBox::warning(this, tr("RTP packet search failed"), + err, + QMessageBox::Ok); + } else { + openTelephonyRtpAnalysisDialog()->addRtpStreams(stream_ids); + } + foreach(rtpstream_id_t *id, stream_ids) { + rtpstream_id_free(id); + g_free(id); + } +} + +void WiresharkMainWindow::openRtpPlayerDialog() +{ + QVector stream_ids; + QString err; + + if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) { + err = findRtpStreams(&stream_ids, true); + } else { + err = findRtpStreams(&stream_ids, false); + } + if (!err.isNull()) { + QMessageBox::warning(this, tr("RTP packet search failed"), + err, + QMessageBox::Ok); +#ifdef QT_MULTIMEDIA_LIB + } else { + openTelephonyRtpPlayerDialog()->addRtpStreams(stream_ids); +#endif // QT_MULTIMEDIA_LIB + } + foreach(rtpstream_id_t *id, stream_ids) { + rtpstream_id_free(id); + g_free(id); + } +} + +// Wireless Menu + +void WiresharkMainWindow::connectWirelessMenuActions() +{ + connect(main_ui_->actionBluetoothATT_Server_Attributes, &QAction::triggered, this, [=]() { + BluetoothAttServerAttributesDialog *bluetooth_att_sever_attributes_dialog = new BluetoothAttServerAttributesDialog(*this, capture_file_); + connect(bluetooth_att_sever_attributes_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); + connect(bluetooth_att_sever_attributes_dialog, SIGNAL(updateFilter(QString, bool)), + this, SLOT(filterPackets(QString, bool))); + bluetooth_att_sever_attributes_dialog->show(); + }); + + connect(main_ui_->actionBluetoothDevices, &QAction::triggered, this, [=]() { + BluetoothDevicesDialog *bluetooth_devices_dialog = new BluetoothDevicesDialog(*this, capture_file_, packet_list_); + connect(bluetooth_devices_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); + connect(bluetooth_devices_dialog, SIGNAL(updateFilter(QString, bool)), + this, SLOT(filterPackets(QString, bool))); + bluetooth_devices_dialog->show(); + }); + + connect(main_ui_->actionBluetoothHCI_Summary, &QAction::triggered, this, [=]() { + BluetoothHciSummaryDialog *bluetooth_hci_summary_dialog = new BluetoothHciSummaryDialog(*this, capture_file_); + connect(bluetooth_hci_summary_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); + connect(bluetooth_hci_summary_dialog, SIGNAL(updateFilter(QString, bool)), + this, SLOT(filterPackets(QString, bool))); + bluetooth_hci_summary_dialog->show(); + }); + + connect(main_ui_->actionWirelessWlanStatistics, &QAction::triggered, this, [=]() { statCommandWlanStatistics(NULL, NULL); }); +} + +// Tools Menu + +void WiresharkMainWindow::connectToolsMenuActions() +{ + connect(main_ui_->actionToolsFirewallAclRules, &QAction::triggered, this, [=]() { + FirewallRulesDialog *firewall_rules_dialog = new FirewallRulesDialog(*this, capture_file_); + firewall_rules_dialog->show(); + }); + + connect(main_ui_->actionToolsCredentials, &QAction::triggered, this, [=]() { + CredentialsDialog *credentials_dialog = new CredentialsDialog(*this, capture_file_, packet_list_); + credentials_dialog->show(); + }); + + connect(main_ui_->actionToolsMacLookup, &QAction::triggered, this, [=]() { + ManufDialog *manuf_dialog = new ManufDialog(*this, capture_file_); + manuf_dialog->show(); + }); + + connect(main_ui_->actionToolsTLSKeylog, &QAction::triggered, this, &WiresharkMainWindow::openTLSKeylogDialog); +} + +// Help Menu +void WiresharkMainWindow::connectHelpMenuActions() +{ + + connect(main_ui_->actionHelpAbout, &QAction::triggered, this, [=]() { + AboutDialog *about_dialog = new AboutDialog(this); + + if (about_dialog->isMinimized() == true) + { + about_dialog->showNormal(); + } + else + { + about_dialog->show(); + } + + about_dialog->raise(); + about_dialog->activateWindow(); + }); + + connect(main_ui_->actionHelpContents, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(HELP_CONTENT); }); + connect(main_ui_->actionHelpMPWireshark, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_WIRESHARK); }); + connect(main_ui_->actionHelpMPWireshark_Filter, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_WIRESHARK_FILTER); }); + connect(main_ui_->actionHelpMPCapinfos, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_CAPINFOS); }); + connect(main_ui_->actionHelpMPDumpcap, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_DUMPCAP); }); + connect(main_ui_->actionHelpMPEditcap, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_EDITCAP); }); + connect(main_ui_->actionHelpMPMergecap, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_MERGECAP); }); + connect(main_ui_->actionHelpMPRawshark, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_RAWSHARK); }); + connect(main_ui_->actionHelpMPReordercap, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_REORDERCAP); }); + connect(main_ui_->actionHelpMPText2pcap, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_TEXT2PCAP); }); + connect(main_ui_->actionHelpMPTShark, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_MAN_TSHARK); }); + connect(main_ui_->actionHelpWebsite, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(ONLINEPAGE_HOME); }); + connect(main_ui_->actionHelpFAQ, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(ONLINEPAGE_FAQ); }); + connect(main_ui_->actionHelpAsk, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(ONLINEPAGE_ASK); }); + connect(main_ui_->actionHelpDownloads, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(ONLINEPAGE_DOWNLOAD); }); + connect(main_ui_->actionHelpWiki, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(ONLINEPAGE_WIKI); }); + connect(main_ui_->actionHelpSampleCaptures, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(ONLINEPAGE_SAMPLE_FILES); }); + connect(main_ui_->actionHelpReleaseNotes, &QAction::triggered, this, [=]() { mainApp->helpTopicAction(LOCALPAGE_RELEASE_NOTES); }); +} + +#ifdef HAVE_SOFTWARE_UPDATE +void WiresharkMainWindow::checkForUpdates() +{ + software_update_check(); +} +#endif + +void WiresharkMainWindow::setPreviousFocus() { + previous_focus_ = mainApp->focusWidget(); + if (previous_focus_ != nullptr) { + connect(previous_focus_, SIGNAL(destroyed()), this, SLOT(resetPreviousFocus())); + } +} + +void WiresharkMainWindow::resetPreviousFocus() { + previous_focus_ = nullptr; +} + +void WiresharkMainWindow::goToCancelClicked() +{ + main_ui_->goToFrame->animatedHide(); + if (previous_focus_) { + disconnect(previous_focus_, SIGNAL(destroyed()), this, SLOT(resetPreviousFocus())); + previous_focus_->setFocus(); + resetPreviousFocus(); + } +} + +void WiresharkMainWindow::goToGoClicked() +{ + gotoFrame(main_ui_->goToLineEdit->text().toInt()); + + goToCancelClicked(); +} + +void WiresharkMainWindow::goToLineEditReturnPressed() +{ + goToGoClicked(); +} + +void WiresharkMainWindow::showResolvedAddressesDialog() +{ + QString capFileName; + wtap* wth = Q_NULLPTR; + if (capture_file_.isValid()) + { + capFileName = capture_file_.capFile()->filename; + wth = capture_file_.capFile()->provider.wth; + } + ResolvedAddressesDialog *resolved_addresses_dialog = + new ResolvedAddressesDialog(this, capFileName, wth); + resolved_addresses_dialog->show(); +} + +void WiresharkMainWindow::showConversationsDialog() +{ + ConversationDialog *conv_dialog = new ConversationDialog(*this, capture_file_); + connect(conv_dialog, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + connect(conv_dialog, SIGNAL(openFollowStreamDialog(int, guint, guint)), + this, SLOT(openFollowStreamDialog(int, guint, guint))); + connect(conv_dialog, SIGNAL(openTcpStreamGraph(int)), + this, SLOT(openTcpStreamDialog(int))); + conv_dialog->show(); +} + +void WiresharkMainWindow::showEndpointsDialog() +{ + EndpointDialog *endp_dialog = new EndpointDialog(*this, capture_file_); + connect(endp_dialog, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType)), + this, SIGNAL(filterAction(QString, FilterAction::Action, FilterAction::ActionType))); + connect(endp_dialog, SIGNAL(openFollowStreamDialog(int)), + this, SLOT(openFollowStreamDialog(int))); + connect(endp_dialog, SIGNAL(openTcpStreamGraph(int)), + this, SLOT(openTcpStreamDialog(int))); + endp_dialog->show(); +} + +void WiresharkMainWindow::externalMenuItemTriggered() +{ + QAction * triggerAction = NULL; + QVariant v; + ext_menubar_t * entry = NULL; + + if (QObject::sender()) { + triggerAction = (QAction *)QObject::sender(); + v = triggerAction->data(); + + if (v.canConvert()) { + entry = (ext_menubar_t *)v.value(); + + if (entry->type == EXT_MENUBAR_ITEM) { + entry->callback(EXT_MENUBAR_QT_GUI, (gpointer)((void *)main_ui_), entry->user_data); + } else { + QDesktopServices::openUrl(QUrl(QString((gchar *)entry->user_data))); + } + } + } +} + +void WiresharkMainWindow::extcap_options_finished(int result) +{ + if (result == QDialog::Accepted) { + QString before_what(tr(" before starting a new capture")); + if (testCaptureFileClose(before_what)) { + startCapture(QStringList()); + } + } + this->welcome_page_->getInterfaceFrame()->interfaceListChanged(); +} + +void WiresharkMainWindow::showExtcapOptionsDialog(QString &device_name, bool startCaptureOnClose) +{ + ExtcapOptionsDialog * extcap_options_dialog = ExtcapOptionsDialog::createForDevice(device_name, startCaptureOnClose, this); + /* The dialog returns null, if the given device name is not a valid extcap device */ + if (extcap_options_dialog) { + extcap_options_dialog->setModal(true); + extcap_options_dialog->setAttribute(Qt::WA_DeleteOnClose); + if (startCaptureOnClose) { + connect(extcap_options_dialog, SIGNAL(finished(int)), + this, SLOT(extcap_options_finished(int))); + } +#ifdef HAVE_LIBPCAP + if (capture_options_dialog_ && startCaptureOnClose) { + /* Allow capture options dialog to close */ + connect(extcap_options_dialog, SIGNAL(accepted()), + capture_options_dialog_, SLOT(accept())); + } +#endif + extcap_options_dialog->show(); + } +} + +void WiresharkMainWindow::on_actionContextWikiProtocolPage_triggered() +{ + QAction *wa = qobject_cast(sender()); + if (!wa) return; + + bool ok = false; + int field_id = wa->data().toInt(&ok); + if (!ok) return; + + const QString proto_abbrev = proto_registrar_get_abbrev(field_id); + + int ret = QMessageBox::question(this, mainApp->windowTitleString(tr("Wiki Page for %1").arg(proto_abbrev)), + tr("

The Wireshark Wiki is maintained by the community.

" + "

The page you are about to load might be wonderful, " + "incomplete, wrong, or nonexistent.

" + "

Proceed to the wiki?

"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (ret != QMessageBox::Yes) return; + + QUrl wiki_url = QString(WS_WIKI_URL("Protocols/%1")).arg(proto_abbrev); + QDesktopServices::openUrl(wiki_url); +} + +void WiresharkMainWindow::on_actionContextFilterFieldReference_triggered() +{ + QAction *wa = qobject_cast(sender()); + if (!wa) return; + + bool ok = false; + int field_id = wa->data().toInt(&ok); + if (!ok) return; + + const QString proto_abbrev = proto_registrar_get_abbrev(field_id); + + QUrl dfref_url = QString(WS_DOCS_URL "/dfref/%1/%2") + .arg(proto_abbrev[0]) + .arg(proto_abbrev); + QDesktopServices::openUrl(dfref_url); +} + +void WiresharkMainWindow::activatePluginIFToolbar(bool) +{ + QAction *sendingAction = dynamic_cast(sender()); + if (!sendingAction || !sendingAction->data().isValid()) + return; + + ext_toolbar_t *toolbar = VariantPointer::asPtr(sendingAction->data()); + + QList toolbars = findChildren(); + foreach(QToolBar *bar, toolbars) { + AdditionalToolBar *iftoolbar = dynamic_cast(bar); + if (iftoolbar && iftoolbar->menuName().compare(toolbar->name) == 0) { + if (iftoolbar->isVisible()) { + iftoolbar->setVisible(false); + sendingAction->setChecked(true); + } else { + iftoolbar->setVisible(true); + sendingAction->setChecked(true); + } + } + } +} + +void WiresharkMainWindow::rtpPlayerDialogReplaceRtpStreams(QVector stream_ids _U_) +{ +#ifdef QT_MULTIMEDIA_LIB + openTelephonyRtpPlayerDialog()->replaceRtpStreams(stream_ids); +#endif +} + +void WiresharkMainWindow::rtpPlayerDialogAddRtpStreams(QVector stream_ids _U_) +{ +#ifdef QT_MULTIMEDIA_LIB + openTelephonyRtpPlayerDialog()->addRtpStreams(stream_ids); +#endif +} + +void WiresharkMainWindow::rtpPlayerDialogRemoveRtpStreams(QVector stream_ids _U_) +{ +#ifdef QT_MULTIMEDIA_LIB + openTelephonyRtpPlayerDialog()->removeRtpStreams(stream_ids); +#endif +} + +void WiresharkMainWindow::rtpAnalysisDialogReplaceRtpStreams(QVector stream_ids) +{ + openTelephonyRtpAnalysisDialog()->replaceRtpStreams(stream_ids); +} + +void WiresharkMainWindow::rtpAnalysisDialogAddRtpStreams(QVector stream_ids) +{ + openTelephonyRtpAnalysisDialog()->addRtpStreams(stream_ids); +} + +void WiresharkMainWindow::rtpAnalysisDialogRemoveRtpStreams(QVector stream_ids) +{ + openTelephonyRtpAnalysisDialog()->removeRtpStreams(stream_ids); +} + +void WiresharkMainWindow::rtpStreamsDialogSelectRtpStreams(QVector stream_ids) +{ + openTelephonyRtpStreamsDialog()->selectRtpStream(stream_ids); +} + +void WiresharkMainWindow::rtpStreamsDialogDeselectRtpStreams(QVector stream_ids) +{ + openTelephonyRtpStreamsDialog()->deselectRtpStream(stream_ids); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/ui/qt/wireshark_pl.ts b/ui/qt/wireshark_pl.ts new file mode 100644 index 00000000..7569259b --- /dev/null +++ b/ui/qt/wireshark_pl.ts @@ -0,0 +1,14754 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + O programie Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Analizator Protokołów</span> + + + Copy the version information to the clipboard + + + + Copy to Clipboard + + + + Authors + Autorzy + + + Search Authors + Szukaj autorów + + + Folders + Foldery + + + Filter by path + Filtruj po ścieżce + + + Plugins + Wtyczki + + + No plugins found. + Nie znaleziono wtyczek. + + + Search Plugins + Szukaj wtyczek + + + Filter by type: + Filtruj po typie: + + + Keyboard Shortcuts + Skróty klawiaturowe + + + Search Shortcuts + Szukaj skrótów + + + Acknowledgments + Podziękowania + + + License + Licencja + + + The directory does not exist + Katalog nie istnieje. + + + Should the directory %1 be created? + Czy utworzyć katalog %1? + + + The directory could not be created + Nie można utworzyć katalogu. + + + The directory %1 could not be created. + + + + Show in Finder + Pokaż w wyszukiwarce + + + Show in Folder + Pokaż w folderze + + + Copy + Kopiuj + + + Copy Row(s) + + + + + + + + + AddressEditorFrame + + Frame + Ramka + + + Name Resolution Preferences… + Name Resolution Preferences... + Ustawienia Rozwiązywania Nazw… + + + Address: + Adres: + + + Name: + Nazwa: + + + Can't assign %1 to %2. + + + + + AdvancedPrefsModel + + Name + Nazwa + + + Status + Status + + + Type + typ + + + Value + Wartość + + + + ApplyLineEdit + + Apply changes + Zastosuj zmiany + + + + AuthorListModel + + Name + Nazwa + + + Email + Email + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Bluetooth ATT Atrybuty Serwera + + + Handle + Handle + + + UUID + UUID + + + UUID Name + Nazwa UUID + + + All Interfaces + Wszystkie interfejsy + + + All Devices + Wszystkie urządzenia + + + Remove duplicates + Usuwaj duplikaty + + + Copy Cell + Kopiuj komórkę + + + Copy Rows + Kopiuj wiersze + + + Copy All + Kopiuj wszystko + + + Save as image + Zapisz jako obraz + + + Mark/Unmark Row + Zaznacz/Odznacz wiersz + + + Ctrl-M + + + + Mark/Unmark Cell + Zaznacz/Odznacz komórkę + + + Save Table Image + Zapisz jako obraz + + + PNG Image (*.png) + Obraz PNG (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + Urządzenie Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Nazwa + + + Class of Device + Klasa urządzenia + + + LMP Version + Wersja LMP + + + LMP Subversion + Podwersja LMP + + + Manufacturer + Producent + + + HCI Version + Wersja HCI + + + HCI Revision + Rewizja HCI + + + Scan + Scan + + + Authentication + Authentication + + + Encryption + Encryption + + + ACL MTU + ACL MTU + + + ACL Total Packets + ACL Total Packets + + + SCO MTU + SCO MTU + + + SCO Total Packets + SCO Total Packets + + + LE ACL MTU + LE ACL MTU + + + LE ACL Total Packets + LE ACL Total Packets + + + LE ISO MTU + + + + LE ISO Total Packets + + + + Inquiry Mode + Inquiry Mode + + + Page Timeout + Page Timeout + + + Simple Pairing Mode + Simple Pairing Mode + + + Voice Setting + Voice Setting + + + Value + Wartość + + + Changes + Zmiany + + + %1 changes + %1 zmian + + + Copy Cell + Kopiuj komórkę + + + Copy Rows + Kopiuj wiersze + + + Copy All + Kopiuj wszystko + + + Save as image + Zapisz jako obraz + + + Mark/Unmark Row + Zaznacz/Odznacz wiersz + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Zaznacz/Odznacz komórkę + + + Unknown + Nieznane + + + Bluetooth Device - %1%2 + Urządzenie Bluetooth - %1%2 + + + enabled + włączony + + + disabled + wyłączony + + + %1 ms (%2 slots) + %1 ms (%2 sloty) + + + Save Table Image + Zapisz jako obraz + + + PNG Image (*.png) + Obraz PNG (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Urządzenia Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Nazwa + + + LMP Version + Wersja LMP + + + LMP Subversion + Podwersja LMP + + + Manufacturer + Producent + + + HCI Version + Wersja HCI + + + HCI Revision + Rewizja HCI + + + Is Local Adapter + Czy lokalny adapter + + + All Interfaces + Wszystkie interfejsy + + + Show information steps + Pokaż poszczególne kroki informacyjne + + + %1 items; Right click for more option; Double click for device details + %1 urządzeń; Kliknij prawy przycisk myszy by zobaczyć więcej opcji; Podwójnie kliknij by przejść do szczegółów urządzenia + + + Copy Cell + Kopiuj komórkę + + + Copy Rows + Kopiuj wiersze + + + Copy All + Kopiuj wszystko + + + Save as image + Zapisz jako obrazek + + + Mark/Unmark Row + Zaznacz/Odznacz wiersz + + + Ctrl-M + + + + Mark/Unmark Cell + Zaznacz/Odznacz komórkę + + + true + tak + + + Save Table Image + Zapisz obraz tabeli + + + PNG Image (*.png) + Obraz PNG (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Bluetooth Podsumowanie HCI + + + Name + Nazwa + + + OGF + OGF + + + OCF + OCF + + + Opcode + Opcode + + + Event + Event + + + Subevent + Subevent + + + Status + Status + + + Reason + Reason + + + Hardware Error + Hardware Error + + + Occurrence + Wystąpienia + + + Link Control Commands + Komendy Kontroli Łącza + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Komendy Polityki Łącza + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Komendy Kontrolera i Pasma + + + 0x03 + 0x03 + + + Informational Parameters + Parametry Informacyjne + + + 0x04 + 0x04 + + + Status Parameters + Parametry Statusu + + + 0x05 + 0x05 + + + Testing Commands + Komendy Testujące + + + 0x06 + 0x06 + + + LE Controller Commands + Komendy Kontrolera LE + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + Komendy Bluetooth Logo Testing + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Komendy producenta + + + 0x3F + 0x3F + + + Unknown OGF + Nieznany OGF + + + Events + Events + + + Hardware Errors + Hardware Errors + + + Results filter: + Filtr wyników: + + + Display filter: + Filtr wyświetlania: + + + All Interfaces + Wszystkie interfejsy + + + All Adapters + Wszystkie adaptery + + + Copy Cell + Kopiuj komórkę + + + Copy Rows + Kopiuj wiersze + + + Copy All + Kopiuj wszystko + + + Save as image + Zapisz jako obraz + + + Mark/Unmark Row + Zaznacz/Odznacz wiersz + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Zaznacz/Odznacz komórkę + + + Unknown + Nieznany + + + Adapter %1 + Adapter %1 + + + Frame %1 + Ramka %1 + + + Pending + Pending + + + Save Table Image + Zapisz obraz tabeli + + + PNG Image (*.png) + Obraz PNG (*.png) + + + + ByteViewTab + + Packet bytes + Bajty pakietu + + + + ByteViewText + + Allow hover highlighting + + + + Show bytes as hexadecimal + Pokaż szesnastkowo + + + …as decimal + + + + …as octal + + + + …as bits + + + + Show text based on packet + + + + …as ASCII + + + + …as EBCDIC + + + + + CaptureFile + + [closing] + [zamykanie] + + + [closed] + [zamknięty] + + + + CaptureFileDialog + + This capture file contains comments. + Plik zawiera komentarze. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + Ten format nie obsługuje komentarzy. Czy chcesz zapisać plik w formacie, który je obsługuje czy porzucić komentarze? + + + Discard comments and save + Porzuć komentarze i zapisz + + + Save in another format + Zapisz w innym formacie + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + Nie ma formatu w którym możliwy jest zapis z komentarzami. Czy chcesz porzucić komentarze i zapisać plik? + + + All Files ( + Wszystkie pliki ( + + + All Capture Files + Wszystkie pliki przechwytywania + + + Format: + Format: + + + Size: + Rozmiar: + + + Start / elapsed: + Start / upłynęło: + + + Automatically detect file type + Automatycznie wykryj typ pliku + + + Prepend packets + Dodaj pakiety na początku + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Wstaw pakiety z wybranego pliku na początek. Czasy pakietów będą zignorowane. + + + Merge chronologically + Scalaj chronologicznie + + + Insert packets in chronological order. + Wstaw pakiety w kolejności chronologicznej + + + Append packets + Dodaj pakiety + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Wstaw pakiety z wybranego pliku na koniec. Czasy pakietów będą zignorowane. + + + Read filter: + Filtr wczytywania: + + + Compress with g&zip + Kompresuj używając gzip + + + Open Capture File + Wireshark: Open Capture File + Otwórz plik przechwytywania + + + Save Capture File As + Wireshark: Save Capture File As + Zapisz plik przechwytywania jako + + + Save as: + Zapisz jako: + + + Export Specified Packets + Wireshark: Export Specified Packets + Eksportuj wybrane pakiety + + + Export as: + Eksportuj jako: + + + Merge Capture File + Wireshark: Merge Capture File + Scal pliki przechwytywania + + + Unknown file type returned by save as dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + directory + katalog + + + unknown file format + nieznany format pliku + + + error opening file + błąd podczas otwierania pliku + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + + + + + + + %1, timed out at %Ln data record(s) + + + + + + + + %1, %Ln data record(s) + + + + + + + + unknown + nieznany + + + + CaptureFilePropertiesDialog + + Details + Szczegóły + + + Capture file comments + Komentarze pliku przechwytywania + + + Refresh + Odśwież + + + Copy To Clipboard + Skopiuj do schowka + + + Save Comments + Zapisz komentarze + + + Capture File Properties + Szczegóły pliku przechwytywania + + + Unknown + Nieznane + + + File + Plik + + + Name + Nazwa + + + Length + Rozmiar + + + Hash (SHA256) + Hash (SHA256) + + + Hash (SHA1) + Hash (SHA1) + + + Format + Format + + + Encapsulation + Enkapsulacja + + + Snapshot length + Długość wycinka + + + Time + Czas + + + First packet + Pierwszy pakiet + + + Last packet + Ostatni pakiet + + + Elapsed + Minęło + + + Section %1 + + + + Capture + Przechwytuj + + + Hardware + Sprzęt + + + OS + OS + + + Application + Aplikacja + + + Interfaces + Interfejsy + + + Interface + Interfejs + + + Dropped packets + Porzucone pakiety + + + Capture filter + Filtr przechwytywania + + + Link type + Typ łącza + + + Packet size limit (snaplen) + + + + none + brak + + + %1 bytes + %1 bajtów + + + Statistics + Statystyki + + + Measurement + Pomiary + + + Captured + Przechwycone + + + Displayed + Wyświetlane + + + Marked + Zaznaczone + + + Packets + Pakiety + + + Time span, s + Okres czasu, s + + + Average pps + Średni pps + + + Average packet size, B + Średni rozmiar pakietu, B + + + Bytes + Bajty + + + Average bytes/s + Średnio bajtów/s + + + Average bits/s + Średnio bitów/s + + + Section Comment + + + + Packet Comments + Komentarze pakietu + + + <p>Frame %1: + <p>Ramka %1: + + + Created by Wireshark %1 + + + Stworzony dzięki Wiresharkowi %1 + + + + CaptureFilterCombo + + Capture filter selector + Wybór filtru przechwytywania + + + + CaptureFilterEdit + + Capture filter entry + Wpis filtru przechwytywania + + + Manage saved bookmarks. + Zarządzaj zapisanymi zakładkami. + + + Apply this filter string to the display. + Zastosuj filtr. + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + Wybrano wiele filtrów. Nadpisz je lub pozostaw puste by ich nie zmieniać. + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>Wybrano interfejsy, które wymagają różnych filtrów przechwytywania. Wpisanie tutaj filtru spowoduje zastąpienie ich. W przeciwnym przypadku zostaną użyte.</p> + + + Enter a capture filter %1 + Wpisz filtr przechwytywania %1 + + + Save this filter + Zapisz filtr + + + Remove this filter + Usuń filtr + + + Manage Capture Filters + Zarządzaj filtrami przechwytywania + + + + CaptureInfoDialog + + Capture Information + Informacje przechwytywania + + + Stop Capture + Zatrzymaj przechwytywanie + + + %1 packets, %2:%3:%4 + %1 pakietów, %2:%3:%4 + + + + CaptureInfoModel + + Other + Inne + + + + CaptureOptionsDialog + + Input + + + + Interface + Interfejs + + + Traffic + + + + Link-layer Header + + + + Promiscuous + + + + Snaplen (B) + + + + Buffer (MB) + + + + Monitor Mode + + + + Capture Filter + + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Prawdopodobnie chcesz włączyć to. Zwykle karta sieciowa może przechwytywać tylko ruch wysyłany na jej adres sieciowy. Jeśli chcesz przechwytywać cały ruch sieciowy zaznacz tą opcję. Zobacz FAQ by uzyskać więcej informacji na temat przechwytywania pakietów w sieci przełączalnej.</p></body></html> + + + Enable promiscuous mode on all interfaces + + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + + + + Manage Interfaces… + + + + Capture filter for selected interfaces: + + + + Compile BPFs + + + + Output + + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + + + + Capture to a permanent file + + + + File: + Plik: + + + Browse… + Przeglądaj… + + + Output format: + + + + pcapng + + + + pcap + + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + + + + Create a new file automatically… + + + + after + + + + Switch to the next file after the specified number of packets have been captured. + + + + packets + pakietów + + + Switch to the next file after the file size exceeds the specified file size. + + + + kilobytes + + + + megabytes + + + + gigabytes + + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + + + + seconds + + + + minutes + + + + hours + + + + when time is a multiple of + + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + + + + compression + + + + None + Brak + + + gzip + + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + + + + Use a ring buffer with + + + + files + + + + Options + Opcje + + + Display Options + + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + + + + Update list of packets in real-time + + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + + + + Automatically scroll during live capture + + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + + + + Show capture information during live capture + + + + Name Resolution + + + + Perform MAC layer name resolution while capturing. + + + + Resolve MAC addresses + + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + + + + Resolve network names + + + + Perform transport layer name resolution while capturing. + + + + Resolve transport names + + + + Stop capture automatically after… + + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + + + + Stop capturing after the specified number of packets have been captured. + + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + + + + Stop capturing after the specified amount of data has been captured. + + + + Stop capturing after the specified amount of time has passed. + + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + + + + Directory for temporary files + + + + Capture Options + Opcje przechytywania + + + Start + Start + + + Leave blank to use a temporary file + + + + Specify a Capture File + + + + Specify temporary directory + + + + %1: %2 + %1: %2 + + + Addresses + + + + Address + Adres + + + no addresses + + + + Error + + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + + + + + CapturePreferencesFrame + + Frame + Ramka + + + Default interface + Domyślny interfejs + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Prawdopodobnie chcesz włączyć to. Zwykle karta sieciowa może przechwytywać tylko ruch wysyłany na jej adres sieciowy. Jeśli chcesz przechwytywać cały ruch sieciowy zaznacz tą opcję. Zobacz FAQ by uzyskać więcej informacji na temat przechwytywania pakietów w sieci przełączalnej.</p></body></html> + + + Capture packets in promiscuous mode + Zrzucaj pakiety w trybie mieszanym + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Zapisuj pakiety w formacie pcap-ng</p></body></html> + + + Capture packets in pcapng format + Przechwytuj pakiety do formatu pcapng + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Aktualizuj listę pakietów czasie przechwytywania. To może skutkować gubieniem pakietów na sieciach wysokich prędkości.</p></body></html> + + + Update list of packets in real time + Aktualizuj listę pakietów w czasie rzeczywistym + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + Nie sprawdzaj interfejsów w czasie ładowania + + + Disable external capture interfaces + Wyłącz zewnętrzne interfejsy przechwytywania + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + symbol "@" zostanie ignorowany. + + + + ColoringRulesDialog + + Dialog + Okno + + + <small><i>A hint.</i></small> + <small><i>Podpowiedź.</i></small> + + + Add a new coloring rule. + Utwórz nową regułę. + + + Delete this coloring rule. + Usuń tę regułę. + + + Duplicate this coloring rule. + Duplikuj tę regułę. + + + Clear all coloring rules. + Usuń wszystkie reguły kolorowania. + + + Set the foreground color for this rule. + Ustaw kolor dla pierwszego planu. + + + Foreground + Pierwszy plan + + + Set the background color for this rule. + Ustaw kolor tła. + + + Background + Tło + + + Set the display filter using this rule. + Ustaw filtr wyświetlania używając tej reguły. + + + Apply as filter + Zastosuj filtr + + + Select a file and add its filters to the end of the list. + Wybierz plik i dopisz filtry do jego końca. + + + Save filters in a file. + Zapisz filtry w pliku. + + + Coloring Rules %1 + Reguły kolorowania %1 + + + Import… + + + + Export… + + + + Copy coloring rules from another profile. + Kopiuj reguły kolorowania z innego profilu. + + + Open + Otwórz + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Kliknij podwójnie by edytować. Przeciągnij by przenieść. Reguły są przetwarzane zgodnie z kolejnością aż do znalezienia pasującej reguły. + + + Import Coloring Rules + Importuj reguły kolorowania + + + Export %1 Coloring Rules + Wyeksportuj %1 reguły kolorowania + + + + ColoringRulesModel + + New coloring rule + + + + Unable to save coloring rules: %1 + Nie można zapisać reguł kolorowania: %1 + + + Name + Nazwa + + + Filter + Filtr + + + + ColumnEditorFrame + + Frame + Ramka + + + Title: + Title + Tytuł: + + + Type: + Type + Typ: + + + Fields: + Fields + Pola: + + + Occurrence: + Occurrence + Wystąpienia: + + + Resolve Names: + + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + + + + Missing fields. + Pominięte pola. + + + Invalid fields. + Niepoprawne pola. + + + Invalid occurrence value. + Błędne wystąpienia wartości. + + + + ColumnListModel + + Displayed + Wyświetlane + + + Title + + + + Type + + + + Fields + + + + Field Occurrence + + + + Resolved + + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + + + + New Column + + + + + ColumnPreferencesFrame + + Frame + Ramka + + + Add a new column + + + + Delete selected column + + + + Show displayed columns only + + + + Reset all changes + + + + + CompiledFilterOutput + + Compiled Filter Output + Skompilowany filtr wyjścia + + + Copy + Kopiuj + + + Copy filter text to the clipboard. + Skopiuj filtr do schowka. + + + + ConversationDataModel + + Address A + + + + Port A + + + + Address B + + + + Port B + + + + Packets + Pakiety + + + Bytes + + + + Stream ID + + + + Packets A + + + + Bytes A + + + + Packets B + + + + Bytes B + + + + Abs Start + + + + Rel Start + + + + Duration + Czas trwania + + + Bits/s A + + + + Bits/s B + + + + Total Packets + + + + Percent Filtered + + + + + ConversationDialog + + Follow Stream… + + + + Follow a TCP or UDP stream. + Podążaj za strumieniem TCP lub UDP. + + + Graph… + + + + Graph a TCP conversation. + Wykres konwersacji TCP. + + + + ConversationHashTablesDialog + + Dialog + Okno + + + Conversation Hash Tables + Tablice mieszające konwersacji + + + + CopyFromProfileButton + + Copy from + Kopiuj z + + + Copy entries from another profile. + Kopiuj z innego profilu. + + + System default + + + + + CredentialsDialog + + Wireshark - Credentials + + + + Credentials + + + + + CredentialsModel + + Click to select the packet + + + + Click to select the packet with username + + + + Username not available + + + + Packet No. + + + + Protocol + Protokół + + + Username + + + + Additional Info + + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + Kopiuj bajty jako Hex + ASCII + + + Copy packet bytes as a hex and ASCII dump. + Kopiuj bajty pakietu jako Hex i ASCII. + + + …as Hex Dump + + + + Copy packet bytes as a hex dump. + Kopiuj bajty pakietu jako Hex. + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + + + + Copy packet bytes as a stream of hex. + Kopiuj bajty pakietu jako strumień Hex. + + + …as a Base64 String + + + + Copy packet bytes as a base64 encoded string. + + + + Copy packet bytes as application/octet-stream MIME data. + Kopiuj bajty pakietu jako typ MIME application/octet-stream. + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + Nowa reguła. + + + Remove this dissection behavior. + Usuń regułę. + + + Copy this dissection behavior. + Kopiuj regułę. + + + Clear all dissection behaviors. + Usuń wszystkie reguły. + + + Decode As… + + + + Open + Otwórz + + + + DecodeAsModel + + Match using this field + Porównuj używając tego pola + + + Change behavior when the field matches this value + + + + Field value type (and base, if Integer) + + + + Current"Decode As" behavior + Obecne ustawienia dekodowania + + + Default "Decode As" behavior + Domyślne ustawiania dekodowania + + + String + String + + + Integer, base + + + + unknown + nieznany + + + <none> + <none> + + + GUID + GUID + + + Field + Pole + + + Value + Wartość + + + Type + Typ + + + Default + Domyślnie + + + Current + + + + + DisplayFilterCombo + + Display filter selector + + + + Select from previously used filters. + + + + + DisplayFilterEdit + + Display filter entry + Wpis filtru wyświetlania + + + Manage saved bookmarks. + Zarządzaj zapisanymi zakładkami. + + + Display Filter Expression… + + + + Apply a display filter %1 <%2/> + Zastosuj filtr wyświetlania %1 <%2/> + + + Enter a display filter %1 + Wpisz filtr wyświetlania %1 + + + Clear display filter + + + + Apply display filter + + + + Left align buttons + + + + Apply a read filter %1 + Zastosuj filtr wczytania %1 + + + Current filter: %1 + + + + Invalid filter: + Błędny filtr: + + + Save this filter + Zapisz filtr + + + Remove this filter + Usuń filtr + + + Manage Display Filters + Zarządzaj filtrami wyświetlania + + + Filter Button Preferences... + + + + + DisplayFilterExpressionDialog + + Dialog + Okno + + + Select a field to start building a display filter. + Wybierz pole by zacząć tworzenie filtru wyświetlania. + + + Field Name + Nazwa pola + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Przeszukiwanie listy nazw pól.</p></body></html> + + + Search: + Szukaj: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>Relacji można użyć by ograniczyć wyszukiwanie pól do wybranych ich wartości. Każda relacja posiada następujące właściwości:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Sprawdza czy jakiś pakiet zawiera dane pole</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, itd.</span></p></td><td><p>Porównuje z konkretną wartością.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Sprawdza czy pole zawiera w sobie łańcuch znaków (contains) lub stosuje wyrażenie regularne (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Porównuje pole do wartości ze zbioru</p></td></tr></table></body></html> + + + + + Relation + Relacja + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + + + + Quantifier + + + + Any + Każdy + + + All + + + + Match against this value. + Porównuje z tą wartością. + + + Value + Wartość + + + If the field you have selected has a known set of valid values they will be listed here. + Jeśli pole, które wybrano ma znane wartości to będą tu wyświetlane. + + + Predefined Values + Predefiniowane wartości + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + Jeśli pole, które wybrano obejmuje zakres bajtów (np. wybrano protokół) to tutaj można ograniczyć porównywanie do pewnego zakresu bajtów. + + + Range (offset:length) + Zakres (pozycja:długość) + + + No display filter + Brak filtru wyświetlania + + + <small><i>A hint.</i></small> + <small><i>Podpowiedź.</i></small> + + + Display Filter Expression + Wyrażenie filtru wyświetlania + + + Select a field name to get started + Wybierz nazwę pola by rozpocząć + + + Click OK to insert this filter + Kliknij OK by dodać filtr + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + Okno + + + Search: + Szukaj: + + + Dissector Tables + Tabele Dekoderów + + + + DissectorTablesProxyModel + + Table Type + + + + String + String + + + Dissector Description + + + + Integer + + + + Protocol + Protokół + + + Short Name + Krótka Nazwa + + + Table Name + Nazwa Tabeli + + + Selector Name + Selektor + + + + EnabledProtocolsDialog + + Dialog + Okno + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>Wyłączenie protokołu zapobiega dekodowaniu wyższych warstw protokołów</i></small> + + + Search: + Szukaj: + + + in + + + + Enable All + Włącz wszystkie + + + Disable All + Wyłącz wszystkie + + + Invert + Przełącz + + + Enabled Protocols + Używane protokoły + + + Everywhere + + + + Only Protocols + + + + Only Description + + + + Only enabled protocols + + + + Only disabled protocols + + + + any protocol + + + + non-heuristic protocols + + + + heuristic protocols + + + + + EnabledProtocolsModel + + Protocol + Protokół + + + Description + Opis + + + + EndpointDataModel + + Address + Adres + + + Port + + + + Packets + Pakiety + + + Bytes + + + + Tx Packets + + + + Tx Bytes + + + + Rx Packets + + + + Rx Bytes + + + + Country + + + + City + + + + Latitude + + + + Longitude + + + + AS Number + + + + AS Organization + + + + Total Packets + + + + Percent Filtered + + + + + EndpointDialog + + Map + + + + Draw IPv4 or IPv6 endpoints on a map. + + + + Open in browser + + + + Save As… + + + + Map file error + + + + Save Endpoints Map + + + + Failed to save map file %1. + + + + + EthernetAddressModel + + Type + + + + Name + + + + Address + Adres + + + All entries + + + + Hosts + + + + Ethernet Addresses + Adresy Ethernet + + + Ethernet Manufacturers + Producenci Ethernet + + + Ethernet Well-Known Addresses + Dobrze znane adresy Ethernet + + + + ExpertInfoDialog + + Dialog + Okno + + + <small><i>A hint.</i></small> + <small><i>Podpowiedź.</i></small> + + + Limit to Display Filter + Ogranicz do filtru wyświetlania + + + Group by summary + Grupuj według podsumowania + + + Search expert summaries. + Przeszukuj podsumowanie eksperckie. + + + Search: + Szukaj: + + + Show… + Show... + Pokaż… + + + Error + Błędy + + + Show error packets. + Pokaż pakiety z błędami. + + + Warning + Ostrzeżenia + + + Show warning packets. + Pokaż pakiety z ostrzeżeniami. + + + Note + Notki + + + Show note packets. + Pokaż pakiety z notkami. + + + Chat + Czaty + + + Show chat packets. + Pokaż pakiety z czatami. + + + Comment + Komentarze + + + Show comment packets. + Pokaż pakiety z komentarzami. + + + Expert Information + Informacja ekspercka + + + Collapse All + Zwiń wszystko + + + Expand All + Rozwiń wszystko + + + Capture file closed. + Plik przechwytywania został zamknięty. + + + No display filter + Brak filtru wyświetlania. + + + No display filter set. + Brak zbioru filtrów wyświetlania. + + + Limit information to "%1". + Ogranicz informacje do "%1". + + + Display filter: "%1" + Filtr wyświetlania: "%1" + + + + ExpertInfoProxyModel + + Packet + Pakiet + + + Severity + Istotność + + + Summary + Podsumowanie + + + Group + Grupa + + + Protocol + Protokół + + + Count + Wystąpienia + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Eksportuj prezentację pakietów + + + Export As: + Export as: + Eksportuj jako: + + + Plain text (*.txt) + Tekst (*.txt) + + + Comma Separated Values - summary (*.csv) + Wartości rozdzielane przecinkami - podsumowanie (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML - podsumowanie (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PDML - szczegóły (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + Tablica w języku C - bajty (*.c, *.h) + + + + ExportObjectDialog + + Dialog + Okno + + + Content Type: + + + + Searching for objects + Szukanie obiektów + + + Text Filter: + + + + Only display entries containing this string + + + + Preview + + + + All Content-Types + + + + Export + Eksportuj + + + %1 object list + %1 lista obiektów + + + Save Object As… + + + + Save All Objects In… + + + + + ExportObjectModel + + Packet + Pakiet + + + Hostname + Nazwa hosta + + + Content Type + Typ zawartości + + + Size + Rozmiar + + + Filename + Nazwa pliku + + + + ExportPDUDialog + + Dialog + Okno + + + Display filter: + Filtr: + + + + ExtArgSelector + + Reload data + + + + + ExtcapArgumentFileSelection + + Clear + + + + All Files ( + Wszystkie pliki ( + + + Open File + Otwórz plik + + + Select File + + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + Opcje interfejsów + + + Start + Start + + + Save + + + + Default + Domyślnie + + + Restore default value of the item + + + + Extcap Help cannot be found + Nie odnaleziono pomocy dla modułu Extcap + + + The help for the extcap interface %1 cannot be found. Given file: %2 + Nie znaleziono pomocy dla interfejsu extcap %1. Plik: %2 + + + Save parameter(s) on capture start + + + + + FieldFilterEdit + + Display filter entry + Wpis filtru wyświetlania + + + Enter a field %1 + Wpisz pole %1 + + + Invalid filter: + Błędny filtr: + + + + FileSetDialog + + Dialog + Okno + + + Directory: + Katalog: + + + No files in Set + Brak plików w zbiorze + + + No capture loaded + Nie załadowano pliku przechwytywania + + + %Ln File(s) in Set + %1 File%2 in Set + + + + + + + + + FilesetEntryModel + + Open this capture file + Otwórz plik przechwytywania + + + Filename + Nazwa pliku + + + Created + Utworzono + + + Modified + Zmodyfikowano + + + Size + Rozmiar + + + + FilterAction + + Selected + Wybrany + + + Not Selected + Nie wybrany + + + …and Selected + + + + …or Selected + + + + …and not Selected + + + + …or not Selected + + + + + FilterDialog + + Dialog + Okno + + + Create a new filter. + Stwórz nowy filtr. + + + Remove this filter. + Remove this profile. + Usuń filtr. + + + Copy this filter. + Copy this profile. + Kopuj filtr. + + + Capture Filters + Filtry przechwytywania + + + Display Filters + Filtry wyświetlania + + + Open + Otwórz + + + New capture filter + This text is automatically filled in when a new filter is created + Nowy filtr przechwytywania + + + New display filter + This text is automatically filled in when a new filter is created + Nowy filtr wyświetlania + + + + FilterExpressionFrame + + Frame + Ramka + + + Filter Buttons Preferences… + + + + Label: + Opis: + + + Enter a description for the filter button + + + + Filter: + Filtr: + + + Enter a filter expression to be applied + + + + Comment: + + + + Enter a comment for the filter button + + + + Missing label. + + + + Missing filter expression. + + + + Invalid filter expression. + + + + + FilterExpressionToolBar + + Filter Button Preferences... + + + + Edit + + + + Disable + + + + Remove + + + + + FilterListModel + + Filter Name + + + + Filter Expression + + + + + FindLineEdit + + Textual Find + + + + Regular Expression Find + + + + + FirewallRulesDialog + + Create rules for + Stwórz reguły dla + + + Inbound + Przychodzące + + + Deny + Odmów + + + Firewall ACL Rules + Reguły ACL zapory ogniowej + + + Copy + Kopiuj + + + IPv4 source address. + Adres źródłowy IPv4. + + + IPv4 destination address. + Adres docelowy IPv4. + + + Source port. + Port źródłowy. + + + Destination port. + Port docelowy. + + + IPv4 source address and port. + Adres i port źródłowy IPv4. + + + IPv4 destination address and port. + Adres i port docelowy IPv4. + + + MAC source address. + Adres źródłowy MAC + + + MAC destination address. + Adres docelowy MAC + + + Text file (*.txt);;All Files ( + Plik tekstowy (*.txt);;Dowolny plik ( + + + Warning + Ostrzeżenie + + + Unable to save %1 + Nie można zapisać %1 + + + + FolderListModel + + "File" dialogs + + + + capture files + + + + Temp + Temp + + + untitled capture files + + + + Personal configuration + + + + Global configuration + + + + dfilters, preferences, ethers, … + + + + dfilters, preferences, manuf, … + + + + System + + + + ethers, ipxnets + + + + Program + + + + program files + + + + Personal Plugins + + + + binary plugins + + + + Global Plugins + + + + Personal Lua Plugins + + + + Global Lua Plugins + + + + Lua scripts + + + + Personal Extcap path + + + + external capture (extcap) plugins + + + + Global Extcap path + + + + MaxMind DB path + + + + MaxMind DB database search path + + + + MIB/PIB path + + + + SMI MIB/PIB search path + + + + macOS Extras + + + + Extra macOS packages + + + + Name + + + + Location + + + + Typical Files + + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + Odfiltruj ten strumień + + + Print + Drukuj + + + ASCII + ASCII + + + C Arrays + Tablica w języku C + + + EBCDIC + EBCDIC + + + Hex Dump + Szesnastkowo + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + Dane surowe + + + Save as… + Zapisz jako… + + + Back + Wróć + + + Packet %1. + Pakiet %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + + + + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + + + + + + + %Ln turn(s). + + + + + + + + Click to select. + Kliknij by wybrać. + + + Regex Find: + Regex znajdź: + + + No capture file. + Brak pliku przechwytywania. + + + Please make sure you have a capture file opened. + Proszę sprawdzić czy masz otwarty plik przechwytywania. + + + Error following stream. + Błąd śledzenia strumienia. + + + Capture file invalid. + Nieprawidłowy plik przechwytywania. + + + Please make sure you have a %1 packet selected. + Proszę sprawdzić czy zaznaczono pakiet %1. + + + %1 stream not found on the selected packet. + + + + Entire conversation (%1) + + + + Follow %1 Stream (%2) + Śledź strumień %1 (%2) + + + Error creating filter for this stream. + Błąd tworzenia filtru dla tego strumienia. + + + Save Stream Content As… + + + + [Stream output truncated] + + + + %Ln total stream(s). + + + + + + + + Max sub stream ID for the selected stream: %Ln + + + + + + + + File closed. + Plik zamknięty. + + + Follow Stream + Podążaj za strumieniem + + + Hint. + Podpowiedź. + + + Show data as + Show and save data as + + + + Stream + Strumień + + + Substream + + + + Find: + Znajdź: + + + Find &Next + Znajdź &następny + + + + FontColorPreferencesFrame + + Frame + Ramka + + + Main window font: + Główna czcionka: + + + Select Font + Wybierz czcionkę + + + Colors: + Kolory: + + + System Default + + + + Solid + + + + Sample ignored packet text + Przykładowy ignorowany pakiet + + + Sample marked packet text + Przykładowy zaznaczony pakiet + + + Sample active selected item + + + + Style: + + + + Gradient + + + + Sample inactive selected item + + + + Sample "Follow Stream" client text + Przykładowy "Podążaj za strumieniem" pakiet klienta + + + Sample "Follow Stream" server text + Przykładowy "Podążaj za strumieniem" pakiet serwera + + + Sample valid filter + Przykładowy prawidłowy filtr + + + Sample invalid filter + Przykładowy nieprawidłowy filtr + + + Sample warning filter + Sample deprecated filter + + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + + + + Lazy badgers move unique waxy jellyfish packets + + + + Font + Czcionka + + + + FunnelStringDialog + + Dialog + Okno + + + + FunnelTextDialog + + Dialog + Okno + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>Wpisz dowolny tekst lub wyrażenie regularne, a będzie podświetlone powyżej.</p></body></html> + + + Highlight: + Podświetlenie: + + + + GsmMapSummaryDialog + + Dialog + Okno + + + GSM MAP Summary + Podsumowanie GSM MAP + + + File + Plik + + + Name + Nazwa + + + Length + Długość + + + Format + Format + + + Snapshot length + Długość wycinka + + + Data + Dane + + + First packet + Pierwszy pakiet + + + Last packet + Ostatni pakiet + + + Elapsed + Minęło + + + Packets + Pakiety + + + Invokes + Wywołania + + + Total number of Invokes + Liczba wszystkich wywołań + + + Average number of Invokes per second + Średnia liczba wywołań na sekundę + + + Total number of bytes for Invokes + Całkowita liczba bajtów dla wywołań + + + Average number of bytes per Invoke + Średnia liczba bajtów dla wywołania + + + Return Results + Zwrócone wyniki + + + Total number of Return Results + Całkowita liczba zwróconych wyników + + + Average number of Return Results per second + Średnia liczba zwróconych wyników na sekundę + + + Total number of bytes for Return Results + Całkowita liczba bajtów zwróconych wyników + + + Average number of bytes per Return Result + Średnia liczba bajtów na zwrócony wynik + + + Totals + Całkowicie + + + Total number of GSM MAP messages + Całkowita liczba wiadomości GSM MAP + + + Average number of GSM MAP messages per second + Średnia liczba wiadomości GSM MAP na sekundę + + + Total number of bytes for GSM MAP messages + Całkowita liczba bajtów w wiadomościach GSM MAP + + + Average number of bytes per GSM MAP message + Średnia liczba bajtów w wiadomości GSM MAP + + + + IOConsoleDialog + + Dialog + Okno + + + Enter code + + + + Evaluate + + + + Clear + + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + Okno + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Przydatne skróty klawiaturowe</h3> +<table><tbody> + +<tr><th>+</th><td>Powiększ</td></tr> +<tr><th>-</th><td>Pomniejsz</td></tr> +<tr><th>0</th><td>Resetuj wykres do stanu początkowego</td></tr> +<tr><th>→</th><td>Przesuń w prawo o 10 pikseli</td></tr> +<tr><th>←</th><td>Przesuń w lewo o 10 pikseli</td></tr> +<tr><th>↑</th><td>Przesuń w górę o 10 pikseli</td></tr> +<tr><th>↓</th><td>Move down 10 pixels</td></tr> +<tr><th><i>Shift+</i>→</th><td>Przesuń w prawo o 1 piksel</td></tr> +<tr><th><i>Shift+</i>←</th><td>Przesuń w lewo o 1 piksel</td></tr> +<tr><th><i>Shift+</i>↑</th><td>Przesuń w górę o 1 piksel</td></tr> +<tr><th><i>Shift+</i>↓</th><td>Przesuń w dół o 1 piksel</td></tr> +<tr><th>g</th><td>Idź do pakietu pod kursorem</td></tr> +<tr><th>z</th><td>Przełącz mysz pomiędzy funkcją przeciągnięcia a powiększenia/pomniejszenia</td></tr> +<tr><th>t</th><td>Przełącz między czasem przechwytywania a sesji</td></tr> +<tr><th>Space</th><td>Przełącz wskaźniki wykresu</td></tr> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + Usuń ten wykres. + + + Add a new graph. + Utwórz nowy wykres. + + + Duplicate this graph. + Duplikuj ten wykres. + + + Clear all graphs. + + + + Move this graph upwards. + + + + Move this graph downwards. + + + + Mouse + Mysz + + + Drag using the mouse button. + Przeciągnij przy użyciu myszy. + + + drags + przesuwanie + + + Select using the mouse button. + Wybieranie przy pomocy myszy. + + + zooms + powiększanie + + + Interval + Interwał + + + Time of day + Czas dnia + + + Log scale + Skala logarytmiczna + + + Automatic update + + + + Enable legend + + + + Reset + Reset + + + Reset Graph + Resetuj wykres + + + Reset the graph to its initial state. + Resetuj wykres do stanu początkowego. + + + 0 + 0 + + + Zoom In + Powiększ + + + + + + + + + Zoom Out + Pomniejsz + + + - + - + + + Move Up 10 Pixels + Przesuń w górę o 10 pikseli + + + Up + W górę + + + Move Left 10 Pixels + Przesuń w lewo o 10 pikseli + + + Left + W lewo + + + Move Right 10 Pixels + Przesuń w prawo o 10 pikseli + + + Right + W prawo + + + Move Down 10 Pixels + Przesuń w dół o 10 pikseli + + + Down + W dół + + + Move Up 1 Pixel + Przesuń w górę o 1 piksel + + + Shift+Up + Shift+w górę + + + Move Left 1 Pixel + Przesuń w lewo o 1 piksel + + + Shift+Left + Shift+w lewo + + + Move Right 1 Pixel + Przesuń w prawo o 1 piksel + + + Shift+Right + Shift+w prawo + + + Move Down 1 Pixel + Przesuń w dół o 1 piksel + + + Move down 1 Pixel + Move down 1 pixel + Przesuń w dół o 1 piksel + + + Shift+Down + Shift+w dół + + + Go To Packet Under Cursor + Idź do pakietu pod kursorem + + + Go to packet currently under the cursor + Idź do pakietu pod kursorem + + + G + G + + + Drag / Zoom + Przeciągnij / Powiększ/pomniejsz + + + Toggle mouse drag / zoom behavior + Przełącz mysz pomiędzy funkcją przeciągnięcia a powiększenia/pomniejszenia + + + Z + Z + + + Capture / Session Time Origin + Czas sesji / przechwytywania + + + Toggle capture / session time origin + Przełącz między czasem przechwytywania a sesji + + + T + T + + + Crosshairs + Wskaźniki + + + Toggle crosshairs + Przełącz wskaźnik wykresu + + + Space + Spacja + + + Zoom In X Axis + Zwiększ zakres osi X + + + X + X + + + Zoom Out X Axis + Zmniejsz zakres osi X + + + Shift+X + Shift+X + + + Zoom In Y Axis + Zwiększ zakres osi Y + + + Y + Y + + + Zoom Out Y Axis + Zmniejsz zakres osi Y + + + Shift+Y + Shift+Y + + + 1 sec + 1 s + + + 10 sec + 10 s + + + 1 min + 1 min + + + 10 min + 10 min + + + Time (s) + Czas (s) + + + I/O Graphs + + + + Save As… + + + + Copy + Kopiuj + + + Copy graphs from another profile. + + + + 1 ms + 1 ms + + + 2 ms + 100 ms {2 ?} + + + 5 ms + 100 ms {5 ?} + + + 10 ms + 10 ms + + + 20 ms + 100 ms {20 ?} + + + 50 ms + 100 ms {50 ?} + + + 100 ms + 100 ms + + + 200 ms + 100 ms {200 ?} + + + 500 ms + 100 ms {500 ?} + + + 2 sec + 10 s {2 ?} + + + 5 sec + 10 s {5 ?} + + + Wireshark I/O Graphs: %1 + + + + Filtered packets + + + + Filtered events + + + + All Packets + + + + TCP Errors + + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + Przesuń kursor nad wykresem by zobaczyć szczegóły. + + + No packets in interval + Brak pakietów w zadanym czasie + + + No events in interval + + + + Click to select packet + Kliknij by wybrać pakiet + + + Packet + Pakiet + + + Click to select event + + + + Event + Event + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Powiększenie, x = %1 do %2, y = %3 do %4 + + + Unable to select range. + Niemożna wybrać danego zakresu. + + + Click to select a portion of the graph. + Kliknij by wybrać obszar wykresu. + + + Portable Document Format (*.pdf) + Dokument PDF (*.pdf) + + + Portable Network Graphics (*.png) + Obraz PNG (*.png) + + + Windows Bitmap (*.bmp) + Bitmapa Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Obraz JPEG (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + Wartości rozdzielane przecinkami (*.csv) + + + Save Graph As… + + + + + Iax2AnalysisDialog + + Dialog + Okno + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Przewodni</span></p><p><span style=" font-size:medium; font-weight:600;">Powrotny</span></p></body></html> + + + Forward + Przewodni + + + Packet + Pakiet + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter (ms) + + + Bandwidth + Pasmo + + + Status + Status + + + Length + Długość + + + Reverse + Powrotny + + + Graph + Wykres + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>Pokaż lub ukryj wartości jitter strumienia przewodniego.</p></body></html> + + + Forward Jitter + Jitter przewodniego + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>Pokaż lub ukryj wartości różnic strumienia przewodniego.</p></body></html> + + + Forward Difference + Różnica przewodniego + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>Pokaż lub ukryj wartości jitter strumienia powrotnego.</p></body></html> + + + Reverse Jitter + Jitter powrotnego + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>Pokaż lub ukryj wartości różnic strumienia powrotnego.</p></body></html> + + + Reverse Difference + Różnica powrotnego + + + <small><i>A hint.</i></small> + <small><i>Podpowiedź.</i></small> + + + Audio + Audio + + + Save the audio data for both channels. + Zapisz dane audio obu kanałów. + + + Forward Stream Audio + Przewodni strumień audio + + + Save the forward stream audio data. + Zapisz dane przewodniego strumienia audio. + + + Reverse Stream Audio + Powrotny strumień audio + + + Save the reverse stream audio data. + Zapisz dane powrotnego strumienia audio. + + + CSV + CSV + + + Save both tables as CSV. + Zapisz obie tabele jako CSV. + + + Forward Stream CSV + Strumień przewodni CSV + + + Save the forward table as CSV. + Zapisz tabelę strumienia przewodniego jako CSV. + + + Reverse Stream CSV + Strumień powrotny CSV + + + Save the reverse table as CSV. + Zapisz tabelę strumienia powrotnego jako CSV. + + + Save Graph + Zapisz wykres + + + Save the graph image. + Zapisz obraz wykresu. + + + Go to Packet + Idź do pakietu + + + Select the corresponding packet in the packet list. + Wybierz odpowiedni pakiet z listy pakietów. + + + G + G + + + Next Problem Packet + Następny problematyczny pakiet + + + Go to the next problem packet + Idź do następnego pakietu z problemem + + + N + N + + + IAX2 Stream Analysis + Analiza strumienia IAX2 + + + Unable to save RTP data. + Nie można zapisać danych RTP. + + + Please select an IAX2 packet. + + + + G: Go to packet, N: Next problem packet + G: Idź do pakietu, N: Następny problematyczny pakiet + + + Portable Document Format (*.pdf) + Dokument PDF (*.pdf) + + + Portable Network Graphics (*.png) + Obraz PNG (*.png) + + + Windows Bitmap (*.bmp) + Bitmapa Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Obraz JPEG (*.jpeg *.jpg) + + + Save Graph As… + + + + Can't save in a file: Wrong length of captured packets. + Nie można zapisać pliku: Niepoprawna długość przechwyconych pakietów. + + + Can't save in a file: File I/O problem. + Nie można zapisać pliku: Problem wejścia/wyjścia. + + + Save forward stream audio + Zapisz przewodni strumień audio + + + Save reverse stream audio + Zapisz powrotny strumień audio + + + Save audio + Zapisz audio + + + Sun Audio (*.au) + Sun Audio (*.au) + + + ;;Raw (*.raw) + ;;Surowe dane (*.raw) + + + Warning + Ostrzeżenie + + + Unable to save in that format + Nie można zapisać w tym formacie + + + Unable to save %1 + Nie można zapisać %1 + + + Saving %1… + + + + Analyzing IAX2 + + + + Save forward stream CSV + Zapisz przewodni strumień jako CSV + + + Save reverse stream CSV + Zapisz powrotny strumień jako CSV + + + Save CSV + Zapisz jako CSV + + + Comma-separated values (*.csv) + Wartości rozdzielane przecinkami (*.csv) + + + + ImportTextDialog + + File: + Plik: + + + Set name of text file to import + Podaj nazwę tekstowego pliku do zaimportowania + + + Browse for text file to import + Przeglądaj pliki tekstowe do zaimportowania + + + Browse… + Browse... + Przeglądaj… + + + Hex Dump + Szesnastkowo + + + Import a standard hex dump as exported by Wireshark + + + + Offsets in the text file are in octal notation + Przesunięcia w pliku tekstowym w notacji ósemkowej + + + Octal + Ósemkowo + + + Offsets: + Przesunięcia: + + + Offsets in the text file are in hexadecimal notation + Przesunięcia w pliku tekstowym w notacji szesnastkowej + + + Hexadecimal + Szesnastkowo + + + Offsets in the text file are in decimal notation + Przesunięcia w pliku tekstowym w notacji dziesiętnej + + + Decimal + Dziesiętnie + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + + + + ASCII identification: + + + + Regular Expression + Wyrażenie regularne + + + Import a file formatted according to a custom regular expression + + + + Packet format regular expression + + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + + + + This is regexHintLabel, it will be set to default_regex_hint + + + + Data encoding: + + + + How data is encoded + + + + encodingRegexExample + + + + List of characters indicating incoming packets + + + + iI< + + + + List of characters indicating outgoing packets + + + + oO> + + + + Timestamp format: + Format czasu: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Czy plik zawiera informacje wskazujące kierunek przepływu pakietu (przychodzący lub wychodzący). + + + Direction indication: + Wskaźnik kierunku: + + + ExportPDU + + + + IP version: + + + + Interface name: + + + + The name of the interface to write to the import capture file + + + + Fake IF, Import from Hex Dump + + + + Maximum frame length: + Maksymalny rozmiar ramki: + + + Encapsulation + Enkapsulacja + + + The text file has no offset + Plik tekstowy nie zawiera danych przesunięcia + + + None + Brak + + + <small><i>recommended regex:</small></i> + + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + + + + %H:%M:%S.%f + + + + timestampExampleLabel + + + + Encapsulation Type: + Typ enkapsulacji: + + + Encapsulation type of the frames in the import capture file + Typ enkapsulacji ramek w importowanym pliku + + + Prefix each frame with an Ethernet and IP header + + + + IP + + + + Prefix each frame with an Ethernet, IP and UDP header + + + + Prefix each frame with an Ethernet, IP and TCP header + + + + Prefix each frame with an Ethernet, IP and SCTP header + + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + + + + Source address: + + + + Destination address: + + + + Dissector + + + + The IP protocol ID for each frame + + + + The IP source address for each frame + + + + The IP destination address for each frame + + + + The UDP, TCP or SCTP source port for each frame + Port źródłowy UDP, TCP lub SCTP dla każdej ramce + + + The SCTP DATA payload protocol identifier for each frame + Identyfiktor protokołu SCTP (dane) dla każdej ramce + + + The UDP, TCP or SCTP destination port for each frame + Port docelowy UDP, TCP lub SCTP dla każdej ramce + + + Prefix each frame with an Ethernet header + Poprzedź każdą ramkę nagłówkiem Ethernet + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + Protokół (dec): + + + Leave frames unchanged + Pozostaw ramki niezmienione + + + No dummy header + Brak pustego nagłówka + + + Tag: + Tag: + + + UDP + UDP + + + Source port: + Port źródłowy: + + + The Ethertype value of each frame + Ethertype w każdej ramkce + + + TCP + TCP + + + The SCTP verification tag for each frame + Tag weryfikacyjny SCTP w każdej ramce + + + Destination port: + Port docelowy: + + + Ethertype (hex): + Ethertype (hex): + + + SCTP (Data) + SCTP (dane) + + + The dissector to use for each frame + + + + The IP Version to use for the dummy IP header + + + + The maximum size of the frames to write to the import capture file (max 256kiB) + + + + Supported fields are data, dir, time, seqno + + + + Missing capturing group data (use (? + + + + Import From Hex Dump + Zaimportuj HexDump + + + Import + Importuj + + + Import Text File + Importuj plik tekstowy + + + + InterfaceFrame + + Frame + Ramka + + + Wired + + + + AirPCAP + + + + Pipe + + + + STDIN + + + + Bluetooth + + + + Wireless + + + + Dial-Up + + + + USB + + + + External Capture + + + + Virtual + + + + Remote interfaces + + + + Show hidden interfaces + + + + External capture interfaces disabled. + + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + + + + You don't have permission to capture on local interfaces. + + + + No interfaces found. + + + + Interfaces not loaded (due to preference). Go to Capture + + + + Start capture + + + + Hide Interface + + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + + + + + InterfaceToolbar + + Frame + Ramka + + + Select interface + + + + Interface + Interfejs + + + + InterfaceToolbarLineEdit + + Apply changes + Zastosuj zmiany + + + + InterfaceTreeModel + + Show + + + + Friendly Name + + + + Interface Name + + + + No interfaces found. + + + + This version of Wireshark was built without packet capture support. + + + + Local Pipe Path + + + + Comment + + + + Link-Layer Header + + + + Promiscuous + + + + Snaplen (B) + + + + Buffer (MB) + + + + Monitor Mode + + + + Capture Filter + + + + Addresses + + + + Address + Adres + + + Extcap interface: %1 + + + + No addresses + + + + No capture filter + + + + Capture filter + Filtr przechwytywania + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + + + + Sources + Źródła + + + Address/Transport + Adres/Transport + + + Data frames + Ramki danych + + + Data bytes + Bajty danych + + + Data frames/bytes + Ramki/bajty danych + + + Data rate + Transfer danych + + + RX data frames + Ramki danych RX + + + RX data bytes + Bajty danych RX + + + RX data frames/bytes + Ramki/bajty danych RX + + + RX data rate + Transfer danych RX + + + NCF frames + Ramki NCF + + + NCF count + Wystąpień NCF + + + NCF bytes + Bajty NCF + + + NCF frames/bytes + Ramki/bajty NCF + + + NCF count/bytes + Wystąpienia/bajty NCF + + + NCF frames/count + Ramki/wystąpienia NCF + + + NCF frames/count/bytes + Ramki/wystąpienia/bajty NCF + + + NCF rate + Transfer NCF + + + SM frames + Ramki SM + + + SM bytes + Bajty SM + + + SM frames/bytes + Ramki/bajty SM + + + SM rate + Transfer SM + + + Show + Pokaż numery sekwencyjne dla transportu + + + Data + Dane + + + RX Data + Dane RX + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + numery sekwencyjne dla transportu + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Wystąpień + + + Frame + Ramka + + + SQN/Reason + SQN/Powód + + + Receivers + Odbiorcy + + + NAK frames + Ramki NAK + + + NAK count + Wystąpienia NAK + + + NAK bytes + Bajty NAK + + + NAK rate + Transfer NAK + + + NAK sequence numbers for transport + Numery sekwencyjne NAK dla transportu + + + Display filter: + Filtr: + + + Regenerate statistics using this display filter + Wygeneruj statystyki używając filtru + + + Apply + Zastosuj + + + Copy as CSV + Kopiuj jako CSV + + + Copy the tree as CSV + Kopiuj drzewo jako CSV + + + Copy as YAML + Kopiuj jako YAML + + + Copy the tree as YAML + Kopiuj drzewo jako YAML + + + Show the data frames column + Pokaż kolumnę Ramki + + + Show the data bytes column + Pokaż kolumnę Bajty danych + + + Show the data frames/bytes column + Pokaż kolumnę Ramki/Bajty danych + + + Show the RX data frames column + Pokaż kolumnę Ramki RX + + + Show the RX data bytes column + Pokaż kolumnę Bajty danych RX + + + Show the RX data frames/bytes column + Pokaż kolumnę Ramki/bajty danych RX + + + Show the NCF frames column + Pokaż kolumnę Ramki NCF + + + Show the NCF bytes column + Pokaż kolumnę Bajty NCF + + + Show the NCF count column + Pokaż kolumnę Wystąpienia NCF + + + Show the data rate column + Pokaż kolumnę Transfer danych + + + Show the RX data rate column + Pokaż kolumnę Transfer danych RX + + + Show the NCF frames/bytes column + Pokaż kolumnę Ramki/Bajty NCF + + + Show the NCF count/bytes column + Pokaż kolumnę Wystąpienia/bajty NCF + + + Show the NCF frames/count column + Pokaż kolumnę Ramki/wystąpiania NCF + + + Show the NCF frames/count/bytes column + Pokaż kolumnę Ramki/wystąpienia/bajty NCF + + + Show the NCF rate column + Pokaż kolumnę Transfer NCF + + + Show the SM frames column + Pokaż kolumnę Ramki SM + + + Show the SM bytes column + Pokaż kolumnę Bajty SM + + + Show the SM frames/bytes column + Pokaż kolumnę Ramki/bajty SM + + + Show the SM rate column + Pokaż kolumnę Transfer SM + + + Auto-resize columns to content + Automatycznie dopasuj szerokość kolumn do zawartości + + + Resize columns to content size + Dopasuj szerokość kolumn do zawartości + + + LBT-RM Statistics failed to attach to tap + Nie mogą podłączyć statystyk LBT-RM do kanału komunikacyjnego + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + + + + Sources + Źródła + + + Address/Transport/Client + Adres/Transport/Klient + + + Data frames + Ramki danych + + + Data bytes + Bajty danych + + + Data frames/bytes + Ramki/bajty danych + + + Data rate + Transfer danych + + + RX data frames + Ramki danych RX + + + RX data bytes + Bajty danych RX + + + RX data frames/bytes + Ramki/bajty danych RX + + + RX data rate + Transfer danych RX + + + NCF frames + Ramki NCF + + + NCF count + Wystąpień NCF + + + NCF bytes + Bajty NCF + + + NCF frames/count + Ramki/wystąpienia NCF + + + NCF frames/bytes + Ramki/bajty NCF + + + NCF count/bytes + Wystąpienia/bajty NCF + + + NCF frames/count/bytes + Ramki/wystąpienia/bajty NCF + + + NCF rate + Transfer NCF + + + SM frames + Ramki SM + + + SM bytes + Bajty SM + + + SM frames/bytes + Ramki/bajty SM + + + SM rate + Transfer SM + + + RST frames + Ramki RST + + + RST bytes + Bajty RST + + + RST frames/bytes + Ramki/bajty RST + + + RST rate + Transfer RST + + + Show + Pokaż szczegóły dla transportu + + + Data SQN + Dane SQN + + + RX Data SQN + Dane RX SQN + + + NCF SQN + NCF SQN + + + SM SQN + SM SQN + + + RST reason + Powód RST + + + details for transport + szczegóły dla transportu + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Wystąpień + + + Frame + Ramka + + + Reason + Powód + + + SQN/Reason + SQN/Powód + + + Receivers + Odbiorcy + + + Address/Transport + Adres/Transport + + + NAK frames + Ramki NAK + + + NAK count + Wystąpienia NAK + + + NAK bytes + Bajty NAK + + + NAK frames/count + Ramki/wystąpienia NAK + + + NAK count/bytes + Wystąpienia/bajty NAK + + + NAK frames/bytes + Ramki/bajty NAK + + + NAK frames/count/bytes + Ramki/Wystąpienia/Bajty NAK + + + NAK rate + Transfer NAK + + + ACK frames + Ramki ACK + + + ACK bytes + Bajty ACK + + + ACK frames/bytes + Ramki/bajty ACK + + + ACK rate + Transfer ACK + + + CREQ frames + Ramki CREQ + + + CREQ bytes + Bajty CREQ + + + CREQ frames/bytes + Ramki/bajty CREQ + + + CREQ rate + Transfer CREQ + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + Żądanie CREQ + + + Display filter: + Filtr: + + + Regenerate statistics using this display filter + Wygeneruj statystyki używając filtru + + + Apply + Zastosuj + + + Copy as CSV + Kopiuj jako CSV + + + Copy the tree as CSV + Kopiuj drzewo jako CSV + + + Copy as YAML + Kopiuj jako YAML + + + Copy the tree as YAML + Kopiuj drzewo jako YAML + + + Show the data frames column + Pokaż kolumnę Ramki + + + Show the data bytes column + Pokaż kolumnę Bajty danych + + + Show the data frames/bytes column + Pokaż kolumnę Ramki/Bajty danych + + + Show the data rate column + Pokaż kolumnę Transfer danych + + + Show the RX data frames column + Pokaż kolumnę Ramki RX + + + Show the RX data bytes column + Pokaż kolumnę Bajty danych RX + + + Show the RX data frames/bytes column + Pokaż kolumnę Ramki/bajty danych RX + + + Show the RX data rate column + Pokaż kolumnę Transfer danych RX + + + Show the NCF frames column + Pokaż kolumnę Ramki NCF + + + Show the NCF count column + Pokaż kolumnę Wystąpienia NCF + + + Show the NCF bytes column + Pokaż kolumnę Bajty NCF + + + Show the NCF frames/bytes column + Pokaż kolumnę Ramki/Bajty NCF + + + Show the NCF count/bytes column + Pokaż kolumnę Wystąpienia/bajty NCF + + + Show the NCF frames/count column + Pokaż kolumnę Ramki/wystąpiania NCF + + + Show the NCF frames/count/bytes column + Pokaż kolumnę Ramki/Wystąpienia/Bajty NCF + + + Show the SM frames column + Pokaż kolumnę Ramki SM + + + Show the SM bytes column + Pokaż kolumnę Bajty SM + + + Show the SM frames/bytes column + Pokaż kolumnę Ramki/bajty SM + + + Show the SM rate column + Pokaż kolumnę Transfer SM + + + Show the RST frames column + Pokaż kolumnę Ramki RST + + + Show the RST bytes column + Pokaż kolumnę Bajty RST + + + Show the RST frames/bytes column + Pokaż kolumnę Ramki/Bajty RST + + + Show the RST rate column + Pokaż kolumnę Transfer RST + + + Show the NAK frames column + Pokaż kolumnę Ramki NAK + + + Show the NAK count column + Pokaż kolumnę Wystąpienia NAK + + + Show the NAK bytes column + Pokaż kolumnę Bajty NAK + + + Show the NAK frames/count column + Pokaż kolumnę Ramki/wystąpiania NAK + + + Show the NAK count/bytes column + Pokaż kolumnę Wystąpienia/bajty NAK + + + Show the NAK frames/bytes column + Pokaż kolumnę Ramki/Bajty NAK + + + Show the NAK frames/count/bytes column + Pokaż kolumnę Ramki/wystąpienia/bajty NAK + + + Show the NAK rate column + Pokaż kolumnę Transfer NAK + + + Show the ACK frames column + Pokaż kolumnę Ramki ACK + + + Show the ACK bytes column + Pokaż kolumnę Bajty ACK + + + Show the ACK frames/bytes column + Pokaż kolumnę Ramki/bajty ACK + + + Show the ACK rate column + Pokaż kolumnę Transfer ACK + + + Show the CREQ frames column + Pokaż kolumnę Ramki CREQ + + + Show the CREQ bytes column + Pokaż kolumnę Bajty CREQ + + + Show the CREQ frames/bytes column + Pokaż kolumnę Ramki/bajty CREQ + + + Show the CREQ rate column + Pokaż kolumnę Transfer CREQ + + + Auto-resize columns to content + Automatycznie dopasuj szerokość kolumn do zawartości + + + Resize columns to content size + Dopasuj szerokość kolumn do zawartości + + + Show the NCF rate column + Pokaż kolumnę Transfer NCF + + + LBT-RU Statistics failed to attach to tap + Nie mogą podłączyć statystyk LBT-RU do kanału komunikacyjnego + + + + LBMStreamDialog + + Dialog + Okno + + + Stream + Strumień + + + Endpoint A + Punkt krańcowy A + + + Endpoint B + Punkt krańcowy B + + + Messages + Komunikaty + + + Bytes + Bajtów + + + First Frame + Pierwszy pakiet + + + Last Frame + Pierwszy pakiet + + + Display filter: + Filtr: + + + Regenerate statistics using this display filter + Wygeneruj statystyki używając filtru + + + Apply + Zastosuj + + + Copy as CSV + Kopiuj jako CSV + + + Copy the tree as CSV + Kopiuj drzewo jako CSV + + + Copy as YAML + Kopiuj jako YAML + + + Copy the tree as YAML + Kopiuj drzewo jako YAML + + + LBM Stream failed to attach to tap + Strumień LBM nie może się podłączyć do kanału komunikacyjnego + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + + LayoutPreferencesFrame + + Frame + Ramka + + + Pane 1: + Komponent 1: + + + Packet List + Lista pakietów + + + Packet Details + Szczegóły pakietu + + + Packet Bytes + Bajty pakietu + + + Packet Diagram + + + + None + Brak + + + Pane 2: + Komponent 2: + + + Pane 3: + Komponent 3: + + + Packet List settings: + + + + Show packet separator + + + + Show column definition in column context menu + + + + Allow the list to be sorted + + + + Maximum number of cached rows (affects sorting) + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + Enable mouse-over colorization + + + + Status Bar settings: + + + + Show selected packet number + + + + Show file load time + + + + + LteMacStatisticsDialog + + LTE Mac Statistics + Statystyki LTE Mac + + + Include SR frames in filter + Zawieraj ramki SR w filtrze + + + Include RACH frames in filter + Zawieraj ramki RACH w filtrze + + + MAC Statistics + Statystyki MAC + + + + LteRlcGraphDialog + + Dialog + Okno + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Przydatne skróty klawiaturowe</h3> +<table><tbody> + +<tr><th>+</th><td>Powiększ</td></th> +<tr><th>-</th><td>Pomniejsz</td></th> +<tr><th>0</th><td>Resetuj wykres do stanu początkowego</td></th> + +<tr><th>→</th><td>Przesuń w prawo o 10 pikseli</td></th> +<tr><th>←</th><td>Przesuń w lewo o 10 pikseli</td></th> +<tr><th>↑</th><td>Przesuń w górę o 10 pikseli</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Przesuń w prawo o 1 piksel</td></th> +<tr><th><i>Shift+</i>←</th><td>Przesuń w lewo o 1 piksel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Przesuń w górę o 1 piksel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Przesuń w dół o 1 piksel</td></th> + +<tr><th>g</th><td>Idź do pakietu pod kursorem</td></th> + +<tr><th>z</th><td>Przełącz mysz pomiędzy funkcją przeciągnięcia a powiększenia/pomniejszenia</td></th> +<tr><th>t</th><td>Przełącz między czasem przechwytywania a sesji</td></th> +<tr><th>Space</th><td>Przełącz wskaźniki wykresu</td></th> + +</tbody></table> +</body></html> + + + Mouse + Mysz + + + Drag using the mouse button. + Przeciągnij przy użyciu myszy. + + + drags + przesuwanie + + + Select using the mouse button. + Wybieranie przy pomocy myszy. + + + zooms + zmiana rozmiaru + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Resetuj wykres do ustawień początkowych</p></body></html> + + + Reset + Reset + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Zmień kierunek przepływu w połączeniu.</p></body></html> + + + Switch Direction + Odwróć kierunek + + + Reset Graph + Resetuj wykres + + + Reset the graph to its initial state. + Resetuj wykres do stanu początkowego. + + + 0 + 0 + + + Zoom In + Powiększ + + + + + + + + + Zoom Out + Pomniejsz + + + - + - + + + Move Up 10 Pixels + Przesuń w górę o 10 pikseli + + + Up + W górę + + + Move Left 10 Pixels + Przesuń w lewo o 10 pikseli + + + Left + W lewo + + + Move Right 10 Pixels + Przesuń w prawo o 10 pikseli + + + Right + W prawo + + + Move Down 10 Pixels + Przesuń w dół o 10 pikseli + + + Down + W dół + + + Move Up 1 Pixel + Przesuń w górę o 1 piksel + + + Shift+Up + Shift+w górę + + + Move Left 1 Pixel + Przesuń w lewo o 1 piksel + + + Shift+Left + Shift+w lewo + + + Move Right 1 Pixel + Przesuń w prawo o 1 piksel + + + Shift+Right + Shift+w prawo + + + Move Down 1 Pixel + Przesuń w dół o 1 piksel + + + Move down 1 Pixel + Przesuń w dół o 1 piksel + + + Shift+Down + Shift+w dół + + + Drag / Zoom + Przeciągnij / Powiększ/pomniejsz + + + Toggle mouse drag / zoom behavior + Przełącz mysz pomiędzy funkcją przeciągnięcia a powiększenia/pomniejszenia + + + Z + Z + + + Crosshairs + Wskaźniki + + + Toggle crosshairs + Przełącz wskaźnik wykresu + + + Space + Spacja + + + Move Up 100 Pixels + Przesuń w górę o 100 pikseli + + + PgUp + PgUp + + + PgDown + PgDown + + + Go To Packet Under Cursor + Idź do pakietu pod kursorem + + + Go to packet currently under the cursor + Idź do pakietu pod kursorem + + + G + G + + + Zoom In X Axis + Zwiększ zakres osi X + + + X + X + + + Zoom Out Y Axis + Zmniejsz zakres osi Y + + + Shift+Y + Shift+Y + + + Zoom In Y Axis + Zwiększ zakres osi Y + + + Y + Y + + + Zoom Out X Axis + Zmniejsz zakres osi X + + + Shift+X + Shift+X + + + Switch direction (swap between UL and DL) + + + + D + D + + + Time + Czas + + + Sequence Number + Numer sekwencyjny + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + Wykres LTE RLC (UE=%1 kanał=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + Wykres LTE RLC - brak wybranego kanału + + + Save As… + + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s numer sekwencyjny %4 długość %5) + + + Click to select packet + Kliknij by wybrać pakiet + + + Packet + Pakiet + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Powiększenie, x = %1 do %2, y = %3 do %4 + + + Unable to select range. + Niemożna wybrać danego zakresu. + + + Click to select a portion of the graph. + Kliknij by wybrać obszar wykresu. + + + Portable Document Format (*.pdf) + Dokument PDF (*.pdf) + + + Portable Network Graphics (*.png) + Obraz PNG (*.png) + + + Windows Bitmap (*.bmp) + Bitmapa Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Obraz JPEG (*.jpeg *.jpg) + + + Save Graph As… + + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + Statystyki LTE RLC + + + Include SR frames in filter + Zawieraj ramki SR w filtrze + + + Include RACH frames in filter + Zawieraj ramki RACH w filtrze + + + Use RLC frames only from MAC frames + Używaj ramek RLC tylko z ramek MAC + + + UL Frames + UL Ramki + + + UL Bytes + UL Bajty + + + UL MB/s + UL MiB/s + + + UL ACKs + UL ACK'i + + + UL NACKs + UL NACK'i + + + UL Missing + Brakujące UL + + + DL Frames + DL Ramki + + + DL Bytes + DL Bajty + + + DL MB/s + DL MiB/s + + + DL ACKs + DL ACK'i + + + DL NACKs + DL NACK'i + + + DL Missing + Brakujące DL + + + RLC Statistics + Statystyki RLC + + + + MainStatusBar + + Ready to load or capture + Gotowy na wczytanie pliku lub przechwytywanie + + + Ready to load file + Gotowy na wczytanie pliku + + + Open the Capture File Properties dialog + Otwórz okno Ustawień Pliku Przechwytywania + + + Profile: %1 + Profil: %1 + + + Manage Profiles… + + + + New… + + + + Edit… + + + + Import + Importuj + + + Export + Eksportuj + + + Delete + Usuń + + + Switch to + Przełącz do + + + is the highest expert information level + is the highest expert info level + jest najwyższym poziomem informacji eksperckiej + + + ERROR + BŁĄD + + + WARNING + OSTRZEŻENIE + + + NOTE + NOTKA + + + CHAT + CZAT + + + No expert information + No expert info + Brak informacji eksperckiej + + + %Ln byte(s) + , %1 bytes + + + + + + + + Byte %1 + Bajt %1 + + + Bytes %1-%2 + Bajty %1-%2 + + + Selected Packet: %1 %2 + Wybrany pakiet: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + Pakietów: %1 %4 Wyświetlanych: %2 (%3%) + + + %1 Selected: %2 (%3%) + + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 Oznaczonych: %2 (%3%) + + + %1 Dropped: %2 (%3%) + %1 Porzuconych: %2 (%3%) + + + %1 Ignored: %2 (%3%) + %1 Ignorowanych: %2 (%3%) + + + %1 Comments: %2 + + + + %1 Load time: %2:%3.%4 + %1 Czas ładowania: %2:%3.%4 + + + No Packets + Brak pakietów + + + From Zip File... + + + + From Directory... + + + + Selected Personal Profile... + + + + All Personal Profiles... + + + + Packets: %1 + Pakiety: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + + MainWindowPreferencesFrame + + Frame + Ramka + + + Checking this will save the size, position, and maximized state of the main window. + Zaznacz aby zapisywać rozmiar, pozycję i stan okna głównego. + + + Remember main window size and placement + Pamiętaj rozmiar i położenie głównego okna programu + + + Open files in + Otwieraj pliki w + + + This folder: + Ten folder: + + + Browse… + Browse... + Przeglądaj… + + + The most recently used folder + Ostanio użyty folder + + + Show up to + Pokazuj aż do + + + filter entries + wpisów filtra + + + recent files + ostatnich plików + + + Confirm unsaved capture files + Potwierdź zamykanie niezapisanych plików przechwytywania + + + Display autocompletion for filter text + + + + Main toolbar style: + Główny pasek stylów: + + + Icons only + Tylko ikony + + + Text only + Tylko tekst + + + Icons & Text + Ikony i tekst + + + Window title + + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + Prepend window title + + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + Language: + Language / Język: + + + Use system setting + Używaj ustawień systemowych + + + Open Files In + Otwieraj pliki w + + + + ManageInterfacesDialog + + Manage Interfaces + Zarządzaj interfejsami + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Zaznacz pole wyboru aby ukryć lub pokazać ukryty interfejs.</p></body></html> + + + Local Interfaces + Lokalny interfejs + + + Show + Pokaż + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Dodaj lub usuń rurę z listy.</p></body></html> + + + Pipes + Rury + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Dodaj nową rurę używając domyślnych ustawień.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Usuń wybraną rurę z listy.</p></body></html> + + + Remote Interfaces + Zdalne interfejsy + + + Host / Device URL + Host / URL urządzenia + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>Dodaj zdalnego hosta i jego interfejsy.</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>Usuń wybranego hosta z listy</p></body></html> + + + Remote Settings + Zdalne ustawienia + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Ta wersja Wiresharka nie obsługuje ustawień rur. + + + This version of Wireshark does not save remote settings. + Ta wersja Wiresharka nie obsługuje zdalnych ustawień. + + + This version of Wireshark does not support remote interfaces. + Ta wersja Wiresharka nie obsługuje zdalnych interfejsów. + + + New Pipe + Nowa rura + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + + + + Copy + Kopiuj + + + Find + Znajdź + + + Clear + + + + + ManufTableModel + + Address Block + + + + Short Name + Krótka Nazwa + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + ObszarPrzeglądania + + + + Mtp3SummaryDialog + + Dialog + Okno + + + MTP3 Summary + Podsumowanie MTP3 + + + File + Plik + + + Name + Nazwa + + + Length + Długość + + + Format + Format + + + Snapshot length + Długość wycinka + + + Data + Dane + + + First packet + Pierwszy pakiet + + + Last packet + Ostatni pakiet + + + Elapsed + Minęło + + + Packets + Pakiety + + + Service Indicator (SI) Totals + Wszystkich Wskaźników Serwisu (SI) + + + SI + SI + + + MSUs + MSUs + + + MSUs/s + MSUs/s + + + Bytes + Bajty + + + Bytes/MSU + Bajty/MSU + + + Bytes/s + Bajty/s + + + Totals + Całkowicie + + + Total MSUs + Całkowicie MSU + + + Total Bytes + Wszystkich bajtów + + + Average Bytes/MSU + Średnio bajtów/MSU + + + Average Bytes/s + Średnio bajtów/s + + + + MulticastStatisticsDialog + + UDP Multicast Streams + Strumienie rozsyłania grupowego UDP + + + Source Address + Adres źródłowy + + + Source Port + Port źródłowy + + + Destination Address + Adres docelowy + + + Destination Port + Port docelowy + + + Packets + Pakiety + + + Packets/s + Pakietów/s + + + Avg BW (bps) + Średni BW (bps) + + + Max BW (bps) + Max BW (bps) + + + Max Burst + Max Impuls + + + Burst Alarms + Alarmy Impulsu + + + Max Buffers (B) + Max Bufor (B) + + + Buffer Alarms + Alarmy Bufora + + + Burst measurement interval (ms): + Interwał pomiaru impulsu (ms): + + + Burst alarm threshold (packets): + Próg alarmu impulsu (pakiety): + + + Buffer alarm threshold (B): + Próg alarmu bufora (B): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + Prędkość pustki strumienia (Kb/s): + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + Całkowita prędkość pustki (Kb/s): + + + The burst interval must be between 1 and 1000. + Interwał impulsu musi być pomiędzy 1 a 1000. + + + The burst alarm threshold isn't valid. + Próg alarmu impulsu nie jest poprawny. + + + The buffer alarm threshold isn't valid. + Próg alarmu bufora nie jest poprawny. + + + The stream empty speed should be between 1 and 10000000. + Prędkość pustki strumienia powinna być pomiędzy 1 a 10000000. + + + The total empty speed should be between 1 and 10000000. + Całkowita prędkość pustki powinna być pomiędzy 1 a 10000000. + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + %1 strumieni, średni transfer: %2bps, max transfer: %3bps, max impuls: %4 / %5ms, max bufor: %6B + + + + PacketCommentDialog + + Edit Packet Comment + + + + Add Packet Comment + + + + + PacketDiagram + + Packet diagram + + + + Show Field Values + + + + Save Diagram As… + + + + Copy as Raster Image + + + + …as SVG + + + + Portable Network Graphics (*.png) + Obraz PNG (*.png) + + + Windows Bitmap (*.bmp) + Bitmapa Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Obraz JPEG (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + + + + Save Graph As… + + + + + PacketDialog + + Dialog + Okno + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + + + + Packet %1 + Pakiet %1 + + + [%1 closed] + [%1 zamknięty] + + + Byte %1 + Bajt %1 + + + Bytes %1-%2 + Bajty %1-%2 + + + %Ln byte(s) + + + + + + + + + PacketFormatGroupBox + + GroupBox + Element grupujący + + + Packet Format + Format pakietu + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>Podsumowanie pakietów podobne do listy pakietów</p></body></html> + + + Summary line + Linia podsumowania + + + Include column headings + Dołącz nagłówek kolumny + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>Szczegóły pakietu podobne do drzewa protokołów</p></body></html> + + + Details: + Szczegóły: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>Eksportuj tylko szczegóły pakietu najwyższego poziomu</p></body></html> + + + All co&llapsed + Wszystko zwinięte + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>Rozwiń lub zmiń szczegóły pakietu które są aktualnie wyświetlane.</p></body></html> + + + As displa&yed + Jako wyświetlane + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>Eksportuj wszystkie szczegóły pakietu</p></body></html> + + + All e&xpanded + Wszystko rozwinięte + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>Eksportuj dane pakietu w postaci szesnastkowej</p></body></html> + + + Bytes + Bajty + + + Include secondary data sources + + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + + + + + PacketList + + Protocol Preferences + Ustawienia protokołów + + + Summary as Text + Podsumowanie jako tekst + + + …as CSV + + + + …as YAML + + + + Decode As… + + + + Frame %1: %2 + + + Ramka %1: %2 + + + + [ Comment text exceeds %1. Stopping. ] + [ Długość komentarza przekracza %1. Wstrzymano. ] + + + + PacketListHeader + + Align Left + + + + Align Center + + + + Align Right + + + + Edit Column + + + + Resize to Contents + + + + Column Preferences… + + + + Resize Column to Width… + + + + Resolve Names + + + + Remove this Column + + + + Column %1 + + + + Width: + + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + + + + Sorting "%1"… + + + + Sorting … + + + + + PacketRangeGroupBox + + Form + Formularz + + + Packet Range + Zakres pakietów + + + - + - + + + Displayed + Wyświetlane + + + &Marked packets only + Tylko zaznaczone pakiety + + + &Range: + Zakres: + + + Remove &ignored packets + Usuń ignorowane pakiety + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + Od pierwszego do ostatniego zaznaczonego + + + &All packets + Wszystkie pakiety + + + &Selected packets only + Tylko wybrane pakiety + + + Captured + Przechwyconych + + + + PathSelectionDelegate + + Open a pipe + + + + + PathSelectionEdit + + Browse + + + + Select a path + + + + + PluginListModel + + Name + Nazwa + + + Version + Wersja + + + Type + Typ + + + Path + Ścieżka + + + + PortsModel + + All entries + + + + tcp + + + + udp + + + + sctp + + + + dccp + + + + Name + + + + Port + + + + Type + + + + + PreferenceEditorFrame + + Frame + Ramka + + + … + + + + a preference + ustawienie + + + Browse… + Przeglądaj… + + + Open %1 preferences… + + + + Invalid value. + Niepoprawna wartość. + + + + PreferencesDialog + + Search: + Szukaj: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + Preferencje + + + + PrefsModel + + Advanced + Zaawansowane + + + Appearance + Prezentacja + + + Layout + Wygląd + + + Columns + Kolumny + + + Font and Colors + Czcionki i kolory + + + Capture + Przechwytywanie + + + Expert + Informacje eksperckie + + + Filter Buttons + Przyciski filtrowania + + + RSA Keys + + + + + PrintDialog + + Packet Format + Format pakietu + + + Print each packet on a new page + Drukuj każdy pakiet na osobnej stronie + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + + + + Capture information header + + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>Użyj klawiszy &quot;+&quot; i &quot;-&quot; by zmieniać rozmiar podglądu. Użyj klawisza &quot;0&quot; by wrócić domyślny rozmiar.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ powiększ - pomniejsze, 0 domyślny rozmiar</span></p></body></html> + + + Packet Range + Zakres pakietów + + + Print + Drukuj + + + &Print… + Drukuj… + + + Page &Setup… + + + + %1 %2 total packets, %3 shown + %1 %2 wszystkich pakietów, %3 wyświetlanych + + + Print Error + Błąd drukowania + + + Unable to print to %1. + Nie można drukować do %1. + + + + ProfileDialog + + Search for profile … + + + + Create a new profile using default settings. + Utwórz nowy profil używając ustawień domyślnych. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + + + + Copy this profile. + Kopiuj ten profil. + + + Configuration Profiles + Konfiguracja profili + + + Import + noun + Importuj + + + Export + noun + Eksportuj + + + From Zip File... + + + + From Directory... + + + + %Ln Selected Personal Profile(s)... + + + + + + + + All Personal Profiles... + + + + New profile + + + + Profile Error + Błąd w profilu + + + Exporting profiles + + + + No profiles found for export + + + + Select zip file for export + + + + An import of profiles is not allowed, while changes are pending + + + + An import is pending to be saved. Additional imports are not allowed + + + + An export of profiles is only allowed for personal profiles + + + + An export of profiles is not allowed, while changes are pending + + + + %Ln profile(s) exported + + + + + + + + Select zip file for import + + + + Select directory for import + + + + Zip File (*.zip) + + + + Error + + + + An error has occurred while exporting profiles + + + + No profiles found for import in %1 + + + + %Ln profile(s) imported + + + + + + + + , %Ln profile(s) skipped + + + + + + + + Importing profiles + + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + + ProfileModel + + Resetting to default + + + + Imported profile + + + + This is a system provided profile + + + + A profile change for this name is pending + + + + (See: %1) + + + + This is an invalid profile definition + + + + A profile already exists with this name + + + + A profile with this name is being deleted + + + + Created from default settings + + + + system provided + + + + deleted + + + + copy + noun + + + + Exporting profiles while changes are pending is not allowed + + + + No profiles found to export + + + + Can't delete profile directory + + + + A profile name cannot contain the following characters: %1 + + + + A profile name cannot contain the '/' character + + + + A profile cannot start or end with a period (.) + + + + Default + Domyślnie + + + Global + + + + Personal + + + + Renamed from: %1 + + + + Copied from: %1 + + + + renamed to %1 + + + + Profile + + + + Type + + + + + ProfileSortModel + + All profiles + + + + Personal profiles + + + + Global profiles + + + + + ProgressFrame + + Frame + Ramka + + + Loading + Wczytywanie + + + + ProtoTree + + Packet details + Szczegóły pakietu + + + Not a field or protocol + + + + No field reference available for text labels. + + + + Expand Subtrees + + + + Collapse Subtrees + + + + Expand All + Rozwiń wszystko + + + Collapse All + Zwiń wszystko + + + Copy + Kopiuj + + + All Visible Items + Wszystkie widoczne pozycje + + + All Visible Selected Tree Items + Wszystkie widoczne pozycje wybranego poddrzewa + + + Description + Opis + + + Field Name + Nazwa pola + + + Value + Wartość + + + As Filter + Jako filtr + + + Wiki Protocol Page + Strona Wiki Protokołów + + + Filter Field Reference + Odwołania filtru pola + + + Copied + + + + Wiki Page for %1 + Strona Wiki dla %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Strona Wiki Wiresharka jest zarządzania przez społeczność.</p><p>Strona którą chcesz wczytać może być wspaniała, niekompletna, błędna lub nie istnieć.</p><p>Kontynuować wczytywanie strony Wiki?</p> + + + Colorize with Filter + Koloruj z filtrem + + + + ProtocolHierarchyDialog + + Dialog + Okno + + + Protocol + Protokół + + + Percent Packets + Pakiety [%] + + + Packets + Pakiety + + + Percent Bytes + Bajty [%] + + + Bytes + Bajtów + + + Bits/s + Bity/s + + + End Packets + Krańcowych pakietów + + + End Bytes + Krańcowych bajtów + + + End Bits/s + Krańcowych bitów/s + + + PDUs + + + + <small><i>A hint.</i></small> + <small><i>Podpowiedź.</i></small> + + + Copy as CSV + Kopiuj jako CSV + + + Copy stream list as CSV. + Kopiuj listę strumieni jako CSV. + + + Copy as YAML + Kopiuj jako YAML + + + Copy stream list as YAML. + Kopiuj listę strumieni jako YAML. + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + Statystyki Hierarchi Protokołów + + + Copy + Kopiuj + + + as CSV + jako CSV + + + as YAML + jako YAML + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + Brak filtru. + + + Display filter: %1 + Filtr: %1 + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + Ustawienia protokołów + + + No protocol preferences available + Brak dostępnych ustawień protokołu + + + Disable %1 + Wyłączony %1 + + + %1 has no preferences + %1 nie ma ustawień + + + Open %1 preferences… + + + + + QObject + + Average Throughput (bits/s) + Średnia przepustowość (bit/s) + + + Round Trip Time (ms) + Czas podróży (ms) + + + Segment Length (B) + Długość Segmentu (B) + + + Sequence Number (B) + Numer Sekwencyjny (B) + + + Time (s) + Czas (s) + + + Window Size (B) + Rozmiar okna (B) + + + [no capture file] + [nie załadowano pliku przechytywania] + + + Conversation + Konwersacja + + + Bars show the relative timeline for each conversation. + Wykres pokazuje relatywną oś czasu dla każdej konwersacji. + + + Endpoint + Punkt krańcowy + + + Apply as Filter + Zastosuj filtr + + + Prepare as Filter + + + + Find + Znajdź + + + Colorize + Koloruj + + + Look Up + Wyszukaj + + + Copy + Kopiuj + + + UNKNOWN + NIEZNANY + + + Selected + Wybrany + + + Not Selected + Nie wybrany + + + …and Selected + + + + …or Selected + + + + …and not Selected + + + + …or not Selected + + + + A + A + + + B + B + + + Any + Każdy + + + Don't show this message again. + Nie pokazuj więcej tego komunikatu. + + + Multiple problems found + Wystąpiło wiele problemów + + + %1 (%L2%) + %1 (%L2%) + + + No entries. + Brak wpisów. + + + %1 entries. + %1 wpisów. + + + Base station + Stacja bazowa + + + <Broadcast> + <Rozgłoszeniowy> + + + <Hidden> + <Ukryty> + + + BSSID + BSSID + + + Beacons + Sygnalizatory + + + Data Pkts + Dane Pakietów + + + Protection + Ochrona + + + Address + Adres + + + Pkts Sent + Wysłanych Pakietów + + + Pkts Received + Odebranych Pakietów + + + Comment + Komentarz + + + Wrong sequence number + Zły numer sekwencyjny + + + Payload changed to PT=%1 + Typ danych zmieniono na PT=%1 + + + Incorrect timestamp + Niepoprawny znacznik czasu + + + Marker missing? + Brakujący znacznik? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + Typ + + + UEId + UEId + + + UL Frames + Ramki UL + + + UL Bytes + Bajty UL + + + UL MB/s + UL MiB/s + + + UL Padding % + Dopełnienie UL % + + + UL Re TX + UL Re TX + + + DL Frames + Ramki DL + + + DL Bytes + Bajty DL + + + DL MB/s + DL MiB/s + + + DL Padding % + Dopełnienie DL % + + + DL CRC Failed + Niepoprawne DL CRC + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + Predef + + + Unknown (%1) + Nieznany (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + Nienany + + + UE Id + UE Id + + + Name + Nazwa + + + Mode + Tryb + + + Priority + Priorytet + + + default + domyślny + + + DLT %1 + DLT %1 + + + Invalid Display Filter + Niepoprawny filtr wyświetlania + + + The filter expression %1 isn't a valid display filter. (%2). + Filtr %1 nie jest poprawnym filtrem wyświetlania. (%2). + + + Error + Błąd + + + No remote interfaces found. + Nie znaleziono zdalnych interfejsów. + + + PCAP not found + PCAP nie znaleziony + + + Unknown error + Nieznany błąd + + + Default + Domyślnie + + + Changed + Zmienione + + + Has this preference been changed? + Czy to ustawienie zostało zmienione? + + + Default value is empty + Domyślna wartość jest pusta + + + Gap in dissection + + + + Edit… + + + + Browse… + Przeglądaj… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + Zdalny interfejs + + + Host: + Host: + + + Port: + Port: + + + Authentication + Uwierzytelnianie + + + Null authentication + Brak uwierzytelniania + + + Password authentication + Hasło uwierzytelnienia + + + Username: + Nazwa użytkownika: + + + Password: + Hasło: + + + Clear list + Wyczyść listę + + + Error + Błąd + + + No remote interfaces found. + Nie znaleziono zdalnych interfejsów. + + + PCAP not found + PCAP nie znaleziony + + + + RemoteSettingsDialog + + Remote Capture Settings + Ustawienia zdalnego przechwytywania + + + Capture Options + Opcje przechytywania + + + Do not capture own RPCAP traffic + Nie przechwytuj własnego ruchu RPCAP + + + Use UDP for data transfer + Użyj UDP do transferu danych + + + Sampling Options + Opcje próbkowania + + + None + Brak + + + 1 of + 1 z + + + packets + pakietów + + + 1 every + każdy jeden + + + milliseconds + milisekundy + + + + ResolvedAddressesDialog + + Dialog + Okno + + + Hosts + + + + Search for entry (min 3 characters) + + + + Ports + + + + Search for port or name + + + + Capture File Comments + + + + Comment + Komentarz + + + Show the comment. + Pokaż komentarz. + + + IPv4 Hash Table + Tablica mieszająca IPv4 + + + Show the IPv4 hash table entries. + Pokaż elementy tablicy mieszającej IPv4. + + + IPv6 Hash Table + Tablica mieszająca IPv6 + + + Show the IPv6 hash table entries. + Pokaż elementy tablicy mieszającej IPv6. + + + Show All + Pokaż wszystko + + + Show all address types. + Pokaż wszystkie typy adresów + + + Hide All + Ukryj wszystko + + + Hide all address types. + Ukryj wszystkie typy adresów. + + + IPv4 and IPv6 Addresses (hosts) + Adresy IPv4 i IPv6 (gospodarze) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + Pokaż rozwiązane nazwy gospodarzy IPv4 i IPv6 w formacie "hosts". + + + Port names (services) + Nazwy portów (usługi) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + Pokaż rozwiązane nazwy portów w formacie "services". + + + Ethernet Addresses + Adresy Ethernet + + + Show resolved Ethernet addresses in "ethers" format. + Pokaż rozwiązane adresy Ethernet w formacie "ethers". + + + Ethernet Well-Known Addresses + Dobrze znane adresy Ethernet + + + Show well-known Ethernet addresses in "ethers" format. + Pokaż dobrze znane adresy Ethenetowe w formacie "ethers". + + + Ethernet Manufacturers + Producenci Ethernet + + + Show Ethernet manufacturers in "ethers" format. + Pokaż producentów Ethernet w formacie "ethers". + + + [no file] + [brak pliku] + + + Resolved Addresses + Rozwiązane Adresy + + + # Resolved addresses found in %1 + # Rozwiązane adresy znalezione w %1 + + + # Comments +# +# + # Komentarze +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + %1 statystyki czasu opóźnienia odpowiedzi + + + Type + Typ + + + Messages + Wiadomości + + + Min SRT + Min SRT + + + Max SRT + Max SRT + + + Avg SRT + Średnia SRT + + + Min in Frame + Min w ramce + + + Max in Frame + Max w ramce + + + Open Requests + Żądania bez odpowiedzi + + + Discarded Responses + Odrzucone Odpowiedzi + + + Repeated Requests + Powtórzone Żądania + + + Repeated Responses + Powtórzone Odpowiedzi + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>Wybierz program, wersję i podaj filtr jest potrzebna, następnie wciśnij Zastosuj.</i></small> + + + Version: + Wersja: + + + Program: + Program: + + + DCE-RPC Service Response Times + Czas odpowiedzi serwisu DCE-RPC + + + ONC-RPC Service Response Times + Czas odpowiedzi serwisu ONC-RPC + + + + RsaKeysFrame + + RSA Keys + + + + RSA private keys are loaded from a file or PKCS #11 token. + + + + Add new keyfile… + + + + Add new token… + + + + Remove key + + + + PKCS #11 provider libraries. + + + + Add new provider… + + + + Remove provider + + + + Add PKCS #11 token or key + + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + + + + Select a new PKCS #11 token or key + + + + PKCS #11 token or key + + + + Enter PIN or password for %1 (it will be stored unencrypted) + + + + Enter PIN or password for key + + + + Key could not be added: %1 + + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + + + + Select RSA private key file + + + + Libraries (*.dll) + + + + Libraries (*.so) + + + + Select PKCS #11 Provider Library + + + + Changes will apply after a restart + + + + PKCS #11 provider %1 will be removed after the next restart. + + + + + RtpAnalysisDialog + + Dialog + Okno + + + Packet + Pakiet + + + Sequence + Numer sekwencyjny + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter + Jitter (ms) + + + Skew + Odchylenie + + + Bandwidth + Pasmo + + + Marker + Znacznik + + + Status + Status + + + Stream %1 + + + + Stream %1 Jitter + + + + Stream %1 Difference + + + + Stream %1 Delta + + + + %1 streams, + + + + Save one stream CSV + + + + Save all stream's CSV + + + + &Analyze + Analizuj + + + Open the analysis window for the selected stream(s) + + + + &Set List + + + + &Add to List + + + + &Remove from List + + + + Replace existing list in RTP Analysis Dialog with new one + + + + Add new set to existing list in RTP Analysis Dialog + + + + Remove selected streams from list in RTP Analysis Dialog + + + + Graph + Wykres + + + <small><i>A hint.</i></small> + <small><i>Podpowiedź.</i></small> + + + &Export + + + + Open export menu + + + + CSV + CSV + + + Save tables as CSV. + + + + Current Tab Stream CSV + + + + Save the table on the current tab as CSV. + + + + All Tab Streams CSV + + + + Save the table from all tabs as CSV. + + + + Save Graph + Zapisz wykres + + + Save the graph image. + Zapisz obraz wykresu. + + + Go to Packet + Idź do pakietu + + + Select the corresponding packet in the packet list. + Zaznacz odpowiedni pakiet na liście pakietów. + + + G + G + + + Next Problem Packet + Następny problematyczny pakiet + + + Go to the next problem packet + Idź do następnego pakietu z problemem. + + + N + N + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + Przygotuj filtr dla zaznaczonych strumieni. + + + &Current Tab + + + + Prepare a filter matching current tab. + + + + &All Tabs + + + + Prepare a filter matching all tabs. + + + + RTP Stream Analysis + Analiza strumienia RTP + + + Save Graph As… + + + + G: Go to packet, N: Next problem packet + G: Idź do pakiety, N: Następny problematyczny pakiet + + + Portable Document Format (*.pdf) + Dokument PDF (*.pdf) + + + Portable Network Graphics (*.png) + Obraz PNG (*.png) + + + Windows Bitmap (*.bmp) + Bitmapa Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Obraz JPEG (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + Wartości rozdzielane przecinkami (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %1 nie obsługuje formatu PCM na %2. Preferowany format to %3 + + + + RtpPlayerDialog + + RTP Player + Odtwarzacz RTP + + + Play + + + + Source Address + Adres źródłowy + + + Source Port + Port źródłowy + + + Destination Address + Adres docelowy + + + Destination Port + Port docelowy + + + SSRC + SSRC + + + Setup Frame + Ramka konfiguracji + + + Packets + Pakiety + + + Time Span (s) + Okres czasu (s) + + + Payloads + Dane + + + <small><i>No audio</i></small> + <small><i>Brak audio</i></small> + + + Start playback of all unmuted streams + + + + Pause/unpause playback + + + + Stop playback + + + + Enable/disable skipping of silence during playback + + + + Min silence: + + + + Minimum silence duration to skip in seconds + + + + Output Device: + Urządzenie do odtwarzania + + + Output Audio Rate: + + + + Jitter Buffer: + Bufor Jitter: + + + The simulated jitter buffer in milliseconds. + Symulowany bufor jitter w milisekundach. + + + Playback Timing: + Czas strumienia: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Buforowanie Jitter</strong>: Używa buforowania jitter by symulować strumień RTP jaki byłby słyszalny przez użytkownika końcowego. +<br/> +<strong>RTP Timestamp</strong>: Używa znaczników czasu RTP zamiast czasu przyjścia pakietu. To nie spowoduje że strumień będzie taki sam jak słyszał go użytkownik końcowy, ale jest to użyteczne gdy strumień RTP jest tunelowany i czasy przyjścia pakietów zostały utracone. +<br/> +<strong>Tryb ciągły</strong>: Ignoruje wszelkie znaczniki czasu i odgrywa strumień poskładany z przychodzących pakietów. + + + Jitter Buffer + Buforowanie jitter + + + RTP Timestamp + RTP Timestamp + + + Uninterrupted Mode + Tryb ciągły + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>Pokazuj znaczniki czasu jak czas dnia (zaznaczone) lub sekundy od początku przechwytywania (niezaznaczone).</p></body></html> + + + Time of Day + Czas dnia + + + &Export + + + + Export audio of all unmuted selected channels or export payload of one channel. + + + + From &cursor + + + + Save audio data started at the cursor + + + + &Stream Synchronized Audio + + + + Save audio data synchronized to start of the earliest stream. + + + + &File Synchronized Audio + + + + Save audio data synchronized to start of the capture file. + + + + &Payload + + + + Save RTP payload of selected stream. + + + + Reset Graph + Resetuj wykres + + + Reset the graph to its initial state. + Resetuj wykres do stanu początkowego. + + + Go To Setup Packet + + + + Go to setup packet of stream currently under the cursor + + + + Mute + + + + Mute selected streams + + + + Unmute + + + + Unmute selected streams + + + + Invert muting of selected streams + + + + Route audio to left channel of selected streams + + + + Route audio to left and right channel of selected streams + + + + Route audio to right channel of selected streams + + + + Remove Streams + + + + Remove selected streams from the list + + + + All + + + + Select all + + + + None + Brak + + + Clear selection + + + + Invert + Przełącz + + + Invert selection + + + + Play/Pause + + + + Start playing or pause playing + + + + Stop + + + + Stop playing + + + + I&naudible streams + + + + Select/Deselect inaudible streams + + + + Inaudible streams + + + + &Select + + + + Select inaudible streams + + + + &Deselect + + + + Deselect inaudible streams + + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + Przygotuj filtr dla zaznaczonych strumieni. + + + R&efresh streams + + + + Read captured packets from capture in progress to player + + + + Zoom In + Powiększ + + + SR (Hz) + + + + Sample rate of codec + + + + PR (Hz) + + + + Play rate of decoded audio (depends e. g. on selected sound card) + + + + Zoom Out + Pomniejsz + + + Move Left 10 Pixels + Przesuń w lewo o 10 pikseli + + + Move Right 10 Pixels + Przesuń w prawo o 10 pikseli + + + Move Left 1 Pixels + Przesuń w lewo o 1 piksel + + + Move Right 1 Pixels + Przesuń w prawo o 1 piksel + + + Go To Packet Under Cursor + Idź do pakietu pod kursorem + + + Go to packet currently under the cursor + Idź do pakietu pod kursorem + + + Play the stream + + + + To Left + + + + Left + Right + + + + To Right + + + + Invert Muting + + + + No devices available + Brak dostępnych urządzeń + + + Select + + + + Audio Routing + + + + &Play Streams + + + + Open RTP player dialog + + + + &Set playlist + + + + Replace existing playlist in RTP Player with new one + + + + &Add to playlist + + + + Add new set to existing playlist in RTP Player + + + + &Remove from playlist + + + + Remove selected streams from playlist in RTP Player + + + + No Audio + + + + Decoding streams... + + + + Out of Sequence + Poza kolejnością + + + Jitter Drops + Porzucenia Jitter + + + Wrong Timestamps + Błędny znacznik czasu + + + Inserted Silence + Wstawiono ciszę + + + Double click on cell to change audio routing + + + + %1 streams + %1 strumieni + + + , %1 selected + + + + , %1 not muted + + + + , start: %1. Double click on graph to set start of playback. + + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + + + + Playback of stream %1 failed! + + + + Automatic + + + + WAV (*.wav) + + + + Sun Audio (*.au) + Sun Audio (*.au) + + + Save audio + Zapisz audio + + + Raw (*.raw) + + + + Save payload + + + + Warning + + + + No stream selected or none of selected streams provide audio + + + + Error + + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + + + + No streams are suitable for save + + + + Save failed! + + + + Can't write header of AU file + + + + Can't write header of WAV file + + + + Payload save works with just one audio stream. + + + + Double click to change audio routing + + + + Preparing to play... + + + + Unknown + Nieznany + + + + RtpStreamDialog + + Dialog + Okno + + + Source Address + Adres źródłowy + + + Source Port + Port źródłowy + + + Destination Address + Adres docelowy + + + Destination Port + Port docelowy + + + SSRC + SSRC + + + Start Time + Czas startu + + + Duration + Czas trwania + + + Payload + Dane + + + Packets + Pakiety + + + Lost + Utracone + + + Max Delta (ms) + Max Delta (ms) + + + Max Jitter + Max Jitter + + + Mean Jitter + Średni Jitter + + + Status + Status + + + <small><i>A hint.</i></small> + <small><i>Podpowiedź.</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Pokazuj tylko konwersacje pasujące do filtru</p></body></html> + + + Limit to display filter + + + + Time of Day + Czas dnia + + + Find &Reverse + + + + Prepare &Filter + + + + &Export + + + + &Analyze + Analizuj + + + Open the analysis window for the selected stream(s) and add it to it + + + + Find the reverse stream matching the selected forward stream. + Znajdź powrotny strumień pasujący do strumienia przekazanego. + + + Min Delta (ms) + + + + Mean Delta (ms) + + + + Min Jitter + + + + All forward/reverse stream actions + + + + R + R + + + Find All &Pairs + + + + Select all streams which are paired in forward/reverse relation + + + + Shift+R + + + + Find Only &Singles + + + + Find all streams which don't have paired reverse stream + + + + Ctrl+R + + + + Mark Packets + Zaznacz pakiety + + + Mark the packets of the selected stream(s). + Zaznacz pakiety wybranych strumieni. + + + M + M + + + All + + + + Select all + + + + None + Brak + + + Clear selection + + + + Invert + Przełącz + + + Invert selection + + + + Go To Setup + Idź do konfiguracji + + + Go to the setup packet for this stream. + Idź do pakietu konfiguracyjnego dla tego strumienia. + + + G + G + + + Prepare a filter matching the selected stream(s). + Przygotuj filtr dla zaznaczonych strumieni. + + + P + P + + + Export the stream payload as rtpdump + Eksportuj strumień jako rtpdump + + + E + E + + + A + + + + Cop&y + + + + Open copy menu + + + + Copy as CSV + Kopiuj jako CSV + + + Copy stream list as CSV. + Kopiuj listę strumieni jako CSV. + + + Copy as YAML + Kopiuj jako YAML + + + Copy stream list as YAML. + Kopiuj listę strumieni jako YAML. + + + RTP Streams + Strumienie RTP + + + Select + + + + as CSV + jako CSV + + + as YAML + jako YAML + + + %1 streams + %1 strumieni + + + , %1 selected, %2 total packets + , %1 zaznaczonych, %2 wszystkich pakietów + + + Save RTPDump As… + + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark - Asocjacje SCTP + + + ID + ID + + + Port 1 + Port 1 + + + Port 2 + Port 2 + + + Number of Packets + Liczba pakietów + + + Number of DATA Chunks + Liczba kawałków danych + + + Number of Bytes + Liczba bajtów + + + Filter Selected Association + Asocjacja wybranego filtru + + + Analyze + Analizuj + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark: Analiza asocjacji + + + TabWidget + TabWidżet + + + Statistics + Statystyki + + + Chunk Statistics + Statystyki kawałków + + + Filter Association + Filtr asocjacji + + + Number of Data Chunks from EP2 to EP1: + Liczba kawałków danych od PK2 do PK1: + + + Checksum Type: + Typ sumy kontrolnej: + + + Number of Data Chunks from EP1 to EP2: + Liczba kawałków danych od PK1 do PK2: + + + Number of Data Bytes from EP1 to EP2: + Liczba bajtów danych od PK1 do PK2: + + + Number of Data Bytes from EP2 to EP1: + Liczba bajtów danych od PK2 do PK1: + + + Endpoint 1 + Punkt krańcowy 1 + + + Graph TSN + Wykres TSN + + + Graph Bytes + Wykres bajtów + + + Requested Number of Inbound Streams: + Liczba żądanych strumieni przychodzących: + + + Port: + Port: + + + Sent Verification Tag: + Wysłany tag weryfikacyjny: + + + Minimum Number of Inbound Streams: + Minimalna liczba strumieni przychodzących: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + + + + Minimum Number of Outbound Streams: + Minimalna liczba strumieni wychodzących: + + + Graph Arwnd + Wykres Arwnd + + + Endpoint 2 + Punkt krańcowy 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + + + + Provided Number of Outbound Streams: + Liczba dostarczonych strumieni wychodzących: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + Analiza asocjacji SCTP: %1 Port1 %2 Port2 %3 + + + No Association found for this packet. + Brak asocjacji dla tego pakietu. + + + Warning + + + + Could not find SCTP Association with id: %1 + + + + Complete list of IP addresses from INIT Chunk: + + + + Complete list of IP addresses from INIT_ACK Chunk: + + + + List of Used IP Addresses + + + + Used Number of Inbound Streams: + Ilość używanych strumieni przychodzących: + + + Used Number of Outbound Streams: + Ilość używanych strumieni wychodzących: + + + + SCTPChunkStatisticsDialog + + Dialog + Okno + + + Association + Asocjacja + + + Endpoint 1 + Punkt krańcowy 1 + + + Endpoint 2 + Punkt krańcowy 2 + + + Save Chunk Type Order + Zapisz kolejność typu kawałków + + + Hide Chunk Type + Ukryj typ kawałku + + + Remove the chunk type from the table + Usuń typ kawałku z tablicy + + + Chunk Type Preferences + Preferencje typu kawałku + + + Go to the chunk type preferences dialog to show or hide other chunk types + Idź do okna preferencji typu kawałku by pokazać lub ukryć typy kawałków + + + Show All Registered Chunk Types + Pokaż wszystkie zarejestrowane typy kawałĸów + + + Show all chunk types with defined names + Pokaż wszystkie typy kawałów ze zdefiniowaną nazwą + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + SCTP Statystyki Kawałków: %1 Port1 %2 Port2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + Wykres SCTP + + + Reset to full size + Resetuj do pełnego rozmiaru + + + Save Graph + Zapisz wykres + + + goToPacket + IdźDoPakietu + + + Go to Packet + Idź do pakietu + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + Dane SCTP i Okna Ogłoszeń Odbiornika. w czasie: %1 Port1 %2 Port2 %3 + + + No Data Chunks sent + Brak wysłanych kawałów danych + + + Arwnd + Arwnd + + + time [secs] + czas [s] + + + Advertised Receiver Window [Bytes] + Okno Ogłoszeń Odbiornika [bajty] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>Wykres %1: a_rwnd=%2 Czas=%3 s </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + Wykres SCTP + + + Reset to full size + Resetuj do pełnego rozmiaru + + + Save Graph + Zapisz wykres + + + goToPacket + IdźDoPakietu + + + Go to Packet + Idź do pakietu + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + Dane SCTP i Okna Ogłoszeń Odbiornika. w czasie: %1 Port1 %2 Port2 %3 + + + No Data Chunks sent + Brak wysłanych kawałów danych + + + Bytes + Bajty + + + time [secs] + czas [s] + + + Received Bytes + Odebrane bajty + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>Wykres %1: Odebrane bajty=%2 Czas=%3 s</i></small> + + + + SCTPGraphDialog + + SCTP Graph + Wykres SCTP + + + Relative TSNs + + + + Only SACKs + Tylko SACKy + + + Only TSNs + Tylko TSNy + + + Show both + Pokaż obie + + + Reset to full size + Resetuj do pełnego rozmiaru + + + Save Graph + Zapisz wykres + + + goToPacket + IdźDoPakietu + + + Go to Packet + Idź do pakietu + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + SCTP TSNy i SACKy w czasie: %1 Port1 %2 Port2 %3 + + + No Data Chunks sent + Brak wysłanych kawałów danych + + + CumTSNAck + Potierdzenie CumTS + + + Gap Ack + Potwierdzenie luki + + + NR Gap Ack + Potwierdzenie luki NR + + + Duplicate Ack + Podwójne potwierdzenie + + + TSN + TSN + + + time [secs] + czas [s] + + + TSNs + TSNy + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 Czas: %3 s </i></small> + + + Portable Document Format (*.pdf) + Dokument PDF (*.pdf) + + + Portable Network Graphics (*.png) + Obraz PNG (*.png) + + + Windows Bitmap (*.bmp) + Bitmapa Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Obraz JPEG (*.jpeg *.jpg) + + + Save Graph As… + + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + + + + Command: + Komenda: + + + SCSI Service Response Times + Czas odpowiedzi serwisu SCSI + + + + SearchFrame + + Frame + Ramka + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>Przeszukuj kolumnę Info (lista pakietów), zdekodowane pola (szczegóły pakietu) lub ACII/surowe dane pakietu (bajty pakietu).</p></body></html> + + + Packet list + Lista pakietów + + + Packet details + Szczegóły pakietu + + + Packet bytes + Bajty pakietu + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Szukaj ciągu znaków w kodowanie wąskim (UTF-8 i ASCII) lub szerokim (UTF-16).</p></body></html> + + + Narrow & Wide + Wąskie i szerokie + + + Narrow (UTF-8 / ASCII) + Wąskie (UTF-8 / ASCII) + + + Wide (UTF-16) + Szerokie (UTF-16) + + + Case sensitive + Rozróżniaj wielkość znaków + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>Szukaj używając składni filtru wyświetlania (np. ip.addr==10.1.1.1), szesnastkowo (np. fffffda5), ciągu znaków (np. "My String") lub wyrażenia regularnego (np. colou?r).</p></body></html> + + + Display filter + Filtr wyświetlania + + + Hex value + Hex + + + String + String + + + Regular Expression + Wyrażenie regularne + + + Find + Znajdź + + + Cancel + Anuluj + + + No valid search type selected. Please report this to the development team. + Nie wybrano poprawnego typu wyszukiwania. Proszę to zgłosić programistom Wiresharka. + + + Invalid filter. + Błędny filtr. + + + That filter doesn't test anything. + Ten filtr nie sprawdza niczego. + + + That's not a valid hex string. + To niejest prawidłowy tekst w formacie szesnastkowym. + + + You didn't specify any text for which to search. + Nie wpisano tekstu do wyszukania. + + + No valid character set selected. Please report this to the development team. + Nie wybrano poprawnego kodowania znaków. Proszę to zgłosić programistom Wiresharka. + + + No valid search area selected. Please report this to the development team. + Nie wybrano poprawnego obszaru wyszukiwania. Proszę to zgłosić programistom Wiresharka. + + + Searching for %1… + + + + No packet contained those bytes. + Żaden pakiet nie zwiera tych bajów. + + + No packet contained that string in its Info column. + Żaden pakiet nie zawiera tego ciągu znaków w kolumnie Info. + + + No packet contained that string in its dissected display. + Żaden pakiet nie zawiera tego ciągu znaków w zdekodowanych polach. + + + No packet contained that string in its converted data. + Żaden pakiet nie zawierająca tego ciągu znaków w swoich danych. + + + No packet matched that filter. + Żaden pakiet nie spełnia wymogów tego filtru. + + + + SequenceDialog + + Call Flow + Przepływy połączeń + + + Time + Czas + + + Comment + Komentarz + + + No data + Brak danych + + + %Ln node(s) + + %Ln node + %Ln nodes + + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + Portable Document Format (*.pdf) + Dokument PDF (*.pdf) + + + Portable Network Graphics (*.png) + Obraz PNG (*.png) + + + Windows Bitmap (*.bmp) + Bitmapa Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Obraz JPEG (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII (*.txt) + + + Save Graph As… + + + + Flow + Przepływ + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Przydatne skróty klawiaturowe</h3> +<table><tbody> + +<tr><th>0</th><td>Powiększ</td></th> +<tr><th>0</th><td>Pomniejsz</td></th> +<tr><th>0</th><td>Resetuj wykres do stanu początkowego</td></th> + +<tr><th>→</th><td>Przesuń w prawo o 10 pikseli</td></th> +<tr><th>←</th><td>Przesuń w lewo o 10 pikseli</td></th> +<tr><th>↑</th><td>Przesuń w górę o 10 pikseli</td></th> +<tr><th>↓</th><td>Przesuń w dół o 10 pikseli</td></th> +<tr><th><i>Shift+</i>→</th><td>Przesuń w prawo o 1 piksel</td></th> +<tr><th><i>Shift+</i>←</th><td>Przesuń w lewo o 1 piksel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Przesuń w górę o 1 piksel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Przesuń w dół o 1 piksel</td></th> + +<tr><th>g</th><td>Idź do pakietu pod kursorem</td></th> +<tr><th>n</th><td>Idź do następnego pakietu</td></th> +<tr><th>p</th><td>Idź do poprzedniego pakietu</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>Podpowiedź</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>Pokazuj tylko przepływy pasujące do filtru</p></body></html> + + + Limit to display filter + Ogranicz do filtru wyświetlania + + + Flow type: + Typ przepływu: + + + Addresses: + Adresy: + + + Any + Każdy + + + Network + Sieciowy + + + Reset Diagram + Resetuj diagram + + + Reset &Diagram + + + + Reset the diagram to its initial state. + Resetuj diagram do stanu początkowego. + + + 0 + 0 + + + &Reset Diagram + + + + Reset the diagram to its initial state + + + + &Export + + + + Export diagram + + + + Zoom In + Powiększ + + + + + + + + + Zoom Out + Pomniejsz + + + - + - + + + Move Up 10 Pixels + Przesuń w górę o 10 pikseli + + + Up + W górę + + + Move Left 10 Pixels + Przesuń w lewo o 10 pikseli + + + Left + W lewo + + + Move Right 10 Pixels + Przesuń w prawo o 10 pikseli + + + Right + W prawo + + + Move Down 10 Pixels + Przesuń w dół o 10 pikseli + + + Down + W dół + + + Move Up 1 Pixel + Przesuń w górę o 1 piksel + + + Shift+Up + Shift+w górę + + + Move Left 1 Pixel + Przesuń w lewo o 1 piksel + + + Shift+Left + Shift+w lewo + + + Move Right 1 Pixel + Przesuń w prawo o 1 piksel + + + Shift+Right + Shift+w prawo + + + Move Down 1 Pixel + Przesuń w dół o 1 piksel + + + Shift+Down + Shift+w dół + + + Go To Packet Under Cursor + Idź do pakietu pod kursorem + + + Go to packet currently under the cursor + Idź do pakietu pod kursorem + + + G + G + + + All Flows + Wszystkie przepływy + + + Show flows for all packets + Pokazuj przepływy dla wszystkich pakietów + + + 1 + 1 + + + TCP Flows + Przepływy TCP + + + Show only TCP flow information + Pokazuj tylko przepływy dla TCP + + + Go To Next Packet + Idź do następnego pakietu + + + Go to the next packet + Idź do następnego pakietu + + + N + N + + + Go To Previous Packet + Idź do poprzedniego pakietu + + + Go to the previous packet + Idź do poprzedniego pakietu + + + P + P + + + Select RTP Stream + + + + Select RTP stream in RTP Streams dialog + + + + S + S + + + Deselect RTP Stream + + + + Deselect RTP stream in RTP Streams dialog + + + + D + D + + + + ShortcutListModel + + Shortcut + Skrót + + + Name + Name + + + Description + Opis + + + + ShowPacketBytesDialog + + Show Packet Bytes + Prezentuj Bajty Pakietu + + + Hint. + Podpowiedź. + + + Decode as + Dekoduj jako + + + Show as + Pokaż jako + + + Start + Start + + + End + Koniec + + + Find: + Znajdź: + + + Find &Next + Znajdź &następny + + + Frame %1, %2, %Ln byte(s). + + + + + + + + None + Brak + + + Base64 + Base64 + + + Compressed + Skompresowane + + + Hex Digits + + + + Percent-Encoding + + + + Quoted-Printable + Quoted-Printable + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII i znaki kontrolne + + + C Array + Tablica w języku C + + + EBCDIC + EBCDIC + + + Hex Dump + Szesnastkowo + + + HTML + HTML + + + Image + Obraz + + + Raw + Dane surowe + + + Rust Array + + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Drukuj + + + Copy + Kopiuj + + + Save as… + Zapisz jako… + + + Save Selected Packet Bytes As… + + + + Displaying %Ln byte(s). + + + + + + + + JSON + + + + Regex Find: + Regex znajdź: + + + + ShowPacketBytesTextEdit + + Show Selected + Pokaż wybrane + + + Show All + Pokaż wszystko + + + + SplashOverlay + + Initializing dissectors + Inicjacja dekoderów + + + Initializing tap listeners + Inicjacja kanałów komunikacyjnych + + + Initializing external capture plugins + Inicjacja zewnętrznych wtyczek przechwytywania + + + Registering dissectors + Rejestracja dekoderów + + + Registering plugins + Registering dissector + Rejestracja wtyczek + + + Handing off dissectors + Finalizowanie dekoderów + + + Handing off plugins + Finalizowanie wtyczek + + + Loading Lua plugins + Ładowanie wtyczek Lua + + + Removing Lua plugins + Usuwanie wtyczek Lua + + + Loading module preferences + Ładowanie preferencji modułów + + + Finding local interfaces + Szukanie lokalnych interfejsów + + + Applying changed preferences + + + + (Unknown action) + (Nieznana akcja) + + + + StatsTreeDialog + + Configuration not found + Nie znaleziono konfiguracji + + + Unable to find configuration for %1. + Nie można znaleźć konfiguracji dla %1. + + + + StripHeadersDialog + + Dialog + Okno + + + Display filter: + + + + + SupportedProtocolsDialog + + Dialog + Okno + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Przeszukaj listę nazw pól.</p></body></html> + + + Search: + Szukaj: + + + <small><i>Gathering protocol information…</i></small> + <small><i>Zbieranie informacji o protokołach…</i></small> + + + Supported Protocols + Obsługiwane Protokoły + + + %1 protocols, %2 fields. + %1 protokołów, %2 pól. + + + + SupportedProtocolsModel + + Name + Nazwa + + + Filter + Filtr + + + Type + Typ + + + Description + Opis + + + + SyntaxLineEdit + + Invalid filter: %1 + + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + + + + %1 + + + + + TCPStreamDialog + + Dialog + Okno + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Przydatne skróty klawiaturowe</h3> +<table><tbody> + +<tr><th>+</th><td>Powiększ</td></tr> +<tr><th>-</th><td>Pomniejsz</td></tr> +<tr><th>0</th><td>Resetuj wykres do stanu początkowego</td></tr> +<tr><th>→</th><td>Przesuń w prawo o 10 pikseli</td></tr> +<tr><th>←</th><td>Przesuń w lewo o 10 pikseli</td></tr> +<tr><th>↑</th><td>Przesuń w górę o 10 pikseli</td></tr> +<tr><th>↓</th><td>Move down 10 pixels</td></tr> +<tr><th><i>Shift+</i>→</th><td>Przesuń w prawo o 1 piksel</td></tr> +<tr><th><i>Shift+</i>←</th><td>Przesuń w lewo o 1 piksel</td></tr> +<tr><th><i>Shift+</i>↑</th><td>Przesuń w górę o 1 piksel</td></tr> +<tr><th><i>Shift+</i>↓</th><td>Przesuń w dół o 1 piksel</td></tr> + +<tr><th><i>Pg Up</i></th><td>Następny strumień</td></tr> +<tr><th><i>Pg Dn</i></th><td>Poprzedni strumień</td></tr> +<tr><th>d</th><td>Przełącz kierunki (zamień miejscami punkty krańcowe TCP)</td></tr> +<tr><th>g</th><td>Idź do pakietu pod kursorem</td></tr> + +<tr><th>z</th><td>Przełącz mysz pomiędzy funkcją przeciągnięcia a powiększenia/pomniejszenia</td></tr> +<tr><th>s</th><td>Przełącz numery sekwencyjne pomiędzy relatywnymi a absolutnymi</td></tr> +<tr><th>t</th><td>Przełącz między czasem przechwytywania a sesji</td></tr> +<tr><th>Space</th><td>Przełącz wskaźniki wykresu</td></tr> + +<tr><th>1</th><td>Wykres czasu podróży</td></tr> +<tr><th>2</th><td>Wykres przepusowości</td></tr> +<tr><th>3</th><td>Wykres czasu/sekwencji Stevensa</td></tr> +<tr><th>4</th><td>Wykres w stylu czasu/sekwencji programu tcptrace</td></tr> +<tr><th>5</th><td>Wykres okna skalowania</td></tr> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>Przesuń mysz nad element by zobaczyć skrót</i></small> + + + Type + Typ + + + MA Window (s) + + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + + + + Select SACKs + select SACKs + + + + Stream + Strumień + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Zmień kierunek przepływu w połączeniu.</p></body></html> + + + Switch Direction + Odwróć kierunek + + + Mouse + Mysz + + + Drag using the mouse button. + Przeciągnij przy użyciu myszy. + + + drags + przesuwanie + + + Select using the mouse button. + Wybieranie przy pomocy myszy. + + + zooms + zmiana rozmiaru + + + Display Round Trip Time vs Sequence Number + + + + RTT By Sequence Number + + + + Display graph of Segment Length vs Time + + + + Segment Length + + + + Display graph of Mean Transmitted Bytes vs Time + + + + Display graph of Mean ACKed Bytes vs Time + + + + Goodput + + + + Display graph of Receive Window Size vs Time + + + + Rcv Win + + + + Display graph of Outstanding Bytes vs Time + + + + Bytes Out + + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Resetuj wykres do ustawień początkowych</p></body></html> + + + Reset + Resetuj + + + Reset Graph + Resetuj wykres + + + Reset the graph to its initial state. + Resetuj wykres do stanu początkowego. + + + 0 + 0 + + + Zoom In + Powiększ + + + + + + + + + Zoom Out + Pomniejsz + + + - + - + + + Move Up 10 Pixels + Przesuń w górę o 10 pikseli + + + Up + W górę + + + Move Left 10 Pixels + Przesuń w lewo o 10 pikseli + + + Left + W lewo + + + Move Right 10 Pixels + Przesuń w prawo o 10 pikseli + + + Right + W prawo + + + Move Down 10 Pixels + Przesuń w dół o 10 pikseli + + + Down + W dół + + + Move Up 1 Pixel + Przesuń w górę o 1 piksel + + + Shift+Up + Shift+w górę + + + Move Left 1 Pixel + Przesuń w lewo o 1 piksel + + + Shift+Left + Shift+w lewo + + + Move Right 1 Pixel + Przesuń w prawo o 1 piksel + + + Shift+Right + Shift+w prawo + + + Move Down 1 Pixel + Przesuń w dół o 1 piksel + + + Shift+Down + Shift+w dół + + + Next Stream + Następny strumień + + + Go to the next stream in the capture + Idź do następnego strumienia + + + PgUp + PgUp + + + Previous Stream + Poprzedni strumień + + + Go to the previous stream in the capture + Idź do poprzedniego strumienia + + + PgDown + PgDown + + + Switch direction (swap TCP endpoints) + Odwróć kierunk (zamień punkty krańcowe TCP) + + + D + D + + + Go To Packet Under Cursor + Idź do pakietu pod kursorem + + + Go to packet currently under the cursor + Idź do pakietu pod kursorem + + + G + G + + + Drag / Zoom + Przeciągnij / Powiększ/pomniejsz + + + Toggle mouse drag / zoom behavior + Przełącz mysz pomiędzy funkcją przeciągnięcia a powiększenia/pomniejszenia + + + Z + Z + + + Relative / Absolute Sequence Numbers + Relatywne/Absolutne numery sekwencyjne + + + Toggle relative / absolute sequence numbers + Przałącz pomiędzy relatywnymi a absolutnymi numerami sekwencyjnymi + + + S + S + + + Capture / Session Time Origin + Czas sesji / przechwytywania + + + Toggle capture / session time origin + Przełącz między czasem przechwytywania a sesji + + + T + T + + + Crosshairs + Wskaźniki + + + Toggle crosshairs + Przełącz wskaźnik wykresu + + + Space + Spacja + + + Round Trip Time + Czas podróży + + + Switch to the Round Trip Time graph + Przełącz na wykres czasu podróży + + + 1 + 1 + + + Throughput + Przepustowość + + + Switch to the Throughput graph + Przełącz na wykres przepustowości + + + 2 + 2 + + + Time / Sequence (Stevens) + Czas / Sekwencja (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + Przełącz na wykres czasu/sekwencji Stevensa + + + 3 + 3 + + + Window Scaling + Okno skalowania + + + Switch to the Window Scaling graph + Przełącz do wykresu okna skalowania + + + 5 + 5 + + + Time / Sequence (tcptrace) + Czas / Sekwencja (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + Przełącz do wykresu w stylu czasu/sekwencji programu tcptrace + + + 4 + 4 + + + Zoom In X Axis + Zwiększ zakres osi X + + + X + X + + + Zoom Out X Axis + Zmniejsz zakres osi X + + + Shift+X + Shift+X + + + Zoom In Y Axis + Zwiększ zakres osi Y + + + Y + Y + + + Zoom Out Y Axis + Zmniejsz zakres osi Y + + + Shift+Y + Shift+Y + + + Save As… + + + + No Capture Data + Brak przechwyconych danych + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 %2 pakietów, %3 %4 %5 punktów, %6 + + + Sequence Numbers (Stevens) + Numery sekwencyjne (Stevens) + + + Sequence Numbers (tcptrace) + Numery sekwencyjne (tcptrace) + + + (MA) + (MA) + + + (%1 Segment MA) + (%1 Segment MA) + + + [not enough data] + [niewystarczające dane] + + + for %1:%2 %3 %4:%5 + dla %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3s rozmiar %4 numer sekwencyjny %5 potwierdzone %6 wygrane %7) + + + Click to select packet + Kliknij by wybrać pakiet + + + Packet + Pakiet + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Powiększenie, x = %1 do %2, y = %3 do %4 + + + Unable to select range. + Niemożna wybrać danego zakresu. + + + Click to select a portion of the graph. + Kliknij by wybrać obszar wykresu. + + + Portable Document Format (*.pdf) + Dokument PDF (*.pdf) + + + Portable Network Graphics (*.png) + Obraz PNG (*.png) + + + Windows Bitmap (*.bmp) + Bitmapa Windows (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Obraz JPEG (*.jpeg *.jpg) + + + Save Graph As… + + + + + TLSKeylogDialog + + Dialog + Okno + + + Browse… + Przeglądaj… + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + Okno + + + Item + Pozycja + + + <small><i>A hint.</i></small> + <small><i>Podpowiedź.</i></small> + + + Display filter: + Filtr wyświetlania: + + + Regenerate statistics using this display filter + Odśwież statystyki używając filtru wyświetlania + + + Apply + Zastosuj + + + Copy + Kopiuj + + + Copy a text representation of the tree to the clipboard + Kopiuj reprezentację tekstową drzewa do schowka + + + Save as… + Save as... + Zapisz jako… + + + Save the displayed data in various formats + Zapisywanie wyświetlonych dane w różnych formatach + + + Collapse All + Zwiń wszystko + + + Expand All + Rozwiń wszystko + + + Save Statistics As… + + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + Plik tekstowy (*.txt);;Wartości rozdzielone przecinkami (*.csv);;Dokument XML (*.xml);;Dokument YAML (*.yaml) + + + Plain text file (*.txt) + Plik tekstowy (*.txt) + + + Error saving file %1 + Błąd podczas zapisywania pliku %1 + + + + TimeShiftDialog + + Shift all packets by + Przesuń czas wszystkich pakietów o + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + Ustaw czas dla pakietu + + + to + do + + + …then set packet + ...then set packet + …następnie ustaw pakiet + + + and extrapolate the time for all other packets + i ekstrapoluj czas dla dla innych pakietów + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + Cofnij wszystkie zmiany + + + Time Shift + Przesuń czas + + + Frame numbers must be between 1 and %1. + Numer ramki musi być z zakresu 1-%1. + + + Invalid frame number. + Nieprawidłowy numer ramki. + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + + + + Could not open base file %1 for reading: %2 + + + + No endpoints available to map + + + + Unable to create temporary file + + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>Rozwiązuj nazwy adresu i portu. Ustawienie rozwiązywania nazw musi być włączone.</p></body></html> + + + Name resolution + Rozwiązywanie nazw + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Pokazuj tylko konwersacje pasujące do filtru</p></body></html> + + + Limit to display filter + Ogranicz do filtru + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + + + + Filter list for specific type + + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>Pokazuj czas absolutny w kolumnie Start Czasu.</p></body></html> + + + GroupBox + Element grupujący + + + Absolute start time + Absolutny czas startu + + + Copy + Kopiuj + + + Unknown + Nieznany + + + + TrafficTree + + Resize all columns to content + + + + Filter on stream id + + + + Copy %1 table + + + + as CSV + jako CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + + + + as YAML + jako YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + + + + as JSON + + + + Copy all values of this page to the clipboard in the JSON data serialization format. + + + + Save data as raw + + + + Disable data formatting for export/clipboard and save as raw data + + + + + TrafficTreeHeaderView + + Less than + + + + Greater than + + + + Equal + + + + Columns to display + + + + Filter %1 by + + + + Enter filter value + + + + + TrafficTypesModel + + Protocol + Protokół + + + + UatDialog + + Create a new entry. + Stwórz nowy wpis. + + + Remove this entry. + Remove this profile. + Usuń wpis. + + + Copy this entry. + Copy this profile. + Kopiuj wpis. + + + Move entry up. + Przesuń wpis w górę. + + + Move entry down. + Przesuń wpis w dół. + + + Clear all entries. + Usuń wszystkie wpisy. + + + Unknown User Accessible Table + Nieznana Tabela Użytkownika + + + Open + Otwórz + + + + UatFrame + + Frame + Ramka + + + Create a new entry. + Stwórz nowy wpis. + + + Remove this entry. + Usuń wpis. + + + Copy this entry. + Kopiuj wpis. + + + Move entry up. + Przesuń wpis w górę. + + + Move entry down. + Przesuń wpis w dół. + + + Clear all entries. + Usuń wszystkie wpisy. + + + Copy entries from another profile. + Kopiuj z innego profilu. + + + Copy from + Kopiuj z + + + Unknown User Accessible Table + Nieznana Tabela + + + Open + Otwórz + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Pokazuj tylko konwersacje pasujące do filtru</p></body></html> + + + Limit to display filter + + + + Time of Day + Czas dnia + + + Flow &Sequence + + + + Show flow sequence for selected call(s). + + + + Prepare &Filter + + + + Prepare a filter matching the selected calls(s). + + + + Cop&y + + + + Open copy menu + + + + All + + + + Select all + + + + None + Brak + + + Invert + Przełącz + + + Invert selection + + + + Select related RTP streams + + + + Select RTP streams related to selected calls in RTP Streams dialog + + + + S + S + + + Deselect related RTP Streams + + + + D + D + + + Clear selection + + + + Display time as time of day + + + + Copy as CSV + Kopiuj jako CSV + + + Copy stream list as CSV. + Kopiuj listę strumieni jako CSV. + + + Copy as YAML + Kopiuj jako YAML + + + Copy stream list as YAML. + Kopiuj listę strumieni jako YAML. + + + SIP Flows + Przepływy SIP + + + VoIP Calls + Połączenia VoIP + + + as CSV + jako CSV + + + as YAML + jako YAML + + + Select + + + + + VoipCallsInfoModel + + On + Włączone + + + Off + Wyłączone + + + Tunneling: %1 Fast Start: %2 + Tunelowanie: %1 Szybki start: %2 + + + Start Time + Czas startu + + + Stop Time + Czas stopu + + + Initial Speaker + Inicjujący Rozmówca + + + From + Od + + + To + Do + + + Protocol + Protokół + + + Duration + Czas trwania + + + Packets + Pakiety + + + State + Stan + + + Comments + Komentarze + + + + WelcomePage + + Form + Formularz + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Witaj w Wiresharku</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>Otwórz plik</p></body></html> + + + <h2>Open</h2> + <h2>Otwórz</h2> + + + Recent capture files + Ostatnio używane pliki przechwytywania + + + Capture files that have been opened previously + Pliki przechwytywania, które były uprzednio używane + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>Przechwytuj pakiety.</p></body></html> + + + <h2>Capture</h2> + <h2>Przechwytywanie</h2> + + + …using this filter: + ...używając tego filtru: + + + Interface list + Lista interfejsów + + + List of available capture interfaces + Lista dostępnych interfejsów przechwytywania + + + <h2>Learn</h2> + <h2>Nauka</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + + + + Show in Finder + Pokaż w wyszukiwarce + + + Show in Folder + Pokaż w folderze + + + Welcome to %1 + + + + All interfaces shown + Wszystkie interfejsy + + + %n interface(s) shown, %1 hidden + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + You are sniffing the glue that holds the Internet together using Wireshark + Dzięki Wiresharkowi podglądasz fundamenty współczesnego Internetu. + + + You are running Wireshark + Wireshark uruchomiony + + + You receive automatic updates. + Automatyczne aktualizacje są włączone. + + + You have disabled automatic updates. + Automatyczne aktualizacje są wyłączone. + + + not found + nie znaleziono + + + Copy file path + Kopiuj ścieżkę + + + Remove from list + + + + + WirelessFrame + + Frame + Ramka + + + Interface + Interfejs + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>Ustaw kanał 802.11.</p></body></html> + + + Channel + Kanał + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>Podczas przechwytywania pokazuj wszystkie ramki, ramki poprawne (FCS) lub ramki z niepoprawną sumą kontrolną.</p></body></html> + + + FCS Filter + Filtr FCS + + + All Frames + Wszystkie ramki + + + Valid Frames + Poprawne ramki + + + Invalid Frames + Niepoprawne ramki + + + Wireless controls are not supported in this version of Wireshark. + Kontrolki bezprzewodowe nie są dostępne w tej wersji Wireshark'a. + + + External Helper + Zewnętrzny Pomocnik + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>Pokaż ustawienia IEEE 802.11, włączając klucze deszyfrowania.</p></body></html> + + + 802.11 Preferences + Ustawienia 802.11 + + + AirPcap Control Panel + Panel sterowania AirPcap + + + Open the AirPcap Control Panel + Otwórz panel sterowania AirPcap + + + Unable to set channel or offset. + Nie można ustawić kanału lub przesunięcia. + + + Unable to set FCS validation behavior. + Nie można ustawić zachowania walidacji FCS. + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + + + + + WiresharkDialog + + Failed to attach to tap "%1" + Nie można podłączyć do kanału komunikacyjnego "%1" + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Idź do pakietu + + + Cancel + Anuluj + + + File Set + Zbiór plików + + + Export Packet Dissections + Eksportuj prezentację pakietów + + + Export Objects + Eksportuj obiekty + + + &Zoom + Powiększenie/pomniejszenie + + + &Time Display Format + Format czasu + + + Copy + Kopiuj + + + Manual pages + Pliki pomocy + + + Apply as Filter + Zastosuj filtr + + + Prepare as Filter + + + + SCTP + SCTP + + + TCP Stream Graphs + Graf strumienia TCP + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &Plik + + + &Capture + Prze&chwytuj + + + &Help + P&omoc + + + &Go + Idź + + + &View + Widok + + + &Analyze + Analizuj + + + Follow + Podążaj + + + &Statistics + &Statystyki + + + 29West + 29West + + + Topics + Tematy + + + Queues + Kolejki + + + UIM + UIM + + + Telephon&y + Telefonia + + + RTSP + RTSP + + + &Edit + &Edytuj + + + Packet Comments + Komentarze pakietu + + + Main Toolbar + Główny pasek narzędziowy + + + Display Filter Toolbar + Pasek filtrowania + + + Open a capture file + Otwórz plik + + + Quit Wireshark + Zamknij Wiresharka + + + &Start + &Start + + + Start capturing packets + Uruchom przechwytywanie pakietów + + + S&top + S&top + + + Stop capturing packets + Zatrzymaj przechwytywanie pakietów + + + No files found + Nie znaleziono pliku + + + &Contents + Podrę&cznik + + + Wireshark Filter + Filtr + + + TShark + TShark + + + Rawshark + + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + + + + Website + Strona www + + + Downloads + Ściągnij + + + Wiki + Wiki + + + Sample Captures + Przykładowe pliki przechwytywania + + + &About Wireshark + O progr&amie Wireshark + + + Ask (Q&&A) + Zapytaj (Q&&A) + + + Next Packet + Następny pakiet + + + Go to the next packet + Idź do następnego pakietu + + + Previous Packet + Poprzedni pakiet + + + Go to the previous packet + Idź do poprzedniego pakietu + + + First Packet + Pierwszy pakiet + + + Go to the first packet + Idź do pierwszego pakietu + + + Last Packet + Ostatni pakiet + + + Go to the last packet + Idź do ostatniego pakietu + + + E&xpand Subtrees + Rozwiń poddrzewa + + + Expand the current packet detail + Rozwiń szczegóły pakietu + + + &Expand All + Rozwiń wszystko + + + Expand packet details + Rozwiń szczegóły pakietu + + + Collapse &All + Zwiń wszystko + + + Collapse all packet details + Zwiń wszystkie szczegóły pakietu + + + Go to specified packet + Idź do wybranego pakietu + + + Merge one or more files + Scal jeden lub więcej plików + + + Import a file + Importuj plik + + + &Save + Zapisz + + + Save as a different file + Zapisz jako inny plik + + + Export specified packets + Eksportuj wybrane pakiety + + + Export TLS Session Keys… + + + + List Files + Wyświetl pliki + + + Next File + Następny plik + + + Previous File + Poprzedni plik + + + &Reload + Przeładuj + + + Options + Opcje + + + Capture options + Opcje przechwytywania + + + Capture filters + Filtry przechwytywania... + + + Refresh Interfaces + Odśwież interfejsy + + + Refresh interfaces + Odśwież interfejsy + + + &Restart + &Restart + + + Restart current capture + Restartuj aktualne przechwytywanie + + + As &CSV… + + + + As "C" &Arrays… + + + + As P&SML XML… + + + + As P&DML XML… + + + + As &JSON… + + + + Description + Opis + + + Field Name + Nazwa pola + + + Value + Wartość + + + As Filter + Jako filtr + + + Close this capture file + Zamknij ten plik + + + Packet: + Pakiet: + + + Interface Toolbars + + + + Colorize Conversation + Koloruj konwersacje + + + Internals + Właściwości własne + + + Additional Toolbars + + + + Conversation Filter + Filtr Konwersacji + + + Reliable Server Pooling (RSerPool) + + + + SOME/IP + + + + &DTN + + + + Osmux + + + + &Tools + Tools + Narzędzia + + + Wireless Toolbar + Pasek sniffera WiFi + + + Help contents + Pomoc + + + FAQs + + + + Next Packet in Conversation + Następny pakiet w konwersacji + + + Go to the next packet in this conversation + Idź do następnego pakietu w konwersacji + + + Previous Packet in Conversation + Poprzedni pakiet w konwersacji + + + Go to the previous packet in this conversation + Idź do poprzedniego pakietu w konwersacji + + + Next Packet In History + Następny pakiet w historii + + + Go to the next packet in your selection history + Idź do następnego pakietu w wybranej historii + + + Previous Packet In History + + + + Go to the previous packet in your selection history + + + + Collapse Subtrees + + + + Collapse the current packet detail + + + + Go to Packet… + Idź do pakietu… + + + &Merge… + Scal… + + + &Import from Hex Dump… + Za&importuj HexDump… + + + Save this capture file + Zapisz + + + Save &As… + Z&apisz jako… + + + Export Specified Packets… + Eksportuj wybrane pakiety… + + + Export Packet &Bytes… + Eksportuj &bajty pakietu… + + + &Print… + Drukuj… + + + Reload this file + Wczytaj ponownie plik + + + Reload as File Format/Capture + Przeładuj jako format pliku/plik przechwytywania + + + Copy this item's description + Kopiuj opis tej pozycji + + + Copy this item's field name + Kopiuj nazwę pola tej pozycji + + + Copy this item's value + Kopiuj wartość tej pozycji + + + Copy this item as a display filter + Kopiuj tą pozycję jako filtr wyświetlania + + + Apply as Column + Utwórz kolumnę z pola + + + Create a packet list column from the selected field. + Stwórz kolumnę na liście pakietów z wybranego pola. + + + Find a packet + Znajdź pakiet + + + Find the next packet + Znajdź następny pakiet + + + Find the previous packet + Znajdź poprzedni pakiet + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + + + + Mark All Displayed + Zaznacz wszystkie wyświetlane + + + Mark all displayed packets + Zaznacz wszystkie wyświetlane pakiety + + + Unmark all displayed packets + Odzaznacz wszystkie wyświetlane pakiety + + + Next Mark + Następne zaznaczenie + + + Go to the next marked packet + Idź do następnego zaznaczonego pakietu + + + Previous Mark + Poprzednie zaznaczenie + + + Go to the previous marked packet + Idź do poprzedniego zaznaczonego pakietu + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + + + + Ignore All Displayed + Ignoruj wszystkie wyświetlane + + + Ignore all displayed packets + Ignoruj wszystkie wyświetlane pakiety + + + Set/Unset Time Reference + Ustaw/wyłącz referencje czasu + + + Set or unset a time reference for this packet + Ustaw/wyłącz referencje czasu dla pakietu + + + Unset All Time References + Wyłącz wszystkie referencje czasu + + + Remove all time references + Usuń wszystkie referencje czasu + + + Next Time Reference + Następna referencja czasu + + + Go to the next time reference + Idź do następnej referencji czasu + + + Previous Time Reference + Poprzednia referencja czasu + + + Go to the previous time reference + Idź do poprzedniej referencji czasu + + + Shift or change packet timestamps + Przesuń lub zmień czas pakietu + + + Delete All Packet Comments + + + + Remove all packet comments in the capture file + + + + &Configuration Profiles… + + + + Configuration profiles + Konfiguracja profili + + + Manage your configuration profiles + Zarządzaj swoimi konfiguracjami profili + + + Manage Wireshark's preferences + Zarządzaj preferencjami Wiresharka + + + Capture File Properties + Szczegóły pliku przechwytywania + + + Capture file properties + Szczegóły pliku przechwytywania + + + &Protocol Hierarchy + Hierarchia &protokołów + + + Show a summary of protocols present in the capture file. + Pokaż podsumowanie protokołów obecnych w pliku przechwytywania. + + + Capinfos + Capinfos + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + Sekwencje czasu (Stevens) + + + TCP time sequence graph (Stevens) + Wykres sekwencji czasu Stevensa + + + Throughput + Przepustowość + + + Round Trip Time + Czas podróży + + + TCP round trip time + Czas podróży TCP + + + Window Scaling + Okno skalowania + + + TCP window scaling + Okno skalowania TCP + + + HTTP/2 Stream + + + + SIP Call + + + + Time Sequence (tcptrace) + Sekwencja czasu (tcptrace) + + + TCP time sequence graph (tcptrace) + Wykres sekwencji czasu (tcptrace) + + + Analyse this Association + Analizuj asocjacje + + + Show All Associations + Pokaż wszystkie asocjacje + + + Flow Graph + Graf przepływu + + + Flow sequence diagram + Diagram sekwencji przepływu + + + ANCP + ANCP + + + ANCP statistics + Statystyki ANCP + + + Packets sorted by Instance ID + Sortuj pakiety po ID instancji + + + BACapp statistics sorted by instance ID + Sortuj statystyki BACapp po ID instancji + + + Packets sorted by IP + Sortuj pakiety po IP + + + BACapp statistics sorted by IP + Sortuj statystyki BACapp po IP + + + Packets sorted by object type + Sortuj pakiety po typie obiektu + + + BACapp statistics sorted by object type + Statystyki BACapp posortowane po typie obiektu + + + Packets sorted by service + Pakiety posortowane po serwisie + + + BACapp statistics sorted by service + Statystyki BACapp posortowane po serwisie + + + Collectd + Collectd + + + Collectd statistics + Statystyki Collectd + + + DNS + DNS + + + DNS statistics + Statystyki DNS + + + HART-IP + HART-IP + + + HART-IP statistics + Statystyki HART-IP + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + Statystyki HPFEEDS + + + HTTP2 + HTTP2 + + + HTTP2 statistics + Statystyki HTTP2 + + + Packet Counter + Licznik pakietów + + + HTTP packet counter + Licznik pakietów HTTP + + + Requests + Żądania + + + HTTP requests + Żądania HTTP + + + Load Distribution + Rozkład obciążenia + + + HTTP load distribution + Rozkład obciążenia HTPP + + + Packet Lengths + Długości pakietu + + + Packet length statistics + Statystyki długości pakietów + + + Sametime + Sametime + + + Sametime statistics + Statystyki Sametime + + + SOME/IP Messages + + + + SOME/IP Message statistics + + + + SOME/IP-SD Entries + + + + SOME/IP-SD Entries statistics + + + + &LTP + + + + LTP segment and block statistics + + + + &ISUP Messages + Komunikaty &ISUP + + + ISUP message statistics + Statystyki wiadomości ISUP + + + Osmux packet counts + + + + RTSP packet counts + Liczba pakietów RTSP + + + SM&PP Operations + Operacje SM&PP + + + SMPP operation statistics + Statystyki operacji SMPP + + + &UCP Messages + Komunikaty &UCP + + + UCP message statistics + Statystyki wiadomości UCP + + + F1AP + + + + F1AP Messages + + + + NGAP + + + + NGAP Messages + + + + Change the way packets are dissected + Zmień sposób w jaki pakiety są dekodowane + + + Reload Lua Plugins + Przeładuj wtyczki Lua + + + Reload Lua plugins + Przeładuj wtyczki Lua + + + Advertisements by Topic + Ogłoszenia według tematu + + + Advertisements by Source + Ogłoszenia według źródła + + + Advertisements by Transport + Ogłoszenia według transportu + + + Queries by Topic + Zapytania według tematu + + + Queries by Receiver + Zapytania według Odbiorcy + + + Wildcard Queries by Pattern + Wieloznaczne zapytania według wzorca + + + Wildcard Queries by Receiver + Wieloznaczne zapytania według odbiorcy + + + Advertisements by Queue + Ogłoszenia według kolejki + + + Queries by Queue + Zapytania według kolejki + + + Streams + Strumienie + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + Wyfiltruj tą asocjacje + + + Strip Headers… + + + + Strip headers and export higher level encapsulations to file + + + + &I/O Graphs + + + + &Conversations + + + + &Endpoints + + + + Shrink the main window text + Pomniejsz + + + Return the main window text to its normal size + Przywróć domyślny rozmiar + + + Reset Layout + + + + Reset appearance layout to default size + + + + Seconds Since First Captured Packet + + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + + + + Show or hide the packet diagram + + + + Show each conversation hash table + Pokaż tablice mieszające konwersacji + + + Show each dissector table and its entries + Pokaż tabelę dekoderów i jego wpisy + + + Show the currently supported protocols and display filter fields + Pokaż obsługiwane protokoły i filtry wyświetlania pól + + + MAC Statistics + Statystyki MAC + + + LTE MAC statistics + Statystyki LTE MAC + + + RLC Statistics + Statystyki RLC + + + LTE RLC statistics + Statystyki LTE RLC + + + LTE RLC graph + Wykres LTE RLC + + + MTP3 Summary + Podsumowanie MTP3 + + + MTP3 summary statistics + Statystyki podsumowania MTP3 + + + Bluetooth Devices + Bluetooth Urządzenia + + + Bluetooth HCI Summary + Bluetooth Podsumowanie HCI + + + Display Filter &Expression… + + + + Display Filter Expression… + + + + REGISTER_STAT_GROUP_RSERPOOL + + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + + + + No GSM statistics registered + Brak zarejestrowanych statystyk GSM + + + No LTE statistics registered + Brak zarejestrowanych statystyk LTE + + + No MTP3 statistics registered + Brak zarejestrowanych statystyk MTP3 + + + IAX2 Stream Analysis + Analiza strumienia IAX2 + + + Show Packet Bytes… + Prezentuj Bajty Pakietu… + + + Go to &Linked Packet + + + + UDP Multicast Streams + Strumienie rozsyłania grupowego UDP + + + Show UTP multicast stream statistics. + Pokaż statystyki rozsyłania grupowego UTP + + + WLAN Traffic + Ruch WLAN + + + Show IEEE 802.11 wireless LAN statistics. + Pokaż statystyki bezprzewodowych sieci IEEE 802.11 + + + Add a display filter button. + Dodaj przycisk filtru wyświetlania. + + + Firewall ACL Rules + Reguły ACL zapory ogniowej + + + Create firewall ACL rules + Stwórz reguły ACL zapory ogniowej + + + &Full Screen + Pełny ekran + + + Credentials + + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + &Opcje… + + + &Wireless + Bezprze&wodowe + + + Capture &Filters… + &Filtry przechwytywania… + + + As Plain &Text… + Jako tekst… + + + As Plain &Text + + + + As &CSV + + + + As &YAML + + + + All Visible Items + Wszystkie widoczne pozycje + + + All Visible Selected Tree Items + Wszystkie widoczne pozycje wybranego poddrzewa + + + Display Filter &Macros… + Makra filtrów wyświetlania… + + + &Find Packet… + Znajdź pakiet… + + + Find Ne&xt + Znajdź następny… + + + Find Pre&vious + Znajdź poprzedni… + + + Mark or unmark each selected packet + + + + Ignore or unignore each selected packet + + + + U&nignore All Displayed + + + + Unignore all displayed packets + + + + Time Shift… + Przesuń czas… + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + &Preferencje… + + + TCP throughput + + + + Request Sequences + + + + HTTP Request Sequences + + + + Decode &As… + Dekoduj jako… + + + Export PDUs to File… + Eksportuj PDU do pliku… + + + Create graphs based on display filter fields + Stwórz wykres bazując na aktualnym filtrze + + + &Main Toolbar + Główny pasek + + + Show or hide the main toolbar + Pokaż lub ukryj główny pasek narzędziowy + + + &Filter Toolbar + Pasek filtrowania + + + Show or hide the display filter toolbar + Pokaż lub ukryj pasek filtrowania + + + Conversations at different protocol levels + Konwersacje na różnych poziomach protokołów + + + Endpoints at different protocol levels + Punkty krańcowe są na różnych warstwach + + + Colorize Packet List + Koloruj listę pakietów + + + Draw packets using your coloring rules + Wypisuj pakiety używając reguł kolorowania + + + &Zoom In + Powiększ + + + Enlarge the main window text + Powiększ + + + Zoom Out + Pomniejsz + + + Normal Size + Normalny rozmiar + + + Resize Columns + Zmień rozmiar kolumn + + + Resize packet list columns to fit contents + Dostosuj rozmiar kolumn do zawartości + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Data i czas (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + Jako czas pakietów pokazuj datę i czas. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Rok, dzień roku, i czas (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Jako czas pakietów pokazuj rok dzień roku i czas. + + + Time of Day (01:02:03.123456) + Czas dnia (01:02:03.123456) + + + Seconds Since 1970-01-01 + Sekundy od 1970-01-01 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Jako czas pakietów pokazuj sekundy od ery UNIX/POSIX (1970-01-01). + + + Seconds Since Previous Captured Packet + Sekundy od poprzedniego przechwyconego pakietu + + + Show packet times as the seconds since the previous captured packet. + Jako czas pakietów pokazuj sekundy od poprzedniego przechwyconego pakietu. + + + Seconds Since Previous Displayed Packet + Sekundy od poprzedniego wyświetlanego pakiety + + + Show packet times as the seconds since the previous displayed packet. + Jako czas pakietów pokazuj sekundy od poprzedniego wyświetlonego pakietu. + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + UTC (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Wyświetl czas w formacie UTC + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + UTC rok, dzień roku, i czas (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + Wyświetlaj czas w formacie UTC rok dzień roku i czas + + + UTC Time of Day (01:02:03.123456) + Format UTC (01:02:03.123456) + + + Show packet times as the UTC time of day. + Wyświetlaj czas w formacie UTC + + + Automatic (from capture file) + Automatyczna (z pliku) + + + Use the time precision indicated in the capture file. + Używaj precyzji czas z pliku + + + Seconds + Sekundy + + + Tenths of a second + Dziesiętne sekundy + + + Hundredths of a second + Setne sekundy + + + Milliseconds + Milisekundy + + + Microseconds + Mikrosekundy + + + Nanoseconds + Nanosekundy + + + Display Seconds With Hours and Minutes + Wyświetlaj sekundy z godzinami i minutami + + + Display seconds with hours and minutes + Wyświetlaj sekundy z godzinami i minutami + + + Resolve &Physical Addresses + Rozwiązuj nazwy adresów fizycznych + + + Show names for known MAC addresses. Lookups use a local database. + Pokazuj nazwy znanych adresów MAC używając lokalnej bazy nazw. + + + Resolve &Network Addresses + Rozwiązuj nazwy adresów sieciowych + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + Pokazuj nazwy dla znanych serwisów IPv4, IPv6 i IPX. Może to powodować wygenerowanie ruchu sieciowego na niektórych systemach. + + + Resolve &Transport Addresses + Rozwiązuj nazwy adresów transportowych + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + Pokazuj nazwy dla znanych serwisów TCP, UDP i SCTP. Może to powodować wygenerowanie ruchu sieciowego na niektórych systemach. + + + Wire&less Toolbar + Pasek sniffera WiFi + + + Show or hide the wireless toolbar + Pokaż/ukryj pasek sniffera WiFi + + + &Status Bar + Pasek stanu + + + Show or hide the status bar + Pokaż/ukryj pasek stanu + + + Packet &List + Lista pakietów + + + Show or hide the packet list + Pokaż/ukryj listę pakietów + + + Packet &Details + Szczegóły pakietu + + + Show or hide the packet details + Pokaż/ukryj szczegóły pakietu + + + Packet &Bytes + Bajty pakietu + + + Show or hide the packet bytes + Pokaż/ukryj bajty pakietu + + + &Conversation Hash Tables + + + + &Dissector Tables + + + + &Supported Protocols + + + + MAP Summary + Podsumowanie MAP + + + GSM MAP summary statistics + Statystyki podsumowania GSM MAP + + + RLC &Graph + + + + &Coloring Rules… + Reguły kolorowania… + + + Show Linked Packet in New Window + Pokazuj podlinkowane pakiety w nowym oknie + + + New Coloring Rule… + New Conversation Rule… + Nowa reguła kolorowania... + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + + + + RTP Player + Odtwarzacz RTP + + + Play selected stream. Press CTRL key for playing reverse stream too. + + + + IA&X2 Stream Analysis + + + + Enabled Protocols… + Enable Protocols… + Używane protokoły… + + + Wiki Protocol Page + Strona Wiki Protokołów + + + Open the Wireshark wiki page for this protocol. + Otwórz stronę Wiki Wiresharka dla tego protokołu. + + + Filter Field Reference + Odwołania filtru pola + + + Open the display filter reference page for this filter field. + Otwórz stronę filtru wyświetlania dla tego pola. + + + Go to the packet referenced by the selected field. + Idź do pakietu do którego odwołuje się zaznaczone pole. + + + &VoIP Calls + Połączenia VoIP + + + Open &Recent + + + + Name Resol&ution + + + + Service &Response Time + + + + &RTP + + + + S&CTP + + + + &ANSI + + + + &GSM + + + + &LTE + + + + &MTP3 + + + + &Open + + + + &Quit + + + + &Close + + + + Display &Filters… + + + + &Unmark All Displayed + + + + All VoIP Calls + Wszystkie połączenia VoIP + + + SIP &Flows + Przepływy SIP + + + SIP Flows + Przepływy SIP + + + RTP Streams + Strumienie RTP + + + Edit the packet list coloring rules. + Edytuj reguły kolorowania listy pakietów. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Bluetooth ATT Atrybuty Serwera + + + Show Packet in New &Window + Otwórz pakiet w nowym oknie + + + Show this packet in a separate window. + Otwórz pakiet w nowym oknie. + + + Show the linked packet in a separate window. + Otórz podlinkowany pakiet w nowym oknie. + + + Auto Scroll in Li&ve Capture + Automatyczne przewijanie podczas przechwytywania + + + Automatically scroll to the last packet during a live capture. + Automatyczne przewijanie podczas przechwytywania + + + Expert Information + Informacja ekspercka + + + Show expert notifications + Pokazuj notyfikacje eksperckie + + + Add an expression to the display filter. + Dodaj wyrażenie do filtru wyświetlania. + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + Start "REGISTER_STAT_GROUP_UNSORTED" + + + No ANSI statistics registered + No tools registered + Brak zarejestrowanych statystyk ANSI + + + Resolved Addresses + Rozwiązane adresy + + + Show each table of resolved addresses as copyable text. + Pokaż elementy tablicy rozwiązanych adresów jako tekst do skopiowania. + + + Color &1 + Kolor &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + Oznacz zaznaczoną konwersację kolorem. + + + Color &2 + Kolor &2 + + + Color &3 + Kolor &3 + + + Color &4 + Kolor &4 + + + Color &5 + Kolor &5 + + + Color &6 + Kolor &6 + + + Color &7 + Kolor &7 + + + Color &8 + Kolor &8 + + + Color &9 + Kolor &9 + + + Color 1&0 + Kolor &10 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + Utwórz nową regułę kolorowania bazując na tym polu. + + + Reset Colorization + Resetuj kolorowanie + + + Reset colorized conversations. + Resetuj kolorowanie konwersacji. + + + RTP Stream Analysis + Analiza strumienia RTP + + + Edit Resolved Name + Edytuj Rozwiązane Nazwy + + + Manually edit a name resolution entry. + Edytuj ręcznie wpis rozwiązywania nazw. + + + Enable and disable specific protocols + Włącz lub wyłącz wybrane protokoły + + + before quitting + przed wyjściem + + + Save packets before merging? + Zapisać pakiety przed scaleniem? + + + A temporary capture file can't be merged. + Tymczasowy plik przechwytywania nie może zostać scalony. + + + Save changes in "%1" before merging? + Zapisać zmiany w "%1" przed scaleniem? + + + Changes must be saved before the files can be merged. + Zmiany muszą być zapisane przed próbą scalenia plików. + + + Invalid Display Filter + Niepoprawny filtr + + + Invalid Read Filter + Niepoprawny filtr wczytywania + + + The filter expression %1 isn't a valid read filter. (%2). + Postać filtru wczytywania %1 nie jest poprawna. (%2). + + + before importing a capture + before importing a new capture + przed zaimportowaniem nowego pliku przechwytywania + + + Unable to export to "%1". + Nie można wyeksportować do "%1". + + + You cannot export packets to the current capture file. + Nie można wyeksportować pakietów do aktualnego pliku. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + Czy chcesz zapisać zrobione zmiany %1? + + + Your captured packets will be lost if you don't save them. + Twoje przechwycone pakiety zostaną utracone jeśli nie zapiszesz ich. + + + Do you want to save the changes you've made to the capture file "%1"%2? + Czy chcesz zapisać zmiany, które zrobiłeś do pliku przechytywania "%1"%2? + + + Your changes will be lost if you don't save them. + Twoje zmiany zostaną utracone jeśli nie zapiszesz ich. + + + Check for Updates… + + + + Unable to drop files during capture. + + + + Unknown file type returned by merge dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + Unknown file type returned by export dialog. + + + + Do you want to stop the capture and save the captured packets%1? + Czy chcesz zatrzymać przechwytywanie i zapisać przechwycone pakiety%1? + + + Do you want to save the captured packets%1? + Czy chcesz zapisać przechwycone pakiety%1? + + + Save before Continue + Zapisz przed kontynuowaniem + + + Stop and Save + Zatrzymaj i zapisz + + + Stop and Quit &without Saving + Stop and Quit without Saving + Zatrzymaj i wyjdź bez zapisy&wania + + + Quit &without Saving + Quit without Saving + Wyjdź bez zapisy&wania + + + There is no "rtp.ssrc" field in this version of Wireshark. + + + + Please select an RTPv2 packet with an SSRC value + + + + SSRC value not found. + + + + Show or hide the toolbar + + + + Continue &without Saving + Continue without Saving + Kontynuuj bez zapisy&wania + + + Stop and Continue &without Saving + Stop and Continue without Saving + Zatrzymaj i kontynuuj bez zapisy&wania + + + The Wireshark Network Analyzer + Analizator Wireshark + + + Capturing from %1 + Przechwytywanie z %1 + + + before opening another file + przed otwarciem nowego pliku + + + Merging files. + + + + %1: %2 + %1: %2 + + + Clear Menu + Wyczyść Menu + + + before closing the file + przed zapisaniem pliku + + + Export Selected Packet Bytes + Eksportuj zaznaczony bajty pakietu + + + No Keys + Brak kluczy + + + Raw data (*.bin *.dat *.raw);;All Files ( + Surowe dane (*.bin *.dat *.raw);;Dowolny plik ( + + + Couldn't copy text. Try another item. + Nie można skopiować tekstu. Spróbuj inną pozycję. + + + Are you sure you want to remove all packet comments? + + + + Unable to build conversation filter. + Nie można stworzyć filtru konwersacji. + + + before reloading the file + przed przeładowaniem pliku + + + Error compiling filter for this conversation. + Błąd kompilacji filtru dla tej konwersacji. + + + No previous/next packet in conversation. + Brak poprzedniego i następnego pakietu w konwersacji. + + + No interface selected. + + + + Saving %1… + + + + Configure all extcaps before start of capture. + + + + Invalid capture filter. + + + + (empty comment) + placeholder for empty comment + + + + Add New Comment… + + + + Edit "%1" + edit packet comment + + + + Delete "%1" + delete packet comment + + + + Delete packet comments + + + + Delete comments from %n packet(s) + + + + + + + + before starting a new capture + przed wystartowaniem nowego przechwytywania + + + before reloading Lua plugins + + + + Please wait while Wireshark is initializing… + + + + before updating + + + + There are no TLS Session Keys to save. + + + + Export TLS Session Keys (%Ln key(s)) + + + + + + + + TLS Session Keys (*.keys *.txt);;All Files ( + + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + + + + column + + + + item + + + + The "%1" column already exists. + + + + The "%1" column already exists as "%2". + + + + RTP packet search failed + + + + No Interface Selected. + + + + before restarting the capture + przed restartem przechwytywania + + + Wiki Page for %1 + Strona Wiki dla %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Strona Wiki Wiresharka jest zarządzania przez społeczność.</p><p>Strona którą chcesz wczytać może być wspaniała, niekompletna, błędna lub nie istnieć.</p><p>Kontynuować wczytywanie strony Wiki?</p> + + + Loading + Wczytywanie + + + Reloading + Przeładowanie + + + Rescanning + Przeskanowanie + + + + WlanStatisticsDialog + + Wireless LAN Statistics + Statystyki bezprzewodowych sieci LAN + + + Channel + Kanał + + + SSID + SSID + + + Percent Packets + Pakiety [%] + + + Percent Retry + Próby [%] + + + Probe Reqs + Probe Reqs + + + Probe Resp + Probe Resp + + + Auths + Uwierzytelnienia + + + Retry + Próby + + + Deauths + Cofnięcia uwierzytelnień + + + Other + Inne + + + diff --git a/ui/qt/wireshark_ru.ts b/ui/qt/wireshark_ru.ts new file mode 100644 index 00000000..d37b3054 --- /dev/null +++ b/ui/qt/wireshark_ru.ts @@ -0,0 +1,14812 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + О Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Анализатор сетевых протоколов</span> + + + Copy the version information to the clipboard + Копировать данные о версии в буфер обмена + + + Copy to Clipboard + Копировать в буфер обмена + + + Authors + Авторы + + + Search Authors + Поиск по авторам + + + Folders + Папки + + + Filter by path + Фильтр по пути размещения + + + Plugins + Модули + + + No plugins found. + Подключаемые модули не найдены. + + + Search Plugins + Поиск по модулям + + + Filter by type: + Фильтр по типу: + + + Keyboard Shortcuts + Комбинации клавиш + + + Search Shortcuts + Поиск по комбинациям клавиш + + + Acknowledgments + Благодарности + + + License + Лицензия + + + The directory does not exist + Каталог отсутствует + + + Should the directory %1 be created? + Создать каталог %1? + + + The directory could not be created + Создание каталога невозможно + + + The directory %1 could not be created. + Не удалось создать каталог %1. + + + Show in Finder + Показать в модуле поиска + + + Show in Folder + Показать в папке + + + Copy + Копировать + + + Copy Row(s) + + + + + + + + + AddressEditorFrame + + Frame + Кадр + + + Name Resolution Preferences… + Name Resolution Preferences... + Параметры разрешения имён… + + + Address: + Адрес: + + + Name: + Имя: + + + Can't assign %1 to %2. + Невозможно назначить %1 на %2. + + + + AdvancedPrefsModel + + Name + Имя + + + Status + Состояние + + + Type + Тип + + + Value + Значение + + + + ApplyLineEdit + + Apply changes + Применить изменения + + + + AuthorListModel + + Name + Имя + + + Email + Эл. почта + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Атрибуты сервера Bluetooth ATT + + + Handle + Дескриптор + + + UUID + UUID + + + UUID Name + Имя UUID + + + All Interfaces + Все интерфейсы + + + All Devices + Все устройства + + + Remove duplicates + Удалять дублирование + + + Copy Cell + Копировать ячейку + + + Copy Rows + Копировать строки + + + Copy All + Копировать все + + + Save as image + Сохранить как изображение + + + Mark/Unmark Row + Установить/снять отметку строки + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Установить/снять отметку ячейки + + + Save Table Image + Сохранить изображение таблицы + + + PNG Image (*.png) + Изображение PNG (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + Устройство Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Имя + + + Class of Device + Класс устройства + + + LMP Version + Версия LMP + + + LMP Subversion + Подверсия LMP + + + Manufacturer + Производитель + + + HCI Version + Версия HCI + + + HCI Revision + Ревизия HCI + + + Scan + Сканировать + + + Authentication + Аутентификация + + + Encryption + Шифрование + + + ACL MTU + ACL MTU + + + ACL Total Packets + Всего Пакетов ACL + + + SCO MTU + SCO MTU + + + SCO Total Packets + Всего пакетов SCO + + + LE ACL MTU + LE ACL MTU + + + LE ACL Total Packets + Всего пакетов LE ACL + + + LE ISO MTU + LE ISO MTU + + + LE ISO Total Packets + Всего пакетов LE ISO + + + Inquiry Mode + Режим запросов + + + Page Timeout + Время окончания обработки страницы + + + Simple Pairing Mode + Режим простого сопряжения + + + Voice Setting + Голосовая настройка + + + Value + Значение + + + Changes + Изменения + + + %1 changes + изменений: %1 + + + Copy Cell + Копировать ячейку + + + Copy Rows + Копировать строки + + + Copy All + Копировать всё + + + Save as image + Сохранить как изображение + + + Mark/Unmark Row + Установить/снять отметку строки + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Установить/снять отметку ячейки + + + Unknown + Неизвестно + + + Bluetooth Device - %1%2 + Устройство Bluetooth — %1%2 + + + enabled + включено + + + disabled + отключено + + + %1 ms (%2 slots) + %1 мс (%2 слоты) + + + Save Table Image + Сохранить изображение таблицы + + + PNG Image (*.png) + Изображение PNG (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Устройства Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Имя + + + LMP Version + Версия LMP + + + LMP Subversion + Подверсия LMP + + + Manufacturer + Производитель + + + HCI Version + Версия HCI + + + HCI Revision + Ревизия HCI + + + Is Local Adapter + Локальный aдаптер + + + All Interfaces + Все интерфейсы + + + Show information steps + Показать информацию о шагах + + + %1 items; Right click for more option; Double click for device details + Элементов: %1; Правый щелчок для дополнительных опций; Двойной щелчок для вывода подробной информации об устройстве + + + Copy Cell + Копировать ячейку + + + Copy Rows + Копировать строки + + + Copy All + Копировать всё + + + Save as image + Сохранить как изображение + + + Mark/Unmark Row + Установить/снять отметку строки + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Установить/снять отметку ячейки + + + true + истина + + + Save Table Image + Сохранить изображение таблицы + + + PNG Image (*.png) + Изображение PNG (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Общие сведения о хост-контроллере Bluetooth (HCI) + + + Name + Имя + + + OGF + OGF + + + OCF + OCF + + + Opcode + Код операции + + + Event + Событие + + + Subevent + Подчинённое событие + + + Status + Статус + + + Reason + Причина + + + Hardware Error + Ошибка оборудования + + + Occurrence + Число использования + + + Link Control Commands + Команды управления соединениями + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Команды политики соединения + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Команды контроллера и базового уровня + + + 0x03 + 0x03 + + + Informational Parameters + Информационные параметры + + + 0x04 + 0x04 + + + Status Parameters + Параметры статуса + + + 0x05 + 0x05 + + + Testing Commands + Команды тестирования + + + 0x06 + 0x06 + + + LE Controller Commands + Команды контроллера LE + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + Команды тестирования лого Bluetooth + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Команды, определяемые поставщиком + + + 0x3F + 0x3F + + + Unknown OGF + Неизвестный OGF + + + Events + События + + + Hardware Errors + Ошибки оборудования + + + Results filter: + Фильтр результатов: + + + Display filter: + Фильтр отображения: + + + All Interfaces + Все итерфейсы + + + All Adapters + Все адаптеры + + + Copy Cell + Копировать ячейку + + + Copy Rows + Копировать строки + + + Copy All + Копировать всё + + + Save as image + Сохранить как изображение + + + Mark/Unmark Row + Установить/снять отметку строки + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Установить/снять отметку ячейки + + + Unknown + Неизвестно + + + Adapter %1 + Адаптер %1 + + + Frame %1 + Кадр %1 + + + Pending + Ожидание + + + Save Table Image + Сохранить изображение таблицы + + + PNG Image (*.png) + Изображение PNG (*.png) + + + + ByteViewTab + + Packet bytes + Байты пакета + + + + ByteViewText + + Allow hover highlighting + Включить подсвечивание при наведении курсора + + + Show bytes as hexadecimal + Показать байты в шестнадцатеричной системе + + + …as decimal + + + + …as octal + + + + …as bits + …как биты + + + Show text based on packet + Показать привязанный к пакету текст + + + …as ASCII + …в виде ASCII + + + …as EBCDIC + …в виде EBCDIC + + + + CaptureFile + + [closing] + [закрытие] + + + [closed] + [закрыт] + + + + CaptureFileDialog + + This capture file contains comments. + Данный файл захвата содержит комментарии. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + Выбранный формат файла не поддерживает комментарии. Сохранить захваченные данные в формате, поддерживающем комментарии, или сохранить в выбранном формате без комментариев? + + + Discard comments and save + Сохранить без комментариев + + + Save in another format + Сохранить в другом формате + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + Ни один формат, в котором можно сохранить файл, не поддерживает комментарии. Сохранить без комментариев в выбранном формате? + + + All Files ( + Все файлы ( + + + All Capture Files + Все файлы захвата + + + Format: + Формат: + + + Size: + Размер: + + + Start / elapsed: + Старт / прошло: + + + Automatically detect file type + Автоматически определить тип файла + + + Prepend packets + Добавить пакеты к началу файла + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Вставить пакеты из выбранного файла перед текущим файлом. Временные метки пакета будут проигнорированы. + + + Merge chronologically + Объединить хронологически + + + Insert packets in chronological order. + Вставить пакеты в хронологическом порядке. + + + Append packets + Добавить пакеты в конец файла + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Вставить пакеты из выбранного файла после текущего файла. Временные метки пакета будут проигнорированы. + + + Read filter: + Фильтр чтения: + + + Compress with g&zip + Сжать с помощью g&zip + + + Open Capture File + Wireshark: Open Capture File + Открытие файла захвата + + + Save Capture File As + Wireshark: Save Capture File As + Сохранение файла захвата в выбранном формате + + + Save as: + Сохранить как: + + + Export Specified Packets + Wireshark: Export Specified Packets + Экспорт указанных пакетов + + + Export as: + Экспортировать как: + + + Merge Capture File + Wireshark: Merge Capture File + Объединение файла захвата + + + Unknown file type returned by save as dialog. + Диалог «Сохранение файла захвата в выбранном формате» вернул неизвестный тип файла. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Сообщите об этой ошибке Wireshark на https://gitlab.com/wireshark/wireshark/-/issues. + + + directory + каталог + + + unknown file format + неизвестный формат файла + + + error opening file + ошибка при открытии файла + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + + + + + + + %1, timed out at %Ln data record(s) + + + + + + + + %1, %Ln data record(s) + + + + + + + + unknown + неизвестно + + + + CaptureFilePropertiesDialog + + Details + Подробности + + + Capture file comments + Комментарии в файле захвата + + + Refresh + Обновить + + + Copy To Clipboard + Копировать в буфер обмена + + + Save Comments + Сохранить комментарии + + + Capture File Properties + Свойства файла захвата + + + Unknown + Неизвестно + + + File + Файл + + + Name + Имя + + + Length + Длина + + + Hash (SHA256) + Хэш (SHA256) + + + Hash (SHA1) + Хэш (SHA1) + + + Format + Формат + + + Encapsulation + Инкапсуляция + + + Snapshot length + Длина снимка состояния + + + Time + Время + + + First packet + Первый пакет + + + Last packet + Последний пакет + + + Elapsed + Прошло + + + Section %1 + Раздел %1 + + + Capture + Захват + + + Hardware + Оборудование + + + OS + ОС + + + Application + Приложение + + + Interfaces + Интерфейсы + + + Interface + Интерфейс + + + Dropped packets + Потерянные пакеты + + + Capture filter + Фильтр захвата + + + Link type + Тип соединения + + + Packet size limit (snaplen) + Ограничение на размер пакета (snaplen) + + + none + нет + + + %1 bytes + %1 Б + + + Statistics + Статистика + + + Measurement + Измеряемые параметры + + + Captured + Захвачено + + + Displayed + Показано + + + Marked + Отмечено + + + Packets + Пакеты + + + Time span, s + Временной промежуток, с + + + Average pps + В среднем, пакетов/с + + + Average packet size, B + Средний размер пакета, Б + + + Bytes + Байты + + + Average bytes/s + В среднем байт/с + + + Average bits/s + В среднем бит/с + + + Section Comment + Комментарий к разделу + + + Packet Comments + Комментарии к пакету + + + <p>Frame %1: + <p>Кадр %1: + + + Created by Wireshark %1 + + + Создано в Wireshark %1 + + + + + + CaptureFilterCombo + + Capture filter selector + Селектор фильтра захвата + + + + CaptureFilterEdit + + Capture filter entry + Запись фильтра захвата + + + Manage saved bookmarks. + Управление сохранёнными закладками. + + + Apply this filter string to the display. + Применить эту строку фильтра к отображению на экране. + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + Выбрано несколько фильтров. Переопределите их здесь или оставьте поле пустым. + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>У выбранных интерфейсов имеются различные фильтры захвата. Фильтр, введённый в этом поле, будет использоваться вместо них. Если оставить поле пустым, то фильтры останутся без изменений.</p> + + + Enter a capture filter %1 + Введите фильтр захвата %1 + + + Save this filter + Сохранить этот фильтр + + + Remove this filter + Удалить этот фильтр + + + Manage Capture Filters + Управлять фильтрами захвата + + + + CaptureInfoDialog + + Capture Information + Информация о захвате + + + Stop Capture + Остановить захват + + + %1 packets, %2:%3:%4 + Пакетов: %1, %2:%3:%4 + + + + CaptureInfoModel + + Other + Другой + + + + CaptureOptionsDialog + + Input + Ввод + + + Interface + Интерфейс + + + Traffic + Трафик + + + Link-layer Header + Заголовок канального уровня + + + Promiscuous + Смешанный + + + Snaplen (B) + Длина снимка (Б) + + + Buffer (MB) + Буфер (Мбайт) + + + Monitor Mode + Режим мониторинга + + + Capture Filter + Фильтр захвата + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Данный параметр может быть очень полезен. Обычно сетевая плата захватывает только трафик, отправляемый на её сетевой адрес. Включение данного параметра позволяет захватывать весь трафик, который может &quot;видеть&quot; сетевая карта. Смотрите раздел «Вопросы и ответы» для получения дополнительной информации о захвате пакетов из коммутируемой сети.</p></body></html> + + + Enable promiscuous mode on all interfaces + Включить смешанный режим для всех интерфейсов + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + Показывать и скрывать интерфейсы, добавлять комментарии, управлять соединениями и удалёнными интерфейсами. + + + Manage Interfaces… + Управлять интерфейсами… + + + Capture filter for selected interfaces: + Фильтр захвата для выбранных интерфейсов: + + + Compile BPFs + Компилировать BPF + + + Output + Вывод + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>Введите имя файла для записи захваченных данных. По умолчанию используется временный файл.</p></body></html> + + + Capture to a permanent file + Выполнение захвата в постоянный файл + + + File: + Файл: + + + Browse… + Обзор… + + + Output format: + Формат вывода: + + + pcapng + pcapng + + + pcap + pcap + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>Вместо одного файла захвата будет создаваться несколько файлов.</p><p>Имена файлов будут содержать возрастающее числовое значение и время начала захвата.</p><p>ВНИМАНИЕ: При включении параметра НЕОБХОДИМО выбрать хотя бы один из критериев для создания нового файла.</p></body></html> + + + Create a new file automatically… + Автоматически создавать новый файл… + + + after + после + + + Switch to the next file after the specified number of packets have been captured. + Переходить на следующий файл при достижении указанного числа захваченных пакетов. + + + packets + пакета (пакетов) + + + Switch to the next file after the file size exceeds the specified file size. + Переходить на следующий файл при достижении указанного размера файла. + + + kilobytes + килобайт + + + megabytes + мегабайт + + + gigabytes + гигабайт + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + Переходить на следующий файл при превышении указанного интервала времени захвата в текущий файл. + + + seconds + сек + + + minutes + мин + + + hours + час + + + when time is a multiple of + когда время кратно + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + Переходить на следующий файл при достижении указанного кратного интервала времени. +Например, если указать 1 час, то каждый час будет создаваться новый файл. + + + compression + сжатие + + + None + Нет + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>При переходе записи данных на следующий файл превышение указанного здесь числа файлов приведёт к удалению самого старого из них.</p></body></html> + + + Use a ring buffer with + Использовать кольцевой буфер из + + + files + файла (файлов) + + + Options + Параметры + + + Display Options + Параметры отображения + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>При использовании данного параметра захваченные пакеты будут сразу же отображаться на главном экране. Обратите внимание: скорость захвата уменьшится, что может привести к возрастанию числа потерянных пакетов.</p></body></html> + + + Update list of packets in real-time + Обновлять список пакетов в реальном времени + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>Включение данного параметра приведёт к автоматической прокрутке &quot;Списка пакетов&quot; к последнему захваченному пакету при включении параметра &quot;Обновлять список пакетов в реальном времени&quot;.</p></body></html> + + + Automatically scroll during live capture + Автопрокрутка при захвате в реальном времени + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>Показывать диалоговое окно с информацей о захвате в процессе его выполнения.</p></body></html> + + + Show capture information during live capture + Показывать информацию о захвате в реальном времени + + + Name Resolution + Разрешение имён + + + Perform MAC layer name resolution while capturing. + Выполнять разрешение имён на уровне MAC-адресов во время захвата. + + + Resolve MAC addresses + Разрешение MAC-адресов + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>Выполнять разрешение имён сетевого уровня во время захвата.</p></body></html> + + + Resolve network names + Разрешение сетевых имён + + + Perform transport layer name resolution while capturing. + Выполнять разрешение имён транспортного уровня во время захвата. + + + Resolve transport names + Разрешение имён транспортного уровня + + + Stop capture automatically after… + Автоматически останавливать захват после… + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>Останавливать захват при достижении указанного числа захваченных пакетов.</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + Останавливать захват при достижении указанного числа захваченных пакетов. + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>Останавливать захват после создания указанного числа файлов.</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>Остановить захват при достижении указанного объёма захваченных данных.</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + Остановить захват при достижении указанного объёма захваченных данных. + + + Stop capturing after the specified amount of time has passed. + Остановить захват при достижении указанного значения времени. + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>Здесь можно указать временный каталог для безымянных файлов захвата.</p></body></html> + + + Directory for temporary files + Каталог для временных файлов + + + Capture Options + Параметры захвата + + + Start + Старт + + + Leave blank to use a temporary file + Оставьте поле пустым для использования временного файла + + + Specify a Capture File + Выбор файла захвата + + + Specify temporary directory + Укажите временный каталог + + + %1: %2 + %1: %2 + + + Addresses + Адреса + + + Address + Адрес + + + no addresses + нет адресов + + + Error + Ошибка + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + Несколько файлов: Указан слишком большой размер файла. Размер файла не может превышать 2 ГиБ. + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + Несколько файлов: Не указано имя файла захвата. Для использования нескольких файлов необходимо указать имя файла. + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + Несколько файлов: Не указано ограничение по размеру файла. Для каждого файла необходимо указать его размер, интервал или число пакетов. + + + + CapturePreferencesFrame + + Frame + Кадр + + + Default interface + Интерфейс по умолчанию + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Данный параметр может быть очень полезен. Обычно сетевая плата захватывает только трафик, отправляемый на её сетевой адрес. Включение данного параметра позволяет захватывать весь трафик, который может &quot;видеть&quot; сетевая карта. Смотрите раздел «Вопросы и ответы» для получения дополнительной информации о захвате пакетов из коммутируемой сети.</p></body></html> + + + Capture packets in promiscuous mode + Захватывать пакеты в смешанном режиме + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Выполнять захват пакетов в формате файла захвата следующего поколения «pcapng».</p></body></html> + + + Capture packets in pcapng format + Захватывать пакеты в формате pcapng + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Обновлять список пакетов во время захвата. Это может привести к потере пакетов в высокоскоростных сетях.</p></body></html> + + + Update list of packets in real time + Обновлять список пакетов в реальном времени + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + Не загружать интерфейсы при старте + + + Disable external capture interfaces + Отключить внешние интерфейсы захвата + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + символ «@» будет игнорироваться. + + + + ColoringRulesDialog + + Dialog + Диалоговое окно + + + <small><i>A hint.</i></small> + <small><i>Подсказка.</i></small> + + + Add a new coloring rule. + Добавить новое правило выделения цветом. + + + Delete this coloring rule. + Удалить данное правило выделения цветом. + + + Duplicate this coloring rule. + Дублировать это правило выделения цветом. + + + Clear all coloring rules. + Очистить все правила выделения цветом. + + + Set the foreground color for this rule. + Задать цвет переднего плана для этого правила. + + + Foreground + Передний план + + + Set the background color for this rule. + Задать цвет фона для этого правила. + + + Background + Фон + + + Set the display filter using this rule. + Установить фильтр отображения с использованием этого правила. + + + Apply as filter + Применить в качестве фильтра + + + Select a file and add its filters to the end of the list. + Выберите файл и добавьте его фильтры в конец списка. + + + Save filters in a file. + Сохранить фильтры в файле. + + + Coloring Rules %1 + Правила выделения цветом %1 + + + Import… + Импорт… + + + Export… + Экспорт… + + + Copy coloring rules from another profile. + Копировать правила выделения цветом из другого профиля. + + + Open + Открыть + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Дважды щёлкнуть для изменения. Перетащить для перемещения. Правила обрабатываются по порядку, пока не будет найдено совпадение. + + + Import Coloring Rules + Импорт правил выделения цветом + + + Export %1 Coloring Rules + Экспорт правил выделения цветом: %1 + + + + ColoringRulesModel + + New coloring rule + Новое правило выделения цветом + + + Unable to save coloring rules: %1 + Невозможно сохранить правила выделения цветом: %1 + + + Name + Имя + + + Filter + Фильтр + + + + ColumnEditorFrame + + Frame + Кадр + + + Title: + Title + Заголовок: + + + Type: + Type + Тип: + + + Fields: + Fields + Поля: + + + Occurrence: + Occurrence + Вхождение: + + + Resolve Names: + Разрешать имена: + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + <html><head/><p>Отображать человекочитаемые строки вместо необработанных значений полей. Может применяться только к дополнительным столбцам с полями, содержащими строки значений.</p></body></html> + + + Missing fields. + Отсутствуют поля. + + + Invalid fields. + Недопустимые поля. + + + Invalid occurrence value. + Неверное значение экземпляра. + + + + ColumnListModel + + Displayed + Показано + + + Title + Заголовок + + + Type + Тип + + + Fields + Поля + + + Field Occurrence + Вхождение поля + + + Resolved + Разрешено + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + <html>Показывать человекочитаемые строки вместо необработанных значений полей. Может применяться только к дополнительным столбцам с полями, содержащими строки значений.</html> + + + New Column + Новый столбец + + + + ColumnPreferencesFrame + + Frame + Кадр + + + Add a new column + Добавить новый столбец + + + Delete selected column + Удалить выбранный столбец + + + Show displayed columns only + Показывать только отображенные столбцы + + + Reset all changes + Сбросить все изменения + + + + CompiledFilterOutput + + Compiled Filter Output + Вывод скомпилированного фильтра + + + Copy + Копировать + + + Copy filter text to the clipboard. + Копировать текст фильтра в буфер обмена. + + + + ConversationDataModel + + Address A + Адрес A + + + Port A + Порт A + + + Address B + Адрес B + + + Port B + Порт B + + + Packets + Пакеты + + + Bytes + Байт + + + Stream ID + ИД потока + + + Packets A + Пакеты A + + + Bytes A + Байты A + + + Packets B + Пакеты B + + + Bytes B + Байты B + + + Abs Start + Абс. время начала + + + Rel Start + Отн. время начала + + + Duration + Продолжительность + + + Bits/s A + Бит/с A + + + Bits/s B + Бит/с B + + + Total Packets + Всего пакетов + + + Percent Filtered + Отфильтровано в процентах + + + + ConversationDialog + + Follow Stream… + Отслеживать поток… + + + Follow a TCP or UDP stream. + Отслеживание потока TCP или UDP. + + + Graph… + График… + + + Graph a TCP conversation. + Построение графика диалога TCP. + + + + ConversationHashTablesDialog + + Dialog + Диалоговое окно + + + Conversation Hash Tables + Хэш-таблицы диалогов + + + + CopyFromProfileButton + + Copy from + Копировать из + + + Copy entries from another profile. + Копировать записи из другого профиля. + + + System default + Системный по умолчанию + + + + CredentialsDialog + + Wireshark - Credentials + Wireshark — Учётные данные + + + Credentials + Учётные данные + + + + CredentialsModel + + Click to select the packet + Щёлкните для выбора пакета + + + Click to select the packet with username + Щёлкните для выбора пакета с именем пользователя + + + Username not available + Имя пользователя недоступно + + + Packet No. + № пакета + + + Protocol + Протокол + + + Username + Имя пользователя + + + Additional Info + Дополнительные сведения + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + Копировать байты в виде шестнадцатеричных значений + ASCII-дамп + + + Copy packet bytes as a hex and ASCII dump. + Копировать байты пакета в виде шестнадцатеричных значений и ASCII-дампа. + + + …as Hex Dump + …в виде шестнадцатеричного дампа + + + Copy packet bytes as a hex dump. + Копировать байты пакета в виде шестнадцатеричного дампа. + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + …в виде шестнадцатеричного потока + + + Copy packet bytes as a stream of hex. + Копировать байты пакета в виде шестнадцатеричного потока. + + + …as a Base64 String + …в виде строки Base64 + + + Copy packet bytes as a base64 encoded string. + Копировать байты пакета в виде строки в кодировке base64. + + + Copy packet bytes as application/octet-stream MIME data. + Копировать байты пакета в виде данных MIME-типа application/octet-stream. + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + Изменить метод разбора для протокола. + + + Remove this dissection behavior. + Удалить текущий метод разбора. + + + Copy this dissection behavior. + Копировать текущий метод разбора. + + + Clear all dissection behaviors. + Удалить все методы разбора. + + + Decode As… + Декодировать как… + + + Open + Открыть + + + + DecodeAsModel + + Match using this field + Подобрать по этому полю + + + Change behavior when the field matches this value + Изменять метод разбора при совпадении поля с этим значением + + + Field value type (and base, if Integer) + Тип значения поля (а также основание, если тип соответствует целому числу) + + + Current"Decode As" behavior + «Декодировать как» — текущий метод разбора + + + Default "Decode As" behavior + «Декодировать как» — метод разбора по умолчанию + + + String + Строка + + + Integer, base + Целое число с основанием + + + unknown + неизвестно + + + <none> + <none> + + + GUID + GUID + + + Field + Поле + + + Value + Значение + + + Type + Тип + + + Default + По умолчанию + + + Current + Текущий + + + + DisplayFilterCombo + + Display filter selector + Селектор фильтра отображения + + + Select from previously used filters. + Выбрать из использовавшихся ранее фильтров. + + + + DisplayFilterEdit + + Display filter entry + Запись фильтра отображения + + + Manage saved bookmarks. + Управление сохранёнными закладками. + + + Display Filter Expression… + Выражение фильтра отображения… + + + Apply a display filter %1 <%2/> + Примените фильтр отображения %1 <%2/> + + + Enter a display filter %1 + Введите фильтр отображения %1 + + + Clear display filter + Очистить фильтр отображения + + + Apply display filter + Применить фильтр отображения + + + Left align buttons + Выравнять кнопки по левой границе + + + Apply a read filter %1 + Примените фильтр чтения %1 + + + Current filter: %1 + Текущий фильтр: %1 + + + Invalid filter: + Недопустимый фильтр: + + + Save this filter + Сохранить этот фильтр + + + Remove this filter + Удалить этот фильтр + + + Manage Display Filters + Управлять фильтрами отображения + + + Filter Button Preferences... + Параметры кнопок фильтра… + + + + DisplayFilterExpressionDialog + + Dialog + Диалоговое окно + + + Select a field to start building a display filter. + Выберите поле для начала создания фильтра отображения. + + + Field Name + Имя поля + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Поиск по списку имён полей.</p></body></html> + + + Search: + Поиск: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>Отношения позволяют ограничить содержимое полей в пределах указанных величин. Каждое отношение выполняет следующие действия:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Проверка соответствия любому пакету, содержащему указанное поле </p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, и т.д.</span></p></td><td><p>Сравнение поля с указанным значением.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Проверка поля по вхождению подстроки (contains) или соответствия регулярному выражению (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Сравнение поля с указанным набором значений</p></td></tr></table></body></html> + + + + + Relation + Отношение + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + По умолчанию сравнения порядка и отношений «contains», «matches» и «in» истинны при совпадении любого значения. Использование числового показателя «Все» позволяет применить тест ко всем значениям, содержащимся в кадре. + + + Quantifier + Числовой показатель + + + Any + Любые + + + All + Все + + + Match against this value. + Сопоставить с этим значением. + + + Value + Значение + + + If the field you have selected has a known set of valid values they will be listed here. + Если выбранное поле имеет известный набор допустимых значений, то они будут перечислены здесь. + + + Predefined Values + Предустановленные значения + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + Если выбранное поле покрывает диапазон байтов (например, при выборе протокола), сопоставление здесь может быть ограничено диапазоном байтов. + + + Range (offset:length) + Диапазон (смещение:длина) + + + No display filter + Фильтр отображения отсутствует + + + <small><i>A hint.</i></small> + <small><i>Подсказка</i></small> + + + Display Filter Expression + Выражение фильтра отображения + + + Select a field name to get started + Для начала выберите имя поля + + + Click OK to insert this filter + Нажмите OK для вставки этого фильтра + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + Диалоговое окно + + + Search: + Поиск: + + + Dissector Tables + Таблицы диссекторов + + + + DissectorTablesProxyModel + + Table Type + Тип таблицы + + + String + Строка + + + Dissector Description + Описание диссектора + + + Integer + Целое + + + Protocol + Протокол + + + Short Name + Короткое имя + + + Table Name + Имя таблицы + + + Selector Name + Имя селектора + + + + EnabledProtocolsDialog + + Dialog + Диалоговое окно + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>При отключении протокола перестают отображаться протоколы верхнего уровня</i></small> + + + Search: + Поиск: + + + in + в + + + Enable All + Включить всё + + + Disable All + Выключить всё + + + Invert + Инвертировать + + + Enabled Protocols + Включённые протоколы + + + Everywhere + Везде + + + Only Protocols + Только протоколы + + + Only Description + Только описание + + + Only enabled protocols + Только включённые протоколы + + + Only disabled protocols + Только отключённые протоколы + + + any protocol + любой протокол + + + non-heuristic protocols + неэвристические протоколы + + + heuristic protocols + эвристические протоколы + + + + EnabledProtocolsModel + + Protocol + Протокол + + + Description + Описание + + + + EndpointDataModel + + Address + Адрес + + + Port + Порт + + + Packets + Пакеты + + + Bytes + Байты + + + Tx Packets + Пакетов отправлено + + + Tx Bytes + Байтов отправлено + + + Rx Packets + Пакетов получено + + + Rx Bytes + Байтов получено + + + Country + Страна + + + City + Город + + + Latitude + Широта + + + Longitude + Долгота + + + AS Number + Номер AS + + + AS Organization + Oрганизация AS + + + Total Packets + Пакетов всего + + + Percent Filtered + Процент отфильтрованных + + + + EndpointDialog + + Map + Карта + + + Draw IPv4 or IPv6 endpoints on a map. + Вывести конечные точки IPv4 или IPv6 на карте. + + + Open in browser + Открыть в браузере + + + Save As… + Сохранить как… + + + Map file error + Ошибка файла карты + + + Save Endpoints Map + Сохранить карту конечных точек + + + Failed to save map file %1. + Не удалось сохранить файл карты %1. + + + + EthernetAddressModel + + Type + Тип + + + Name + Имя + + + Address + Адрес + + + All entries + Все записи + + + Hosts + Узлы + + + Ethernet Addresses + Адреса Ethernet + + + Ethernet Manufacturers + Производители Ethernet + + + Ethernet Well-Known Addresses + Известные Ethernet-адреса + + + + ExpertInfoDialog + + Dialog + Диалоговое окно + + + <small><i>A hint.</i></small> + <small><i>Подсказка</i></small> + + + Limit to Display Filter + Ограничить по фильтру отображения + + + Group by summary + Группировать по итоговым результатам + + + Search expert summaries. + Искать экспертные сводки. + + + Search: + Поиск: + + + Show… + Show... + Отображать… + + + Error + Ошибка + + + Show error packets. + Отображать пакеты с ошибками. + + + Warning + Предупреждение + + + Show warning packets. + Отображать пакеты с предупреждениями. + + + Note + Замечание + + + Show note packets. + Отображать пакеты с замечаниями. + + + Chat + Чат + + + Show chat packets. + Отображать пакеты чата. + + + Comment + Комментарий + + + Show comment packets. + Отображать пакеты с комментариями. + + + Expert Information + Экспертная информация + + + Collapse All + Свернуть всё + + + Expand All + Развернуть всё + + + Capture file closed. + Файл захвата закрыт. + + + No display filter + Фильтр отображения отсутствует + + + No display filter set. + Фильтр отображения не задан. + + + Limit information to "%1". + Ограничить информацию фильтром «%1». + + + Display filter: "%1" + Фильтр отображения: «%1» + + + + ExpertInfoProxyModel + + Packet + Пакет + + + Severity + Серьёзность + + + Summary + Сводка + + + Group + Группа + + + Protocol + Протокол + + + Count + Количество + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Экспорт результатов разбора пакетов + + + Export As: + Export as: + Экспортировать как: + + + Plain text (*.txt) + Обычный текст (*.txt) + + + Comma Separated Values - summary (*.csv) + Comma Separated Values — сводка (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML — сводка (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PDML — сведения (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + Массивы C — байты (*.c, *.h) + + + + ExportObjectDialog + + Dialog + Диалоговое окно + + + Content Type: + Тип cодержимого: + + + Searching for objects + Поиск объектов + + + Text Filter: + Фильтр текста: + + + Only display entries containing this string + Показывать только записи, содержащие эту строку + + + Preview + Предварительный просмотр + + + All Content-Types + Все типы содержимого + + + Export + Экспорт + + + %1 object list + Список объектов %1 + + + Save Object As… + Сохранение объекта как… + + + Save All Objects In… + Сохранение всех объектов в… + + + + ExportObjectModel + + Packet + Пакет + + + Hostname + Имя узла + + + Content Type + Тип содержимого + + + Size + Размер + + + Filename + Имя файла + + + + ExportPDUDialog + + Dialog + Диалоговое окно + + + Display filter: + Фильтр отображения: + + + + ExtArgSelector + + Reload data + Загрузить данные повторно + + + + ExtcapArgumentFileSelection + + Clear + Очистить + + + All Files ( + Все Файлы ( + + + Open File + Открыть файл + + + Select File + Выбрать файл + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + Параметры интерфейса + + + Start + Старт + + + Save + Сохранить + + + Default + По умолчанию + + + Restore default value of the item + Восстановить значение элемента по умолчанию + + + Extcap Help cannot be found + Невозможно найти справку Extcap + + + The help for the extcap interface %1 cannot be found. Given file: %2 + Справка extcap для интерфейса %1 не найдена. Указан файл: %2 + + + Save parameter(s) on capture start + Сохранять параметры при старте захвата + + + + FieldFilterEdit + + Display filter entry + Запись фильтра отображения + + + Enter a field %1 + Ввести поле %1 + + + Invalid filter: + Недопустимый фильтр: + + + + FileSetDialog + + Dialog + Диалоговое окно + + + Directory: + Каталог: + + + No files in Set + Файлы в наборе отсутствуют + + + No capture loaded + Данные захвата не загружены + + + %Ln File(s) in Set + %1 File%2 in Set + + + + + + + + + FilesetEntryModel + + Open this capture file + Открыть этот файл захвата + + + Filename + Имя файла + + + Created + Создан + + + Modified + Изменён + + + Size + Размер + + + + FilterAction + + Selected + Выбранное + + + Not Selected + НЕ выбранное + + + …and Selected + …И выбранное + + + …or Selected + …ИЛИ выбранное + + + …and not Selected + …И НЕ выбранное + + + …or not Selected + …ИЛИ НЕ выбранное + + + + FilterDialog + + Dialog + Диалоговое окно + + + Create a new filter. + Создать новый фильтр. + + + Remove this filter. + Remove this profile. + Удалить этот фильтр. + + + Copy this filter. + Copy this profile. + Копировать этот фильтр. + + + Capture Filters + Фильтры захвата + + + Display Filters + Фильтры отображения + + + Open + Открыть + + + New capture filter + This text is automatically filled in when a new filter is created + Новый фильтр захвата + + + New display filter + This text is automatically filled in when a new filter is created + Новый фильтр отображения + + + + FilterExpressionFrame + + Frame + Кадр + + + Filter Buttons Preferences… + Параметры кнопок фильтра… + + + Label: + Подпись: + + + Enter a description for the filter button + Введите описание кнопки фильтра + + + Filter: + Фильтр: + + + Enter a filter expression to be applied + Введите применяемое выражение фильтра + + + Comment: + Комментарий: + + + Enter a comment for the filter button + Введите комментарий для кнопки фильтра + + + Missing label. + Отсутствует подпись. + + + Missing filter expression. + Отсутствует выражение фильтра. + + + Invalid filter expression. + Недопустимое выражение фильтра. + + + + FilterExpressionToolBar + + Filter Button Preferences... + Параметры кнопки фильтра… + + + Edit + Редактировать + + + Disable + Отключить + + + Remove + Удалить + + + + FilterListModel + + Filter Name + Имя фильтра + + + Filter Expression + Выражение фильтра + + + + FindLineEdit + + Textual Find + Текстовый поиск + + + Regular Expression Find + Поиск по регулярному выражению + + + + FirewallRulesDialog + + Create rules for + Создать правила для + + + Inbound + Входящие + + + Deny + Запретить + + + Firewall ACL Rules + ACL-правила межсетевого экрана + + + Copy + Копировать + + + IPv4 source address. + IPv4-адрес источника. + + + IPv4 destination address. + IPv4-адрес назначения. + + + Source port. + Порт источника. + + + Destination port. + Порт назначения. + + + IPv4 source address and port. + IPv4-адрес и порт источника. + + + IPv4 destination address and port. + IPv4-адрес и порт назначения. + + + MAC source address. + MAC-адрес источника. + + + MAC destination address. + MAC-адрес назначения. + + + Text file (*.txt);;All Files ( + Текстовый файл (*.txt);;Все файлы ( + + + Warning + Предупреждение + + + Unable to save %1 + Не удалось сохранить %1 + + + + FolderListModel + + "File" dialogs + Диалоговые окна открытия файлов + + + capture files + Файлы захвата + + + Temp + Временные файлы + + + untitled capture files + Безымянные файлы захвата + + + Personal configuration + Персональная конфигурация + + + Global configuration + Глобальная конфигурация + + + dfilters, preferences, ethers, … + dfilters, preferences, ethers, … + + + dfilters, preferences, manuf, … + dfilters, preferences, manuf, … + + + System + Конфигурация системы + + + ethers, ipxnets + ethers, ipxnets + + + Program + Программы + + + program files + Файлы программ + + + Personal Plugins + Пользовательские подключаемые модули + + + binary plugins + Исполняемые файлы подключаемых модулей + + + Global Plugins + Глобальные подключаемые модули + + + Personal Lua Plugins + Пользовательские подключаемые модули Lua + + + Global Lua Plugins + Глобальные подключаемые модули Lua + + + Lua scripts + Сценарии Lua + + + Personal Extcap path + Пользовательский путь Extcap + + + external capture (extcap) plugins + модули внешненго захвата (extcap) + + + Global Extcap path + Глобальный путь к Extcap + + + MaxMind DB path + Путь MaxMind DB + + + MaxMind DB database search path + Путь поиска базы данных MaxMind DB + + + MIB/PIB path + Путь MIB/PIB + + + SMI MIB/PIB search path + Путь поиска SMI MIB/PIB + + + macOS Extras + Дополнения для macOS + + + Extra macOS packages + Дополнительные пакеты для macOS + + + Name + Имя + + + Location + Расположение + + + Typical Files + Размещаемые файлы + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + Фильтровать этот поток + + + Print + Печать + + + ASCII + ASCII + + + C Arrays + Массивы C + + + EBCDIC + EBCDIC + + + Hex Dump + Шестнадцатеричный дамп + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + Необработанный + + + Save as… + Сохранить как… + + + Back + Назад + + + Packet %1. + Пакет %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + + + + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + + + + + + + %Ln turn(s). + + + + + + + + Click to select. + Щёлкните для выбора. + + + Regex Find: + Поиск по регулярному выражению: + + + No capture file. + Файл захвата отсутствует. + + + Please make sure you have a capture file opened. + Убедитесь в том, что файл захвата открыт. + + + Error following stream. + Ошибка отслеживания потока. + + + Capture file invalid. + Недопустимый файл захвата. + + + Please make sure you have a %1 packet selected. + Убедитесь в том, что пакет %1 выбран. + + + %1 stream not found on the selected packet. + Поток %1 не найден в выбранном пакете. + + + Entire conversation (%1) + Весь диалог (%1) + + + Follow %1 Stream (%2) + Отслеживание потока %1 (%2) + + + Error creating filter for this stream. + Ошибка создания фильтра для этого потока. + + + Save Stream Content As… + Сохранить содержимое потока как… + + + [Stream output truncated] + [Вывод потока усечён] + + + %Ln total stream(s). + + + + + + + + Max sub stream ID for the selected stream: %Ln + + + + + + + + File closed. + Файл закрыт. + + + Follow Stream + Отслеживание потока + + + Hint. + Подсказка. + + + Show data as + Show and save data as + Отобразить данные в виде + + + Stream + Поток + + + Substream + Подпоток + + + Find: + Найти: + + + Find &Next + Найти &далее + + + + FontColorPreferencesFrame + + Frame + Кадр + + + Main window font: + Шрифт главного окна: + + + Select Font + Выбрать шрифт + + + Colors: + Цвета: + + + System Default + По умолчанию + + + Solid + Заливка + + + Sample ignored packet text + Образец проигнорированного текста пакета + + + Sample marked packet text + Образец отмеченного текста пакета + + + Sample active selected item + Образец выбранного активного элемента + + + Style: + Стиль: + + + Gradient + Градиент + + + Sample inactive selected item + Образец выбранного неактивного элемента + + + Sample "Follow Stream" client text + Образец клиентского текста «Отслеживание потока» + + + Sample "Follow Stream" server text + Образец текста сервера «Отслеживание потока» + + + Sample valid filter + Образец допустимого фильтра + + + Sample invalid filter + Образец недопустимого фильтра + + + Sample warning filter + Sample deprecated filter + Образец фильтра предупреждения + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + Любя, съешь щипцы, — вздохнёт мэр, — кайф жгуч. + + + Lazy badgers move unique waxy jellyfish packets + Съешь же ещё этих мягких французских булок да выпей чаю. + + + Font + Шрифт + + + + FunnelStringDialog + + Dialog + Диалог + + + + FunnelTextDialog + + Dialog + Диалог + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>Введите текст или регулярное выражение, которое будет выделено выше.</p></body></html> + + + Highlight: + Выделение: + + + + GsmMapSummaryDialog + + Dialog + Диалог + + + GSM MAP Summary + Сводка GSM MAP + + + File + Файл + + + Name + Имя + + + Length + Длина + + + Format + Формат + + + Snapshot length + Длина снимка состояния + + + Data + Данные + + + First packet + Первый пакет + + + Last packet + Последний пакет + + + Elapsed + Прошло + + + Packets + Пакеты + + + Invokes + Вызовы + + + Total number of Invokes + Общее число вызовов + + + Average number of Invokes per second + Среднее число вызовов в секунду + + + Total number of bytes for Invokes + Общее число байт для вызовов + + + Average number of bytes per Invoke + Среднее число байт на вызов + + + Return Results + Возвращаемые результаты + + + Total number of Return Results + Общее число возвращённых результатов + + + Average number of Return Results per second + Среднее число возвращённых результатов в секунду + + + Total number of bytes for Return Results + Общее число байт для возвращённых результатов + + + Average number of bytes per Return Result + Среднее число байт на возвращённый результат + + + Totals + Всего + + + Total number of GSM MAP messages + Общее число сообщений GSM MAP + + + Average number of GSM MAP messages per second + Среднее число сообщений GSM MAP в секунду + + + Total number of bytes for GSM MAP messages + Общее число байт в сообщениях GSM MAP + + + Average number of bytes per GSM MAP message + Среднее число байт в сообщении GSM MAP + + + + IOConsoleDialog + + Dialog + + + + Enter code + + + + Evaluate + + + + Clear + Очистить + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + Диалог + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Практичные и удобные комбинации клавиш для экономии рабочего времени</h3> +<table><tbody> + +<tr><th>+</th><td>Увеличить масштаб</td></th> +<tr><th>-</th><td>Уменьшить масштаб</td></th> +<tr><th>x</th><td>Увеличить масштаб по оси X</td></th> +<tr><th>X</th><td>Уменьшить масштаб по оси X</td></th> +<tr><th>y</th><td>Увеличить масштаб по оси Y</td></th> +<tr><th>Y</th><td>Уменьшить масштаб по оси Y</td></th> +<tr><th>0</th><td>Вернуть график к исходному состоянию</td></th> + +<tr><th>→</th><td>Переместить вправо на 10 пикселов</td></th> +<tr><th>←</th><td>Переместить влево на 10 пикселов</td></th> +<tr><th>↑</th><td>Переместить вверх на 10 пикселов</td></th> +<tr><th>↓</th><td>Переместить вниз на 10 пикселов</td></th> +<tr><th><i>Shift+</i>→</th><td>Переместить вправо на 1 пиксел</td></th> +<tr><th><i>Shift+</i>←</th><td>Переместить влево на 1 пиксел</td></th> +<tr><th><i>Shift+</i>↑</th><td>Переместить вверх на 1 пиксел</td></th> +<tr><th><i>Shift+</i>↓</th><td>Переместить вниз на 1 пиксел</td></th> + +<tr><th>g</th><td>Перейти к пакету под курсором</td></th> + +<tr><th>z</th><td>Переключить режим работы мыши на перетаскивание или изменение масштаба</td></th> +<tr><th>t</th><td>Переключение на время начала захвата или сеанса</td></th> +<tr><th>Пробел</th><td>Переключать перекрестья</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + Удалить этот график. + + + Add a new graph. + Добавить новый график. + + + Duplicate this graph. + Дублировать этот график. + + + Clear all graphs. + Очистить все графики. + + + Move this graph upwards. + Переместить этот график вверх. + + + Move this graph downwards. + Переместить этот график вниз. + + + Mouse + Мышь + + + Drag using the mouse button. + Перетаскивание с помощью кнопки мыши. + + + drags + перетаскивание + + + Select using the mouse button. + Выделять кнопкой мыши. + + + zooms + масштаб + + + Interval + Интервал + + + Time of day + Время суток + + + Log scale + Логарифмический масштаб + + + Automatic update + + + + Enable legend + + + + Reset + Сброс + + + Reset Graph + Сбросить график + + + Reset the graph to its initial state. + Сбросить график в исходное состояние. + + + 0 + 0 + + + Zoom In + Увеличить масштаб + + + + + + + + + Zoom Out + Уменьшить масштаб + + + - + - + + + Move Up 10 Pixels + Переместить вверх на 10 пикселов + + + Up + Вверх + + + Move Left 10 Pixels + Переместить влево на 10 пикселов + + + Left + Влево + + + Move Right 10 Pixels + Переместить вправо на 10 пикселов + + + Right + Вправо + + + Move Down 10 Pixels + Переместить вниз на 10 пикселов + + + Down + Вниз + + + Move Up 1 Pixel + Переместить вверх на 1 пиксел + + + Shift+Up + Shift+Вверх + + + Move Left 1 Pixel + Переместить влево на 1 пиксел + + + Shift+Left + Shift+Влево + + + Move Right 1 Pixel + Переместить вправо на 1 пиксел + + + Shift+Right + Shift+Вправо + + + Move Down 1 Pixel + Переместить вниз на 1 пиксел + + + Move down 1 Pixel + Move down 1 pixel + Переместить вниз на 1 пиксел + + + Shift+Down + Shift+Вниз + + + Go To Packet Under Cursor + Перейти к пакету под курсором + + + Go to packet currently under the cursor + Перейти к пакету, находящемуся под курсором + + + G + G + + + Drag / Zoom + Перетаскивание / масштаб + + + Toggle mouse drag / zoom behavior + Переключить режим работы мыши на перетаскивание или изменение масштаба + + + Z + Z + + + Capture / Session Time Origin + Время начала захвата / сеанс + + + Toggle capture / session time origin + Переключение на время начала захвата или сеанса + + + T + T + + + Crosshairs + Перекрестия + + + Toggle crosshairs + Переключение перекрестия + + + Space + Пробел + + + Zoom In X Axis + Увеличить масштаб по оси X + + + X + X + + + Zoom Out X Axis + Уменьшить масштаб по оси X + + + Shift+X + Shift+X + + + Zoom In Y Axis + Увеличить масштаб по оси Y + + + Y + Y + + + Zoom Out Y Axis + Уменьшить масштаб по оси Y + + + Shift+Y + Shift+Y + + + 1 sec + 1 сек + + + 10 sec + 10 сек + + + 1 min + 1 мин + + + 10 min + 10 мин + + + Time (s) + Время (с) + + + I/O Graphs + Графики ввода/вывода + + + Save As… + Сохранить как… + + + Copy + Копировать + + + Copy graphs from another profile. + Копировать графики из другого профиля. + + + 1 ms + 1 мс + + + 2 ms + 2 мс + + + 5 ms + 5 мс + + + 10 ms + 10 мс + + + 20 ms + 20 мс + + + 50 ms + 50 мс + + + 100 ms + 100 мс + + + 200 ms + 200 мс + + + 500 ms + 500 мс + + + 2 sec + 2 сек + + + 5 sec + 5 сек + + + Wireshark I/O Graphs: %1 + Графики ввода/вывода Wireshark: %1 + + + Filtered packets + Отфильтрованные пакеты + + + Filtered events + + + + All Packets + Все пакеты + + + TCP Errors + Ошибки TCP + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + Поместите указатель мыши над графиком для получения дополнительной информации. + + + No packets in interval + Пакеты в интервале отсутствуют + + + No events in interval + + + + Click to select packet + Щёлкните для выбора пакета + + + Packet + Пакет + + + Click to select event + + + + Event + Событие + + + %1 (%2s%3). + %1 (%2с%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Разместить в соответствии с масштабом, x = %1 до %2, y = %3 до %4 + + + Unable to select range. + Не удалось выбрать диапазон. + + + Click to select a portion of the graph. + Щёлкните для выбора участка графика. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + Comma Separated Values (*.csv) + + + Save Graph As… + Сохранить график как… + + + + Iax2AnalysisDialog + + Dialog + Диалог + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Вперёд</span></p><p><span style=" font-size:medium; font-weight:600;">Назад</span></p></body></html> + + + Forward + Вперёд + + + Packet + Пакет + + + Delta (ms) + Дельта (мс) + + + Jitter (ms) + Джиттер (мс) + + + Bandwidth + Полоса пропускания + + + Status + Статус + + + Length + Длина + + + Reverse + Назад + + + Graph + График + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>Показать или скрыть значения джиттера прямого потока.</p></body></html> + + + Forward Jitter + Джиттер прямого потока + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>Показать или скрыть значения отклонения прямого потока.</p></body></html> + + + Forward Difference + Отклонение прямого потока + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>Показать или скрыть значения джиттера обратного потока.</p></body></html> + + + Reverse Jitter + Джиттер обратного потока + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>Показать или скрыть значения отклонения обратного потока.</p></body></html> + + + Reverse Difference + Отклонение обратного потока + + + <small><i>A hint.</i></small> + <small><i>Подсказка.</i></small> + + + Audio + Звук + + + Save the audio data for both channels. + Сохранить аудиоданные для обоих каналов. + + + Forward Stream Audio + Звук прямого потока + + + Save the forward stream audio data. + Сохранить аудиоданные прямого потока. + + + Reverse Stream Audio + Звук обратного потока + + + Save the reverse stream audio data. + Сохранить аудиоданные обратного потока. + + + CSV + CSV + + + Save both tables as CSV. + Сохранить обе таблицы в формате CSV. + + + Forward Stream CSV + Прямой поток в CSV + + + Save the forward table as CSV. + Сохранить таблицу прямого потока в формате CSV. + + + Reverse Stream CSV + Обратный поток в CSV + + + Save the reverse table as CSV. + Сохранить таблицу обратного потока в формате CSV. + + + Save Graph + Сохранить график + + + Save the graph image. + Сохранить изображение графика. + + + Go to Packet + Перейти к пакету + + + Select the corresponding packet in the packet list. + Выбрать соответствующий пакет в списке пакетов. + + + G + G + + + Next Problem Packet + Следующий проблемный пакет + + + Go to the next problem packet + Перейти к следующему проблемному пакету + + + N + N + + + IAX2 Stream Analysis + Анализ потока IAX2 + + + Unable to save RTP data. + Не удалось сохранить данные RTP. + + + Please select an IAX2 packet. + Выберите пакет IAX2. + + + G: Go to packet, N: Next problem packet + G: Перейти к пакету, N: Следующий проблемный пакет + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Сохранить график как… + + + Can't save in a file: Wrong length of captured packets. + Не удалось сохранить в файл: неверная длина захваченных пакетов. + + + Can't save in a file: File I/O problem. + Не удалось сохранить в файл: проблемы ввода/вывода файла. + + + Save forward stream audio + Сохранить звук прямого потока + + + Save reverse stream audio + Сохранить звук обратного потока + + + Save audio + Сохранить звук + + + Sun Audio (*.au) + Sun Audio (*.au) + + + ;;Raw (*.raw) + ;;Raw (*.raw) + + + Warning + Предупреждение + + + Unable to save in that format + Не удалось сохранить звук в этом формате + + + Unable to save %1 + Не удалось сохранить %1 + + + Saving %1… + Сохранение %1… + + + Analyzing IAX2 + Анализ IAX2 + + + Save forward stream CSV + Сохранение прямого потока в CSV + + + Save reverse stream CSV + Сохранение обратного потока в CSV + + + Save CSV + Сохранение в CSV + + + Comma-separated values (*.csv) + Comma-separated values (*.csv) + + + + ImportTextDialog + + File: + Файл: + + + Set name of text file to import + Задать имя текстового файла для импорта + + + Browse for text file to import + Выбор текстового файла для импорта + + + Browse… + Browse... + Обзор… + + + Hex Dump + Шестнадцатеричный дамп + + + Import a standard hex dump as exported by Wireshark + Импорт стандартного шестнадцатеричного дампа, экспортируемого Wireshark + + + Offsets in the text file are in octal notation + Смещения в текстовом файле представлены в восьмеричной записи + + + Octal + Восьмеричные + + + Offsets: + Смещения: + + + Offsets in the text file are in hexadecimal notation + Смещения в текстовом файле указаны в шестнадцатеричной записи + + + Hexadecimal + Шестнадцатеричные + + + Offsets in the text file are in decimal notation + Смещения в текстовом файле представлены в десятичной записи + + + Decimal + Десятичные + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>Выполнять дополнительную обработку для обнаружения начала ASCII-текста в конце строки hex+ASCII, даже если внешне они похожи на шестнадцатеричные байты.</p><p>Включение этого параметра не рекомендуется в случае отсутствия в шестнадцатеричном дампе ASCII-текста.</p></body></html> + + + ASCII identification: + Идентификация ASCII: + + + Regular Expression + Регулярное выражение + + + Import a file formatted according to a custom regular expression + Импорт файла, отформатированного в соответствии с произвольным регулярным выражением + + + Packet format regular expression + Регулярное выражение для описания формата пакета + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + <html><head/><body><p>Perl-совместимое регулярное выражение для захвата пакета в файл с включением именованных групп, позволяющих идентифицировать импортируемые данные. Якоря ^ и $ также могут использоваться для сопоставления местоположения до или после перевода строк </p><p>Обязательна для включения только группа data, но также поддерживаются группы time, dir и seqno.</p><p>Флаги регулярного выражения: DUPNAMES, MULTILINE и NOEMPTY</p></body></html> + + + This is regexHintLabel, it will be set to default_regex_hint + Это подпись regexHintLabel, которой будет присвоено значение default_regex_hint + + + Data encoding: + Кодирование данных: + + + How data is encoded + Каким образом закодированы данные + + + encodingRegexExample + encodingRegexExample + + + List of characters indicating incoming packets + Список символов, обозначающих входящие пакеты + + + iI< + iI< + + + List of characters indicating outgoing packets + Список символов, обозначающих исходящие пакеты + + + oO> + oO> + + + Timestamp format: + Формат метки времени: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Наличие в файле информации о направлении пакета (входящий или исходящий). + + + Direction indication: + Направление пакета: + + + ExportPDU + ЭкспортPDU + + + IP version: + Версия IP: + + + Interface name: + Имя интерфейса: + + + The name of the interface to write to the import capture file + Имя интерфейса для записи в файл импорта захвата + + + Fake IF, Import from Hex Dump + Fake IF, импорт из шестнадцатеричного дампа + + + Maximum frame length: + Максимальная длина кадра: + + + Encapsulation + Инкапсуляция + + + The text file has no offset + Текстовый файл не имеет смещения + + + None + Не используются + + + <small><i>recommended regex:</small></i> + <small><i>Рекомендуемое регулярное выражение:</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + Формат разбора временных меток в текстовом файле (например, %H:%M:%S.). Спецификаторы формата основаны на strptime(3) + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + <html><head/><body><p>Формат для разбора временных меток в текстовом файле (например, %H:%M:%S.%f).</p><p>Спецификаторы формата основаны на strptime(3) с добавлением %f для долей секунд. Точность параметра %f определяется его длиной.</p></body></html> + + + %H:%M:%S.%f + %H:%M:%S.%f + + + timestampExampleLabel + timestampExampleLabel + + + Encapsulation Type: + Тип инкапсуляции: + + + Encapsulation type of the frames in the import capture file + Тип инкапсуляции кадров в импортируемом файле захвата + + + Prefix each frame with an Ethernet and IP header + Предварять каждый кадр заголовком Ethernet и IP + + + IP + IP + + + Prefix each frame with an Ethernet, IP and UDP header + Предварять каждый кадр заголовком Ethernet, IP и UDP + + + Prefix each frame with an Ethernet, IP and TCP header + Предварять каждый кадр заголовком Ethernet, IP и TCP + + + Prefix each frame with an Ethernet, IP and SCTP header + Предварять каждый кадр заголовком Ethernet, IP и SCTP + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + Предварять каждый кадр заголовком Ethernet, IP и SCTP (DATA) + + + Source address: + Адрес источника: + + + Destination address: + Адрес назначения: + + + Dissector + Диссектор + + + The IP protocol ID for each frame + ИД протокола IP для каждого кадра + + + The IP source address for each frame + IP-адрес источника для каждого кадра + + + The IP destination address for each frame + IP-адрес назначения для каждого кадра + + + The UDP, TCP or SCTP source port for each frame + Порт-источник UDP, TCP или SCTP для каждого кадра + + + The SCTP DATA payload protocol identifier for each frame + Идентификатор полезных данных протокола SCTP DATA для каждого кадра + + + The UDP, TCP or SCTP destination port for each frame + Порт назначения UDP, TCP или SCTP для каждого кадра + + + Prefix each frame with an Ethernet header + Предварять каждый кадр заголовком Ethernet + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + Протокол (десятичная система): + + + Leave frames unchanged + Оставлять кадры без изменений + + + No dummy header + Без фиктивного заголовка + + + Tag: + Тег: + + + UDP + UDP + + + Source port: + Порт источника: + + + The Ethertype value of each frame + Значение Ethertype каждого кадра + + + TCP + TCP + + + The SCTP verification tag for each frame + Тег верификации SCTP для каждого кадра + + + Destination port: + Порт назначения: + + + Ethertype (hex): + Ethertype (шестнадцатеричная система): + + + SCTP (Data) + SCTP (данные) + + + The dissector to use for each frame + Диссектор, применяемый для каждого пакета + + + The IP Version to use for the dummy IP header + Версия IP для фиктивного заголовка IP + + + The maximum size of the frames to write to the import capture file (max 256kiB) + Максимальный размер кадров для записи в импортируемый файл захвата (максимум 256 кбайт) + + + Supported fields are data, dir, time, seqno + Поддерживаемые поля: data, dir, time, seqno + + + Missing capturing group data (use (? + Отсутствуют данные группы захвата (use (? + + + Import From Hex Dump + Импорт из шестнадцатеричного дампа + + + Import + Импорт + + + Import Text File + Импорт текстового файла + + + + InterfaceFrame + + Frame + Кадр + + + Wired + Проводное + + + AirPCAP + AirPCAP + + + Pipe + По каналу + + + STDIN + STDIN + + + Bluetooth + Bluetooth + + + Wireless + Беспроводное + + + Dial-Up + Коммутируемое + + + USB + USB + + + External Capture + Внешний захват + + + Virtual + Виртуальное + + + Remote interfaces + Удалённые интерфейсы + + + Show hidden interfaces + Показать скрытые интерфейсы + + + External capture interfaces disabled. + Внешние интерфейсы захвата отключены. + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + <p>Локальные интерфейсы недоступны, так как не установлен драйвер захвата пакетов.</p><p>Для устранения этой проблемы необходимо установить <a href="https://npcap.com/">Npcap</a>.</p> + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + <p>Локальные интерфейсы недоступны, так как не загружен драйвер захвата пакетов.</p><p>Для устранения этой проблемы необходимо запустить команду <pre>net start npcap</pre>, если установлен Npcap, или командой <pre>net start npf</pre>, если установлен WinPcap. Обе команды должны быть запущены от имени «Администратора».</p> + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + <p>Отсутствуют разрешения для захвата на локальных интерфейсах.</p><p> Для устранения этой проблемы необходимо <a href="file://%1">установить ChmodBPF</a>.</p> + + + You don't have permission to capture on local interfaces. + Отсутствуют разрешения для захвата пакетов на локальных интерфейсах. + + + No interfaces found. + Интерфейсы не найдены. + + + Interfaces not loaded (due to preference). Go to Capture + Интерфейсы не загружены (в соответствии с параметрами настройки). Перейти к захвату + + + Start capture + Начать захват + + + Hide Interface + Скрыть интерфейс + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + Отображаемые интерфейсы отсутствуют. Cкрыто интерфейсов: %1. + + + + InterfaceToolbar + + Frame + Кадр + + + Select interface + Выбрать интерфейс + + + Interface + Интерфейс + + + + InterfaceToolbarLineEdit + + Apply changes + Применить изменения + + + + InterfaceTreeModel + + Show + Показать + + + Friendly Name + Понятное имя + + + Interface Name + Имя интерфейса + + + No interfaces found. + Интерфейсы не найдены. + + + This version of Wireshark was built without packet capture support. + Эта версия Wireshark было собрана без поддержки захвата пакетов. + + + Local Pipe Path + Путь к локальному каналу + + + Comment + Комментарий + + + Link-Layer Header + Заголовок канального уровня + + + Promiscuous + Смешанный + + + Snaplen (B) + Длина снимка (Байт) + + + Buffer (MB) + Буфер (МБайт) + + + Monitor Mode + Режим монитора + + + Capture Filter + Фильтр захвата + + + Addresses + Адреса + + + Address + Адрес + + + Extcap interface: %1 + Интерфейс Extcap: %1 + + + No addresses + Нет адресов + + + No capture filter + Нет фильтра захвата + + + Capture filter + Фильтр захвата + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + Статистика транспорта LBT-RM + + + Sources + Источники + + + Address/Transport + Адрес/Транспорт + + + Data frames + Кадры с данными + + + Data bytes + Байты данных + + + Data frames/bytes + Кадры/байты данных + + + Data rate + Скорость передачи данных + + + RX data frames + Кадры данных RX + + + RX data bytes + Байты данных RX + + + RX data frames/bytes + Кадры/байты данных RX + + + RX data rate + Скорость передачи данных RX + + + NCF frames + Кадры NCF + + + NCF count + Количество NCF + + + NCF bytes + Байты NCF + + + NCF frames/bytes + Кадры/байты NCF + + + NCF count/bytes + NCF количество/байты + + + NCF frames/count + NCF кадры/количество + + + NCF frames/count/bytes + NCF кадры/количество/байты + + + NCF rate + Скорость NCF + + + SM frames + Кадры SM + + + SM bytes + Байты SM + + + SM frames/bytes + Кадры/байты SM + + + SM rate + Скорость SM + + + Show + Показать + + + Data + Данные + + + RX Data + Данные RX + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + номера последовательностей для транспорта + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Количество + + + Frame + Кадр + + + SQN/Reason + SQN/Причина + + + Receivers + Приёмники + + + NAK frames + Кадры NAK + + + NAK count + Количество NAK + + + NAK bytes + Байты NAK + + + NAK rate + Скорость NAK + + + NAK sequence numbers for transport + номера последовательностей NAK для транспорта + + + Display filter: + Фильтр отображения: + + + Regenerate statistics using this display filter + Регенерировать статистику с использованием этого фильтра отображения + + + Apply + Применить + + + Copy as CSV + Копировать в виде CSV + + + Copy the tree as CSV + Копировать дерево в виде CSV + + + Copy as YAML + Копировать в виде YAML + + + Copy the tree as YAML + Копировать дерево в виде YAML + + + Show the data frames column + Показать столбец кадров данных + + + Show the data bytes column + Показать столбец байтов данных + + + Show the data frames/bytes column + Показать столбец кадров/байтов данных + + + Show the RX data frames column + Показать столбец кадров данных RX + + + Show the RX data bytes column + Показать столбец байтов данных RX + + + Show the RX data frames/bytes column + Показать столбец кадров/байтов данных RX + + + Show the NCF frames column + Показать столбец кадров NCF + + + Show the NCF bytes column + Показать столбец байтов NCF + + + Show the NCF count column + Показать столбец подсчёта NCF + + + Show the data rate column + Показать столбец скорости передачи данных + + + Show the RX data rate column + Показать столбец скорости передачи данных RX + + + Show the NCF frames/bytes column + Показать столбец кадров/байтов NCF + + + Show the NCF count/bytes column + Показать столбец количество/байтов NCF + + + Show the NCF frames/count column + Показать столбец кадров/количество NCF + + + Show the NCF frames/count/bytes column + Показать столбец кадров/количество/байтов NCF + + + Show the NCF rate column + Показать столбец скорости передачи данных NCF + + + Show the SM frames column + Показать столбец кадров SM + + + Show the SM bytes column + Показать столбец байтов SM + + + Show the SM frames/bytes column + Показать столбец кадров/байтов SM + + + Show the SM rate column + Показать столбец скорости передачи данных SM + + + Auto-resize columns to content + Автоматически менять размер столбцов для полного размещения содержимого + + + Resize columns to content size + Изменить размер столбцов для полного размещения содержимого + + + LBT-RM Statistics failed to attach to tap + Сбой cтатистики LBT-RM при подключении к перехватчику данных + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + Статистика транспорта LBT-RU + + + Sources + Источники + + + Address/Transport/Client + Адрес/Транспорт/Клиент + + + Data frames + Кадры с данными + + + Data bytes + Байты данных + + + Data frames/bytes + Кадры/байты данных + + + Data rate + Скорость передачи данных + + + RX data frames + Кадры данных RX + + + RX data bytes + Байты данных RX + + + RX data frames/bytes + Кадры/байты данных RX + + + RX data rate + Скорость передачи данных RX + + + NCF frames + Кадры NCF + + + NCF count + Количество NCF + + + NCF bytes + Байты NCF + + + NCF frames/count + Кадры/количество NCF + + + NCF frames/bytes + Кадры/байты NCF + + + NCF count/bytes + Количество/байты NCF + + + NCF frames/count/bytes + Кадры/количество/байты NCF + + + NCF rate + Скорость NCF + + + SM frames + Кадры SM + + + SM bytes + Байты SM + + + SM frames/bytes + Кадры/байты SM + + + SM rate + Скорость SM + + + RST frames + Кадры RST + + + RST bytes + Байты RST + + + RST frames/bytes + Кадры/байты RST + + + RST rate + Скорость RST + + + Show + Показать + + + Data SQN + Данные SQN + + + RX Data SQN + RX Данные SQN + + + NCF SQN + NCF SQN + + + SM SQN + SM SQN + + + RST reason + Причина RST + + + details for transport + подробные сведения для транспорта + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Количество + + + Frame + Кадр + + + Reason + Причина + + + SQN/Reason + SQN/Причина + + + Receivers + Приёмники + + + Address/Transport + Адрес/Транспорт + + + NAK frames + Кадры NAK + + + NAK count + Количество NAK + + + NAK bytes + Байты NAK + + + NAK frames/count + Кадры/количество NAK + + + NAK count/bytes + Количество/байты NAK + + + NAK frames/bytes + Кадры/байты NAK + + + NAK frames/count/bytes + Кадры/количество/байты NAK + + + NAK rate + Скорость NAK + + + ACK frames + Кадры ACK + + + ACK bytes + Байты ACK + + + ACK frames/bytes + Кадры/байты ACK + + + ACK rate + Скорость ACK + + + CREQ frames + Кадры CREQ + + + CREQ bytes + Байты CREQ + + + CREQ frames/bytes + Кадры/байты CREQ + + + CREQ rate + Скорость CREQ + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + Запрос CREQ + + + Display filter: + Фильтр отображения: + + + Regenerate statistics using this display filter + Регенерировать статистику с использованием этого фильтра отображения + + + Apply + Применить + + + Copy as CSV + Копировать в виде CSV + + + Copy the tree as CSV + Копировать дерево в виде CSV + + + Copy as YAML + Копировать в виде YAML + + + Copy the tree as YAML + Копировать дерево в виде YAML + + + Show the data frames column + Показать столбец кадров данных + + + Show the data bytes column + Показать столбец байтов данных + + + Show the data frames/bytes column + Показать столбец кадров/байтов данных + + + Show the data rate column + Показать столбец скорости передачи данных + + + Show the RX data frames column + Показать столбец кадров данных RX + + + Show the RX data bytes column + Показать столбец байтов данных RX + + + Show the RX data frames/bytes column + Показать столбец кадров/байтов данных RX + + + Show the RX data rate column + Показать столбец скорости передачи данных RX + + + Show the NCF frames column + Показать столбец кадров NCF + + + Show the NCF count column + Показать столбец подсчёта NCF + + + Show the NCF bytes column + Показать столбец байтов NCF + + + Show the NCF frames/bytes column + Показать столбец кадров/байтов NCF + + + Show the NCF count/bytes column + Показать столбец количества/байтов NCF + + + Show the NCF frames/count column + Показать столбец кадров/количества NCF + + + Show the NCF frames/count/bytes column + Показать столбец кадров/количества/байтов NCF + + + Show the SM frames column + Показать столбец кадров SM + + + Show the SM bytes column + Показать столбец байтов SM + + + Show the SM frames/bytes column + Показать столбец кадров/байтов SM + + + Show the SM rate column + Показать столбец скорости передачи данных SM + + + Show the RST frames column + Показать столбец кадров RST + + + Show the RST bytes column + Показать столбец байтов RST + + + Show the RST frames/bytes column + Показать столбец кадров/байтов RST + + + Show the RST rate column + Показать столбец скорости передачи данных RST + + + Show the NAK frames column + Показать столбец кадров NAK + + + Show the NAK count column + Показать столбец количества NAK + + + Show the NAK bytes column + Показать столбец байтов NAK + + + Show the NAK frames/count column + Показать столбец кадров/количества NAK + + + Show the NAK count/bytes column + Показать столбец количества/байтов NAK + + + Show the NAK frames/bytes column + Показать столбец кадров/байтов NAK + + + Show the NAK frames/count/bytes column + Показать столбец кадров/количества/байтов NAK + + + Show the NAK rate column + Показать столбец скорости передачи данных NAK + + + Show the ACK frames column + Показать столбец кадров ACK + + + Show the ACK bytes column + Показать столбец байтов ACK + + + Show the ACK frames/bytes column + Показать столбец кадров/байтов ACK + + + Show the ACK rate column + Показать столбец скорости передачи данных ACK + + + Show the CREQ frames column + Показать столбец кадров CREQ + + + Show the CREQ bytes column + Показать столбец байтов CREQ + + + Show the CREQ frames/bytes column + Показать столбец кадров/байтов CREQ + + + Show the CREQ rate column + Показать столбец скорости передачи данных CREQ + + + Auto-resize columns to content + Автоматически изменять размер столбцов для полного размещения содержимого + + + Resize columns to content size + Изменить размер столбцов для полного размещения содержимого + + + Show the NCF rate column + Показать столбец скорости передачи данных NCF + + + LBT-RU Statistics failed to attach to tap + Сбой статистики LBT-RU при подключении к перехватчику данных + + + + LBMStreamDialog + + Dialog + Диалоговое окно + + + Stream + Поток + + + Endpoint A + Конечная точка A + + + Endpoint B + Конечная точка B + + + Messages + Сообщения + + + Bytes + Байты + + + First Frame + Первый кадр + + + Last Frame + Последний кадр + + + Display filter: + Фильтр отображения: + + + Regenerate statistics using this display filter + Регенерировать статистику, используя этот фильтр отображения + + + Apply + Применить + + + Copy as CSV + Копировать в виде CSV + + + Copy the tree as CSV + Копировать дерево в виде CSV + + + Copy as YAML + Копировать в виде YAML + + + Copy the tree as YAML + Копировать дерево в виде YAML + + + LBM Stream failed to attach to tap + Ошибка подключения потока LBM к перехватчику данных + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + + LayoutPreferencesFrame + + Frame + Кадр + + + Pane 1: + Область 1: + + + Packet List + Список пакетов + + + Packet Details + Информация о пакете + + + Packet Bytes + Байты пакета + + + Packet Diagram + Диаграмма пакета + + + None + Отсутствует + + + Pane 2: + Область 2: + + + Pane 3: + Область 3: + + + Packet List settings: + Параметры списка пакетов: + + + Show packet separator + Показывать разделитель пакетов + + + Show column definition in column context menu + Показывать определение столбца в его контекстом меню + + + Allow the list to be sorted + Разрешать сортировку списка + + + Maximum number of cached rows (affects sorting) + Максимальное число кэшированных строк (влияет на сортировку) + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + <html><head/><body><p>Если количество отображаемых строк будет превышать указанное значение, сортировка столбцов, требующих разбора пакетов, будет отключена. Увеличение данного значения будет увеличивать потребление памяти на кэширование значений столбцов.</p></body></html> + + + Enable mouse-over colorization + Включить изменение цвета при наведении указателя мыши + + + Status Bar settings: + Настройки строки состояния: + + + Show selected packet number + Показать номер выбранного пакета + + + Show file load time + Показать время загрузки файла + + + + LteMacStatisticsDialog + + LTE Mac Statistics + Статистика LTE Mac + + + Include SR frames in filter + Включать кадры SR в фильтр + + + Include RACH frames in filter + Включать кадры RACH в фильтр + + + MAC Statistics + Статистика MAC + + + + LteRlcGraphDialog + + Dialog + Диалог + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Практичные и удобные комбинации клавиш для экономии рабочего времени</h3> +<table><tbody> + +<tr><th>+</th><td>Увеличить масштаб</td></th> +<tr><th>-</th><td>Уменьшить масштаб</td></th> +<tr><th>0</th><td>Вернуть график к исходному состоянию</td></th> + +<tr><th>→</th><td>Переместить вправо на 10 пикселов</td></th> +<tr><th>←</th><td>Переместить влево на 10 пикселов</td></th> +<tr><th>↑</th><td>Переместить вверх на 10 пикселов</td></th> +<tr><th>↓</th><td>Переместить вниз на 10 пикселов</td></th> +<tr><th><i>Shift+</i>→</th><td>Переместить вправо на 1 пиксел</td></th> +<tr><th><i>Shift+</i>←</th><td>Переместить влево на 1 пиксел</td></th> +<tr><th><i>Shift+</i>↑</th><td>Переместить вверх на 1 пиксел</td></th> +<tr><th><i>Shift+</i>↓</th><td>Переместить вниз на 1 пиксел</td></th> + +<tr><th>g</th><td>Перейти к пакету под курсором</td></th> + +<tr><th>z</th><td>Переключить режим работы мыши на перетаскивание или изменение масштаба</td></th> +<tr><th>t</th><td>Переключение на время начала захвата или сеанса</td></th> +<tr><th>Пробел</th><td>Переключить перекрестье</td></th> + +</tbody></table> +</body></html> + + + Mouse + Мышь + + + Drag using the mouse button. + Перетаскивание с помощью кнопки мыши. + + + drags + перетаскивание + + + Select using the mouse button. + Выделять кнопкой мыши. + + + zooms + масштаб + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Вернуть график в исходное состояние.</p></body></html> + + + Reset + Сброс + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Переключить направление соединения (просмотр обратного потока).</p></body></html> + + + Switch Direction + Переключить направление + + + Reset Graph + Сбросить график + + + Reset the graph to its initial state. + Сбросить график в исходное состояние. + + + 0 + 0 + + + Zoom In + Увеличить масштаб + + + + + + + + + Zoom Out + Уменьшить масштаб + + + - + - + + + Move Up 10 Pixels + Переместить вверх на 10 пикселов + + + Up + Вверх + + + Move Left 10 Pixels + Переместить влево на 10 пикселов + + + Left + Влево + + + Move Right 10 Pixels + Переместить вправо на 10 пикселов + + + Right + Вправо + + + Move Down 10 Pixels + Переместить вниз на 10 пикселов + + + Down + Вниз + + + Move Up 1 Pixel + Переместить вверх на 1 пиксел + + + Shift+Up + Shift+Вверх + + + Move Left 1 Pixel + Переместить влево на 1 пиксел + + + Shift+Left + Shift+Влево + + + Move Right 1 Pixel + Переместить вправо на 1 пиксел + + + Shift+Right + Shift+Вправо + + + Move Down 1 Pixel + Переместить вниз на 1 пиксел + + + Move down 1 Pixel + Переместить вниз на 1 пиксел + + + Shift+Down + Shift+Вниз + + + Drag / Zoom + Перетаскивание / масштаб + + + Toggle mouse drag / zoom behavior + Переключить режим работы мыши на перетаскивание или изменение масштаба + + + Z + Z + + + Crosshairs + Перекрестия + + + Toggle crosshairs + Переключение перекрестий + + + Space + Пробел + + + Move Up 100 Pixels + Переместить вверх на 100 пикселов + + + PgUp + PgUp + + + PgDown + PgDown + + + Go To Packet Under Cursor + Перейти к пакету под курсором + + + Go to packet currently under the cursor + Перейти к пакету, находящемуся под курсором + + + G + G + + + Zoom In X Axis + Увеличить масштаб по оси X + + + X + X + + + Zoom Out Y Axis + Уменьшить масштаб по оси Y + + + Shift+Y + Shift+Y + + + Zoom In Y Axis + Увеличить масштаб по оси Y + + + Y + Y + + + Zoom Out X Axis + Уменьшить масштаб по оси X + + + Shift+X + Shift+X + + + Switch direction (swap between UL and DL) + Переключить направление (поменять местами UL и DL) + + + D + D + + + Time + Время + + + Sequence Number + Номер последовательности + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + График LTE RLC (UE=%1 канал=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + График LTE RLC — канал не выбран + + + Save As… + Сохранить как… + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3с последовательность %4 длина %5) + + + Click to select packet + Щёлкните для выбора пакета + + + Packet + Пакет + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Разместить в соответствии с масштабом, x = %1 до %2, y = %3 до %4 + + + Unable to select range. + Невозможно выбрать диапазон. + + + Click to select a portion of the graph. + Щёлкните для выбора участка графика. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Сохранение графика как… + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + Статистика LTE RLC + + + Include SR frames in filter + Включать кадры SR в фильтр + + + Include RACH frames in filter + Включать кадры RACH в фильтр + + + Use RLC frames only from MAC frames + Использовать кадры RLC только из кадров MAC + + + UL Frames + Кадры UL + + + UL Bytes + Байты UL + + + UL MB/s + UL, МБайт/с + + + UL ACKs + UL ACK + + + UL NACKs + UL NACK + + + UL Missing + UL отсутствует + + + DL Frames + Кадры DL + + + DL Bytes + Байты DL + + + DL MB/s + Мбайт/с DL + + + DL ACKs + DL ACK + + + DL NACKs + DL NACK + + + DL Missing + DL отсутствует + + + RLC Statistics + Статистика RLC + + + + MainStatusBar + + Ready to load or capture + Всё готово к загрузке или захвату + + + Ready to load file + Всё готово к загрузке файла + + + Open the Capture File Properties dialog + Открыть диалоговое окно свойств файла захвата + + + Profile: %1 + Профиль: %1 + + + Manage Profiles… + Управление профилями… + + + New… + Новый… + + + Edit… + Редактировать… + + + Import + Импортировать + + + Export + Экспортировать + + + Delete + Удалить + + + Switch to + Переключиться на + + + is the highest expert information level + is the highest expert info level + это экспертная информация наивысшего уровня серьёзности + + + ERROR + ОШИБКА + + + WARNING + ПРЕДУПРЕЖДЕНИЕ + + + NOTE + ЗАМЕЧАНИЕ + + + CHAT + ЧАТ + + + No expert information + No expert info + Экспертная информация отсутствует + + + %Ln byte(s) + , %1 bytes + + + + + + + + Byte %1 + Байт %1 + + + Bytes %1-%2 + Байты %1-%2 + + + Selected Packet: %1 %2 + Выбран пакет: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + Пакеты: %1 %4 Отображаются: %2 (%3%) + + + %1 Selected: %2 (%3%) + %1 Выбрано: %2 (%3%) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 Отмечено: %2 (%3%) + + + %1 Dropped: %2 (%3%) + %1 Потеряно: %2 (%3%) + + + %1 Ignored: %2 (%3%) + %1 Проигнорировано: %2 (%3%) + + + %1 Comments: %2 + %1 Комментарии: %2 + + + %1 Load time: %2:%3.%4 + %1 Время загрузки: %2:%3.%4 + + + No Packets + Пакеты отсутствуют + + + From Zip File... + Из zip-фйла… + + + From Directory... + Из каталога… + + + Selected Personal Profile... + Выбранный личный профиль… + + + All Personal Profiles... + Все личные профили… + + + Packets: %1 + Пакеты: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + + MainWindowPreferencesFrame + + Frame + Кадр + + + Checking this will save the size, position, and maximized state of the main window. + Включение этого параметра приведёт к сохранению размера, расположения и распахнутого состояния главного окна. + + + Remember main window size and placement + Запомнить размер и расположение главного окна + + + Open files in + Папка для открытия файлов + + + This folder: + Эта папка: + + + Browse… + Browse... + Обзор… + + + The most recently used folder + Последняя использованная папка + + + Show up to + Показывать до + + + filter entries + записей фильтра + + + recent files + последних файлов + + + Confirm unsaved capture files + Подтверждать несохранённые файлы захвата + + + Display autocompletion for filter text + Показывать автоподстановку для текстового фильтра + + + Main toolbar style: + Стиль главной панели инструментов: + + + Icons only + Только значки + + + Text only + Только текст + + + Icons & Text + Значки и текст + + + Window title + Заголовок окна + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Изменяемый заголовок окна будет добавлен после существующего заголовка<br/>%F = путь к файлу захвата<br/>%P = имя профиля<br/>%S = условный разделитель (&quot; — &quot;), отображающийся только между переменными, содержащими значения или статический текст<br/>%V = информация о версии</p></body></html> + + + Prepend window title + Добавить перед заголовком окна + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Изменяемый заголовок окна будет добавлен после существующего заголовка<br/>%F = путь к файлу захвата<br/>%P = имя профиля<br/>%S = условный разделитель (&quot; — &quot;), отображающийся только между переменными, содержащими значения или статический текст<br/>%V = информация о версии</p></body></html> + + + Language: + Язык: + + + Use system setting + Использовать системную настройку + + + Open Files In + Папка для открытия файлов + + + + ManageInterfacesDialog + + Manage Interfaces + Управление интерфейсами + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Снимите или установите флажок, чтобы скрыть или показать скрытый интерфейс.</p></body></html> + + + Local Interfaces + Локальные интерфейсы + + + Show + Показать + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Добавить канал захвата из списка или удалить существующий канал.</p></body></html> + + + Pipes + Каналы + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Добавить новый канал с использованием настроек по умолчанию.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Удалить выбранный канал из списка.</p></body></html> + + + Remote Interfaces + Удалённые интерфейсы + + + Host / Device URL + URL-адрес узла или устройства + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>Добавить удалённый узел и его интерфейсы</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>Удалить выбранный узел из списка.</p></body></html> + + + Remote Settings + Удалённые настройки + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Данная версия Wireshark не сохраняет параметры канала. + + + This version of Wireshark does not save remote settings. + Данная версия Wireshark не сохраняет удалённые параметры. + + + This version of Wireshark does not support remote interfaces. + Данная версия Wireshark не поддерживает удалённые интерфейсы. + + + New Pipe + Новый канал + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + Выбрать все + + + Copy + Копировать + + + Find + Найти + + + Clear + Очистить + + + + ManufTableModel + + Address Block + + + + Short Name + Короткое имя + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + Зона прокрутки + + + + Mtp3SummaryDialog + + Dialog + Диалоговое окно + + + MTP3 Summary + Сводка MTP3 + + + File + Файл + + + Name + Имя + + + Length + Длина + + + Format + Формат + + + Snapshot length + Длина снимка + + + Data + Данные + + + First packet + Первый пакет + + + Last packet + Последний пакет + + + Elapsed + Истекло + + + Packets + Пакетов + + + Service Indicator (SI) Totals + Итоговые значения для индикатора службы (SI) + + + SI + SI + + + MSUs + MSU + + + MSUs/s + MSU/с + + + Bytes + Байтов + + + Bytes/MSU + Байтов/MSU + + + Bytes/s + Байтов/с + + + Totals + Всего + + + Total MSUs + Всего MSU + + + Total Bytes + Всего байтов + + + Average Bytes/MSU + Среднее число байтов/MSU + + + Average Bytes/s + Среднее число байтов/с + + + + MulticastStatisticsDialog + + UDP Multicast Streams + Потоки UDP Multicast + + + Source Address + Адрес источника + + + Source Port + Порт источника + + + Destination Address + Адрес назначения + + + Destination Port + Порт назначения + + + Packets + Пакеты + + + Packets/s + Пакетов/с + + + Avg BW (bps) + Средняя пропускная способность (бит/с) + + + Max BW (bps) + Максимальная пропускная способность(бит/с) + + + Max Burst + Максимальный размер очереди + + + Burst Alarms + Предупреждений по размеру очереди + + + Max Buffers (B) + Максимальный размер буферов (Б) + + + Buffer Alarms + Предупреждений по размеру буфера + + + Burst measurement interval (ms): + Интервал измерения очереди (мс): + + + Burst alarm threshold (packets): + Порог выдачи предупреждения по длине очереди (пакеты): + + + Buffer alarm threshold (B): + Порог выдачи предупреждения по размеру буфера (Б): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + Скорость потока без данных (кбит/с): + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + Общая скорость без данных (кбит/с): + + + The burst interval must be between 1 and 1000. + Интервал измерения очереди должен быть в диапазоне от 1 до 1000. + + + The burst alarm threshold isn't valid. + Недопустимый порог выдачи предупреждений по размеру очереди. + + + The buffer alarm threshold isn't valid. + Недопустимый порог выдачи предупреждений по размеру буфера. + + + The stream empty speed should be between 1 and 10000000. + Cкорость потока без данных должна быть в диапазоне от 1 до 10000000. + + + The total empty speed should be between 1 and 10000000. + Общая скорость без данных должна быть между 1 и 10000000. + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + Потоков: %1, средняя пропускная способность: %2 бит/с, максимальная пропускная способность: %3 бит/с, максимальный размер очереди: %4 / %5 мс, максимальный размер буфера: %6 Б + + + + PacketCommentDialog + + Edit Packet Comment + Изменение комментария к пакету + + + Add Packet Comment + Добавление комментария к пакету + + + + PacketDiagram + + Packet diagram + Диаграмма пакета + + + Show Field Values + Показать значения полей + + + Save Diagram As… + Сохранить диаграмму как… + + + Copy as Raster Image + Копировать в виде растрового изображения + + + …as SVG + …в формате SVG + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + Scalable Vector Graphics (*.svg) + + + Save Graph As… + Сохранение графика как… + + + + PacketDialog + + Dialog + Диалоговое окно + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + Показывать байты пакета + + + Packet %1 + Пакет %1 + + + [%1 closed] + [%1 закрыт] + + + Byte %1 + Байт %1 + + + Bytes %1-%2 + Байты %1-%2 + + + %Ln byte(s) + + + + + + + + + PacketFormatGroupBox + + GroupBox + Групповой блок + + + Packet Format + Формат пакета + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>Строки сводки пакетов соответствуют списку пакетов</p></body></html> + + + Summary line + Итоговая строка + + + Include column headings + Добавлять заголовки столбцов + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>Подробная информация о пакете, аналогичная представлению в виде дерева протоколов</p></body></html> + + + Details: + Подробная информация: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>Экспортировать только элементы информации о пакетах самого высокого уровня</p></body></html> + + + All co&llapsed + Всё свё&рнуто + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>Разворачивать и сворачивать подробную информацию о пакетах в соответствии с текущим отображением.</p></body></html> + + + As displa&yed + Согласно отображ&ению + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>Экспортировать все элементы информации о пакетах</p></body></html> + + + All e&xpanded + Всё р&азвёрнуто + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>Экспортировать шестнадцатеричный дамп данных пакета аналогично просмотру байтов пакета</p></body></html> + + + Bytes + Байты + + + Include secondary data sources + Включать вторичные источники данных + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + <html><head/><body><p>Генерировать шестнадцатеричные дампы для вторичных источников данных в виде пересобранных или расшифрованных буферов, в добавление к кадру</p></body></html> + + + + PacketList + + Protocol Preferences + Параметры протокола + + + Summary as Text + Сводка в текстовом формате + + + …as CSV + …в виде CSV + + + …as YAML + …в виде YAML + + + Decode As… + Декодировать как… + + + Frame %1: %2 + + + Кадр %1: %2 + + + + + [ Comment text exceeds %1. Stopping. ] + [ Текст комментария превышает %1. Остановка. ] + + + + PacketListHeader + + Align Left + Выровнять по левому краю + + + Align Center + Выровнять по центру + + + Align Right + Выровнять по правому краю + + + Edit Column + Редактировать столбец + + + Resize to Contents + Размер по содержимому + + + Column Preferences… + Параметры столбца… + + + Resize Column to Width… + Изменить размер столбцов по ширине… + + + Resolve Names + Выполнить разрешение имён + + + Remove this Column + Удалить данный столбец + + + Column %1 + Столбец %1 + + + Width: + Ширина: + + + + PacketListModel + + Column + Столбец + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + %1 может быть отсортирован только на %2 или меньшее количество видимых строк; необходимо увеличить размер кэша в параметрах разметки + + + Sorting "%1"… + Сортировка «%1»… + + + Sorting … + + + + + PacketRangeGroupBox + + Form + Форма + + + Packet Range + Диапазон пакетов + + + - + - + + + Displayed + Показано + + + &Marked packets only + &Только отмеченные пакеты + + + &Range: + &Диапазон: + + + Remove &ignored packets + Удалять &проигнорированные пакеты + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + От первого &к последнему отмеченному + + + &All packets + &Все пакеты + + + &Selected packets only + &Только выбранные пакеты + + + Captured + Захвачено + + + + PathSelectionDelegate + + Open a pipe + Открыть канал + + + + PathSelectionEdit + + Browse + Обзор + + + Select a path + Выбрать путь + + + + PluginListModel + + Name + Имя + + + Version + Версия + + + Type + Тип + + + Path + Путь + + + + PortsModel + + All entries + Все записи + + + tcp + tcp + + + udp + udp + + + sctp + sctp + + + dccp + dccp + + + Name + Имя + + + Port + Порт + + + Type + Тип + + + + PreferenceEditorFrame + + Frame + Кадр + + + … + + + + a preference + параметр + + + Browse… + Обзор… + + + Open %1 preferences… + Открыть параметры %1… + + + Invalid value. + Недопустимое значение. + + + + PreferencesDialog + + Search: + Найти: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + Параметры + + + + PrefsModel + + Advanced + Дополнительно + + + Appearance + Внешний вид + + + Layout + Разметка + + + Columns + Столбцы + + + Font and Colors + Шрифт и цвета + + + Capture + Захват + + + Expert + Экспертный анализ + + + Filter Buttons + Кнопки фильтра + + + RSA Keys + Ключи RSA + + + + PrintDialog + + Packet Format + Формат пакета + + + Print each packet on a new page + Печатать каждый пакет на новой странице + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>Печатать информацию о файле захвата на каждой странице</p></body></html> + + + Capture information header + Заголовок с информацией о захвате + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>Используйте клавиши &quot;+&quot; и &quot;-&quot; для увеличения и уменьшения масштаба предварительного просмотра. Сброс масштаба осуществляется клавишей &quot;0&quot;.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ и - масштаб, 0 сброс</span></p></body></html> + + + Packet Range + Диапазон пакетов + + + Print + Печать + + + &Print… + &Печатать… + + + Page &Setup… + Параметры &страницы… + + + %1 %2 total packets, %3 shown + %1 %2 всего пакетов, %3 показано + + + Print Error + Ошибка печати + + + Unable to print to %1. + Не удалось выполнить печать на %1. + + + + ProfileDialog + + Search for profile … + Поиск профиля … + + + Create a new profile using default settings. + Создать новый профиль с использованием параметров по умолчанию. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>Удалить данный профиль. Системные профили не могут быть удалены. Профиль по умолчанию после удаления будет восстановлен.</p></body></html> + + + Copy this profile. + Копировать данный профиль. + + + Configuration Profiles + Профили конфигурации + + + Import + noun + Импорт + + + Export + noun + Экспорт + + + From Zip File... + Из zip-файла... + + + From Directory... + Из каталога… + + + %Ln Selected Personal Profile(s)... + + + + + + + + All Personal Profiles... + Все личные профили… + + + New profile + Новый профиль + + + Profile Error + Ошибка профиля + + + Exporting profiles + Экспортирование профилей + + + No profiles found for export + Профили для экспорта не найдены + + + Select zip file for export + Выбрать zip-файл для экспорта + + + An import of profiles is not allowed, while changes are pending + Импорт профилей при наличии ожидающих сохранения изменений не допускается + + + An import is pending to be saved. Additional imports are not allowed + Импорт ожидает сохранения. Выполнение других операций импорта не допускаются + + + An export of profiles is only allowed for personal profiles + Допускается экспорт только личных профилей + + + An export of profiles is not allowed, while changes are pending + Экспорт профилей при наличии ожидающих сохранения изменений не допускается + + + %Ln profile(s) exported + + + + + + + + Select zip file for import + Выбор zip-файла для импорта + + + Select directory for import + Выбор каталога для импорта + + + Zip File (*.zip) + Файл ZIP (*.zip) + + + Error + Ошибка + + + An error has occurred while exporting profiles + При экспорте профилей произошла ошибка + + + No profiles found for import in %1 + Профили для импорта не найдены в %1 + + + %Ln profile(s) imported + + + + + + + + , %Ln profile(s) skipped + + + + + + + + Importing profiles + Импортирование профилей + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + + ProfileModel + + Resetting to default + Возврат к настройкам по умолчанию + + + Imported profile + Импортированный профиль + + + This is a system provided profile + Это системный профиль + + + A profile change for this name is pending + Для этого имени ожидается изменение профиля + + + (See: %1) + (Смотри: %1) + + + This is an invalid profile definition + Это недопустимое определение профиля + + + A profile already exists with this name + Профиль с таким именем уже существует + + + A profile with this name is being deleted + Профиль с этим именем удаляется + + + Created from default settings + Создан с применением параметров по умолчанию + + + system provided + системный + + + deleted + удалённый + + + copy + noun + копия + + + Exporting profiles while changes are pending is not allowed + Экспорт профилей при наличии ожидающих сохранения изменений не допускается + + + No profiles found to export + Профили для экспорта не найдены + + + Can't delete profile directory + Не удалось удалить каталог профиля + + + A profile name cannot contain the following characters: %1 + Имя профиля не может содержать следующие символы: %1 + + + A profile name cannot contain the '/' character + Имя профиля не может содержать символ «/» + + + A profile cannot start or end with a period (.) + Профиль не может начинаться или заканчиваться точкой (.) + + + Default + По умолчанию + + + Global + Глобальный + + + Personal + Личный + + + Renamed from: %1 + Переименован из: %1 + + + Copied from: %1 + Скопирован из: %1 + + + renamed to %1 + переименован в %1 + + + Profile + Профиль + + + Type + Тип + + + + ProfileSortModel + + All profiles + Все профили + + + Personal profiles + Личные профили + + + Global profiles + Глобальные профили + + + + ProgressFrame + + Frame + Кадр + + + Loading + Загрузка + + + + ProtoTree + + Packet details + Подробная информация о пакете + + + Not a field or protocol + Это не поле/протокол + + + No field reference available for text labels. + Недоступна ссылка на поле для текстовых подписей. + + + Expand Subtrees + Развернуть поддеревья + + + Collapse Subtrees + Свернуть поддеревья + + + Expand All + Развернуть всё + + + Collapse All + Свернуть всё + + + Copy + Копировать + + + All Visible Items + Все видимые элементы + + + All Visible Selected Tree Items + Все видимые элементы выбранного дерева + + + Description + Описание + + + Field Name + Имя поля + + + Value + Значение + + + As Filter + В качестве фильтра + + + Wiki Protocol Page + Вики-страница протокола + + + Filter Field Reference + Справка по полю фильтра + + + Copied + Скопировано + + + Wiki Page for %1 + Вики-страница для %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Вики-страница Wireshark поддерживается сообществом.</p><p>Загружаемая страница может оказаться полностью выполненной или незавершённой, содержать ошибки или отсутствовать.</p><p>Продолжить переход на Вики-страницу?</p> + + + Colorize with Filter + Выполнить выделение цветом с помощью фильтра + + + + ProtocolHierarchyDialog + + Dialog + Диалоговое окно + + + Protocol + Протокол + + + Percent Packets + Процент пакетов + + + Packets + Пакеты + + + Percent Bytes + Процент байтов + + + Bytes + Байты + + + Bits/s + Бит/с + + + End Packets + Конечные пакеты + + + End Bytes + Конечные байты + + + End Bits/s + Конечные бит/с + + + PDUs + PDU + + + <small><i>A hint.</i></small> + <small><i>Подсказка.</i></small> + + + Copy as CSV + Копировать в виде CSV + + + Copy stream list as CSV. + Копировать список потоков в виде CSV. + + + Copy as YAML + Копировать в виде YAML + + + Copy stream list as YAML. + Копировать список потоков в виде YAML. + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + Статистика иерархии протоколов + + + Copy + Копировать + + + as CSV + в виде CSV + + + as YAML + в виде YAML + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + Нет фильтра отображения. + + + Display filter: %1 + Фильтр отображения: %1 + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + Параметры протокола + + + No protocol preferences available + Доступные параметры протокола отсутствуют + + + Disable %1 + Отключить %1 + + + %1 has no preferences + %1 не имеет параметров + + + Open %1 preferences… + Открыть параметры %1… + + + + QObject + + Average Throughput (bits/s) + Средняя пропускная способность (бит/с) + + + Round Trip Time (ms) + Время приёма-передачи (мс) + + + Segment Length (B) + Длина сегмента (Б) + + + Sequence Number (B) + Порядковый номер (Б) + + + Time (s) + Время (с) + + + Window Size (B) + Размер окна (Б) + + + [no capture file] + [нет файла захвата] + + + Conversation + Диалог + + + Bars show the relative timeline for each conversation. + Прямоугольники показывают относительную временную шкалу для каждого диалога. + + + Endpoint + Конечная точка + + + Apply as Filter + Применить в качестве фильтра + + + Prepare as Filter + Подготовить в качестве фильтра + + + Find + Найти + + + Colorize + Выделить цветом + + + Look Up + Искать + + + Copy + Копировать + + + UNKNOWN + НЕИЗВЕСТНОЕ + + + Selected + Выбрано + + + Not Selected + Не выбрано + + + …and Selected + …и выбрано + + + …or Selected + …или выбрано + + + …and not Selected + …и не выбрано + + + …or not Selected + …или не выбрано + + + A + A + + + B + B + + + Any + Любой + + + Don't show this message again. + Не показывать это сообщение снова. + + + Multiple problems found + Обнаружено несколько проблем + + + %1 (%L2%) + %1 (%L2%) + + + No entries. + Нет записей. + + + %1 entries. + Записей: %1. + + + Base station + Базовая станция + + + <Broadcast> + <Broadcast> + + + <Hidden> + <Hidden> + + + BSSID + BSSID + + + Beacons + Маячки + + + Data Pkts + Пакеты данных + + + Protection + Защита + + + Address + Адрес + + + Pkts Sent + Отправлено пакетов + + + Pkts Received + Получено пакетов + + + Comment + Комментарий + + + Wrong sequence number + Неверный номер последовательности + + + Payload changed to PT=%1 + Полезные данные изменены на PT=%1 + + + Incorrect timestamp + Некорректная метка времени + + + Marker missing? + Отсутствует маркер? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + Тип + + + UEId + UEId + + + UL Frames + Кадры UL + + + UL Bytes + Байты UL + + + UL MB/s + UL, МБайт/с + + + UL Padding % + UL, заполнение % + + + UL Re TX + UL Re TX + + + DL Frames + Кадры DL + + + DL Bytes + Байты DL + + + DL MB/s + DL, Мбайт/с + + + DL Padding % + DL, заполнение % + + + DL CRC Failed + Сбой, DL CRC + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + Предопределено + + + Unknown (%1) + Неизвестный (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + Неизвестно + + + UE Id + UE Id + + + Name + Имя + + + Mode + Режим + + + Priority + Приоритет + + + default + по умолчанию + + + DLT %1 + DLT %1 + + + Invalid Display Filter + Недопустимый фильтр отображения + + + The filter expression %1 isn't a valid display filter. (%2). + Выражение фильтра %1 не является допустимым фильтром отображения. (%2). + + + Error + Ошибка + + + No remote interfaces found. + Удалённые интерфейсы не найдены. + + + PCAP not found + PCAP не найден + + + Unknown error + Неизвестная ошибка + + + Default + По умолчанию + + + Changed + Изменено + + + Has this preference been changed? + Изменялся ли этот параметр? + + + Default value is empty + Значение по умолчанию отсутствует + + + Gap in dissection + Пробел при разборе + + + Edit… + Изменить… + + + Browse… + Обзор… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + Удалённый интерфейс + + + Host: + Узел: + + + Port: + Порт: + + + Authentication + Проверка подлинности + + + Null authentication + Открытая проверка подлинности + + + Password authentication + Проверка подлинности по паролю + + + Username: + Имя пользователя: + + + Password: + Пароль: + + + Clear list + Очистить список + + + Error + Ошибка + + + No remote interfaces found. + Удалённые интерфейсы не найдены. + + + PCAP not found + PCAP не найдена + + + + RemoteSettingsDialog + + Remote Capture Settings + Настройки удалённого захвата + + + Capture Options + Параметры захвата + + + Do not capture own RPCAP traffic + Не захватывать собственный RPCAP-трафик + + + Use UDP for data transfer + Использовать UDP для передачи данных + + + Sampling Options + Параметры выборки + + + None + Нет + + + 1 of + 1 из + + + packets + пакета (пакетов) + + + 1 every + 1 каждые + + + milliseconds + мс + + + + ResolvedAddressesDialog + + Dialog + Диалоговое окно + + + Hosts + Узлы + + + Search for entry (min 3 characters) + Поиск записи (не менее 3 символов) + + + Ports + Порты + + + Search for port or name + Найти порт или имя + + + Capture File Comments + Комментарии в файле захвата + + + Comment + Комментарий + + + Show the comment. + Показать комментарий. + + + IPv4 Hash Table + Хэш-таблица IPv4 + + + Show the IPv4 hash table entries. + Показать записи хэш-таблицы IPv4. + + + IPv6 Hash Table + Хэш-таблица IPv6 + + + Show the IPv6 hash table entries. + Показать записи хэш-таблицы IPv6. + + + Show All + Показать все + + + Show all address types. + Показать все типы адресов. + + + Hide All + Скрыть все + + + Hide all address types. + Скрыть все типы адресов. + + + IPv4 and IPv6 Addresses (hosts) + Адреса IPv4 и IPv6 (узлы) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + Показывать разрешённые имена узлов IPv4 и IPv6 в формате «hosts». + + + Port names (services) + Имена (службы) портов + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + Показывать разрешённые имена портов в формате «services». + + + Ethernet Addresses + Ethernet-адреса + + + Show resolved Ethernet addresses in "ethers" format. + Показывать разрешённые Ethernet-адреса в формате «ethers». + + + Ethernet Well-Known Addresses + Известные Ethernet-адреса + + + Show well-known Ethernet addresses in "ethers" format. + Показывать известные Ethernet-адреса в формате «ethers». + + + Ethernet Manufacturers + Производители Ethernet + + + Show Ethernet manufacturers in "ethers" format. + Показать производителей Ethernet в формате «ethers». + + + [no file] + [нет файла] + + + Resolved Addresses + Разрешённые адреса + + + # Resolved addresses found in %1 + # Разрешённые адреса найденные в %1 + + + # Comments +# +# + # Комментарии +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + Статистика задержки времени ответа %1 + + + Type + Тип + + + Messages + Сообщения + + + Min SRT + Мин. ВОС + + + Max SRT + Макс. ВОС + + + Avg SRT + Среднее ВОС + + + Min in Frame + Мин. в кадре + + + Max in Frame + Макс. в кадре + + + Open Requests + Открытые запросы + + + Discarded Responses + Отклонённые ответы + + + Repeated Requests + Повторные запросы + + + Repeated Responses + Повторные ответы + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>Выберите программу и версию, при необходимости введите фильтр, а затем нажмите «Применить».</i></small> + + + Version: + Версия: + + + Program: + Программа: + + + DCE-RPC Service Response Times + Время ответа службы DCE-RPC + + + ONC-RPC Service Response Times + Время ответа службы ONC-RPC + + + + RsaKeysFrame + + RSA Keys + Ключи RSA + + + RSA private keys are loaded from a file or PKCS #11 token. + Закрытые ключи RSA загружены из файла или токена PKCS #11. + + + Add new keyfile… + Добавить новый файл ключей… + + + Add new token… + Добавить новый токен… + + + Remove key + Удалить ключ + + + PKCS #11 provider libraries. + Библиотеки поставщика PKCS #11. + + + Add new provider… + Добавить нового поставщика… + + + Remove provider + Удалить поставщика + + + Add PKCS #11 token or key + Добавить токен или ключ PKCS #11 + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + Новые токены или ключи PKCS #11 не найдены, рекомендуется добавить поставщика PKCS #11. + + + Select a new PKCS #11 token or key + Выбрать новый токен или ключ PKCS #11 + + + PKCS #11 token or key + PKCS #11 токен или ключ + + + Enter PIN or password for %1 (it will be stored unencrypted) + Введите PIN или пароль для %1 (он будет сохранён в незашифрованном виде) + + + Enter PIN or password for key + Введите PIN или пароль для ключа + + + Key could not be added: %1 + Ключ не может быть добавлен: %1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + Закрытый ключ RSA (*.pem *.p12 *.pfx *.key);;Все Файлы ( + + + Select RSA private key file + Выбрать файл закрытого ключа RSA + + + Libraries (*.dll) + Библиотеки (*.dll) + + + Libraries (*.so) + Библиотеки (*.so) + + + Select PKCS #11 Provider Library + Выбрать библиотеку поставщика PKCS #11 + + + Changes will apply after a restart + Изменения вступят в силу после перезапуска + + + PKCS #11 provider %1 will be removed after the next restart. + Поставщик PKCS #11 %1 будет удалён после следующего перезапуска. + + + + RtpAnalysisDialog + + Dialog + Диалоговое окно + + + Packet + Пакет + + + Sequence + Последовательность + + + Delta (ms) + Дельта (мс) + + + Jitter (ms) + Jitter + Джиттер (мс) + + + Skew + Разброс + + + Bandwidth + Полоса пропускания + + + Marker + Маркер + + + Status + Статус + + + Stream %1 + Поток %1 + + + Stream %1 Jitter + Джиттер потока %1 + + + Stream %1 Difference + Отклонение потока %1 + + + Stream %1 Delta + Дельта потока %1 + + + %1 streams, + потоков: %1, + + + Save one stream CSV + Сохранить в CSV один поток + + + Save all stream's CSV + Сохранить в CSV все потоки + + + &Analyze + &Анализ + + + Open the analysis window for the selected stream(s) + Открыть окно анализа для выбранных потоков + + + &Set List + &Составить список + + + &Add to List + &Добавить в список + + + &Remove from List + &Удалить из списка + + + Replace existing list in RTP Analysis Dialog with new one + Заменить существующий список новым в диалоговом окне анализа RTP + + + Add new set to existing list in RTP Analysis Dialog + Добавить новый набор к существующему списку в диалоговом окне анализа RTP + + + Remove selected streams from list in RTP Analysis Dialog + Удалить выбранные потоки из списка в диалоговом окне анализа RTP + + + Graph + График + + + <small><i>A hint.</i></small> + <small><i>Подсказка.</i></small> + + + &Export + &Экспорт + + + Open export menu + Открыть меню экспорта + + + CSV + CSV + + + Save tables as CSV. + Сохранить таблицы в виде CSV. + + + Current Tab Stream CSV + Поток на текущей вкладке в CSV + + + Save the table on the current tab as CSV. + Сохранить таблицу на текущей вкладке в виде CSV. + + + All Tab Streams CSV + Потоки на всех вкладках в CSV + + + Save the table from all tabs as CSV. + Сохранить таблицы со всех вкладок в виде CSV. + + + Save Graph + Сохранить график + + + Save the graph image. + Сохранить изображение графика. + + + Go to Packet + Перейти к пакету + + + Select the corresponding packet in the packet list. + Выбрать соответствующий пакет в списке пакетов. + + + G + G + + + Next Problem Packet + Следующий проблемный пакет + + + Go to the next problem packet + Перейти к следующему проблемному пакету + + + N + N + + + Prepare &Filter + Подготовить &фильтр + + + Prepare a filter matching the selected stream(s). + Подготовить фильтр, совпадающий с выбранными потоками. + + + &Current Tab + &Текущая вкладка + + + Prepare a filter matching current tab. + Подготовить фильтр, совпадающий с текущей вкладкой. + + + &All Tabs + &Все вкладки + + + Prepare a filter matching all tabs. + Подготовить фильтр, совпадающий со всеми вкладками. + + + RTP Stream Analysis + Анализ потока RTP + + + Save Graph As… + Сохранить график как… + + + G: Go to packet, N: Next problem packet + G: Перейти к пакету, N: Следующий проблемный пакет + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + Comma-separated values (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %1 не поддерживает PCM в %2. Рекомендуется использовать формат %3 + + + + RtpPlayerDialog + + RTP Player + RTP-проигрыватель + + + Play + Воспроизводить + + + Source Address + Адрес источника + + + Source Port + Порт источника + + + Destination Address + Адрес назначения + + + Destination Port + Порт назначения + + + SSRC + SSRC + + + Setup Frame + Кадр настройки + + + Packets + Пакеты + + + Time Span (s) + Временной промежуток (с) + + + Payloads + Полезные данные + + + <small><i>No audio</i></small> + <small><i>Нет аудио</i></small> + + + Start playback of all unmuted streams + Начать воспроизведение всех потоков со включенным звуком + + + Pause/unpause playback + Пауза или продолжение воспроизведения + + + Stop playback + Остановить воспроизведение + + + Enable/disable skipping of silence during playback + Включить или выключить пропуск тишины в процессе воспроизведения + + + Min silence: + Мин. тишина: + + + Minimum silence duration to skip in seconds + Минимальная длительность тишины при пропуске в секундах + + + Output Device: + Выходное устройство: + + + Output Audio Rate: + Скорость исходящего звукового сигнала: + + + Jitter Buffer: + Буфер джиттера: + + + The simulated jitter buffer in milliseconds. + Имитируемый буфер джиттера в миллисекундах. + + + Playback Timing: + Время воспроизведения: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Буфер джиттера</strong>: Использовать буфер джиттера для имитации потока RTP, в том виде, в котором он прослушивается конечным пользователем. +<br/> +<strong>Метка времени RTP</strong>: Использовать метку времени RTP, вместо времени получения пакета. В этом случае поток RTP не будет воспроизводится в тов виде, в котором он прослушивается конечным пользователем, однако это полезно при туннелировании RTP в случае отсутствия изначальных временных параметров пакета. +<br/> +<strong>Непрерывный режим</strong>: Игнорировать временную метку RTP. Воспроизводить поток при его завершении. Такой вариант полезен при отсутствии временной метки RTP. + + + Jitter Buffer + Буфер джиттера + + + RTP Timestamp + Метка времени RTP + + + Uninterrupted Mode + Непрерывный режим + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>Отображать временные метки в виде времени суток (отмечено) или в виде секунд с начала захвата (не отмечено).</p></body></html> + + + Time of Day + Время суток + + + &Export + &Экспорт + + + Export audio of all unmuted selected channels or export payload of one channel. + Экспорт звука всех выбранных каналов со включенным звуком или экспорт полезных данных канала. + + + From &cursor + От &курсора + + + Save audio data started at the cursor + Сохранить аудио данные, начиная с позиции курсора + + + &Stream Synchronized Audio + &Синхронизация звука с потоком + + + Save audio data synchronized to start of the earliest stream. + Сохранить звуковые данные, синхронизированные с началом самого первого потока. + + + &File Synchronized Audio + &Синхронизация звука с файлом + + + Save audio data synchronized to start of the capture file. + Сохранить звуковые данные, синхронизированные с началом файла захвата. + + + &Payload + &Полезные данные + + + Save RTP payload of selected stream. + Сохранить полезные данные RTP выбранного потока. + + + Reset Graph + Сбросить график + + + Reset the graph to its initial state. + Сбросить график в исходное состояние. + + + Go To Setup Packet + Перейти к первому пакету + + + Go to setup packet of stream currently under the cursor + Перейти к первому пакету потока, находящегося под курсором + + + Mute + Отключить звук + + + Mute selected streams + Отключить звук на выбранных потоках + + + Unmute + Отключить звук + + + Unmute selected streams + Включить звук на выбранных потоках + + + Invert muting of selected streams + Обратить отключение звука на выбранных потоках + + + Route audio to left channel of selected streams + Перенаправить звук на левый канал выбранных потоков + + + Route audio to left and right channel of selected streams + Перенаправить звук на правый и левый канал выбранных потоков + + + Route audio to right channel of selected streams + Перенаправить звук на правый канал выбранных потоков + + + Remove Streams + Удалить потоки + + + Remove selected streams from the list + Удалить выбранные потоки из списка + + + All + Все + + + Select all + Выбрать все + + + None + Нет + + + Clear selection + Очистить выбор + + + Invert + Инвертировать + + + Invert selection + Инвертировать выбор + + + Play/Pause + Воспроизведение/Пауза + + + Start playing or pause playing + Начать или остановить воспроизведение + + + Stop + Остановить + + + Stop playing + Остановить воспроизведение + + + I&naudible streams + Б&еззвучные потоки + + + Select/Deselect inaudible streams + Выбрать или отменить выбор беззвучных потоков + + + Inaudible streams + Беззвучные потоки + + + &Select + &Выбрать + + + Select inaudible streams + Выбрать беззвучные потоки + + + &Deselect + &Отменить выбор + + + Deselect inaudible streams + Отменить выбор беззвучных потоков + + + Prepare &Filter + Подготовить &фильтр + + + Prepare a filter matching the selected stream(s). + Подготовить фильтр, совпадающий с выбранными потоками. + + + R&efresh streams + О&бновить потоки + + + Read captured packets from capture in progress to player + Считывать захваченные в процессе записи пакеты для проигрывателя + + + Zoom In + Увеличить масштаб + + + SR (Hz) + Частота дискретизации (Гц) + + + Sample rate of codec + Частота дискретизации кодека + + + PR (Hz) + Скорость воспроизведения (Гц) + + + Play rate of decoded audio (depends e. g. on selected sound card) + Частота воспроизведения декодированного звука (например, может зависеть от выбранной звуковой карты) + + + Zoom Out + Уменьшить масштаб + + + Move Left 10 Pixels + Переместить влево на 10 пикселов + + + Move Right 10 Pixels + Переместить вправо на 10 пикселов + + + Move Left 1 Pixels + Переместить влево на 1 пиксел + + + Move Right 1 Pixels + Переместить вправо на 1 пиксел + + + Go To Packet Under Cursor + Перейти к пакету под курсором + + + Go to packet currently under the cursor + Перейти к пакету под курсором + + + Play the stream + Воспроизвести поток + + + To Left + Левый канал + + + Left + Right + Левый + Правый + + + To Right + Правый канал + + + Invert Muting + Инвертировать отключение звука + + + No devices available + Нет доступных устройств + + + Select + Выбрать + + + Audio Routing + Маршрутизация звука + + + &Play Streams + &Воспроизвести потоки + + + Open RTP player dialog + Открыть диалоговое окно RTP-проигрывателя + + + &Set playlist + &Задать список воспроизведения + + + Replace existing playlist in RTP Player with new one + Заменить существующий список воспроизведения в RTP-проигрывателе новым + + + &Add to playlist + &Добавить к списку воспроизведения + + + Add new set to existing playlist in RTP Player + Добавить новый набор элементов к текущему списку воспроизведения в RTP-проигрывателе + + + &Remove from playlist + &Удалить из списка воспроизведения + + + Remove selected streams from playlist in RTP Player + Удалить выбранные потоки из списка воспроизведения в RTP-проигрывателе + + + No Audio + Без звука + + + Decoding streams... + Декодирование потоков… + + + Out of Sequence + Нарушение последовательности + + + Jitter Drops + Снижение джиттера + + + Wrong Timestamps + Неверные метки времени + + + Inserted Silence + Вставлена пауза + + + Double click on cell to change audio routing + Двойной щелчок по ячейке позволяет изменить маршрутизацию звука + + + %1 streams + Потоков: %1 + + + , %1 selected + , выбрано %1 + + + , %1 not muted + , звук не отключён %1 + + + , start: %1. Double click on graph to set start of playback. + , начать: %1. Двойной щелчок по графику позволяет задать начало воспроизведения. + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + , начало: %1, курсор: %2. Нажмите «G» для перехода к пакету %3. Двойной щелчок по графику позволяет задать начало воспроизведения. + + + Playback of stream %1 failed! + Сбой воспроизведения потока %1! + + + Automatic + Автоматическая + + + WAV (*.wav) + WAV (*.wav) + + + Sun Audio (*.au) + Sun Audio (*.au) + + + Save audio + Сохранение звука + + + Raw (*.raw) + Raw (*.raw) + + + Save payload + Сохранение полезных данных + + + Warning + Предупреждение + + + No stream selected or none of selected streams provide audio + Потоки не выбраны или ни один выбранный поток не содержит звука + + + Error + Ошибка + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + Воспроизведение во всех выбранных потоках должно осуществляться с одинаковой скоростью. Это можно сделать с помощью ручной установки скорости исходящего звукового сигнала. + + + No streams are suitable for save + Отсутствуют потоки пригодные для сохранения + + + Save failed! + Ошибка сохранения! + + + Can't write header of AU file + Не удалось записать заголовок AU-файла + + + Can't write header of WAV file + Не удалось записать заголовок WAV-файла + + + Payload save works with just one audio stream. + Сохранение полезных данных работает только с одним звуковым потоком. + + + Double click to change audio routing + Щелкните дважды для изменения маршрутизации звука + + + Preparing to play... + Подготовка к воспроизведению… + + + Unknown + Неизвестно + + + + RtpStreamDialog + + Dialog + Диалог + + + Source Address + Адрес источника + + + Source Port + Порт источника + + + Destination Address + Адрес назначения + + + Destination Port + Порт назначения + + + SSRC + SSRC + + + Start Time + Время начала + + + Duration + Продолжительность + + + Payload + Полезные данные + + + Packets + Пакеты + + + Lost + Потеряно + + + Max Delta (ms) + Макс. дельта (мс) + + + Max Jitter + Макс. джиттер + + + Mean Jitter + Средний джиттер + + + Status + Состояние + + + <small><i>A hint.</i></small> + <small><i>Подсказка.</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Показывать только диалоги, совпадающие с текущим фильтром отображения</p></body></html> + + + Limit to display filter + Ограничить по фильтру отображения + + + Time of Day + Время суток + + + Find &Reverse + Обратный &поиск + + + Prepare &Filter + Подготовить &фильтр + + + &Export + &Экспорт + + + &Analyze + &Анализ + + + Open the analysis window for the selected stream(s) and add it to it + Открыть окно анализа для выбранного потока (потоков) и добавить их в него + + + Find the reverse stream matching the selected forward stream. + Искать обратный поток, совпадающий с выбранным прямым потоком. + + + Min Delta (ms) + Мин. дельта (мс) + + + Mean Delta (ms) + Средняя дельта (мс) + + + Min Jitter + Мин. джиттер + + + All forward/reverse stream actions + Все действия с прямыми или обратными потоками + + + R + R + + + Find All &Pairs + Найти все &пары + + + Select all streams which are paired in forward/reverse relation + Выбрать все парные разнонаправленные потоки + + + Shift+R + Shift+R + + + Find Only &Singles + Найти &только одиночные потоки + + + Find all streams which don't have paired reverse stream + Найти все потоки, не имеющие парного обратного потока + + + Ctrl+R + Ctrl+R + + + Mark Packets + Отметить пакеты + + + Mark the packets of the selected stream(s). + Отметить пакеты выбранного потока (потоков). + + + M + M + + + All + Все + + + Select all + Выбрать все + + + None + Нет + + + Clear selection + Очистить выбор + + + Invert + Инвертировать + + + Invert selection + Инвертировать выбор + + + Go To Setup + Перейти к настройке + + + Go to the setup packet for this stream. + Перейти к первому пакету для данного потока. + + + G + G + + + Prepare a filter matching the selected stream(s). + Подготовить фильтр, совпадающий с выбранным потоком (потоками). + + + P + P + + + Export the stream payload as rtpdump + Экспортировать полезные данные потока в виде дампа RTP + + + E + E + + + A + А + + + Cop&y + Коп&ировать + + + Open copy menu + Открыть меню копирования + + + Copy as CSV + Копировать в виде CSV + + + Copy stream list as CSV. + Копировать список потоков в виде CSV. + + + Copy as YAML + Копировать в виде YAML + + + Copy stream list as YAML. + Копировать список потоков в виде YAML. + + + RTP Streams + Потоки RTP + + + Select + Выбор + + + as CSV + в виде CSV + + + as YAML + в виде YAML + + + %1 streams + потоки: %1 + + + , %1 selected, %2 total packets + , выбрано: %1, всего пакетов: %2 + + + Save RTPDump As… + Сохранить RTPDump как… + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark — Ассоциации SCTP + + + ID + ИД + + + Port 1 + Порт 1 + + + Port 2 + Порт 2 + + + Number of Packets + Число пакетов + + + Number of DATA Chunks + Число блоков данных + + + Number of Bytes + Число байт + + + Filter Selected Association + Применить фильтр к выбранной ассоциации + + + Analyze + Анализировать + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark — Анализ ассоциации + + + TabWidget + TabWidget + + + Statistics + Статистика + + + Chunk Statistics + Статистика блоков + + + Filter Association + Ассоциация фильтра + + + Number of Data Chunks from EP2 to EP1: + Число блоков данных от EP2 до EP1: + + + Checksum Type: + Тип контрольной суммы: + + + Number of Data Chunks from EP1 to EP2: + Число блоков данных от EP1 до EP2: + + + Number of Data Bytes from EP1 to EP2: + Число байтов данных от EP1 до EP2: + + + Number of Data Bytes from EP2 to EP1: + Число байтов данных от EP2 до EP1: + + + Endpoint 1 + Конечная точка 1 + + + Graph TSN + График TSN + + + Graph Bytes + График байтов + + + Requested Number of Inbound Streams: + Запрошенное число входящих потоков: + + + Port: + Порт: + + + Sent Verification Tag: + Отправленный тег верификации: + + + Minimum Number of Inbound Streams: + Минимальное число входящих потоков: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + Полный список IP-адресов из блока INIT: + + + Minimum Number of Outbound Streams: + Минимальное число исходящих потоков: + + + Graph Arwnd + График Arwnd + + + Endpoint 2 + Конечная точка 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + Полный список IP-адресов из блока INIT_ACK: + + + Provided Number of Outbound Streams: + Поддерживаемое число исходящих потоков: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + Анализ ассоциации SCTP: %1 Порт1 %2 Порт2 %3 + + + No Association found for this packet. + Не удалось найти ассоциацию для данного пакета. + + + Warning + Предупреждение + + + Could not find SCTP Association with id: %1 + Не удалось найти ассоциацию SCTP с ИД: %1 + + + Complete list of IP addresses from INIT Chunk: + Полный список IP-адресов из блока INIT: + + + Complete list of IP addresses from INIT_ACK Chunk: + Полный список IP-адресов из блока INIT_ACK: + + + List of Used IP Addresses + Список использованных IP-адресов + + + Used Number of Inbound Streams: + Используемое число входящих потоков: + + + Used Number of Outbound Streams: + Используемое число исходящих потоков: + + + + SCTPChunkStatisticsDialog + + Dialog + Диалоговое окно + + + Association + Ассоциация + + + Endpoint 1 + Конечная точка 1 + + + Endpoint 2 + Конечная точка 2 + + + Save Chunk Type Order + Сохранить порядок типов блоков + + + Hide Chunk Type + Скрыть тип блока + + + Remove the chunk type from the table + Удалить тип блока из таблицы + + + Chunk Type Preferences + Параметры типа блока + + + Go to the chunk type preferences dialog to show or hide other chunk types + Перейти к диалоговому окну параметров типа блока, чтобы отобразить или скрыть другие типы блоков + + + Show All Registered Chunk Types + Показать все зарегистрированные типы блоков + + + Show all chunk types with defined names + Показать все типы блоков с определёнными именами + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + Статистика блока SCTP: %1 Порт1 %2 Порт2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + График SCTP + + + Reset to full size + Сбросить до полного размера + + + Save Graph + Сохранить график + + + goToPacket + Перейти к пакету + + + Go to Packet + Перейти к пакету + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + Данные и объявленное окно приёмника SCTP за период времени: %1 Порт1 %2 Порт2 %3 + + + No Data Chunks sent + Блоки данных не были отправлены + + + Arwnd + Arwnd + + + time [secs] + время [секунды] + + + Advertised Receiver Window [Bytes] + Объявленное окно приёмника [Байты] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>График %1: a_rwnd=%2 Время=%3 сек </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + График SCTP + + + Reset to full size + Сбросить до полного размера + + + Save Graph + Сохранить график + + + goToPacket + Перейти к пакету + + + Go to Packet + Перейти к пакету + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + Данные и объявленное окно приёмника SCTP за период времени: %1 Порт1 %2 Порт2 %3 + + + No Data Chunks sent + Блоки данных не были отправлены + + + Bytes + Байты + + + time [secs] + время [секунды] + + + Received Bytes + Полученные байты + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>График %1: Получено байт=%2 Время=%3 сек </i></small> + + + + SCTPGraphDialog + + SCTP Graph + График SCTP + + + Relative TSNs + Относительные TSN + + + Only SACKs + Только SACK + + + Only TSNs + Только TSN + + + Show both + Показать оба + + + Reset to full size + Сброс до полного размера + + + Save Graph + Сохранить график + + + goToPacket + Перейти к пакету + + + Go to Packet + Перейти к пакету + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + TSN и SACK в SCTP за период времени: %1 Порт1 %2 Порт2 %3 + + + No Data Chunks sent + Блоки данных не были отправлены + + + CumTSNAck + CumTSNAck + + + Gap Ack + Gap Ack + + + NR Gap Ack + NR Gap Ack + + + Duplicate Ack + Дублированные Ack + + + TSN + TSN + + + time [secs] + время [секунды] + + + TSNs + TSN + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 Время: %3 сек </i></small> + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Сохранить график как… + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <small><i>Выберите команду и при необходимости введите фильтр, а затем нажмите «Применить».</i></small> + + + Command: + Команда: + + + SCSI Service Response Times + Время ответа сервиса SCSI + + + + SearchFrame + + Frame + Кадр + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>Поиск в столбце «Info» списка пакетов (область сводных данных), в декодированных метках содержимого пакетов (область просмотра дерева) или в преобразованных в ASCII данных пакета (область просмотра данных в шестнадцатеричном отображении).</p></body></html> + + + Packet list + Список пакетов + + + Packet details + Подробная информация о пакете + + + Packet bytes + Байты пакета + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Поиск строк, содержащих символы в обычной (UTF-8 и ASCII) или многобайтовой (UTF-16) кодировке.</p></body></html> + + + Narrow & Wide + Обычные и многобайтовые + + + Narrow (UTF-8 / ASCII) + Обычные (UTF-8 / ASCII) + + + Wide (UTF-16) + Многобайтовые (UTF-16) + + + Case sensitive + Чувствительность к регистру + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>Поиск данных можно выполнять с использованием фильтров отображения (например, «ip.addr==10.1.1.1»), шестнадцатеричных строк (например, «fffffda5»), обычных текстовых строк (например, «Моя строка») и регулярных выражений (например, «colou?r»).</p></body></html> + + + Display filter + Фильтр отображения + + + Hex value + Шестнадцатеричное значение + + + String + Строка + + + Regular Expression + Регулярное выражение + + + Find + Найти + + + Cancel + Отмена + + + No valid search type selected. Please report this to the development team. + Не выбран допустимый тип поиска. Cообщите об этом команде разработчиков. + + + Invalid filter. + Недопустимый фильтр. + + + That filter doesn't test anything. + Этот фильтр не производит тестирование. + + + That's not a valid hex string. + Это недопустимая шестнадцатеричная строка. + + + You didn't specify any text for which to search. + Не указан текст для поиска. + + + No valid character set selected. Please report this to the development team. + Не выбран допустимый набор символов. Сообщите об этом команде разработчиков. + + + No valid search area selected. Please report this to the development team. + Не выбрано допустимое пространство поиска. Сообщите об этом команде разработчиков. + + + Searching for %1… + Поиск %1… + + + No packet contained those bytes. + Отсутствует пакет, содержащий эти байты. + + + No packet contained that string in its Info column. + Отсутствует пакет, содержащий эту строку в столбце «Info». + + + No packet contained that string in its dissected display. + Отсутствует пакет, содержащий эту строку в декодированном виде. + + + No packet contained that string in its converted data. + Отсутствует пакет, содержащий эту строку в своих преобразованных данных. + + + No packet matched that filter. + Ни в одном пакете не найдено совпадений с этим фильтром. + + + + SequenceDialog + + Call Flow + Вызов потока + + + Time + Время + + + Comment + Комментарий + + + No data + Нет данных + + + %Ln node(s) + + %Ln node + %Ln nodes + + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII (*.txt) + + + Save Graph As… + Сохранить график как… + + + Flow + Поток + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Практичные и удобные комбинации клавиш для экономии рабочего времени</h3> +<table><tbody> + +<tr><th>+</th><td>Увеличить масштаб</td></th> +<tr><th>-</th><td>Уменьшить масштаб</td></th> +<tr><th>0</th><td>Вернуть график к исходному состоянию</td></th> + +<tr><th>→</th><td>Переместить вправо на 10 пикселов</td></th> +<tr><th>←</th><td>Переместить влево на 10 пикселов</td></th> +<tr><th>↑</th><td>Переместить вверх на 10 пикселов</td></th> +<tr><th>↓</th><td>Переместить вниз на 10 пикселов</td></th> +<tr><th><i>Shift+</i>→</th><td>Переместить вправо на 1 пиксел</td></th> +<tr><th><i>Shift+</i>←</th><td>Переместить влево на 1 пиксел</td></th> +<tr><th><i>Shift+</i>↑</th><td>Переместить вверх на 1 пиксел</td></th> +<tr><th><i>Shift+</i>↓</th><td>Переместить вниз на 1 пиксел</td></th> + +<tr><th>g</th><td>Перейти к пакету под курсором</td></th> +<tr><th>n</th><td>Перейти к следующему пакету</td></th> +<tr><th>p</th><td>Перейти к предыдущему пакету</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>Подсказка</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>Показывать только потоки, совпадающие с текущим значением фильтра отображения</p></body></html> + + + Limit to display filter + Ограничить по фильтру отображения + + + Flow type: + Тип потока: + + + Addresses: + Адреса: + + + Any + Любой + + + Network + Сетевой + + + Reset Diagram + Сброс диаграммы + + + Reset &Diagram + Сброс &диаграммы + + + Reset the diagram to its initial state. + Сброс диаграммы в исходное состояние. + + + 0 + 0 + + + &Reset Diagram + &Сброс диаграммы + + + Reset the diagram to its initial state + Сбросить диаграмму в исходное состояние + + + &Export + &Экспорт + + + Export diagram + Экспорт диаграммы + + + Zoom In + Увеличить масштаб + + + + + + + + + Zoom Out + Уменьшить масштаб + + + - + - + + + Move Up 10 Pixels + Переместить вверх на 10 пикселов + + + Up + Вверх + + + Move Left 10 Pixels + Переместить влево на 10 пикселов + + + Left + Влево + + + Move Right 10 Pixels + Переместить вправо на 10 пикселов + + + Right + Вправо + + + Move Down 10 Pixels + Переместить вниз на 10 пикселов + + + Down + Вниз + + + Move Up 1 Pixel + Переместить вверх на 1 пиксел + + + Shift+Up + Shift+Вверх + + + Move Left 1 Pixel + Переместить влево на 1 пиксел + + + Shift+Left + Shift+Влево + + + Move Right 1 Pixel + Переместить вправо на 1 пиксел + + + Shift+Right + Shift+Вправо + + + Move Down 1 Pixel + Переместить вниз на 1 пиксел + + + Shift+Down + Shift+Вниз + + + Go To Packet Under Cursor + Переход к пакету под курсором + + + Go to packet currently under the cursor + Перейти к пакету, находящемуся под курсором + + + G + G + + + All Flows + Все потоки + + + Show flows for all packets + Показать потоки для всех пакетов + + + 1 + 1 + + + TCP Flows + Потоки TCP + + + Show only TCP flow information + Показать только сведения о потоке TCP + + + Go To Next Packet + Переход к следующему пакету + + + Go to the next packet + Перейти к следующему пакету + + + N + N + + + Go To Previous Packet + Переход к предыдущему пакету + + + Go to the previous packet + Перейти к предыдущему пакету + + + P + P + + + Select RTP Stream + Выбрать поток RTP + + + Select RTP stream in RTP Streams dialog + Выбрать поток RTP в диалоговом окне «Пакеты RTP» + + + S + S + + + Deselect RTP Stream + Отменить выбор потока RTP + + + Deselect RTP stream in RTP Streams dialog + Отменить выбор потока RTP в диалоговом окне «Пакеты RTP» + + + D + D + + + + ShortcutListModel + + Shortcut + Комбинация клавиш + + + Name + Имя + + + Description + Описание + + + + ShowPacketBytesDialog + + Show Packet Bytes + Отображение байтов пакета + + + Hint. + Подсказка. + + + Decode as + Декодировать как + + + Show as + Отображать как + + + Start + Начало + + + End + Конец + + + Find: + Найти: + + + Find &Next + Найти &следующий + + + Frame %1, %2, %Ln byte(s). + + + + + + + + None + Нет + + + Base64 + Base64 + + + Compressed + Сжато + + + Hex Digits + Шестнадцатеричные символы + + + Percent-Encoding + Процентное кодирование + + + Quoted-Printable + Quoted-Printable + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII и управление + + + C Array + Массив C + + + EBCDIC + EBCDIC + + + Hex Dump + Шестнадцатеричный дамп + + + HTML + HTML + + + Image + Изображение + + + Raw + Необработанные данные + + + Rust Array + Массив Rust + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Печатать + + + Copy + Копировать + + + Save as… + Сохранить как… + + + Save Selected Packet Bytes As… + Сохранение выбранных байтов пакета как… + + + Displaying %Ln byte(s). + + + + + + + + JSON + JSON + + + Regex Find: + Поиск по регулярному выражению: + + + + ShowPacketBytesTextEdit + + Show Selected + Показать выбранное + + + Show All + Показать всё + + + + SplashOverlay + + Initializing dissectors + Инициализация диссекторов + + + Initializing tap listeners + Инициализация перехватчиков для прослушивания трафика + + + Initializing external capture plugins + Инициализация внешних модулей захвата + + + Registering dissectors + Регистрация диссекторов + + + Registering plugins + Registering dissector + Регистрация подключаемых модулей + + + Handing off dissectors + Передача диссекторов + + + Handing off plugins + Передача подключаемых модулей + + + Loading Lua plugins + Загрузка подключаемых модулей Lua + + + Removing Lua plugins + Удаление подключаемых модулей Lua + + + Loading module preferences + Загрузка параметров модуля + + + Finding local interfaces + Поиск локальных интерфейсов + + + Applying changed preferences + + + + (Unknown action) + (Неизвестное действие) + + + + StatsTreeDialog + + Configuration not found + Конфигурация не найдена + + + Unable to find configuration for %1. + Не удалось найти конфигурацию для %1. + + + + StripHeadersDialog + + Dialog + Диалоговое окно + + + Display filter: + Фильтр отображения: + + + + SupportedProtocolsDialog + + Dialog + Диалоговое окно + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Поиск в списке имён полей.</p></body></html> + + + Search: + Поиск: + + + <small><i>Gathering protocol information…</i></small> + <small><i>Сбор информации о протоколе…</i></small> + + + Supported Protocols + Поддерживаемые протоколы + + + %1 protocols, %2 fields. + Протоколов: %1, полей: %2. + + + + SupportedProtocolsModel + + Name + Имя + + + Filter + Фильтр + + + Type + Тип + + + Description + Описание + + + + SyntaxLineEdit + + Invalid filter: %1 + Недопустимый фильтр: %1 + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + Вместо устаревшего «%1» рекомендуется использовать «%2». Подробную информацию смотри в Справке, раздел 6.4.8. + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + Диалоговое окно + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Практичные и удобные комбинации клавиш для экономии рабочего времени</h3> +<table><tbody> + +<tr><th>+</th><td>Увеличить масштаб</td></th> +<tr><th>-</th><td>Уменьшить масштаб</td></th> +<tr><th>x</th><td>Увеличить масштаб по оси X</td></th> +<tr><th>X</th><td>Уменьшить масштаб по оси X</td></th> +<tr><th>y</th><td>Увеличить масштаб по оси Y</td></th> +<tr><th>Y</th><td>Уменьшить масштаб по оси Y</td></th> +<tr><th>0</th><td>Вернуть график к исходному состоянию</td></th> + +<tr><th>→</th><td>Переместить вправо на 10 пикселов</td></th> +<tr><th>←</th><td>Переместить влево на 10 пикселов</td></th> +<tr><th>↑</th><td>Переместить вверх на 10 пикселов</td></th> +<tr><th>↓</th><td>Переместить вниз на 10 пикселов</td></th> +<tr><th><i>Shift+</i>→</th><td>Переместить вправо на 1 пиксел</td></th> +<tr><th><i>Shift+</i>←</th><td>Переместить влево на 1 пиксел</td></th> +<tr><th><i>Shift+</i>↑</th><td>Переместить вверх на 1 пиксел</td></th> +<tr><th><i>Shift+</i>↓</th><td>Переместить вниз на 1 пиксел</td></th> + +<tr><th><i>Pg Up</i></th><td>Следующий поток</td></th> +<tr><th><i>Pg Dn</i></th><td>Предыдущий поток</td></th> +<tr><th>d</th><td>Переключить направление (поменять местами конечные точки TCP)</td></th> +<tr><th>g</th><td>Перейти к пакету под курсором</td></th> + +<tr><th>z</th><td>Переключить режим работы мыши на перетаскивание или изменение масштаба</td></th> +<tr><th>s</th><td>Переключение номеров последовательностей с относительных на абсолютные и обратно</td></th> +<tr><th>t</th><td>Переключение на время начала захвата или сеанса</td></th> +<tr><th>Space</th><td>Переключить перекрестье</td></th> + +<tr><th>1</th><td>График времени приёма-передачи</td></th> +<tr><th>2</th><td>График пропускной способности</td></th> +<tr><th>3</th><td>График времени / последовательности в стиле Stevens</td></th> +<tr><th>4</th><td>График времени / последовательности в стиле tcptrace</td></th> +<tr><th>5</th><td>График масштабирования окна</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>Наведите указатель мыши для отображения комбинаций клавиш</i></small> + + + Type + Тип + + + MA Window (s) + Окно MA (с) + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + Разрешить выбор сегментов SACK и пакетов данных щелчком мыши на графике + + + Select SACKs + select SACKs + Выбирать SACK + + + Stream + Поток + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Переключить направление соединения (просмотр обратного потока).</p></body></html> + + + Switch Direction + Переключить направление + + + Mouse + Мышь + + + Drag using the mouse button. + Перетаскивание с помощью кнопки мыши. + + + drags + перетаскивание + + + Select using the mouse button. + Выделять кнопкой мыши. + + + zooms + масштаб + + + Display Round Trip Time vs Sequence Number + Отображать время приёма-передачи в зависимости от номера последовательности + + + RTT By Sequence Number + RTT по номеру последовательности + + + Display graph of Segment Length vs Time + Показывать график зависимости длины сегмента от времени + + + Segment Length + Длина сегмента + + + Display graph of Mean Transmitted Bytes vs Time + Показывать график зависимости средней величины переданных байтов от времени + + + Display graph of Mean ACKed Bytes vs Time + Показать график зависимости cреднего числа подтверждённых байтов от времени + + + Goodput + Полезная пропускная способность + + + Display graph of Receive Window Size vs Time + Показать график зависимости размера окна получения от времени + + + Rcv Win + Окно получения + + + Display graph of Outstanding Bytes vs Time + Показать график зависимости неподтверждённых байтов от времени + + + Bytes Out + Отправленные байты + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Вернуть график в исходное состояние.</p></body></html> + + + Reset + Сброс + + + Reset Graph + Сбросить график + + + Reset the graph to its initial state. + Сбросить график в исходное состояние. + + + 0 + 0 + + + Zoom In + Увеличить масштаб + + + + + + + + + Zoom Out + Уменьшить масштаб + + + - + - + + + Move Up 10 Pixels + Переместить вверх на 10 пикселов + + + Up + Вверх + + + Move Left 10 Pixels + Переместить влево на 10 пикселов + + + Left + Влево + + + Move Right 10 Pixels + Переместить вправо на 10 пикселов + + + Right + Вправо + + + Move Down 10 Pixels + Переместить вниз на 10 пикселов + + + Down + Вниз + + + Move Up 1 Pixel + Переместить вверх на 1 пиксел + + + Shift+Up + Shift+Вверх + + + Move Left 1 Pixel + Переместить влево на 1 пиксел + + + Shift+Left + Shift+Влево + + + Move Right 1 Pixel + Переместить вправо на 1 пиксел + + + Shift+Right + Shift+Вправо + + + Move Down 1 Pixel + Переместить вниз на 1 пиксел + + + Shift+Down + Shift+Вниз + + + Next Stream + Следующий поток + + + Go to the next stream in the capture + Перейти к следующему потоку в составе захваченных данных + + + PgUp + PgUp + + + Previous Stream + Предыдущий поток + + + Go to the previous stream in the capture + Перейти к предыдущему потоку в составе захваченных данных + + + PgDown + PgDown + + + Switch direction (swap TCP endpoints) + Переключить направление (поменять местами конечные точки TCP) + + + D + D + + + Go To Packet Under Cursor + Переход к пакету под курсором + + + Go to packet currently under the cursor + Перейти к пакету, находящемуся под курсором + + + G + G + + + Drag / Zoom + Перетаскивание / масштаб + + + Toggle mouse drag / zoom behavior + Переключить режим работы мыши на перетаскивание или изменение масштаба + + + Z + Z + + + Relative / Absolute Sequence Numbers + Относительные / абсолютные номера последовательностей + + + Toggle relative / absolute sequence numbers + Переключение номеров последовательностей с относительных на абсолютные и обратно + + + S + S + + + Capture / Session Time Origin + Время начала захвата / сеанса + + + Toggle capture / session time origin + Переключение на время начала захвата или сеанса + + + T + T + + + Crosshairs + Перекрестия + + + Toggle crosshairs + Переключить перекрестия + + + Space + Пробел + + + Round Trip Time + Время приёма-передачи + + + Switch to the Round Trip Time graph + Переключить на отображение графика времени приёма-передачи + + + 1 + 1 + + + Throughput + Пропускная способность + + + Switch to the Throughput graph + Переключить на отображение графика пропускной способности + + + 2 + 2 + + + Time / Sequence (Stevens) + Время / последовательность (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + Переключить на отображение графика времени или последовательности в стиле Stevens + + + 3 + 3 + + + Window Scaling + Масштабирование окна + + + Switch to the Window Scaling graph + Переключить на отображение графика масштабирования окна + + + 5 + 5 + + + Time / Sequence (tcptrace) + Время / последовательность (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + Переключить на отображение графика времени или последовательности в стиле tcptrace + + + 4 + 4 + + + Zoom In X Axis + Увеличить масштаб по оси X + + + X + X + + + Zoom Out X Axis + Уменьшить масштаб по оси X + + + Shift+X + Shift+X + + + Zoom In Y Axis + Увеличить масштаб по оси Y + + + Y + Y + + + Zoom Out Y Axis + Уменьшить масштаб по оси Y + + + Shift+Y + Shift+Y + + + Save As… + Сохранить как… + + + No Capture Data + Данные захвата отсутствуют + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + пакетов: %1 %2, %3 пакетов: %4 %5, %6 + + + Sequence Numbers (Stevens) + Номера последовательностей (Stevens) + + + Sequence Numbers (tcptrace) + Номера последовательностей (tcptrace) + + + (MA) + (Скользящее среднее) + + + (%1 Segment MA) + (Скользящее среднее сегмента: %1) + + + [not enough data] + [недостаточно данных] + + + for %1:%2 %3 %4:%5 + для %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3 длина %4 последовательность %5 подтверждение %6 окно %7) + + + Click to select packet + Щёлкните для выбора пакета + + + Packet + Пакет + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Разместить в соответствии с масштабом, x = от %1 до %2, y = от %3 до %4 + + + Unable to select range. + Не удалось выбрать диапазон. + + + Click to select a portion of the graph. + Щёлкните для выбора участка графика. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Сохранить график как… + + + + TLSKeylogDialog + + Dialog + + + + Browse… + Обзор… + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + Сохранить + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + Диалоговое окно + + + Item + Пункт + + + <small><i>A hint.</i></small> + <small><i>Подсказка.</i></small> + + + Display filter: + Фильтр отображения: + + + Regenerate statistics using this display filter + Переформировать статистику с применением этого фильтра отображения + + + Apply + Применить + + + Copy + Копировать + + + Copy a text representation of the tree to the clipboard + Копировать текстовое представление дерева в буфер обмена + + + Save as… + Save as... + Сохранить как… + + + Save the displayed data in various formats + Сохранение отображаемых данных в различных форматах + + + Collapse All + Свернуть всё + + + Expand All + Развернуть всё + + + Save Statistics As… + Сохранить статистику как… + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + Тестовый файл (*.txt);;Файл CSV (*.csv);;Документ XML (*.xml);;Документ YAML (*.yaml) + + + Plain text file (*.txt) + Текстовый файл (*.txt) + + + Error saving file %1 + Ошибка сохранения файла %1 + + + + TimeShiftDialog + + Shift all packets by + Сместить все пакеты на + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[чч:]мм:]сс[.ссс] </span></p></body></html> + + + Set the time for packet + Задать время для пакета + + + to + на + + + …then set packet + ...then set packet + …затем для пакета + + + and extrapolate the time for all other packets + и экстраполировать время для всех остальных пакетов + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[ГГГГ-ММ-ДД] чч:мм:сс[.ссс] </span></p></body></html> + + + Undo all shifts + Отменить все сдвиги + + + Time Shift + Временной сдвиг + + + Frame numbers must be between 1 and %1. + Номера кадров должны находится в диапазоне между 1 и %1. + + + Invalid frame number. + Недопустимый номер кадра. + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + Ошибка файла карты + + + Could not open base file %1 for reading: %2 + Не удалось открыть файл базы %1 для чтения: %2 + + + No endpoints available to map + Отсутствуют конечные точки для сопоставления + + + Unable to create temporary file + Не удалось создать временный файл + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>Показывать разрешённые адреса и имена портов вместо их значений в обычном виде. Для разрешения имён необходимо включить соответствующий параметр.</p></body></html> + + + Name resolution + Разрешение имён + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Показывать только диалоги, соответствующие текущему фильтру отображения</p></body></html> + + + Limit to display filter + Ограничить по фильтру отображения + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + <html><head/><body><p>Отображать только типы, совпадающие со значением фильтра</p></body></html> + + + Filter list for specific type + Фильтровать список по указанному типу + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>Показывать абсолютное время в столбце времени запуска.</p></body></html> + + + GroupBox + Групповой блок + + + Absolute start time + Абсолютное время запуска + + + Copy + Копировать + + + Unknown + Неизвестно + + + + TrafficTree + + Resize all columns to content + Изменять размер всех колонок по размеру содержимого + + + Filter on stream id + Фильтровать по ИД потока + + + Copy %1 table + Копировать таблицу %1 + + + as CSV + в виде CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + Копировать все значения этой страницы в буфер обмена в формате CSV (Comma Separated Values). + + + as YAML + в виде YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + Копировать все значения этой страницы в буфер обмена в формате сериализации данных YAML. + + + as JSON + в виде JSON + + + Copy all values of this page to the clipboard in the JSON data serialization format. + Копировать все значения этой страницы в буфер обмена в формате сериализации данных JSON. + + + Save data as raw + Сохранить данные в необработанном виде + + + Disable data formatting for export/clipboard and save as raw data + Не применять форматирование данных для экспорта или буфера обмена и сохранить их в необработанном виде + + + + TrafficTreeHeaderView + + Less than + Меньше + + + Greater than + Больше + + + Equal + Равно + + + Columns to display + Отображаемые столбцы + + + Filter %1 by + Фильтровать «%1» по + + + Enter filter value + Введите значение фильтра + + + + TrafficTypesModel + + Protocol + Протокол + + + + UatDialog + + Create a new entry. + Создать новую запись. + + + Remove this entry. + Remove this profile. + Удалить эту запись. + + + Copy this entry. + Copy this profile. + Копировать эту запись. + + + Move entry up. + Переместить запись выше. + + + Move entry down. + Переместить запись ниже. + + + Clear all entries. + Очистить все записи. + + + Unknown User Accessible Table + Неизвестная таблица доступа пользователей + + + Open + Открыть + + + + UatFrame + + Frame + Кадр + + + Create a new entry. + Создать новую запись. + + + Remove this entry. + Удалить эту запись. + + + Copy this entry. + Копировать эту запись. + + + Move entry up. + Переместить запись выше. + + + Move entry down. + Переместить запись ниже. + + + Clear all entries. + Очистить все записи. + + + Copy entries from another profile. + Копировать записи из другого профиля. + + + Copy from + Копировать из + + + Unknown User Accessible Table + Неизвестная таблица доступа пользователей + + + Open + Открыть + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Показывать только диалоги, совпадающие с текущим фильтром отображения</p></body></html> + + + Limit to display filter + Ограничить по фильтру отображения + + + Time of Day + Время суток + + + Flow &Sequence + &Последовательность потоков + + + Show flow sequence for selected call(s). + Показать последовательность потоков для выбранного звонка (звонков). + + + Prepare &Filter + Подготовить &фильтр + + + Prepare a filter matching the selected calls(s). + Подготовить фильтр, совпадающий с выбранным звонком (звонками). + + + Cop&y + Коп&ировать + + + Open copy menu + Открыть меню копирования + + + All + Все + + + Select all + Выбрать все + + + None + Нет + + + Invert + Инвертировать + + + Invert selection + Инвертировать выбор + + + Select related RTP streams + Выбрать связанные потоки RTP + + + Select RTP streams related to selected calls in RTP Streams dialog + Выбрать потоки RTP, связанные с выбранными звонками, в диалоговом окне «Потоки RTP» + + + S + S + + + Deselect related RTP Streams + Отменить выделение связанных потоков RTP + + + D + D + + + Clear selection + Очистить выбор + + + Display time as time of day + Отображать время в виде время суток + + + Copy as CSV + Копировать в виде CSV + + + Copy stream list as CSV. + Копировать список потоков в виде CSV. + + + Copy as YAML + Копировать в виде YAML + + + Copy stream list as YAML. + Копировать список потоков в виде YAML. + + + SIP Flows + Потоки SIP + + + VoIP Calls + Звонки VoIP + + + as CSV + в виде CSV + + + as YAML + в виде YAML + + + Select + Выбор + + + + VoipCallsInfoModel + + On + Включено + + + Off + Выключено + + + Tunneling: %1 Fast Start: %2 + Туннелирование: %1 Быстрый старт: %2 + + + Start Time + Время старта + + + Stop Time + Время останова + + + Initial Speaker + Инициатор звонка + + + From + Из + + + To + В + + + Protocol + Протокол + + + Duration + Продолжительность + + + Packets + Пакеты + + + State + Состояние + + + Comments + Комментарии + + + + WelcomePage + + Form + Форма + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Добро пожаловать в Wireshark</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>Открыть файл в файловой системе компьютера</p></body></html> + + + <h2>Open</h2> + <h2>Открыть</h2> + + + Recent capture files + Недавние файлы захвата + + + Capture files that have been opened previously + Открывавшиеся ранее файлы захвата + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>Пакеты, захваченные в реальном времени в сети.</p></body></html> + + + <h2>Capture</h2> + <h2>Захват</h2> + + + …using this filter: + …с помощью этого фильтра: + + + Interface list + Список интерфейсов + + + List of available capture interfaces + Список доступных интерфейсов для захвата + + + <h2>Learn</h2> + <h2>Учить</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">Руководство пользовователя</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Вики-страница</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Вопросы и ответы</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Список рассылки</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">Конференции «SharkFest»</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Чат Wireshark на Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Пожертвовать</a></th> + +</tr></table> +</body></html> + + + Show in Finder + Показать в поиске + + + Show in Folder + Показать в папке + + + Welcome to %1 + Добро пожаловать в %1 + + + All interfaces shown + Все интерфейсы показаны + + + %n interface(s) shown, %1 hidden + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + You are sniffing the glue that holds the Internet together using Wireshark + Все составные звенья Интернета анализировать поможет Wireshark + + + You are running Wireshark + Работа производится в Wireshark + + + You receive automatic updates. + Обновления выполняются автоматически. + + + You have disabled automatic updates. + Автоматическое получение обновлений отключено. + + + not found + не найдено + + + Copy file path + Копировать путь к файлу + + + Remove from list + Удалить из списка + + + + WirelessFrame + + Frame + Кадр + + + Interface + Интерфейс + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>Задать канал 802.11.</p></body></html> + + + Channel + Канал + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>Во время захвата показывать все кадры , как с допустимой последовательностью проверки кадров (FCS), так и с недопустимой FCS.</p></body></html> + + + FCS Filter + Фильтр FCS + + + All Frames + Все кадры + + + Valid Frames + Допустимые кадры + + + Invalid Frames + Недопустимые кадры + + + Wireless controls are not supported in this version of Wireshark. + Управление беспроводными соединениями в этой версии Wireshark не поддерживается. + + + External Helper + Внешний помощник + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>Показать параметры IEEE 802.11, включая ключи дешифрования.</p></body></html> + + + 802.11 Preferences + Параметры 802.11 + + + AirPcap Control Panel + Панель управления AirPcap + + + Open the AirPcap Control Panel + Открыть панель управления AirPcap + + + Unable to set channel or offset. + Невозможно задать канал или смещение. + + + Unable to set FCS validation behavior. + Невозможно установить режим проверки FCS. + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + Пакет с номером %1 не содержит метку времени TSF, шкала времени отображаться не будет. + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + Пакет с номером %u имеет значительное отрицательное смещение TSF, шкала времени отображаться не будет. Возможно, неверно задана точка отсчёта TSF? + + + + WiresharkDialog + + Failed to attach to tap "%1" + Сбой при подключении к перехватчику данных «%1» + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Перейти к пакету + + + Cancel + Отмена + + + File Set + Набор файлов + + + Export Packet Dissections + Экспорт результатов разбора пакетов + + + Export Objects + Экспортировать объекты + + + &Zoom + &Масштаб + + + &Time Display Format + &Формат отображения времени + + + Copy + Копировать + + + Manual pages + Документация + + + Apply as Filter + Применить как фильтр + + + Prepare as Filter + Подготовить в качестве фильтра + + + SCTP + SCTP + + + TCP Stream Graphs + Графики потока TCP + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &Файл + + + &Capture + &Захват + + + &Help + &Справка + + + &Go + &Запуск + + + &View + &Вид + + + &Analyze + &Анализ + + + Follow + Отслеживать + + + &Statistics + &Статистика + + + 29West + 29West + + + Topics + Темы + + + Queues + Очереди + + + UIM + UIM + + + Telephon&y + Телефони&я + + + RTSP + RTSP + + + &Edit + &Правка + + + Packet Comments + Комментарии к пакету + + + Main Toolbar + Главная панель инструментов + + + Display Filter Toolbar + Панель инструментов фильтра отображения + + + Open a capture file + Открыть файл захвата + + + Quit Wireshark + Выйти из Wireshark + + + &Start + &Старт + + + Start capturing packets + Начать захват пакетов + + + S&top + С&топ + + + Stop capturing packets + Остановить захват пакетов + + + No files found + Файлы не найдены + + + &Contents + &Содержание + + + Wireshark Filter + Фильтр Wireshark + + + TShark + TShark + + + Rawshark + Rawshark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + Text2pcap + + + Website + Веб-сайт + + + Downloads + Загрузки + + + Wiki + Вики-страница + + + Sample Captures + Образцы захвата + + + &About Wireshark + &О Wireshark + + + Ask (Q&&A) + Задать вопрос + + + Next Packet + Следующий пакет + + + Go to the next packet + Перейти к следующему пакету + + + Previous Packet + Предыдущий пакет + + + Go to the previous packet + Перейти к предыдущему пакету + + + First Packet + Первый пакет + + + Go to the first packet + Перейти к первому пакету + + + Last Packet + Последний пакет + + + Go to the last packet + Перейти к последнему пакету + + + E&xpand Subtrees + Р&азвернуть поддеревья + + + Expand the current packet detail + Развернуть подробное описание текущего пакета + + + &Expand All + &Развернуть всё + + + Expand packet details + Развернуть подробное описание пакета + + + Collapse &All + Свернуть &всё + + + Collapse all packet details + Свернуть подробное описание всех пакетов + + + Go to specified packet + Перейти к указанному пакету + + + Merge one or more files + Объединить один или несколько файлов + + + Import a file + Импортировать файл + + + &Save + &Сохранить + + + Save as a different file + Сохранить в другом файле + + + Export specified packets + Экспортировать указанные пакеты + + + Export TLS Session Keys… + Экспортировать ключи сеансов TLS… + + + List Files + Список файлов + + + Next File + Следующий файл + + + Previous File + Предыдущий файл + + + &Reload + &Перезагрузить + + + Options + Опции + + + Capture options + Опции захвата + + + Capture filters + Фильтры захвата + + + Refresh Interfaces + Обновить интерфейсы + + + Refresh interfaces + Обновить интерфейсы + + + &Restart + &Перезапуск + + + Restart current capture + Перезапустить текущий захват + + + As &CSV… + В виде &CSV… + + + As "C" &Arrays… + В виде &массива C… + + + As P&SML XML… + В виде P&SML XML… + + + As P&DML XML… + В виде P&DML XML… + + + As &JSON… + В виде &JSON… + + + Description + Описание + + + Field Name + Имя поля + + + Value + Значение + + + As Filter + В виде фильтра + + + Close this capture file + Закрыть этот файл захвата + + + Packet: + Пакет: + + + Interface Toolbars + Панели инструментов для интерфейсов + + + Colorize Conversation + Выделить диалог цветом + + + Internals + Внутренняя организация + + + Additional Toolbars + Дополнительные панели инструментов + + + Conversation Filter + Фильтр диалога + + + Reliable Server Pooling (RSerPool) + Reliable Server Pooling (RSerPool) + + + SOME/IP + SOME/IP + + + &DTN + &DTN + + + Osmux + Osmux + + + &Tools + Tools + &Инструменты + + + Wireless Toolbar + Панель инструментов беспроводного соединения + + + Help contents + Содержание справки + + + FAQs + Вопросы и ответы + + + Next Packet in Conversation + Следующий пакет в диалоге + + + Go to the next packet in this conversation + Перейти к следующему пакету в этом диалоге + + + Previous Packet in Conversation + Предыдущий пакет в диалоге + + + Go to the previous packet in this conversation + Перейти к предыдущему пакету в этом диалоге + + + Next Packet In History + Следующий пакет в истории выбора + + + Go to the next packet in your selection history + Перейти к следующему пакету в истории выбора пакетов + + + Previous Packet In History + Предыдущий пакет в истории выбора + + + Go to the previous packet in your selection history + Перейти к предыдущему пакету в истории выбора пакетов + + + Collapse Subtrees + Свернуть поддеревья + + + Collapse the current packet detail + Свернуть подробную информацию о текущем пакете + + + Go to Packet… + Перейти к пакету… + + + &Merge… + &Объединить… + + + &Import from Hex Dump… + &Импорт из шестнадцатеричного дампа… + + + Save this capture file + Сохранить этот файл захвата + + + Save &As… + Сохранить &как… + + + Export Specified Packets… + Экспортировать указанные пакеты… + + + Export Packet &Bytes… + Экспортировать &байты пакета… + + + &Print… + &Печать… + + + Reload this file + Перезагрузить этот файл + + + Reload as File Format/Capture + Перезагрузить в виде формата файла или захвата + + + Copy this item's description + Копировать описание этого элемента + + + Copy this item's field name + Копировать имя поля этого элемента + + + Copy this item's value + Копировать значение этого элемента + + + Copy this item as a display filter + Копировать этот элемент в качестве фильтра отображения + + + Apply as Column + Использовать в качестве столбца + + + Create a packet list column from the selected field. + Создать из выбранного поля столбец в списке пакетов. + + + Find a packet + Найти пакет + + + Find the next packet + Найти следующий пакет + + + Find the previous packet + Найти предыдущий пакет + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + &Установить/снять отметку пакета (пакетов) + + + Mark All Displayed + Отметить всё отображаемое + + + Mark all displayed packets + Отметить все отображённые пакеты + + + Unmark all displayed packets + Снять отметку всех отображённых пакетов + + + Next Mark + Следующая отметка + + + Go to the next marked packet + Перейти к следующему отмеченному пакету + + + Previous Mark + Предыдущая отметка + + + Go to the previous marked packet + Перейти к предыдущему отмеченному пакету + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + &Игнорировать/отменить игнорирование пакета + + + Ignore All Displayed + Игнорировать всё отображаемое + + + Ignore all displayed packets + Игнорировать все отображённые пакеты + + + Set/Unset Time Reference + Установить/отменить привязку ко времени + + + Set or unset a time reference for this packet + Установить или отменить привязку ко времени для этого пакета + + + Unset All Time References + Отменить все привязки ко времени + + + Remove all time references + Удалить все привязки ко времени + + + Next Time Reference + Следующая привязка ко времени + + + Go to the next time reference + Перейти к следующей привязке ко времени + + + Previous Time Reference + Предыдущая привязка ко времени + + + Go to the previous time reference + Перейти к предыдущей привязке ко времени + + + Shift or change packet timestamps + Сдвинуть или изменить метки времени пакета + + + Delete All Packet Comments + Удалить все комментарии к пакетам + + + Remove all packet comments in the capture file + Удалить все комментарии к пакетам в файле захвата + + + &Configuration Profiles… + &Конфигурационные профили… + + + Configuration profiles + Конфигурационные профили + + + Manage your configuration profiles + Управление конфигурационными профилями + + + Manage Wireshark's preferences + Управление параметрами Wireshark + + + Capture File Properties + Свойства файла захвата + + + Capture file properties + Свойства файла захвата + + + &Protocol Hierarchy + &Иерархия протоколов + + + Show a summary of protocols present in the capture file. + Показать сводную информацию о протоколах, присутствующих в файле захвата. + + + Capinfos + Capinfos + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + Временная последовательность (Stevens) + + + TCP time sequence graph (Stevens) + График временной последовательности TCP (Stevens) + + + Throughput + Пропускная способность + + + Round Trip Time + Время приёма-передачи + + + TCP round trip time + Время приёма-передачи TCP + + + Window Scaling + Масштабирование окна + + + TCP window scaling + Масштабирование окна TCP + + + HTTP/2 Stream + Поток HTTP/2 + + + SIP Call + SIP-звонок + + + Time Sequence (tcptrace) + Временная последовательность (tcptrace) + + + TCP time sequence graph (tcptrace) + График временной последовательности TCP (tcptrace) + + + Analyse this Association + Проанализировать эту ассоциацию + + + Show All Associations + Показать все ассоциации + + + Flow Graph + График потока + + + Flow sequence diagram + Диаграмма последовательности потоков + + + ANCP + ANCP + + + ANCP statistics + Статистика ANCP + + + Packets sorted by Instance ID + Сортировка пакетов по ИД экземпляра + + + BACapp statistics sorted by instance ID + Статистика BACapp с сортировкой по ИД экземпляра + + + Packets sorted by IP + Сортировка пакетов по IP-адресу + + + BACapp statistics sorted by IP + Статистика BACapp с сортировкой по IP-адресу + + + Packets sorted by object type + Сортировка пакетов по типу объекта + + + BACapp statistics sorted by object type + Статистика BACapp с сортировкой по типу объекта + + + Packets sorted by service + Сортировка пакетов по типу службы + + + BACapp statistics sorted by service + Статистика BACapp с сортировкой по типу службы + + + Collectd + Collectd + + + Collectd statistics + Статистика Collectd + + + DNS + DNS + + + DNS statistics + Статистика DNS + + + HART-IP + HART-IP + + + HART-IP statistics + Статистика HART-IP + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + Статистика hpfeeds + + + HTTP2 + HTTP2 + + + HTTP2 statistics + Статистика HTTP2 + + + Packet Counter + Счётчик пакетов + + + HTTP packet counter + Счётчик пакетов HTTP + + + Requests + Запросы + + + HTTP requests + Запросы HTTP + + + Load Distribution + Распределение нагрузки + + + HTTP load distribution + Распределение нагрузки HTTP + + + Packet Lengths + Длины пакетов + + + Packet length statistics + Статистика по длине пакетов + + + Sametime + Sametime + + + Sametime statistics + Статистика Sametime + + + SOME/IP Messages + Сообщения SOME/IP + + + SOME/IP Message statistics + Статистика по сообщениям SOME/IP + + + SOME/IP-SD Entries + Записи SOME/IP-SD + + + SOME/IP-SD Entries statistics + Статистика по записям SOME/IP-SD + + + &LTP + &LTP + + + LTP segment and block statistics + Статистика по сегментам и блокам LTP + + + &ISUP Messages + Сообщения &ISUP + + + ISUP message statistics + Статистика по сообщениям ISUP + + + Osmux packet counts + Подсчёт пакетов Osmux + + + RTSP packet counts + Подсчёт пакетов RTSP + + + SM&PP Operations + Операции SM&PP + + + SMPP operation statistics + Статистика по операциям SMPP + + + &UCP Messages + Сообщения &UCP + + + UCP message statistics + Статистика по сообщениям UCP + + + F1AP + F1AP + + + F1AP Messages + Сообщения F1AP + + + NGAP + NGAP + + + NGAP Messages + Сообщения NGAP + + + Change the way packets are dissected + Изменить способ разбора пакетов + + + Reload Lua Plugins + Перезагрузить подключаемые модули Lua + + + Reload Lua plugins + Перезагрузить подключаемые модули Lua + + + Advertisements by Topic + Объявления по теме + + + Advertisements by Source + Объявления по источнику + + + Advertisements by Transport + Объявления по транспорту + + + Queries by Topic + Запросы по теме + + + Queries by Receiver + Запросы по получателю + + + Wildcard Queries by Pattern + Маскированные запросы по шаблону + + + Wildcard Queries by Receiver + Маскированные запросы по получателю + + + Advertisements by Queue + Объявления по очереди + + + Queries by Queue + Запросы по очереди + + + Streams + Потоки + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + Фильтровать эту ассоциацию + + + Strip Headers… + Удалить заголовки… + + + Strip headers and export higher level encapsulations to file + Удаление заголовков и экспорт инкапсуляций более высокого уровня в файл + + + &I/O Graphs + &Графики ввода/вывода + + + &Conversations + &Диалоги + + + &Endpoints + &Конечные точки + + + Shrink the main window text + Уменьшить текст главного окна + + + Return the main window text to its normal size + Вернуть нормальный размер текста главного окна + + + Reset Layout + Сбросить разметку + + + Reset appearance layout to default size + Сбросить внешний вид разметки к размеру по умолчанию + + + Seconds Since First Captured Packet + Количество секунд с момента захвата первого пакета + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + Диаграмма &пакета + + + Show or hide the packet diagram + Показать или скрыть диаграмму пакета + + + Show each conversation hash table + Показать хэш-таблицу каждого диалога + + + Show each dissector table and its entries + Показать таблицу каждого диссектора и входящие в неё записи + + + Show the currently supported protocols and display filter fields + Показать поддерживаемые в настоящий момент протоколы и поля фильтра отображения + + + MAC Statistics + Статистика MAC + + + LTE MAC statistics + Статистика LTE MAC + + + RLC Statistics + Статистика RLC + + + LTE RLC statistics + Статистика LTE RLC + + + LTE RLC graph + График LTE RLC + + + MTP3 Summary + Сводные данные MTP3 + + + MTP3 summary statistics + Сводные данные статистики MTP3 + + + Bluetooth Devices + Устройства Bluetooth + + + Bluetooth HCI Summary + Общие сведения о Bluetooth HCI + + + Display Filter &Expression… + Показать &фильтр отображения… + + + Display Filter Expression… + Показать фильтр отображения… + + + REGISTER_STAT_GROUP_RSERPOOL + REGISTER_STAT_GROUP_RSERPOOL + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + Запуск «REGISTER_STAT_GROUP_RSERPOOL» + + + No GSM statistics registered + Статистика GSM не зарегистрирована + + + No LTE statistics registered + Cтатистика LTE не зарегистрирована + + + No MTP3 statistics registered + Статистика MTP3 не зарегистрирована + + + IAX2 Stream Analysis + Анализ потока IAX2 + + + Show Packet Bytes… + Показать байты пакета… + + + Go to &Linked Packet + Перейти к &связанному пакету + + + UDP Multicast Streams + Потоки UDP Multicast + + + Show UTP multicast stream statistics. + Показать статистику потока UTP multicast. + + + WLAN Traffic + Трафик WLAN + + + Show IEEE 802.11 wireless LAN statistics. + Показать статистику беспроводной локальной сети IEEE 802.11. + + + Add a display filter button. + Добавить кнопку фильтра отображения. + + + Firewall ACL Rules + ACL-правила межсетевого экрана + + + Create firewall ACL rules + Создать ACL-правила межсетевого экрана + + + &Full Screen + &Полный экран + + + Credentials + Учётные данные + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + &Опции… + + + &Wireless + &Беспроводная связь + + + Capture &Filters… + Фильтры &захвата… + + + As Plain &Text… + В виде обычного &текста… + + + As Plain &Text + В виде обычного &текста + + + As &CSV + В виде &CSV + + + As &YAML + В виде &YAML + + + All Visible Items + Все видимые элементы + + + All Visible Selected Tree Items + Все видимые элементы выбранного дерева + + + Display Filter &Macros… + &Макросы фильтра отображения… + + + &Find Packet… + &Найти пакет… + + + Find Ne&xt + Найти сл&едующий + + + Find Pre&vious + Найти пр&едыдущий + + + Mark or unmark each selected packet + Установить или снять отметку всех выбранных пакетов + + + Ignore or unignore each selected packet + Игнорировать или отменить игнорирование всех выбранных пакетов + + + U&nignore All Displayed + О&тменить игнорирование всего отображаемого + + + Unignore all displayed packets + Отменить игнорирование всех отображённых пакетов + + + Time Shift… + Временной сдвиг… + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + &Параметры… + + + TCP throughput + Пропускная способность TCP + + + Request Sequences + Последовательности запросов + + + HTTP Request Sequences + Последовательности HTTP-запросов + + + Decode &As… + Декодировать &как… + + + Export PDUs to File… + Экспортировать PDU в файл… + + + Create graphs based on display filter fields + Создать графики на базе полей фильтра отображения + + + &Main Toolbar + &Главная панель инструментов + + + Show or hide the main toolbar + Показать или скрыть главную панель инструментов + + + &Filter Toolbar + Панель инструментов &фильтра + + + Show or hide the display filter toolbar + Показать или скрыть панель фильтра отображения + + + Conversations at different protocol levels + Диалоги на различных уровнях протоколов + + + Endpoints at different protocol levels + Конечные точки на различных уровнях протоколов + + + Colorize Packet List + Выделить цветом список пакетов + + + Draw packets using your coloring rules + Выводить пакеты на экран c использованием правил выделения цветом + + + &Zoom In + &Увеличить масштаб + + + Enlarge the main window text + Увеличить текст главного окна + + + Zoom Out + Уменьшить масштаб + + + Normal Size + Обычный размер + + + Resize Columns + Изменить размер столбцов + + + Resize packet list columns to fit contents + Изменить размер столбцов списка пакетов по содержимому + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Дата и время суток (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + Показывать временные характеристики пакетов в виде даты и времени суток. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Год, день года и время суток (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Показывать временные характеристики пакетов в виде года, дня года и времени суток. + + + Time of Day (01:02:03.123456) + Время суток (01:02:03.123456) + + + Seconds Since 1970-01-01 + Число секунд с 1970-01-01 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Показывать временные характеристики пакетов в виде числа секунд с начала эпохи UNIX / POSIX (1970-01-01). + + + Seconds Since Previous Captured Packet + Число секунд с момента захвата предыдущего пакета + + + Show packet times as the seconds since the previous captured packet. + Показывать временные характеристики пакетов в виде числа секунд с момента захвата предыдущего пакета. + + + Seconds Since Previous Displayed Packet + Число секунд с момента захвата отображаемого пакета + + + Show packet times as the seconds since the previous displayed packet. + Показывать временные характеристики пакетов в виде числа секунд с момента захвата отображаемого пакета. + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + Дата и время суток в UTC (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Показывать временные характеристики пакетов в виде даты и времени суток в UTC. + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Год, день года и время суток в UTC (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + Показывать временные характеристики пакетов в виде года, дня года и времени суток в UTC. + + + UTC Time of Day (01:02:03.123456) + Время суток в UTC (01:02:03.123456) + + + Show packet times as the UTC time of day. + Показывать временные характеристики пакетов в виде времени суток в UTC. + + + Automatic (from capture file) + Автоматически (из файла захвата) + + + Use the time precision indicated in the capture file. + Использовать точность временных параметров файла захвата. + + + Seconds + Секунды + + + Tenths of a second + Десятки секунд + + + Hundredths of a second + Сотни секунд + + + Milliseconds + Миллисекунды + + + Microseconds + Микросекунды + + + Nanoseconds + Наносекунды + + + Display Seconds With Hours and Minutes + Отображать секунды с часами и минутами + + + Display seconds with hours and minutes + Отображать секунды с часами и минутами + + + Resolve &Physical Addresses + Разрешать &физические адреса + + + Show names for known MAC addresses. Lookups use a local database. + Показывать имена для известных MAC-адресов. При поиске используется локальная база данных. + + + Resolve &Network Addresses + Разрешать &сетевые адреса + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + Показывать имена для известных адресов IPv4, IPv6 и IPX. При поиске может генерировать сетевой трафик. + + + Resolve &Transport Addresses + Разрешать &транспортные адреса + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + Показывать имена для известных служб TCP, UDP и SCTP. В некоторых системах при поиске может генерировать трафик. + + + Wire&less Toolbar + Панель &беспроводной связи + + + Show or hide the wireless toolbar + Показать или скрыть панель беспроводной связи + + + &Status Bar + &Строка состояния + + + Show or hide the status bar + Показать или скрыть строку состояния + + + Packet &List + &Список пакетов + + + Show or hide the packet list + Показать или скрыть список пакетов + + + Packet &Details + Подробная &информация о пакете + + + Show or hide the packet details + Показать или скрыть подробную информацию о пакете + + + Packet &Bytes + &Байты пакета + + + Show or hide the packet bytes + Показать или скрыть байты пакета + + + &Conversation Hash Tables + &Хэш-таблицы диалогов + + + &Dissector Tables + &Таблицы диссекторов + + + &Supported Protocols + &Поддерживаемые протоколы + + + MAP Summary + Сводные данные MAP + + + GSM MAP summary statistics + Сводные данные статистики GSM MAP + + + RLC &Graph + &График RLC + + + &Coloring Rules… + &Правила выделения цветом… + + + Show Linked Packet in New Window + Показать связанный пакет в новом окне + + + New Coloring Rule… + New Conversation Rule… + Новое правило выделения цветом… + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + Анализ потока RTP для выбранного потока. Нажмите клавишу CTRL для добавления обратного потока. + + + RTP Player + RTP-проигрыватель + + + Play selected stream. Press CTRL key for playing reverse stream too. + Воспроизведение выбранного потока. Нажмите клавишу CTRL для добавления воспроизведения обратного потока. + + + IA&X2 Stream Analysis + Анализ потока IA&X2 + + + Enabled Protocols… + Enable Protocols… + Включённые протоколы… + + + Wiki Protocol Page + Вики-страница протокола + + + Open the Wireshark wiki page for this protocol. + Открыть вики-страницу Wireshark для данного протокола. + + + Filter Field Reference + Справка по полю фильтра + + + Open the display filter reference page for this filter field. + Открыть справочную страницу фильтра отображения для данного поля фильтра. + + + Go to the packet referenced by the selected field. + Перейти к пакету по ссылке из выбранного поля. + + + &VoIP Calls + Вызовы &VoIP + + + Open &Recent + Открыть &последние + + + Name Resol&ution + Разрешение и&мён + + + Service &Response Time + Время &ответа сервиса + + + &RTP + &RTP + + + S&CTP + S&CTP + + + &ANSI + &ANSI + + + &GSM + &GSM + + + &LTE + &LTE + + + &MTP3 + &MTP3 + + + &Open + &Открыть + + + &Quit + &Выход + + + &Close + &Закрыть + + + Display &Filters… + &Фильтры отображения… + + + &Unmark All Displayed + &Снять отметку со всего отображённого + + + All VoIP Calls + Все вызовы VoIP + + + SIP &Flows + &Потоки SIP + + + SIP Flows + Потоки SIP + + + RTP Streams + Потоки RTP + + + Edit the packet list coloring rules. + Редактировать правила выделения цветом списка пакетов. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Атрибуты сервера Bluetooth ATT + + + Show Packet in New &Window + Показать пакет в новом &окне + + + Show this packet in a separate window. + Показать этот пакет в отдельном окне. + + + Show the linked packet in a separate window. + Показать связанный пакет в отдельном окне. + + + Auto Scroll in Li&ve Capture + Автопрокрутка при &захвате + + + Automatically scroll to the last packet during a live capture. + Автоматическая прокрутка к последнему пакету при захвате в режиме реального времени. + + + Expert Information + Экспертная информация + + + Show expert notifications + Показать расширенные уведомления + + + Add an expression to the display filter. + Добавить выражение к фильтру отображения. + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + Старт «REGISTER_STAT_GROUP_UNSORTED» + + + No ANSI statistics registered + No tools registered + Статистика ANSI не зарегистрирована + + + Resolved Addresses + Разрешённые адреса + + + Show each table of resolved addresses as copyable text. + Отображать все таблицы разрешённых адресов в виде доступного для копирования текста. + + + Color &1 + Цвет &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + Выделить текущий диалог своим собственным цветом. + + + Color &2 + Цвет &2 + + + Color &3 + Цвет &3 + + + Color &4 + Цвет &4 + + + Color &5 + Цвет &5 + + + Color &6 + Цвет &6 + + + Color &7 + Цвет &7 + + + Color &8 + Цвет &8 + + + Color &9 + Цвет &9 + + + Color 1&0 + Цвет 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + Создать новое правило выделения цветом на основе этого поля. + + + Reset Colorization + Сбросить выделение цветом + + + Reset colorized conversations. + Сбросить выделение цветом диалогов. + + + RTP Stream Analysis + Анализ потока RTP + + + Edit Resolved Name + Изменить разрешённое имя + + + Manually edit a name resolution entry. + Вручную изменить запись разрешения имён. + + + Enable and disable specific protocols + Включение и отключение отдельных протоколов + + + before quitting + перед завершением работы + + + Save packets before merging? + Сохранить пакеты перед объединением? + + + A temporary capture file can't be merged. + Невозможно выполнить объединение с временным файлом захвата. + + + Save changes in "%1" before merging? + Сохранить изменения в «%1» перед объединением? + + + Changes must be saved before the files can be merged. + Необходимо сохранить изменения перед объединением файлов. + + + Invalid Display Filter + Недопустимый фильтр отображения + + + Invalid Read Filter + Недопустимый фильтр чтения + + + The filter expression %1 isn't a valid read filter. (%2). + Выражение фильтра %1 не является допустимым фильтром чтения. (%2). + + + before importing a capture + before importing a new capture + перед импортированием захваченных данных + + + Unable to export to "%1". + Не удалось экспортировать в «%1». + + + You cannot export packets to the current capture file. + Экспорт пакетов в текущий файл захвата невозможен. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + Сохранить внесённые изменения%1? + + + Your captured packets will be lost if you don't save them. + Захваченные пакеты будут потеряны, если их не сохранить. + + + Do you want to save the changes you've made to the capture file "%1"%2? + Сохранить изменения, внесённые в файл захвата «%1»%2? + + + Your changes will be lost if you don't save them. + Внесённые изменения будут потеряны, если их не сохранить. + + + Check for Updates… + Проверить обновления… + + + Unable to drop files during capture. + Нельзя перетаскивать файлы во время захвата. + + + Unknown file type returned by merge dialog. + Диалог объединения вернул неизвестный тип файла. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Сообщите об этой ошибке Wireshark на https://gitlab.com/wireshark/wireshark/-/issues. + + + Unknown file type returned by export dialog. + Диалог экспорта вернул неизвестный тип файла. + + + Do you want to stop the capture and save the captured packets%1? + Остановить захват и сохранить захваченные пакеты%1? + + + Do you want to save the captured packets%1? + Сохранить захваченные пакеты%1? + + + Save before Continue + Сохранить перед перезапуском + + + Stop and Save + Остановить и сохранить + + + Stop and Quit &without Saving + Stop and Quit without Saving + Остановить и выйти &без сохранения + + + Quit &without Saving + Quit without Saving + Выйти &без сохранения + + + There is no "rtp.ssrc" field in this version of Wireshark. + В этой версии Wireshark отсутствует поле «rtp.ssrc». + + + Please select an RTPv2 packet with an SSRC value + Выберите пакет RTPv2 с идентификатором SSRC + + + SSRC value not found. + Идентификатор SSRC не найден. + + + Show or hide the toolbar + Показать или скрыть панель инструментов + + + Continue &without Saving + Continue without Saving + Продолжить &без сохранения + + + Stop and Continue &without Saving + Stop and Continue without Saving + Остановить и продолжить &без сохранения + + + The Wireshark Network Analyzer + Сетевой анализатор Wireshark + + + Capturing from %1 + Захват из %1 + + + before opening another file + перед открытием другого файла + + + Merging files. + Объединение файлов. + + + %1: %2 + %1: %2 + + + Clear Menu + Очистить меню + + + before closing the file + перед закрытием файла + + + Export Selected Packet Bytes + Экспорт выбранных байтов пакета + + + No Keys + Отсутствуют ключи + + + Raw data (*.bin *.dat *.raw);;All Files ( + Необработанные данные (*.bin *.dat *.raw);;Все Файлы ( + + + Couldn't copy text. Try another item. + Не удалось скопировать текст. Попробуйте другой элемент. + + + Are you sure you want to remove all packet comments? + Удалить все комментарии к пакету? + + + Unable to build conversation filter. + Не удалось построить фильтр диалога. + + + before reloading the file + перед перезагрузкой файла + + + Error compiling filter for this conversation. + Ошибка компиляции фильтра для этого диалога. + + + No previous/next packet in conversation. + Предыдущий или следующий пакет в диалоге отсутствуют. + + + No interface selected. + Интерфейс не выбран. + + + Saving %1… + Сохранение %1… + + + Configure all extcaps before start of capture. + Настроить конфигурацию всех extcaps перед началом захвата. + + + Invalid capture filter. + Недопустимый фильтр захвата. + + + (empty comment) + placeholder for empty comment + (пустой комментарий) + + + Add New Comment… + Добавить новый комментарий… + + + Edit "%1" + edit packet comment + Изменить «%1» + + + Delete "%1" + delete packet comment + Удалить «%1» + + + Delete packet comments + Удалить комментарии к пакету + + + Delete comments from %n packet(s) + + + + + + + + before starting a new capture + перед началом нового захвата + + + before reloading Lua plugins + перед перезагрузкой подключаемых модулей Lua + + + Please wait while Wireshark is initializing… + Дождитесь завершения инициализации Wireshark… + + + before updating + + + + There are no TLS Session Keys to save. + Нет ключей сеанса TLS для сохранения. + + + Export TLS Session Keys (%Ln key(s)) + + + + + + + + TLS Session Keys (*.keys *.txt);;All Files ( + Ключи сеанса TLS (*.keys *.txt);;Все Файлы ( + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + Доступные фильтры отсутствуют. Попробуйте другой %1. + + + column + столбец + + + item + элемент + + + The "%1" column already exists. + Столбец «%1» уже существует. + + + The "%1" column already exists as "%2". + Столбец «%1» уже существует под именем «%2». + + + RTP packet search failed + Неудачный поиск пакета RTP + + + No Interface Selected. + Интерфейс не выбран. + + + before restarting the capture + перед перезапуском захвата + + + Wiki Page for %1 + Вики-страница для %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Вики-страница Wireshark поддерживается сообществом.</p><p>Загружаемая страница может содержать всю необходимую информацию, но также может быть не завершена, содержать ошибки или отсутствовать.</p><p>Продолжить переход на вики-страницу?</p> + + + Loading + Загрузка + + + Reloading + Перезагрузка + + + Rescanning + Повторное сканирование + + + + WlanStatisticsDialog + + Wireless LAN Statistics + Статистика локальной беспроводной сети + + + Channel + Канал + + + SSID + SSID + + + Percent Packets + Процент пакетов + + + Percent Retry + Процент повторных попыток + + + Probe Reqs + Пробные запросы + + + Probe Resp + Пробные ответы + + + Auths + Аутентификации + + + Retry + Повторные попытки + + + Deauths + Деаутентификации + + + Other + Прочее + + + diff --git a/ui/qt/wireshark_sv.ts b/ui/qt/wireshark_sv.ts new file mode 100644 index 00000000..e9e52f66 --- /dev/null +++ b/ui/qt/wireshark_sv.ts @@ -0,0 +1,14743 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + Om Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Analysator av nätverksprotokoll</span> + + + Copy the version information to the clipboard + Kopiera versionsinformationen till urklipp + + + Copy to Clipboard + Kopiera till urklipp + + + Authors + Upphovsmän + + + Search Authors + Sök upphovsmän + + + Folders + Mappar + + + Filter by path + Filtrera efter sökväg + + + Plugins + Insticksmoduler + + + No plugins found. + Inga insticksmoduler hittades. + + + Search Plugins + Sök efter insticksmoduler + + + Filter by type: + Filtrera efter typ: + + + Keyboard Shortcuts + Tangentbordsgenvägar + + + Search Shortcuts + Sök efter genvägar + + + Acknowledgments + Tack + + + License + Licens + + + The directory does not exist + Katalogen finns inte + + + Should the directory %1 be created? + Skall katalogen %1 skapas? + + + The directory could not be created + Katalogen kunde inte skapas + + + The directory %1 could not be created. + Katalogen %1 kunde inte skapas. + + + Show in Finder + Visa i sökaren + + + Show in Folder + Visa i mappen + + + Copy + Kopiera + + + Copy Row(s) + + Kopiera rad + Kopiera rader + + + + + AddressEditorFrame + + Frame + Ram + + + Name Resolution Preferences… + Name Resolution Preferences... + Namnuppslagningsinställningar… + + + Address: + Adress: + + + Name: + Namn: + + + Can't assign %1 to %2. + Kan inte tilldela %1 till %2. + + + + AdvancedPrefsModel + + Name + Namn + + + Status + Status + + + Type + Typ + + + Value + Värde + + + + ApplyLineEdit + + Apply changes + Verkställ ändringar + + + + AuthorListModel + + Name + Namn + + + Email + E-post + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Blåtands ATT-serverattribut + + + Handle + Handtag + + + UUID + UUID + + + UUID Name + UUID-namn + + + All Interfaces + Alla gränssnitt + + + All Devices + Alla enheter + + + Remove duplicates + Ta bort dubbletter + + + Copy Cell + Kopiera cell + + + Copy Rows + Kopiera rader + + + Copy All + Kopiera allt + + + Save as image + Spara som en bild + + + Mark/Unmark Row + Markera/avmarkera rad + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Markera/avmarkera cell + + + Save Table Image + Spara tabellbild + + + PNG Image (*.png) + PNG-bild (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + Blåtandsenhet + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Namn + + + Class of Device + Enhetsklass + + + LMP Version + LMP-version + + + LMP Subversion + LMP-underversion + + + Manufacturer + Tillverkare + + + HCI Version + HCI-version + + + HCI Revision + HCI-revision + + + Scan + Skanning + + + Authentication + Autentisering + + + Encryption + Kryptering + + + ACL MTU + ACL-MTU + + + ACL Total Packets + ACL totalt antal paket + + + SCO MTU + SCO-MTU + + + SCO Total Packets + SCO totalt antal paket + + + LE ACL MTU + LE ACL-MTU + + + LE ACL Total Packets + LE ACL totalt antal paket + + + LE ISO MTU + LE ISO MTU + + + LE ISO Total Packets + LE ISO totalt antal paket + + + Inquiry Mode + Frågeläge + + + Page Timeout + Sidtidsgräns + + + Simple Pairing Mode + Enkelt hopparningsläge + + + Voice Setting + Röstinställning + + + Value + Värde + + + Changes + Ändringar + + + %1 changes + %1-ändringar + + + Copy Cell + Kopiera cell + + + Copy Rows + Kopiera rader + + + Copy All + Kopiera allt + + + Save as image + Spara som en bild + + + Mark/Unmark Row + Markera/avmarkera rad + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Markera/avmarkera cell + + + Unknown + Okänd + + + Bluetooth Device - %1%2 + Blåtandsenhet – %1%2 + + + enabled + aktiverad + + + disabled + avaktiverad + + + %1 ms (%2 slots) + %1 ms (%2 fack) + + + Save Table Image + Spara tabellbild + + + PNG Image (*.png) + PNG-bild (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Blåtandsenheter + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Namn + + + LMP Version + LMP-version + + + LMP Subversion + LMP-underversion + + + Manufacturer + Tillverkare + + + HCI Version + HCI-version + + + HCI Revision + HCI-revision + + + Is Local Adapter + Är en lokal adapter + + + All Interfaces + Alla gränssnitt + + + Show information steps + Visa informationssteg + + + %1 items; Right click for more option; Double click for device details + %1 objekt; högerklicka för fler alternativ; dubbelklicka för enhetsdetaljer + + + Copy Cell + Kopiera cell + + + Copy Rows + Kopiera rader + + + Copy All + Kopiera allt + + + Save as image + Spara som en bild + + + Mark/Unmark Row + Markera/avmarkera rad + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Markera/avmarkera cell + + + true + sant + + + Save Table Image + Spara tabellbild + + + PNG Image (*.png) + PNG-bild (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Blåtands HCI-sammanfattning + + + Name + Namn + + + OGF + OGF + + + OCF + OCF + + + Opcode + Op-kod + + + Event + Händelse + + + Subevent + Underhändelse + + + Status + Status + + + Reason + Anledning + + + Hardware Error + Hårdvarufel + + + Occurrence + Förekomster + + + Link Control Commands + Länkstyrkommandon + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Länkpolicykommando + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Styr- och basbandskommandon + + + 0x03 + 0x03 + + + Informational Parameters + Informationsparametrar + + + 0x04 + 0x04 + + + Status Parameters + Statusparametrar + + + 0x05 + 0x05 + + + Testing Commands + Testkommandon + + + 0x06 + 0x06 + + + LE Controller Commands + LE-styrkommandon + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + Blåtands logotyp-testningskommandon + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Leverantörspecifika kommandon + + + 0x3F + 0x3F + + + Unknown OGF + Okänd OGF + + + Events + Händelser + + + Hardware Errors + Hårdvarufel + + + Results filter: + Resultatfilter: + + + Display filter: + Visningsfilter: + + + All Interfaces + Alla gränssnitt + + + All Adapters + Alla adaptrar + + + Copy Cell + Kopiera cell + + + Copy Rows + Kopiera rader + + + Copy All + Kopiera allt + + + Save as image + Spara som en bild + + + Mark/Unmark Row + Markera/avmarkera rad + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Markera/avmarkera cell + + + Unknown + Okänd + + + Adapter %1 + Adapter %1 + + + Frame %1 + Ram %1 + + + Pending + Väntande + + + Save Table Image + Spara tabellbild + + + PNG Image (*.png) + PNG-bild (*.png) + + + + ByteViewTab + + Packet bytes + Paketbyte + + + + ByteViewText + + Allow hover highlighting + Tillåt markering vid hovring + + + Show bytes as hexadecimal + Visa byte hexadecimalt + + + …as decimal + + + + …as octal + + + + …as bits + … som bitar + + + Show text based on packet + Visa text baserat på paket + + + …as ASCII + … som ASCII + + + …as EBCDIC + … som EBCDIC + + + + CaptureFile + + [closing] + [stänger] + + + [closed] + [stängd] + + + + CaptureFileDialog + + This capture file contains comments. + Denna fångstfil innehåller kommentarer. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + Filformatet du valde stödjer inte kommentarer. Vill du spara fångsten i ett format som stödjer kommentarer eller slänga kommentarerna och spara i formatet du valde? + + + Discard comments and save + Släng kommentarerna och spara + + + Save in another format + Spara i ett annat format + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + Inget filformat som den kan sparas i stödjer kommentarer. Vill du slänga kommentarerna och spara i formatet du valde? + + + All Files ( + Alla filer ( + + + All Capture Files + Alla fångstfiler + + + Format: + Format: + + + Size: + Storlek: + + + Start / elapsed: + Start / passerat: + + + Automatically detect file type + Detektera automatiskt filtyp + + + Prepend packets + Lägg paket före + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Infoga paket från den valda filen före den aktuella filen. Pakettidsstämplar kommer ignoreras. + + + Merge chronologically + Sammanfoga kronologiskt + + + Insert packets in chronological order. + Infoga paket i kronologisk ordning. + + + Append packets + Lägg paket efter + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Infoga paket från den valda filen efter den aktuella filen. Pakettidsstämplar kommer ignoreras. + + + Read filter: + Läsfilter: + + + Compress with g&zip + Komprimera med g&zip + + + Open Capture File + Wireshark: Open Capture File + Öppna fångstfil + + + Save Capture File As + Wireshark: Save Capture File As + Spara fångstfil som + + + Save as: + Spara som: + + + Export Specified Packets + Wireshark: Export Specified Packets + Exportera angivna paket + + + Export as: + Exportera som: + + + Merge Capture File + Wireshark: Merge Capture File + Sammanfoga fångstfil + + + Unknown file type returned by save as dialog. + Okänd filtyp returnerad av spara som-dialogen. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Rapportera gärna detta som ett problem med Wireshark på https://gitlab.com/wireshark/wireshark/-/issues. + + + directory + katalog + + + unknown file format + okänt filformat + + + error opening file + fel när filen öppnades + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + %1, fel efter %Ln dataposter + %1, fel efter %Ln dataposter + + + + %1, timed out at %Ln data record(s) + + %1, tidsgränsen överskreds efter %Ln datapost + %1, tidsgränsen överskreds efter %Ln dataposter + + + + %1, %Ln data record(s) + + %1, %Ln datapost + %1, %Ln dataposter + + + + unknown + okänd + + + + CaptureFilePropertiesDialog + + Details + Detaljer + + + Capture file comments + Fångstfilkommentarer + + + Refresh + Uppdatera + + + Copy To Clipboard + Kopiera till urklipp + + + Save Comments + Spara kommentarer + + + Capture File Properties + Fångstfilegenskaper + + + Unknown + Okänd + + + File + Fil + + + Name + Namn + + + Length + Längd + + + Hash (SHA256) + Hash (SHA256) + + + Hash (SHA1) + Hash (SHA1) + + + Format + Format + + + Encapsulation + Inkapsling + + + Snapshot length + Längd på ögonblicksbild + + + Time + Tid + + + First packet + Första paket + + + Last packet + Sista paket + + + Elapsed + Förflutet + + + Section %1 + Avsnitt %1 + + + Capture + Fånga + + + Hardware + Hårdvara + + + OS + OS + + + Application + Program + + + Interfaces + Gränssnitt + + + Interface + Gränssnitt + + + Dropped packets + Släppta paket + + + Capture filter + Fångstfilter + + + Link type + Länktyp + + + Packet size limit (snaplen) + Storleksgräns på paket (snaplen) + + + none + ingen + + + %1 bytes + %1 byte + + + Statistics + Statistik + + + Measurement + Mätning + + + Captured + Fångat + + + Displayed + Visat + + + Marked + Markerat + + + Packets + Paket + + + Time span, s + Tidsintervall, s + + + Average pps + Genomsnittligt p/s + + + Average packet size, B + Genomsnittlig paketstorlek, B + + + Bytes + Byte + + + Average bytes/s + Genomsnittligt byte/s + + + Average bits/s + Genomsnittligt bitar/s + + + Section Comment + Avsnittskommentar + + + Packet Comments + Paketkommentarer + + + <p>Frame %1: + <p>Ram %1: + + + Created by Wireshark %1 + + + Skapad av Wireshark %1 + + + + + + CaptureFilterCombo + + Capture filter selector + Fångstfilterväljare + + + + CaptureFilterEdit + + Capture filter entry + Fångstfilterpost + + + Manage saved bookmarks. + Hantera sparade bokmärken. + + + Apply this filter string to the display. + Använd denna filtersträng på det visade. + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + Flera vilter valda. Åsidosätt dem här eller lämna detta blankt för att bevara dem. + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>Gränssnittet du har valt har olika fångstfilter. Att skriva ett filter här kommer åsidosätta dem. Att inte göra något kommer att bevara dem.</p> + + + Enter a capture filter %1 + Ange ett fångstfilter %1 + + + Save this filter + Spara detta filter + + + Remove this filter + Ta bort detta filter + + + Manage Capture Filters + Hantera fångstfilter + + + + CaptureInfoDialog + + Capture Information + Fångstinformation + + + Stop Capture + Sluta fånga + + + %1 packets, %2:%3:%4 + %1 paket, %2:%3:%4 + + + + CaptureInfoModel + + Other + Annat + + + + CaptureOptionsDialog + + Input + Indata + + + Interface + Gränssnitt + + + Traffic + Trafik + + + Link-layer Header + Länknivåhuvud + + + Promiscuous + Promiskuös + + + Snaplen (B) + Provlängd (B) + + + Buffer (MB) + Buffert (MB) + + + Monitor Mode + Monitorläge + + + Capture Filter + Fångstfilter + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Du vill troligen aktivera detta. Vanligen kommer ett nätverkskort endast fånga trafiken som skickas till dess egen nätverksadress. Om du vill fånga all trafik som nätverkskortet kan &quot;se&quot;, markera detta alternativ. Se FAQ:n för lite fler detaljer om att fånga paket från ett switchat nätverk.</p></body></html> + + + Enable promiscuous mode on all interfaces + Aktivera promiskuöst läge för alla gränssnitt + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + Visa och dölj gränssnitt, lägg till kommentarer och hantera rör och fjärrgränssnitt. + + + Manage Interfaces… + Hantera gränssnitt … + + + Capture filter for selected interfaces: + Fångsfilter för valda gränssnitt: + + + Compile BPFs + Kompilera BPF:er + + + Output + Utdata + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>Ange filnamnet som infångad data skall skrivas till. Som standard kommer en temporärfil användas.</p></body></html> + + + Capture to a permanent file + Fånga till en permanent fil + + + File: + Fil: + + + Browse… + Bläddra … + + + Output format: + Utdataformat: + + + pcapng + pcapng + + + pcap + pcap + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>Istället för att använda en enda fångstfil kommer flera filer skapas.</p><p>De genererade filnamnen kommer innehålla ett ökande nummer och starttidpunkten för fångsten.</p><p>OBS: om aktiverat MÅSTE åtminstone ett av kriterierna för ny fil väljas.</p></body></html> + + + Create a new file automatically… + Skapa en ny fil automatiskt … + + + after + efter + + + Switch to the next file after the specified number of packets have been captured. + Byt till nästa fil efter det angivna antalet paket har infångats. + + + packets + paket + + + Switch to the next file after the file size exceeds the specified file size. + Byt till nästa fil efter att filstorleken överskrider den angivna filstorleken. + + + kilobytes + kilobyte + + + megabytes + megabyte + + + gigabytes + gigabyte + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + Byt till nästa fil när tiden under vilken fångsten gjorts till den nuvarande filen överskrider den angivna tiden. + + + seconds + sekunder + + + minutes + minuter + + + hours + timmar + + + when time is a multiple of + när tiden är en multipel av + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + Byt till nästa fil när (väggklocks)tiden är en jämn multipel av det angivna intervallet +Till exempel, använd 1 timma för att en ny fil skall skapas varje timma vid hel timma. + + + compression + komprimering + + + None + Ingen + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>Efter att infångandet har bytt till nästa fil och det angivna antalet filer har överskridits tas den äldsta filen bort.</p></body></html> + + + Use a ring buffer with + Använd en ringbuffert med + + + files + filer + + + Options + Flaggor + + + Display Options + Visningsflaggor + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>Används denna flagga kommer infångade paket visas omedelbart på huvudskärmen. Observera: detta kommer sakta ned fångandet, så fler paket kan komma att släppas.</p></body></html> + + + Update list of packets in real-time + Uppdatera paketlistan i realtid + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>Detta kommer rulla &quot;Paketlistan&quot; automatiskt till det sista fångade paketet, när flaggan &quot;Uppdatera paketlistan i realtid&quot; används.</p></body></html> + + + Automatically scroll during live capture + Rulla automatiskt under live-fångst + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>Visa fångstinformationsdialogen under fångandet.</p></body></html> + + + Show capture information during live capture + Visa fångstinformation under live-fångst + + + Name Resolution + Namnuppslagning + + + Perform MAC layer name resolution while capturing. + Utför namnuppslagning på MAC-nivå under fångandet. + + + Resolve MAC addresses + Slå upp MAC-adresser + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>Utför namnuppslagning på nätverksnivå under fångandet.</p></body></html> + + + Resolve network names + Slå upp nätverksnamn + + + Perform transport layer name resolution while capturing. + Utför namnuppslagning på transportnivån under fångandet. + + + Resolve transport names + Slå upp transportnamn + + + Stop capture automatically after… + Sluta fånga automatiskt efter … + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>Sluta fånga efter att det angivna antalet paket har fångats.</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + Sluta fånga efter att det angivna antalet paket har infångats. + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>Sluta fånga efter att det angivna antalet filer har skapats.</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>Sluta fånga efter att den angivna mängden data har infångats.</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + Sluta fånga efter att den angivna mängden data har infångats. + + + Stop capturing after the specified amount of time has passed. + Sluta fånga efter den angivna tidsrymden har passerat. + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>Ange om så önskas en tillfällig katalog för namnlösa fångstfiler.</p></body></html> + + + Directory for temporary files + Katalog för tillfälliga filer + + + Capture Options + Fångstalternativ + + + Start + Starta + + + Leave blank to use a temporary file + Lämna tomt för att använda en temporärfil + + + Specify a Capture File + Ange en fångstfil + + + Specify temporary directory + Ange temporärkatalog + + + %1: %2 + %1: %2 + + + Addresses + Adresser + + + Address + Adress + + + no addresses + inga adresser + + + Error + Fel + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + Flera filer: den begärda filstorleken är för stor. Filstorleken får inte vara större än 2 GiB. + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + Flera filer: inget fångstfilnamn angivet. Du måste ange ett filnamn ifall du vill använda flera filer. + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + Flera filer: ingen filgräns angiven. Du måste ange en filstorlek, ett intervall eller ett antal paket för varje fil. + + + + CapturePreferencesFrame + + Frame + Ram + + + Default interface + Standardgränssnitt + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Du vill troligen aktivera detta. Vanligen kommer ett nätverkskort endast fånga trafiken som skickas till dess egen nätverksadress. Om du vill fånga all trafik som nätverkskortet kan &quot;se&quot;, markera detta alternativ. Se FAQ:n för lite fler detaljer om att fånga paket från ett switchat nätverk.</p></body> + + + Capture packets in promiscuous mode + Fånga paket i promiskuöst läge + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Fånga paket i nästa generations format för fångstfiler.</p></body></html> + + + Capture packets in pcapng format + Fånga paket i pcapng-format + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Uppdatera listan av paket under tiden infångandet pågår. Detta kan medföra att paket släpps på höghastighetsnätverk.</p></body></html> + + + Update list of packets in real time + Uppdatera paketlistan i realtid + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + Läs inte in gränssnitt vid uppstart + + + Disable external capture interfaces + Avaktivera externa fångstgränssnitt + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + symbolen ”@” kommer ignoreras. + + + + ColoringRulesDialog + + Dialog + Dialog + + + <small><i>A hint.</i></small> + <small><i>Ett tips.</i></small> + + + Add a new coloring rule. + Lägg till en ny färgläggningsregel. + + + Delete this coloring rule. + Ta bort denna färgläggningsregel. + + + Duplicate this coloring rule. + Dubblera denna färgläggningsregel. + + + Clear all coloring rules. + Nollställ alla färgläggningsregler. + + + Set the foreground color for this rule. + Ange förgrundsfärgen för denna regel. + + + Foreground + Förgrund + + + Set the background color for this rule. + Ange bakgrundsfärgen för denna regel. + + + Background + Bakgrund + + + Set the display filter using this rule. + Ange visningsfiltret som använder denna regel. + + + Apply as filter + Använd som ett filter + + + Select a file and add its filters to the end of the list. + Välj en fil och lägg till dess filter till slutet av listan. + + + Save filters in a file. + Spara filter i en fil. + + + Coloring Rules %1 + Färgläggningsregler %1 + + + Import… + Importera … + + + Export… + Exportera … + + + Copy coloring rules from another profile. + Kopiera färgläggningsregler från en annan profil. + + + Open + Öppna + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Dubbelklicka för att redigera. Dra för att flytta. Regler bearbetas i ordning tills en matchning hittas. + + + Import Coloring Rules + Importera färgläggningsregler + + + Export %1 Coloring Rules + Exportera %1 färgläggningsregler + + + + ColoringRulesModel + + New coloring rule + Ny färgläggningsregel + + + Unable to save coloring rules: %1 + Kan inte spara färgläggningsregler: %1 + + + Name + Namn + + + Filter + Filter + + + + ColumnEditorFrame + + Frame + Ram + + + Title: + Title + Titel: + + + Type: + Type + Typ: + + + Fields: + Fields + Fält: + + + Occurrence: + Occurrence + Förekomst: + + + Resolve Names: + Slå upp namn: + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + <html><head/><p>Visa mänskligt läsbara strängar istället för råa värden för fält. Endast tillämpligt på anpassade kolumner med fält som har värdesträngar.</p></body></html> + + + Missing fields. + Saknade fält. + + + Invalid fields. + Ogiltiga fält. + + + Invalid occurrence value. + Ogiltigt förekomstvärde. + + + + ColumnListModel + + Displayed + Visat + + + Title + Titel + + + Type + Typ + + + Fields + Fält + + + Field Occurrence + Fältförekomst + + + Resolved + Uppslagen + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + <html>Visa mänskligt läsbara strängar istället för råa värden för fält. Endast tillämpligt på anpassade kolumner med fält som har värdesträngar.</html> + + + New Column + Ny kolumn + + + + ColumnPreferencesFrame + + Frame + Ram + + + Add a new column + Lägg till en ny kolumn + + + Delete selected column + Ta bort den angivna kolumnen + + + Show displayed columns only + Visa endast synliga kolumner + + + Reset all changes + Återställ alla ändringar + + + + CompiledFilterOutput + + Compiled Filter Output + Kompilerad filterutdata + + + Copy + Kopiera + + + Copy filter text to the clipboard. + Kopiera filtertext till urklipp. + + + + ConversationDataModel + + Address A + Adress A + + + Port A + Port A + + + Address B + Adress B + + + Port B + Port B + + + Packets + Paket + + + Bytes + Byte + + + Stream ID + Ström-ID + + + Packets A + Paket A + + + Bytes A + Byte A + + + Packets B + + + + Bytes B + Byte B + + + Abs Start + Abs start + + + Rel Start + Rel start + + + Duration + Varaktighet + + + Bits/s A + Bitar/s A + + + Bits/s B + Bitar/s B + + + Total Packets + Totalt antal paket + + + Percent Filtered + Procent filtrerade + + + + ConversationDialog + + Follow Stream… + Följ strömmen … + + + Follow a TCP or UDP stream. + Följ en TCP- eller UDP-ström. + + + Graph… + Graf … + + + Graph a TCP conversation. + Visa en graf över en TCP-konversation. + + + + ConversationHashTablesDialog + + Dialog + Dialog + + + Conversation Hash Tables + Konversationshashtabeller + + + + CopyFromProfileButton + + Copy from + Kopiera ifrån + + + Copy entries from another profile. + Kopiera poster från en annan profil. + + + System default + Systemstandard + + + + CredentialsDialog + + Wireshark - Credentials + Wireshark – kreditiv + + + Credentials + Kreditiv + + + + CredentialsModel + + Click to select the packet + Klicka för att välja paketet + + + Click to select the packet with username + Klicka för att välja paketet med användarnamnet + + + Username not available + Användarnamnet är inte tillgängligt + + + Packet No. + Paket nr. + + + Protocol + Protokoll + + + Username + Användarnamn + + + Additional Info + Ytterligare information + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + Kopiera byte som hex + ASCII-dump + + + Copy packet bytes as a hex and ASCII dump. + Kopiera paketbyte som hex och ASCII-dump. + + + …as Hex Dump + … som hexdump + + + Copy packet bytes as a hex dump. + Kopiera paketbyte som en hex-dump. + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + … som en hex-ström + + + Copy packet bytes as a stream of hex. + Kopiera paketbyte som en ström av hex. + + + …as a Base64 String + + + + Copy packet bytes as a base64 encoded string. + + + + Copy packet bytes as application/octet-stream MIME data. + Kopiera paketbyte som application/octet-stream MIME-data. + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + Ändra dissekeringsbeteendet för ett protokoll. + + + Remove this dissection behavior. + Ta bort detta dissekeringsbeteende. + + + Copy this dissection behavior. + Kopiera detta dissekeringsbeteende. + + + Clear all dissection behaviors. + Nollställ alla dissekeringsbeteenden. + + + Decode As… + Avkoda som … + + + Open + Öppna + + + + DecodeAsModel + + Match using this field + Matcha mot detta fält + + + Change behavior when the field matches this value + Ändra beteenden när fältet matchar detta värde + + + Field value type (and base, if Integer) + Fältvärdetypen (och basen, ifall heltal) + + + Current"Decode As" behavior + Aktuellt ”avkoda som”-beteende + + + Default "Decode As" behavior + Standard ”avkoda som”-beteende + + + String + Sträng + + + Integer, base + Heltal, bas + + + unknown + okänd + + + <none> + <none> + + + GUID + GUID + + + Field + Fält + + + Value + Värde + + + Type + Typ + + + Default + Standard + + + Current + Aktuellt + + + + DisplayFilterCombo + + Display filter selector + Visningsfilterval + + + Select from previously used filters. + Välj bland tidigare använda filter. + + + + DisplayFilterEdit + + Display filter entry + Visningsfilterpost + + + Manage saved bookmarks. + Hantera sparade bokmärken. + + + Display Filter Expression… + Visa filteruttryck … + + + Apply a display filter %1 <%2/> + Använd ett visningsfilter %1 <%2/> + + + Enter a display filter %1 + Skriv in ett visningsfilter %1 + + + Clear display filter + Töm visningsfiltret + + + Apply display filter + Använd visningsfiltret + + + Left align buttons + Vänsterjustera knappar + + + Apply a read filter %1 + Använd ett läsningsfilter %1 + + + Current filter: %1 + Aktuellt filter: %1 + + + Invalid filter: + Felaktigt filter: + + + Save this filter + Spara detta filter + + + Remove this filter + Ta bort detta filter + + + Manage Display Filters + Hantera visningsfilter + + + Filter Button Preferences... + Filterknappinställningar … + + + + DisplayFilterExpressionDialog + + Dialog + Dialog + + + Select a field to start building a display filter. + Välj ett fält för att börja bygga ett visningsfilter. + + + Field Name + Fältnamn + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Sök i listan av fältnamn.</p></body></html> + + + Search: + Sök: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>Relationer kan användas för att begränsa fält till specifika värden. Varje relation gör följande:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Matcha alla paket som innehåller detta fält</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Jämför fältet med ett visst värde.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Jämför fältet med en sträng (contains) eller reguljärt uttryck (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Jämför fältet med en uppsättning värden</p></td></tr></table></body></html> + + + + + Relation + Relation + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + Som standard är ordningsjämförelser och innehåller-/matchar-/i-relationer sanna om något värde matchar. Kvantifieraren ”all” kan användas för att tillämpa testet på alla värden i en ram. + + + Quantifier + Kvantifierare + + + Any + Något + + + All + Alla + + + Match against this value. + Matcha med detta värde. + + + Value + Värde + + + If the field you have selected has a known set of valid values they will be listed here. + Om fälted du har valt har en känd uppsättning giltiga värden listas de här. + + + Predefined Values + Fördefinierade värden + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + Om fältet du har valt täcker ett intervall av byte (t.ex. om du har valt ett protokoll) kan du begränsa matchningen till ett intervall av byte här. + + + Range (offset:length) + Intervall (avstånd:längd) + + + No display filter + Inget visningsfilter + + + <small><i>A hint.</i></small> + <small><i>Ett tips.</i></small> + + + Display Filter Expression + Visningsfilteruttryck + + + Select a field name to get started + Välj ett fältnamn för att börja + + + Click OK to insert this filter + Klicka OK för att infoga detta filter + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + Dialog + + + Search: + Sök: + + + Dissector Tables + Dissekeringstabeller + + + + DissectorTablesProxyModel + + Table Type + Tabelltyp + + + String + Sträng + + + Dissector Description + + + + Integer + Heltal + + + Protocol + Protokoll + + + Short Name + Kort namn + + + Table Name + Tabellnamn + + + Selector Name + Urvalsnamn + + + + EnabledProtocolsDialog + + Dialog + Dialog + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>Att avaktivera ett protokoll hindrar protokoll på högre nivå från att visas</i></small> + + + Search: + Sök: + + + in + i + + + Enable All + Aktivera allt + + + Disable All + Avaktivera allt + + + Invert + Invertera + + + Enabled Protocols + Aktiverade protokoll + + + Everywhere + Överallt + + + Only Protocols + Endast protokoll + + + Only Description + Endast beskrivning + + + Only enabled protocols + Endast aktiverade protokoll + + + Only disabled protocols + Endast avaktiverade protokoll + + + any protocol + alla protokoll + + + non-heuristic protocols + icke-heuristiska protokoll + + + heuristic protocols + heuristiska protokoll + + + + EnabledProtocolsModel + + Protocol + Protokoll + + + Description + Beskrivning + + + + EndpointDataModel + + Address + Adress + + + Port + Port + + + Packets + Paket + + + Bytes + Byte + + + Tx Packets + Snd paket + + + Tx Bytes + Snd byte + + + Rx Packets + Mott paket + + + Rx Bytes + Mott byte + + + Country + Land + + + City + Stad + + + Latitude + + + + Longitude + + + + AS Number + AS-nummer + + + AS Organization + AS-organisation + + + Total Packets + Totalt antal paket + + + Percent Filtered + Procent filtrerade + + + + EndpointDialog + + Map + Karta + + + Draw IPv4 or IPv6 endpoints on a map. + Rita IPv4- och IPv6-ändpunkter på en karta + + + Open in browser + Öppna i en webbläsare + + + Save As… + Spara som … + + + Map file error + Kartfilsfel + + + Save Endpoints Map + Spara ändpunktskarta + + + Failed to save map file %1. + Misslyckades att spara kartfilen %1. + + + + EthernetAddressModel + + Type + Typ + + + Name + Namn + + + Address + Adress + + + All entries + Alla poster + + + Hosts + Värdar + + + Ethernet Addresses + Ethernet-adresser + + + Ethernet Manufacturers + Ethernet-tillverkare + + + Ethernet Well-Known Addresses + Välkända Ethernet-adresser + + + + ExpertInfoDialog + + Dialog + Dialog + + + <small><i>A hint.</i></small> + <small><i>Ett tips.</i></small> + + + Limit to Display Filter + Begränsa till visningsfilter + + + Group by summary + Gruppera efter sammanfattning + + + Search expert summaries. + Sök i expertsammanfattningar. + + + Search: + Sök: + + + Show… + Show... + Visa … + + + Error + Fel + + + Show error packets. + Visa felpaket. + + + Warning + Varning + + + Show warning packets. + Visa varningspaket. + + + Note + Notering + + + Show note packets. + Visa noteringspaket. + + + Chat + Tjatt + + + Show chat packets. + Visa tjattpaket. + + + Comment + Kommentar + + + Show comment packets. + Visa kommentarpaket. + + + Expert Information + Expertinformation + + + Collapse All + Fäll ihop alla + + + Expand All + Fäll ut alla + + + Capture file closed. + Fångstfilen stängd. + + + No display filter + Inget visningsfilter + + + No display filter set. + Inget visningsfilter angett. + + + Limit information to "%1". + Begränsa informationen till ”%1”. + + + Display filter: "%1" + Visningsfilter: ”%1” + + + + ExpertInfoProxyModel + + Packet + Paket + + + Severity + Allvarlighet + + + Summary + Sammanfattning + + + Group + Grupp + + + Protocol + Protokoll + + + Count + Antal + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Exportera paket dissekeringar + + + Export As: + Export as: + Exportera som: + + + Plain text (*.txt) + Vanlig text (*.txt) + + + Comma Separated Values - summary (*.csv) + Kommaseparerade värden – sammanfattning (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML – sammanfattning (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PDML – detaljer (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + C-vektorer – byte (*.c, *.h) + + + + ExportObjectDialog + + Dialog + Dialog + + + Content Type: + Innehållstyp: + + + Searching for objects + Sök efter objekt + + + Text Filter: + Textfilter: + + + Only display entries containing this string + Visa endast poster som innehåller denna sträng + + + Preview + Förhandsvisning + + + All Content-Types + Alla innehållstyper + + + Export + Exportera + + + %1 object list + %1-objektlista + + + Save Object As… + Spara objekt som … + + + Save All Objects In… + Spara alla objekt i … + + + + ExportObjectModel + + Packet + Paket + + + Hostname + Värdnamn + + + Content Type + Innehållstyp + + + Size + Storlek + + + Filename + Filnamn + + + + ExportPDUDialog + + Dialog + Dialog + + + Display filter: + Visningsfilter: + + + + ExtArgSelector + + Reload data + Läs om data + + + + ExtcapArgumentFileSelection + + Clear + Töm + + + All Files ( + Alla filer ( + + + Open File + Öppna fil + + + Select File + Välj fil + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + Gränssnittsalternativ + + + Start + Starta + + + Save + Spara + + + Default + Standard + + + Restore default value of the item + Återställ standardvärdet för objektet + + + Extcap Help cannot be found + Extcap-hjälp kan inte hittas + + + The help for the extcap interface %1 cannot be found. Given file: %2 + Hjälpen för extcap-gränssnittet %1 kan inte hittas. Angiven fil: %2 + + + Save parameter(s) on capture start + Spara parametrarna vid fångststart + + + + FieldFilterEdit + + Display filter entry + Visningsfilterpost + + + Enter a field %1 + Skriv in ett fält %1 + + + Invalid filter: + Felaktigt filter: + + + + FileSetDialog + + Dialog + Dialog + + + Directory: + Katalog: + + + No files in Set + Inga filer i uppsättningen + + + No capture loaded + Ingen fångst inläst + + + %Ln File(s) in Set + %1 File%2 in Set + + %Ln fil i mängden + %Ln filer i mängden + + + + + FilesetEntryModel + + Open this capture file + Öppna denna fångstfil + + + Filename + Filnamn + + + Created + Skapad + + + Modified + Ändrad + + + Size + Storlek + + + + FilterAction + + Selected + Vald + + + Not Selected + Ej vald + + + …and Selected + … och valda + + + …or Selected + … eller valda + + + …and not Selected + … och inte valda + + + …or not Selected + … eller inte valda + + + + FilterDialog + + Dialog + Dialog + + + Create a new filter. + Skapa ett nytt filter. + + + Remove this filter. + Remove this profile. + Ta bort detta filter. + + + Copy this filter. + Copy this profile. + Kopiera detta filter. + + + Capture Filters + Fångstfilter + + + Display Filters + Visningsfilter + + + Open + Öppna + + + New capture filter + This text is automatically filled in when a new filter is created + Nytt fångstfilter + + + New display filter + This text is automatically filled in when a new filter is created + Nytt visningsfilter + + + + FilterExpressionFrame + + Frame + Ram + + + Filter Buttons Preferences… + Filterknappinställningar… + + + Label: + Etikett: + + + Enter a description for the filter button + Skriv in en beskrivning för filterknappen + + + Filter: + Filter: + + + Enter a filter expression to be applied + Skriv in ett filteruttryck att använda + + + Comment: + Kommentar: + + + Enter a comment for the filter button + Skriv in en kommentar för filterknappen + + + Missing label. + Etikett saknas. + + + Missing filter expression. + Filteruttryck saknas. + + + Invalid filter expression. + Felaktigt filteruttryck. + + + + FilterExpressionToolBar + + Filter Button Preferences... + Filterknappinställningar … + + + Edit + Redigera + + + Disable + Avaktivera + + + Remove + Ta bort + + + + FilterListModel + + Filter Name + Filternamn + + + Filter Expression + Filteruttryck + + + + FindLineEdit + + Textual Find + Textsökning + + + Regular Expression Find + Sök med reguljärt uttryck + + + + FirewallRulesDialog + + Create rules for + Skapa regler för + + + Inbound + Inkommande + + + Deny + Neka + + + Firewall ACL Rules + Brandväggens ACL-regler + + + Copy + Kopiera + + + IPv4 source address. + IPv4-källadress. + + + IPv4 destination address. + IPv4-destinationsadress. + + + Source port. + Källport. + + + Destination port. + Destinationsport. + + + IPv4 source address and port. + IPv4-källadress och port. + + + IPv4 destination address and port. + IPv4-destinationsadress och port. + + + MAC source address. + MAC-källadress. + + + MAC destination address. + MAC-destinationsadress. + + + Text file (*.txt);;All Files ( + Textfil (*.txt);;Alla filer ( + + + Warning + Varning + + + Unable to save %1 + Kan inte spara %1 + + + + FolderListModel + + "File" dialogs + ”Arkiv”-dialoger + + + capture files + fångstfiler + + + Temp + Temp + + + untitled capture files + namnlösa fångstfiler + + + Personal configuration + Personlig konfiguration + + + Global configuration + Global konfiguration + + + dfilters, preferences, ethers, … + dfilter, inställningar, ether:ar, … + + + dfilters, preferences, manuf, … + dfilter, inställningar, manuf, … + + + System + System + + + ethers, ipxnets + ether:ar, ipxnets + + + Program + Program + + + program files + programfiler + + + Personal Plugins + Personliga insticksmoduler + + + binary plugins + binära insticksmoduler + + + Global Plugins + Globala insticksmoduler + + + Personal Lua Plugins + Personliga Lua-insticksmoduler + + + Global Lua Plugins + Globala Lua-insticksmoduler + + + Lua scripts + + + + Personal Extcap path + Personlig extcap-sökväg + + + external capture (extcap) plugins + + + + Global Extcap path + Global extcap-sökväg + + + MaxMind DB path + MaxMind DB-sökväg + + + MaxMind DB database search path + MaxMind DB databassökväg + + + MIB/PIB path + MIB/PIB-sökväg + + + SMI MIB/PIB search path + SMI MIB/PIB-sökväg + + + macOS Extras + macOS-extra + + + Extra macOS packages + Extra macOS-paket + + + Name + Namn + + + Location + Plats + + + Typical Files + Typiska filer + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + Filtrera ut denna ström + + + Print + Skriv + + + ASCII + ASCII + + + C Arrays + C-vektorer + + + EBCDIC + EBCDIC + + + Hex Dump + Hex-dump + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + + + + Save as… + Spara som … + + + Back + Tillbaka + + + Packet %1. + Paket %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">klient</span>paket, + %Ln <span style="color: %1; background-color:%2">klient</span>paket, + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">server</span>paket, + %Ln <span style="color: %1; background-color:%2">server</span>paket, + + + + %Ln turn(s). + + %Ln vändning. + %Ln vändningar. + + + + Click to select. + Klicka för att välja. + + + Regex Find: + Reguttr-sök: + + + No capture file. + Ingen fångstfil. + + + Please make sure you have a capture file opened. + Se till att du har en fångstfil öppnad. + + + Error following stream. + Fel när strömmen följdes. + + + Capture file invalid. + Fångstfilen felaktig. + + + Please make sure you have a %1 packet selected. + Se till att du har ett %1-paket valt. + + + %1 stream not found on the selected packet. + + + + Entire conversation (%1) + Hela konversationen (%1) + + + Follow %1 Stream (%2) + Följ %1-ström (%2) + + + Error creating filter for this stream. + Fel när filter för denna ström skapades. + + + Save Stream Content As… + Spara ströminnehållet som … + + + [Stream output truncated] + [Strömutdatan avhuggen] + + + %Ln total stream(s). + + %Ln total ström. + %Ln totala strömmar. + + + + Max sub stream ID for the selected stream: %Ln + + + + + + + File closed. + Filen är stängd. + + + Follow Stream + Följ strömmen + + + Hint. + Tips. + + + Show data as + Show and save data as + Visa data som + + + Stream + Ström + + + Substream + Underström + + + Find: + Sök: + + + Find &Next + Sök &nästa + + + + FontColorPreferencesFrame + + Frame + Ram + + + Main window font: + Huvudfönstrets typsnitt: + + + Select Font + Välj typsnitt + + + Colors: + Färger: + + + System Default + Systemstandard + + + Solid + Enfärgad + + + Sample ignored packet text + Exempel på text för ignorerat paket + + + Sample marked packet text + Exempel på text för markerat paket + + + Sample active selected item + Exempel på aktiv vald post + + + Style: + Stil: + + + Gradient + Gradient + + + Sample inactive selected item + Exempel på inaktiv vald post + + + Sample "Follow Stream" client text + Exempel på text för ”följ strömmen”-klient + + + Sample "Follow Stream" server text + Exempel på text för ”följ strömmen"-server + + + Sample valid filter + Exempel på giltigt filter + + + Sample invalid filter + Exempel på felaktigt filter + + + Sample warning filter + Sample deprecated filter + Exempel på varningsfilter + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + Flygande bäckasiner söka hwila på mjuka tuvor + + + Lazy badgers move unique waxy jellyfish packets + Yxmördaren Julia Blomqvist på fäktning i Schweiz + + + Font + Typsnitt + + + + FunnelStringDialog + + Dialog + Dialog + + + + FunnelTextDialog + + Dialog + Dialog + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>Skriv in lite text eller ett reguljärt uttryck. Det kommer att markeras ovan.</p></body></html> + + + Highlight: + Markera: + + + + GsmMapSummaryDialog + + Dialog + Dialog + + + GSM MAP Summary + GSM MAP-sammanfattning + + + File + Fil + + + Name + Namn + + + Length + Längd + + + Format + Format + + + Snapshot length + Längd på ögonblicksbild + + + Data + Data + + + First packet + Första paket + + + Last packet + Sista paket + + + Elapsed + Förflutet + + + Packets + Paket + + + Invokes + Anrop + + + Total number of Invokes + Totalt antal anrop + + + Average number of Invokes per second + Genomsnittligt antal anrop per sekund + + + Total number of bytes for Invokes + Totalt antal byte för anrop + + + Average number of bytes per Invoke + Genomsnittligt antal byte per anrop + + + Return Results + Returvärden + + + Total number of Return Results + Totalt antal returvärden + + + Average number of Return Results per second + Genomsnittligt antal returvärden per sekund + + + Total number of bytes for Return Results + Totalt antal byte med returvärden + + + Average number of bytes per Return Result + Genomsnittligt antal byte per returvärde + + + Totals + Totalt + + + Total number of GSM MAP messages + Totalt antal GSM MAP-meddelanden + + + Average number of GSM MAP messages per second + Genomsnittligt antal GSM MAP-meddelanden per sekund + + + Total number of bytes for GSM MAP messages + Totalt antal byte med GSM MAP-meddelanden + + + Average number of bytes per GSM MAP message + Genomsnittligt antal byte per GSM MAP-meddelande + + + + IOConsoleDialog + + Dialog + Dialog + + + Enter code + + + + Evaluate + + + + Clear + Töm + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Värdefulla och fantastiska tidsbesparande tangentbordsgenvägar</h3> +<table><tbody> + +<tr><th>+</th><td>Zooma in</td></th> +<tr><th>-</th><td>Zooma ut</td></th> +<tr><th>x</th><td>Zooma in X-axeln</td></th> +<tr><th>X</th><td>Zooma ut X-axeln</td></th> +<tr><th>y</th><td>Zooma in Y-axeln</td></th> +<tr><th>Y</th><td>Zooma ut Y-axeln</td></th> +<tr><th>0</th><td>Återställ grafen till sitt utgångsläge</td></th> + +<tr><th>→</th><td>Flytta åt höger 10 bildpunkter</td></th> +<tr><th>←</th><td>Flytta åt vänster 10 bildpunkter</td></th> +<tr><th>↑</th><td>Flytta upp 10 bildpunkter</td></th> +<tr><th>↓</th><td>Flytta ner 10 bildpunkter</td></th> +<tr><th><i>Skift+</i>→</th><td>Flytta åt höger 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>←</th><td>Flytta åt vänster 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>↑</th><td>Flytta upp 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>↓</th><td>Flytta ner 1 bildpunkt</td></th> + +<tr><th>g</th><td>Gå till paketet under markören</td></th> + +<tr><th>z</th><td>Växla mellan att musen drar/zoomar</td></th> +<tr><th>t</th><td>Växla mellan fångstens/sessionens starttid</td></th> +<tr><th>Mellanslag</th><td>Växla hårkors</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + Ta bort denna graf. + + + Add a new graph. + Lägg till en ny graf. + + + Duplicate this graph. + Dubblera denna graf. + + + Clear all graphs. + Nollställ alla grafer. + + + Move this graph upwards. + + + + Move this graph downwards. + + + + Mouse + Mus + + + Drag using the mouse button. + Dra genom att använda musknappen. + + + drags + dragningar + + + Select using the mouse button. + Välj genom att använda musknappen. + + + zooms + zoomningar + + + Interval + Intervall + + + Time of day + Tid på dagen + + + Log scale + Log-skala + + + Automatic update + + + + Enable legend + + + + Reset + Återställ + + + Reset Graph + Återställ grafen + + + Reset the graph to its initial state. + Återställ grafen till sitt ursprungstillstånd. + + + 0 + 0 + + + Zoom In + Zooma in + + + + + + + + + Zoom Out + Zooma ut + + + - + - + + + Move Up 10 Pixels + Flytta upp 10 bildpunkter + + + Up + Upp + + + Move Left 10 Pixels + Flytta åt vänster 10 bildpunkter + + + Left + Vänster + + + Move Right 10 Pixels + Flytta åt höger 10 bildpunkter + + + Right + Höger + + + Move Down 10 Pixels + Flytta ned 10 bildpunkter + + + Down + Ned + + + Move Up 1 Pixel + Flytta upp 1 bildpunkt + + + Shift+Up + Skift+upp + + + Move Left 1 Pixel + Flytta åt vänster 1 bildpunkt + + + Shift+Left + Skift+vänster + + + Move Right 1 Pixel + Flytta åt höger 1 bildpunkt + + + Shift+Right + Skift+höger + + + Move Down 1 Pixel + Flytta ned 1 bildpunkt + + + Move down 1 Pixel + Move down 1 pixel + Flytta ned 1 bildpunkt + + + Shift+Down + Skift+ned + + + Go To Packet Under Cursor + Gå till paketet under markören + + + Go to packet currently under the cursor + Gå till paketet som just nu är under markören + + + G + G + + + Drag / Zoom + Dra/zooma + + + Toggle mouse drag / zoom behavior + Växla musbeteende mellan dra/zooma + + + Z + Z + + + Capture / Session Time Origin + Ursprungstid för fångst/session + + + Toggle capture / session time origin + Växla mellan fångst/session som ursprungstid + + + T + T + + + Crosshairs + Hårkors + + + Toggle crosshairs + Byt hårkors + + + Space + Mellanslag + + + Zoom In X Axis + Zooma in X-axeln + + + X + X + + + Zoom Out X Axis + Zooma ut X-axeln + + + Shift+X + Skift+X + + + Zoom In Y Axis + Zooma in Y-axeln + + + Y + Y + + + Zoom Out Y Axis + Zooma ut Y-axeln + + + Shift+Y + Skift+Y + + + 1 sec + 1 s + + + 10 sec + 10 s + + + 1 min + 1 min + + + 10 min + 10 min + + + Time (s) + Tid (s) + + + I/O Graphs + I/O-grafer + + + Save As… + Spara som … + + + Copy + Kopiera + + + Copy graphs from another profile. + Kopiera grafer från en annan profil. + + + 1 ms + 1 ms + + + 2 ms + 2 ms + + + 5 ms + 5 ms + + + 10 ms + 10 ms + + + 20 ms + 20 ms + + + 50 ms + 50 ms + + + 100 ms + 100 ms + + + 200 ms + 200 ms + + + 500 ms + 500 ms + + + 2 sec + 2 s + + + 5 sec + 5 s + + + Wireshark I/O Graphs: %1 + Wireshark I/O-grafer: %1 + + + Filtered packets + Filtrerade paket + + + Filtered events + + + + All Packets + Alla paket + + + TCP Errors + TCP-fel + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + Håll över grafen för detaljer. + + + No packets in interval + Inga paket i intervallet + + + No events in interval + + + + Click to select packet + Klicka för att välja ett paket + + + Packet + Paket + + + Click to select event + + + + Event + Händelse + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Släpp för att zooma, x = %1 till %2, y = %3 till %4 + + + Unable to select range. + Kan inte välja intervallet. + + + Click to select a portion of the graph. + Klicka för att välja en del av grafen. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + Kommaseparerade värden (*.csv) + + + Save Graph As… + Spara grafen som … + + + + Iax2AnalysisDialog + + Dialog + Dialog + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Framåt</span></p><p><span style=" font-size:medium; font-weight:600;">Tillbaka</span></p></body></html> + + + Forward + Framåt + + + Packet + Paket + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Spridning (ms) + + + Bandwidth + Bandbredd + + + Status + Status + + + Length + Längd + + + Reverse + Omvänt + + + Graph + Graf + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>Visa eller dölj framåtspridningsvärden.</p></body></html> + + + Forward Jitter + Framåtspridning + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>Visa eller dölj framåtskillnadsvärden.</p></body></html> + + + Forward Difference + Framåtskillnad + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>Visa eller dölj omvända spridningsvärden.</p></body></html> + + + Reverse Jitter + Omvänd spridning + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>Visa eller dölj omvända skillnadsvärden.</p></body></html> + + + Reverse Difference + Omvänd skillnad + + + <small><i>A hint.</i></small> + <small><i>Ett tips.</i></small> + + + Audio + Audio + + + Save the audio data for both channels. + Spara audiodata för båda kanalerna. + + + Forward Stream Audio + Framåtströmsaudio + + + Save the forward stream audio data. + Spara data i framåtströmsaudio. + + + Reverse Stream Audio + Omvänd strömaudio + + + Save the reverse stream audio data. + Spara data i omvänd strömaudio. + + + CSV + CSV + + + Save both tables as CSV. + Spara båda tabellerna som CSV. + + + Forward Stream CSV + Framåtströms-CSV + + + Save the forward table as CSV. + Spara framåttabellen som CSV. + + + Reverse Stream CSV + Omvänd ströms CSV + + + Save the reverse table as CSV. + Spara den omvända tabellen som CSV. + + + Save Graph + Spara grafen + + + Save the graph image. + Spara grafbilden. + + + Go to Packet + Gå till paket + + + Select the corresponding packet in the packet list. + Välj det motsvarande paketet i paketlistan. + + + G + G + + + Next Problem Packet + Nästa problempaket + + + Go to the next problem packet + Gå till nästa problempaket + + + N + N + + + IAX2 Stream Analysis + IAX2-strömanalys + + + Unable to save RTP data. + Kan inte spara RTP-data. + + + Please select an IAX2 packet. + Välj ett IAX2-paket. + + + G: Go to packet, N: Next problem packet + G: gå till paket, N: nästa problempaket + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Spara grafen som … + + + Can't save in a file: Wrong length of captured packets. + Kan inte spara i en fil: fel längd på fångade paket. + + + Can't save in a file: File I/O problem. + Kan inte spara i en fil: problem med fil-I/O. + + + Save forward stream audio + Spara framåtströmsaudio + + + Save reverse stream audio + Spara omvänd strömaudio + + + Save audio + Spara audio + + + Sun Audio (*.au) + Sun Audio (*.au) + + + ;;Raw (*.raw) + ;;Rå (*.raw) + + + Warning + Varning + + + Unable to save in that format + Kan inte spara i det formatet + + + Unable to save %1 + Kan inte spara %1 + + + Saving %1… + Sparar %1 … + + + Analyzing IAX2 + Analyserar IAX2 + + + Save forward stream CSV + Spara framåtströms-CSV + + + Save reverse stream CSV + Spara omvänd ström-CSV + + + Save CSV + Spara CSV + + + Comma-separated values (*.csv) + Kommaseparerade värden (*.csv) + + + + ImportTextDialog + + File: + Fil: + + + Set name of text file to import + Ange namnet på textfil att importera + + + Browse for text file to import + Bläddra efter textfiler att importera + + + Browse… + Browse... + Bläddra… + + + Hex Dump + Hex-dump + + + Import a standard hex dump as exported by Wireshark + Importera en standardhexdump som exporterats av Wireshark + + + Offsets in the text file are in octal notation + Avstånd i textfilen är i oktal notation + + + Octal + Oktalt + + + Offsets: + Avstånd: + + + Offsets in the text file are in hexadecimal notation + Avstånd i textfilen är i hexadecimal notation + + + Hexadecimal + Hexadecimalt + + + Offsets in the text file are in decimal notation + Avstånd i textfilen är i decimal notation + + + Decimal + Decimalt + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>Huruvida den extra bearbetningen skall göras för att upptäcka början på ASCII-representationen vid slutet av en hex+ASCII-rad även om det ser ut som hexbyte:ar.</p><p>Aktivera inte om hexdumpen inte innehåller ASCII.</p></body></html> + + + ASCII identification: + ASCII-identifikation: + + + Regular Expression + Reguljärt uttryck + + + Import a file formatted according to a custom regular expression + Importera en fil formaterad enligt ett anpassat reguljärt uttryck + + + Packet format regular expression + Paketformat reguljärt uttryck + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + <html><head/><body><p>Perl-kompatibelt reguljärt uttryck för att fånga ett enskilt paket i filen med namngivna grupper som identifierar data att importera. Ankaren ^ och $ matchar även före/efter nyrader</p><p>Datagruppen är det enda nödvändiga, stödjer även tid, katalog och sekvensnummer.</p><p>Flaggor för reguljäruttryck: DUPNAMES, MULTILINE och NOEMPTY</p></body></html> + + + This is regexHintLabel, it will be set to default_regex_hint + Detta är regexHintLabel, det kommer sättas till default_regex_hint + + + Data encoding: + Datakodning: + + + How data is encoded + Hur data är kodat + + + encodingRegexExample + encodingRegexExample + + + List of characters indicating incoming packets + Lista över tecken som indikerar inkommande paket + + + iI< + iI< + + + List of characters indicating outgoing packets + Lista över tecken som indikerar utgående paket + + + oO> + oO> + + + Timestamp format: + Tidsstämpelformat: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Huruvida filen innehåller information som indikerar paketets riktning (inkommande eller utgående) eller inte. + + + Direction indication: + Riktningsindikationer: + + + ExportPDU + ExportPDU + + + IP version: + IP-version: + + + Interface name: + Gränssnittsnamn: + + + The name of the interface to write to the import capture file + Namnet på gränssnittet att skriva till importfångstfilen + + + Fake IF, Import from Hex Dump + Låtsat IF, importera från hexdump + + + Maximum frame length: + Maximal ramlängd: + + + Encapsulation + Inkapsling + + + The text file has no offset + Textfilen har ingen förskjutning + + + None + Ingen + + + <small><i>recommended regex:</small></i> + <small><i>rekommenderat reguljäruttryck:</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + Formatet i vilket tidsstämplar i textfilen skall tolkas (t.ex. %H:%M:%S). Formatspecifikationer baseras på strptime(3) + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + <html><head/><body><p>Formatet i vilket tidsstämplar skall tolkas i textfilen (t.ex. %H.%M.%S,%f).</p><p>Formatspecificerare baseras på strptime(3) med tillägget %f för delar av sekunder. Upplösningen på %f avgörs från dess längd.</p></body></html> + + + %H:%M:%S.%f + %H.%M.%S,%f + + + timestampExampleLabel + timestampExampleLabel + + + Encapsulation Type: + Inkapslingstyp: + + + Encapsulation type of the frames in the import capture file + Inkapslingstyp på ramarna i filen för importfångst + + + Prefix each frame with an Ethernet and IP header + Föregå varje ram med ett Ethernet- och IP-huvud + + + IP + IP + + + Prefix each frame with an Ethernet, IP and UDP header + Föregå varje ram med ett Ethernet-, IP-, och UDP-huvud + + + Prefix each frame with an Ethernet, IP and TCP header + Föregå varje ram med ett Ethernet-, IP-, och TCP-huvud + + + Prefix each frame with an Ethernet, IP and SCTP header + Föregå varje ram med ett Ethernet-, IP-, och SCTP-huvud + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + Föregå varje ram med ett Ethernet-, IP-, och SCTP (DATA)-huvud + + + Source address: + Källadress: + + + Destination address: + Destinationsadress: + + + Dissector + Dissekerare + + + The IP protocol ID for each frame + IP protokoll-ID för varje ram + + + The IP source address for each frame + IP-källadressen för varje ram + + + The IP destination address for each frame + IP-destinationsadressen för varje ram + + + The UDP, TCP or SCTP source port for each frame + UDP-, TCP- eller SCTP-källporten för varje ram + + + The SCTP DATA payload protocol identifier for each frame + Protokollidentifieraren för SCTP DATA-lasten för varje ram + + + The UDP, TCP or SCTP destination port for each frame + UDP-, TCP- eller SCTP-destinationsporten för varje ram + + + Prefix each frame with an Ethernet header + Föregå varje ram med ett Ethernet-huvud + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + Protokoll (dec): + + + Leave frames unchanged + Lämna ramar oförändrade + + + No dummy header + Inget attrapphuvud + + + Tag: + Tagg: + + + UDP + UDP + + + Source port: + Källport.: + + + The Ethertype value of each frame + Ethertype-värdet för varje ram + + + TCP + TCP + + + The SCTP verification tag for each frame + SCTP-verifikationstaggen för varje ram + + + Destination port: + Destinationsport: + + + Ethertype (hex): + Ethertype (hex): + + + SCTP (Data) + SCTP (Data) + + + The dissector to use for each frame + Dissekeraren att använda för varje ram + + + The IP Version to use for the dummy IP header + IP-versionen att använda för attrapp-IP-huvud + + + The maximum size of the frames to write to the import capture file (max 256kiB) + Den maximala storleken på ramarna som skrivs till filen för importfångst (max 256 kiB) + + + Supported fields are data, dir, time, seqno + Fält som stödjs är data, dir, time, seqno + + + Missing capturing group data (use (? + Data för fångstgrupp saknas (använd (? + + + Import From Hex Dump + Importera ifrån hexdump + + + Import + Importera + + + Import Text File + Importera textfil + + + + InterfaceFrame + + Frame + Ram + + + Wired + Trådat + + + AirPCAP + AirPCAP + + + Pipe + Rör + + + STDIN + STANDARD IN + + + Bluetooth + Blåtand + + + Wireless + Trådlöst + + + Dial-Up + Uppringt + + + USB + USB + + + External Capture + Extern fångst + + + Virtual + Virtuellt + + + Remote interfaces + Fjärrgränssnitt + + + Show hidden interfaces + Visa dolda gränssnitt + + + External capture interfaces disabled. + Externa fångstgränssnitt avaktiverade. + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + <p>Lokala gränssnitt är inte tillgängliga för att ingen paketfångstdrivrutin är installerad</p><p>Man kan lösa detta genom att installera <a href="https://npcap.com/">Npcap</a>.</p> + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + <p>Lokala gränssnitt är otillgängliga för att paketfångstdrivrutinen inte är inläst.</p><p>Du kan fixa detta genom att köra <pre>net start npcap</pre> om du har Npcap installerat eller <pre>net start npf</pre> om du har WinPcap installerat. Båda kommandona måste köras som administratör.</p> + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + <p>Du har inte rättigheter att fånga från lokala gränssnitt.</p><p>Du kan fixa detta genom att <a href="file://%1">installera ChmodBPF</a>.</p> + + + You don't have permission to capture on local interfaces. + Du har inte rättigheter att fånga från lokala gränssnitt. + + + No interfaces found. + Inga gränssnitt hittades. + + + Interfaces not loaded (due to preference). Go to Capture + Gränssnitt inte inlästa (på grund av inställningar). Gå till fångst + + + Start capture + Börja fångst + + + Hide Interface + Dölj gränssnitt + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + Inga gränssnitt att visa. %1 gränssnitt dolda. + + + + InterfaceToolbar + + Frame + Ram + + + Select interface + Välj gränssnitt + + + Interface + Gränssnitt + + + + InterfaceToolbarLineEdit + + Apply changes + Verkställ ändringar + + + + InterfaceTreeModel + + Show + Visa + + + Friendly Name + Trevligt namn + + + Interface Name + Gränssnittsnamn + + + No interfaces found. + Inga gränssnitt hittades. + + + This version of Wireshark was built without packet capture support. + Denna version av Wireshark byggdes utan stöd för paketfångst. + + + Local Pipe Path + Lokal rörsökväg + + + Comment + Kommentar + + + Link-Layer Header + Länknivåhuvud + + + Promiscuous + Promiskuös + + + Snaplen (B) + Provlängd (B) + + + Buffer (MB) + Buffert (MB) + + + Monitor Mode + Monitorläge + + + Capture Filter + Fångstfilter + + + Addresses + Adresser + + + Address + Adress + + + Extcap interface: %1 + Extcap-gränssnitt: %1 + + + No addresses + Inga adresser + + + No capture filter + Inget fångstfilter + + + Capture filter + Fångstfilter + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + LBT-RM-transportstatistik + + + Sources + Källor + + + Address/Transport + Adress/transport + + + Data frames + Dataramar + + + Data bytes + Databyte + + + Data frames/bytes + Dataramar/-byte + + + Data rate + Datahastighet + + + RX data frames + RX-dataramar + + + RX data bytes + RX-databyte + + + RX data frames/bytes + RX-dataramar/-byte + + + RX data rate + RX-datahastighet + + + NCF frames + NCF-ramar + + + NCF count + NCF-antal + + + NCF bytes + NCF-byte + + + NCF frames/bytes + NCF-ramar/-byte + + + NCF count/bytes + NCF-antal/-byte + + + NCF frames/count + NCF-ramar/-antal + + + NCF frames/count/bytes + NCF-ramar/-antal/-byte + + + NCF rate + NCF-hastighet + + + SM frames + SM-ramar + + + SM bytes + SM-byte + + + SM frames/bytes + SM-ramar/-byte + + + SM rate + SM-hastighet + + + Show + Visa + + + Data + Data + + + RX Data + RX-data + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + sekvensnummer i transporten + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Antal + + + Frame + Ram + + + SQN/Reason + SQN/anledning + + + Receivers + Mottagare + + + NAK frames + NAK-ramar + + + NAK count + NAK-antal + + + NAK bytes + NAK-byte + + + NAK rate + NAK-hastighet + + + NAK sequence numbers for transport + NAK-sekvensnummer i transporten + + + Display filter: + Visningsfilter: + + + Regenerate statistics using this display filter + Generera om statistiken med detta visningsfilter + + + Apply + Verkställ + + + Copy as CSV + Kopiera som CSV + + + Copy the tree as CSV + Kopiera trädet som CSV + + + Copy as YAML + Kopiera som YAML + + + Copy the tree as YAML + Kopiera trädet som YAML + + + Show the data frames column + Visa dataramskolumnen + + + Show the data bytes column + Visa databyteskolumnen + + + Show the data frames/bytes column + Visa datarams-/-bytekolumnen + + + Show the RX data frames column + Visa RX-dataramskolumnen + + + Show the RX data bytes column + Visa RX-databyteskolumnen + + + Show the RX data frames/bytes column + Visa RX-datarams-/-byteskolumnen + + + Show the NCF frames column + Visa NCF-ramskolumnen + + + Show the NCF bytes column + Visa NCF-bytekolumnen + + + Show the NCF count column + Visa NCF-antalkolumnen + + + Show the data rate column + Visa datahastighetskolumnen + + + Show the RX data rate column + Visa RX-datahastighetskolumnen + + + Show the NCF frames/bytes column + Visa NCF-ram-/-bytekolumnen + + + Show the NCF count/bytes column + Visa NCF-antal-/-bytekolumnen + + + Show the NCF frames/count column + Visa NCF-ram-/-antalkolumnen + + + Show the NCF frames/count/bytes column + Visa NCF-ram-/antal-/-bytekolumnen + + + Show the NCF rate column + Visa NCF-hastighetskolumnen + + + Show the SM frames column + Visa SM-ramkolumnen + + + Show the SM bytes column + Visa SM-bytekolumnen + + + Show the SM frames/bytes column + Visa SM-ram-/-bytekolumnen + + + Show the SM rate column + Visa SM-hastighetskolumnen + + + Auto-resize columns to content + Skala automatiskt om kolumner till innehållet + + + Resize columns to content size + Skala om kolumner till innehållets storlek + + + LBT-RM Statistics failed to attach to tap + LBT-RM-statistik misslyckades att koppla till uttaget + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + LBT-RU-transportstatistik + + + Sources + Källor + + + Address/Transport/Client + Adress/transport/klient + + + Data frames + Dataramar + + + Data bytes + Databyte + + + Data frames/bytes + Dataramar/-byte + + + Data rate + Datahastighet + + + RX data frames + RX-dataramar + + + RX data bytes + RX-databyte + + + RX data frames/bytes + RX-dataramar/-byte + + + RX data rate + RX-datahastighet + + + NCF frames + NCF-ramar + + + NCF count + NCF-antal + + + NCF bytes + NCF-byte + + + NCF frames/count + NCF-ramar/-antal + + + NCF frames/bytes + NCF-ramar/-byte + + + NCF count/bytes + NCF-antal/-byte + + + NCF frames/count/bytes + NCF-ramar/-antal/-byte + + + NCF rate + NCF-hastighet + + + SM frames + SM-ramar + + + SM bytes + SM-byte + + + SM frames/bytes + SM-ramar/-byte + + + SM rate + SM-hastighet + + + RST frames + RST-ramar + + + RST bytes + RST-byte + + + RST frames/bytes + RST-ramar/-byte + + + RST rate + RST-hastighet + + + Show + Visa + + + Data SQN + Data-SQN + + + RX Data SQN + RX-data-SQN + + + NCF SQN + NCF SQN + + + SM SQN + SM SQN + + + RST reason + RST-anledning + + + details for transport + detaljer om transporten + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Antal + + + Frame + Ram + + + Reason + Anledning + + + SQN/Reason + SQN/anledning + + + Receivers + Mottagare + + + Address/Transport + Adress/transport + + + NAK frames + NAK-ramar + + + NAK count + NAK-antal + + + NAK bytes + NAK-byte + + + NAK frames/count + NAK-ramar/-antal + + + NAK count/bytes + NAK-antal/-byte + + + NAK frames/bytes + NAK-ramar/-byte + + + NAK frames/count/bytes + NAK-ramar/-antal/-byte + + + NAK rate + NAK-hastighet + + + ACK frames + ACK-ramar + + + ACK bytes + ACK-byte + + + ACK frames/bytes + ACK-ramar/-byte + + + ACK rate + ACK-hastighet + + + CREQ frames + CREQ-ramar + + + CREQ bytes + CREQ-byte + + + CREQ frames/bytes + CREQ-ramar/-byte + + + CREQ rate + CREQ-hastighet + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + CREQ-begäran + + + Display filter: + Visningsfilter: + + + Regenerate statistics using this display filter + Generera om statistiken med detta visningsfilter + + + Apply + Verkställ + + + Copy as CSV + Kopiera som CSV + + + Copy the tree as CSV + Kopiera trädet som CSV + + + Copy as YAML + Kopiera som YAML + + + Copy the tree as YAML + Kopiera trädet som YAML + + + Show the data frames column + Visa dataramskolumnen + + + Show the data bytes column + Visa databyteskolumnen + + + Show the data frames/bytes column + Visa datarams-/-bytekolumnen + + + Show the data rate column + Visa datahastighetskolumnen + + + Show the RX data frames column + Visa RX-dataramskolumnen + + + Show the RX data bytes column + Visa RX-databyteskolumnen + + + Show the RX data frames/bytes column + Visa RX-datarams-/-byteskolumnen + + + Show the RX data rate column + Visa RX-datahastighetskolumnen + + + Show the NCF frames column + Visa NCF-ramskolumnen + + + Show the NCF count column + Visa NCF-antalkolumnen + + + Show the NCF bytes column + Visa NCF-bytekolumnen + + + Show the NCF frames/bytes column + Visa NCF-ram-/-bytekolumnen + + + Show the NCF count/bytes column + Visa NCF-antal-/-bytekolumnen + + + Show the NCF frames/count column + Visa NCF-ram-/-antalkolumnen + + + Show the NCF frames/count/bytes column + Visa NCF-ram-/antal-/-bytekolumnen + + + Show the SM frames column + Visa SM-ramkolumnen + + + Show the SM bytes column + Visa SM-bytekolumnen + + + Show the SM frames/bytes column + Visa SM-ram-/-bytekolumnen + + + Show the SM rate column + Visa SM-hastighetskolumnen + + + Show the RST frames column + Visa RST-ramkolumnen + + + Show the RST bytes column + Visa RST-bytekolumnen + + + Show the RST frames/bytes column + Visa RST-ram-/-bytekolumnen + + + Show the RST rate column + Visa RST-hastighetskolumnen + + + Show the NAK frames column + Visa NAK-ramskolumnen + + + Show the NAK count column + Visa NAK-antalkolumnen + + + Show the NAK bytes column + Visa NAK-bytekolumnen + + + Show the NAK frames/count column + Visa NAK-ram-/-antalkolumnen + + + Show the NAK count/bytes column + Visa NAK-antal-/-bytekolumnen + + + Show the NAK frames/bytes column + Visa NAK-ram-/-bytekolumnen + + + Show the NAK frames/count/bytes column + Visa NAK-ram-/antal-/-bytekolumnen + + + Show the NAK rate column + Visa NAK-hastighetskolumnen + + + Show the ACK frames column + Visa ACK-ramskolumnen + + + Show the ACK bytes column + Visa ACK-bytekolumnen + + + Show the ACK frames/bytes column + Visa ACK-ram-/-bytekolumnen + + + Show the ACK rate column + Visa ACK-hastighetskolumnen + + + Show the CREQ frames column + Visa CREQ-ramskolumnen + + + Show the CREQ bytes column + Visa CREQ-bytekolumnen + + + Show the CREQ frames/bytes column + Visa CREQ-ram-/-bytekolumnen + + + Show the CREQ rate column + Visa CREQ-hastighetskolumnen + + + Auto-resize columns to content + Skala automatisk om kolumner till innehållet + + + Resize columns to content size + Skala om kolumner till innehållets storlek + + + Show the NCF rate column + Visa NCF-hastighetskolumnen + + + LBT-RU Statistics failed to attach to tap + LBT-RU-statistik misslyckades att koppla till uttaget + + + + LBMStreamDialog + + Dialog + Dialog + + + Stream + Ström + + + Endpoint A + Ändpunkt A + + + Endpoint B + Ändpunkt B + + + Messages + Meddelanden + + + Bytes + Byte + + + First Frame + Första ram + + + Last Frame + Sista ram + + + Display filter: + Visningsfilter: + + + Regenerate statistics using this display filter + Generera om statistiken med detta visningsfilter + + + Apply + Verkställ + + + Copy as CSV + Kopiera som CSV + + + Copy the tree as CSV + Kopiera trädet som CSV + + + Copy as YAML + Kopiera som YAML + + + Copy the tree as YAML + Kopiera trädet som YAML + + + LBM Stream failed to attach to tap + LBM-strömmen misslyckades att koppla till uttaget + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + LayoutPreferencesFrame + + Frame + Ram + + + Pane 1: + Panel 1: + + + Packet List + Paketlista + + + Packet Details + Paketdetaljer + + + Packet Bytes + Paketbyte + + + Packet Diagram + Paketdiagram + + + None + Inga + + + Pane 2: + Panel 2: + + + Pane 3: + Panel 3: + + + Packet List settings: + Paketlisteinställningar: + + + Show packet separator + Visa paketavdelare + + + Show column definition in column context menu + Visa kolumndefinitioner i kolumnkontextmenyn + + + Allow the list to be sorted + Tillåt att listan sorteras + + + Maximum number of cached rows (affects sorting) + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + Enable mouse-over colorization + Aktivera färgläggning vid musen över + + + Status Bar settings: + Statusradinställningar: + + + Show selected packet number + Visa det valda paketets nummer + + + Show file load time + Visa tiden för inläsning av filen + + + + LteMacStatisticsDialog + + LTE Mac Statistics + LTE Mac-statistik + + + Include SR frames in filter + Inkludera SR-ramar i filter + + + Include RACH frames in filter + Inkludera RACH-ramar i filter + + + MAC Statistics + MAC-statistik + + + + LteRlcGraphDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Värdefulla och fantastiska tidssparande tangenbordsgenvägar</h3> +<table><tbody> + +<tr><th>+</th><td>Zooma in</td></th> +<tr><th>-</th><td>Zooma ut</td></th> +<tr><th>0</th><td>Återställ grafen till sitt ursprungliga läge</td></th> + +<tr><th>→</th><td>Flytta åt höger 10 bildpunkter</td></th> +<tr><th>←</th><td>Flytta åt vänster 10 bildpunkter</td></th> +<tr><th>↑</th><td>Flytta uppåt 10 bildpunkter</td></th> +<tr><th>↓</th><td>Flytta nedåt 10 bildpunkter</td></th> +<tr><th><i>Skift+</i>→</th><td>Flytta åt höger 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>←</th><td>Flytta åt vänster 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>↑</th><td>Flytta uppåt 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>↓</th><td>Flytta nedåt 1 bildpunkt</td></th> + +<tr><th>g</th><td>Gå till paketet under markören</td></th> + +<tr><th>z</th><td>Byt mellan att musen drar/zoomar</td></th> +<tr><th>t</th><td>Byt mellan fångst-/sessionsstarttid</td></th> +<tr><th>Mellanslag</th><td>Byt hårkors</td></th> + +</tbody></table> +</body></html> + + + Mouse + Mus + + + Drag using the mouse button. + Dra genom att använda musknappen. + + + drags + dragningar + + + Select using the mouse button. + Välj genom att använda musknappen. + + + zooms + zoomningar + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Återställ grafen till sitt ursprungstillstånd.</p></body></html> + + + Reset + Återställ + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Byt riktning på förbindelsen (visa det omvända flödet).</p></body></html> + + + Switch Direction + Byt riktning + + + Reset Graph + Återställ grafen + + + Reset the graph to its initial state. + Återställ grafen till sitt ursprungstillstånd. + + + 0 + 0 + + + Zoom In + Zooma in + + + + + + + + + Zoom Out + Zooma ut + + + - + - + + + Move Up 10 Pixels + Flytta upp 10 bildpunkter + + + Up + Upp + + + Move Left 10 Pixels + Flytta åt vänster 10 bildpunkter + + + Left + Vänster + + + Move Right 10 Pixels + Flytta åt höger 10 bildpunkter + + + Right + Höger + + + Move Down 10 Pixels + Flytta ned 10 bildpunkter + + + Down + Ned + + + Move Up 1 Pixel + Flytta upp 1 bildpunkt + + + Shift+Up + Skift+upp + + + Move Left 1 Pixel + Flytta åt vänster 1 bildpunkt + + + Shift+Left + Skift+vänster + + + Move Right 1 Pixel + Flytta åt höger 1 bildpunkt + + + Shift+Right + Skift+höger + + + Move Down 1 Pixel + Flytta ned 1 bildpunkt + + + Move down 1 Pixel + Flytta ned 1 bildpunkt + + + Shift+Down + Skift+ned + + + Drag / Zoom + Dra/zooma + + + Toggle mouse drag / zoom behavior + Växla musbeteende mellan dra/zooma + + + Z + Z + + + Crosshairs + Hårkors + + + Toggle crosshairs + Byt hårkors + + + Space + Mellanslag + + + Move Up 100 Pixels + Flytta upp 100 bildpunkter + + + PgUp + Sida upp + + + PgDown + Sida ned + + + Go To Packet Under Cursor + Gå till paketet under markören + + + Go to packet currently under the cursor + Gå till paketet som just nu är under markören + + + G + G + + + Zoom In X Axis + Zooma in X-axeln + + + X + X + + + Zoom Out Y Axis + Zooma ut Y-axeln + + + Shift+Y + Skift+Y + + + Zoom In Y Axis + Zooma in Y-axeln + + + Y + Y + + + Zoom Out X Axis + Zooma ut X-axeln + + + Shift+X + Skift+X + + + Switch direction (swap between UL and DL) + Byt riktning (växla mellan UL och DL) + + + D + D + + + Time + Tid + + + Sequence Number + Sekvensnummer + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + LTE RLC-graf (UE=%1 kan=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + LTE RLC-graf – ingen kanal vald + + + Save As… + Spara som … + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s sek %4 län %5) + + + Click to select packet + Klicka för att välja ett paket + + + Packet + Paket + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Släpp för att zooma, x = %1 till %2, y = %3 till %4 + + + Unable to select range. + Kan inte välja intervallet. + + + Click to select a portion of the graph. + Klicka för att välja en del av grafen. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Spara grafen som … + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + LTE RLC-statistik + + + Include SR frames in filter + Inkludera SR-ramar i filter + + + Include RACH frames in filter + Inkludera RACH-ramar i filter + + + Use RLC frames only from MAC frames + Använd endast RLC-ramar från MAC-ramar + + + UL Frames + UL-ramar + + + UL Bytes + UL-byte + + + UL MB/s + UL MB/s + + + UL ACKs + UL ACK:ar + + + UL NACKs + UL NACK:ar + + + UL Missing + UL saknade + + + DL Frames + DL-ramar + + + DL Bytes + DL-byte + + + DL MB/s + DL MB/s + + + DL ACKs + DL ACK:ar + + + DL NACKs + DL NACK:ar + + + DL Missing + DL saknade + + + RLC Statistics + RLC-statistik + + + + MainStatusBar + + Ready to load or capture + Klar att läsa in eller fånga + + + Ready to load file + Klar att läsa in en fil + + + Open the Capture File Properties dialog + Öppna dialogen för egenskaper hos fångstfilen + + + Profile: %1 + Profil: %1 + + + Manage Profiles… + Hantera profiler … + + + New… + Ny … + + + Edit… + Redigera … + + + Import + Importera + + + Export + Exportera + + + Delete + Ta bort + + + Switch to + Byt till + + + is the highest expert information level + is the highest expert info level + är den högsta expertinformationsnivån + + + ERROR + FEL + + + WARNING + VARNING + + + NOTE + OBS + + + CHAT + TJATT + + + No expert information + No expert info + Ingen expertinformation + + + %Ln byte(s) + , %1 bytes + + %Ln byte + %Ln byte + + + + Byte %1 + Byte %1 + + + Bytes %1-%2 + Byte %1-%2 + + + Selected Packet: %1 %2 + Välj paket: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + Paket: %1 %4 Visat: %2 (%3 %) + + + %1 Selected: %2 (%3%) + %1 valda: %2 (%3 %) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 markerade: %2 (%3 %) + + + %1 Dropped: %2 (%3%) + %1 släppta: %2 (%3 %) + + + %1 Ignored: %2 (%3%) + %1 ignorerade: %2 (%3 %) + + + %1 Comments: %2 + %1 kommentarer: %2 + + + %1 Load time: %2:%3.%4 + %1 inläsningstid: %2.%3.%4 + + + No Packets + Inga paket + + + From Zip File... + + + + From Directory... + + + + Selected Personal Profile... + + + + All Personal Profiles... + + + + Packets: %1 + Paket: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + Ram + + + Checking this will save the size, position, and maximized state of the main window. + Att markera detta kommer spara storleken, positionen och maximeringstillståndet för huvudfönstret. + + + Remember main window size and placement + Kom ihåg huvudfönstrets storlek och placering + + + Open files in + Öppna filer i + + + This folder: + Denna mapp: + + + Browse… + Browse... + Bläddra … + + + The most recently used folder + Den senast använda mappen + + + Show up to + Visa upp till + + + filter entries + filtrera poster + + + recent files + nyligen använda filer + + + Confirm unsaved capture files + Bekräfta osparade fångstfiler + + + Display autocompletion for filter text + Visa autokomplettering av filtertext + + + Main toolbar style: + Stil för huvudverktygsrad: + + + Icons only + Endast ikoner + + + Text only + Endast text + + + Icons & Text + Ikoner & text + + + Window title + Fönstertitel + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Anpassad fönstertitel att läggas till efter den befintliga titeln<br/>%F = filsökväg till fångstfilen<br/>%P = profilnamn<br/>%S = en villkorlig separator (&quot; - &quot;) som bara visas när den omges av variabler med värden eller statisk text<br/>%V = versionsinformation</p></body></html> + + + Prepend window title + Lägg till före fönstertiteln + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Anpassad fönstertitel att läggas till före den befintliga titeln<br/>%F = filsökväg till fångstfilen<br/>%P = profilnamn<br/>%S = en villkorlig separator (&quot; - &quot;) som bara visas när den omges av variabler med värden eller statisk text<br/>%V = versionsinformation</p></body></html> + + + Language: + Språk: + + + Use system setting + Använd systeminställningar + + + Open Files In + Öppna filer i + + + + ManageInterfacesDialog + + Manage Interfaces + Hantera gränssnitt + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Klicka i kryssrutan för att dölja eller visa ett dolt gränssnitt.</p></body></html> + + + Local Interfaces + Lokala gränssnitt + + + Show + Visa + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Lägg till ett rör för att fånga från eller ta bort eller ta bort ett befintligt rör från listan.</p></body></html> + + + Pipes + Rör + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Lägg till ett nytt rör som använder standardinställningar.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Ta bort det valda röret från listan.</p></body></html> + + + Remote Interfaces + Fjärrgränssnitt + + + Host / Device URL + Värd-/enhets-URL + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>Lägg till en fjärrvärd och dess gränssnitt</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>Ta bort den valda värden från listan.</p></body></html> + + + Remote Settings + Fjärrinställningar + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Denna version av Wireshark sparar inte rörinställningar. + + + This version of Wireshark does not save remote settings. + Denna version av Wireshark sparar inte fjärrinställningar. + + + This version of Wireshark does not support remote interfaces. + Denna version av Wireshark stödjer inte fjärrgränssnitt. + + + New Pipe + Nytt rör + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + Välj allt + + + Copy + Kopiera + + + Find + Sök + + + Clear + Töm + + + + ManufTableModel + + Address Block + + + + Short Name + Kort namn + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + Rullningsområde + + + + Mtp3SummaryDialog + + Dialog + Dialog + + + MTP3 Summary + MTP3-sammanfattning + + + File + Fil + + + Name + Namn + + + Length + Längd + + + Format + Format + + + Snapshot length + Längd på ögonblicksbild + + + Data + Data + + + First packet + Första paket + + + Last packet + Sista paket + + + Elapsed + Förflutet + + + Packets + Paket + + + Service Indicator (SI) Totals + Tjänsteindikator- (TI-)totaler + + + SI + TI + + + MSUs + MSU:er + + + MSUs/s + MSU:er/s + + + Bytes + Byte + + + Bytes/MSU + Byte/MSU + + + Bytes/s + Byte/s + + + Totals + Totalt + + + Total MSUs + Totalt MSU:er + + + Total Bytes + Totalt byte + + + Average Bytes/MSU + Genomsnittligt byte/MSU + + + Average Bytes/s + Genomsnittligt byte/s + + + + MulticastStatisticsDialog + + UDP Multicast Streams + UDP-multicast-strömmar + + + Source Address + Källadress + + + Source Port + Källport + + + Destination Address + Destinationsadress + + + Destination Port + Destinationsport + + + Packets + Paket + + + Packets/s + Paket/s + + + Avg BW (bps) + Snitt BW (b/s) + + + Max BW (bps) + Max BW (b/s) + + + Max Burst + Max utbrott + + + Burst Alarms + Utbrottslarm + + + Max Buffers (B) + Max buffertar (B) + + + Buffer Alarms + Buffertlarm + + + Burst measurement interval (ms): + Utbrottsmätningsintervall (ms): + + + Burst alarm threshold (packets): + Utbrottslarmsgräns (paket): + + + Buffer alarm threshold (B): + Buffertlarmsgräns (B): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + Strömmens tomgångshastigheten (kb/s): + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + Total tomgångshastighet (kb/s): + + + The burst interval must be between 1 and 1000. + Utbrottsintervallet måste vara mellan 1 och 1000. + + + The burst alarm threshold isn't valid. + Utbrottslarmsgränsen är inte riktig. + + + The buffer alarm threshold isn't valid. + Buffertlarmsgränsen är inte riktig. + + + The stream empty speed should be between 1 and 10000000. + Strömmens tomgångshastighet skall vara mellan 1 och 10000000. + + + The total empty speed should be between 1 and 10000000. + Den totala tomgångshastighet skall vara mellan 1 och 10000000. + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + %1 strömmar, snitt bw: %2 b/s, max bw: %3 b/s, max utbrott: %4 / %5 ms, max buffert: %6 B + + + + PacketCommentDialog + + Edit Packet Comment + Redigera paketkommentaren + + + Add Packet Comment + Lägg till en paketkommentar + + + + PacketDiagram + + Packet diagram + Paketdiagram + + + Show Field Values + Visa fältvärden + + + Save Diagram As… + Spara diagrammet som … + + + Copy as Raster Image + Kopiera som rasterbild + + + …as SVG + … som SVG + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + Scalable Vector Graphics (*.svg) + + + Save Graph As… + Spara grafen som … + + + + PacketDialog + + Dialog + Dialog + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + Visa paketbyte + + + Packet %1 + Paket %1 + + + [%1 closed] + [%1 stängd] + + + Byte %1 + Byte %1 + + + Bytes %1-%2 + Byte %1-%2 + + + %Ln byte(s) + + %Ln byte + %Ln byte + + + + + PacketFormatGroupBox + + GroupBox + GroupBox + + + Packet Format + Paketformat + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>Paketsammanfattningsrader liknande dem i paketlistan</p></body></html> + + + Summary line + Sammanfattningsrad + + + Include column headings + Inkludera kolumnrubriker + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>Paketdetaljer liknande dem i protokollträdet</p></body></html> + + + Details: + Detaljer: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>Exportera endast poster med detaljer på toppnivåpaket</p></body></html> + + + All co&llapsed + Allt ih&opfällt + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>Expandera och fäll ihop paketdetaljer så som de för närvarande visas.</p></body></html> + + + As displa&yed + Som &visat + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>Exportera alla paketdetaljposter</p></body></html> + + + All e&xpanded + Alla e&xpanderade + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>Exportera en hexdump av paketdatan liknande paketbytevyn</p></body></html> + + + Bytes + Byte + + + Include secondary data sources + Inkludera sekundära datakällor + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + <html><head/><body><p>Generera hexdumpar för sekundära datakällor såsom återsammansatta eller dekrypterade buffertar utöver ramen</p></body></html> + + + + PacketList + + Protocol Preferences + Protokollinställningar + + + Summary as Text + Sammanfattning som text + + + …as CSV + … som CSV + + + …as YAML + … som YAML + + + Decode As… + Avkoda som … + + + Frame %1: %2 + + + Ram %1: %2 + + + + + [ Comment text exceeds %1. Stopping. ] + [ Kommentartext överskrider %1. Stannar. ] + + + + PacketListHeader + + Align Left + Vänsterjustera + + + Align Center + Centrera + + + Align Right + Högerjustera + + + Edit Column + Redigera kolumnen + + + Resize to Contents + Ändra storlek till innehåll + + + Column Preferences… + Kolumninställningar … + + + Resize Column to Width… + Ändra kolumnstorlek till bredden … + + + Resolve Names + Slå upp namn + + + Remove this Column + Ta bort denna kolumn + + + Column %1 + Kolumn %1 + + + Width: + Bredd: + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + + + + Sorting "%1"… + Sorterar ”%1” … + + + Sorting … + + + + + PacketRangeGroupBox + + Form + Formulär + + + Packet Range + Paketintervall + + + - + - + + + Displayed + Visat + + + &Marked packets only + Endast &markerade paket + + + &Range: + I&ntervall: + + + Remove &ignored packets + Ta bort &ignorerade paket + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + Första &till sista markerat + + + &All packets + &Alla paket + + + &Selected packets only + Endast &valda paket + + + Captured + Fångade + + + + PathSelectionDelegate + + Open a pipe + Öppna ett rör + + + + PathSelectionEdit + + Browse + Bläddra + + + Select a path + Välj en sökväg + + + + PluginListModel + + Name + Namn + + + Version + Version + + + Type + Typ + + + Path + Sökväg + + + + PortsModel + + All entries + Alla poster + + + tcp + tcp + + + udp + udp + + + sctp + sctp + + + dccp + dccp + + + Name + Namn + + + Port + Port + + + Type + Typ + + + + PreferenceEditorFrame + + Frame + Ram + + + … + + + + a preference + en inställning + + + Browse… + Bläddra… + + + Open %1 preferences… + Öppna %1 inställningar … + + + Invalid value. + Felaktigt värde. + + + + PreferencesDialog + + Search: + Sök: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + Inställningar + + + + PrefsModel + + Advanced + Avancerat + + + Appearance + Utseende + + + Layout + Layout + + + Columns + Kolumner + + + Font and Colors + Typsnitt och färger + + + Capture + Fångst + + + Expert + Expert + + + Filter Buttons + Filterknappar + + + RSA Keys + RSA-nycklar + + + + PrintDialog + + Packet Format + Paketformat + + + Print each packet on a new page + Skriv ut varje paket på en ny sida + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>Skriv ut fångstfilsinformation på varje sida</p></body></html> + + + Capture information header + Fångstinformationshuvud + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>Använd tangenterna &quot;+&quot; och &quot;-&quot; för att zooma in och ut förhandsvisningen. Använd tangenten &quot;0&quot; för att återställa zoomnivån.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ och - zoomar, 0 återställer</span></p></body></html> + + + Packet Range + Paketintervall + + + Print + Skriv ut + + + &Print… + &Skriv ut … + + + Page &Setup… + Sid&inställning … + + + %1 %2 total packets, %3 shown + %1 %2 paket totalt, %3 visade + + + Print Error + Skrivfel + + + Unable to print to %1. + Kan inte skriva till %1. + + + + ProfileDialog + + Search for profile … + Sök efter profil … + + + Create a new profile using default settings. + Skapa en ny profil baserat på standardinställningarna. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>Ta bort denna profil. Profiler som tillhandahålls av systemet kan inte tas bort. Standardprofilen kommer återställas vid radering.</p></body></html> + + + Copy this profile. + Kopiera denna profil. + + + Configuration Profiles + Konfigurationsprofiler + + + Import + noun + Import + + + Export + noun + Export + + + From Zip File... + + + + From Directory... + + + + %Ln Selected Personal Profile(s)... + + + + + + + All Personal Profiles... + + + + New profile + Ny profil + + + Profile Error + Profilfel + + + Exporting profiles + Exporterar profiler + + + No profiles found for export + Inga profiler finns att exportera + + + Select zip file for export + Välj zip-fil för exporten + + + An import of profiles is not allowed, while changes are pending + En import av profiler är inte tillåtet medans ändringar väntar + + + An import is pending to be saved. Additional imports are not allowed + En import väntar på att sparas. Ytterligare importer är inte tillåtna + + + An export of profiles is only allowed for personal profiles + En export av profiler är endast tillåtet för personliga profiler + + + An export of profiles is not allowed, while changes are pending + En export av profiler är inte tillåtet medan ändringar väntar + + + %Ln profile(s) exported + + %Ln profil exporterad + %Ln profiler exporterade + + + + Select zip file for import + Välj zip-fil för importen + + + Select directory for import + Välj katalog för importen + + + Zip File (*.zip) + Zip-fil (*.zip) + + + Error + Fel + + + An error has occurred while exporting profiles + Ett fel uppstod vid export av profiler + + + No profiles found for import in %1 + Inga profiler finns att importera i %1 + + + %Ln profile(s) imported + + %Ln profil importerad + %Ln profiler importerade + + + + , %Ln profile(s) skipped + + , %Ln profil skippad + , %Ln profiler skippade + + + + Importing profiles + Importerar profiler + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + ProfileModel + + Resetting to default + Återställ till standard + + + Imported profile + Importerade profil + + + This is a system provided profile + Detta är en profil systemet tillhandahåller + + + A profile change for this name is pending + En profiländring för detta namn väntar + + + (See: %1) + (Se: %1) + + + This is an invalid profile definition + Detta är en felaktig profildefinition + + + A profile already exists with this name + Det finns redan en profil med det namnet + + + A profile with this name is being deleted + En profil med detta namn raderas + + + Created from default settings + Skapad från standardinställningarna + + + system provided + tillhandahålls av systemet + + + deleted + borttagen + + + copy + noun + kopia + + + Exporting profiles while changes are pending is not allowed + Att exportera profiler medan ändringar väntar är inte tillåtet + + + No profiles found to export + Inga profiler finns att exportera + + + Can't delete profile directory + Det går inte att radera profilkatalogen + + + A profile name cannot contain the following characters: %1 + Ett profilnamn får inte innehållande följande tecken: %1 + + + A profile name cannot contain the '/' character + Ett profilnamn får inte innehålla tecknet ”/” + + + A profile cannot start or end with a period (.) + En profil kan inte börja eller sluta med en punkt (.) + + + Default + Standard + + + Global + Global + + + Personal + Personlig + + + Renamed from: %1 + Bytt namn från: %1 + + + Copied from: %1 + Kopierad ifrån: %1 + + + renamed to %1 + bytt namn till %1 + + + Profile + Profil + + + Type + Typ + + + + ProfileSortModel + + All profiles + Alla profiler + + + Personal profiles + Personliga profiler + + + Global profiles + Globala profiler + + + + ProgressFrame + + Frame + Ram + + + Loading + Läser in + + + + ProtoTree + + Packet details + Paketdetaljer + + + Not a field or protocol + Inte ett fält eller protokoll + + + No field reference available for text labels. + Ingen fältreferens finns tillgänglig för textetiketter. + + + Expand Subtrees + Expandera underträd + + + Collapse Subtrees + Fäll ihop underträd + + + Expand All + Fäll ut alla + + + Collapse All + Fäll ihop alla + + + Copy + Kopiera + + + All Visible Items + Alla synliga objekt + + + All Visible Selected Tree Items + Alla synliga valda trädobjekt + + + Description + Beskrivning + + + Field Name + Fältnamn + + + Value + Värde + + + As Filter + Som filter + + + Wiki Protocol Page + Wiki-protokollsida + + + Filter Field Reference + Filterfältreferens + + + Copied + Kopierade + + + Wiki Page for %1 + Wiki-sida för %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Wireshark-wikin underhålls av gemenskapen.</p><p>Sidan du står i begrepp att läsa in kan vara underbar, ofullständig, felaktig, eller saknas.</p><p>Gå vidare till wikin?</p> + + + Colorize with Filter + Färglägg med filter + + + + ProtocolHierarchyDialog + + Dialog + Dialog + + + Protocol + Protokoll + + + Percent Packets + Procent paket + + + Packets + Paket + + + Percent Bytes + Procent byte + + + Bytes + Byte + + + Bits/s + Bitar/s + + + End Packets + Slutpaket + + + End Bytes + Slutbyte + + + End Bits/s + Slutbit/s + + + PDUs + PDU:er + + + <small><i>A hint.</i></small> + <small><i>Ett tips.</i></small> + + + Copy as CSV + Kopiera som CSV + + + Copy stream list as CSV. + Kopiera strömlistan som CSV. + + + Copy as YAML + Kopiera som YAML + + + Copy stream list as YAML. + Kopiera strömlistan som YAML. + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + Protokollhierarkistatistik + + + Copy + Kopiera + + + as CSV + som CSV + + + as YAML + som YAML + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + Inget visningsfilter. + + + Display filter: %1 + Visningsfilter: %1 + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + Protokollinställningar + + + No protocol preferences available + Inga protokollinställningar tillgängliga + + + Disable %1 + Avaktivera %1 + + + %1 has no preferences + %1 har inga inställningar + + + Open %1 preferences… + Öppna %1 inställningar … + + + + QObject + + Average Throughput (bits/s) + Genomsnittlig genomströmning (bitar/s) + + + Round Trip Time (ms) + Rundturstid (ms) + + + Segment Length (B) + Segmentlängd (B) + + + Sequence Number (B) + Sekvensnummer (B) + + + Time (s) + Tid (s) + + + Window Size (B) + Fönsterstorlek (B) + + + [no capture file] + [ingen fångstfil] + + + Conversation + Konversation + + + Bars show the relative timeline for each conversation. + Streck visar den relativa tidslinjen för varje konversation. + + + Endpoint + Slutpunkt + + + Apply as Filter + Använd som ett filter + + + Prepare as Filter + Förbered som filter + + + Find + Sök + + + Colorize + Färglägg + + + Look Up + Slå upp + + + Copy + Kopiera + + + UNKNOWN + OKÄND + + + Selected + Vald + + + Not Selected + Ej vald + + + …and Selected + … och valda + + + …or Selected + … eller valda + + + …and not Selected + … och inte valda + + + …or not Selected + … eller inte valda + + + A + A + + + B + B + + + Any + Godtycklig + + + Don't show this message again. + Visa inte detta meddelande igen. + + + Multiple problems found + Flera problem hittades + + + %1 (%L2%) + %1 (%L2%) + + + No entries. + Inga poster. + + + %1 entries. + %1 poster. + + + Base station + Basstation + + + <Broadcast> + <Utsändning> + + + <Hidden> + <Dold> + + + BSSID + BSSID + + + Beacons + Fyrar + + + Data Pkts + Datapaket + + + Protection + Skydd + + + Address + Adress + + + Pkts Sent + Paket skickade + + + Pkts Received + Paket mottagna + + + Comment + Kommentar + + + Wrong sequence number + Fel sekvensnummer + + + Payload changed to PT=%1 + Lasten ändrades till PT=%1 + + + Incorrect timestamp + Felaktig tidsstämpel + + + Marker missing? + Saknad markör? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + Typ + + + UEId + UEId + + + UL Frames + UL-ramar + + + UL Bytes + UL-byte + + + UL MB/s + UL MB/s + + + UL Padding % + UL-utfyllnad % + + + UL Re TX + UL Re TX + + + DL Frames + DL-ramar + + + DL Bytes + DL-byte + + + DL MB/s + DL MB/s + + + DL Padding % + DL-utfyllnad % + + + DL CRC Failed + DL CRC misslyckades + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + Fördefinierad + + + Unknown (%1) + Okänd (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + Okänd + + + UE Id + UE-Id + + + Name + Namn + + + Mode + Läge + + + Priority + Prioritet + + + default + standard + + + DLT %1 + DLT %1 + + + Invalid Display Filter + Felaktigt visningsfilter + + + The filter expression %1 isn't a valid display filter. (%2). + Filteruttrycket %1 är inte ett giltigt visningsfilter. (%2). + + + Error + Fel + + + No remote interfaces found. + Inga fjärrgränssnitt hittades + + + PCAP not found + PCAP finns inte + + + Unknown error + Okänt fel + + + Default + Standard + + + Changed + Ändrad + + + Has this preference been changed? + Har denna inställning ändrats? + + + Default value is empty + Standardvärdet är tomt + + + Gap in dissection + Gap i dissektionen + + + Edit… + Redigera … + + + Browse… + Bläddra… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + Fjärrgränssnitt + + + Host: + Värd: + + + Port: + Port: + + + Authentication + Autentisering + + + Null authentication + Tom autentisering + + + Password authentication + Lösenordsautentisering + + + Username: + Användarnamn: + + + Password: + Lösenord: + + + Clear list + Töm listan + + + Error + Fel + + + No remote interfaces found. + Inga fjärrgränssnitt hittades + + + PCAP not found + PCAP finns inte + + + + RemoteSettingsDialog + + Remote Capture Settings + Fjärrfångstinställningar + + + Capture Options + Fångstalternativ + + + Do not capture own RPCAP traffic + Fånga inte egen RPCAP-trafik + + + Use UDP for data transfer + Använd UDP för dataöverföringar + + + Sampling Options + Samplingsalternativ + + + None + Inga + + + 1 of + 1 av + + + packets + paket + + + 1 every + 1 var + + + milliseconds + millisekund + + + + ResolvedAddressesDialog + + Dialog + Dialog + + + Hosts + Värdar + + + Search for entry (min 3 characters) + Sök efter post (minst 3 tecken) + + + Ports + Portar + + + Search for port or name + Sök efter port eller namn + + + Capture File Comments + Fångstfilkommentarer + + + Comment + Kommentar + + + Show the comment. + Visa kommentaren. + + + IPv4 Hash Table + IPv4-hashtabell + + + Show the IPv4 hash table entries. + Visa posterna i IPv4-hashtabellen. + + + IPv6 Hash Table + IPv6-hashtabell + + + Show the IPv6 hash table entries. + Visa posterna i IPv6-hashtabellen. + + + Show All + Visa alla + + + Show all address types. + Visa alla adresstyper. + + + Hide All + Dölj alla + + + Hide all address types. + Dölj alla adresstyper. + + + IPv4 and IPv6 Addresses (hosts) + Ipv4- och IPv6-adresser (värdar) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + Visa uppslagna IPv4- och IPv6-värdnamn i ”värd”-format. + + + Port names (services) + Portnamn (tjänster) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + Visa uppslagna portnamn i ”tjänste”-format. + + + Ethernet Addresses + Ethernet-adresser + + + Show resolved Ethernet addresses in "ethers" format. + Visa uppslagna Ethernet-adresser i ”ethers”-format. + + + Ethernet Well-Known Addresses + Välkända Ethernet-adresser + + + Show well-known Ethernet addresses in "ethers" format. + Visa välkända Ethernet-adresser i ”ethers”-format. + + + Ethernet Manufacturers + Ethernet-tillverkare + + + Show Ethernet manufacturers in "ethers" format. + Visa Ethernet-tillverkare i ”ethers”-format. + + + [no file] + [ingen fil] + + + Resolved Addresses + Uppslagna adresser + + + # Resolved addresses found in %1 + # Uppslagna adresser hittade i %1 + + + # Comments +# +# + # Kommentarer +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + %1 svarstidsfördröjningsstatistik + + + Type + Typ + + + Messages + Meddelanden + + + Min SRT + Min SRT + + + Max SRT + Max SRT + + + Avg SRT + Snitt SRT + + + Min in Frame + Min i ram + + + Max in Frame + Max i ram + + + Open Requests + Öppna begäranden + + + Discarded Responses + Kastade svar + + + Repeated Requests + Upprepade begäranden + + + Repeated Responses + Upprepade svar + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>Välj ett program och en version och fyll i ett filter om så önskas, tryck sedan Verkställ.</i></small> + + + Version: + Version: + + + Program: + Program: + + + DCE-RPC Service Response Times + DCE-RPC-serversvarstider + + + ONC-RPC Service Response Times + ONC-RPC-serversvarstider + + + + RsaKeysFrame + + RSA Keys + RSA-nycklar + + + RSA private keys are loaded from a file or PKCS #11 token. + Privata RSA-nycklar läses in från en fil eller PKCS #11-enhet. + + + Add new keyfile… + Lägg till en ny nyckelfil … + + + Add new token… + Lägg till en ny enhet … + + + Remove key + Ta bort en nyckel + + + PKCS #11 provider libraries. + PKCS #11 leverantörsbibliotek. + + + Add new provider… + Lägg till en ny leverantör … + + + Remove provider + Ta bort en leverantör + + + Add PKCS #11 token or key + Lägg till en ny PKCS #11-enhet eller nyckel + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + Inga nya PKCS #11-enheter eller nycklar hittades, överväg att lägga till en PKCS #11-leverantör. + + + Select a new PKCS #11 token or key + Välj en ny PKCS #11-enhet eller nyckel + + + PKCS #11 token or key + PKCS #11-enhet eller nyckel + + + Enter PIN or password for %1 (it will be stored unencrypted) + Ange PIN eller lösenord för %1 (det kommer lagras okrypterat) + + + Enter PIN or password for key + Ange PIN eller lösenord för nyckeln + + + Key could not be added: %1 + Nyckeln kunde inte läggas till %1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + Privat RSA-nyckel (*.pem *.p12 *.pfx *.key);;Alla filer ( + + + Select RSA private key file + Välj privat RSA-nyckelfil + + + Libraries (*.dll) + Bibliotek (*.dll) + + + Libraries (*.so) + Bibliotek (*.so) + + + Select PKCS #11 Provider Library + Välj PKCS #11-leverantörsbibliotek + + + Changes will apply after a restart + Ändringarna får effekt efter en omstart + + + PKCS #11 provider %1 will be removed after the next restart. + PKCS #11-leverantör %1 kommer tas bort efter nästa omstart. + + + + RtpAnalysisDialog + + Dialog + Dialog + + + Packet + Paket + + + Sequence + Sekvens + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter + Spridning (ms) + + + Skew + Förskjutning + + + Bandwidth + Bandbredd + + + Marker + Markör + + + Status + Status + + + Stream %1 + Ström %1 + + + Stream %1 Jitter + Ström %1 spridning + + + Stream %1 Difference + Ström %1 skillnad + + + Stream %1 Delta + Ström %1 delta + + + %1 streams, + %1 strömmar, + + + Save one stream CSV + Spara en ström-CSV + + + Save all stream's CSV + Spara alla strömmars CSV + + + &Analyze + A&nalysera + + + Open the analysis window for the selected stream(s) + Öppna analysfönstret för de valda strömmarna + + + &Set List + &Sätt lista + + + &Add to List + &Lägg till i lista + + + &Remove from List + &Ta bort från lista + + + Replace existing list in RTP Analysis Dialog with new one + Ersätt den befintliga listan i RTP-analysdialogen med en ny + + + Add new set to existing list in RTP Analysis Dialog + Lägg till en ny mängt till en befintlig lista i RTP-analysdialogen + + + Remove selected streams from list in RTP Analysis Dialog + Ta bort de valda strömmarna från listan i RTP-analysdialogen + + + Graph + Graf + + + <small><i>A hint.</i></small> + <small><i>Ett tips.</i></small> + + + &Export + &Exportera + + + Open export menu + Öppna exportmenyn + + + CSV + CSV + + + Save tables as CSV. + Spara tabeller som CSV + + + Current Tab Stream CSV + Aktuell fliks ström-CSV + + + Save the table on the current tab as CSV. + Spara tabellen för den aktuella fliken som CSV + + + All Tab Streams CSV + Alla flikars strömmars CSV + + + Save the table from all tabs as CSV. + Spara tabellen från alla flikar som CSV + + + Save Graph + Spara grafen + + + Save the graph image. + Spara grafbilden. + + + Go to Packet + Gå till paket + + + Select the corresponding packet in the packet list. + Välj det motsvarande paketet i paketlistan. + + + G + G + + + Next Problem Packet + Nästa problempaket + + + Go to the next problem packet + Gå till nästa problempaket + + + N + N + + + Prepare &Filter + Förbered &filter + + + Prepare a filter matching the selected stream(s). + Förbered ett filter som matchar de valda strömmarna. + + + &Current Tab + A&ktuell flik + + + Prepare a filter matching current tab. + Förbered ett filter som matchar den aktuella fliken + + + &All Tabs + &Alla flikar + + + Prepare a filter matching all tabs. + Förbered ett filter som matchar alla flikar + + + RTP Stream Analysis + RTP-strömanalys + + + Save Graph As… + Spara grafen som … + + + G: Go to packet, N: Next problem packet + G: gå till paket, N: nästa problempaket + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + Kommaseparerade värden (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %1 stödjer inte PCM på %2. Föredragna format är %3 + + + + RtpPlayerDialog + + RTP Player + RTP-spelare + + + Play + Spela + + + Source Address + Källadress + + + Source Port + Källport + + + Destination Address + Destinationsadress + + + Destination Port + Destinationsport + + + SSRC + SSRC + + + Setup Frame + Uppstartsram + + + Packets + Paket + + + Time Span (s) + Tidsintervall (s) + + + Payloads + Laster + + + <small><i>No audio</i></small> + <small><i>Inget ljud</i></small> + + + Start playback of all unmuted streams + Börja återuppspelning av alla icke tystade strömmar + + + Pause/unpause playback + Pausa/avpausa återuppspelningen + + + Stop playback + Stoppa återuppspelningen + + + Enable/disable skipping of silence during playback + Aktivera/avaktivera att hoppa över tystnad under återuppspelning + + + Min silence: + Min tystnad: + + + Minimum silence duration to skip in seconds + Minsta varaktighet på tystnad för att hoppa över den i sekunder + + + Output Device: + Utmatningsenhet: + + + Output Audio Rate: + Hastighet på audioutmatning: + + + Jitter Buffer: + Spridningsbuffert: + + + The simulated jitter buffer in milliseconds. + Den simulerade spridningsbufferten i millisekunder. + + + Playback Timing: + Uppspelningstiming: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Spridningsbuffert</strong>: använd en spridningsbuffert för att simulera RTP-strömmen som den hörs av slutanvändaren. +<br/> +<strong>RTP-tidsstämpel</strong>: använd RTP-tidsstämpel istället för paketets ankomsttid. Detta kommer inte upprepa RTP-strömmen som användaren hörde den, men är användbart när RTP:n tunnlas och originalpaketets tidsstämpel saknas. +<br/> +<strong>Oavbrutet läge</strong>: ignorera RTP-tidsstämpeln. Spela upp strömmen som den avslutades. Detta är användbart när RTP-tidsstämpeln saknas. + + + Jitter Buffer + Spridningsbuffert + + + RTP Timestamp + RTP-tidsstämpel + + + Uninterrupted Mode + Oavbrutet läge + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>Visa tidsstämplarna som tiden på dagen (markerat) eller sekunder sedan början av fångsten (omarkerat).</p></body></html> + + + Time of Day + Tid på dagen + + + &Export + &Exportera + + + Export audio of all unmuted selected channels or export payload of one channel. + Exportera audio av alla icke tystade valda kanaler eller exportera lasten i en kanal. + + + From &cursor + Från &markör + + + Save audio data started at the cursor + Spara audiodata som börjar vid markören + + + &Stream Synchronized Audio + &Strömma synkroniserad audio + + + Save audio data synchronized to start of the earliest stream. + Spara audiodata synkroniserad till början på en tidigaste strömmen. + + + &File Synchronized Audio + &Arkivera synkroniserad audio + + + Save audio data synchronized to start of the capture file. + Spara audiodata synkroniserad till början av fångstfilen. + + + &Payload + &Last + + + Save RTP payload of selected stream. + Spara RTP-lasten för den valda strömmen. + + + Reset Graph + Återställ grafen + + + Reset the graph to its initial state. + Återställ grafen till sitt ursprungstillstånd. + + + Go To Setup Packet + Gå till uppsättnigspaketet + + + Go to setup packet of stream currently under the cursor + Gå till uppsättningspaketet för strömmen som just nu är under markören + + + Mute + Tysta + + + Mute selected streams + Tysta de valda strömmarna + + + Unmute + Avtysta + + + Unmute selected streams + Avtysta de valda strömmarna + + + Invert muting of selected streams + Invertera tystandet av de valda strömmarna + + + Route audio to left channel of selected streams + Skicka audio till vänsterkanalen av de valda strömmarna + + + Route audio to left and right channel of selected streams + Skicka audio till vänster- och högerkanalen av de valda strömmarna + + + Route audio to right channel of selected streams + Skicka audio till högerkanalen av de valda strömmarna + + + Remove Streams + Ta bort strömmar + + + Remove selected streams from the list + Ta bort de valda strömmarna från listan + + + All + Allt + + + Select all + Välj allt + + + None + Inget + + + Clear selection + Nollställ valet + + + Invert + Invertera + + + Invert selection + Invertera valet + + + Play/Pause + Spela/pausa + + + Start playing or pause playing + Börja spela eller pausa uppspelning + + + Stop + Stopp + + + Stop playing + Sluta spela + + + I&naudible streams + &Ohörbara strömmar + + + Select/Deselect inaudible streams + Välj/välj bort ohörbara strömmar + + + Inaudible streams + Ohörbara strömmar + + + &Select + &Välj + + + Select inaudible streams + Välj ohörbara strömmar + + + &Deselect + Välj &bort + + + Deselect inaudible streams + Välj bort ohörbara strömmar + + + Prepare &Filter + Förbered &filter + + + Prepare a filter matching the selected stream(s). + Förbered ett filter som matchar de valda strömmarna. + + + R&efresh streams + &Uppdatera strömmar + + + Read captured packets from capture in progress to player + Läs fångade paket från den pågående fångsten till spelaren + + + Zoom In + Zooma in + + + SR (Hz) + SH (Hz) + + + Sample rate of codec + Samplingshastighet för omkodaren + + + PR (Hz) + UH (Hz) + + + Play rate of decoded audio (depends e. g. on selected sound card) + Spelhastighet för avkodad audio (beror t.ex. på det valda ljudkortet) + + + Zoom Out + Zooma ut + + + Move Left 10 Pixels + Flytta åt vänster 10 bildpunkter + + + Move Right 10 Pixels + Flytta åt höger 10 bildpunkter + + + Move Left 1 Pixels + Flytta åt vänster 1 bildpunkt + + + Move Right 1 Pixels + Flytta åt höger 1 bildpunkt + + + Go To Packet Under Cursor + Gå till paketet under markören + + + Go to packet currently under the cursor + Gå till paketet som just nu är under markören + + + Play the stream + Spela strömmen + + + To Left + Till vänster + + + Left + Right + Vänster + Höger + + + To Right + Till höger + + + Invert Muting + Invertera tystande + + + No devices available + Inga enheter tillgängliga + + + Select + Välj + + + Audio Routing + Audioruttläggning + + + &Play Streams + &Spela strömmar + + + Open RTP player dialog + Öppna RTP-spelardialogen + + + &Set playlist + S&ätt spellistan + + + Replace existing playlist in RTP Player with new one + Ersätt den befintliga spellistan i RTP-spelaren med en ny + + + &Add to playlist + &Lägg till till spellistan + + + Add new set to existing playlist in RTP Player + Lägg till en ny mängd till den befintliga spellistan i RTP-spelaren + + + &Remove from playlist + &Ta bort från spellistan + + + Remove selected streams from playlist in RTP Player + Ta bort de valda strömmarna från spellistan i RTP-spelaren + + + No Audio + Inget ljud + + + Decoding streams... + Avkodar strömmar … + + + Out of Sequence + Ur sekvensordning + + + Jitter Drops + Spridningsförluster + + + Wrong Timestamps + Felaktig tidsstämpel + + + Inserted Silence + Infogad tystnad + + + Double click on cell to change audio routing + Dubbelklicka på en cell för att ändra audioruttläggning + + + %1 streams + %1 strömmar + + + , %1 selected + , %1 valda + + + , %1 not muted + , %1 inte tystade + + + , start: %1. Double click on graph to set start of playback. + , start: %1. Dubbelklicka på grafen för att ange start av uppspelning. + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + , start: %1, markör: %2. Tryck ”G” för att gå till paket %3. Dubbelklicka på grafen för att ange start av uppspelning. + + + Playback of stream %1 failed! + Uppspelningen av sträm %1 misslyckades! + + + Automatic + Automatisk + + + WAV (*.wav) + WAV (*.wav) + + + Sun Audio (*.au) + Sun Audio (*.au) + + + Save audio + Spara audio + + + Raw (*.raw) + Rå (*.raw) + + + Save payload + Spara lasten + + + Warning + Varning + + + No stream selected or none of selected streams provide audio + Ingen ström vald eller ingen av de valda strömmarna tillhandahåller ljud + + + Error + Fel + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + Alla valda strömmar måste använda samma spelhastighet. En manuell inställning av utmatningshastighet för ljud kan hjälpa. + + + No streams are suitable for save + Inga strömmar passar för att spara + + + Save failed! + Att spara misslyckades! + + + Can't write header of AU file + Det går inte att skriva huvudet till AU-filen + + + Can't write header of WAV file + Det går inte att skriva huvudet till WAV-filen + + + Payload save works with just one audio stream. + Att spara lasten fungerar med bara en ljudström. + + + Double click to change audio routing + Dubbelklicka för att ändra audioruttläggning + + + Preparing to play... + Förbereder för att spela … + + + Unknown + Okänd + + + + RtpStreamDialog + + Dialog + Dialog + + + Source Address + Källadress + + + Source Port + Källport + + + Destination Address + Destinationsadress + + + Destination Port + Destinationsport + + + SSRC + SSRC + + + Start Time + Starttid + + + Duration + Varaktighet + + + Payload + Last + + + Packets + Paket + + + Lost + Förlorade + + + Max Delta (ms) + Maxdelta (ms) + + + Max Jitter + Maxspridning + + + Mean Jitter + Genomsnittsspridning + + + Status + Status + + + <small><i>A hint.</i></small> + <small><i>Ett tips.</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Visa endast konversationer som matchar det aktuella visningsfiltret</p></body></html> + + + Limit to display filter + Begränsa till visningsfilter + + + Time of Day + Tid på dagen + + + Find &Reverse + Sök &bakåt + + + Prepare &Filter + Förbered &filter + + + &Export + &Exportera + + + &Analyze + A&nalysera + + + Open the analysis window for the selected stream(s) and add it to it + Öppna analysfönstret för de valda strömmarna och lägg till till det + + + Find the reverse stream matching the selected forward stream. + Hitta den omvända strömmen som matchar den valda framåtströmmen. + + + Min Delta (ms) + Mindelta (ms) + + + Mean Delta (ms) + Genomsnittsdelta (ms) + + + Min Jitter + Minspridning + + + All forward/reverse stream actions + Alla framåt-/bakåtströmåtgärder + + + R + R + + + Find All &Pairs + Hitta alla &par + + + Select all streams which are paired in forward/reverse relation + Välj alla strömmar som hör ihop i en framåt/bakåt-relation + + + Shift+R + Skift+R + + + Find Only &Singles + Hitta endast &ensamma + + + Find all streams which don't have paired reverse stream + Hitta alla strömmar som inte hör ihop med en omvänd ström + + + Ctrl+R + Ctrl+R + + + Mark Packets + Markera paket + + + Mark the packets of the selected stream(s). + Markera paketen i den valda strömmen. + + + M + M + + + All + Allt + + + Select all + Välj allt + + + None + Inget + + + Clear selection + Nollställ valet + + + Invert + Invertera + + + Invert selection + Invertera valet + + + Go To Setup + Gå till uppsättning + + + Go to the setup packet for this stream. + Gå till uppsättningspaketet för denna ström. + + + G + G + + + Prepare a filter matching the selected stream(s). + Förbered ett filter som matchar den valda strömmen. + + + P + P + + + Export the stream payload as rtpdump + Exportera strömlasten som rtpdump + + + E + E + + + A + A + + + Cop&y + &Kopiera + + + Open copy menu + Öppna kopieringsmenyn + + + Copy as CSV + Kopiera som CSV + + + Copy stream list as CSV. + Kopiera strömlistan som CSV. + + + Copy as YAML + Kopiera som YAML + + + Copy stream list as YAML. + Kopiera strömlistan som YAML. + + + RTP Streams + RTP-strömmar + + + Select + Välj + + + as CSV + som CSV + + + as YAML + som YAML + + + %1 streams + %1 strömmar + + + , %1 selected, %2 total packets + , %1 valda, %2 paket totalt + + + Save RTPDump As… + Spara RTPDump som … + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark — SCTP-associationer + + + ID + ID + + + Port 1 + Port 1 + + + Port 2 + Port 2 + + + Number of Packets + Antal paket + + + Number of DATA Chunks + Antal DATA-stycken + + + Number of Bytes + Antal byte + + + Filter Selected Association + Filtervalsassociation + + + Analyze + Analysera + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark — analysera associationer + + + TabWidget + Flik-widget + + + Statistics + Statistik + + + Chunk Statistics + Styckesstatistik + + + Filter Association + Filterassociation + + + Number of Data Chunks from EP2 to EP1: + Antal datastycken från EP2 till EP1: + + + Checksum Type: + Kontrollsummetyp: + + + Number of Data Chunks from EP1 to EP2: + Antal datastycken från EP1 till EP2: + + + Number of Data Bytes from EP1 to EP2: + Antal databyte från EP1 till EP2: + + + Number of Data Bytes from EP2 to EP1: + Antal databyte från EP2 till EP1: + + + Endpoint 1 + Ändpunkt 1 + + + Graph TSN + Graf-TSN + + + Graph Bytes + Grafbyte + + + Requested Number of Inbound Streams: + Begärt antal av inkommande strömmar: + + + Port: + Port: + + + Sent Verification Tag: + Skickade verifikationstagg: + + + Minimum Number of Inbound Streams: + Minsta antal av inkommande strömmar: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + Fullständig lista över IP-adresser från INIT-stycket: + + + Minimum Number of Outbound Streams: + Minsta antal av utgående strömmar: + + + Graph Arwnd + Graf-Arwnd + + + Endpoint 2 + Ändpunkt 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + Fullständig lista över IP-adresser från INIT_ACK-stycket: + + + Provided Number of Outbound Streams: + Levererat antal utgående strömmar: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + SCTP-analysassociation: %1 Port1 %2 Port 2 %3 + + + No Association found for this packet. + Ingen association hittades för detta paket. + + + Warning + Varning + + + Could not find SCTP Association with id: %1 + Kunde inte hitta någon SCTP-association med id: %1 + + + Complete list of IP addresses from INIT Chunk: + Fullständig lista över IP-adresser från INIT-stycket: + + + Complete list of IP addresses from INIT_ACK Chunk: + Fullständig lista över IP-adresser från INIT_ACK-stycket: + + + List of Used IP Addresses + Lista över använda IP-adresser + + + Used Number of Inbound Streams: + Använt antal av inkommande strömmar: + + + Used Number of Outbound Streams: + Använt antal utgående strömmar: + + + + SCTPChunkStatisticsDialog + + Dialog + Dialog + + + Association + Association + + + Endpoint 1 + Ändpunkt 1 + + + Endpoint 2 + Ändpunkt 2 + + + Save Chunk Type Order + Spara styckestypsordning + + + Hide Chunk Type + Dölj styckestyp + + + Remove the chunk type from the table + Ta bort styckestypen från tabellen + + + Chunk Type Preferences + Styckestypsinställningar + + + Go to the chunk type preferences dialog to show or hide other chunk types + Gå till dialogen för styckestypsinställningar för att visa eller dölja andra styckestyper + + + Show All Registered Chunk Types + Visa alla registrerade styckestyper + + + Show all chunk types with defined names + Visa alla styckestyper med definierade namn + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + SCTP-styckesstatistik: %1 Port1 %2 Port 2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + SCTP-graf + + + Reset to full size + Återställ till full storlek + + + Save Graph + Spara grafen + + + goToPacket + gåTillPaket + + + Go to Packet + Gå till paket + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP-data och avancerade inspelningsfönster över tid: %1 Port1 %2 Port 2 %3 + + + No Data Chunks sent + Inga datastycken skickade + + + Arwnd + Arwnd + + + time [secs] + tid [s] + + + Advertised Receiver Window [Bytes] + Annonserat mottagningsfönster [byte] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>Graf %1: a_rwnd=%2 tid=%3 s </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + SCTP-graf + + + Reset to full size + Återställ till full storlek + + + Save Graph + Spara grafen + + + goToPacket + gåTillPaket + + + Go to Packet + Gå till paket + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP-data och avancerade inspelningsfönster över tid: %1 Port1 %2 Port 2 %3 + + + No Data Chunks sent + Inga datastycken skickade + + + Bytes + Byte + + + time [secs] + tid [s] + + + Received Bytes + Mottagna byte + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>Graf %1: mottagna byte=%2 tid=%3 s </i></small> + + + + SCTPGraphDialog + + SCTP Graph + SCTP-graf + + + Relative TSNs + Relativa TSN:er + + + Only SACKs + Endast SACK:er + + + Only TSNs + Endast TSN:er + + + Show both + Visa båda + + + Reset to full size + Återställ till full storlek + + + Save Graph + Spara grafen + + + goToPacket + gåTillPaket + + + Go to Packet + Gå till paket + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + SCTP TSN:er och SACK:er över tid: %1 Port1 %2 Port 2 %3 + + + No Data Chunks sent + Inga datastycken skickade + + + CumTSNAck + CumTSNAck + + + Gap Ack + Gap Ack + + + NR Gap Ack + NR Gap Ack + + + Duplicate Ack + Duplicerat Ack + + + TSN + TSN + + + time [secs] + tid [s] + + + TSNs + TSN:er + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 tid: %3 s </i></small> + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Spara grafen som … + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <small><i>Välj ett kommando och fyll i ett filter om så önskas, tryck sedan Verkställ.</i></small> + + + Command: + Kommando: + + + SCSI Service Response Times + SCSI-tjänstesvarstid + + + + SearchFrame + + Frame + Ram + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>Sök i infokolumnen i paketlistan (sammanfattningspanel), avkodade paketvisningsetiketter (trädvypanelen) eller den ASCII-konverterade paketdatan (hexvypanelen).</p></body></html> + + + Packet list + Paketlista + + + Packet details + Paketdetaljer + + + Packet bytes + Paketbyte + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Sök efter strängar som innehåller smala (UTF-8 och ASCII) eller breda (UTF-16) tecken.</p></body></html> + + + Narrow & Wide + Smal & bred + + + Narrow (UTF-8 / ASCII) + Smal (UTF-8 / ASCII) + + + Wide (UTF-16) + Bred (UTF-16) + + + Case sensitive + Skiftlägeskänsligt + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>Sök efter data med användning av visningsfiltersyntax (t.ex. ip.addr==10.1.1.1), en hexadecimal sträng (t.ex. fffffda5), en enkel sträng (t.ex. Min Sträng) eller ett reguljärt uttryck (t.ex. schyss?t).</p></body></html> + + + Display filter + Visningsfilter + + + Hex value + Hexvärde + + + String + Sträng + + + Regular Expression + Reguljärt uttryck + + + Find + Sök + + + Cancel + Avbryt + + + No valid search type selected. Please report this to the development team. + Ingen giltig söktyp vald. Rapportera gärna detta till utvecklingsgruppen. + + + Invalid filter. + Felaktigt filter. + + + That filter doesn't test anything. + Det filtret testar inte någonting. + + + That's not a valid hex string. + Det är inte en giltig hex-sträng. + + + You didn't specify any text for which to search. + Du angav ingen text att söka efter. + + + No valid character set selected. Please report this to the development team. + Ingen giltig teckenuppsättning vald. Rapportera gärna detta till utvecklingsgruppen. + + + No valid search area selected. Please report this to the development team. + Ingen giltig areatyp vald. Rapportera gärna detta till utvecklingsgruppen. + + + Searching for %1… + Sök efter %1 … + + + No packet contained those bytes. + Inget paket innehöll dessa byte. + + + No packet contained that string in its Info column. + Inget paket innehöll den strängen i sin info-kolumn. + + + No packet contained that string in its dissected display. + Inget paket innehöll den strängen i sin dissekerade visning. + + + No packet contained that string in its converted data. + Inget paket innehöll dessa byte i sina konverterade data. + + + No packet matched that filter. + Inget paket matchade det filtret. + + + + SequenceDialog + + Call Flow + Anropsflöde + + + Time + Tid + + + Comment + Kommentar + + + No data + Inga data + + + %Ln node(s) + + %Ln nod + %Ln noder + + + + %Ln item(s) + + %Ln post + %Ln poster + + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII (*.txt) + + + Save Graph As… + Spara grafen som … + + + Flow + Flöde + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Värdefulla och fantastiska tidssparande tangenbordsgenvägar</h3> +<table><tbody> + +<tr><th>+</th><td>Zooma in</td></th> +<tr><th>-</th><td>Zooma ut</td></th> +<tr><th>0</th><td>Återställ grafen till sitt ursprungliga läge</td></th> + +<tr><th>→</th><td>Flytta åt höger 10 bildpunkter</td></th> +<tr><th>←</th><td>Flytta åt vänster 10 bildpunkter</td></th> +<tr><th>↑</th><td>Flytta uppåt 10 bildpunkter</td></th> +<tr><th>↓</th><td>Flytta nedåt 10 bildpunkter</td></th> +<tr><th><i>Skift+</i>→</th><td>Flytta åt höger 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>←</th><td>Flytta åt vänster 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>↑</th><td>Flytta uppåt 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>↓</th><td>Flytta nedåt 1 bildpunkt</td></th> + +<tr><th>g</th><td>Gå till paketet under markören</td></th> +<tr><th>n</th><td>Gå till nästa paket</td></th> +<tr><th>p</th><td>Gå till föregående paket</td> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>Ett tips</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>Visa endast flöden som matchar det aktuella visningsfiltret</p></body></html> + + + Limit to display filter + Begränsa till visningsfilter + + + Flow type: + Flödestyp: + + + Addresses: + Adresser: + + + Any + Godtycklig + + + Network + Nätverk + + + Reset Diagram + Återställ diagrammet + + + Reset &Diagram + Återställ &diagrammet + + + Reset the diagram to its initial state. + Återställ diagrammet till sitt ursprungsläge + + + 0 + 0 + + + &Reset Diagram + &Återställ diagrammet + + + Reset the diagram to its initial state + Återställ diagrammet till sitt ursprungsläge + + + &Export + &Exportera + + + Export diagram + Exportdiagram + + + Zoom In + Zooma in + + + + + + + + + Zoom Out + Zooma ut + + + - + - + + + Move Up 10 Pixels + Flytta uppåt 10 bildpunkter + + + Up + Upp + + + Move Left 10 Pixels + Flytta åt vänster 10 bildpunkter + + + Left + Vänster + + + Move Right 10 Pixels + Flytta åt höger 10 bildpunkter + + + Right + Höger + + + Move Down 10 Pixels + Flytta nedåt 10 bildpunkter + + + Down + Ned + + + Move Up 1 Pixel + Flytta uppåt 1 bildpunkt + + + Shift+Up + Skift+upp + + + Move Left 1 Pixel + Flytta åt vänster 1 bildpunkt + + + Shift+Left + Skift+vänster + + + Move Right 1 Pixel + Flytta åt höger 1 bildpunkt + + + Shift+Right + Skift+höger + + + Move Down 1 Pixel + Flytta nedåt 1 bildpunkt + + + Shift+Down + Skift+ned + + + Go To Packet Under Cursor + Gå till paketet under markören + + + Go to packet currently under the cursor + Gå till paketet som just nu är under markören + + + G + G + + + All Flows + Alla flöden + + + Show flows for all packets + Visa flöden för alla paket + + + 1 + 1 + + + TCP Flows + TCP-flöden + + + Show only TCP flow information + Visa endast TCP-flödesinformation + + + Go To Next Packet + Gå till nästa paket + + + Go to the next packet + Gå till nästa paket + + + N + N + + + Go To Previous Packet + Gå till föregående paket + + + Go to the previous packet + Gå till föregående paket + + + P + P + + + Select RTP Stream + Välj RTP-ström + + + Select RTP stream in RTP Streams dialog + Välj RTP-ström i RTP-strömdialogen + + + S + S + + + Deselect RTP Stream + Välj bort RTP-ström + + + Deselect RTP stream in RTP Streams dialog + Välj bort RTP-ström i RTP-strömdialogen + + + D + D + + + + ShortcutListModel + + Shortcut + Genväg + + + Name + Namn + + + Description + Beskrivning + + + + ShowPacketBytesDialog + + Show Packet Bytes + Visa paketbyte + + + Hint. + Tips. + + + Decode as + Avkoda som + + + Show as + Visa som + + + Start + Början + + + End + Slut + + + Find: + Sök: + + + Find &Next + Sök &nästa + + + Frame %1, %2, %Ln byte(s). + + Ram %1, %2, %Ln byte. + Ram %1, %2, %Ln byte. + + + + None + Ingen + + + Base64 + Base64 + + + Compressed + Komprimerad + + + Hex Digits + Hexsiffror + + + Percent-Encoding + + + + Quoted-Printable + Quoted-Printable + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII & Styrtecken + + + C Array + C-vektor + + + EBCDIC + EBCDIC + + + Hex Dump + Hex-dump + + + HTML + HTML + + + Image + Bild + + + Raw + + + + Rust Array + Rust-vektor + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Skriv ut + + + Copy + Kopiera + + + Save as… + Spara som … + + + Save Selected Packet Bytes As… + Spara valda paketbyte som … + + + Displaying %Ln byte(s). + + Visar %Ln byte. + Visar %Ln byte. + + + + JSON + JSON + + + Regex Find: + Reguttr-sök: + + + + ShowPacketBytesTextEdit + + Show Selected + Visa valda + + + Show All + Visa alla + + + + SplashOverlay + + Initializing dissectors + Initierar dissekerare + + + Initializing tap listeners + Initierar uttagslyssnare + + + Initializing external capture plugins + Initierar externa fångstinsticksmoduler + + + Registering dissectors + Registrerar dissekerare + + + Registering plugins + Registering dissector + Registrerar insticksmoduler + + + Handing off dissectors + Överlämnar dissekerare + + + Handing off plugins + Överlämnar insticksmoduler + + + Loading Lua plugins + Läser in Lua-insticksmoduler + + + Removing Lua plugins + Tar bort Lua-insticksmoduler + + + Loading module preferences + Läser in modulinställningar + + + Finding local interfaces + Letar efter lokala gränssnitt + + + Applying changed preferences + + + + (Unknown action) + (Okänd åtgärd) + + + + StatsTreeDialog + + Configuration not found + Konfigurationen finns inte + + + Unable to find configuration for %1. + Kan inte hitta konfigurationen för %1. + + + + StripHeadersDialog + + Dialog + Dialog + + + Display filter: + Visningsfilter: + + + + SupportedProtocolsDialog + + Dialog + Dialog + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Sök i listan av fältnamn.</p></body></html> + + + Search: + Sök: + + + <small><i>Gathering protocol information…</i></small> + <small><i>Samlar protokollinformation …</i></small> + + + Supported Protocols + Protokoll som stödjs + + + %1 protocols, %2 fields. + %1 protokoll, %2 fält. + + + + SupportedProtocolsModel + + Name + Namn + + + Filter + Filter + + + Type + Typ + + + Description + Beskrivning + + + + SyntaxLineEdit + + Invalid filter: %1 + + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + ”%1” bör undvikas till förmån för ”%2”. Se hjälpen avsnitt 6.4.8 för detaljer. + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + Dialog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Värdefulla och fantastiska tidsbesparande tangentbordsgenvägar</h3> +<table><tbody> + +<tr><th>+</th><td>Zooma in</td></th> +<tr><th>-</th><td>Zooma ut</td></th> +<tr><th>x</th><td>Zooma in X-axeln</td></th> +<tr><th>X</th><td>Zooma ut X axeln</td></th> +<tr><th>y</th><td>Zooma in Y-axeln</td></th> +<tr><th>Y</th><td>Zooma ut Y-axeln</td></th> +<tr><th>0</th><td>Återställ grafen till sitt ursprungstillstånd</td></th> + +<tr><th>→</th><td>Flytta åt höger 10 bildpunkter</td></th> +<tr><th>←</th><td>Flytta åt vänster 10 bildpunkter</td></th> +<tr><th>↑</th><td>Flytta uppåt 10 bildpunkter</td></th> +<tr><th>↓</th><td>Flytta nedåt 10 bildpunkter</td></th> +<tr><th><i>Skift+</i>→</th><td>Flytta åt höger 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>←</th><td>Flytta åt vänster 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>↑</th><td>Flytta uppåt 1 bildpunkt</td></th> +<tr><th><i>Skift+</i>↓</th><td>Flytta nedåt 1 bildpunkt</td></th> + +<tr><th><i>Pg Up</i></th><td>Nästa ström</td></th> +<tr><th><i>Pg Dn</i></th><td>Föregående ström</td></th> +<tr><th>d</th><td>Byt riktning (växla TCP-ändpunkter)</td></th> +<tr><th>g</th><td>Gå till paketet under markören</td></th> + +<tr><th>z</th><td>Växla mellan att musen drar/zoomar</td></th> +<tr><th>s</th><td>Växla relativa/absoluta sekvensnummer</td></th> +<tr><th>t</th><td>Växla fångst-/sessionstidsstart</td></th> +<tr><th>Mellanslag</th><td>Byt hårkors</td></th> + +<tr><th>1</th><td>Rundturstidsgraf</td></th> +<tr><th>2</th><td>Genomströmningsgraf</td></th> +<tr><th>3</th><td>Tid/sekvensgraf i Stevens stil</td></th> +<tr><th>4</th><td>Tid/sekvensgraf i tcptrace stil</td></th> +<tr><th>5</th><td>Fönsterskalningsgraf</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>Håll musen över för genvägar</i></small> + + + Type + Typ + + + MA Window (s) + MA-fönster (s) + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + Tillåt både SACK-segment och datapaket att väljas genom att klicka på grafen + + + Select SACKs + select SACKs + Valda SACK:er + + + Stream + Ström + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Byt riktning på förbindelsen (visa det omvända flödet).</p></body></html> + + + Switch Direction + Byt riktning + + + Mouse + Mus + + + Drag using the mouse button. + Dra genom att använda musknappen. + + + drags + dragningar + + + Select using the mouse button. + Välj genom att använda musknappen. + + + zooms + zoomningar + + + Display Round Trip Time vs Sequence Number + Visa rundturstider mot sekvensnummer + + + RTT By Sequence Number + RTT efter sekvensnummer + + + Display graph of Segment Length vs Time + Visa en graf över segmentlängd mot tid + + + Segment Length + Segmentlängd + + + Display graph of Mean Transmitted Bytes vs Time + Visa en graf över skickade byte i snitt mot tid + + + Display graph of Mean ACKed Bytes vs Time + Visa en graf över kvitterade byte i snitt mot tid + + + Goodput + Dataflöde + + + Display graph of Receive Window Size vs Time + Visa en graf över mottagningsfönsterstorlek mot tid + + + Rcv Win + Mot.fnst. + + + Display graph of Outstanding Bytes vs Time + Visa en graf över utestående byte mot tid + + + Bytes Out + Byte ute + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Återställ grafen till sitt ursprungstillstånd.</p></body></html> + + + Reset + Återställ + + + Reset Graph + Återställ grafen + + + Reset the graph to its initial state. + Återställ grafen till sitt ursprungstillstånd. + + + 0 + 0 + + + Zoom In + Zooma in + + + + + + + + + Zoom Out + Zooma ut + + + - + - + + + Move Up 10 Pixels + Flytta uppåt 10 bildpunkter + + + Up + Upp + + + Move Left 10 Pixels + Flytta åt vänster 10 bildpunkter + + + Left + Vänster + + + Move Right 10 Pixels + Flytta åt höger 10 bildpunkter + + + Right + Höger + + + Move Down 10 Pixels + Flytta nedåt 10 bildpunkter + + + Down + Ned + + + Move Up 1 Pixel + Flytta uppåt 1 bildpunkt + + + Shift+Up + Skift+upp + + + Move Left 1 Pixel + Flytta åt vänster 1 bildpunkt + + + Shift+Left + Skift+vänster + + + Move Right 1 Pixel + Flytta åt höger 1 bildpunkt + + + Shift+Right + Skift+höger + + + Move Down 1 Pixel + Flytta nedåt 1 bildpunkt + + + Shift+Down + Skift+ned + + + Next Stream + Nästa ström + + + Go to the next stream in the capture + Gå till nästa ström i fångsten + + + PgUp + Sida upp + + + Previous Stream + Föregående ström + + + Go to the previous stream in the capture + Gå till föregående ström i fångsten + + + PgDown + Sida ned + + + Switch direction (swap TCP endpoints) + Byt riktning (växla TCP-ändpunkter) + + + D + D + + + Go To Packet Under Cursor + Gå till paketet under markören + + + Go to packet currently under the cursor + Gå till paketet som just nu är under markören + + + G + G + + + Drag / Zoom + Dra/zooma + + + Toggle mouse drag / zoom behavior + Växla musbeteende mellan dra/zooma + + + Z + Z + + + Relative / Absolute Sequence Numbers + Relativa/absoluta sekvensnummer + + + Toggle relative / absolute sequence numbers + Byt mellan relativa/absoluta sekvensnummer + + + S + S + + + Capture / Session Time Origin + Ursprungstid för fångst/session + + + Toggle capture / session time origin + Växla mellan fångst/session som ursprungstid + + + T + T + + + Crosshairs + Hårkors + + + Toggle crosshairs + Byt hårkors + + + Space + Mellanslag + + + Round Trip Time + Rundturstid + + + Switch to the Round Trip Time graph + Byt till grafen för rundturstid + + + 1 + 1 + + + Throughput + Genomströmning + + + Switch to the Throughput graph + Byt till grafen för genomströmning + + + 2 + 2 + + + Time / Sequence (Stevens) + Tid/sekvens (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + Byt till grafen för tid/sekvens i Stevens stil + + + 3 + 3 + + + Window Scaling + Fönsterskalning + + + Switch to the Window Scaling graph + Byt till grafen för fönsterskalning + + + 5 + 5 + + + Time / Sequence (tcptrace) + Tid/sekvens (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + Byt till grafen för tid/sekvens i tcptraces stil + + + 4 + 4 + + + Zoom In X Axis + Zooma in X-axeln + + + X + X + + + Zoom Out X Axis + Zooma ut X-axeln + + + Shift+X + Skift+X + + + Zoom In Y Axis + Zooma in Y-axeln + + + Y + Y + + + Zoom Out Y Axis + Zooma ut Y-axeln + + + Shift+Y + Skift+Y + + + Save As… + Spara som … + + + No Capture Data + Ingen fångstdata + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 %2 pkt, %3 %4 %5 pkt, %6 + + + Sequence Numbers (Stevens) + Sekvensnummer (Stevens) + + + Sequence Numbers (tcptrace) + Sekvensnummer (tcptrace) + + + (MA) + (GM) + + + (%1 Segment MA) + (%1-segments GM) + + + [not enough data] + [inte tillräckligt med data] + + + for %1:%2 %3 %4:%5 + för %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3s lng %4 skv %5 bkr %6 fön %7) + + + Click to select packet + Klicka för att välja ett paket + + + Packet + Paket + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Släpp för att zooma, x = %1 till %2, y = %3 till %4 + + + Unable to select range. + Kan inte välja intervallet. + + + Click to select a portion of the graph. + Klicka för att välja en del av grafen. + + + Portable Document Format (*.pdf) + Portable Document Format (*.pdf) + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG File Interchange Format (*.jpeg *.jpg) + + + Save Graph As… + Spara grafen som … + + + + TLSKeylogDialog + + Dialog + Dialog + + + Browse… + + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + Spara + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + Dialog + + + Item + Artikel + + + <small><i>A hint.</i></small> + <small><i>Ett tips.</i></small> + + + Display filter: + Visningsfilter: + + + Regenerate statistics using this display filter + Generera om statistiken med detta visningsfilter + + + Apply + Verkställ + + + Copy + Kopiera + + + Copy a text representation of the tree to the clipboard + Kopiera en textrepresentation av trädet till urklipp. + + + Save as… + Save as... + Spara som … + + + Save the displayed data in various formats + Spara den visade datan i diverse format + + + Collapse All + Fäll ihop alla + + + Expand All + Fäll ut alla + + + Save Statistics As… + Spara statistik som … + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + Vanlig textfil (*.txt);;Kommaseparerade värden (*.csv);;XML-dokument (*.xml);;YAML-dokument (*.yaml) + + + Plain text file (*.txt) + Vanlig textfil (*.txt) + + + Error saving file %1 + Fel när filen %1 sparades + + + + TimeShiftDialog + + Shift all packets by + Skifta alla paket med + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + Sätt tiden för paketet + + + to + till + + + …then set packet + ...then set packet + … sätt sedan paketet + + + and extrapolate the time for all other packets + och extrapolera tiden för alla andra paket + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[ÅÅÅÅ-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + Ta bort alla skiften + + + Time Shift + Tidsförskjutning + + + Frame numbers must be between 1 and %1. + Ramnummer måste vara mellan 1 och %1. + + + Invalid frame number. + Felaktigt ramnummer. + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + Kartfilsfel + + + Could not open base file %1 for reading: %2 + Kunde inte öppna basfien %1 för läsning: %2 + + + No endpoints available to map + Inga ändpunkter tillgängliga att kartlägga + + + Unable to create temporary file + Kan inte skapa en temporärfil + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>Visa uppslagna adresser och portnamn istället för enkla värden. Motsvarande namnuppslagningsinställning måste vara aktiverad.</p></body></html> + + + Name resolution + Namnuppslagning + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Visa endast konversationer som matchar det aktuella visningsfiltret</p></body></html> + + + Limit to display filter + Begränsa till visningsfilter + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + <html><head/><body><p>Visa endast typer som matchar filtervärdet</p></body></html> + + + Filter list for specific type + Filterlista för specifik typ + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>Visa absoluta tider i starttidskolumen.</p></body></html> + + + GroupBox + Gruppruta + + + Absolute start time + Absolut starttid + + + Copy + Kopiera + + + Unknown + Okänd + + + + TrafficTree + + Resize all columns to content + Ändra storleken på alla kolumner för att passa med innehållet + + + Filter on stream id + Filtrera på ström-id + + + Copy %1 table + Kopiera tabellen %1 + + + as CSV + som CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + Kopiera alla värden på denna sida till urklipp i CSV-format (kommaseparerade värden). + + + as YAML + som YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + Kopiera alla värden på denna sida till urklipp i dataserialiseringsformatet YAML. + + + as JSON + som JSON + + + Copy all values of this page to the clipboard in the JSON data serialization format. + Kopiera alla värden på denna sida till urklipp i dataserialiseringsformatet JSON. + + + Save data as raw + Spara data som rått + + + Disable data formatting for export/clipboard and save as raw data + Avaktivera dataformatering vid export/urklipp och spara som råa data + + + + TrafficTreeHeaderView + + Less than + Mindre än + + + Greater than + Större än + + + Equal + Lika med + + + Columns to display + Kolumner att visa + + + Filter %1 by + Filtrera %1 enligt + + + Enter filter value + Ange filtervärde + + + + TrafficTypesModel + + Protocol + Protokoll + + + + UatDialog + + Create a new entry. + Skapa en ny post. + + + Remove this entry. + Remove this profile. + Ta bort denna post. + + + Copy this entry. + Copy this profile. + Kopiera denna post. + + + Move entry up. + Flytta upp posten. + + + Move entry down. + Flytta ned posten. + + + Clear all entries. + Nollställ alla poster. + + + Unknown User Accessible Table + Okänd användaråtkomlig tabell + + + Open + Öppna + + + + UatFrame + + Frame + Ram + + + Create a new entry. + Skapa en ny post. + + + Remove this entry. + Ta bort denna post. + + + Copy this entry. + Kopiera denna post. + + + Move entry up. + Flytta upp posten. + + + Move entry down. + Flytta ned posten. + + + Clear all entries. + Nollställ alla poster. + + + Copy entries from another profile. + Kopiera poster från en annan profil. + + + Copy from + Kopiera ifrån + + + Unknown User Accessible Table + Okänd användaråtkomlig tabell + + + Open + Öppna + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Visa endast konversationer som matchar det aktuella visningsfiltret</p></body></html> + + + Limit to display filter + Begränsa till visningsfilter + + + Time of Day + Tid på dagen + + + Flow &Sequence + Flödes&sekvens + + + Show flow sequence for selected call(s). + Visa flödessekvens för de valda anropen. + + + Prepare &Filter + Förbered &filter + + + Prepare a filter matching the selected calls(s). + Förbered ett filter som matchar de valda anropen. + + + Cop&y + &Kopiera + + + Open copy menu + Öppna kopieringsmenyn + + + All + Allt + + + Select all + Välj allt + + + None + Inget + + + Invert + Invertera + + + Invert selection + Invertera valet + + + Select related RTP streams + Välj relaterade RTP-strömmar + + + Select RTP streams related to selected calls in RTP Streams dialog + Välj RTP-strömmar relaterade till de valda anropen i RTP-strömdialogen + + + S + V + + + Deselect related RTP Streams + Välj bort relaterade RTP-strömmar + + + D + B + + + Clear selection + Nollställ valet + + + Display time as time of day + Visa tiden som tiden på dagen + + + Copy as CSV + Kopiera som CSV + + + Copy stream list as CSV. + Kopiera strömlistan som CSV. + + + Copy as YAML + Kopiera som YAML + + + Copy stream list as YAML. + Kopiera strömlistan som YAML. + + + SIP Flows + SIP-flöden + + + VoIP Calls + VoIP-anrop + + + as CSV + som CSV + + + as YAML + som YAML + + + Select + Välj + + + + VoipCallsInfoModel + + On + + + + Off + Av + + + Tunneling: %1 Fast Start: %2 + Tunnlar: %1 Snabbstart: %2 + + + Start Time + Starttid + + + Stop Time + Sluttid + + + Initial Speaker + Ursprunglig talare + + + From + Från + + + To + Till + + + Protocol + Protokoll + + + Duration + Varaktighet + + + Packets + Paket + + + State + Tillstånd + + + Comments + Kommentarer + + + + WelcomePage + + Form + Formulär + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Välkommen till Wireshark</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>Öppna en fil i ditt filsystem</p></body></html> + + + <h2>Open</h2> + <h2>Öppna</h2> + + + Recent capture files + Senaste fångstfilerna + + + Capture files that have been opened previously + Fångstfiler som har öppnats förut + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>Fånga aktiva paket på ditt nätverk.</p></body></html> + + + <h2>Capture</h2> + <h2>Fånga</h2> + + + …using this filter: + … med användning av detta filter: + + + Interface list + Gränssnittslista + + + List of available capture interfaces + Lista över tillgängliga fångstgränssnitt + + + <h2>Learn</h2> + <h2>Lär dig</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + + + + Show in Finder + Visa i sökaren + + + Show in Folder + Visa i mappen + + + Welcome to %1 + Välkommen till %1 + + + All interfaces shown + Alla gränssnitt visade + + + %n interface(s) shown, %1 hidden + + %n gränssnitt visat, %1 dolda + %n gränssnitt visade, %1 dolda + + + + You are sniffing the glue that holds the Internet together using Wireshark + Du luktar på limmet som håller samman Internet med Wireshark + + + You are running Wireshark + Du kör Wireshark + + + You receive automatic updates. + Du får automatiska uppdateringar. + + + You have disabled automatic updates. + Du har avaktiverat automatiska uppdateringar. + + + not found + finns inte + + + Copy file path + Kopiera filsökvägen + + + Remove from list + Ta bort från listan + + + + WirelessFrame + + Frame + Ram + + + Interface + Gränssnitt + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>Ange 802.11-kanal.</p></body></html> + + + Channel + Kanal + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>Vid fångst, visa alla ramar, de som har en giltig ramkontrollsekvens (FCS) eller de med en ogiltig FCS.</p></body></html> + + + FCS Filter + FCS-filter + + + All Frames + Alla ramar + + + Valid Frames + Giltiga ramar + + + Invalid Frames + Ogiltiga ramar + + + Wireless controls are not supported in this version of Wireshark. + Styrning av trådlöst stödjs inte i denna version av Wireshark. + + + External Helper + Externt hjälpprogram + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>Visa IEEE 802.11-inställningarna, inklusive dekrypteringsnycklar.</p></body></html> + + + 802.11 Preferences + 802.11-inställningar + + + AirPcap Control Panel + AirPcap-instrumentpanel + + + Open the AirPcap Control Panel + Öppna AirPcap-instrumentpanelen + + + Unable to set channel or offset. + Kan inte sätta kanalen eller förskjutningen. + + + Unable to set FCS validation behavior. + Kan inte sätta FCS-valideringsbeteende. + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + Paket nummer %1 inkluderar inte en TSF-tidsstämpel, visar inte tidsskalan. + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + Paket nummer %u har stora negativa hopp i TSF, visar inte tidsskalan. Kanske TSF-referenspunkten är felaktigt satt? + + + + WiresharkDialog + + Failed to attach to tap "%1" + Misslyckades att koppla till uttaget ”%1” + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Gå till paket + + + Cancel + Avbryt + + + File Set + Filuppsättning + + + Export Packet Dissections + Exportera paket dissekeringar + + + Export Objects + Exportera objekt + + + &Zoom + &Zooma + + + &Time Display Format + &Tidsvisningsformat + + + Copy + Kopiera + + + Manual pages + Manualsidor + + + Apply as Filter + Använd som ett filter + + + Prepare as Filter + Förbered som filter + + + SCTP + SCTP + + + TCP Stream Graphs + TCP-strömgrafer + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &Arkiv + + + &Capture + &Fånga + + + &Help + &Hjälp + + + &Go + &Kör + + + &View + &Visa + + + &Analyze + A&nalysera + + + Follow + Följ + + + &Statistics + &Statistik + + + 29West + 29West + + + Topics + Ämnen + + + Queues + Köer + + + UIM + UIM + + + Telephon&y + Telefon&i + + + RTSP + RTSP + + + &Edit + &Redigera + + + Packet Comments + Paketkommentarer + + + Main Toolbar + Huvudverktygsrad + + + Display Filter Toolbar + Visningsfilterverktygsrad + + + Open a capture file + Öppna en fångstfil + + + Quit Wireshark + Avsluta Wireshark + + + &Start + &Starta + + + Start capturing packets + Börja fånga paket + + + S&top + St&opp + + + Stop capturing packets + Sluta fånga paket + + + No files found + Inga filer hittades + + + &Contents + &Innehåll + + + Wireshark Filter + Wireshark-filter + + + TShark + TShark + + + Rawshark + Rawshark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + Text2pcap + + + Website + Webbsida + + + Downloads + Hämtningar + + + Wiki + Wiki + + + Sample Captures + Exempel på fångster + + + &About Wireshark + &Om Wireshark + + + Ask (Q&&A) + Fråga (F&&S) + + + Next Packet + Nästa paket + + + Go to the next packet + Gå till nästa paket + + + Previous Packet + Föregående paket + + + Go to the previous packet + Gå till föregående paket + + + First Packet + Första paket + + + Go to the first packet + Gå till det första paketet + + + Last Packet + Sista paket + + + Go to the last packet + Gå till det sista paketet + + + E&xpand Subtrees + E&xpandera underträd + + + Expand the current packet detail + Expandera det aktuella paketets detaljer + + + &Expand All + &Expandera alla + + + Expand packet details + Expandera paketdetaljer + + + Collapse &All + Fäll ihop &alla + + + Collapse all packet details + Fäll ihop alla paketdetaljer + + + Go to specified packet + Gå till det angivna paketet + + + Merge one or more files + Slå samman en eller fler filer + + + Import a file + Importera en fil + + + &Save + &Spara + + + Save as a different file + Spara som en annan fil + + + Export specified packets + Exportera angivna paket + + + Export TLS Session Keys… + Exportera TLS-sessionsnycklar … + + + List Files + Lista filer + + + Next File + Nästa fil + + + Previous File + Föregående fil + + + &Reload + L&äs om + + + Options + Alternativ + + + Capture options + Fångstalternativ + + + Capture filters + Fångstfilter + + + Refresh Interfaces + Uppdatera gränssnitten + + + Refresh interfaces + Uppdatera gränssnitten + + + &Restart + Sta&rta om + + + Restart current capture + Starta om den aktuella fångsten + + + As &CSV… + Som &CSV … + + + As "C" &Arrays… + Som ”C”-&vektorer … + + + As P&SML XML… + Som P&SML XML … + + + As P&DML XML… + Som P&DML XML … + + + As &JSON… + Som &JSON … + + + Description + Beskrivning + + + Field Name + Fältnamn + + + Value + Värde + + + As Filter + Som filter + + + Close this capture file + Stäng denna fångstfil + + + Packet: + Paket: + + + Interface Toolbars + Gränssnittsverktygsrader + + + Colorize Conversation + Färglägg konversationen + + + Internals + Internt + + + Additional Toolbars + Ytterligare verktygsrader + + + Conversation Filter + Konversationsfilter + + + Reliable Server Pooling (RSerPool) + Pålitlig server-poolning (RSerPool) + + + SOME/IP + SOME/IP + + + &DTN + + + + Osmux + Osmux + + + &Tools + Tools + &Verktyg + + + Wireless Toolbar + Verktygsrad för trådlöst + + + Help contents + Hjälpinnehåll + + + FAQs + Frågor och svar + + + Next Packet in Conversation + Nästa paket i konversationen + + + Go to the next packet in this conversation + Gå till nästa paket i denna konversation + + + Previous Packet in Conversation + Föregående paket i konversationen + + + Go to the previous packet in this conversation + Gå till föregående paket i denna konversation + + + Next Packet In History + Nästa paket i historiken + + + Go to the next packet in your selection history + Gå till nästa paket i din valhistorik + + + Previous Packet In History + Föregående paket i historien + + + Go to the previous packet in your selection history + Gå till föregående paket i din valhistorik + + + Collapse Subtrees + Fäll ihop underträd + + + Collapse the current packet detail + Fäll ihop de aktuella paketdetaljerna + + + Go to Packet… + Gå till paket … + + + &Merge… + &Slå samman … + + + &Import from Hex Dump… + &Importera ifrån hexdump … + + + Save this capture file + Spara denna fångstfil + + + Save &As… + Spara s&om … + + + Export Specified Packets… + Exportera angivna paket … + + + Export Packet &Bytes… + Exportera paket&byte … + + + &Print… + &Skriv ut … + + + Reload this file + Läs om denna fil + + + Reload as File Format/Capture + Läs om som filformat/-fångst + + + Copy this item's description + Kopiera detta objekts beskrivning + + + Copy this item's field name + Kopiera detta objekts fältnamn + + + Copy this item's value + Kopiera detta objekts värde + + + Copy this item as a display filter + Kopiera detta objekt som ett visningsfilter + + + Apply as Column + Använd som kolumn + + + Create a packet list column from the selected field. + Skapa en paketlistekolumn från det valda fältet. + + + Find a packet + Sök efter ett paket + + + Find the next packet + Sök efter nästa paket + + + Find the previous packet + Sök efter föregående paket + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + &Markera/avmarkera paket + + + Mark All Displayed + Markera alla visade + + + Mark all displayed packets + Markera alla visade paket + + + Unmark all displayed packets + Avmarkera alla visade paket + + + Next Mark + Nästa märke + + + Go to the next marked packet + Gå till nästa märkta paket + + + Previous Mark + Föregående märke + + + Go to the previous marked packet + Gå till föregående märkta paket + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + &Ignorera/avignorera paket + + + Ignore All Displayed + Ignorera alla visade + + + Ignore all displayed packets + Ignorera alla visade paket + + + Set/Unset Time Reference + Ange/ta bort tidsreferens + + + Set or unset a time reference for this packet + Ange eller ta bort en tidsreferens för detta paket + + + Unset All Time References + Ta bort alla tidsreferenser + + + Remove all time references + Ta bort alla tidsreferenser + + + Next Time Reference + Nästa tidsreferens + + + Go to the next time reference + Gå till nästa tidsreferens + + + Previous Time Reference + Föregående tidsreferens + + + Go to the previous time reference + Gå till föregående tidsreferens + + + Shift or change packet timestamps + Skifta eller ändra pakettidsstämplar + + + Delete All Packet Comments + Ta bort alla paketkommentarer + + + Remove all packet comments in the capture file + Ta bort alla paketkommentarer i fångstfilen + + + &Configuration Profiles… + &Konfigurationsprofiler … + + + Configuration profiles + Konfigurationsprofiler + + + Manage your configuration profiles + Hantera dina konfigurationsprofiler + + + Manage Wireshark's preferences + Hantera Wiresharks inställningar + + + Capture File Properties + Fångstfilegenskaper + + + Capture file properties + Fångstfilegenskaper + + + &Protocol Hierarchy + &Protokollhierarki + + + Show a summary of protocols present in the capture file. + Visa en sammanfattning av protokoll som finns i fångstfilen. + + + Capinfos + Capinfos + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + Tidssekvenser (Stevens) + + + TCP time sequence graph (Stevens) + TCP-tidssekvensgraf (Stevens) + + + Throughput + Genomströmning + + + Round Trip Time + Rundturstid + + + TCP round trip time + TCP-rundturstid + + + Window Scaling + Fönsterskalning + + + TCP window scaling + TCP-fönsterskalning + + + HTTP/2 Stream + HTTP/2-ström + + + SIP Call + SIP-anrop + + + Time Sequence (tcptrace) + Tidssekvens (tcptrace) + + + TCP time sequence graph (tcptrace) + TCP-tidssekvensgraf (tcptrace) + + + Analyse this Association + Analysera denna association + + + Show All Associations + Visa alla associationer + + + Flow Graph + Flödesgraf + + + Flow sequence diagram + Flödessekvensdiagram + + + ANCP + ANCP + + + ANCP statistics + ANCP-statistik + + + Packets sorted by Instance ID + Paket sorterade efter instans-ID + + + BACapp statistics sorted by instance ID + BACapp-statistik sorterad efter instans-ID + + + Packets sorted by IP + Paket sorterade efter IP + + + BACapp statistics sorted by IP + BACapp-statistik sorterad efter IP + + + Packets sorted by object type + Paket sorterade efter objekttyp + + + BACapp statistics sorted by object type + BACapp-statistik sorterad efter objekttyp + + + Packets sorted by service + Paket sorterade efter tjänst + + + BACapp statistics sorted by service + BACapp-statistik sorterad efter tjänst + + + Collectd + Collectd + + + Collectd statistics + Collectd-statistik + + + DNS + DNS + + + DNS statistics + DNS-statistik + + + HART-IP + HART-IP + + + HART-IP statistics + HART-IP-statistik + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + hpfeeds-statistik + + + HTTP2 + HTTP2 + + + HTTP2 statistics + HTTP2-statistik + + + Packet Counter + Paketräknare + + + HTTP packet counter + HTTP-paketräknare + + + Requests + Begäranden + + + HTTP requests + HTTP-begäranden + + + Load Distribution + Lastfördelning + + + HTTP load distribution + HTTP-lastfördelning + + + Packet Lengths + Paketlängder + + + Packet length statistics + Paketlängdsstatistik + + + Sametime + Sametime + + + Sametime statistics + Sametime-statistik + + + SOME/IP Messages + SOME/IP-meddelanden + + + SOME/IP Message statistics + SOME/IP-meddelandestatistik + + + SOME/IP-SD Entries + SOME/IP-SD-poster + + + SOME/IP-SD Entries statistics + SOME/IP-SD-poststatistik + + + &LTP + + + + LTP segment and block statistics + + + + &ISUP Messages + &ISUP-meddelanden + + + ISUP message statistics + ISUP-meddelandestatistik + + + Osmux packet counts + Osmux-paketräknare + + + RTSP packet counts + RTSP-paketräknare + + + SM&PP Operations + SM&PP-åtgärder + + + SMPP operation statistics + SMPP-åtgärdsstatistik + + + &UCP Messages + &UCP-meddelanden + + + UCP message statistics + UCP-meddelandestatistik + + + F1AP + F1AP + + + F1AP Messages + F1AP-meddelanden + + + NGAP + NGAP + + + NGAP Messages + NGAP-meddelanden + + + Change the way packets are dissected + Ändra det sätt på vilket paket dissekeras + + + Reload Lua Plugins + Läs om Lua-insticksmoduler + + + Reload Lua plugins + Läs om Lua-insticksmoduler + + + Advertisements by Topic + Annonser efter ämne + + + Advertisements by Source + Annonser efter källa + + + Advertisements by Transport + Annonser efter transport + + + Queries by Topic + Frågor efter ämne + + + Queries by Receiver + Frågor efter mottagare + + + Wildcard Queries by Pattern + Jokerfrågor efter mönster + + + Wildcard Queries by Receiver + Jokerfrågor efter mottagare + + + Advertisements by Queue + Annonser efter kö + + + Queries by Queue + Frågor efter kö + + + Streams + Strömmar + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + Filtrera denna association + + + Strip Headers… + Ta bort huvuden … + + + Strip headers and export higher level encapsulations to file + Ta bort huvuden och exportera högnivåinkapsling till en fil + + + &I/O Graphs + &I/O-grafer + + + &Conversations + &Konversationer + + + &Endpoints + &Ändpunkter + + + Shrink the main window text + Krymp huvudfönstrets text + + + Return the main window text to its normal size + Återställ huvudfönstret till sin normalstorlek + + + Reset Layout + Återställ layouten + + + Reset appearance layout to default size + Återställ utseendelayouten till sin standardstorlek + + + Seconds Since First Captured Packet + Sekunder sedan första fångade paket + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + Paket&diagram + + + Show or hide the packet diagram + Visa eller dölj paketduagrannet + + + Show each conversation hash table + Visa varje konversations hashtabell + + + Show each dissector table and its entries + Visa varje dissekeringstabell och dess poster + + + Show the currently supported protocols and display filter fields + Visa de protokoll och visningsfilterfält som stödjs + + + MAC Statistics + MAC-statistik + + + LTE MAC statistics + LTE MAC-statistik + + + RLC Statistics + RLC-statistik + + + LTE RLC statistics + LTE RLC-statistik + + + LTE RLC graph + LTE RLC-graf + + + MTP3 Summary + MTP3-sammanfattning + + + MTP3 summary statistics + MTP3 sammanfattande statistik + + + Bluetooth Devices + Blåtandsenheter + + + Bluetooth HCI Summary + Blåtands HCI-sammanfattning + + + Display Filter &Expression… + Visningsfilter&uttryck … + + + Display Filter Expression… + Visningsfilteruttryck … + + + REGISTER_STAT_GROUP_RSERPOOL + REGISTER_STAT_GROUP_RSERPOOL + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + Start på "REGISTER_STAT_GROUP_RSERPOOL" + + + No GSM statistics registered + Ingen GSM-statistik registrerad + + + No LTE statistics registered + Ingen LTE-statistik registrerad + + + No MTP3 statistics registered + Ingen MTP3-statistik registrerad + + + IAX2 Stream Analysis + IAX2-strömanalys + + + Show Packet Bytes… + Visa paketbyte … + + + Go to &Linked Packet + Gå till &länkade paket + + + UDP Multicast Streams + UDP multicast-strömmar + + + Show UTP multicast stream statistics. + Visa statistik över UTP-multicast-strömmar. + + + WLAN Traffic + WLAN-trafik + + + Show IEEE 802.11 wireless LAN statistics. + Visa statistik över IEEE 802.11 trådlöst LAN. + + + Add a display filter button. + Lägg till en visningsfilterknapp. + + + Firewall ACL Rules + Brandväggens ACL-regler + + + Create firewall ACL rules + Skapa ACL-regler för brandvägg + + + &Full Screen + &Helskärm + + + Credentials + Kreditiv + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + &Alternativ … + + + &Wireless + &Trådlöst + + + Capture &Filters… + &Fångstfilter … + + + As Plain &Text… + Som vanlig &text … + + + As Plain &Text + Som vanlig &text + + + As &CSV + Som &CSV + + + As &YAML + Som &YAML + + + All Visible Items + Alla synliga objekt + + + All Visible Selected Tree Items + Alla synliga valda trädobjekt + + + Display Filter &Macros… + Visningsfilter&makron … + + + &Find Packet… + &Sök paket … + + + Find Ne&xt + Sök n&ästa + + + Find Pre&vious + Sök f&öregående + + + Mark or unmark each selected packet + Markera eller avmarkera varje valt paket + + + Ignore or unignore each selected packet + Ignorera eller avignorera varje valt paket + + + U&nignore All Displayed + Avig&norera alla visade + + + Unignore all displayed packets + Avignorera alla visade paket + + + Time Shift… + Tidsförskjutning … + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + &Inställningar … + + + TCP throughput + TCP-genomströmning + + + Request Sequences + Begärandesekvenser + + + HTTP Request Sequences + HTTP-begärandesekvenser + + + Decode &As… + Avkoda &som … + + + Export PDUs to File… + Exportera PDU:er till fil … + + + Create graphs based on display filter fields + Skapa grafer baserat på visningsfilterfält + + + &Main Toolbar + &Huvudverktygsrad + + + Show or hide the main toolbar + Visa eller dölj huvudverktygsraden + + + &Filter Toolbar + &Filterverktygsrad + + + Show or hide the display filter toolbar + Visa eller dölj filterverktygsraden + + + Conversations at different protocol levels + Konversationer på olika protokollnivåer + + + Endpoints at different protocol levels + Ändpunkter på olika protokollnivåer + + + Colorize Packet List + Färglägg paketlistan + + + Draw packets using your coloring rules + Skriv ut paket och använd dina färgläggningsregler + + + &Zoom In + &Zooma in + + + Enlarge the main window text + Förstora huvudfönstrets text + + + Zoom Out + Zooma ut + + + Normal Size + Normal storlek + + + Resize Columns + Ändra storlek på kolumner + + + Resize packet list columns to fit contents + Ändra storleken på kolumner med paketlistor för att passa med innehållet + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Datum och tid på dagen (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + Visa pakettider som datum och tid på dagen. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + År, dag på året, och tid på dagen (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Visa pakettider som år, dag på året och tid på dagen. + + + Time of Day (01:02:03.123456) + Tid på dagen (01:02:03.123456) + + + Seconds Since 1970-01-01 + Sekunder sedan 1970-01-01 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Visa pakettider som antal sekunder sedan UNIX-/POSIX-epoken (1970-01-01). + + + Seconds Since Previous Captured Packet + Sekunder sedan föregående fångade paket + + + Show packet times as the seconds since the previous captured packet. + Visa pakettider som antalet sekunder sedan föregående fångade paket. + + + Seconds Since Previous Displayed Packet + Sekunder sedan föregående visade paket + + + Show packet times as the seconds since the previous displayed packet. + Visa pakettider som antalet sekunder sedan föregående visade paket. + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + UTC-datum och tid på dagen (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Visa pakettider som UTC-datumet och tiden på dagen. + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + UTC-år, dag på året, och tid på dagen (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + Visa pakettider som UTC-år, dag på året och tid på dagen. + + + UTC Time of Day (01:02:03.123456) + UTC-tid på dagen (01:02:03.123456) + + + Show packet times as the UTC time of day. + Visa pakettider som UTC-tiden på dagen. + + + Automatic (from capture file) + Automatiskt (från fångstfilen) + + + Use the time precision indicated in the capture file. + Använd tidsprecisionen som indikeras i fångstfilen. + + + Seconds + Sekunder + + + Tenths of a second + Tiondelar av en sekund + + + Hundredths of a second + Hundradelar av en sekund + + + Milliseconds + Millisekunder + + + Microseconds + Mikrosekunder + + + Nanoseconds + Nanosekunder + + + Display Seconds With Hours and Minutes + Visa sekunder med timmar och minuter + + + Display seconds with hours and minutes + Visa sekunder med timmar och minuter + + + Resolve &Physical Addresses + Slå upp &fysiska adresser + + + Show names for known MAC addresses. Lookups use a local database. + Visa namn för kända MAC-adresser. Uppslagningar använder en lokal databas. + + + Resolve &Network Addresses + Slå upp &nätverksadresser + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + Visa namn på kända IPv4-, IPv6- och IPX-adresser. Uppslagningar kan generera nätverkstrafik. + + + Resolve &Transport Addresses + Slå upp &transportadresser + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + Visa namn på kända TCP-, UDP- och SCTP-tjänster. Uppslagningar kan generera trafik på några system. + + + Wire&less Toolbar + Tråd&lös verktygsrad + + + Show or hide the wireless toolbar + Visa eller dölj den trådlösa verktygsraden + + + &Status Bar + &Statusrad + + + Show or hide the status bar + Visa eller dölj statusraden + + + Packet &List + Paket&lista + + + Show or hide the packet list + Visa eller dölj paketlistan + + + Packet &Details + Paket&detaljer + + + Show or hide the packet details + Visa eller dölj paketdetaljerna + + + Packet &Bytes + Paket&byte + + + Show or hide the packet bytes + Visa eller dölj paketbyte + + + &Conversation Hash Tables + &Konversationshashtabeller + + + &Dissector Tables + &Dissekeringstabeller + + + &Supported Protocols + Protokoll som &stödjs + + + MAP Summary + MAP-sammanfattning + + + GSM MAP summary statistics + GSM MAP-sammanfattande statistik + + + RLC &Graph + RLC-&graf + + + &Coloring Rules… + &Färgläggningsregler … + + + Show Linked Packet in New Window + Visa länkade paket i ett nytt fönster + + + New Coloring Rule… + New Conversation Rule… + Ny färgläggningsregel … + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + RTP-strömanalys för den valda strömmen. Tryck CTRL-tangenten för att lägga till den omvända strömmen också. + + + RTP Player + RTP-spelare + + + Play selected stream. Press CTRL key for playing reverse stream too. + Spela den valda strömmen. Tryck CTRL-tangenten för att spela den omvända strömmen också. + + + IA&X2 Stream Analysis + IA&X2-strömanalys + + + Enabled Protocols… + Enable Protocols… + Aktiverade protokoll … + + + Wiki Protocol Page + Wiki-protokollsida + + + Open the Wireshark wiki page for this protocol. + Öppna Wireshark-wiki-sidan för detta protokoll. + + + Filter Field Reference + Filterfältreferens + + + Open the display filter reference page for this filter field. + Öppna visningsfiltrets referenssida för detta filterfält. + + + Go to the packet referenced by the selected field. + Gå till paketet refererat av det valda fältet. + + + &VoIP Calls + &VoIP-anrop + + + Open &Recent + Öppna &senaste + + + Name Resol&ution + Namn&uppslagning + + + Service &Response Time + Tjänste&svarstid + + + &RTP + &RTP + + + S&CTP + S&CTP + + + &ANSI + &ANSI + + + &GSM + &GSM + + + &LTE + &LTE + + + &MTP3 + &MTP3 + + + &Open + &Öppna + + + &Quit + &Avsluta + + + &Close + &Stäng + + + Display &Filters… + Visnings&filter … + + + &Unmark All Displayed + &Avmarkera alla visade + + + All VoIP Calls + Alla VoIP-anrop + + + SIP &Flows + SIP-&flöden + + + SIP Flows + SIP-flöden + + + RTP Streams + RTP-strömmar + + + Edit the packet list coloring rules. + Redigera färgläggningsreglerna för paketlistan. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Blåtands-ATT-serverattribut + + + Show Packet in New &Window + Visa paket i ett nytt &fönster + + + Show this packet in a separate window. + Visa detta paket i ett separat fönster. + + + Show the linked packet in a separate window. + Visa det länkade paketet i ett separat fönster. + + + Auto Scroll in Li&ve Capture + Rulla automatiskt i li&ve-fångst + + + Automatically scroll to the last packet during a live capture. + Rulla automatiskt till det sista paketet under en live-fångst. + + + Expert Information + Expertinformation + + + Show expert notifications + Visa expertnotifieringar + + + Add an expression to the display filter. + Lägg till ett uttryck till visningsfiltret. + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + Start på ”REGISTER_STAT_GROUP_UNSORTED” + + + No ANSI statistics registered + No tools registered + Ingen ANSI-statistik registrerad + + + Resolved Addresses + Uppslagna adresser + + + Show each table of resolved addresses as copyable text. + Visa varje tabell över uppslagna adresser som kopierbar text. + + + Color &1 + Färg &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + Markera den aktuella konversationen med sin egen färg. + + + Color &2 + Färg &2 + + + Color &3 + Färg &3 + + + Color &4 + Färg &4 + + + Color &5 + Färg &5 + + + Color &6 + Färg &6 + + + Color &7 + Färg &7 + + + Color &8 + Färg &8 + + + Color &9 + Färg &9 + + + Color 1&0 + Färg 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + Skapa en ny färgläggningsregel baserat på detta fält. + + + Reset Colorization + Återställ färgläggningen + + + Reset colorized conversations. + Återställ färglagda konversationer. + + + RTP Stream Analysis + RTP-strömanalys + + + Edit Resolved Name + Redigera uppslagna namn + + + Manually edit a name resolution entry. + Redigera manuellt en namnuppslagningspost. + + + Enable and disable specific protocols + Aktivera och avaktivera specifika protokoll + + + before quitting + före avslut + + + Save packets before merging? + Spara paket före sammanslagning? + + + A temporary capture file can't be merged. + En temporär fångstfil kan inte slås samman. + + + Save changes in "%1" before merging? + Spara ändringar i ”%1” före sammanslagning? + + + Changes must be saved before the files can be merged. + Ändringar måste sparas före filerna kan slås samman. + + + Invalid Display Filter + Felaktigt visningsfilter + + + Invalid Read Filter + Felaktigt läsningsfilter + + + The filter expression %1 isn't a valid read filter. (%2). + Filteruttrycket %1 är inte ett giltigt läsningsfilter. (%2). + + + before importing a capture + before importing a new capture + före import av en fångst + + + Unable to export to "%1". + Kan inte exportera till ”%1”. + + + You cannot export packets to the current capture file. + Du kan inte exportera paket till den nuvarande fångstfilen. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + Vill du spara ändringarna du gjort%1? + + + Your captured packets will be lost if you don't save them. + Dina fångade paket kommer gå förlorade om du inte sparar dem. + + + Do you want to save the changes you've made to the capture file "%1"%2? + Vill du spara ändringarna du gjort till fångstfilen ”%1”%2? + + + Your changes will be lost if you don't save them. + Dina ändringar kommer gå förlorade om du inte sparar dem. + + + Check for Updates… + Leta efter uppdateringar … + + + Unable to drop files during capture. + Kan inte släppa filer under fångsten. + + + Unknown file type returned by merge dialog. + Okänd filtyp returnerad av sammanslagningsdialogen. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Rapportera gärna detta som ett problem med Wireshark på https://gitlab.com/wireshark/wireshark/-/issues. + + + Unknown file type returned by export dialog. + Okänd filtyp returnerad av exportdialogen. + + + Do you want to stop the capture and save the captured packets%1? + Vill du sluta fångsten och spara de fångade paketen%1? + + + Do you want to save the captured packets%1? + Vill du sparade de fångade paketen%1? + + + Save before Continue + Spara före fortsättningen + + + Stop and Save + Stoppa och spara + + + Stop and Quit &without Saving + Stop and Quit without Saving + Stoppa och avsluta &utan att spara + + + Quit &without Saving + Quit without Saving + Avsluta &utan att spara + + + There is no "rtp.ssrc" field in this version of Wireshark. + Det finns inget "rtp.ssrc"-fält i denna version av Wireshark. + + + Please select an RTPv2 packet with an SSRC value + Välj ett RTPv2-paket med ett SSRC-värde + + + SSRC value not found. + SSRC-värdet finns inte. + + + Show or hide the toolbar + Visa eller dölj verktygsraden + + + Continue &without Saving + Continue without Saving + Fortsätt &utan att spara + + + Stop and Continue &without Saving + Stop and Continue without Saving + Stoppa och fortsätt &utan att spara + + + The Wireshark Network Analyzer + Nätverksanalysatorn Wireshark + + + Capturing from %1 + Fångst från %1 + + + before opening another file + före en annan fil öppnas + + + Merging files. + Slår samman filer. + + + %1: %2 + %1: %2 + + + Clear Menu + Töm menyn + + + before closing the file + före filen stängs + + + Export Selected Packet Bytes + Exportera valda paketbyte + + + No Keys + Inga nycklar + + + Raw data (*.bin *.dat *.raw);;All Files ( + Rå data (*.bin *.dat *.raw);;Alla filer ( + + + Couldn't copy text. Try another item. + Kunde inte kopiera texten. Försök med ett annat objekt. + + + Are you sure you want to remove all packet comments? + Är du säker på att du vill ta bort alla paketkommentarer? + + + Unable to build conversation filter. + Kan inte bygga ett konversationsfilter. + + + before reloading the file + före filen läses om + + + Error compiling filter for this conversation. + Fel vid kompilering av filter för denna konversation. + + + No previous/next packet in conversation. + Inget föregående/nästa paket i konversationen. + + + No interface selected. + Inget gränssnitt valt. + + + Saving %1… + Sparar %1 … + + + Configure all extcaps before start of capture. + Konfigurera alla extcap:ar före start av fångst + + + Invalid capture filter. + Felaktigt fångstfilter. + + + (empty comment) + placeholder for empty comment + (tom kommentar) + + + Add New Comment… + Lägg till en ny kommentar … + + + Edit "%1" + edit packet comment + Redigera ”%1” + + + Delete "%1" + delete packet comment + Ta bort ”%1” + + + Delete packet comments + Ta bort paketkommentarer + + + Delete comments from %n packet(s) + + Ta bort kommentarer från %n paket + Ta bort kommentarer från %n paket + + + + before starting a new capture + före en ny fångst startas + + + before reloading Lua plugins + före Lua-insticksmodulen laddas om + + + Please wait while Wireshark is initializing… + Vänta medan Wireshark initieras … + + + before updating + + + + There are no TLS Session Keys to save. + Det finns inga TLS-sessionsnycklar att spara. + + + Export TLS Session Keys (%Ln key(s)) + + Exportera TLS-sessionsnycklar (%Ln nyckel) + Exportera TLS-sessionsnycklar (%Ln nycklar) + + + + TLS Session Keys (*.keys *.txt);;All Files ( + TLS-sessionsnycklar (*.keys *.txt);;Alla filer ( + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + Inget filter tillgängligt. Försök med ett annat %1. + + + column + kolumn + + + item + post + + + The "%1" column already exists. + Kolumnen ”%1” finns redan. + + + The "%1" column already exists as "%2". + Kolumnen ”%1” finns redan som ”%2”. + + + RTP packet search failed + RTP-paketsökningen misslyckades + + + No Interface Selected. + Inget gränssnitt valt. + + + before restarting the capture + före omstart av fångsten + + + Wiki Page for %1 + Wiki-sida för %1 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Wireshark-wikin underhålls av gemenskapen.</p><p>Sidan du står i begrepp att läsa in kan vara underbar, ofullständig, felaktig, eller saknas.</p><p>Gå vidare till wikin?</p> + + + Loading + Läser in + + + Reloading + Läser om + + + Rescanning + Söker om + + + + WlanStatisticsDialog + + Wireless LAN Statistics + Trådlöst LAN-statistik + + + Channel + Kanal + + + SSID + SSID + + + Percent Packets + Procent paket + + + Percent Retry + Procent omförsök + + + Probe Reqs + Probbegäranden + + + Probe Resp + Probsvar + + + Auths + Autentiseringar + + + Retry + Omförsök + + + Deauths + Avautentiseringar + + + Other + Annat + + + diff --git a/ui/qt/wireshark_tr_TR.ts b/ui/qt/wireshark_tr_TR.ts new file mode 100644 index 00000000..e0885634 --- /dev/null +++ b/ui/qt/wireshark_tr_TR.ts @@ -0,0 +1,14758 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + Wireshark Hakkında + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Ağ Protokolü Çözümleyicisi</span> + + + Copy the version information to the clipboard + Sürüm bilgilerini panoya kopyalayın + + + Copy to Clipboard + Panoya Kopyala + + + Authors + Yazarlar + + + Search Authors + Yazarları Ara + + + Folders + Klasörler + + + Filter by path + Yola göre filtrele + + + Plugins + Eklentiler + + + No plugins found. + Eklenti bulunamadı. + + + Search Plugins + Eklentileri Ara + + + Filter by type: + Türe göre filtrele: + + + Keyboard Shortcuts + Klavye Kısayolları + + + Search Shortcuts + Arama Kısayolları + + + Acknowledgments + Teşekkürler + + + License + Lisans + + + The directory does not exist + Dizin mevcut değil + + + Should the directory %1 be created? + %1 dizini oluşturulmalı mı? + + + The directory could not be created + Dizin oluşturulamadı + + + The directory %1 could not be created. + %1 dizini oluşturulamadı. + + + Show in Finder + Finder'da Göster + + + Show in Folder + Klasörde Göster + + + Copy + Kopyala + + + Copy Row(s) + + + + + + + AddressEditorFrame + + Frame + Çerçeve + + + Name Resolution Preferences… + Name Resolution Preferences... + Ad Çözümleme Tercihleri… + + + Address: + Adres: + + + Name: + Adı: + + + Can't assign %1 to %2. + %1, %2'ye atanamıyor. + + + + AdvancedPrefsModel + + Name + Ad + + + Status + Durum + + + Type + Biçim + + + Value + Değer + + + + ApplyLineEdit + + Apply changes + Değişiklikleri uygula + + + + AuthorListModel + + Name + Ad + + + Email + E-posta + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Bluetooth ATT Sunucusu Özellikleri + + + Handle + Elle + + + UUID + UUID + + + UUID Name + UUID Adı + + + All Interfaces + Tüm Arayüzler + + + All Devices + Tüm cihazlar + + + Remove duplicates + Yinelenenleri kaldır + + + Copy Cell + Hücreyi Kopyala + + + Copy Rows + Satırları Kopyala + + + Copy All + Tümünü Kopyala + + + Save as image + Resim olarak kaydet + + + Mark/Unmark Row + Satırı İşaretle/İşaretini Kaldır + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Hücreyi İşaretle/İşaretini Kaldır + + + Save Table Image + Tablo Resmini Kaydet + + + PNG Image (*.png) + PNG görüntüsü (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + Bluetooth Cihazı + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Ad + + + Class of Device + Cihaz Sınıfı + + + LMP Version + LMP Sürümü + + + LMP Subversion + LMP Alt sürüm + + + Manufacturer + Üretici + + + HCI Version + HCI Sürümü + + + HCI Revision + HCI Revizyonu + + + Scan + Tara + + + Authentication + Kimlik Doğrulama + + + Encryption + Şifreleme + + + ACL MTU + ACL MTU + + + ACL Total Packets + ACL Toplam Paketleri + + + SCO MTU + SCO MTU + + + SCO Total Packets + SCO Toplam Paketleri + + + LE ACL MTU + LE ACL MTU + + + LE ACL Total Packets + LE ACL Toplam Paketler + + + LE ISO MTU + LE ISO MTU + + + LE ISO Total Packets + LE ISO Toplam Paketler + + + Inquiry Mode + Sorgu Modu + + + Page Timeout + Sayfa Zaman Aşımı + + + Simple Pairing Mode + Basit Eşleştirme Modu + + + Voice Setting + Ses Ayarı + + + Value + Değer + + + Changes + Değişiklikler + + + %1 changes + %1 değişiklik + + + Copy Cell + Hücreyi Kopyala + + + Copy Rows + Satırları Kopyala + + + Copy All + Tümünü Kopyala + + + Save as image + Resim olarak kaydet + + + Mark/Unmark Row + Satırı İşaretle/İşaretini Kaldır + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Hücreyi İşaretle/İşaretini Kaldır + + + Unknown + Bilinmiyor + + + Bluetooth Device - %1%2 + Bluetooth Cihazı - %1%2 + + + enabled + etkin + + + disabled + devre dışı + + + %1 ms (%2 slots) + %1 ms (%2 yuva) + + + Save Table Image + Tablo Resmini Kaydet + + + PNG Image (*.png) + PNG görüntüsü (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Bluetooth Cihazları + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + Ad + + + LMP Version + LMP Sürümü + + + LMP Subversion + LMP Alt sürüm + + + Manufacturer + Üretici + + + HCI Version + HCI Sürümü + + + HCI Revision + HCI Revizyonu + + + Is Local Adapter + Yerel Bağdaştırıcı + + + All Interfaces + Tüm Arayüzler + + + Show information steps + Bilgi adımlarını göster + + + %1 items; Right click for more option; Double click for device details + %1 öğe; Daha fazla seçenek için sağ tıklayın; Cihaz detayları için çift tıklayın + + + Copy Cell + Hücreyi Kopyala + + + Copy Rows + Satırları Kopyala + + + Copy All + Tümünü Kopyala + + + Save as image + Resim olarak kaydet + + + Mark/Unmark Row + Satırı İşaretle/İşaretini Kaldır + + + Ctrl-M + Ctrl-M + + + Mark/Unmark Cell + Hücreyi İşaretle/İşaretini Kaldır + + + true + doğru + + + Save Table Image + Tablo Resmini Kaydet + + + PNG Image (*.png) + PNG görüntüsü (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Bluetooth HCI Özeti + + + Name + Ad + + + OGF + OGF + + + OCF + OCF + + + Opcode + İşlemci Kodu + + + Event + Olay + + + Subevent + Alt olay + + + Status + Durum + + + Reason + Sebep + + + Hardware Error + Donanım Hatası + + + Occurrence + Oluşum + + + Link Control Commands + Bağlantı Kontrol Komutları + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Bağlantı Politikası Komutları + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Denetleyici ve Temel Bant Komutları + + + 0x03 + 0x03 + + + Informational Parameters + Bilgi Parametreleri + + + 0x04 + 0x04 + + + Status Parameters + Durum Parametreleri + + + 0x05 + 0x05 + + + Testing Commands + Test Komutları + + + 0x06 + 0x06 + + + LE Controller Commands + LE Kontrolör Komutları + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + Bluetooth Logo Test Komutları + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Satıcıya Özel Komutlar + + + 0x3F + 0x3F + + + Unknown OGF + Bilinmeyen OGF + + + Events + Etkinlikler + + + Hardware Errors + Donanım Hataları + + + Results filter: + Sonuçlar filtresi: + + + Display filter: + Ekran filtresi: + + + All Interfaces + Tüm Arayüzler + + + All Adapters + Tüm Adaptörler + + + Copy Cell + Hücreyi Kopyala + + + Copy Rows + Satırları Kopyala + + + Copy All + Tümünü Kopyala + + + Save as image + Resim olarak kaydet + + + Mark/Unmark Row + Satırı İşaretle/İşaretini Kaldır + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + Hücreyi İşaretle/İşaretini Kaldır + + + Unknown + Bilinmiyor + + + Adapter %1 + %1 bağdaştırıcısı + + + Frame %1 + Çerçeve %1 + + + Pending + Sırada + + + Save Table Image + Tablo Resmini Kaydet + + + PNG Image (*.png) + PNG görüntüsü (*.png) + + + + ByteViewTab + + Packet bytes + Paket bayt + + + + ByteViewText + + Allow hover highlighting + Fareyle vurgulamaya izin ver + + + Show bytes as hexadecimal + Baytları onaltılık olarak göster + + + …as decimal + + + + …as octal + + + + …as bits + …bit olarak + + + Show text based on packet + Pakete göre metni göster + + + …as ASCII + …ASCII olarak + + + …as EBCDIC + …EBCDIC olarak + + + + CaptureFile + + [closing] + [kapanıyor] + + + [closed] + [kapandı] + + + + CaptureFileDialog + + This capture file contains comments. + Bu yakalama dosyası yorumlar içerir. + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + Seçtiğiniz dosya biçimi yorumları desteklemiyor. Yakalamayı yorumları destekleyen bir biçimde kaydetmek mi yoksa yorumları atıp seçtiğiniz biçimde kaydetmek mi istiyorsunuz? + + + Discard comments and save + Yorumları sil ve kaydet + + + Save in another format + Başka bir biçimde kaydet + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + Kaydedilebilecek hiçbir dosya biçimi yorumları desteklemez. Yorumları silmek ve seçtiğiniz biçimde kaydetmek istiyor musunuz? + + + All Files ( + Tüm Dosyalar ( + + + All Capture Files + Tüm Yakalama Dosyaları + + + Format: + Biçim: + + + Size: + Boyut: + + + Start / elapsed: + Başlangıç / geçen: + + + Automatically detect file type + Dosya türünü otomatik olarak algıla + + + Prepend packets + Paketleri başa ekle + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Geçerli dosyadan önce seçilen dosyadan paketleri ekleyin. Paket zaman damgaları yok sayılır. + + + Merge chronologically + Kronolojik olarak birleştir + + + Insert packets in chronological order. + Paketleri kronolojik sıraya göre yerleştirin. + + + Append packets + Paketleri ekle + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Geçerli dosyadan sonra seçilen dosyadan paketleri ekleyin. Paket zaman damgaları yok sayılır. + + + Read filter: + Filtreyi oku: + + + Compress with g&zip + G&zip ile sıkıştır + + + Open Capture File + Wireshark: Open Capture File + Yakalama Dosyasını Aç + + + Save Capture File As + Wireshark: Save Capture File As + Yakalama Dosyasını Farklı Kaydet + + + Save as: + Farklı kaydet: + + + Export Specified Packets + Wireshark: Export Specified Packets + Belirtilen Paketleri Dışa Aktar + + + Export as: + Şu şekilde dışa aktar: + + + Merge Capture File + Wireshark: Merge Capture File + Yakalama Dosyasını Birleştir + + + Unknown file type returned by save as dialog. + Farklı kaydet iletişim kutusu tarafından döndürülen bilinmeyen dosya türü. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Lütfen bunu https://gitlab.com/wireshark/wireshark/-/issues adresinde bir Wireshark sorunu olarak bildirin. + + + directory + dizin + + + unknown file format + bilinmeyen dosya formatı + + + error opening file + dosya açma hatası + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + + + + + %1, timed out at %Ln data record(s) + + + + + + %1, %Ln data record(s) + + + + + + unknown + bilinmeyen + + + + CaptureFilePropertiesDialog + + Details + Detaylar + + + Capture file comments + Dosya yorumlarını yakala + + + Refresh + Yenile + + + Copy To Clipboard + Panoya Kopyala + + + Save Comments + Yorumları Kaydet + + + Capture File Properties + Yakalama Dosyası Özellikleri + + + Unknown + Bilinmiyor + + + File + Dosya + + + Name + Ad + + + Length + Uzunluk + + + Hash (SHA256) + Karma (SHA256) + + + Hash (SHA1) + Karma (SHA1) + + + Format + Biçim + + + Encapsulation + Kapsülleme + + + Snapshot length + Anlık görüntü uzunluğu + + + Time + Süre + + + First packet + İlk paket + + + Last packet + Son paket + + + Elapsed + Geçen + + + Section %1 + Bölüm %1 + + + Capture + Yakala + + + Hardware + Donanım + + + OS + OS + + + Application + Uygulama + + + Interfaces + Arayüzler + + + Interface + Arayüz + + + Dropped packets + Bırakılan paketler + + + Capture filter + Yakalama filtresi + + + Link type + Bağlantı türü + + + Packet size limit (snaplen) + Paket boyutu sınırı (snaplen) + + + none + yok + + + %1 bytes + %1 bayt + + + Statistics + İstatistikler + + + Measurement + Ölçüm + + + Captured + Yakalandı + + + Displayed + Görüntülenen + + + Marked + İşaretlenmiş + + + Packets + Paketler + + + Time span, s + Zaman aralığı, s + + + Average pps + Ortalama kişi sayısı + + + Average packet size, B + Ortalama paket boyutu, B + + + Bytes + Bayt + + + Average bytes/s + Ortalama bayt/sn + + + Average bits/s + Ortalama bit/sn + + + Section Comment + Bölüm Yorumu + + + Packet Comments + Paket Yorumları + + + <p>Frame %1: + <p>Çerçeve %1: + + + Created by Wireshark %1 + + + Wireshark tarafından oluşturuldu %1 + + + + + + CaptureFilterCombo + + Capture filter selector + Yakalama filtresi seçici + + + + CaptureFilterEdit + + Capture filter entry + Filtre girişini yakala + + + Manage saved bookmarks. + Kaydedilmiş yer imlerini yönetin. + + + Apply this filter string to the display. + Bu filtre dizesini ekrana uygulayın. + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + Birden çok filtre seçildi. Bunları burada geçersiz kılın veya korumak için burayı boş bırakın. + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>Seçtiğiniz arayüzlerde farklı yakalama filtreleri var. Buraya bir filtre yazmak onları geçersiz kılar. Hiçbir şey yapmamak onları koruyacaktır.</p> + + + Enter a capture filter %1 + Bir yakalama filtresi girin %1 + + + Save this filter + Bu filtreyi kaydet + + + Remove this filter + Bu filtreyi kaldır + + + Manage Capture Filters + Yakalama Filtrelerini Yönet + + + + CaptureInfoDialog + + Capture Information + Yakalama Bilgileri + + + Stop Capture + Yakalamayı Durdur + + + %1 packets, %2:%3:%4 + %1 paket, %2:%3:%4 + + + + CaptureInfoModel + + Other + Diğer + + + + CaptureOptionsDialog + + Input + Giriş + + + Interface + Arayüz + + + Traffic + Trafik + + + Link-layer Header + Bağlantı katmanı Başlığı + + + Promiscuous + Karışık + + + Snaplen (B) + Snaplen (B) + + + Buffer (MB) + Arabellek (MB) + + + Monitor Mode + Monitör Modu + + + Capture Filter + Yakalama Filtresi + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Muhtemelen bunu etkinleştirmek istiyorsunuz. Genellikle bir ağ kartı yalnızca kendi ağ adresine gönderilen trafiği yakalar. Ağ kartının &quot;görebildiği&quot;, tüm trafiği yakalamak istiyorsanız, bu seçeneği işaretleyin. Anahtarlı bir ağdan paket yakalamayla ilgili daha fazla ayrıntı için SSS'ye bakın.</p></body></html> + + + Enable promiscuous mode on all interfaces + Tüm arayüzlerde karışık modu etkinleştir + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + Arayüzleri gösterin ve gizleyin, yorumlar ekleyin ve boruları ve uzak arayüzleri yönetin. + + + Manage Interfaces… + Arayüzleri Yönet… + + + Capture filter for selected interfaces: + Seçilen arayüzler için yakalama filtresi: + + + Compile BPFs + BPF'leri Derleyin + + + Output + Çıktı + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + <html><head/><body><p>Yakalanan verilerin yazılacağı dosya adını girin. Varsayılan olarak geçici bir dosya kullanılacaktır.</p></body></html> + + + Capture to a permanent file + Kalıcı bir dosyaya yakalayın + + + File: + Dosya: + + + Browse… + Gözat… + + + Output format: + Çıkış biçimi: + + + pcapng + pcapng + + + pcap + pcap + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>Tek bir yakalama dosyası kullanmak yerine, birden çok dosya oluşturulacaktır.</p><p>Oluşturulan dosya adları, artan bir sayı ve yakalamanın başlangıç zamanını içerecektir. .</p><p>NOT: Etkinleştirilirse, yeni dosya ölçütlerinden en az biri SEÇİLMELİDİR.</p></body></html> + + + Create a new file automatically… + Otomatik olarak yeni bir dosya oluştur… + + + after + sonra + + + Switch to the next file after the specified number of packets have been captured. + Belirtilen sayıda paket yakalandıktan sonra bir sonraki dosyaya geçin. + + + packets + paketler + + + Switch to the next file after the file size exceeds the specified file size. + Dosya boyutu belirtilen dosya boyutunu aştığında sonraki dosyaya geçin. + + + kilobytes + kilobayt + + + megabytes + megabayt + + + gigabytes + gigabayt + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + Geçerli dosyaya yakalama süresi belirtilen süreyi aştığında sonraki dosyaya geçin. + + + seconds + saniye + + + minutes + dakika + + + hours + saat + + + when time is a multiple of + zaman çoklu olduğunda + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + (duvar saati) süresi belirtilen aralığın çift katı olduğunda sonraki dosyaya geçin. +Örneğin, her saat başı yeni bir dosyanın oluşturulması için 1 saat kullanın. + + + compression + sıkıştırma + + + None + Yok + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>Yakalama bir sonraki dosyaya geçtikten ve verilen dosya sayısı aşıldıktan sonra, en eski dosya kaldırılacaktır.</p></body></html> + + + Use a ring buffer with + İle bir halka arabelleği kullanın + + + files + dosyalar + + + Options + Seçenekler + + + Display Options + Ekran Seçenekleri + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + <html><head/><body><p>Bu seçeneği kullanmak, yakalanan paketleri hemen ana ekranda gösterecektir. Lütfen unutmayın: Bu, yakalamayı yavaşlatacağından, artan paket düşüşleri görünebilir.</p></body></html> + + + Update list of packets in real-time + Paket listesini gerçek zamanlı olarak güncelleyin + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + <html><head/><body><p>Bu, &quot;Paketlerin listesini&quot; gerçek zamanlı olarak güncelle seçeneği kullanıldığında, &quot;Paket Listesini otomatik olarak en son&quot; yakalanan pakete kaydırır.</p></body></html> + + + Automatically scroll during live capture + Canlı yakalama sırasında otomatik olarak kaydırın + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + <html><head/><body><p>Yakalarken yakalama bilgisi iletişim kutusunu göster.</p></body></html> + + + Show capture information during live capture + Canlı yakalama sırasında yakalama bilgilerini göster + + + Name Resolution + Ad Çözümlemesi + + + Perform MAC layer name resolution while capturing. + Yakalarken MAC katman adı çözümlemesi gerçekleştirin. + + + Resolve MAC addresses + MAC adreslerini çözümle + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + <html><head/><body><p>Yakalarken ağ katmanı adı çözümlemesi gerçekleştirin.</p></body></html> + + + Resolve network names + Ağ adlarını çözümle + + + Perform transport layer name resolution while capturing. + Yakalarken aktarım katmanı ad çözümlemesi gerçekleştirin. + + + Resolve transport names + Aktarım adlarını çözümle + + + Stop capture automatically after… + Aşağıdakilerden sonra yakalamayı otomatik olarak durdur… + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + <html><head/><body><p>Belirtilen sayıda paket yakalandıktan sonra yakalamayı durdurun.</p></body></html> + + + Stop capturing after the specified number of packets have been captured. + Belirtilen sayıda paket yakalandıktan sonra yakalamayı durdurun. + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + <html><head/><body><p>Belirtilen sayıda dosya oluşturulduktan sonra yakalamayı durdurun.</p></body></html> + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + <html><head/><body><p>Belirtilen miktarda veri yakalandıktan sonra yakalamayı durdurun.</p></body></html> + + + Stop capturing after the specified amount of data has been captured. + Belirtilen miktarda veri yakalandıktan sonra yakalamayı durdurun. + + + Stop capturing after the specified amount of time has passed. + Belirtilen süre geçtikten sonra yakalamayı durdurun. + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + <html><head/><body><p>İsteğe bağlı olarak, adsız yakalama dosyaları için geçici bir dizin belirtin.</p></body></html> + + + Directory for temporary files + Geçici dosyalar için dizin + + + Capture Options + Yakalama Seçenekleri + + + Start + Başla + + + Leave blank to use a temporary file + Geçici bir dosya kullanmak için boş bırakın + + + Specify a Capture File + Bir Yakalama Dosyası Belirtin + + + Specify temporary directory + Geçici dizini belirtin + + + %1: %2 + %1: %2 + + + Addresses + Adresler + + + Address + Adres + + + no addresses + adres yok + + + Error + Hata + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + Birden çok dosya: İstenen dosya boyutu çok büyük. Dosya boyutu 2 GiB'den büyük olamaz. + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + Birden çok dosya: Yakalama dosyası adı verilmedi. Birden fazla dosya kullanmak istiyorsanız bir dosya adı belirtmelisiniz. + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + Birden çok dosya: Dosya sınırı verilmedi. Her dosya için bir dosya boyutu, aralığı veya paket sayısı belirtmelisiniz. + + + + CapturePreferencesFrame + + Frame + Çerçeve + + + Default interface + Varsayılan arayüz + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Muhtemelen bunu etkinleştirmek istiyorsunuz. Genellikle bir ağ kartı yalnızca kendi ağ adresine gönderilen trafiği yakalar. Ağ kartının &quot;görebildiği&quot;, tüm trafiği yakalamak istiyorsanız, bu seçeneği işaretleyin. Anahtarlı bir ağdan paket yakalamayla ilgili daha fazla ayrıntı için SSS'ye bakın.</p></body></html> + + + Capture packets in promiscuous mode + Paketleri karışık modda yakalayın + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Paketleri yeni nesil yakalama dosyası biçiminde yakalayın.</p></body></html> + + + Capture packets in pcapng format + Paketleri pcapng formatında yakalayın + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Yakalama devam ederken paket listesini güncelleyin. Bu, yüksek hızlı ağlarda paketlerin düşmesine neden olabilir.</p></body></html> + + + Update list of packets in real time + Paket listesini gerçek zamanlı olarak güncelleyin + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + Başlangıçta arayüzleri yüklemeyin + + + Disable external capture interfaces + Harici yakalama arabirimlerini devre dışı bırakın + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + "@" sembolü yok sayılır. + + + + ColoringRulesDialog + + Dialog + Diyalog + + + <small><i>A hint.</i></small> + <small><i>Bir ipucu.</i></small> + + + Add a new coloring rule. + Yeni bir renklendirme kuralı ekleyin. + + + Delete this coloring rule. + Bu renklendirme kuralını silin. + + + Duplicate this coloring rule. + Bu renklendirme kuralını çoğaltın. + + + Clear all coloring rules. + Tüm renklendirme kurallarını temizleyin. + + + Set the foreground color for this rule. + Bu kural için ön plan rengini ayarlayın. + + + Foreground + Ön Plan + + + Set the background color for this rule. + Bu kural için arka plan rengini ayarlayın. + + + Background + Arka Plan + + + Set the display filter using this rule. + Bu kuralı kullanarak görüntü filtresini ayarlayın. + + + Apply as filter + Filtre olarak uygula + + + Select a file and add its filters to the end of the list. + Bir dosya seçin ve filtrelerini listenin sonuna ekleyin. + + + Save filters in a file. + Filtreleri bir dosyaya kaydedin. + + + Coloring Rules %1 + Renklendirme Kuralları %1 + + + Import… + İçe aktar… + + + Export… + Dışa aktar… + + + Copy coloring rules from another profile. + Başka bir profilden renklendirme kurallarını kopyalayın. + + + Open + + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Düzenlemek için çift tıkla. Taşımak için sürükleyin. Kurallar, bir eşleşme bulunana kadar sırayla işlenir. + + + Import Coloring Rules + Renklendirme Kurallarını İçe Aktar + + + Export %1 Coloring Rules + %1 Renklendirme Kuralını Dışa Aktar + + + + ColoringRulesModel + + New coloring rule + Yeni renklendirme kuralı + + + Unable to save coloring rules: %1 + Renklendirme kuralları kaydedilemiyor: %1 + + + Name + Ad + + + Filter + Filtrele + + + + ColumnEditorFrame + + Frame + Çerçeve + + + Title: + Title + Başlık: + + + Type: + Type + Tür: + + + Fields: + Fields + Alanlar: + + + Occurrence: + Occurrence + Oluşum: + + + Resolve Names: + İsimleri Çöz: + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + <html><head/><p>Alanlar için ham değerler yerine insan tarafından okunabilir dizeleri gösterin. Yalnızca değer dizeleri olan alanlara sahip özel sütunlar için geçerlidir.</p></body></html> + + + Missing fields. + Eksik alanlar. + + + Invalid fields. + Geçersiz alanlar. + + + Invalid occurrence value. + Geçersiz oluşum değeri. + + + + ColumnListModel + + Displayed + Görüntülenen + + + Title + Başlık + + + Type + Biçim + + + Fields + Alanlar + + + Field Occurrence + Alan Oluşumu + + + Resolved + Çözüldü + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + <html>Alanlar için ham değerler yerine insan tarafından okunabilir dizeleri gösterin. Yalnızca değer dizeleri olan alanlara sahip özel sütunlar için geçerlidir.</html> + + + New Column + Yeni Sütun + + + + ColumnPreferencesFrame + + Frame + Çerçeve + + + Add a new column + Yeni bir sütun ekle + + + Delete selected column + Seçili sütunu sil + + + Show displayed columns only + Yalnızca görüntülenen sütunları göster + + + Reset all changes + Tüm değişiklikleri sıfırla + + + + CompiledFilterOutput + + Compiled Filter Output + Derlenmiş Filtre Çıkışı + + + Copy + Kopyala + + + Copy filter text to the clipboard. + Filtre metnini panoya kopyalayın. + + + + ConversationDataModel + + Address A + Adres A + + + Port A + Bağlantı noktası A + + + Address B + Adres B + + + Port B + Bağlantı noktası B + + + Packets + Paketler + + + Bytes + Bayt + + + Stream ID + Akış Kimliği + + + Packets A + Paketler A + + + Bytes A + Bayt A + + + Packets B + Paketler B + + + Bytes B + Bayt B + + + Abs Start + Abs Başlangıç + + + Rel Start + Rel Başlangıç + + + Duration + Süre + + + Bits/s A + Bit/s A + + + Bits/s B + Bit/s B + + + Total Packets + Toplam Paket + + + Percent Filtered + Filtrelenmiş Yüzde + + + + ConversationDialog + + Follow Stream… + Akışı takip edin… + + + Follow a TCP or UDP stream. + Bir TCP veya UDP akışını izleyin. + + + Graph… + Grafik… + + + Graph a TCP conversation. + Bir TCP konuşmasının grafiğini çizin. + + + + ConversationHashTablesDialog + + Dialog + Diyalog + + + Conversation Hash Tables + Konuşma Karma Tabloları + + + + CopyFromProfileButton + + Copy from + Şuradan kopyala + + + Copy entries from another profile. + Girişleri başka bir profilden kopyalayın. + + + System default + Sistem öntanımlıı + + + + CredentialsDialog + + Wireshark - Credentials + Wireshark - Kimlik Bilgileri + + + Credentials + Kimlik bilgileri + + + + CredentialsModel + + Click to select the packet + Paketi seçmek için tıklayın + + + Click to select the packet with username + Kullanıcı adı ile paketi seçmek için tıklayın + + + Username not available + Kullanıcı adı geçerli değil + + + Packet No. + Paket No. + + + Protocol + İletişim Kuralı + + + Username + Kullanıcı adı + + + Additional Info + İlave Bilgi + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + Baytları Hex + ASCII Dump olarak kopyalayın + + + Copy packet bytes as a hex and ASCII dump. + Paket baytlarını hex ve ASCII dökümü olarak kopyalayın. + + + …as Hex Dump + …Hex Dump olarak + + + Copy packet bytes as a hex dump. + Paket baytlarını hex dökümü olarak kopyalayın. + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + …Hex Akışı olarak + + + Copy packet bytes as a stream of hex. + Paket baytlarını bir hex akışı olarak kopyalayın. + + + …as a Base64 String + …Base64 Dizesi olarak + + + Copy packet bytes as a base64 encoded string. + Paket baytlarını base64 kodlu bir dize olarak kopyalayın. + + + Copy packet bytes as application/octet-stream MIME data. + Paket baytlarını uygulama/sekizli akış MIME verileri olarak kopyalayın. + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + Bir protokol için diseksiyon davranışını değiştirin. + + + Remove this dissection behavior. + Bu diseksiyon davranışını kaldırın. + + + Copy this dissection behavior. + Bu diseksiyon davranışını kopyalayın. + + + Clear all dissection behaviors. + Tüm diseksiyon davranışlarını temizleyin. + + + Decode As… + Kodu Çöz… + + + Open + + + + + DecodeAsModel + + Match using this field + Bu alanı kullanarak eşleştir + + + Change behavior when the field matches this value + Alan bu değerle eşleştiğinde davranışı değiştir + + + Field value type (and base, if Integer) + Alan değeri türü (ve Tamsayı ise taban) + + + Current"Decode As" behavior + Geçerli "Kodu Farklı Çöz" davranışı + + + Default "Decode As" behavior + Varsayılan "Kodu Farklı Çöz" davranışı + + + String + Dize + + + Integer, base + Tamsayı, taban + + + unknown + bilinmeyen + + + <none> + <none> + + + GUID + KLAVUZ + + + Field + Alan + + + Value + Değer + + + Type + Biçim + + + Default + Ön tanımlı + + + Current + Mevcut + + + + DisplayFilterCombo + + Display filter selector + Filtre seçiciyi görüntüle + + + Select from previously used filters. + Daha önce kullanılan filtreler arasından seçim yapın. + + + + DisplayFilterEdit + + Display filter entry + Filtre girişini göster + + + Manage saved bookmarks. + Kaydedilmiş yer imlerini yönetin. + + + Display Filter Expression… + Filtre İfadesini Görüntüle… + + + Apply a display filter %1 <%2/> + Bir görüntüleme filtresi uygula %1 <%2/> + + + Enter a display filter %1 + Bir görüntüleme filtresi girin %1 + + + Clear display filter + Ekran filtresini temizle + + + Apply display filter + Görüntüleme filtresi uygula + + + Left align buttons + Sola hizalama düğmeleri + + + Apply a read filter %1 + %1 okuma filtresi uygula + + + Current filter: %1 + Geçerli filtre: %1 + + + Invalid filter: + Geçersiz filtre: + + + Save this filter + Bu filtreyi kaydet + + + Remove this filter + Bu filtreyi kaldır + + + Manage Display Filters + Görüntü Filtrelerini Yönet + + + Filter Button Preferences... + Filtre Düğmesi Tercihleri... + + + + DisplayFilterExpressionDialog + + Dialog + Diyalog + + + Select a field to start building a display filter. + Bir görüntü filtresi oluşturmaya başlamak için bir alan seçin. + + + Field Name + Alan Adı + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Alan adları listesinde arama yapın.</p></body></html> + + + Search: + Ara: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>İlişkiler, alanları belirli değerlerle sınırlamak için kullanılabilir. Her ilişki aşağıdakileri yapar:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">mevcut</span></p></td><td><p>Bu alanı içeren herhangi bir paketi eşleştirin</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Alanı belirli bir değerle karşılaştırın.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">içerir, eşleşir</span></p></td><td><p>Alanı bir dizeye (içerir) veya normal bir ifadeye (eşleşir) karşı kontrol edin</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Alanı belirli bir değer kümesiyle karşılaştırın</p></td></tr></table></body></html> + + + + + Relation + İlişki + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + Herhangi bir değer eşleşirse, varsayılan olarak sıra karşılaştırmaları ve içerir/eşleşir/içinde ilişkileri doğrudur. "tümü" niceleyicisi, testi bir çerçevedeki tüm değerlere uygulamak için kullanılabilir. + + + Quantifier + Niceleyici + + + Any + Herhangi biri + + + All + Tümü + + + Match against this value. + Bu değere karşı eşleştirin. + + + Value + Değer + + + If the field you have selected has a known set of valid values they will be listed here. + Seçtiğiniz alanın bilinen bir geçerli değerleri varsa bunlar burada listelenecektir. + + + Predefined Values + Önceden Tanımlanmış Değerler + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + Seçtiğiniz alan bir bayt aralığını kapsıyorsa (örneğin bir protokol seçtiyseniz), burada eşleşmeyi bir bayt aralığıyla sınırlayabilirsiniz. + + + Range (offset:length) + Aralık (ofset:uzunluk) + + + No display filter + Görüntüleme filtresi yok + + + <small><i>A hint.</i></small> + <small><i>Bir ipucu.</i></small> + + + Display Filter Expression + Filtre İfadesini Görüntüle + + + Select a field name to get started + Başlamak için bir alan adı seçin + + + Click OK to insert this filter + Bu filtreyi eklemek için Tamam'ı tıklayın + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + Diyalog + + + Search: + Ara: + + + Dissector Tables + Tespit ediciler Tabloları + + + + DissectorTablesProxyModel + + Table Type + Tablo Tipi + + + String + Dize + + + Dissector Description + Tespit edici Tanımı + + + Integer + Tamsayı + + + Protocol + İletişim Kuralı + + + Short Name + Kısa İsim + + + Table Name + Tablo İsmi + + + Selector Name + Seçici Adı + + + + EnabledProtocolsDialog + + Dialog + Diyalog + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>Bir protokolü devre dışı bırakmak, daha yüksek katman protokollerinin görüntülenmesini engeller</i></small> + + + Search: + Ara: + + + in + de + + + Enable All + Tümünü Etkinleştir + + + Disable All + Tümünü Pasifleştir + + + Invert + Evir + + + Enabled Protocols + Etkin Protokoller + + + Everywhere + Her yerde + + + Only Protocols + Yalnızca Protokoller + + + Only Description + Sadece Açıklama + + + Only enabled protocols + Yalnızca etkin protokoller + + + Only disabled protocols + Yalnızca devre dışı bırakılmış protokoller + + + any protocol + herhangi bir protokol + + + non-heuristic protocols + sezgisel protokoller + + + heuristic protocols + buluşsal protokoller + + + + EnabledProtocolsModel + + Protocol + İletişim Kuralı + + + Description + Açıklama + + + + EndpointDataModel + + Address + Adres + + + Port + Bağlantı noktası + + + Packets + Paketler + + + Bytes + Bayt + + + Tx Packets + Tx Paketleri + + + Tx Bytes + Tx Baytları + + + Rx Packets + Rx Paketleri + + + Rx Bytes + Rx Baytları + + + Country + Ülke + + + City + Şehir + + + Latitude + Enlem + + + Longitude + Boylam + + + AS Number + AS Numarası + + + AS Organization + AS Organizasyonu + + + Total Packets + Toplam Paket + + + Percent Filtered + Filtrelenmiş Yüzde + + + + EndpointDialog + + Map + Harita + + + Draw IPv4 or IPv6 endpoints on a map. + Bir harita üzerinde IPv4 veya IPv6 uç noktaları çizin. + + + Open in browser + Tarayıcıda aç + + + Save As… + Farklı Kaydet… + + + Map file error + Harita dosyası hatası + + + Save Endpoints Map + Uç Nokta Haritasını Kaydet + + + Failed to save map file %1. + %1 harita dosyası kaydedilemedi. + + + + EthernetAddressModel + + Type + Biçim + + + Name + Ad + + + Address + Adres + + + All entries + Bütün girdiler + + + Hosts + Sunucular + + + Ethernet Addresses + Ethernet Adresleri + + + Ethernet Manufacturers + Ethernet Üreticileri + + + Ethernet Well-Known Addresses + Ethernet'in Tanınmış Adresleri + + + + ExpertInfoDialog + + Dialog + Diyalog + + + <small><i>A hint.</i></small> + <small><i>Bir ipucu.</i></small> + + + Limit to Display Filter + Görüntü Filtresini Sınırla + + + Group by summary + Özete göre gruplandır + + + Search expert summaries. + Uzman özetlerini arayın. + + + Search: + Ara: + + + Show… + Show... + Göster… + + + Error + Hata + + + Show error packets. + Hata paketlerini göster. + + + Warning + Uyarı + + + Show warning packets. + Uyarı paketlerini göster. + + + Note + Not + + + Show note packets. + Not paketlerini göster. + + + Chat + Sohbet + + + Show chat packets. + Sohbet paketlerini göster. + + + Comment + Yorum + + + Show comment packets. + Yorum paketlerini göster. + + + Expert Information + Uzman Bilgileri + + + Collapse All + Hepsini Daralt + + + Expand All + Tümünü Genişlet + + + Capture file closed. + Yakalama dosyası kapatıldı. + + + No display filter + Görüntüleme filtresi yok + + + No display filter set. + Ekran filtresi ayarlanmadı. + + + Limit information to "%1". + Bilgileri "%1" ile sınırlayın. + + + Display filter: "%1" + Görüntüleme filtresi: "%1" + + + + ExpertInfoProxyModel + + Packet + Paket + + + Severity + Önem + + + Summary + Özet + + + Group + Grup + + + Protocol + İletişim Kuralı + + + Count + Miktar + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Paket Diseksiyonlarını Dışa Aktar + + + Export As: + Export as: + Farklı Dışa Aktar: + + + Plain text (*.txt) + Düz metin (*.txt) + + + Comma Separated Values - summary (*.csv) + Virgülle Ayrılmış Değerler - özet (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML - özet (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PDML - ayrıntılar (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + C Diziler - baytlar (*.c, *.h) + + + + ExportObjectDialog + + Dialog + Diyalog + + + Content Type: + İçerik türü: + + + Searching for objects + Nesneleri arama + + + Text Filter: + Metin Filtresi: + + + Only display entries containing this string + Yalnızca bu dizeyi içeren girişleri görüntüle + + + Preview + Önizleme + + + All Content-Types + Tüm İçerik Türleri + + + Export + Dışarı aktar + + + %1 object list + %1 nesne listesi + + + Save Object As… + Nesneyi Farklı Kaydet… + + + Save All Objects In… + Tüm Nesneleri İçeri Kaydet… + + + + ExportObjectModel + + Packet + Paket + + + Hostname + Ana Makine Adı + + + Content Type + İçerik Türü + + + Size + Boyut + + + Filename + Dosya Adı + + + + ExportPDUDialog + + Dialog + Diyalog + + + Display filter: + Ekran filtresi: + + + + ExtArgSelector + + Reload data + Verileri yeniden yükle + + + + ExtcapArgumentFileSelection + + Clear + Temiz + + + All Files ( + Tüm Dosyalar ( + + + Open File + Dosyayı Aç + + + Select File + Dosya Seçin + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + Arayüz Seçenekleri + + + Start + Başla + + + Save + Kaydet + + + Default + Ön tanımlı + + + Restore default value of the item + Öğenin varsayılan değerini geri yükle + + + Extcap Help cannot be found + Extcap Yardımı bulunamıyor + + + The help for the extcap interface %1 cannot be found. Given file: %2 + %1 extcap arabirimi için yardım bulunamadı. Verilen dosya: %2 + + + Save parameter(s) on capture start + Yakalama başlangıcında parametreyi/parametreleri kaydet + + + + FieldFilterEdit + + Display filter entry + Filtre girişini göster + + + Enter a field %1 + %1 alanını girin + + + Invalid filter: + Geçersiz filtre: + + + + FileSetDialog + + Dialog + Diyalog + + + Directory: + Dizin: + + + No files in Set + Kümede dosya yok + + + No capture loaded + Yakalama yüklenmedi + + + %Ln File(s) in Set + %1 File%2 in Set + + + + + + + FilesetEntryModel + + Open this capture file + Bu yakalama dosyasını aç + + + Filename + Dosya Adı + + + Created + Oluşturuldu + + + Modified + Düzenle + + + Size + Boyut + + + + FilterAction + + Selected + Seçili + + + Not Selected + Seçili değil + + + …and Selected + …ve Seçilmiş + + + …or Selected + …veya Seçilmiş + + + …and not Selected + …ve Seçilmedi + + + …or not Selected + …veya Seçilmedi + + + + FilterDialog + + Dialog + Diyalog + + + Create a new filter. + Yeni bir filtre oluşturun. + + + Remove this filter. + Remove this profile. + Bu filtreyi kaldırın. + + + Copy this filter. + Copy this profile. + Bu filtreyi kopyalayın. + + + Capture Filters + Yakalama Filtreleri + + + Display Filters + Ekran Filtreleri + + + Open + + + + New capture filter + This text is automatically filled in when a new filter is created + Yeni yakalama filtresi + + + New display filter + This text is automatically filled in when a new filter is created + Yeni ekran filtresi + + + + FilterExpressionFrame + + Frame + Çerçeve + + + Filter Buttons Preferences… + Filtre Düğmeleri Tercihleri… + + + Label: + Etiket: + + + Enter a description for the filter button + Filtre düğmesi için bir açıklama girin + + + Filter: + Süzgeç: + + + Enter a filter expression to be applied + Uygulanacak bir filtre ifadesi girin + + + Comment: + Yorum: + + + Enter a comment for the filter button + Filtre düğmesi için bir yorum girin + + + Missing label. + Eksik etiket. + + + Missing filter expression. + Eksik filtre ifadesi. + + + Invalid filter expression. + Geçersiz filtre ifadesi. + + + + FilterExpressionToolBar + + Filter Button Preferences... + Filtre Düğmesi Tercihleri... + + + Edit + Düzenle + + + Disable + Devre Dışı + + + Remove + Kaldır + + + + FilterListModel + + Filter Name + Filtre Adı + + + Filter Expression + Filtre İfadesi + + + + FindLineEdit + + Textual Find + Metinsel Bul + + + Regular Expression Find + Normal İfade Bul + + + + FirewallRulesDialog + + Create rules for + İçin kurallar oluşturun + + + Inbound + Gelen + + + Deny + Reddet + + + Firewall ACL Rules + Güvenlik Duvarı ACL Kuralları + + + Copy + Kopyala + + + IPv4 source address. + IPv4 kaynak adresi. + + + IPv4 destination address. + IPv4 hedef adresi. + + + Source port. + Kaynak bağlantı noktası. + + + Destination port. + Hedef bağlantı noktası. + + + IPv4 source address and port. + IPv4 kaynak adresi ve bağlantı noktası. + + + IPv4 destination address and port. + IPv4 hedef adresi ve bağlantı noktası. + + + MAC source address. + MAC kaynak adresi. + + + MAC destination address. + MAC hedef adresi. + + + Text file (*.txt);;All Files ( + Metin dosyası (*.txt);;Tüm Dosyalar ( + + + Warning + Uyarı + + + Unable to save %1 + %1 kaydedilemedi + + + + FolderListModel + + "File" dialogs + "Dosya" diyalogları + + + capture files + yakalama dosyaları + + + Temp + Geçici + + + untitled capture files + başlıksız yakalama dosyaları + + + Personal configuration + Kişisel yapılandırma + + + Global configuration + Genel yapılandırma + + + dfilters, preferences, ethers, … + filtreler, tercihler, eterler, … + + + dfilters, preferences, manuf, … + filtreler, tercihler, üretici, … + + + System + Sistem + + + ethers, ipxnets + eterler, ipxnet'ler + + + Program + Uygulama + + + program files + program dosyaları + + + Personal Plugins + Kişisel Eklentiler + + + binary plugins + ikili eklentiler + + + Global Plugins + Genel Eklentiler + + + Personal Lua Plugins + Kişisel Lua Eklentileri + + + Global Lua Plugins + Genel Lua Eklentileri + + + Lua scripts + Lua betikleri + + + Personal Extcap path + Kişisel Excap yolu + + + external capture (extcap) plugins + harici yakalama (extcap) eklentileri + + + Global Extcap path + Genel Extcap yolu + + + MaxMind DB path + MaxMind DB yolu + + + MaxMind DB database search path + MaxMind DB veritabanı arama yolu + + + MIB/PIB path + MIB/PIB yolu + + + SMI MIB/PIB search path + SMI MIB/PIB arama yolu + + + macOS Extras + macOS Ekstraları + + + Extra macOS packages + Ekstra macOS paketleri + + + Name + Ad + + + Location + Konum + + + Typical Files + Tipik Dosyalar + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + Bu Akışı Filtrele + + + Print + Yazdır + + + ASCII + ASCII + + + C Arrays + C Dizileri + + + EBCDIC + EBCDIC + + + Hex Dump + Onaltılık Dökümü + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + Ham + + + Save as… + Farklı kaydet… + + + Back + Geri Dön + + + Packet %1. + Paket %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + + + + + %Ln turn(s). + + + + + + Click to select. + Seçmek için tıkla. + + + Regex Find: + Normal İfade Bul: + + + No capture file. + Yakalama dosyası yok. + + + Please make sure you have a capture file opened. + Lütfen bir yakalama dosyanızın açık olduğundan emin olun. + + + Error following stream. + Akış takip edilirken hata oluştu. + + + Capture file invalid. + Yakalama dosyası geçersiz. + + + Please make sure you have a %1 packet selected. + Lütfen %1 paketinin seçili olduğundan emin olun. + + + %1 stream not found on the selected packet. + Seçili pakette %1 akışı bulunamadı. + + + Entire conversation (%1) + Tüm konuşma (%1) + + + Follow %1 Stream (%2) + %1 Akışı izle (%2) + + + Error creating filter for this stream. + Bu akış için filtre oluşturulurken hata oluştu. + + + Save Stream Content As… + Akış İçeriğini Farklı Kaydet… + + + [Stream output truncated] + [Akış çıkışı kesildi] + + + %Ln total stream(s). + + + + + + Max sub stream ID for the selected stream: %Ln + + + + + + File closed. + Dosya kapandı. + + + Follow Stream + Akışı Takip Et + + + Hint. + İpucu. + + + Show data as + Show and save data as + Verileri şu şekilde göster + + + Stream + Akış + + + Substream + Alt akış + + + Find: + Bul: + + + Find &Next + Sonrakini &Bul + + + + FontColorPreferencesFrame + + Frame + Çerçeve + + + Main window font: + Ana pencere yazı tipi: + + + Select Font + Yazı Tipini Seç + + + Colors: + Renkler: + + + System Default + Sistem Varsayılanı + + + Solid + Düz + + + Sample ignored packet text + Örnek yok sayılan paket metni + + + Sample marked packet text + Örnek işaretli paket metni + + + Sample active selected item + Örnek etkin seçili öğe + + + Style: + Biçim: + + + Gradient + Değişim + + + Sample inactive selected item + Örnek etkin olmayan seçili öğe + + + Sample "Follow Stream" client text + Örnek "Yayını İzle" istemci metni + + + Sample "Follow Stream" server text + Örnek "Yayını İzle" sunucu metni + + + Sample valid filter + Örnek geçerli filtre + + + Sample invalid filter + Örnek geçersiz filtre + + + Sample warning filter + Sample deprecated filter + Örnek uyarı filtresi + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + Örnek GIF sorgu paketlerinin jumbo pencere boyutları vardır + + + Lazy badgers move unique waxy jellyfish packets + Tembel porsuklar benzersiz mumsu denizanası paketlerini taşır + + + Font + Yazıtipi + + + + FunnelStringDialog + + Dialog + Diyalog + + + + FunnelTextDialog + + Dialog + Diyalog + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>Bir metin veya normal bir ifade girin. Yukarıda vurgulanacaktır.</p></body></html> + + + Highlight: + Vurgu: + + + + GsmMapSummaryDialog + + Dialog + Diyalog + + + GSM MAP Summary + GSM HARİTA Özeti + + + File + Dosya + + + Name + Ad + + + Length + Uzunluk + + + Format + Biçim + + + Snapshot length + Anlık görüntü uzunluğu + + + Data + Veri + + + First packet + İlk paket + + + Last packet + Son paket + + + Elapsed + Geçen + + + Packets + Paketler + + + Invokes + Çağırır + + + Total number of Invokes + Toplam Çağrı Sayısı + + + Average number of Invokes per second + Saniyedeki ortalama Çağrı sayısı + + + Total number of bytes for Invokes + Çağrılar için toplam bayt sayısı + + + Average number of bytes per Invoke + Çağrı başına ortalama bayt sayısı + + + Return Results + Sonuçları Döndür + + + Total number of Return Results + Toplam İade Sonuçları Sayısı + + + Average number of Return Results per second + Saniye başına ortalama Dönüş Sonuçları sayısı + + + Total number of bytes for Return Results + Dönüş Sonuçları için toplam bayt sayısı + + + Average number of bytes per Return Result + Dönüş Sonucu başına ortalama bayt sayısı + + + Totals + Toplam + + + Total number of GSM MAP messages + GSM HARİTA mesajlarının toplam sayısı + + + Average number of GSM MAP messages per second + Saniyedeki ortalama GSM HARİTA mesajı sayısı + + + Total number of bytes for GSM MAP messages + GSM HARİTA mesajları için toplam bayt sayısı + + + Average number of bytes per GSM MAP message + GSM HARİTA mesajı başına ortalama bayt sayısı + + + + IOConsoleDialog + + Dialog + + + + Enter code + + + + Evaluate + + + + Clear + Temiz + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + Diyalog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Değerli ve şaşırtıcı zaman kazandıran klavye kısayolları</h3> +<tablo><tbody> + +<tr><th>+</th><td>Yakınlaştır</td></th> +<tr><th>-</th><td>Uzaklaştır</td></th> +<tr><th>x</th><td>X ekseninde yakınlaştır</td></th> +<tr><th>X</th><td>X ekseninde uzaklaştırın</td></th> +<tr><th>y</th><td>Y ekseninde yakınlaştır</td></th> +<tr><th>Y</th><td>Y ekseninde uzaklaştır</td></th> +<tr><th>0</th><td>Grafiği ilk durumuna sıfırlayın</td></th> + +<tr><th>→</th><td>10 piksel sağa hareket ettir</td></th> +<tr><th>←</th><td>10 piksel sola git</td></th> +<tr><th>↑</th><td>10 piksel yukarı git</td></th> +<tr><th>↓</th><td>10 piksel aşağı git</td></th> +<tr><th><i>Üst Karakter+</i>→</th><td>1 piksel sağa git</td></th> +<tr><th><i>Üst Karakter+</i>←</th><td>1 piksel sola git</td></th> +<tr><th><i>Üst Karakter+</i>↑</th><td>1 piksel yukarı git</td></th> +<tr><th><i>Üst Karakter+</i>↓</th><td>1 piksel aşağı git</td></th> + +<tr><th>g</th><td>İmlecin altındaki pakete git</td></th> + +<tr><th>z</th><td>Fare sürükleme / yakınlaştırma arasında geçiş yapın</td></th> +<tr><th>t</th><td>Yakalama / oturum süresi kaynağı arasında geçiş yap</td></th> +<tr><th>Boşluk</th><td>Artı işaretlerini aç/kapat</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + Bu grafiği kaldırın. + + + Add a new graph. + Yeni bir grafik ekleyin. + + + Duplicate this graph. + Bu grafiği çoğaltın. + + + Clear all graphs. + Tüm grafikleri temizle. + + + Move this graph upwards. + Bu grafiği yukarı doğru götür. + + + Move this graph downwards. + Bu grafiği aşağı doğru götür. + + + Mouse + Fare + + + Drag using the mouse button. + Fare düğmesini kullanarak sürükleyin. + + + drags + sürükler + + + Select using the mouse button. + Fare düğmesini kullanarak seçin. + + + zooms + yakınlaştırır + + + Interval + Aralık + + + Time of day + Günün zamanı + + + Log scale + Günlük ölçeği + + + Automatic update + + + + Enable legend + + + + Reset + Sıfırla + + + Reset Graph + Grafiği Sıfırla + + + Reset the graph to its initial state. + Grafiği ilk durumuna sıfırlayın. + + + 0 + 0 + + + Zoom In + Yakınlaş + + + + + + + + + Zoom Out + Uzaklaş + + + - + - + + + Move Up 10 Pixels + 10 Piksel Yukarı Taşı + + + Up + Üst + + + Move Left 10 Pixels + 10 Piksel Sola Taşı + + + Left + Sol + + + Move Right 10 Pixels + 10 Piksel Sağa Taşı + + + Right + Sağa + + + Move Down 10 Pixels + 10 Piksel Aşağı Taşı + + + Down + Aşağı + + + Move Up 1 Pixel + 1 Piksel Yukarı Taşı + + + Shift+Up + Shift+Yukarı + + + Move Left 1 Pixel + Sola 1 Piksel Taşı + + + Shift+Left + Shift+Sol + + + Move Right 1 Pixel + 1 Piksel Sağa Taşı + + + Shift+Right + Shift+Sağ + + + Move Down 1 Pixel + 1 Piksel Aşağı Taşı + + + Move down 1 Pixel + Move down 1 pixel + 1 Piksel aşağı taşı + + + Shift+Down + Shift+Aşağı + + + Go To Packet Under Cursor + İmleç Altındaki Pakete Git + + + Go to packet currently under the cursor + İmlecin altındaki pakete git + + + G + G + + + Drag / Zoom + Sürükle / Yakınlaştır + + + Toggle mouse drag / zoom behavior + Fare sürükle / yakınlaştırma davranışını değiştir + + + Z + Z + + + Capture / Session Time Origin + Yakalama / Oturum Süresi Menşei + + + Toggle capture / session time origin + Yakalama / oturum zamanı kaynağını aç/kapat + + + T + T + + + Crosshairs + Artılar + + + Toggle crosshairs + Artı işaretlerini aç/kapat + + + Space + Boşluk + + + Zoom In X Axis + X Eksenini Yakınlaştır + + + X + X + + + Zoom Out X Axis + X Eksenini Uzaklaştır + + + Shift+X + Shift+X + + + Zoom In Y Axis + A Eksenini Yakınlaştır + + + Y + Y + + + Zoom Out Y Axis + A Eksenini Uzaklaştır + + + Shift+Y + Shift+Y + + + 1 sec + 1 saniye + + + 10 sec + 10 saniye + + + 1 min + 1 dakika + + + 10 min + 10 dakika + + + Time (s) + Süre () + + + I/O Graphs + G/Ç Grafikleri + + + Save As… + Farklı Kaydet… + + + Copy + Kopyala + + + Copy graphs from another profile. + Grafikleri başka bir profilden kopyalayın. + + + 1 ms + 1 ms + + + 2 ms + 2 ms + + + 5 ms + 5 ms + + + 10 ms + 10 ms + + + 20 ms + 20 ms + + + 50 ms + 50 ms + + + 100 ms + 100 ms + + + 200 ms + 200 ms + + + 500 ms + 500 ms + + + 2 sec + 2 san + + + 5 sec + 5 san + + + Wireshark I/O Graphs: %1 + Wireshark G/Ç Grafikleri: %1 + + + Filtered packets + Filtrelenmiş paketler + + + Filtered events + + + + All Packets + Tüm Paketler + + + TCP Errors + TCP Hataları + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + Ayrıntılar için grafiğin üzerine gelin. + + + No packets in interval + Aralıkta paket yok + + + No events in interval + + + + Click to select packet + Paket seçmek için tıklayın + + + Packet + Paket + + + Click to select event + + + + Event + Olay + + + %1 (%2s%3). + %1 (%2s%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Yakınlaştırmak için bırakın, x = %1 ila %2, y = %3 ila %4 + + + Unable to select range. + Aralık seçilemiyor. + + + Click to select a portion of the graph. + Grafiğin bir bölümünü seçmek için tıklayın. + + + Portable Document Format (*.pdf) + Taşınabilir Belge Formatı (*.pdf) + + + Portable Network Graphics (*.png) + Taşınabilir Ağ Grafikleri (* .png) + + + Windows Bitmap (*.bmp) + Windows Bit Eşlem (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG Dosya Değişim Biçimi (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + Virgülle Ayrılmış Değerler (*.csv) + + + Save Graph As… + Grafiği Farklı Kaydet… + + + + Iax2AnalysisDialog + + Dialog + Diyalog + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">İleri</span></p><p><span style=" font-size:medium; font-weight:600;">Ters</span></p></body></html> + + + Forward + İleri + + + Packet + Paket + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Titreşim (ms) + + + Bandwidth + Bant genişliği + + + Status + Durum + + + Length + Uzunluk + + + Reverse + Ters çevir + + + Graph + Grafik + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>İleri sapma değerlerini gösterin veya gizleyin.</p></body></html> + + + Forward Jitter + İleri Sarsıntı + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>İleri fark değerlerini göster veya gizle.</p></body></html> + + + Forward Difference + İleri Fark + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>Ters titreşim değerlerini gösterin veya gizleyin.</p></body></html> + + + Reverse Jitter + Ters Titreşim + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>Ters fark değerlerini göster veya gizle.</p></body></html> + + + Reverse Difference + Ters Fark + + + <small><i>A hint.</i></small> + <small><i>Bir ipucu.</i></small> + + + Audio + Ses + + + Save the audio data for both channels. + Her iki kanal için ses verilerini kaydedin. + + + Forward Stream Audio + İleri Akış Ses + + + Save the forward stream audio data. + İleri akış ses verilerini kaydedin. + + + Reverse Stream Audio + Ters Akış Sesi + + + Save the reverse stream audio data. + Ters akış ses verilerini kaydedin. + + + CSV + CSV + + + Save both tables as CSV. + Her iki tabloyu da CSV olarak kaydedin. + + + Forward Stream CSV + İleri Akış CSV'si + + + Save the forward table as CSV. + Yönlendirme tablosunu CSV olarak kaydedin. + + + Reverse Stream CSV + Ters Akış CSV'si + + + Save the reverse table as CSV. + Ters tabloyu CSV olarak kaydedin. + + + Save Graph + Grafiği Kaydet + + + Save the graph image. + Grafik görüntüsünü kaydedin. + + + Go to Packet + Pakete Git + + + Select the corresponding packet in the packet list. + Paket listesinden ilgili paketi seçin. + + + G + G + + + Next Problem Packet + Sonraki Sorun Paketi + + + Go to the next problem packet + Sonraki sorun paketine git + + + N + N + + + IAX2 Stream Analysis + IAX2 Akış Analizi + + + Unable to save RTP data. + RTP verileri kaydedilemiyor. + + + Please select an IAX2 packet. + Lütfen bir IAX2 paketi seçin. + + + G: Go to packet, N: Next problem packet + G: Pakete git, N: Sonraki sorun paketi + + + Portable Document Format (*.pdf) + Taşınabilir Belge Formatı (*.pdf) + + + Portable Network Graphics (*.png) + Taşınabilir Ağ Grafikleri (* .png) + + + Windows Bitmap (*.bmp) + Windows Bit Eşlem (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG Dosya Değişim Biçimi (*.jpeg *.jpg) + + + Save Graph As… + Grafiği Farklı Kaydet… + + + Can't save in a file: Wrong length of captured packets. + Dosyaya kaydedilemiyor: Yakalanan paketlerin uzunluğu yanlış. + + + Can't save in a file: File I/O problem. + Bir dosyaya kaydedilemiyor: Dosya G/Ç sorunu. + + + Save forward stream audio + İleri akış sesini kaydet + + + Save reverse stream audio + Ters akış sesini kaydet + + + Save audio + Sesi kaydet + + + Sun Audio (*.au) + Güneş Ses (*.au) + + + ;;Raw (*.raw) + ;;Ham (*.raw) + + + Warning + Uyarı + + + Unable to save in that format + Bu biçimde kaydedilemiyor + + + Unable to save %1 + %1 kaydedilemedi + + + Saving %1… + %1 kaydediliyor… + + + Analyzing IAX2 + IAX2'yi analiz etme + + + Save forward stream CSV + İleri akış CSV'sini kaydet + + + Save reverse stream CSV + Ters akış CSV'sini kaydet + + + Save CSV + CSV'yi kaydet + + + Comma-separated values (*.csv) + Virgülle ayrılmış değerler (*.csv) + + + + ImportTextDialog + + File: + Dosya: + + + Set name of text file to import + İçe aktarılacak metin dosyasının adını ayarlayın + + + Browse for text file to import + İçe aktarılacak metin dosyasına göz atın + + + Browse… + Browse... + Gözat… + + + Hex Dump + Onaltılık Dökümü + + + Import a standard hex dump as exported by Wireshark + Wireshark tarafından dışa aktarıldığı gibi standart bir altıgen dökümü içe aktarın + + + Offsets in the text file are in octal notation + Metin dosyasındaki ofsetler sekizli gösterimde + + + Octal + Sekizli + + + Offsets: + Ofsetler: + + + Offsets in the text file are in hexadecimal notation + Metin dosyasındaki ofsetler onaltılık gösterimdedir + + + Hexadecimal + Onaltılık + + + Offsets in the text file are in decimal notation + Metin dosyasındaki ofsetler ondalık gösterimdedir + + + Decimal + Onluk + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + <html><head/><body><p>Onaltılık bayt gibi görünse bile, bir hex+ASCII satırının sonunda ASCII gösteriminin başlangıcını tespit etmek için ek işlem yapılıp yapılmayacağı.</p><p>Onaltılık döküm ASCII içermiyorsa etkinleştirmeyin.</p></body></html> + + + ASCII identification: + ASCII tanımlaması: + + + Regular Expression + Düzenli İfade + + + Import a file formatted according to a custom regular expression + Özel bir normal ifadeye göre biçimlendirilmiş bir dosyayı içe aktarın + + + Packet format regular expression + Paket biçimi düzenli ifadesi + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + <html><head/><body><p>İçe aktarılacak verileri tanımlayan adlandırılmış gruplarla dosyada tek bir paket yakalayan Perl uyumlu normal ifade. Bağlantılar ^ ve $ ayrıca yeni satırlardan önce/sonra eşleşir </p><p>Zorunlu yalnızca bir veri grubudur, ayrıca zaman, dizin ve sıra numarası da desteklenir.</p><p>Düzenli ifade işaretleri: DUPNAMES, MULTILINE ve NOEMPTY</p><p> p></body></html> + + + This is regexHintLabel, it will be set to default_regex_hint + Bu normal ifade İpucu Etiketidir, varsayılan_normal_ifade_ipucuna ayarlanacaktır + + + Data encoding: + Veri kodlaması: + + + How data is encoded + Veriler nasıl kodlanır + + + encodingRegexExample + kodlamaRegexÖrneği + + + List of characters indicating incoming packets + Gelen paketleri gösteren karakter listesi + + + iI< + gI< + + + List of characters indicating outgoing packets + Giden paketleri gösteren karakter listesi + + + oO> + oO> + + + Timestamp format: + Zaman damgası biçimi: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Dosyanın, paketin yönünü (gelen veya giden) gösteren bilgileri içerip içermediği. + + + Direction indication: + Yön göstergesi: + + + ExportPDU + PDU Dışa aktarma + + + IP version: + IP sürümü: + + + Interface name: + Arayüz adı: + + + The name of the interface to write to the import capture file + İçe aktarma yakalama dosyasına yazılacak arabirimin adı + + + Fake IF, Import from Hex Dump + Sahte EĞER, Hex Dump'tan İçe Aktar + + + Maximum frame length: + Azami çerçeve uzunluğu: + + + Encapsulation + Kapsülleme + + + The text file has no offset + Metin dosyasında ofset yok + + + None + Yok + + + <small><i>recommended regex:</small></i> + <small><i>önerilen normal ifade:</small></i> + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + Metin dosyasındaki zaman damgalarının ayrıştırılacağı biçim (ör. %H:%M:%S.). Biçim belirteçleri strptime(3)'e dayalıdır + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + <html><head/><body><p>Metin dosyasındaki zaman damgalarının ayrıştırılacağı biçim (ör. %H:%M:%S.%f).</p><p>Biçim belirteçleri, ikinci kesirler için %f eklenmesiyle strptime(3)'e dayanır. %f'nin kesinliği, uzunluğundan belirlenir.</p></body></html> + + + %H:%M:%S.%f + %H:%M:%S.%f + + + timestampExampleLabel + zamandamgasıÖrnekEtiket + + + Encapsulation Type: + Kapsülleme Türü: + + + Encapsulation type of the frames in the import capture file + İçe aktarma yakalama dosyasındaki çerçevelerin kapsülleme türü + + + Prefix each frame with an Ethernet and IP header + Her kareye bir Ethernet ve IP başlığı ile önek ekleyin + + + IP + IP + + + Prefix each frame with an Ethernet, IP and UDP header + Her çerçeveye bir Ethernet, IP ve UDP başlığı ile önek ekleyin + + + Prefix each frame with an Ethernet, IP and TCP header + Her çerçeveye bir Ethernet, IP ve TCP başlığı ile önek ekleyin + + + Prefix each frame with an Ethernet, IP and SCTP header + Her çerçeveye bir Ethernet, IP ve SCTP başlığı ile önek ekleyin + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + Her çerçeveye bir Ethernet, IP ve SCTP (DATA) başlığı ekleyin + + + Source address: + Kaynak adresi: + + + Destination address: + Hedef adresi: + + + Dissector + Tespit edici + + + The IP protocol ID for each frame + Her çerçeve için IP protokolü kimliği + + + The IP source address for each frame + Her çerçeve için IP kaynak adresi + + + The IP destination address for each frame + Her çerçeve için IP hedef adresi + + + The UDP, TCP or SCTP source port for each frame + Her çerçeve için UDP, TCP veya SCTP kaynak bağlantı noktası + + + The SCTP DATA payload protocol identifier for each frame + Her çerçeve için SCTP DATA veri yükü protokolü tanımlayıcısı + + + The UDP, TCP or SCTP destination port for each frame + Her çerçeve için UDP, TCP veya SCTP hedef bağlantı noktası + + + Prefix each frame with an Ethernet header + Her kareye bir Ethernet başlığı ile önek ekleyin + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + Protokol (dec): + + + Leave frames unchanged + Çerçeveleri değiştirmeden bırak + + + No dummy header + Sahte başlık yok + + + Tag: + İşaret: + + + UDP + UDP + + + Source port: + Kaynak bağlantı noktası: + + + The Ethertype value of each frame + Her çerçevenin Ethertype değeri + + + TCP + TCP + + + The SCTP verification tag for each frame + Her çerçeve için SCTP doğrulama etiketi + + + Destination port: + Hedef bağlantı noktası: + + + Ethertype (hex): + Etertipi (hex): + + + SCTP (Data) + SCTP (Veri) + + + The dissector to use for each frame + Her çerçeve için kullanılacak disektör + + + The IP Version to use for the dummy IP header + Sahte IP başlığı için kullanılacak IP Sürümü + + + The maximum size of the frames to write to the import capture file (max 256kiB) + İçe aktarma yakalama dosyasına yazılacak çerçevelerin maksimum boyutu (azami 256kiB) + + + Supported fields are data, dir, time, seqno + Desteklenen alanlar data, dir, time, seqno + + + Missing capturing group data (use (? + Eksik yakalama grubu verileri (kullanın (? + + + Import From Hex Dump + Hex Dump'tan İçe Aktar + + + Import + İçe Aktar + + + Import Text File + Metin Dosyasını İçe Aktar + + + + InterfaceFrame + + Frame + Çerçeve + + + Wired + Kablolu + + + AirPCAP + AirPCAP + + + Pipe + Boru + + + STDIN + STDIN + + + Bluetooth + Bluetooth + + + Wireless + Kablosuz + + + Dial-Up + Çeviemeli-Bağlantı + + + USB + USB + + + External Capture + Harici Yakalama + + + Virtual + Sanal + + + Remote interfaces + Uzak arayüzler + + + Show hidden interfaces + Gizli arayüzleri göster + + + External capture interfaces disabled. + Harici yakalama arayüzleri devre dışı bırakıldı. + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + <p>Paket yakalama sürücüsü yüklü olmadığı için yerel arabirimler kullanılamıyor.</p><p>Bu sorunu <a href="https://npcap.com/">Nccap</a>.yükleyerek düzeltebilirsiniz.</p> + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + <p>Paket yakalama sürücüsü yüklenmediğinden yerel arayüzler kullanılamıyor.</p><p>Npcap veya <pre>net kuruluysa <pre>net start npcap</pre>'i çalıştırarak bunu düzeltebilirsiniz. WinPcap kuruluysa npf</pre>'yi başlatın. Her iki komut da Yönetici olarak çalıştırılmalıdır.</p> + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + <p>Yerel arayüzlerde yakalama izniniz yok.</p><p>Bunu <a href="file://%1">ChmodBPF'yi yükleyerek</a> düzeltebilirsiniz.</p > + + + You don't have permission to capture on local interfaces. + Yerel arabirimlerde yakalama izniniz yok. + + + No interfaces found. + Arayüz bulunamadı. + + + Interfaces not loaded (due to preference). Go to Capture + Arayüzler yüklenmedi (tercih nedeniyle). Yakalamaya Git + + + Start capture + Yakalamayı başlat + + + Hide Interface + Arayüzü Gizle + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + Görüntülenecek arayüz yok. %1 arayüz gizlendi. + + + + InterfaceToolbar + + Frame + Çerçeve + + + Select interface + Arayüz seçin + + + Interface + Arayüz + + + + InterfaceToolbarLineEdit + + Apply changes + Değişiklikleri uygula + + + + InterfaceTreeModel + + Show + Göster + + + Friendly Name + Dost İsim + + + Interface Name + Arayüz Adı + + + No interfaces found. + Arayüz bulunamadı. + + + This version of Wireshark was built without packet capture support. + Wireshark'ın bu sürümü, paket yakalama desteği olmadan oluşturulmuştur. + + + Local Pipe Path + Dahili Boru Yolu + + + Comment + Yorum + + + Link-Layer Header + Bağlantı Katmanı Başlığı + + + Promiscuous + Karışık + + + Snaplen (B) + Snaplen (B) + + + Buffer (MB) + Arabellek (MB) + + + Monitor Mode + Monitör Modu + + + Capture Filter + Yakalama Filtresi + + + Addresses + Adresler + + + Address + Adres + + + Extcap interface: %1 + Extcap arayüzü: %1 + + + No addresses + Adres yok + + + No capture filter + Yakalama filtresi yok + + + Capture filter + Yakalama filtresi + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + LBT-RM Taşıma İstatistikleri + + + Sources + Kaynaklar + + + Address/Transport + Adres/Aktarım + + + Data frames + Veri çerçeveleri + + + Data bytes + Veri baytları + + + Data frames/bytes + Veri çerçeveleri/baytlar + + + Data rate + Veri oranı + + + RX data frames + RX veri çerçeveleri + + + RX data bytes + RX veri baytları + + + RX data frames/bytes + RX veri çerçeveleri/baytları + + + RX data rate + RX veri oranı + + + NCF frames + NCF çerçeveleri + + + NCF count + NCF sayısı + + + NCF bytes + NCF baytları + + + NCF frames/bytes + NCF çerçeveleri/baytları + + + NCF count/bytes + NCF sayısı/bayt + + + NCF frames/count + NCF çerçeveleri/sayısı + + + NCF frames/count/bytes + NCF çerçeveleri/sayı/bayt + + + NCF rate + NCF oranı + + + SM frames + SM çerçeveleri + + + SM bytes + SM baytları + + + SM frames/bytes + SM çerçeveleri/baytları + + + SM rate + SM oranı + + + Show + Göster + + + Data + Veri + + + RX Data + RX Verileri + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + taşıma sıra numaraları + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Miktar + + + Frame + Çerçeve + + + SQN/Reason + SQN/Nedeni + + + Receivers + Alıcılar + + + NAK frames + NAK çerçeveler + + + NAK count + NAK sayısı + + + NAK bytes + NAK bayt + + + NAK rate + NAK oranı + + + NAK sequence numbers for transport + Nakil için NAK sıra numaraları + + + Display filter: + Ekran filtresi: + + + Regenerate statistics using this display filter + Bu görüntü filtresini kullanarak istatistikleri yeniden oluşturun + + + Apply + Uygula + + + Copy as CSV + CSV olarak kopyala + + + Copy the tree as CSV + Ağacı CSV olarak kopyalayın + + + Copy as YAML + YAML olarak kopyala + + + Copy the tree as YAML + Ağacı YAML olarak kopyalayın + + + Show the data frames column + Veri çerçeveleri sütununu göster + + + Show the data bytes column + Veri bayt sütununu göster + + + Show the data frames/bytes column + Veri çerçeveleri/bayt sütununu göster + + + Show the RX data frames column + RX veri çerçeveleri sütununu göster + + + Show the RX data bytes column + RX veri bayt sütununu göster + + + Show the RX data frames/bytes column + RX veri çerçeveleri/bayt sütununu göster + + + Show the NCF frames column + NCF çerçeveleri sütununu göster + + + Show the NCF bytes column + NCF bayt sütununu göster + + + Show the NCF count column + NCF sayısı sütununu göster + + + Show the data rate column + Veri oranı sütununu göster + + + Show the RX data rate column + RX veri oranı sütununu göster + + + Show the NCF frames/bytes column + NCF çerçeveleri/bayt sütununu göster + + + Show the NCF count/bytes column + NCF sayısı/bayt sütununu göster + + + Show the NCF frames/count column + NCF çerçeve/sayım sütununu göster + + + Show the NCF frames/count/bytes column + NCF çerçeve/sayım/bayt sütununu göster + + + Show the NCF rate column + NCF oranı sütununu göster + + + Show the SM frames column + SM çerçeveleri sütununu göster + + + Show the SM bytes column + SM bayt sütununu göster + + + Show the SM frames/bytes column + SM çerçeveleri/bayt sütununu göster + + + Show the SM rate column + SM oranı sütununu göster + + + Auto-resize columns to content + Sütunları içeriğe göre otomatik olarak yeniden boyutlandır + + + Resize columns to content size + Sütunları içerik boyutuna göre yeniden boyutlandır + + + LBT-RM Statistics failed to attach to tap + LBT-RM İstatistikleri, dokunmaya eklenemedi + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + LBT-RU Taşıma İstatistikleri + + + Sources + Kaynaklar + + + Address/Transport/Client + Adres/Aktarım/İstemci + + + Data frames + Veri çerçeveleri + + + Data bytes + Veri baytları + + + Data frames/bytes + Veri çerçeveleri/baytlar + + + Data rate + Veri oranı + + + RX data frames + RX veri çerçeveleri + + + RX data bytes + RX veri baytları + + + RX data frames/bytes + RX veri çerçeveleri/baytları + + + RX data rate + RX veri oranı + + + NCF frames + NCF çerçeveleri + + + NCF count + NCF sayısı + + + NCF bytes + NCF baytları + + + NCF frames/count + NCF çerçeveleri/sayısı + + + NCF frames/bytes + NCF çerçeveleri/baytları + + + NCF count/bytes + NCF sayısı/bayt + + + NCF frames/count/bytes + NCF çerçeveleri/sayı/bayt + + + NCF rate + NCF oranı + + + SM frames + SM çerçeveleri + + + SM bytes + SM baytları + + + SM frames/bytes + SM çerçeveleri/baytları + + + SM rate + SM oranı + + + RST frames + RST çerçeveleri + + + RST bytes + RST baytları + + + RST frames/bytes + RST çerçeveleri/baytları + + + RST rate + RST oranı + + + Show + Göster + + + Data SQN + Veri SQN'si + + + RX Data SQN + RX Veri SQN'si + + + NCF SQN + NCF SQN'si + + + SM SQN + SM SQN'si + + + RST reason + RST nedeni + + + details for transport + aktarım için detaylar + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + Miktar + + + Frame + Çerçeve + + + Reason + Sebep + + + SQN/Reason + SQN/Nedeni + + + Receivers + Alıcılar + + + Address/Transport + Adres/Aktarım + + + NAK frames + NAK çerçeveler + + + NAK count + NAK sayısı + + + NAK bytes + NAK bayt + + + NAK frames/count + NAK kare/sayı + + + NAK count/bytes + NAK sayısı/bayt + + + NAK frames/bytes + NAK çerçeve/bayt + + + NAK frames/count/bytes + NAK çerçeve/sayım/bayt + + + NAK rate + NAK oranı + + + ACK frames + ACK çerçeveleri + + + ACK bytes + ACK baytları + + + ACK frames/bytes + ACK çerçeveleri/baytları + + + ACK rate + ACK oranı + + + CREQ frames + CREQ çerçeveleri + + + CREQ bytes + CREQ bayt + + + CREQ frames/bytes + CREQ çerçeveleri/baytları + + + CREQ rate + CREQ oranı + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + CREQ isteği + + + Display filter: + Ekran filtresi: + + + Regenerate statistics using this display filter + Bu görüntü filtresini kullanarak istatistikleri yeniden oluşturun + + + Apply + Uygula + + + Copy as CSV + CSV olarak kopyala + + + Copy the tree as CSV + Ağacı CSV olarak kopyalayın + + + Copy as YAML + YAML olarak kopyala + + + Copy the tree as YAML + Ağacı YAML olarak kopyalayın + + + Show the data frames column + Veri çerçeveleri sütununu göster + + + Show the data bytes column + Veri bayt sütununu göster + + + Show the data frames/bytes column + Veri çerçeveleri/bayt sütununu göster + + + Show the data rate column + Veri oranı sütununu göster + + + Show the RX data frames column + RX veri çerçeveleri sütununu göster + + + Show the RX data bytes column + RX veri bayt sütununu göster + + + Show the RX data frames/bytes column + RX veri çerçeveleri/bayt sütununu göster + + + Show the RX data rate column + RX veri oranı sütununu göster + + + Show the NCF frames column + NCF çerçeveleri sütununu göster + + + Show the NCF count column + NCF sayısı sütununu göster + + + Show the NCF bytes column + NCF bayt sütununu göster + + + Show the NCF frames/bytes column + NCF çerçeveleri/bayt sütununu göster + + + Show the NCF count/bytes column + NCF sayısı/bayt sütununu göster + + + Show the NCF frames/count column + NCF çerçeve/sayım sütununu göster + + + Show the NCF frames/count/bytes column + NCF çerçeve/sayım/bayt sütununu göster + + + Show the SM frames column + SM çerçeveleri sütununu göster + + + Show the SM bytes column + SM bayt sütununu göster + + + Show the SM frames/bytes column + SM çerçeveleri/bayt sütununu göster + + + Show the SM rate column + SM oranı sütununu göster + + + Show the RST frames column + RST çerçeveleri sütununu göster + + + Show the RST bytes column + RST bayt sütununu göster + + + Show the RST frames/bytes column + RST çerçeveleri/bayt sütununu göster + + + Show the RST rate column + RST oranı sütununu göster + + + Show the NAK frames column + NAK çerçeveleri sütununu göster + + + Show the NAK count column + NAK sayısı sütununu göster + + + Show the NAK bytes column + NAK bayt sütununu göster + + + Show the NAK frames/count column + NAK kare/sayım sütununu göster + + + Show the NAK count/bytes column + NAK sayısı/bayt sütununu göster + + + Show the NAK frames/bytes column + NAK çerçeveleri/bayt sütununu göster + + + Show the NAK frames/count/bytes column + NAK çerçeve/sayım/bayt sütununu göster + + + Show the NAK rate column + NAK oranı sütununu göster + + + Show the ACK frames column + ACK çerçeveleri sütununu göster + + + Show the ACK bytes column + ACK bayt sütununu göster + + + Show the ACK frames/bytes column + ACK çerçeveleri/bayt sütununu göster + + + Show the ACK rate column + ACK oranı sütununu göster + + + Show the CREQ frames column + CREQ çerçeveleri sütununu göster + + + Show the CREQ bytes column + CREQ bayt sütununu göster + + + Show the CREQ frames/bytes column + CREQ çerçeveleri/bayt sütununu göster + + + Show the CREQ rate column + CREQ oranı sütununu göster + + + Auto-resize columns to content + Sütunları içeriğe göre otomatik olarak yeniden boyutlandır + + + Resize columns to content size + Sütunları içerik boyutuna göre yeniden boyutlandır + + + Show the NCF rate column + NCF oranı sütununu göster + + + LBT-RU Statistics failed to attach to tap + LBT-RU İstatistikleri, dokunmaya eklenemedi + + + + LBMStreamDialog + + Dialog + Diyalog + + + Stream + Akış + + + Endpoint A + Uç nokta A + + + Endpoint B + Uç nokta B + + + Messages + İletiler + + + Bytes + Bayt + + + First Frame + İlk Kare + + + Last Frame + Son Kare + + + Display filter: + Ekran filtresi: + + + Regenerate statistics using this display filter + Bu görüntü filtresini kullanarak istatistikleri yeniden oluşturun + + + Apply + Uygula + + + Copy as CSV + CSV olarak kopyala + + + Copy the tree as CSV + Ağacı CSV olarak kopyalayın + + + Copy as YAML + YAML olarak kopyala + + + Copy the tree as YAML + Ağacı YAML olarak kopyalayın + + + LBM Stream failed to attach to tap + LBM Akışı, dokunmaya eklenemedi + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + + + + %Ln item(s) + + %Ln item + + + + + LayoutPreferencesFrame + + Frame + Çerçeve + + + Pane 1: + Bölme 1: + + + Packet List + Paket Listesi + + + Packet Details + Paket Ayrıntıları + + + Packet Bytes + Paket Baytları + + + Packet Diagram + Paket Şeması + + + None + Yok + + + Pane 2: + Bölme 2: + + + Pane 3: + Bölme 3: + + + Packet List settings: + Paket Listesi ayarları: + + + Show packet separator + Paket ayırıcıyı göster + + + Show column definition in column context menu + Sütun bağlam menüsünde sütun tanımını göster + + + Allow the list to be sorted + Listenin sıralanmasına izin ver + + + Maximum number of cached rows (affects sorting) + Azami önbelleğe alınmış satır sayısı (sıralamayı etkiler) + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + <html><head/><body><p>Bu sayıdan daha fazla satır görüntülenirse, paket incelemesi gerektiren sütunlara göre sıralama devre dışı bırakılır. Bu sayıyı artırmak, sütun değerlerini önbelleğe alarak bellek tüketimini artırır.</p></body></html> + + + Enable mouse-over colorization + Fareyle üzerine gelindiğinde renklendirmeyi etkinleştir + + + Status Bar settings: + Durum Çubuğu ayarları: + + + Show selected packet number + Seçili paket numarasını göster + + + Show file load time + Dosya yükleme süresini göster + + + + LteMacStatisticsDialog + + LTE Mac Statistics + LTE Mac İstatistikleri + + + Include SR frames in filter + SR çerçevelerini filtreye dahil et + + + Include RACH frames in filter + RACH çerçevelerini filtreye dahil et + + + MAC Statistics + MAC İstatistikleri + + + + LteRlcGraphDialog + + Dialog + Diyalog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Değerli ve şaşırtıcı zaman kazandıran klavye kısayolları</h3> +<tablo><tbody> + +<tr><th>+</th><td>Yakınlaştır</td></th> +<tr><th>-</th><td>Uzaklaştır</td></th> +<tr><th>0</th><td>Grafiği ilk durumuna sıfırlayın</td></th> + +<tr><th>→</th><td>10 piksel sağa git</td></th> +<tr><th>←</th><td>10 piksel sola git</td></th> +<tr><th>↑</th><td>10 piksel yukarı git</td></th> +<tr><th>↓</th><td>10 piksel aşağı git</td></th> +<tr><th><i>Üst Karakter+</i>→</th><td>1 piksel sağa git</td></th> +<tr><th><i>Üst Karakter+</i>←</th><td>1 piksel sola git</td></th> +<tr><th><i>Üst Karakter+</i>↑</th><td>1 piksel yukarı git</td></th> +<tr><th><i>Üst Karakter+</i>↓</th><td>1 piksel aşağı git</td></th> + +<tr><th>g</th><td>İmlecin altındaki pakete git</td></th> + +<tr><th>z</th><td>Fare sürükle / yakınlaştırma arasında geçiş yapın</td></th> +<tr><th>t</th><td>Yakalama / oturum süresi kaynağı arasında geçiş yap</td></th> +<tr><th>Boşluk</th><td>Artı işaretlerini değiştir</td></th> + +</tbody></table> +</body></html> + + + Mouse + Fare + + + Drag using the mouse button. + Fare düğmesini kullanarak sürükleyin. + + + drags + sürükler + + + Select using the mouse button. + Fare düğmesini kullanarak seçin. + + + zooms + yakınlaştırır + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Grafiği ilk durumuna sıfırlayın.</p></body></html> + + + Reset + Sıfırla + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Bağlantının yönünü değiştirin (karşı akışa bakın).</p></body></html> + + + Switch Direction + Yön Değiştir + + + Reset Graph + Grafiği Sıfırla + + + Reset the graph to its initial state. + Grafiği ilk durumuna sıfırlayın. + + + 0 + 0 + + + Zoom In + Yakınlaş + + + + + + + + + Zoom Out + Uzaklaş + + + - + - + + + Move Up 10 Pixels + 10 Piksel Yukarı Taşı + + + Up + Üst + + + Move Left 10 Pixels + 10 Piksel Sola Taşı + + + Left + Sol + + + Move Right 10 Pixels + 10 Piksel Sağa Taşı + + + Right + Sağa + + + Move Down 10 Pixels + 10 Piksel Aşağı Taşı + + + Down + Aşağı + + + Move Up 1 Pixel + 1 Piksel Yukarı Taşı + + + Shift+Up + Shift+Yukarı + + + Move Left 1 Pixel + Sola 1 Piksel Taşı + + + Shift+Left + Shift+Sol + + + Move Right 1 Pixel + 1 Piksel Sağa Taşı + + + Shift+Right + Shift+Sağ + + + Move Down 1 Pixel + 1 Piksel Aşağı Taşı + + + Move down 1 Pixel + 1 Piksel aşağı taşı + + + Shift+Down + Shift+Aşağı + + + Drag / Zoom + Sürükle / Yakınlaştır + + + Toggle mouse drag / zoom behavior + Fare sürükle / yakınlaştırma davranışını değiştir + + + Z + Z + + + Crosshairs + Artılar + + + Toggle crosshairs + Artı işaretlerini aç/kapat + + + Space + Boşluk + + + Move Up 100 Pixels + 100 Piksel Yukarı Taşı + + + PgUp + Sayfa yukarı + + + PgDown + Sayfa aşağı + + + Go To Packet Under Cursor + İmleç Altındaki Pakete Git + + + Go to packet currently under the cursor + İmlecin altındaki pakete git + + + G + G + + + Zoom In X Axis + X Eksenini Yakınlaştır + + + X + X + + + Zoom Out Y Axis + A Eksenini Uzaklaştır + + + Shift+Y + Shift+Y + + + Zoom In Y Axis + A Eksenini Yakınlaştır + + + Y + Y + + + Zoom Out X Axis + X Eksenini Uzaklaştır + + + Shift+X + Shift+X + + + Switch direction (swap between UL and DL) + Yön değiştir (UL ve DL arasında geçiş yap) + + + D + D + + + Time + Süre + + + Sequence Number + Sıra Numarası + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + LTE RLC Grafiği (UE=%1 chan=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + LTE RLC Grafiği - kanal seçilmedi + + + Save As… + Farklı Kaydet… + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s sıra %4 len %5) + + + Click to select packet + Paket seçmek için tıklayın + + + Packet + Paket + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Yakınlaştırmak için bırakın, x = %1 ila %2, y = %3 ila %4 + + + Unable to select range. + Aralık seçilemiyor. + + + Click to select a portion of the graph. + Grafiğin bir bölümünü seçmek için tıklayın. + + + Portable Document Format (*.pdf) + Taşınabilir Belge Formatı (*.pdf) + + + Portable Network Graphics (*.png) + Taşınabilir Ağ Grafikleri (* .png) + + + Windows Bitmap (*.bmp) + Windows Bit Eşlem (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG Dosya Değişim Biçimi (*.jpeg *.jpg) + + + Save Graph As… + Grafiği Farklı Kaydet… + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + LTE RLC İstatistikleri + + + Include SR frames in filter + SR çerçevelerini filtreye dahil et + + + Include RACH frames in filter + RACH çerçevelerini filtreye dahil et + + + Use RLC frames only from MAC frames + RLC çerçevelerini yalnızca MAC çerçevelerinden kullanın + + + UL Frames + YÜ Çerçeveler + + + UL Bytes + YÜ Bayt + + + UL MB/s + YÜ MB/s + + + UL ACKs + YÜ ACKs + + + UL NACKs + YÜ NACKs + + + UL Missing + YÜ Kayıpları + + + DL Frames + İN Çerçeveler + + + DL Bytes + İN Bayt + + + DL MB/s + İN MB/s + + + DL ACKs + İN ACKs + + + DL NACKs + İN NACKs + + + DL Missing + İN Kayıpları + + + RLC Statistics + RLC İstatistikleri + + + + MainStatusBar + + Ready to load or capture + Yüklemeye veya yakalamaya hazır + + + Ready to load file + Dosyayı yüklemeye hazır + + + Open the Capture File Properties dialog + Yakalama Dosyası Özellikleri iletişim kutusunu açın + + + Profile: %1 + Profil: %1 + + + Manage Profiles… + Profilleri Yönet… + + + New… + Yeni… + + + Edit… + Düzenle… + + + Import + İçe Aktar + + + Export + Dışarı aktar + + + Delete + Sil + + + Switch to + Çevir + + + is the highest expert information level + is the highest expert info level + en yüksek uzman bilgi seviyesidir + + + ERROR + HATA + + + WARNING + UYARI + + + NOTE + NOT + + + CHAT + SOHBET + + + No expert information + No expert info + Uzman bilgisi yok + + + %Ln byte(s) + , %1 bytes + + + + + + Byte %1 + Bayt %1 + + + Bytes %1-%2 + Bayt %1-%2 + + + Selected Packet: %1 %2 + Seçilen Paket: %1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + Paketler: %1 %4 Görüntülenen: %2 (%3%) + + + %1 Selected: %2 (%3%) + %1 Seçildi: %2 (%3) + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 İşaretli: %2 (%3) + + + %1 Dropped: %2 (%3%) + %1 Düştü: %2 (%3) + + + %1 Ignored: %2 (%3%) + %1 Yoksayıldı: %2 (%3) + + + %1 Comments: %2 + %1 Yorumlar: %2 + + + %1 Load time: %2:%3.%4 + %1 Yükleme süresi: %2:%3.%4 + + + No Packets + Paket Yok + + + From Zip File... + Zip Dosyasından... + + + From Directory... + Dizinden... + + + Selected Personal Profile... + Seçilen Kişisel Profil... + + + All Personal Profiles... + Tüm Kişisel Profiller... + + + Packets: %1 + Paketler: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + Çerçeve + + + Checking this will save the size, position, and maximized state of the main window. + Bunu kontrol etmek, ana pencerenin boyutunu, konumunu ve ekranı kaplamış durumunu kaydeder. + + + Remember main window size and placement + Ana pencere boyutunu ve yerleşimini hatırla + + + Open files in + Dosyaları şurada aç + + + This folder: + Bu klasör: + + + Browse… + Browse... + Gözat… + + + The most recently used folder + En son kullanılan klasör + + + Show up to + Kadar göster + + + filter entries + filtre girişleri + + + recent files + son dosyalar + + + Confirm unsaved capture files + Kaydedilmemiş yakalama dosyalarını onaylayın + + + Display autocompletion for filter text + Filtre metni için otomatik tamamlamayı görüntüle + + + Main toolbar style: + Ana araç çubuğu stili: + + + Icons only + Sadece simge + + + Text only + Sadece metin + + + Icons & Text + Simgeler ve Metin + + + Window title + Pencere başlığı + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Mevcut başlığa eklenecek özel pencere başlığı<br/>%F = yakalama dosyasının dosya yolu<br/>%P = profil adı<br/>% S = yalnızca değerler veya statik metin içeren değişkenlerle çevrelendiğinde gösterilen koşullu bir ayırıcı (&quot; - &quot;)<br/>%V = sürüm bilgisi</p></body></html> + + + Prepend window title + Pencere başlığını başa ekle + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + <html><head/><body><p>Mevcut başlığın başına eklenecek özel pencere başlığı<br/>%F = yakalama dosyasının dosya yolu<br/>%P = profil adı<br/>%S = yalnızca değerler veya statik metin içeren değişkenlerle çevrelendiğinde gösterilen koşullu bir ayırıcı (&quot; - &quot;)<br/>%V = sürüm bilgisi</p></body></html> + + + Language: + Dil: + + + Use system setting + Sistem ayarını kullan + + + Open Files In + Dosyaları Aç + + + + ManageInterfacesDialog + + Manage Interfaces + Arayüzleri Yönet + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Gizli bir arayüzü gizlemek veya göstermek için onay kutusunu tıklayın.</p></body></html> + + + Local Interfaces + Yerel Arayüzler + + + Show + Göster + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Listeden yakalamak veya mevcut bir boruyu listeden çıkarmak için bir boru ekleyin.</p></body></html> + + + Pipes + Borular + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Varsayılan ayarları kullanarak yeni bir kanal ekleyin.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Seçilen boruyu listeden kaldırın.</p></body></html> + + + Remote Interfaces + Uzak Arayüzler + + + Host / Device URL + Ana Bilgisayar / Cihaz URL'si + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>Uzak bir ana bilgisayar ve onun arayüzlerini ekleyin</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>Seçilen ana bilgisayarı listeden kaldırın.</p></body></html> + + + Remote Settings + Kontrol ayarları + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Wireshark'ın bu sürümü boru ayarlarını kaydetmez. + + + This version of Wireshark does not save remote settings. + Wireshark'ın bu sürümü uzak ayarları kaydetmez. + + + This version of Wireshark does not support remote interfaces. + Wireshark'ın bu sürümü uzak arayüzleri desteklemez. + + + New Pipe + Yeni Boru + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + Tümünü seç + + + Copy + Kopyala + + + Find + Bul + + + Clear + Temiz + + + + ManufTableModel + + Address Block + + + + Short Name + Kısa İsim + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + Kaydırma Alanı + + + + Mtp3SummaryDialog + + Dialog + Diyalog + + + MTP3 Summary + MTP3 Özeti + + + File + Dosya + + + Name + Ad + + + Length + Uzunluk + + + Format + Biçim + + + Snapshot length + Anlık görüntü uzunluğu + + + Data + Veri + + + First packet + İlk paket + + + Last packet + Son paket + + + Elapsed + Geçen + + + Packets + Paketler + + + Service Indicator (SI) Totals + Hizmet Göstergesi (SI) Toplamları + + + SI + SI + + + MSUs + MSUs + + + MSUs/s + MSUs/s + + + Bytes + Bayt + + + Bytes/MSU + Bayt/MSU + + + Bytes/s + Bayt/s + + + Totals + Toplam + + + Total MSUs + Toplam MSU + + + Total Bytes + Toplam Bayt + + + Average Bytes/MSU + Ortalama Bayt/MSU + + + Average Bytes/s + Ortalama Bayt/sn + + + + MulticastStatisticsDialog + + UDP Multicast Streams + UDP Çok Noktaya Yayın Akışları + + + Source Address + Kaynak Adresi + + + Source Port + Kaynak Bağlantı noktası + + + Destination Address + Hedef Adres + + + Destination Port + Hedef Bağlantı Noktası + + + Packets + Paketler + + + Packets/s + Paketler/s + + + Avg BW (bps) + Ort BW (bps) + + + Max BW (bps) + Azm BW (bps) + + + Max Burst + Azm Patlama + + + Burst Alarms + Patlama Alarmları + + + Max Buffers (B) + Azami Arabellek (B) + + + Buffer Alarms + Arabellek Alarmı + + + Burst measurement interval (ms): + Burst ölçüm aralığı (ms): + + + Burst alarm threshold (packets): + Patlama alarm eşiği (paketler): + + + Buffer alarm threshold (B): + Arabellek alarm eşiği (B): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + Akış boş hızı (Kb/s): + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + Toplam boş hız (Kb/s): + + + The burst interval must be between 1 and 1000. + Patlama aralığı 1 ile 1000 arasında olmalıdır. + + + The burst alarm threshold isn't valid. + Patlama alarmı eşiği geçerli değil. + + + The buffer alarm threshold isn't valid. + Arabellek alarm eşiği geçerli değil. + + + The stream empty speed should be between 1 and 10000000. + Akış boş hızı 1 ile 10000000 arasında olmalıdır. + + + The total empty speed should be between 1 and 10000000. + Toplam boş hız 1 ile 10000000 arasında olmalıdır. + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + %1 akış, ortalama bw: %2bps, maksimum bw: %3bps, maksimum patlama: %4 / %5ms, maksimum arabellek: %6B + + + + PacketCommentDialog + + Edit Packet Comment + Paket Yorumunu Düzenle + + + Add Packet Comment + Paket Yorumu Ekle + + + + PacketDiagram + + Packet diagram + Paket şeması + + + Show Field Values + Alan Değerlerini Göster + + + Save Diagram As… + Şemayı Farklı Kaydet… + + + Copy as Raster Image + Raster Görüntü Olarak Kopyala + + + …as SVG + …SVG olarak + + + Portable Network Graphics (*.png) + Taşınabilir Ağ Grafikleri (* .png) + + + Windows Bitmap (*.bmp) + Windows Bit Eşlem (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG Dosya Değişim Biçimi (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + Ölçeklenebilir Vektör Grafikleri (*.svg) + + + Save Graph As… + Grafiği Farklı Kaydet… + + + + PacketDialog + + Dialog + Diyalog + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + Paket baytlarını göster + + + Packet %1 + Paket %1 + + + [%1 closed] + [%1 kapandı] + + + Byte %1 + Bayt %1 + + + Bytes %1-%2 + Bayt %1-%2 + + + %Ln byte(s) + + + + + + + PacketFormatGroupBox + + GroupBox + GrupKutusu + + + Packet Format + Paket Formatı + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>Paket listesine benzer paket özet satırları</p></body></html> + + + Summary line + Özet satırı + + + Include column headings + Sütun başlıklarını dahil et + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>Protokol ağacına benzer paket ayrıntıları</p></body></html> + + + Details: + Ayrıntılar: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>Yalnızca üst düzey paket ayrıntı öğelerini dışa aktarın</p></body></html> + + + All co&llapsed + Hepsi dar&altıldı + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>Paket ayrıntılarını şu anda görüntülendikleri şekilde genişletin ve daraltın.</p></body></html> + + + As displa&yed + Gösterild&iği gibi + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>Tüm paket ayrıntı öğelerini dışa aktarın</p></body></html> + + + All e&xpanded + Tüm g&enişletilmiş + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>Paket bayt görünümüne benzer şekilde paket verilerinin onaltılık dökümünü dışa aktarın</p></body></html> + + + Bytes + Bayt + + + Include secondary data sources + İkincil veri kaynaklarını dahil et + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + <html><head/><body><p>Çerçeveye ek olarak yeniden birleştirilmiş veya şifresi çözülmüş arabellekler gibi ikincil veri kaynakları için onaltılık dökümler oluşturun</p></body></html> + + + + PacketList + + Protocol Preferences + Protokol Tercihleri + + + Summary as Text + Metin Olarak Özet + + + …as CSV + …CSV olarak + + + …as YAML + …YAML olarak + + + Decode As… + Kodu Çöz… + + + Frame %1: %2 + + + %1 karesi: %2 + + + + + [ Comment text exceeds %1. Stopping. ] + [ Yorum metni %1 değerini aşıyor. Durduruluyor. ] + + + + PacketListHeader + + Align Left + Sola Hizala + + + Align Center + Ortaya Hizala + + + Align Right + Sağa Hizala + + + Edit Column + Sütunu Düzenle + + + Resize to Contents + İçeriği Yeniden Boyutlandır + + + Column Preferences… + Sütun Tercihleri… + + + Resize Column to Width… + Sütunu Genişliğe Yeniden Boyutlandır… + + + Resolve Names + Adları Çöz + + + Remove this Column + Bu Sütunu Kaldır + + + Column %1 + %1 sütunu + + + Width: + Genişlik: + + + + PacketListModel + + Column + Sütun + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + %1 yalnızca %2 veya daha az görünür satırla sıralanabilir; Düzen tercihlerinde önbellek boyutunu artırın + + + Sorting "%1"… + "%1" sıralanıyor… + + + Sorting … + + + + + PacketRangeGroupBox + + Form + Form + + + Packet Range + Paket Aralığı + + + - + - + + + Displayed + Görüntülenen + + + &Marked packets only + &Yalnızca işaretli paketler + + + &Range: + &Aralık: + + + Remove &ignored packets + Yok &sayılan paketleri kaldır + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + Sondan &ilk işaretlenen + + + &All packets + &Tüm Paketler + + + &Selected packets only + &Yalnızca seçili paketler + + + Captured + Yakalandı + + + + PathSelectionDelegate + + Open a pipe + Boru aç + + + + PathSelectionEdit + + Browse + Gözat + + + Select a path + Bir yol seçin + + + + PluginListModel + + Name + Ad + + + Version + Sürüm + + + Type + Biçim + + + Path + Dizin + + + + PortsModel + + All entries + Bütün girdiler + + + tcp + tcp + + + udp + udp + + + sctp + sctp + + + dccp + dccp + + + Name + Ad + + + Port + Bağlantı Noktası + + + Type + Biçim + + + + PreferenceEditorFrame + + Frame + Çerçeve + + + … + + + + a preference + bir tercih + + + Browse… + Gözat… + + + Open %1 preferences… + %1 tercihlerini aç… + + + Invalid value. + Geçersiz değer. + + + + PreferencesDialog + + Search: + Ara: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + Tercihler + + + + PrefsModel + + Advanced + Gelişmiş + + + Appearance + Görünüm + + + Layout + Düzen + + + Columns + Sütunlar + + + Font and Colors + Yazı Tipi ve Renkler + + + Capture + Yakala + + + Expert + Uzman + + + Filter Buttons + Filtre Düğmeleri + + + RSA Keys + RSA Anahtarları + + + + PrintDialog + + Packet Format + Paket Formatı + + + Print each packet on a new page + Her paketi yeni bir sayfada yazdırın + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + <html><head/><body><p>Her sayfada yakalama dosyası bilgilerini yazdırın</p></body></html> + + + Capture information header + Bilgi başlığını yakala + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>&quot;+&quot; ve &quot;-&quot; Önizlemeyi yakınlaştırmak ve uzaklaştırmak için tuşlar. &quot;0&quot; yakınlaştırma düzeyini sıfırlamak için tuşuna basın.</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ ve - yakınlaştırma, 0 sıfırlama</span></p></body ></html> + + + Packet Range + Paket Aralığı + + + Print + Yazdır + + + &Print… + &Yazdır… + + + Page &Setup… + Sayfa &Ayarı… + + + %1 %2 total packets, %3 shown + %1 %2 toplam paket, %3 gösteriliyor + + + Print Error + Yazdırma Hatası + + + Unable to print to %1. + %1'e yazdırılamıyor. + + + + ProfileDialog + + Search for profile … + Profil ara… + + + Create a new profile using default settings. + Varsayılan ayarları kullanarak yeni bir profil oluşturun. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + <html><head/><body><p>Bu profili kaldırın. Sistem tarafından sağlanan profiller kaldırılamaz. Silindikten sonra varsayılan profil sıfırlanacak.</p></body></html> + + + Copy this profile. + Bu profili kopyalayın. + + + Configuration Profiles + Yapılandırma Profilleri + + + Import + noun + İçe Aktar + + + Export + noun + Dışarı aktar + + + From Zip File... + Zip Dosyasından... + + + From Directory... + Dizinden... + + + %Ln Selected Personal Profile(s)... + + + + + + All Personal Profiles... + Tüm Kişisel Profiller... + + + New profile + Yeni profil + + + Profile Error + Profil Hatası + + + Exporting profiles + Profilleri dışa aktarma + + + No profiles found for export + Dışa aktarılacak profil bulunamadı + + + Select zip file for export + Dışa aktarmak için zip dosyasını seçin + + + An import of profiles is not allowed, while changes are pending + Değişiklikler beklemedeyken profillerin içe aktarılmasına izin verilmez + + + An import is pending to be saved. Additional imports are not allowed + Bir içe aktarma kaydedilmeyi bekliyor. Ek aktarmaya izin verilmez + + + An export of profiles is only allowed for personal profiles + Profillerin dışa aktarılmasına yalnızca kişisel profiller için izin verilir + + + An export of profiles is not allowed, while changes are pending + Değişiklikler beklemedeyken profillerin dışa aktarılmasına izin verilmez + + + %Ln profile(s) exported + + + + + + Select zip file for import + İçe aktarmak için zip dosyasını seçin + + + Select directory for import + İçe aktarılacak dizini seçin + + + Zip File (*.zip) + Zip Dosyası (*.zip) + + + Error + Hata + + + An error has occurred while exporting profiles + Profilleri dışa aktarırken bir hata oluştu + + + No profiles found for import in %1 + %1'de içe aktarılacak profil bulunamadı + + + %Ln profile(s) imported + + + + + + , %Ln profile(s) skipped + + + + + + Importing profiles + Profilleri içe aktarma + + + %Ln profile(s) selected + + %Ln profile selected + + + + + ProfileModel + + Resetting to default + Varsayılana sıfırlanıyor + + + Imported profile + İçe aktarılan profil + + + This is a system provided profile + Bu, sistem tarafından sağlanan bir profildir + + + A profile change for this name is pending + Bu ad için bir profil değişikliği bekleniyor + + + (See: %1) + (Bakınız: %1) + + + This is an invalid profile definition + Bu geçersiz bir profil tanımıdır + + + A profile already exists with this name + Bu ada sahip bir profil zaten var + + + A profile with this name is being deleted + Bu ada sahip bir profil siliniyor + + + Created from default settings + Varsayılan ayarlardan oluşturuldu + + + system provided + sağlanan sistem + + + deleted + silindi + + + copy + noun + kopyala + + + Exporting profiles while changes are pending is not allowed + Değişiklikler beklemedeyken profillerin dışa aktarılmasına izin verilmez + + + No profiles found to export + Dışa aktarılacak profil bulunamadı + + + Can't delete profile directory + Profil dizini silinemiyor + + + A profile name cannot contain the following characters: %1 + Bir profil adı şu karakterleri içeremez: %1 + + + A profile name cannot contain the '/' character + Bir profil adı '/' karakterini içeremez + + + A profile cannot start or end with a period (.) + Bir profil nokta (.) ile başlayamaz veya bitemez + + + Default + Ön tanımlı + + + Global + Genel + + + Personal + Kişisel + + + Renamed from: %1 + Yeniden adlandırıldı: %1 + + + Copied from: %1 + Kopyalandı: %1 + + + renamed to %1 + %1 olarak yeniden adlandırıldı + + + Profile + Profil + + + Type + Biçim + + + + ProfileSortModel + + All profiles + Tüm profiller + + + Personal profiles + Kişisel profiller + + + Global profiles + Genel profiller + + + + ProgressFrame + + Frame + Çerçeve + + + Loading + Yükleniyor + + + + ProtoTree + + Packet details + Paket ayrıntıları + + + Not a field or protocol + Bir alan veya protokol değil + + + No field reference available for text labels. + Metin etiketleri için alan referansı yok. + + + Expand Subtrees + Alt Ağaçları Genişlet + + + Collapse Subtrees + Alt Ağaçları Daralt + + + Expand All + Tümünü Genişlet + + + Collapse All + Hepsini Daralt + + + Copy + Kopyala + + + All Visible Items + Tüm Görünür Öğeler + + + All Visible Selected Tree Items + Tüm Görünür Seçilmiş Ağaç Öğeleri + + + Description + Açıklama + + + Field Name + Alan Adı + + + Value + Değer + + + As Filter + Filtre Olarak + + + Wiki Protocol Page + Wiki Protokol Sayfası + + + Filter Field Reference + Filtre Alanı Referansı + + + Copied + Kopyalandı + + + Wiki Page for %1 + %1 için Wiki Sayfası + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Wiki'nin bakımı topluluk tarafından sağlanır.</p><p>Yüklemek üzere olduğunuz sayfa harika, eksik, yanlış veya hiç yok olabilir.</p><p>Wiki'ye geçilsin mi?</P> + + + Colorize with Filter + Filtre ile Renklendirin + + + + ProtocolHierarchyDialog + + Dialog + Diyalog + + + Protocol + İletişim Kuralı + + + Percent Packets + Yüzde Paketler + + + Packets + Paketler + + + Percent Bytes + Yüzde Bayt + + + Bytes + Bayt + + + Bits/s + Bits/s + + + End Packets + Bitiş Paketleri + + + End Bytes + Bitiş Baytları + + + End Bits/s + Bit/sn Bitiş + + + PDUs + PDUs + + + <small><i>A hint.</i></small> + <small><i>Bir ipucu.</i></small> + + + Copy as CSV + CSV olarak kopyala + + + Copy stream list as CSV. + Akış listesini CSV olarak kopyalayın. + + + Copy as YAML + YAML olarak kopyala + + + Copy stream list as YAML. + Akış listesini YAML olarak kopyalayın. + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + Protokol Hiyerarşi İstatistikleri + + + Copy + Kopyala + + + as CSV + CSV olarak + + + as YAML + YAML olarak + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + Görüntüleme filtresi yok. + + + Display filter: %1 + Görüntüleme filtresi: %1 + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + Protokol Tercihleri + + + No protocol preferences available + Kullanılabilir protokol tercihi yok + + + Disable %1 + %1'i devre dışı bırak + + + %1 has no preferences + %1 tercihi yok + + + Open %1 preferences… + %1 tercihlerini aç… + + + + QObject + + Average Throughput (bits/s) + Ortalama Verim (bit/sn) + + + Round Trip Time (ms) + Gidiş-Dönüş Süresi (ms) + + + Segment Length (B) + Segment Uzunluğu (B) + + + Sequence Number (B) + Sıra Numarası (B) + + + Time (s) + Süre () + + + Window Size (B) + Pencere Boyutu (B) + + + [no capture file] + [yakalama dosyası yok] + + + Conversation + Konuşma + + + Bars show the relative timeline for each conversation. + Çubuklar, her konuşma için göreli zaman çizelgesini gösterir. + + + Endpoint + Uç nokta + + + Apply as Filter + Filtre Olarak Uygula + + + Prepare as Filter + Filtre Olarak Hazırla + + + Find + Bul + + + Colorize + Renklendir + + + Look Up + Yukarı Bak + + + Copy + Kopyala + + + UNKNOWN + BİLİNMEYEN + + + Selected + Seçili + + + Not Selected + Seçili değil + + + …and Selected + …ve Seçilmiş + + + …or Selected + …veya Seçilmiş + + + …and not Selected + …ve Seçilmedi + + + …or not Selected + …veya Seçilmedi + + + A + A + + + B + B + + + Any + Herhangi biri + + + Don't show this message again. + Bu mesajı tekrar gösterme. + + + Multiple problems found + Birden fazla sorun bulundu + + + %1 (%L2%) + %1 (%L2%) + + + No entries. + Öge yok. + + + %1 entries. + %1 giriş. + + + Base station + Baz istasyonu + + + <Broadcast> + <Broadcast> + + + <Hidden> + <Gizli> + + + BSSID + BSSID + + + Beacons + İşaretçiler + + + Data Pkts + Veri Paketleri + + + Protection + Koruma + + + Address + Adres + + + Pkts Sent + Gönderilen Pkt'ler + + + Pkts Received + Alınan Pkt'ler + + + Comment + Yorum + + + Wrong sequence number + Yanlış sıra numarası + + + Payload changed to PT=%1 + Yük, PT=%1 olarak değiştirildi + + + Incorrect timestamp + Yanlış zaman damgası + + + Marker missing? + İşaretçi eksik mi? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + Biçim + + + UEId + UEId + + + UL Frames + YÜ Çerçeveler + + + UL Bytes + YÜ Bayt + + + UL MB/s + YÜ MB/s + + + UL Padding % + YÜ Dolgu % + + + UL Re TX + YÜ Re TX + + + DL Frames + İN Çerçeveler + + + DL Bytes + İN Bayt + + + DL MB/s + İN MB/s + + + DL Padding % + İN Dolgu % + + + DL CRC Failed + İN CRC Başarısız + + + DL ReTX + İN ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + ÖÖ + + + Predef + Ön tanım + + + Unknown (%1) + Bilinmeyen (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + Bilinmiyor + + + UE Id + UE Id + + + Name + Ad + + + Mode + Mod + + + Priority + Öncelik + + + default + öntanımlı + + + DLT %1 + DLT %1 + + + Invalid Display Filter + Geçersiz Görüntü Filtresi + + + The filter expression %1 isn't a valid display filter. (%2). + %1 filtre ifadesi geçerli bir görüntüleme filtresi değil. (%2). + + + Error + Hata + + + No remote interfaces found. + Uzak arabirim bulunamadı. + + + PCAP not found + PCAP bulunamadı + + + Unknown error + Bilinmeyen hata + + + Default + Ön tanımlı + + + Changed + Değişti + + + Has this preference been changed? + Bu tercih değişti mi? + + + Default value is empty + Varsayılan değer boş + + + Gap in dissection + Diseksiyondaki boşluk + + + Edit… + Düzenle… + + + Browse… + Gözat… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + Uzak Arayüz + + + Host: + Sunucu: + + + Port: + Bağlantı Noktası: + + + Authentication + Kimlik Doğrulama + + + Null authentication + Boş kimlik doğrulama + + + Password authentication + Şifre doğrulama + + + Username: + Kullanıcı adı: + + + Password: + Şifre: + + + Clear list + Listeyi temizle + + + Error + Hata + + + No remote interfaces found. + Uzak arabirim bulunamadı. + + + PCAP not found + PCAP bulunamadı + + + + RemoteSettingsDialog + + Remote Capture Settings + Uzaktan Yakalama Ayarları + + + Capture Options + Yakalama Seçenekleri + + + Do not capture own RPCAP traffic + Kendi RPCAP trafiğini yakalama + + + Use UDP for data transfer + Veri aktarımı için UDP kullanın + + + Sampling Options + Örnekleme Seçenekleri + + + None + Yok + + + 1 of + 1 + + + packets + paketler + + + 1 every + 1 her + + + milliseconds + milisaniye + + + + ResolvedAddressesDialog + + Dialog + Diyalog + + + Hosts + Sunucular + + + Search for entry (min 3 characters) + Giriş arayın (en az 3 karakter) + + + Ports + Bağlantı Noktaları + + + Search for port or name + Bağlantı noktası veya ad arayın + + + Capture File Comments + Dosya Yorumlarını Yakala + + + Comment + Yorum + + + Show the comment. + Yorumu göster. + + + IPv4 Hash Table + IPv4 Karma Tablosu + + + Show the IPv4 hash table entries. + IPv4 karma tablosu girişlerini gösterin. + + + IPv6 Hash Table + IPv6 Karma Tablosu + + + Show the IPv6 hash table entries. + IPv6 karma tablosu girişlerini gösterin. + + + Show All + Tümünü Görüntüle + + + Show all address types. + Tüm adres türlerini göster. + + + Hide All + Tümünü Gizle + + + Hide all address types. + Tüm adres türlerini gizleyin. + + + IPv4 and IPv6 Addresses (hosts) + IPv4 ve IPv6 Adresleri (ana bilgisayarlar) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + Çözümlenen IPv4 ve IPv6 ana bilgisayar adlarını "ana bilgisayar" biçiminde gösterin. + + + Port names (services) + Bağlantı noktası adları (hizmetler) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + Çözümlenen bağlantı noktası adlarını "hizmetler" biçiminde göster. + + + Ethernet Addresses + Ethernet Adresleri + + + Show resolved Ethernet addresses in "ethers" format. + Çözümlenmiş Ethernet adreslerini "eterler" biçiminde göster. + + + Ethernet Well-Known Addresses + Ethernet'in Tanınmış Adresleri + + + Show well-known Ethernet addresses in "ethers" format. + İyi bilinen Ethernet adreslerini "eter" formatında gösterin. + + + Ethernet Manufacturers + Ethernet Üreticileri + + + Show Ethernet manufacturers in "ethers" format. + Ethernet üreticilerini "eter" formatında gösterin. + + + [no file] + [dosya yok] + + + Resolved Addresses + Çözümlenen Adresler + + + # Resolved addresses found in %1 + # Çözülmüş adresler %1'de bulundu + + + # Comments +# +# + # Yorumlar +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + %1 Tepki Süresi Gecikme İstatistikleri + + + Type + Biçim + + + Messages + Iletiler + + + Min SRT + Asgari SRT + + + Max SRT + Azami SRT + + + Avg SRT + Ortalama SRT + + + Min in Frame + Çerçevede Asgari + + + Max in Frame + Çerçevede Azami + + + Open Requests + Açık İstekler + + + Discarded Responses + Silinen Yanıtlar + + + Repeated Requests + Tekrarlanan İstekler + + + Repeated Responses + Tekrarlanan Yanıtlar + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>Bir program ve sürüm seçin ve isterseniz bir filtre girin, ardından Uygula'ya basın.</i></small> + + + Version: + Sürüm: + + + Program: + Program: + + + DCE-RPC Service Response Times + DCE-RPC Hizmet Yanıt Süreleri + + + ONC-RPC Service Response Times + ONC-RPC Hizmet Yanıt Süreleri + + + + RsaKeysFrame + + RSA Keys + RSA Anahtarları + + + RSA private keys are loaded from a file or PKCS #11 token. + RSA özel anahtarları bir dosyadan veya PKCS #11 belirtecinden yüklenir. + + + Add new keyfile… + Yeni anahtar dosyası ekle… + + + Add new token… + Yeni belirteç ekle… + + + Remove key + Anahtarı kaldır + + + PKCS #11 provider libraries. + PKCS #11 sağlayıcı kitaplıkları. + + + Add new provider… + Yeni sağlayıcı ekle… + + + Remove provider + Sağlayıcıyı kaldır + + + Add PKCS #11 token or key + PKCS #11belirteci veya anahtarı ekleyin + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + Yeni PKCS #11belirteci veya anahtarı bulunamadı, bir PKCS #11 sağlayıcı eklemeyi düşünün. + + + Select a new PKCS #11 token or key + Yeni bir PKCS #11 belirteci veya anahtarı seçin + + + PKCS #11 token or key + PKCS #11 belirteci veya anahtarı + + + Enter PIN or password for %1 (it will be stored unencrypted) + %1 için PIN veya parola girin (şifrelenmemiş olarak saklanacaktır) + + + Enter PIN or password for key + Anahtar için PIN veya şifre girin + + + Key could not be added: %1 + Anahtar eklenemedi: %1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + RSA özel anahtarı (*.pem *.p12 *.pfx *.key);;Tüm Dosyalar ( + + + Select RSA private key file + RSA özel anahtar dosyasını seçin + + + Libraries (*.dll) + Kitaplıklar (*.dll) + + + Libraries (*.so) + Kiaplıklar (*.so) + + + Select PKCS #11 Provider Library + PKCS #11 Sağlayıcı Kitaplığını Seçin + + + Changes will apply after a restart + Değişiklikler yeniden başlatmanın ardından uygulanacak + + + PKCS #11 provider %1 will be removed after the next restart. + PKCS #11 sağlayıcısı %1, bir sonraki yeniden başlatmanın ardından kaldırılacak. + + + + RtpAnalysisDialog + + Dialog + Diyalog + + + Packet + Paket + + + Sequence + Dize + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter + Titreşim (ms) + + + Skew + Eğrilik + + + Bandwidth + Bant genişliği + + + Marker + İşaretleyici + + + Status + Durum + + + Stream %1 + %1 akışı + + + Stream %1 Jitter + %1 Titreşim Akışı + + + Stream %1 Difference + Akış %1 Farkı + + + Stream %1 Delta + %1 Delta Akışı + + + %1 streams, + %1 akış, + + + Save one stream CSV + Bir akış CSV'sini kaydedin + + + Save all stream's CSV + Tüm akışın CSV'sini kaydet + + + &Analyze + &Analiz + + + Open the analysis window for the selected stream(s) + Seçilen akış(lar) için analiz penceresini açın + + + &Set List + &Küme Listesi + + + &Add to List + &Listeye ekle + + + &Remove from List + &Listeden Kaldır + + + Replace existing list in RTP Analysis Dialog with new one + RTP Analizi İletişim Kutusundaki mevcut listeyi yenisiyle değiştirin + + + Add new set to existing list in RTP Analysis Dialog + RTP Analizi İletişim Kutusunda mevcut listeye yeni küme ekle + + + Remove selected streams from list in RTP Analysis Dialog + RTP Analizi İletişim Kutusundaki seçili akışları listeden kaldır + + + Graph + Grafik + + + <small><i>A hint.</i></small> + <small><i>Bir ipucu.</i></small> + + + &Export + &Dışa Aktar + + + Open export menu + Dışa aktarma menüsünü aç + + + CSV + CSV + + + Save tables as CSV. + Tabloları CSV olarak kaydedin. + + + Current Tab Stream CSV + Geçerli Sekme Akışı CSV'si + + + Save the table on the current tab as CSV. + Mevcut sekmedeki tabloyu CSV olarak kaydedin. + + + All Tab Streams CSV + Tüm Sekme Akışları CSV + + + Save the table from all tabs as CSV. + Tabloyu tüm sekmelerden CSV olarak kaydedin. + + + Save Graph + Grafiği Kaydet + + + Save the graph image. + Grafik görüntüsünü kaydedin. + + + Go to Packet + Pakete Git + + + Select the corresponding packet in the packet list. + Paket listesinden ilgili paketi seçin. + + + G + G + + + Next Problem Packet + Sonraki Sorun Paketi + + + Go to the next problem packet + Sonraki sorun paketine git + + + N + N + + + Prepare &Filter + Hazırla &Filtrele + + + Prepare a filter matching the selected stream(s). + Seçilen akış(lar)la eşleşen bir filtre hazırlayın. + + + &Current Tab + &Geçerli Sekme + + + Prepare a filter matching current tab. + Geçerli sekmeyle eşleşen bir filtre hazırlayın. + + + &All Tabs + &Tüm Sekmeler + + + Prepare a filter matching all tabs. + Tüm sekmelerle eşleşen bir filtre hazırlayın. + + + RTP Stream Analysis + RTP Akış Analizi + + + Save Graph As… + Grafiği Farklı Kaydet… + + + G: Go to packet, N: Next problem packet + G: Pakete git, N: Sonraki sorun paketi + + + Portable Document Format (*.pdf) + Taşınabilir Belge Formatı (*.pdf) + + + Portable Network Graphics (*.png) + Taşınabilir Ağ Grafikleri (* .png) + + + Windows Bitmap (*.bmp) + Windows Bit Eşlem (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG Dosya Değişim Biçimi (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + Virgülle ayrılmış değerler (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %1, %2'de PCM'yi desteklemiyor. Tercih edilen biçim %3 + + + + RtpPlayerDialog + + RTP Player + RTP Oynatıcı + + + Play + Oynat + + + Source Address + Kaynak Adresi + + + Source Port + Kaynak Bağlantı noktası + + + Destination Address + Hedef Adres + + + Destination Port + Hedef Bağlantı Noktası + + + SSRC + SSRC + + + Setup Frame + Kurulum Çerçevesi + + + Packets + Paketler + + + Time Span (s) + Zaman Aralığı (s) + + + Payloads + Yükler + + + <small><i>No audio</i></small> + <small><i>Ses yok</i></small> + + + Start playback of all unmuted streams + Sesi açılmamış tüm akışların oynatımını başlat + + + Pause/unpause playback + Oynatmayı duraklat/duraklat + + + Stop playback + Duraklat + + + Enable/disable skipping of silence during playback + Oynatma sırasında sessizliğin atlanmasını etkinleştirme/devre dışı bırakma + + + Min silence: + Asgari sessizlik: + + + Minimum silence duration to skip in seconds + Saniye cinsinden atlanacak asgari sessizlik süresi + + + Output Device: + Çıktı Aygıtı: + + + Output Audio Rate: + Çıkış Ses Oranı: + + + Jitter Buffer: + Titreşim Arabelleği: + + + The simulated jitter buffer in milliseconds. + Milisaniye cinsinden simüle edilmiş titreşim arabelleği. + + + Playback Timing: + Oynatma Zamanlaması: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Titreşim Arabelleği</strong>: Son kullanıcı tarafından duyulan RTP akışını simüle etmek için titreşim arabelleğini kullanın. +<br/> +<strong>RTP Zaman Damgası</strong>: Gelen paket zamanı yerine RTP Zaman Damgasını kullanın. Bu, RTP akışını kullanıcının duyduğu gibi yeniden oluşturmaz, ancak RTP tünellenirken ve orijinal paket zamanlaması eksik olduğunda kullanışlıdır. +<br/> +<strong>Kesintisiz Mod</strong>: RTP Zaman Damgasını yoksayın. Akışı tamamlandıkça oynatın. Bu, RTP zaman damgası eksik olduğunda kullanışlıdır. + + + Jitter Buffer + Titreşim Arabelleği + + + RTP Timestamp + RTP Zaman Damgası + + + Uninterrupted Mode + Kesintisiz Mod + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>Zaman damgalarını günün saati (işaretli) veya yakalama başlangıcından itibaren saniye (işaretsiz) olarak görüntüleyin.</p></body></html> + + + Time of Day + Günün zamanı + + + &Export + &Dışa Aktar + + + Export audio of all unmuted selected channels or export payload of one channel. + Sesi açılmamış tüm seçili kanalların sesini dışa aktarın veya bir kanalın yükünü dışa aktarın. + + + From &cursor + &imleçten + + + Save audio data started at the cursor + İmleçte başlatılan ses verilerini kaydet + + + &Stream Synchronized Audio + &Senkronize Ses Akışı + + + Save audio data synchronized to start of the earliest stream. + En erken akışın başlaması için senkronize edilmiş ses verilerini kaydedin. + + + &File Synchronized Audio + &Dosya Senkronize Ses + + + Save audio data synchronized to start of the capture file. + Yakalama dosyasının başlangıcına senkronize edilmiş ses verilerini kaydedin. + + + &Payload + &Yük + + + Save RTP payload of selected stream. + Seçilen akışın RTP yükünü kaydedin. + + + Reset Graph + Grafiği Sıfırla + + + Reset the graph to its initial state. + Grafiği ilk durumuna sıfırlayın. + + + Go To Setup Packet + Kurulum Paketine Git + + + Go to setup packet of stream currently under the cursor + Şu anda imlecin altındaki akış kurulum paketine gidin + + + Mute + Sessiz + + + Mute selected streams + Seçili akışların sesini kapat + + + Unmute + Sesini aç + + + Unmute selected streams + Seçili akışların sesini aç + + + Invert muting of selected streams + Seçili akışların sessize alınmasını tersine çevir + + + Route audio to left channel of selected streams + Sesi seçilen akışların sol kanalına yönlendir + + + Route audio to left and right channel of selected streams + Sesi seçilen akışların sol ve sağ kanalına yönlendirin + + + Route audio to right channel of selected streams + Sesi seçilen akışların sağ kanalına yönlendirin + + + Remove Streams + Akışları Kaldır + + + Remove selected streams from the list + Seçili akışları listeden kaldır + + + All + Tümü + + + Select all + Tümünü seç + + + None + Yok + + + Clear selection + Seçimi temizle + + + Invert + Evir + + + Invert selection + Seçimi tersine çevir + + + Play/Pause + Oynat/Duraklat + + + Start playing or pause playing + Oynatmaya başlayın veya oynatmayı duraklatın + + + Stop + Dur + + + Stop playing + Oynatmayı durdur + + + I&naudible streams + D&uyulamayan akışlar + + + Select/Deselect inaudible streams + Duyulamayan akışları seçin/seçimi kaldırın + + + Inaudible streams + Duyulamayan akışlar + + + &Select + &Seç + + + Select inaudible streams + Duyulamayan akışları seçin + + + &Deselect + &Seçimi kaldır + + + Deselect inaudible streams + Duyulamayan akışların seçimini kaldırın + + + Prepare &Filter + Hazırla &Filtrele + + + Prepare a filter matching the selected stream(s). + Seçilen akış(lar)la eşleşen bir filtre hazırlayın. + + + R&efresh streams + A&kışları yenile + + + Read captured packets from capture in progress to player + Yakalanan paketleri yakalama sürecinden oynatıcıya oku + + + Zoom In + Yakınlaştır + + + SR (Hz) + SR (Hz) + + + Sample rate of codec + Örnek kod çözücü oranı + + + PR (Hz) + PR (Hz) + + + Play rate of decoded audio (depends e. g. on selected sound card) + Kodu çözülen sesin oynatma hızı (örn. seçilen ses kartına bağlıdır) + + + Zoom Out + Uzaklaştır + + + Move Left 10 Pixels + 10 Piksel Sola Taşı + + + Move Right 10 Pixels + 10 Piksel Sağa Taşı + + + Move Left 1 Pixels + 1 Piksel Sola Taşı + + + Move Right 1 Pixels + 1 Piksel Sağa Taşı + + + Go To Packet Under Cursor + İmleç Altındaki Pakete Git + + + Go to packet currently under the cursor + İmlecin altındaki pakete git + + + Play the stream + Akışı oynat + + + To Left + Sola + + + Left + Right + Sol + Sağ + + + To Right + Sağa + + + Invert Muting + Muting'i Ters Çevir + + + No devices available + Kullanılabilir cihaz yok + + + Select + Seç + + + Audio Routing + Ses Yönlendirme + + + &Play Streams + &Akışları Oynat + + + Open RTP player dialog + RTP oynatıcı iletişim kutusunu aç + + + &Set playlist + &Oynatma listesi ayarla + + + Replace existing playlist in RTP Player with new one + RTP Player'daki mevcut çalma listesini yenisiyle değiştirin + + + &Add to playlist + &Oynatma listesine ekle + + + Add new set to existing playlist in RTP Player + RTP Player'da mevcut çalma listesine yeni set ekle + + + &Remove from playlist + &Oynatma listesinden kaldır + + + Remove selected streams from playlist in RTP Player + RTP Player'da seçilen akışları oynatma listesinden kaldır + + + No Audio + Ses Yok + + + Decoding streams... + Akışların kodunu çözme... + + + Out of Sequence + Sıra Dışı + + + Jitter Drops + Titreşim Damlaları + + + Wrong Timestamps + Yanlış Zaman Damgaları + + + Inserted Silence + Sessizlik Eklendi + + + Double click on cell to change audio routing + Ses yönlendirmesini değiştirmek için hücreye çift tıklayın + + + %1 streams + %1 akış + + + , %1 selected + , %1 seçildi + + + , %1 not muted + , %1 sesi kapatılmadı + + + , start: %1. Double click on graph to set start of playback. + , başlangıç: %1. Oynatmanın başlangıcını ayarlamak için grafiğe çift tıklayın. + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + , başlangıç: %1, imleç: %2. %3 paketine gitmek için "G"ye basın. Oynatmanın başlangıcını ayarlamak için grafiğe çift tıklayın. + + + Playback of stream %1 failed! + %1 akışının oynatılması başarısız oldu! + + + Automatic + Otomatik + + + WAV (*.wav) + WAV (*.wav) + + + Sun Audio (*.au) + Güneş Ses (*.au) + + + Save audio + Sesi kaydet + + + Raw (*.raw) + Ham (*.raw) + + + Save payload + Yükü kaydet + + + Warning + Uyarı + + + No stream selected or none of selected streams provide audio + Seçili akış yok veya seçilen akışların hiçbiri ses sağlamıyor + + + Error + Hata + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + Seçilen tüm akışlar aynı oynatma hızını kullanmalıdır. Elle ayarlanmış Çıkış Ses Oranı yardımcı olabilir. + + + No streams are suitable for save + Hiçbir akış kaydetmeye uygun değil + + + Save failed! + Kayıt başarısız! + + + Can't write header of AU file + AU dosyasının başlığı yazılamıyor + + + Can't write header of WAV file + WAV dosyasının başlığı yazılamıyor + + + Payload save works with just one audio stream. + Yük kaydı, yalnızca bir ses akışıyla çalışır. + + + Double click to change audio routing + Ses yönlendirmesini değiştirmek için çift tıklayın + + + Preparing to play... + Oynamaya hazırlanıyor... + + + Unknown + Bilinmiyor + + + + RtpStreamDialog + + Dialog + Diyalog + + + Source Address + Kaynak Adresi + + + Source Port + Kaynak Bağlantı noktası + + + Destination Address + Hedef Adres + + + Destination Port + Hedef Bağlantı Noktası + + + SSRC + SSRC + + + Start Time + Başlangıç Saati + + + Duration + Süre + + + Payload + Yük + + + Packets + Paketler + + + Lost + Kayıp + + + Max Delta (ms) + Azami Delta (ms) + + + Max Jitter + Azami Titreme + + + Mean Jitter + Ortalama Titreme + + + Status + Durum + + + <small><i>A hint.</i></small> + <small><i>Bir ipucu.</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Yalnızca geçerli görüntüleme filtresiyle eşleşen konuşmaları göster</p></body></html> + + + Limit to display filter + Görüntüleme filtresini sınırla + + + Time of Day + Günün zamanı + + + Find &Reverse + Tersini &Bul + + + Prepare &Filter + Hazırla &Filtrele + + + &Export + &Dışa Aktar + + + &Analyze + &Analiz + + + Open the analysis window for the selected stream(s) and add it to it + Seçilen akış(lar) için analiz penceresini açın ve buna ekleyin + + + Find the reverse stream matching the selected forward stream. + Seçili ileri akışla eşleşen ters akışı bulun. + + + Min Delta (ms) + Asg Delta (ms) + + + Mean Delta (ms) + Ortalama Delta (ms) + + + Min Jitter + Asg Titreşim + + + All forward/reverse stream actions + Tüm ileri/geri akış işlemleri + + + R + R + + + Find All &Pairs + Tüm &Çiftleri Bul + + + Select all streams which are paired in forward/reverse relation + İleri/geri ilişkide eşleştirilen tüm akışları seçin + + + Shift+R + Shift+R + + + Find Only &Singles + Yalnızca &Tekleri Bul + + + Find all streams which don't have paired reverse stream + Eşleştirilmiş ters akışa sahip olmayan tüm akışları bulun + + + Ctrl+R + Ctrl+R + + + Mark Packets + Paketleri İşaretle + + + Mark the packets of the selected stream(s). + Seçili akış(lar)ın paketlerini işaretleyin. + + + M + M + + + All + Tümü + + + Select all + Tümünü seç + + + None + Yok + + + Clear selection + Seçimi temizle + + + Invert + Evir + + + Invert selection + Seçimi tersine çevir + + + Go To Setup + Kuruluma Git + + + Go to the setup packet for this stream. + Bu akış için kurulum paketine gidin. + + + G + G + + + Prepare a filter matching the selected stream(s). + Seçilen akış(lar)la eşleşen bir filtre hazırlayın. + + + P + P + + + Export the stream payload as rtpdump + Akış yükünü rtpdump olarak dışa aktarın + + + E + D + + + A + A + + + Cop&y + Kopyal&a + + + Open copy menu + Kopyalama menüsünü aç + + + Copy as CSV + CSV olarak kopyala + + + Copy stream list as CSV. + Akış listesini CSV olarak kopyalayın. + + + Copy as YAML + YAML olarak kopyala + + + Copy stream list as YAML. + Akış listesini YAML olarak kopyalayın. + + + RTP Streams + RTP Akışları + + + Select + Seç + + + as CSV + CSV olarak + + + as YAML + YAML olarak + + + %1 streams + %1 akış + + + , %1 selected, %2 total packets + , %1 seçildi, toplam %2 paket + + + Save RTPDump As… + RTPDump'ı Farklı Kaydet… + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark - SCTP Dernekleri + + + ID + KİMLİK + + + Port 1 + 1 numaralı bağlantı noktası + + + Port 2 + 2 numaralı bağlantı noktası + + + Number of Packets + Paket Sayısı + + + Number of DATA Chunks + VERİ Yığınlarının Sayısı + + + Number of Bytes + Bayt Sayısı + + + Filter Selected Association + Seçili İlişkilendirmeyi Filtrele + + + Analyze + Analiz + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark - Analiz İlişkilendirme + + + TabWidget + Sekme Widget'ı + + + Statistics + İstatistikler + + + Chunk Statistics + Parça İstatistikleri + + + Filter Association + Filtre İlişkilendirme + + + Number of Data Chunks from EP2 to EP1: + EP2'den EP1'e Veri Parçası Sayısı: + + + Checksum Type: + Sağlama Türü: + + + Number of Data Chunks from EP1 to EP2: + EP1'den EP2'ye Veri Parçası Sayısı: + + + Number of Data Bytes from EP1 to EP2: + EP1'den EP2'ye Veri Bayt Sayısı: + + + Number of Data Bytes from EP2 to EP1: + EP2'den EP1'e Veri Baytları Sayısı: + + + Endpoint 1 + Uç nokta 1 + + + Graph TSN + Grafik TSN + + + Graph Bytes + Grafik Baytları + + + Requested Number of Inbound Streams: + İstenen Gelen Akış Sayısı: + + + Port: + Bağlantı Noktası: + + + Sent Verification Tag: + Gönderilen Doğrulama Etiketi: + + + Minimum Number of Inbound Streams: + Minimum Gelen Akış Sayısı: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + INIT Chunk'tan IP adreslerinin tam listesi: + + + Minimum Number of Outbound Streams: + Minimum Giden Akış Sayısı: + + + Graph Arwnd + Grafik Arwnd + + + Endpoint 2 + Uç nokta 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + INIT_ACK Chunk'tan IP adreslerinin tam listesi: + + + Provided Number of Outbound Streams: + Sağlanan Giden Akış Sayısı: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + SCTP Analiz İlişkisi: %1 Bağlantı Noktası1 %2 Bağlantı Noktası2 %3 + + + No Association found for this packet. + Bu paket için İlişki bulunamadı. + + + Warning + Uyarı + + + Could not find SCTP Association with id: %1 + Şu kimliğe sahip SCTP İlişkisi bulunamadı: %1 + + + Complete list of IP addresses from INIT Chunk: + INIT Chunk'tan IP adreslerinin tam listesi: + + + Complete list of IP addresses from INIT_ACK Chunk: + INIT_ACK Chunk'tan IP adreslerinin tam listesi: + + + List of Used IP Addresses + Kullanılan IP Adreslerinin Listesi + + + Used Number of Inbound Streams: + Kullanılan Gelen Akış Sayısı: + + + Used Number of Outbound Streams: + Kullanılan Giden Akış Sayısı: + + + + SCTPChunkStatisticsDialog + + Dialog + Diyalog + + + Association + İlişkilendirme + + + Endpoint 1 + Uç nokta 1 + + + Endpoint 2 + Uç nokta 2 + + + Save Chunk Type Order + Yığın Türü Sırasını Kaydet + + + Hide Chunk Type + Yığın Türünü Gizle + + + Remove the chunk type from the table + Yığın türünü tablodan kaldırın + + + Chunk Type Preferences + Yığın Türü Tercihleri + + + Go to the chunk type preferences dialog to show or hide other chunk types + Diğer yığın türlerini göstermek veya gizlemek için yığın türü tercihleri iletişim kutusuna gidin + + + Show All Registered Chunk Types + Tüm Kayıtlı Yığın Türlerini Göster + + + Show all chunk types with defined names + Tanımlanmış adlara sahip tüm yığın türlerini göster + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + SCTP Yığın İstatistikleri: %1 Bağlantı Noktası1 %2 Bağlantı Noktası2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + SCTP Grafiği + + + Reset to full size + Tam boyuta sıfırla + + + Save Graph + Grafiği Kaydet + + + goToPacket + PaketeGit + + + Go to Packet + Pakete Git + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP Verileri ve Adv. Kayıt Zaman İçinde Pencere: %1 Bağlantı Noktası1 %2 Bağlantı Noktası2 %3 + + + No Data Chunks sent + Veri Yığını gönderilmedi + + + Arwnd + Arwnd + + + time [secs] + süre [saniye] + + + Advertised Receiver Window [Bytes] + Reklamı Yapılan Alıcı Penceresi [Bayt] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>Grafik %1: a_rwnd=%2 Zaman=%3 saniye </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + SCTP Grafiği + + + Reset to full size + Tam boyuta sıfırla + + + Save Graph + Grafiği Kaydet + + + goToPacket + PaketeGit + + + Go to Packet + Pakete Git + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP Verileri ve Adv. Kayıt Zaman İçinde Pencere: %1 Bağlantı Noktası1 %2 Bağlantı Noktası2 %3 + + + No Data Chunks sent + Veri Yığını gönderilmedi + + + Bytes + Bayt + + + time [secs] + süre [saniye] + + + Received Bytes + Alınan Bayt + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>Grafik %1: Alınan bayt=%2 Zaman=%3 saniye </i></small> + + + + SCTPGraphDialog + + SCTP Graph + SCTP Grafiği + + + Relative TSNs + Göreli TSN'ler + + + Only SACKs + Sadece SACKs + + + Only TSNs + Yalnızca TSN'ler + + + Show both + İkisini de göster + + + Reset to full size + Tam boyuta sıfırla + + + Save Graph + Grafiği Kaydet + + + goToPacket + PaketeGit + + + Go to Packet + Pakete Git + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + Zaman içinde SCTP TSN'leri ve SACK'ler: %1 Bağlantı Noktası1 %2 Bağlantı Noktası2 %3 + + + No Data Chunks sent + Veri Yığını gönderilmedi + + + CumTSNAck + CumTSNAck + + + Gap Ack + Boşluk Ack + + + NR Gap Ack + NR Boşluğu Ack + + + Duplicate Ack + Yinelenen Onay + + + TSN + TSN + + + time [secs] + süre [saniye] + + + TSNs + TSNs + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 Süre: %3 saniye </i></small> + + + Portable Document Format (*.pdf) + Taşınabilir Belge Formatı (*.pdf) + + + Portable Network Graphics (*.png) + Taşınabilir Ağ Grafikleri (* .png) + + + Windows Bitmap (*.bmp) + Windows Bit Eşlem (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG Dosya Değişim Biçimi (*.jpeg *.jpg) + + + Save Graph As… + Grafiği Farklı Kaydet… + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <small><i>Bir komut seçin ve isterseniz bir filtre girin, ardından Uygula'ya basın.</i></small> + + + Command: + Komut: + + + SCSI Service Response Times + SCSI Hizmet Yanıt Süreleri + + + + SearchFrame + + Frame + Çerçeve + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>Paket listesinin Bilgi sütununda (özet bölmesi), kodu çözülen paket görüntüleme etiketlerinde (ağaç görünümü bölmesi) veya ASCII ile dönüştürülmüş paket verisinde (onaltılık görünüm bölmesi) arama yapın.</ p></body></html> + + + Packet list + Paket listesi + + + Packet details + Paket ayrıntıları + + + Packet bytes + Paket bayt + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Dar (UTF-8 ve ASCII) veya geniş (UTF-16) karakterler içeren dizeleri arayın.</p></body></html> + + + Narrow & Wide + Dar ve Geniş + + + Narrow (UTF-8 / ASCII) + Dar (UTF-8 / ASCII) + + + Wide (UTF-16) + Geniş (UTF-16) + + + Case sensitive + Büyük küçük harf duyarlı + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>Görüntü filtresi sözdizimini (ör. ip.addr==10.1.1.1), onaltılık bir dizeyi (ör. fffffda5), düz bir dizeyi (ör. My Dize) veya normal bir ifadeyi (ör. colou?r) kullanarak veri arayın.</p></body></html> + + + Display filter + Ekran filtresi + + + Hex value + Onaltılık değer + + + String + Dize + + + Regular Expression + Düzenli İfade + + + Find + Bul + + + Cancel + İptal et + + + No valid search type selected. Please report this to the development team. + Geçerli bir arama türü seçilmedi. Lütfen bunu geliştirme ekibine bildirin. + + + Invalid filter. + Geçersiz filtre. + + + That filter doesn't test anything. + O filtre hiçbir şeyi test etmez. + + + That's not a valid hex string. + Bu geçerli bir onaltılı dize değil. + + + You didn't specify any text for which to search. + Aranacak herhangi bir metin belirtmediniz. + + + No valid character set selected. Please report this to the development team. + Geçerli karakter kümesi seçilmedi. Lütfen bunu geliştirme ekibine bildirin. + + + No valid search area selected. Please report this to the development team. + Geçerli arama alanı seçilmedi. Lütfen bunu geliştirme ekibine bildirin. + + + Searching for %1… + %1 aranıyor… + + + No packet contained those bytes. + Hiçbir paket bu baytları içermiyordu. + + + No packet contained that string in its Info column. + Bilgi sütununda bu dize hiçbir paket içermiyordu. + + + No packet contained that string in its dissected display. + Parçalanmış görüntüsünde hiçbir paket bu dizeyi içermiyordu. + + + No packet contained that string in its converted data. + Dönüştürülen verilerinde bu dize hiçbir paket içermiyordu. + + + No packet matched that filter. + Bu filtreyle eşleşen paket yok. + + + + SequenceDialog + + Call Flow + Çağrı Akışı + + + Time + Süre + + + Comment + Yorum + + + No data + Veri yok + + + %Ln node(s) + + %Ln node + + + + %Ln item(s) + + %Ln item + + + + Portable Document Format (*.pdf) + Taşınabilir Belge Formatı (*.pdf) + + + Portable Network Graphics (*.png) + Taşınabilir Ağ Grafikleri (* .png) + + + Windows Bitmap (*.bmp) + Windows Bit Eşlem (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG Dosya Değişim Biçimi (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII (*.txt) + + + Save Graph As… + Grafiği Farklı Kaydet… + + + Flow + Akış + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Değerli ve şaşırtıcı zaman kazandıran klavye kısayolları</h3> +<table><tbody> + +<tr><th>+</th><td>Yaklaştır</td></th> +<tr><th>-</th><td>Uzaklaştır</td></th> +<tr><th>0</th><td>Grafiği ilk durumuna sıfırlayın</td></th> + +<tr><th>→</th><td>10 piksel sağa taşı</td></th> +<tr><th>←</th><td>10 piksel sola taşı</td></th> +<tr><th>↑</th><td>10 piksel yukarı taşı</td></th> +<tr><th>↓</th><td>10 piksel aşağı taşı</td></th> +<tr><th><i>Shift+</i>→</th><td>1 piksel sağa taşı</td></th> +<tr><th><i>Shift+</i>←</th><td>1 piksel sola taşı</td></th> +<tr><th><i>Shift+</i>↑</th><td>1 piksel yukarı taşı</td></th> +<tr><th><i>Shift+</i>↓</th><td>1 piksel aşağı taşı</td></th> + +<tr><th>g</th><td>İmlecin altındaki pakete git</td></th> +<tr><th>n</th><td>Sonraki pakete git</td></th> +<tr><th>p</th><td>Önceki pakete git</td></th> + +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>Bir ipucu</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>Yalnızca mevcut görüntüleme filtresiyle eşleşen akışları göster</p></body></html> + + + Limit to display filter + Görüntüleme filtresini sınırla + + + Flow type: + Akış tipi: + + + Addresses: + Adresler: + + + Any + Herhangi biri + + + Network + + + + Reset Diagram + Şemayı Sıfırla + + + Reset &Diagram + Şemayı &Sıfırla + + + Reset the diagram to its initial state. + Diyagramı ilk durumuna sıfırlayın. + + + 0 + 0 + + + &Reset Diagram + &Şemayı Sıfırla + + + Reset the diagram to its initial state + Şemayı ilk durumuna sıfırlayın + + + &Export + &Dışa Aktar + + + Export diagram + Şemayı dışa aktar + + + Zoom In + Yakınlaş + + + + + + + + + Zoom Out + Uzaklaş + + + - + - + + + Move Up 10 Pixels + 10 Piksel Yukarı Taşı + + + Up + Üst + + + Move Left 10 Pixels + 10 Piksel Sola Taşı + + + Left + Sol + + + Move Right 10 Pixels + 10 Piksel Sağa Taşı + + + Right + Sağa + + + Move Down 10 Pixels + 10 Piksel Aşağı Taşı + + + Down + Aşağı + + + Move Up 1 Pixel + 1 Piksel Yukarı Taşı + + + Shift+Up + Shift+Yukarı + + + Move Left 1 Pixel + Sola 1 Piksel Taşı + + + Shift+Left + Shift+Sol + + + Move Right 1 Pixel + 1 Piksel Sağa Taşı + + + Shift+Right + Shift+Sağ + + + Move Down 1 Pixel + 1 Piksel Aşağı Taşı + + + Shift+Down + Shift+Aşağı + + + Go To Packet Under Cursor + İmleç Altındaki Pakete Git + + + Go to packet currently under the cursor + İmlecin altındaki pakete git + + + G + G + + + All Flows + Tüm Akışlar + + + Show flows for all packets + Tüm paketler için akışları göster + + + 1 + 1 + + + TCP Flows + TCP Akışları + + + Show only TCP flow information + Yalnızca TCP akış bilgilerini göster + + + Go To Next Packet + Sonraki Pakete Git + + + Go to the next packet + Sonraki pakete git + + + N + N + + + Go To Previous Packet + Önceki Pakete Git + + + Go to the previous packet + Önceki pakete git + + + P + P + + + Select RTP Stream + RTP Akışını Seçin + + + Select RTP stream in RTP Streams dialog + RTP Akışları iletişim kutusunda RTP akışını seçin + + + S + S + + + Deselect RTP Stream + RTP Akışının Seçimini Kaldır + + + Deselect RTP stream in RTP Streams dialog + RTP Akışları iletişim kutusunda RTP akışının seçimini kaldırın + + + D + D + + + + ShortcutListModel + + Shortcut + Kısayol + + + Name + Ad + + + Description + Tanım + + + + ShowPacketBytesDialog + + Show Packet Bytes + Paket Baytlarını Göster + + + Hint. + İpucu. + + + Decode as + Olarak deşifre et + + + Show as + Olarak göstermek + + + Start + Başla + + + End + Son + + + Find: + Bul: + + + Find &Next + Sonrakini &Bul + + + Frame %1, %2, %Ln byte(s). + + + + + + None + Yok + + + Base64 + Temel64 + + + Compressed + Sıkıştırılmış + + + Hex Digits + Onaltılı Rakamlar + + + Percent-Encoding + Yüzde-Kodlama + + + Quoted-Printable + Alıntı-Yazdırılabilir + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII ve Kontrol + + + C Array + C Dizisi + + + EBCDIC + EBCDIC + + + Hex Dump + Onaltılık Dökümü + + + HTML + HTML + + + Image + Görüntü + + + Raw + Ham + + + Rust Array + Rust Dizisi + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Yazdır + + + Copy + Kopyala + + + Save as… + Farklı kaydet… + + + Save Selected Packet Bytes As… + Seçili Paket Baytlarını Farklı Kaydet… + + + Displaying %Ln byte(s). + + + + + + JSON + JSON + + + Regex Find: + Normal İfade Bul: + + + + ShowPacketBytesTextEdit + + Show Selected + Seçilenleri Göster + + + Show All + Tümünü Görüntüle + + + + SplashOverlay + + Initializing dissectors + Tespit ediciler başlatılıyor + + + Initializing tap listeners + Dokunma dinleyicilerini başlatma + + + Initializing external capture plugins + Harici yakalama eklentilerini başlatılıyor + + + Registering dissectors + Tespit edicilerin kaydedilmesi + + + Registering plugins + Registering dissector + Eklentileri kaydetme + + + Handing off dissectors + Tespit edicileri dağıtma + + + Handing off plugins + Eklentileri dağıtma + + + Loading Lua plugins + Lua eklentilerini yükleme + + + Removing Lua plugins + Lua eklentilerini kaldırma + + + Loading module preferences + Modül tercihleri yükleniyor + + + Finding local interfaces + Yerel arayüzleri bulma + + + Applying changed preferences + + + + (Unknown action) + (Bilinmeyen eylem) + + + + StatsTreeDialog + + Configuration not found + Yapılandırma bulunamadı + + + Unable to find configuration for %1. + %1 için yapılandırma bulunamadı. + + + + StripHeadersDialog + + Dialog + İletişim + + + Display filter: + Görüntüleme filtresi: + + + + SupportedProtocolsDialog + + Dialog + Diyalog + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>Alan adları listesinde arama yapın.</p></body></html> + + + Search: + Ara: + + + <small><i>Gathering protocol information…</i></small> + <small><i>Protokol bilgileri toplanıyor…</i></small> + + + Supported Protocols + Desteklenen Protokoller + + + %1 protocols, %2 fields. + %1 protokol, %2 alan. + + + + SupportedProtocolsModel + + Name + Ad + + + Filter + Filtrele + + + Type + Biçim + + + Description + Tanımlama + + + + SyntaxLineEdit + + Invalid filter: %1 + Geçersiz filtre: %1 + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + "%1", "%2" yerine kullanımdan kaldırıldı. Ayrıntılar için Yardım bölümü 6.4.8'e bakın. + + + %1 + %1 + + + + TCPStreamDialog + + Dialog + Diyalog + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Değerli ve şaşırtıcı zaman kazandıran klavye kısayolları</h3> +<table><tbody> + +<tr><th>+</th><td>Yaklaştır</td></th> +<tr><th>-</th><td>Uzaklaş</td></th> +<tr><th>x</th><td>X Ekseninde yaklaştır</td></th> +<tr><th>X</th><td>X Ekseninde uzaklaştır</td></th> +<tr><th>y</th><td>Y Ekseninde yaklaştır</td></th> +<tr><th>Y</th><td>Y Ekseninde uzaklaştır</td></th> +<tr><th>0</th><td>Grafiği ilk durumuna sıfırla</td></th> + +<tr><th>→</th><td>10 piksel sağa taşı</td></th> +<tr><th>←</th><td>10 piksel sola taşı</td></th> +<tr><th>↑</th><td>10 piksel yukarı taşı</td></th> +<tr><th>↓</th><td>10 piksel aşağı taşı</td></th> +<tr><th><i>Shift+</i>→</th><td>1 piksel sağa taşı</td></th> +<tr><th><i>Shift+</i>←</th><td>1 piksel sola taşı</td></th> +<tr><th><i>Shift+</i>↑</th><td>1 piksel yukarı taşı</td></th> +<tr><th><i>Shift+</i>↓</th><td>1 piksel aşağı taşı</td></th> + +<tr><th><i>Pg Up</i></th><td>Sonraki akış</td></th> +<tr><th><i>Pg Dn</i></th><td>Önceki akış</td></th> +<tr><th>d</th><td>Yön değiştir (TCP uç noktalarını değiştir)</td></th> +<tr><th>g</th><td>İmlecin altındaki pakete git</td></th> + +<tr><th>z</th><td>Fare sürükle / yakınlaştırma arasında geçiş yap</td></th> +<tr><th>s</th><td>Göreceli / mutlak sıra numaralarını değiştir</td></th> +<tr><th>t</th><td>Yakalama / oturum zamanı kaynağını aç/kapat</td></th> +<tr><th>Boşluk</th><td>Artı işaretlerini aç/kapat</td></th> + +<tr><th>1</th><td>Gidiş-Dönüş Süresi grafiği</td></th> +<tr><th>2</th><td>Verim grafiği</td></th> +<tr><th>3</th><td>Stevens tarzı Zaman / Sıra grafiği</td></th> +<tr><th>4</th><td>tcpizleme tarzı Zaman / Sıra grafiği</td></th> +<tr><th>5</th><td>Pencere Ölçekleme grafiği</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>Kısayollar için fareyle üzerine gelin</i></small> + + + Type + Biçim + + + MA Window (s) + MA Pencere(ler)i + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + Grafiğe tıklayarak SACK segmentlerinin yanı sıra veri paketlerinin seçilmesine izin verin + + + Select SACKs + select SACKs + SACK'leri seçin + + + Stream + Akış + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Bağlantının yönünü değiştirin (karşı akışa bakın).</p></body></html> + + + Switch Direction + Yön Değiştir + + + Mouse + Fare + + + Drag using the mouse button. + Fare düğmesini kullanarak sürükleyin. + + + drags + sürükler + + + Select using the mouse button. + Fare düğmesini kullanarak seçin. + + + zooms + yakınlaştırır + + + Display Round Trip Time vs Sequence Number + Gidiş-Dönüş Süresini Sıra Numarasına Karşı Görüntüleme + + + RTT By Sequence Number + Sıra Numarasına Göre RTT + + + Display graph of Segment Length vs Time + Segment Uzunluğu - Zaman grafiğini görüntüle + + + Segment Length + Segment Uzunluğu + + + Display graph of Mean Transmitted Bytes vs Time + Ortalama İletilen Bayt ve Zamanın grafiğini görüntüle + + + Display graph of Mean ACKed Bytes vs Time + Ortalama ACK Edilen Bayt ve Zamanın grafiğini görüntüle + + + Goodput + İyi çıktı + + + Display graph of Receive Window Size vs Time + Alma Penceresi Boyutuna karşı Zaman grafiğini görüntüle + + + Rcv Win + Rcv Galibiyet + + + Display graph of Outstanding Bytes vs Time + Olağanüstü Bayt - Zaman grafiğini görüntüle + + + Bytes Out + Bayt Bitti + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Grafiği ilk durumuna sıfırlayın.</p></body></html> + + + Reset + Sıfırla + + + Reset Graph + Grafiği Sıfırla + + + Reset the graph to its initial state. + Grafiği ilk durumuna sıfırlayın. + + + 0 + 0 + + + Zoom In + Yakınlaştır + + + + + + + + + Zoom Out + Uzaklaştır + + + - + - + + + Move Up 10 Pixels + 10 Piksel Yukarı Taşı + + + Up + Üst + + + Move Left 10 Pixels + 10 Piksel Sola Taşı + + + Left + Sol + + + Move Right 10 Pixels + 10 Piksel Sağa Taşı + + + Right + Sağa + + + Move Down 10 Pixels + 10 Piksel Aşağı Taşı + + + Down + Aşağı + + + Move Up 1 Pixel + 1 Piksel Yukarı Taşı + + + Shift+Up + Shift+Yukarı + + + Move Left 1 Pixel + Sola 1 Piksel Taşı + + + Shift+Left + Shift+Sol + + + Move Right 1 Pixel + 1 Piksel Sağa Taşı + + + Shift+Right + Shift+Sağ + + + Move Down 1 Pixel + 1 Piksel Aşağı Taşı + + + Shift+Down + Shift+Aşağı + + + Next Stream + Sonraki Akış + + + Go to the next stream in the capture + Yakalamadaki sonraki akışa git + + + PgUp + Sayfa yukarı + + + Previous Stream + Önceki Akış + + + Go to the previous stream in the capture + Yakalamada önceki akışa git + + + PgDown + Sayfa aşağı + + + Switch direction (swap TCP endpoints) + Yön değiştir (TCP uç noktalarını değiştir) + + + D + D + + + Go To Packet Under Cursor + İmleç Altındaki Pakete Git + + + Go to packet currently under the cursor + İmlecin altındaki pakete git + + + G + G + + + Drag / Zoom + Sürükle / Yakınlaştır + + + Toggle mouse drag / zoom behavior + Fare sürükle / yakınlaştırma davranışını değiştir + + + Z + Z + + + Relative / Absolute Sequence Numbers + Bağıl / Mutlak Sıra Numaraları + + + Toggle relative / absolute sequence numbers + Göreceli / mutlak sıra numaralarını değiştir + + + S + S + + + Capture / Session Time Origin + Yakalama / Oturum Süresi Menşei + + + Toggle capture / session time origin + Yakalama / oturum zamanı kaynağını aç/kapat + + + T + T + + + Crosshairs + Artılar + + + Toggle crosshairs + Artı işaretlerini aç/kapat + + + Space + Boşluk + + + Round Trip Time + Gidiş-dönüş süresi + + + Switch to the Round Trip Time graph + Gidiş-Dönüş Süresi grafiğine geçin + + + 1 + 1 + + + Throughput + Verim + + + Switch to the Throughput graph + Verim grafiğine geç + + + 2 + 2 + + + Time / Sequence (Stevens) + Zaman / Sıra (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + Stevens tarzı Zaman / Sıra grafiğine geçin + + + 3 + 3 + + + Window Scaling + Pencere Ölçekleme + + + Switch to the Window Scaling graph + Pencere Ölçekleme grafiğine geç + + + 5 + 5 + + + Time / Sequence (tcptrace) + Zaman / Sıra (tcpizi) + + + Switch to the tcptrace-style Time / Sequence graph + TCP izleme stili Zaman / Sıra grafiğine geçin + + + 4 + 4 + + + Zoom In X Axis + X Eksenini Yakınlaştır + + + X + X + + + Zoom Out X Axis + X Eksenini Uzaklaştır + + + Shift+X + Shift+X + + + Zoom In Y Axis + A Eksenini Yakınlaştır + + + Y + Y + + + Zoom Out Y Axis + A Eksenini Uzaklaştır + + + Shift+Y + Shift+Y + + + Save As… + Farklı Kaydet… + + + No Capture Data + Yakalama Verisi Yok + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 %2 pkts, %3 %4 %5 pkts, %6 + + + Sequence Numbers (Stevens) + Sıra Numaraları (Stevens) + + + Sequence Numbers (tcptrace) + Sıra Numaraları (tcpizleme) + + + (MA) + (MA) + + + (%1 Segment MA) + (%1 Segment MA) + + + [not enough data] + [yeterli veri yok] + + + for %1:%2 %3 %4:%5 + %1 için:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + Click to select packet + Paket seçmek için tıklayın + + + Packet + Paket + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Yakınlaştırmak için bırakın, x = %1 ila %2, y = %3 ila %4 + + + Unable to select range. + Aralık seçilemiyor. + + + Click to select a portion of the graph. + Grafiğin bir bölümünü seçmek için tıklayın. + + + Portable Document Format (*.pdf) + Taşınabilir Belge Formatı (*.pdf) + + + Portable Network Graphics (*.png) + Taşınabilir Ağ Grafikleri (* .png) + + + Windows Bitmap (*.bmp) + Windows Bit Eşlem (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG Dosya Değişim Biçimi (*.jpeg *.jpg) + + + Save Graph As… + Grafiği Farklı Kaydet… + + + + TLSKeylogDialog + + Dialog + + + + Browse… + Gözat… + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + Kaydet + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + Diyalog + + + Item + Öğe + + + <small><i>A hint.</i></small> + <small><i>Bir ipucu.</i></small> + + + Display filter: + Ekran filtresi: + + + Regenerate statistics using this display filter + Bu görüntü filtresini kullanarak istatistikleri yeniden oluşturun + + + Apply + Uygula + + + Copy + Kopyala + + + Copy a text representation of the tree to the clipboard + Ağacın bir metin temsilini panoya kopyalayın + + + Save as… + Save as... + Farklı Kaydet… + + + Save the displayed data in various formats + Görüntülenen verileri çeşitli biçimlerde kaydedin + + + Collapse All + Hepsini Daralt + + + Expand All + Hepsini Genişlet + + + Save Statistics As… + İstatistikleri Farklı Kaydet… + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + Düz metin dosyası (*.txt);;Virgülle ayrılmış değerler (*.csv);;XML belgesi (*.xml);;YAML belgesi (*.yaml) + + + Plain text file (*.txt) + Düz metin dosyası (*.txt) + + + Error saving file %1 + %1 dosyası kaydedilirken hata oluştu + + + + TimeShiftDialog + + Shift all packets by + Tüm paketleri kaydır + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + Paket için zamanı ayarlayın + + + to + için + + + …then set packet + ...then set packet + …sonra paketi ayarla + + + and extrapolate the time for all other packets + ve diğer tüm paketler için zamanı tahmin edin + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[DD-MM-YYYY] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + Tüm kaydırmaları geri al + + + Time Shift + Zaman Kaydırma + + + Frame numbers must be between 1 and %1. + Çerçeve numaraları 1 ile %1 arasında olmalıdır. + + + Invalid frame number. + Geçersiz çerçeve numarası. + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + Harita dosyası hatası + + + Could not open base file %1 for reading: %2 + %1 temel dosyası şu okuma için açılamadı: %2 + + + No endpoints available to map + Haritalanacak uç nokta yok + + + Unable to create temporary file + Geçici dosya oluşturulamıyor + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>Düz değerler yerine çözümlenmiş adresleri ve bağlantı noktası adlarını gösterin. İlgili ad çözümleme tercihi etkinleştirilmelidir.</p></body></html> + + + Name resolution + Ad çözümlemesi + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Yalnızca geçerli görüntüleme filtresiyle eşleşen konuşmaları göster</p></body></html> + + + Limit to display filter + Görüntüleme filtresini sınırla + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + <html><head/><body><p>Yalnızca filtre değeriyle eşleşen türleri göster</p></body></html> + + + Filter list for specific type + Belirli bir tür için filtre listesi + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>Başlangıç zamanı sütununda mutlak zamanları göster.</p></body></html> + + + GroupBox + GrupKutusu + + + Absolute start time + Mutlak başlangıç zamanı + + + Copy + Kopyala + + + Unknown + Bilinmiyor + + + + TrafficTree + + Resize all columns to content + Tüm sütunları içeriğe göre yeniden boyutlandır + + + Filter on stream id + Akış kimliğine göre filtrele + + + Copy %1 table + %1 tablosunu kopyala + + + as CSV + CSV olarak + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + Bu sayfanın tüm değerlerini CSV (Virgülle Ayrılmış Değerler) formatında panoya kopyalayın. + + + as YAML + YAML olarak + + + Copy all values of this page to the clipboard in the YAML data serialization format. + Bu sayfanın tüm değerlerini YAML veri serileştirme biçiminde panoya kopyalayın. + + + as JSON + JSON olarak + + + Copy all values of this page to the clipboard in the JSON data serialization format. + Bu sayfanın tüm değerlerini JSON veri serileştirme biçiminde panoya kopyalayın. + + + Save data as raw + Verileri ham olarak kaydet + + + Disable data formatting for export/clipboard and save as raw data + Dışa aktarma/pano için veri biçimlendirmesini devre dışı bırakın ve ham veri olarak kaydedin + + + + TrafficTreeHeaderView + + Less than + Daha az + + + Greater than + Daha büyük + + + Equal + Eşit + + + Columns to display + Görüntülenecek sütunlar + + + Filter %1 by + %1'e göre filtrele + + + Enter filter value + Filtre değerini girin + + + + TrafficTypesModel + + Protocol + İletişim Kuralı + + + + UatDialog + + Create a new entry. + Yeni bir giriş oluşturun. + + + Remove this entry. + Remove this profile. + Bu girişi kaldırın. + + + Copy this entry. + Copy this profile. + Bu girişi kopyalayın. + + + Move entry up. + Girişi yukarı taşı. + + + Move entry down. + Girişi aşağı taşı. + + + Clear all entries. + Tüm girişleri temizle. + + + Unknown User Accessible Table + Bilinmeyen Kullanıcı Erişilebilir Tablosu + + + Open + + + + + UatFrame + + Frame + Çerçeve + + + Create a new entry. + Yeni bir giriş oluşturun. + + + Remove this entry. + Bu girişi kaldırın. + + + Copy this entry. + Bu girişi kopyalayın. + + + Move entry up. + Girişi yukarı taşı. + + + Move entry down. + Girişi aşağı taşı. + + + Clear all entries. + Tüm girişleri temizle. + + + Copy entries from another profile. + Girişleri başka bir profilden kopyalayın. + + + Copy from + Şuradan kopyala + + + Unknown User Accessible Table + Bilinmeyen Kullanıcı Erişilebilir Tablosu + + + Open + + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>Yalnızca geçerli görüntüleme filtresiyle eşleşen konuşmaları göster</p></body></html> + + + Limit to display filter + Görüntüleme filtresini sınırla + + + Time of Day + Günün zamanı + + + Flow &Sequence + Akış &Sırası + + + Show flow sequence for selected call(s). + Seçilen arama(lar) için akış sırasını göster. + + + Prepare &Filter + Hazırla &Filtrele + + + Prepare a filter matching the selected calls(s). + Seçilen çağrı(lar) ile eşleşen bir filtre hazırlayın. + + + Cop&y + Kopyal&a + + + Open copy menu + Kopyalama menüsünü aç + + + All + Tümü + + + Select all + Tümünü seç + + + None + Yok + + + Invert + Evir + + + Invert selection + Seçimi tersine çevir + + + Select related RTP streams + İlgili RTP akışlarını seçin + + + Select RTP streams related to selected calls in RTP Streams dialog + RTP Akışları iletişim kutusunda seçilen çağrılarla ilgili RTP akışlarını seçin + + + S + S + + + Deselect related RTP Streams + İlgili RTP Akışlarının seçimini kaldırın + + + D + D + + + Clear selection + Seçimi temizle + + + Display time as time of day + Saati günün saati olarak göster + + + Copy as CSV + CSV olarak kopyala + + + Copy stream list as CSV. + Akış listesini CSV olarak kopyalayın. + + + Copy as YAML + YAML olarak kopyala + + + Copy stream list as YAML. + Akış listesini YAML olarak kopyalayın. + + + SIP Flows + SIP Akışları + + + VoIP Calls + VoIP Aramaları + + + as CSV + CSV olarak + + + as YAML + YAML olarak + + + Select + Seç + + + + VoipCallsInfoModel + + On + Açık + + + Off + Kapalı + + + Tunneling: %1 Fast Start: %2 + Tünel Açma: %1 Hızlı Başlangıç: %2 + + + Start Time + Başlangıç Saati + + + Stop Time + Durma zamanı + + + Initial Speaker + İlk Konuşmacı + + + From + Şu Zamandan + + + To + İçin + + + Protocol + İletişim Kuralı + + + Duration + Süre + + + Packets + Paketler + + + State + Durum + + + Comments + Yorumlar + + + + WelcomePage + + Form + Form + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">Wireshark a Hoşgeldiniz</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>Dosya sisteminizde bir dosya açın</p></body></html> + + + <h2>Open</h2> + <h2>Aç</h2> + + + Recent capture files + Son yakalama dosyaları + + + Capture files that have been opened previously + Daha önce açılmış dosyaları yakalayın + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>Ağınızdan canlı paketleri yakalayın.</p></body></html> + + + <h2>Capture</h2> + <h2>Ele geçir</h2> + + + …using this filter: + …bu filtreyi kullanarak: + + + Interface list + Arayüz listesi + + + List of available capture interfaces + Kullanılabilir yakalama arayüzlerinin listesi + + + <h2>Learn</h2> + <h2>Öğren</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + <html><head> +<style> +a:bağlantı { + renk: paleti(metin); + metin süslemesi: yok; +} +a:üzerine gelin{ + renk: paleti(metin); + metin süslemesi: altı çizili; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">Kullanıcı Kılavuzu</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Sorular ve Cevaplar</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Posta Listeleri</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Bağış yap</a></th> + +</tr></table> +</body></html> + + + Show in Finder + Finder'da Göster + + + Show in Folder + Klasörde Göster + + + Welcome to %1 + %1 e Hoşgeldiniz + + + All interfaces shown + Gösterilen tüm arayüzler + + + %n interface(s) shown, %1 hidden + + %n interface shown, %1 hidden + + + + You are sniffing the glue that holds the Internet together using Wireshark + Wireshark kullanarak interneti bir arada tutan yapıştırıcıyı kokluyorsunuz + + + You are running Wireshark + Wireshark'ı çalıştırıyorsunuz + + + You receive automatic updates. + Otomatik güncelleme alıyorsunuz. + + + You have disabled automatic updates. + Otomatik güncellemeleri devre dışı bıraktınız. + + + not found + bulunamadı + + + Copy file path + Dosya yolunu kopyala + + + Remove from list + Listeden sil + + + + WirelessFrame + + Frame + Çerçeve + + + Interface + Arayüz + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>802.11 kanalını ayarlayın.</p></body></html> + + + Channel + Kanal + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>Yakalarken, geçerli bir kare kontrol dizisine (FCS) sahip olanları veya geçersiz bir FCS'ye sahip olan tüm kareleri gösterin.</p></body></html> + + + FCS Filter + FCS Filtresi + + + All Frames + Tüm Çerçeveler + + + Valid Frames + Geçerli Çerçeveler + + + Invalid Frames + Geçersiz Çerçeveler + + + Wireless controls are not supported in this version of Wireshark. + Wireshark'ın bu sürümünde kablosuz kontroller desteklenmez. + + + External Helper + Harici Yardımcı + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>Şifre çözme anahtarları dahil IEEE 802.11 tercihlerini gösterin.</p></body></html> + + + 802.11 Preferences + 802.11 Tercihleri + + + AirPcap Control Panel + AirPcap Kontrol Paneli + + + Open the AirPcap Control Panel + AirPcap Kontrol Panelini açın + + + Unable to set channel or offset. + Kanal veya ofset ayarlanamıyor. + + + Unable to set FCS validation behavior. + FCS doğrulama davranışı ayarlanamıyor. + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + %1 numaralı paket, TSF zaman damgasını içermiyor, zaman çizelgesini göstermiyor. + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + %u numaralı paket, TSF'de zaman çizelgesini göstermiyor, büyük bir negatif sıçramaya sahip. Belki de TSF referans noktası yanlış ayarlanmış? + + + + WiresharkDialog + + Failed to attach to tap "%1" + "%1" öğesine dokunarak eklenemedi + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Pakete git + + + Cancel + İptal et + + + File Set + Dosya Kümesi + + + Export Packet Dissections + Paket Diseksiyonlarını Dışa Aktar + + + Export Objects + Nesneleri Dışa Aktar + + + &Zoom + &Yaklaş + + + &Time Display Format + &Zaman Görüntüleme Biçimi + + + Copy + Kopyala + + + Manual pages + Elle ayarlanmış sayfalar + + + Apply as Filter + Filtre Olarak Uygula + + + Prepare as Filter + Filtre Olarak Hazırla + + + SCTP + SCTP + + + TCP Stream Graphs + TCP Akış Grafikleri + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &DOSYA + + + &Capture + &Yakala + + + &Help + &Yardım + + + &Go + &Git + + + &View + &Görünüm + + + &Analyze + &Analiz + + + Follow + Takip + + + &Statistics + &İstatistikler + + + 29West + 29Batı + + + Topics + Konular + + + Queues + Kuyruklar + + + UIM + UIM + + + Telephon&y + Telefo&n + + + RTSP + RTSP + + + &Edit + &Düzenle + + + Packet Comments + Paket Yorumları + + + Main Toolbar + Ana Araç Çubuğu + + + Display Filter Toolbar + Filtre Araç Çubuğunu Görüntüle + + + Open a capture file + Bir yakalama dosyası açın + + + Quit Wireshark + Wireshark'tan çıkın + + + &Start + &Başlat + + + Start capturing packets + Paketleri yakalamaya başlayın + + + S&top + D&urdur + + + Stop capturing packets + Paketleri yakalamayı durdur + + + No files found + Dosya bulunamadı + + + &Contents + &İçerik + + + Wireshark Filter + Wireshark Filtresi + + + TShark + TShark + + + Rawshark + Rawshark + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + Text2pcap + + + Website + İnternet Sitesi + + + Downloads + İndirilenler + + + Wiki + Viki + + + Sample Captures + Örnek Yakalamalar + + + &About Wireshark + &Wireshark Hakkında + + + Ask (Q&&A) + Sor (Soru-Cevap) + + + Next Packet + Sonraki Paket + + + Go to the next packet + Sonraki pakete git + + + Previous Packet + Önceki Paket + + + Go to the previous packet + Önceki pakete git + + + First Packet + İlk Paket + + + Go to the first packet + İlk pakete git + + + Last Packet + Son Paket + + + Go to the last packet + Son pakete git + + + E&xpand Subtrees + Alt Ağaçları G&enişlet + + + Expand the current packet detail + Geçerli paket ayrıntısını genişlet + + + &Expand All + &Tümünü Genişlet + + + Expand packet details + Paket ayrıntılarını genişlet + + + Collapse &All + Tümünü &Daralt + + + Collapse all packet details + Tüm paket ayrıntılarını daralt + + + Go to specified packet + Belirtilen pakete git + + + Merge one or more files + Bir veya daha fazla dosyayı birleştirme + + + Import a file + Bir dosyayı içe aktar + + + &Save + &Kaydet + + + Save as a different file + Farklı bir dosya olarak kaydet + + + Export specified packets + Belirtilen paketleri dışa aktar + + + Export TLS Session Keys… + TLS Oturum Anahtarlarını Dışa Aktar… + + + List Files + Dosyaları Listele + + + Next File + Sonraki Dosya + + + Previous File + Önceki Dosya + + + &Reload + &Tekrar yükle + + + Options + Seçenekler + + + Capture options + Yakalama seçenekleri + + + Capture filters + Yakalama filtreleri + + + Refresh Interfaces + Arayüzleri Yenile + + + Refresh interfaces + Arayüzleri yenile + + + &Restart + &Yeniden Başlat + + + Restart current capture + Mevcut yakalamayı yeniden başlat + + + As &CSV… + &CSV olarak… + + + As "C" &Arrays… + "C" &Diziler olarak… + + + As P&SML XML… + P&SML XML olarak… + + + As P&DML XML… + P&DML XML olarak… + + + As &JSON… + &JSON olarak… + + + Description + Açıklama + + + Field Name + Alan Adı + + + Value + Değer + + + As Filter + Filtre Olarak + + + Close this capture file + Bu yakalama dosyasını kapat + + + Packet: + Paket: + + + Interface Toolbars + Arayüz Araç Çubukları + + + Colorize Conversation + Konuşmayı Renklendir + + + Internals + Dahili + + + Additional Toolbars + Ek Araç Çubukları + + + Conversation Filter + Konuşma Filtresi + + + Reliable Server Pooling (RSerPool) + Güvenilir Sunucu Havuzu (RSerPool) + + + SOME/IP + BAZI/IP + + + &DTN + &DTN + + + Osmux + Osmux + + + &Tools + Tools + &Araçlar + + + Wireless Toolbar + Kablosuz Araç Çubuğu + + + Help contents + Yardım içeriği + + + FAQs + SSS + + + Next Packet in Conversation + Konuşmadaki Sonraki Paket + + + Go to the next packet in this conversation + Bu görüşmede sonraki pakete git + + + Previous Packet in Conversation + Görüşmede Önceki Paket + + + Go to the previous packet in this conversation + Bu görüşmede önceki pakete git + + + Next Packet In History + Geçmişteki Sonraki Paket + + + Go to the next packet in your selection history + Seçim geçmişinizde bir sonraki pakete gidin + + + Previous Packet In History + Geçmişteki Önceki Paket + + + Go to the previous packet in your selection history + Seçim geçmişinizde önceki pakete gidin + + + Collapse Subtrees + Alt Ağaçları Daralt + + + Collapse the current packet detail + Geçerli paket ayrıntısını daralt + + + Go to Packet… + Pakete Git… + + + &Merge… + &Birleştir… + + + &Import from Hex Dump… + &Hex Dökümünden içe aktar… + + + Save this capture file + Bu yakalama dosyasını kaydet + + + Save &As… + Farklı &Kaydet… + + + Export Specified Packets… + Belirtilen Paketleri Dışa Aktar… + + + Export Packet &Bytes… + Paket &Baytlarını Dışa Aktar… + + + &Print… + &Yazdır… + + + Reload this file + Bu dosyayı yeniden yükle + + + Reload as File Format/Capture + Dosya Biçimi/Yakalama Olarak Yeniden Yükle + + + Copy this item's description + Bu öğenin açıklamasını kopyala + + + Copy this item's field name + Bu öğenin alan adını kopyala + + + Copy this item's value + Bu öğenin değerini kopyala + + + Copy this item as a display filter + Bu öğeyi bir ekran filtresi olarak kopyala + + + Apply as Column + Sütun olarak uygula + + + Create a packet list column from the selected field. + Seçili alandan bir paket listesi sütunu oluşturun. + + + Find a packet + Bir paket bul + + + Find the next packet + Sonraki paketi bul + + + Find the previous packet + Önceki paketi bul + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + &Paketi İşaretle/İşaretini Kaldır + + + Mark All Displayed + Tüm Görüntülenenleri İşaretle + + + Mark all displayed packets + Görüntülenen tüm paketleri işaretle + + + Unmark all displayed packets + Görüntülenen tüm paketlerin işaretini kaldırın + + + Next Mark + Sonraki İşaret + + + Go to the next marked packet + Sonraki işaretli pakete git + + + Previous Mark + Önceki İşaret + + + Go to the previous marked packet + Önceki işaretli pakete git + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + &Paketi Yoksay/Yoksaymaktan vazgeç + + + Ignore All Displayed + Tüm Görüntülenenleri Yoksay + + + Ignore all displayed packets + Görüntülenen tüm paketleri yoksay + + + Set/Unset Time Reference + Zaman Referansını Ayarla/Ayarlamayı Kaldır + + + Set or unset a time reference for this packet + Bu paket için bir zaman referansı ayarlayın veya ayarlayın + + + Unset All Time References + Tüm Zaman Referans Ayarlarını Kaldır + + + Remove all time references + Tüm zaman referanslarını kaldır + + + Next Time Reference + Sonraki Zaman Referansı + + + Go to the next time reference + Sonraki zaman referansına git + + + Previous Time Reference + Önceki Zaman Referansı + + + Go to the previous time reference + Önceki zaman referansına git + + + Shift or change packet timestamps + Paket zaman damgalarını kaydırın veya değiştirin + + + Delete All Packet Comments + Tüm Paket Yorumlarını Sil + + + Remove all packet comments in the capture file + Yakalama dosyasındaki tüm paket yorumlarını kaldırın + + + &Configuration Profiles… + &Yapılandırma Profilleri… + + + Configuration profiles + Yapılandırma profilleri + + + Manage your configuration profiles + Yapılandırma profillerinizi yönetin + + + Manage Wireshark's preferences + Wireshark'ın tercihlerini yönetin + + + Capture File Properties + Yakalama Dosyası Özellikleri + + + Capture file properties + Yakalama dosyası özellikleri + + + &Protocol Hierarchy + &Protokol Hiyerarşisi + + + Show a summary of protocols present in the capture file. + Yakalama dosyasında bulunan protokollerin bir özetini gösterin. + + + Capinfos + Başlık bilgileri + + + Reordercap + Başlığı Yeniden Sırala + + + Time Sequence (Stevens) + Zaman Dizisi (Stevens) + + + TCP time sequence graph (Stevens) + TCP zaman dizisi grafiği (Stevens) + + + Throughput + Verim + + + Round Trip Time + Gidiş-dönüş süresi + + + TCP round trip time + TCP gidiş dönüş süresi + + + Window Scaling + Pencere Ölçekleme + + + TCP window scaling + TCP pencere ölçeklendirme + + + HTTP/2 Stream + HTTP/2 Akışı + + + SIP Call + SIP Çağrısı + + + Time Sequence (tcptrace) + Zaman Sırası (tcptrace) + + + TCP time sequence graph (tcptrace) + TCP zaman sırası grafiği (tcptrace) + + + Analyse this Association + Bu Derneği analiz et + + + Show All Associations + Tüm İlişkilendirmeleri Göster + + + Flow Graph + Akış Grafiği + + + Flow sequence diagram + Akış sırası diyagramı + + + ANCP + ANCP + + + ANCP statistics + ANCP istatistikleri + + + Packets sorted by Instance ID + Örnek Kimliğine göre sıralanmış paketler + + + BACapp statistics sorted by instance ID + Örnek kimliğine göre sıralanmış BACapp istatistikleri + + + Packets sorted by IP + IP'ye göre sıralanmış paketler + + + BACapp statistics sorted by IP + IP'ye göre sıralanmış BACapp istatistikleri + + + Packets sorted by object type + Nesne türüne göre sıralanmış paketler + + + BACapp statistics sorted by object type + Nesne türüne göre sıralanmış BACapp istatistikleri + + + Packets sorted by service + Servise göre sıralanmış paketler + + + BACapp statistics sorted by service + Hizmete göre sıralanmış BACapp istatistikleri + + + Collectd + Toplanan + + + Collectd statistics + Toplanan istatistikler + + + DNS + DNS + + + DNS statistics + DNS istatistikleri + + + HART-IP + HART-IP + + + HART-IP statistics + HART-IP istatistikleri + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + hpfeeds istatistikleri + + + HTTP2 + HTTP2 + + + HTTP2 statistics + HTTP2 istatistikleri + + + Packet Counter + Paket Sayacı + + + HTTP packet counter + HTTP paket sayacı + + + Requests + İstekler + + + HTTP requests + HTTP istekleri + + + Load Distribution + Yük Dağılımı + + + HTTP load distribution + HTTP yük dağılımı + + + Packet Lengths + Paket Uzunlukları + + + Packet length statistics + Paket uzunluğu istatistikleri + + + Sametime + Aynı zamanda + + + Sametime statistics + Aynı Zaman istatistikleri + + + SOME/IP Messages + BAZI/IP Mesajları + + + SOME/IP Message statistics + BAZI/IP Mesaj istatistikleri + + + SOME/IP-SD Entries + BAZI/IP-SD Girdileri + + + SOME/IP-SD Entries statistics + BAZI/IP-SD Girdi istatistikleri + + + &LTP + &LTP + + + LTP segment and block statistics + LTP bölümü ve blok istatistikleri + + + &ISUP Messages + &ISUP Mesajları + + + ISUP message statistics + ISUP mesaj istatistikleri + + + Osmux packet counts + Osmux paket sayısı + + + RTSP packet counts + RTSP paket sayıları + + + SM&PP Operations + SM&PP İşlemleri + + + SMPP operation statistics + SMPP işlem istatistikleri + + + &UCP Messages + &UCP Mesajları + + + UCP message statistics + UCP mesaj istatistikleri + + + F1AP + F1AP + + + F1AP Messages + F1AP Mesajları + + + NGAP + NGAP + + + NGAP Messages + NGAP Mesajları + + + Change the way packets are dissected + Paketlerin parçalanma şeklini değiştirin + + + Reload Lua Plugins + Lua Eklentilerini Yeniden Yükle + + + Reload Lua plugins + Lua eklentilerini yeniden yükle + + + Advertisements by Topic + Konuya Göre Reklamlar + + + Advertisements by Source + Kaynağa Göre Reklamlar + + + Advertisements by Transport + Ulaşım Reklamları + + + Queries by Topic + Konuya Göre Sorgular + + + Queries by Receiver + Alıcıya Göre Sorgular + + + Wildcard Queries by Pattern + Desene Göre Joker Karakter Sorguları + + + Wildcard Queries by Receiver + Alıcıya Göre Joker Karakter Sorguları + + + Advertisements by Queue + Sıraya Göre Reklamlar + + + Queries by Queue + Kuyruğa Göre Sorgular + + + Streams + Canlı Yayınlar + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + Bu İlişkilendirmeyi Filtrele + + + Strip Headers… + Üst Bilgiler… + + + Strip headers and export higher level encapsulations to file + Üstbilgileri çıkarın ve daha üst düzey kapsüllemeleri dosyaya dışa aktarın + + + &I/O Graphs + &G/Ç Grafikleri + + + &Conversations + &Konuşmalar + + + &Endpoints + &Uç Noktalar + + + Shrink the main window text + Ana pencere metnini küçült + + + Return the main window text to its normal size + Ana pencere metnini normal boyutuna döndür + + + Reset Layout + Düzeni Sıfırla + + + Reset appearance layout to default size + Görünüm düzenini varsayılan boyuta sıfırla + + + Seconds Since First Captured Packet + İlk Yakalanan Paketten Beri Saniye + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + Paket &Şeması + + + Show or hide the packet diagram + Paket diyagramını göster veya gizle + + + Show each conversation hash table + Her konuşma karma tablosunu göster + + + Show each dissector table and its entries + Her bir tespit edici tablosunu ve girişlerini göster + + + Show the currently supported protocols and display filter fields + Şu anda desteklenen protokolleri göster ve filtre alanlarını göster + + + MAC Statistics + MAC İstatistikleri + + + LTE MAC statistics + LTE MAC istatistikleri + + + RLC Statistics + RLC İstatistikleri + + + LTE RLC statistics + LTE RLC istatistikleri + + + LTE RLC graph + LTE RLC grafiği + + + MTP3 Summary + MTP3 Özeti + + + MTP3 summary statistics + MTP3 özet istatistikleri + + + Bluetooth Devices + Bluetooth Cihazları + + + Bluetooth HCI Summary + Bluetooth HCI Özeti + + + Display Filter &Expression… + Filtre İfadesini &Görüntüle… + + + Display Filter Expression… + Filtre İfadesini Görüntüle… + + + REGISTER_STAT_GROUP_RSERPOOL + KAYIT_STAT_GRUBU_GSUNHAVUZU + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + "KAYIT_STAT_GRUBU_GSUNHAVUZU" başlangıcı + + + No GSM statistics registered + Kayıtlı GSM istatistikleri yok + + + No LTE statistics registered + Kayıtlı LTE istatistiği yok + + + No MTP3 statistics registered + Kayıtlı MTP3 istatistiği yok + + + IAX2 Stream Analysis + IAX2 Akış Analizi + + + Show Packet Bytes… + Paket Baytlarını Göster… + + + Go to &Linked Packet + Bağlantılı &Pakete Git + + + UDP Multicast Streams + UDP Çok Noktaya Yayın Akışları + + + Show UTP multicast stream statistics. + UTP çok noktaya yayın akışı istatistiklerini göster. + + + WLAN Traffic + WLAN Trafiği + + + Show IEEE 802.11 wireless LAN statistics. + IEEE 802.11 kablosuz LAN istatistiklerini göster. + + + Add a display filter button. + Bir ekran filtresi düğmesi ekleyin. + + + Firewall ACL Rules + Güvenlik Duvarı ACL Kuralları + + + Create firewall ACL rules + Güvenlik duvarı ACL kuralları oluşturun + + + &Full Screen + &Tam ekran + + + Credentials + Kimlik bilgileri + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + &Seçenekler… + + + &Wireless + &Kablosuz + + + Capture &Filters… + Yakalama &Filtreleri… + + + As Plain &Text… + Düz Metin &Olarak… + + + As Plain &Text + Düz Metin &Olarak + + + As &CSV + &CSV olarak + + + As &YAML + &YAML olarak + + + All Visible Items + Tüm Görünür Öğeler + + + All Visible Selected Tree Items + Tüm Görünür Seçilmiş Ağaç Öğeleri + + + Display Filter &Macros… + Filtre Makrolarını &Görüntüle… + + + &Find Packet… + &Paket Bul… + + + Find Ne&xt + Sonrakini Bu&l + + + Find Pre&vious + Öncekini Bu&l + + + Mark or unmark each selected packet + Seçilen her paketi işaretleyin veya işaretini kaldırın + + + Ignore or unignore each selected packet + Seçilen her paketi yoksay veya yoksay + + + U&nignore All Displayed + Tüm Görüntülenenleri Y&oksay + + + Unignore all displayed packets + Görüntülenen tüm paketleri yoksay + + + Time Shift… + Zaman Kaydırması… + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + &Tercihler… + + + TCP throughput + TCP verimi + + + Request Sequences + İstek Dizileri + + + HTTP Request Sequences + HTTP İstek Dizileri + + + Decode &As… + Kodu &Çöz… + + + Export PDUs to File… + PDU'ları Dosyaya Aktar… + + + Create graphs based on display filter fields + Ekran filtresi alanlarına dayalı grafikler oluşturun + + + &Main Toolbar + &Ana Araç Çubuğu + + + Show or hide the main toolbar + Ana araç çubuğunu göster veya gizle + + + &Filter Toolbar + &Araç Çubuğunu Filtrele + + + Show or hide the display filter toolbar + Ekran filtresi araç çubuğunu göster veya gizle + + + Conversations at different protocol levels + Farklı protokol seviyelerinde konuşmalar + + + Endpoints at different protocol levels + Farklı protokol seviyelerinde uç noktalar + + + Colorize Packet List + Paket Listesini Renklendir + + + Draw packets using your coloring rules + Renklendirme kurallarınızı kullanarak paketler çizin + + + &Zoom In + &Yaklaştır + + + Enlarge the main window text + Ana pencere metnini büyüt + + + Zoom Out + Uzaklaş + + + Normal Size + Normal Boyut + + + Resize Columns + Sütunları Yeniden Boyutlandır + + + Resize packet list columns to fit contents + İçeriğe sığdırmak için paket listesi sütunlarını yeniden boyutlandırın + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Günün Tarihi ve Saati (01-01-1970 01:02:03.123456) + + + Show packet times as the date and time of day. + Paket sürelerini günün tarihi ve saati olarak göster. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Yıl, Yılın Günü ve Günün Saati (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Paket sürelerini yıl, yılın günü ve günün saati olarak gösterin. + + + Time of Day (01:02:03.123456) + Günün Saati (01:02:03.123456) + + + Seconds Since 1970-01-01 + 1970-01-01'den Beri Saniye + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Paket sürelerini UNIX / POSIX döneminden (1970-01-01) bu yana saniye olarak göster. + + + Seconds Since Previous Captured Packet + Önceki Yakalanan Paketten Beri Saniye + + + Show packet times as the seconds since the previous captured packet. + Paket sürelerini, bir önceki yakalanan paketten bu yana geçen saniye olarak göster. + + + Seconds Since Previous Displayed Packet + Önceki Görüntülenen Paketten Beri Saniye + + + Show packet times as the seconds since the previous displayed packet. + Paket sürelerini önceki görüntülenen paketten bu yana geçen saniye olarak göster. + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + UTC Tarihi ve Günün Saati (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Paket saatlerini UTC tarihi ve günün saati olarak göster. + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + UTC Yılı, Yılın Günü ve Günün Saati (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + Paket sürelerini UTC yılı, yılın günü ve günün saati olarak gösterin. + + + UTC Time of Day (01:02:03.123456) + UTC Günün Saati (01:02:03.123456) + + + Show packet times as the UTC time of day. + Paket sürelerini günün UTC saati olarak göster. + + + Automatic (from capture file) + Otomatik (yakalama dosyasından) + + + Use the time precision indicated in the capture file. + Yakalama dosyasında belirtilen zaman hassasiyetini kullanın. + + + Seconds + Saniye + + + Tenths of a second + Saniyenin onda biri + + + Hundredths of a second + Saniyenin yüzde biri + + + Milliseconds + Milisaniye + + + Microseconds + Mikrosaniye + + + Nanoseconds + Nanosaniye + + + Display Seconds With Hours and Minutes + Saniyeleri Saat ve Dakikalarla Göster + + + Display seconds with hours and minutes + Saniyeyi saat ve dakika ile göster + + + Resolve &Physical Addresses + &Fiziksel Adresleri Çözümle + + + Show names for known MAC addresses. Lookups use a local database. + Bilinen MAC adreslerinin adlarını göster. Aramalar yerel bir veritabanı kullanır. + + + Resolve &Network Addresses + &Ağ Adreslerini Çözümle + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + Bilinen IPv4, IPv6 ve IPX adreslerinin adlarını gösterin. Aramalar ağ trafiği oluşturabilir. + + + Resolve &Transport Addresses + &Taşıma Adreslerini Çözümle + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + Bilinen TCP, UDP ve SCTP hizmetlerinin adlarını gösterin. Aramalar bazı sistemlerde trafik oluşturabilir. + + + Wire&less Toolbar + Kab&losuz Araç çubuğu + + + Show or hide the wireless toolbar + Kablosuz araç çubuğunu göster veya gizle + + + &Status Bar + &Durum Çubuğu + + + Show or hide the status bar + Durum çubuğunu göster veya gizle + + + Packet &List + Paket &Listesi + + + Show or hide the packet list + Paket listesini göster veya gizle + + + Packet &Details + Paket &Ayrıntıları + + + Show or hide the packet details + Paket ayrıntılarını göster veya gizle + + + Packet &Bytes + Paket &Bayt + + + Show or hide the packet bytes + Paket baytlarını göster veya gizle + + + &Conversation Hash Tables + &Konuşma Hash Tabloları + + + &Dissector Tables + &Tespit edici Tabloları + + + &Supported Protocols + &Desteklenen Protokoller + + + MAP Summary + HARİTA Özeti + + + GSM MAP summary statistics + GSM MAP özet istatistikleri + + + RLC &Graph + RLC &Grafiği + + + &Coloring Rules… + &Renklendirme Kuralları… + + + Show Linked Packet in New Window + Bağlantılı Paketi Yeni Pencerede Göster + + + New Coloring Rule… + New Conversation Rule… + Yeni Renklendirme Kuralı… + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + Seçilen akış için RTP Akış Analizi. Ters akış eklemek için de CTRL tuşuna basın. + + + RTP Player + RTP Oynatıcı + + + Play selected stream. Press CTRL key for playing reverse stream too. + Seçilen akışı oynat. Ters akışı oynatmak için de CTRL tuşuna basın. + + + IA&X2 Stream Analysis + IA&X2 Akış Analizi + + + Enabled Protocols… + Enable Protocols… + Etkin Protokoller… + + + Wiki Protocol Page + Wiki Protokol Sayfası + + + Open the Wireshark wiki page for this protocol. + Bu protokol için Wireshark wiki sayfasını açın. + + + Filter Field Reference + Filtre Alanı Referansı + + + Open the display filter reference page for this filter field. + Bu filtre alanı için ekran filtresi referans sayfasını açın. + + + Go to the packet referenced by the selected field. + Seçili alan tarafından başvurulan pakete gidin. + + + &VoIP Calls + &VoIP Aramaları + + + Open &Recent + &Son Kullanılanlardan Aç + + + Name Resol&ution + Ad Çözüm&lemesi + + + Service &Response Time + Servis &Yanıt Süresi + + + &RTP + &RTP + + + S&CTP + S&CTP + + + &ANSI + &ANSI + + + &GSM + &GSM + + + &LTE + &LTE + + + &MTP3 + &MTP3 + + + &Open + &Aç + + + &Quit + &Çık + + + &Close + &Kapat + + + Display &Filters… + Görüntüleme &Filtreleri… + + + &Unmark All Displayed + &Tüm Görüntülenenlerin İşaretini Kaldır + + + All VoIP Calls + Tüm VoIP Aramaları + + + SIP &Flows + SIP &Akışları + + + SIP Flows + SIP Akışları + + + RTP Streams + RTP Akışları + + + Edit the packet list coloring rules. + Paket listesi renklendirme kurallarını düzenleyin. + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Bluetooth ATT Sunucusu Özellikleri + + + Show Packet in New &Window + Paketi Yeni &Pencerede Göster + + + Show this packet in a separate window. + Bu paketi ayrı bir pencerede göster. + + + Show the linked packet in a separate window. + Bağlantılı paketi ayrı bir pencerede gösterin. + + + Auto Scroll in Li&ve Capture + Can&lı Yakalamada Otomatik Kaydırma + + + Automatically scroll to the last packet during a live capture. + Canlı yakalama sırasında otomatik olarak son pakete ilerleyin. + + + Expert Information + Uzman Bilgileri + + + Show expert notifications + Uzman bildirimlerini göster + + + Add an expression to the display filter. + Görüntü filtresine bir ifade ekleyin. + + + REGISTER_STAT_GROUP_UNSORTED + KAYIT_STAT_GRUBU_SIRALANMAMIŞ + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + "KAYIT_STAT_GRUBU_SIRALANMAMIŞ" başlangıcı + + + No ANSI statistics registered + No tools registered + ANSI istatistiği kayıtlı değil + + + Resolved Addresses + Çözümlenen Adresler + + + Show each table of resolved addresses as copyable text. + Çözümlenen adreslerin her bir tablosunu kopyalanabilir metin olarak gösterin. + + + Color &1 + Renk &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + Mevcut konuşmayı kendi rengiyle işaretleyin. + + + Color &2 + Renk &2 + + + Color &3 + Renk &3 + + + Color &4 + Renk &4 + + + Color &5 + Renk &5 + + + Color &6 + Renk &6 + + + Color &7 + Renk &7 + + + Color &8 + Renk &8 + + + Color &9 + Renk &9 + + + Color 1&0 + Renk 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + Bu alana dayalı yeni bir renklendirme kuralı oluşturun. + + + Reset Colorization + Renklendirmeyi Sıfırla + + + Reset colorized conversations. + Renklendirilmiş konuşmaları sıfırlayın. + + + RTP Stream Analysis + RTP Akış Analizi + + + Edit Resolved Name + Çözümlenen Adı Düzenle + + + Manually edit a name resolution entry. + Bir ad çözümleme girişini el ile düzenleyin. + + + Enable and disable specific protocols + Belirli protokolleri etkinleştirin ve devre dışı bırakın + + + before quitting + bırakmadan önce + + + Save packets before merging? + Paketler birleştirmeden önce kaydedilsin mi? + + + A temporary capture file can't be merged. + Geçici bir yakalama dosyası birleştirilemez. + + + Save changes in "%1" before merging? + Birleştirmeden önce "%1" içindeki değişiklikler kaydedilsin mi? + + + Changes must be saved before the files can be merged. + Dosyalar birleştirilmeden önce değişiklikler kaydedilmelidir. + + + Invalid Display Filter + Geçersiz Görüntü Filtresi + + + Invalid Read Filter + Geçersiz Okuma Filtresi + + + The filter expression %1 isn't a valid read filter. (%2). + %1 filtre ifadesi geçerli bir okuma filtresi değil. (%2). + + + before importing a capture + before importing a new capture + bir yakalamayı içe aktarmadan önce + + + Unable to export to "%1". + "%1"e dışa aktarılamıyor. + + + You cannot export packets to the current capture file. + Paketleri geçerli yakalama dosyasına aktaramazsınız. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + %1 yaptığınız değişiklikleri kaydetmek istiyor musunuz? + + + Your captured packets will be lost if you don't save them. + Yakaladığınız paketleri kaydetmezseniz kaybolacaktır. + + + Do you want to save the changes you've made to the capture file "%1"%2? + "%1"%2 yakalama dosyasında yaptığınız değişiklikleri kaydetmek istiyor musunuz? + + + Your changes will be lost if you don't save them. + Kaydetmezseniz değişiklikleriniz kaybolacak. + + + Check for Updates… + Güncellemeleri kontrol et… + + + Unable to drop files during capture. + Yakalama sırasında dosyalar bırakılamıyor. + + + Unknown file type returned by merge dialog. + Birleştirme iletişim kutusu tarafından döndürülen bilinmeyen dosya türü. + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + Lütfen bunu https://gitlab.com/wireshark/wireshark/-/issues adresinde bir Wireshark sorunu olarak bildirin. + + + Unknown file type returned by export dialog. + Dışa aktarma iletişim kutusu tarafından döndürülen bilinmeyen dosya türü. + + + Do you want to stop the capture and save the captured packets%1? + Yakalamayı durdurmak ve yakalanan paketleri %1 kaydetmek istiyor musunuz? + + + Do you want to save the captured packets%1? + Yakalanan paketleri %1 kaydetmek istiyor musunuz? + + + Save before Continue + Devam etmeden önce kaydet + + + Stop and Save + Durdur ve Kaydet + + + Stop and Quit &without Saving + Stop and Quit without Saving + Kaydetmeden &Durdurun ve Çıkın + + + Quit &without Saving + Quit without Saving + Kaydetmeden &çık + + + There is no "rtp.ssrc" field in this version of Wireshark. + Wireshark'ın bu sürümünde "rtp.ssrc" alanı yoktur. + + + Please select an RTPv2 packet with an SSRC value + Lütfen SSRC değerine sahip bir RTPv2 paketi seçin + + + SSRC value not found. + SSRC değeri bulunamadı. + + + Show or hide the toolbar + Araç çubuğunu göster ya da gizle + + + Continue &without Saving + Continue without Saving + Kaydetmeden &devam et + + + Stop and Continue &without Saving + Stop and Continue without Saving + Durdur ve Devam Et &Kaydetmeden + + + The Wireshark Network Analyzer + Wireshark Ağ Çözümleyicisi + + + Capturing from %1 + %1'den yakalama + + + before opening another file + başka bir dosyayı açmadan önce + + + Merging files. + Dosyaları birleştirme. + + + %1: %2 + %1: %2 + + + Clear Menu + Menüyü Temizle + + + before closing the file + dosyayı kapatmadan önce + + + Export Selected Packet Bytes + Seçili Paket Baytlarını Dışa Aktar + + + No Keys + Anahtar Yok + + + Raw data (*.bin *.dat *.raw);;All Files ( + Ham veriler (*.bin *.dat *.raw);;Tüm Dosyalar ( + + + Couldn't copy text. Try another item. + Metin kopyalanamadı. Başka bir öğe deneyin. + + + Are you sure you want to remove all packet comments? + Tüm paket yorumlarını kaldırmak istediğinizden emin misiniz? + + + Unable to build conversation filter. + Görüşme filtresi oluşturulamıyor. + + + before reloading the file + dosyayı yeniden yüklemeden önce + + + Error compiling filter for this conversation. + Bu görüşme için filtre derlenirken hata oluştu. + + + No previous/next packet in conversation. + Görüşmede önceki/sonraki paket yok. + + + No interface selected. + Arayüz seçilmedi. + + + Saving %1… + %1 kaydediliyor… + + + Configure all extcaps before start of capture. + Yakalamaya başlamadan önce tüm dış harfleri yapılandırın. + + + Invalid capture filter. + Geçersiz yakalama filtresi. + + + (empty comment) + placeholder for empty comment + (boş yorum) + + + Add New Comment… + Yeni Yorum Ekle… + + + Edit "%1" + edit packet comment + "%1"i düzenle + + + Delete "%1" + delete packet comment + "%1"i sil + + + Delete packet comments + Paket yorumlarını sil + + + Delete comments from %n packet(s) + + + + + + before starting a new capture + yeni bir yakalamaya başlamadan önce + + + before reloading Lua plugins + Lua eklentilerini yeniden yüklemeden önce + + + Please wait while Wireshark is initializing… + Wireshark başlatılırken lütfen bekleyin… + + + before updating + + + + There are no TLS Session Keys to save. + Kaydedilecek TLS Oturum Anahtarı yok. + + + Export TLS Session Keys (%Ln key(s)) + + + + + + TLS Session Keys (*.keys *.txt);;All Files ( + TLS Oturum Anahtarları (*.keys *.txt);;Tüm Dosyalar ( + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + Filtre mevcut değil. Başka bir %1 deneyin. + + + column + sütun + + + item + öğe + + + The "%1" column already exists. + "%1" sütunu zaten var. + + + The "%1" column already exists as "%2". + "%1" sütunu zaten "%2" olarak var. + + + RTP packet search failed + RTP paket araması başarısız oldu + + + No Interface Selected. + Arayüz Seçilmedi. + + + before restarting the capture + yakalamayı yeniden başlatmadan önce + + + Wiki Page for %1 + %1 için Wiki Sayfası + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Wiki'nin bakımı topluluk tarafından sağlanır.</p><p>Yüklemek üzere olduğunuz sayfa harika, eksik, yanlış veya hiç yok olabilir.</p><p>Wiki'ye geçilsin mi?</P> + + + Loading + Yükleniyor + + + Reloading + Yeniden yükleniyor + + + Rescanning + Yeniden tarama + + + + WlanStatisticsDialog + + Wireless LAN Statistics + Kablosuz LAN İstatistikleri + + + Channel + Kanal + + + SSID + SSID + + + Percent Packets + Yüzde Paketler + + + Percent Retry + Yüzde Yeniden Deneme + + + Probe Reqs + Prob Gereksinimleri + + + Probe Resp + Prob Yanıtı + + + Auths + Yetkiler + + + Retry + Yeniden Dene + + + Deauths + Kesinti + + + Other + Diğer + + + diff --git a/ui/qt/wireshark_uk.ts b/ui/qt/wireshark_uk.ts new file mode 100644 index 00000000..54831f2c --- /dev/null +++ b/ui/qt/wireshark_uk.ts @@ -0,0 +1,14643 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + Про Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">Аналізатор Мережевих Протоколів</span> + + + Copy the version information to the clipboard + Скопіювати інформацію про версію до буферу обміну + + + Copy to Clipboard + + + + Authors + Автори + + + Search Authors + Пошук авторів + + + Folders + Каталоги + + + Filter by path + Фільтр за шляхом + + + Plugins + Плаґіни + + + No plugins found. + Плагіни не знайдено + + + Search Plugins + Пошук Плагінів + + + Filter by type: + Фільтр за типом: + + + Keyboard Shortcuts + Комбінації Клавіш + + + Search Shortcuts + Шукати Комбінації Клавіш + + + Acknowledgments + Подяки + + + License + Ліцензія + + + The directory does not exist + Директорія не існує + + + Should the directory %1 be created? + Створити директорію %1? + + + The directory could not be created + Неможливо створити директорію + + + The directory %1 could not be created. + Неможливо створити директорію %1. + + + Show in Finder + Показати у Finder'і + + + Show in Folder + Показати у Папці + + + Copy + Скопіювати + + + Copy Row(s) + + + + + + + + + AddressEditorFrame + + Frame + Кадр + + + Name Resolution Preferences… + Name Resolution Preferences... + Налаштування Визначення Імен... + + + Address: + Адреса: + + + Name: + Ім'я: + + + Can't assign %1 to %2. + + + + + AdvancedPrefsModel + + Name + + + + Status + Статус + + + Type + Тип + + + Value + Значення + + + + ApplyLineEdit + + Apply changes + Застосувати зміни + + + + AuthorListModel + + Name + + + + Email + Email + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + Атрибути Серверу (Bluetooth ATT) + + + Handle + Дескриптор + + + UUID + UUID + + + UUID Name + Назва UUID + + + All Interfaces + Всі Інтерфейси + + + All Devices + Всі Пристрої + + + Remove duplicates + Прибрати дублікати + + + Copy Cell + Скопіювати Клітинку + + + Copy Rows + Скопіювати Рядки + + + Copy All + Скопіювати Все + + + Save as image + Зберегти як зображення + + + Mark/Unmark Row + + + + Ctrl-M + + + + Mark/Unmark Cell + + + + Save Table Image + Зберегти Зображення Таблиці + + + PNG Image (*.png) + Зображення PNG (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + + + + BD_ADDR + BD_ADDR + + + OUI + УІО (OUI) + + + Name + + + + Class of Device + + + + LMP Version + Версія LMP + + + LMP Subversion + Підверсія LMP + + + Manufacturer + Виробник + + + HCI Version + Версія HCI + + + HCI Revision + Ревізія HCI + + + Scan + + + + Authentication + + + + Encryption + + + + ACL MTU + + + + ACL Total Packets + + + + SCO MTU + + + + SCO Total Packets + + + + LE ACL MTU + + + + LE ACL Total Packets + + + + LE ISO MTU + + + + LE ISO Total Packets + + + + Inquiry Mode + + + + Page Timeout + + + + Simple Pairing Mode + + + + Voice Setting + + + + Value + Значення + + + Changes + + + + %1 changes + + + + Copy Cell + Скопіювати Клітинку + + + Copy Rows + Скопіювати Рядки + + + Copy All + Скопіювати Все + + + Save as image + Зберегти як зображення + + + Mark/Unmark Row + + + + Ctrl+M + + + + Mark/Unmark Cell + + + + Unknown + Невідомо + + + Bluetooth Device - %1%2 + + + + enabled + + + + disabled + + + + %1 ms (%2 slots) + + + + Save Table Image + Зберегти Зображення Таблиці + + + PNG Image (*.png) + Зображення PNG (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + Пристрої Bluetooth + + + BD_ADDR + BD_ADDR + + + OUI + УІО (OUI) + + + Name + Назва + + + LMP Version + Версія LMP + + + LMP Subversion + Підверсія LMP + + + Manufacturer + Виробник + + + HCI Version + Версія HCI + + + HCI Revision + Ревізія HCI + + + Is Local Adapter + Локальний Адаптер? + + + All Interfaces + Всі Інтерфейси + + + Show information steps + + + + %1 items; Right click for more option; Double click for device details + + + + Copy Cell + Скопіювати Клітинку + + + Copy Rows + Скопіювати Рядки + + + Copy All + Скопіювати Все + + + Save as image + Зберегти як зображення + + + Mark/Unmark Row + + + + Ctrl-M + + + + Mark/Unmark Cell + + + + true + + + + Save Table Image + Зберегти Зображення Таблиці + + + PNG Image (*.png) + Зображення PNG (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + Зведена інформація по Bluetooth HCI + + + Name + Назва + + + OGF + OGF + + + OCF + OCF + + + Opcode + Опкод + + + Event + Подія + + + Subevent + + + + Status + Статус + + + Reason + Причина + + + Hardware Error + Помилка Апаратного Забезпечення + + + Occurrence + + + + Link Control Commands + Команди Контролю Зв'язку + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + Команди Налаштувань Зв'язку + + + 0x02 + 0x02 + + + Controller & Baseband Commands + Команди контролеру / радіомодуля + + + 0x03 + 0x03 + + + Informational Parameters + Інформаційні Дані + + + 0x04 + 0x04 + + + Status Parameters + Дані Стану + + + 0x05 + 0x05 + + + Testing Commands + Команди Тестування + + + 0x06 + 0x06 + + + LE Controller Commands + Команди Контролеру З Низьким Енергоспоживанням + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + + + + 0x3E + 0x3E + + + Vendor-Specific Commands + Команди, Що Надаються Постачальником + + + 0x3F + 0x3F + + + Unknown OGF + Невідомий OGF + + + Events + Події + + + Hardware Errors + Помилки Апаратного Забезпечення + + + Results filter: + + + + Display filter: + Фільтр відображення: + + + All Interfaces + Всі Інтерфейси + + + All Adapters + Всі Адаптери + + + Copy Cell + Скопіювати Клітинку + + + Copy Rows + Скопіювати Рядки + + + Copy All + Скопіювати Все + + + Save as image + Зберегти як зображення + + + Mark/Unmark Row + + + + Ctrl+M + + + + Mark/Unmark Cell + + + + Unknown + Невідомо + + + Adapter %1 + Адаптер %1 + + + Frame %1 + Кадр %1 + + + Pending + Очікується + + + Save Table Image + Зберегти Зображення Таблиці + + + PNG Image (*.png) + Зображення PNG (*.png) + + + + ByteViewTab + + Packet bytes + Байти пакету + + + + ByteViewText + + Allow hover highlighting + + + + Show bytes as hexadecimal + + + + …as decimal + + + + …as octal + + + + …as bits + + + + Show text based on packet + + + + …as ASCII + + + + …as EBCDIC + + + + + CaptureFile + + [closing] + [закривається] + + + [closed] + [закрито] + + + + CaptureFileDialog + + This capture file contains comments. + Цей файл захоплення містить коментарі + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + Вибраний вами формат файлу не підтримує збереження коментарів. Зберегти захоплене в форматі, що підтримує коментарі - чи відкинути коментарі та зберегти в вибраному форматі? + + + Discard comments and save + Відкинути коментарі та зберегти + + + Save in another format + Зберегти в іншому форматі + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + Не знайдено такого формату файлу, котрий би підтримував коментарі. Чи ви хочете відкинути коментарі та зберегти в вибраному форматі? + + + All Files ( + + + + All Capture Files + + + + Format: + Формат: + + + Size: + Розмір: + + + Start / elapsed: + + + + Automatically detect file type + + + + Prepend packets + Додати пакети на початку + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + Вставити пакети з вибраного файлу перед поточним файлом. Мітки часу пакетів буде проігноровано. + + + Merge chronologically + Об'єднати в хронологічному порядку + + + Insert packets in chronological order. + Вставити пакети в хронологічному порядку. + + + Append packets + Додати пакети в кінець + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + Вставити пакети з вибраного файлу після поточного файлу. Мітки часу пакетів буде проігноровано. + + + Read filter: + + + + Compress with g&zip + &Стиснути gzip'ом + + + Open Capture File + Wireshark: Open Capture File + Відкрити Файл Захоплення + + + Save Capture File As + Wireshark: Save Capture File As + Зберегти Файл Захоплення Як + + + Save as: + Зберегти як: + + + Export Specified Packets + Wireshark: Export Specified Packets + Експортувати Вказані Пакети + + + Export as: + Експортувати як: + + + Merge Capture File + Wireshark: Merge Capture File + Об'єднати Файл Захоплення + + + Unknown file type returned by save as dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + directory + каталог + + + unknown file format + невідомий формат файлу + + + error opening file + помилка при відкритті файлу + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + + + + + + + %1, timed out at %Ln data record(s) + + + + + + + + %1, %Ln data record(s) + + + + + + + + unknown + невідомо + + + + CaptureFilePropertiesDialog + + Details + Подробиці + + + Capture file comments + Коментарі файлу захоплення + + + Refresh + Оновити + + + Copy To Clipboard + Скопіювати До Буферу Обміну + + + Save Comments + Зберегти Коментарі + + + Capture File Properties + Властивості Файлу Захоплення + + + Unknown + Невідомо + + + File + Файл + + + Name + Ім'я + + + Length + Розмір + + + Hash (SHA256) + + + + Hash (SHA1) + + + + Format + Формат + + + Encapsulation + Інкапсуляція + + + Snapshot length + Розмір знімку + + + Time + Час + + + First packet + Перший пакет + + + Last packet + Останній пакет + + + Elapsed + Витрачено + + + Section %1 + + + + Capture + Захоплення + + + Hardware + Апаратне забезпечення + + + OS + ОС + + + Application + Застосунок + + + Interfaces + Інтерфейси + + + Interface + Інтерфейс + + + Dropped packets + Відкинуті пакети + + + Capture filter + Фільтр захоплення + + + Link type + Тип з'єднання + + + Packet size limit (snaplen) + + + + none + відсутній + + + %1 bytes + %1 байтів + + + Statistics + Статистика + + + Measurement + Вимір + + + Captured + Захоплено + + + Displayed + Відображено + + + Marked + Позначено + + + Packets + Пакетів + + + Time span, s + Проміжок часу, с + + + Average pps + Середнє пзс + + + Average packet size, B + Середній розмір пакету, Б + + + Bytes + Байтів + + + Average bytes/s + Байт/с (середнє значення) + + + Average bits/s + біт/с (середнє значення) + + + Section Comment + + + + Packet Comments + + + + <p>Frame %1: + + + + Created by Wireshark %1 + + + Створено Wireshark'ом %1 + + + + + + CaptureFilterCombo + + Capture filter selector + + + + + CaptureFilterEdit + + Capture filter entry + + + + Manage saved bookmarks. + + + + Apply this filter string to the display. + + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + + + + Enter a capture filter %1 + + + + Save this filter + + + + Remove this filter + + + + Manage Capture Filters + + + + + CaptureInfoDialog + + Capture Information + + + + Stop Capture + + + + %1 packets, %2:%3:%4 + + + + + CaptureInfoModel + + Other + + + + + CaptureOptionsDialog + + Input + + + + Interface + Інтерфейс + + + Traffic + + + + Link-layer Header + + + + Promiscuous + + + + Snaplen (B) + + + + Buffer (MB) + + + + Monitor Mode + + + + Capture Filter + + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Ймовірно, ви захочете це ввімкнути. Зазвичай, мережева карта захоплює лише ті пакети, що надсилаються до її власної мережевої адреси. Якщо ж ви хочете захопити весь трафік, що карта може &quot;побачити&quot;, виберіть цю опцію. Перегляньте ЧаПи щодо нюансів захоплення пакетів в мережах з комутацією.</p></body></html> + + + Enable promiscuous mode on all interfaces + + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + + + + Manage Interfaces… + + + + Capture filter for selected interfaces: + + + + Compile BPFs + + + + Output + + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + + + + Capture to a permanent file + + + + File: + Файл: + + + Browse… + Перегляд... + + + Output format: + + + + pcapng + + + + pcap + + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + + + + Create a new file automatically… + + + + after + + + + Switch to the next file after the specified number of packets have been captured. + + + + packets + + + + Switch to the next file after the file size exceeds the specified file size. + + + + kilobytes + + + + megabytes + + + + gigabytes + + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + + + + seconds + + + + minutes + + + + hours + + + + when time is a multiple of + + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + + + + compression + + + + None + Відсутня + + + gzip + + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + + + + Use a ring buffer with + + + + files + + + + Options + Опції + + + Display Options + + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + + + + Update list of packets in real-time + + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + + + + Automatically scroll during live capture + + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + + + + Show capture information during live capture + + + + Name Resolution + + + + Perform MAC layer name resolution while capturing. + + + + Resolve MAC addresses + + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + + + + Resolve network names + + + + Perform transport layer name resolution while capturing. + + + + Resolve transport names + + + + Stop capture automatically after… + + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + + + + Stop capturing after the specified number of packets have been captured. + + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + + + + Stop capturing after the specified amount of data has been captured. + + + + Stop capturing after the specified amount of time has passed. + + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + + + + Directory for temporary files + + + + Capture Options + + + + Start + + + + Leave blank to use a temporary file + + + + Specify a Capture File + + + + Specify temporary directory + + + + %1: %2 + %1: %2 + + + Addresses + + + + Address + + + + no addresses + + + + Error + Помилка + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + + + + + CapturePreferencesFrame + + Frame + Кадр + + + Default interface + Стандартний інтерфейс + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>Ймовірно, ви захочете це ввімкнути. Зазвичай, мережева карта захоплює лише ті пакети, що надсилаються до її власної мережевої адреси. Якщо ж ви хочете захопити весь трафік, що карта може &quot;побачити&quot;, виберіть цю опцію. Перегляньте ЧаПи щодо нюансів захоплення пакетів в мережах з комутацією.</p></body></html> + + + Capture packets in promiscuous mode + Захоплювати пакети в нерозбірливому режимі + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>Захоплювати пакети в форматі файлу захоплення наступного покоління.</p></body></html> + + + Capture packets in pcapng format + + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>Оновлювати список пакетів поки триває захоплення. На високошвидкісних мережах може призвести до відкидання пакетів.</p></body></html> + + + Update list of packets in real time + Оновлювати список пакетів в реальному часі + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + + + + Disable external capture interfaces + + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + + + + + ColoringRulesDialog + + Dialog + Діалог + + + <small><i>A hint.</i></small> + <small><i>Підказка</i></small> + + + Add a new coloring rule. + Додати нове правило розфарбовування. + + + Delete this coloring rule. + Видалити це правило розфарбовування. + + + Duplicate this coloring rule. + Продублювати це правило розфарбовування. + + + Clear all coloring rules. + + + + Set the foreground color for this rule. + Встановити колір переднього плану для цього правила + + + Foreground + Передній план + + + Set the background color for this rule. + Встановити колір фону для цього правила + + + Background + Фон + + + Set the display filter using this rule. + + + + Apply as filter + + + + Select a file and add its filters to the end of the list. + Вибрати файл та додати фільтри з нього до кінця списку. + + + Save filters in a file. + Зберегти фільтри в файл. + + + Coloring Rules %1 + + + + Import… + + + + Export… + + + + Copy coloring rules from another profile. + + + + Open + Відкрити + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + Двічі клікніть для редагування. Перетягніть, щоб перемістити. Правила оброблюються по порядку, допоки не буде знайдено відповідник. + + + Import Coloring Rules + Імпортувати Правила Розфарбовування + + + Export %1 Coloring Rules + Експортувати Правила Розфарбовування %1 + + + + ColoringRulesModel + + New coloring rule + + + + Unable to save coloring rules: %1 + + + + Name + + + + Filter + + + + + ColumnEditorFrame + + Frame + Кадр + + + Title: + Title + + + + Type: + Type + + + + Fields: + Fields + + + + Occurrence: + Occurrence + + + + Resolve Names: + + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + + + + Missing fields. + + + + Invalid fields. + + + + Invalid occurrence value. + + + + + ColumnListModel + + Displayed + + + + Title + + + + Type + Тип + + + Fields + + + + Field Occurrence + + + + Resolved + + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + + + + New Column + + + + + ColumnPreferencesFrame + + Frame + Кадр + + + Add a new column + + + + Delete selected column + + + + Show displayed columns only + + + + Reset all changes + + + + + CompiledFilterOutput + + Compiled Filter Output + + + + Copy + Скопіювати + + + Copy filter text to the clipboard. + Скопіювати текст фільтру до буферу обміну + + + + ConversationDataModel + + Address A + + + + Port A + + + + Address B + + + + Port B + + + + Packets + Пакетів + + + Bytes + + + + Stream ID + + + + Packets A + + + + Bytes A + + + + Packets B + + + + Bytes B + + + + Abs Start + + + + Rel Start + + + + Duration + + + + Bits/s A + + + + Bits/s B + + + + Total Packets + + + + Percent Filtered + + + + + ConversationDialog + + Follow Stream… + + + + Follow a TCP or UDP stream. + Простежити за TCP- або UDP-потоком. + + + Graph… + + + + Graph a TCP conversation. + + + + + ConversationHashTablesDialog + + Dialog + Діалог + + + Conversation Hash Tables + + + + + CopyFromProfileButton + + Copy from + + + + Copy entries from another profile. + + + + System default + + + + + CredentialsDialog + + Wireshark - Credentials + + + + Credentials + + + + + CredentialsModel + + Click to select the packet + + + + Click to select the packet with username + + + + Username not available + + + + Packet No. + + + + Protocol + Протокол + + + Username + + + + Additional Info + + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + + + + Copy packet bytes as a hex and ASCII dump. + + + + …as Hex Dump + + + + Copy packet bytes as a hex dump. + + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + + + + Copy packet bytes as a stream of hex. + + + + …as a Base64 String + + + + Copy packet bytes as a base64 encoded string. + + + + Copy packet bytes as application/octet-stream MIME data. + + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + + + + Remove this dissection behavior. + + + + Copy this dissection behavior. + + + + Clear all dissection behaviors. + + + + Decode As… + + + + Open + Відкрити + + + + DecodeAsModel + + Match using this field + + + + Change behavior when the field matches this value + + + + Field value type (and base, if Integer) + + + + Current"Decode As" behavior + + + + Default "Decode As" behavior + + + + String + Рядок + + + Integer, base + + + + unknown + невідомо + + + <none> + + + + GUID + + + + Field + + + + Value + Значення + + + Type + Тип + + + Default + + + + Current + + + + + DisplayFilterCombo + + Display filter selector + + + + Select from previously used filters. + + + + + DisplayFilterEdit + + Display filter entry + + + + Manage saved bookmarks. + + + + Display Filter Expression… + + + + Apply a display filter %1 <%2/> + + + + Enter a display filter %1 + + + + Clear display filter + + + + Apply display filter + + + + Left align buttons + + + + Apply a read filter %1 + + + + Current filter: %1 + + + + Invalid filter: + + + + Save this filter + + + + Remove this filter + + + + Manage Display Filters + + + + Filter Button Preferences... + + + + + DisplayFilterExpressionDialog + + Dialog + Діалог + + + Select a field to start building a display filter. + + + + Field Name + Назва Поля + + + <html><head/><body><p>Search the list of field names.</p></body></html> + + + + Search: + Пошук: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + + + + Relation + + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + + + + Quantifier + + + + Any + Будь-який + + + All + + + + Match against this value. + + + + Value + Значення + + + If the field you have selected has a known set of valid values they will be listed here. + + + + Predefined Values + + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + + + + Range (offset:length) + + + + No display filter + Фільтр відображення відсутній + + + <small><i>A hint.</i></small> + <small><i>Підказка</i></small> + + + Display Filter Expression + + + + Select a field name to get started + + + + Click OK to insert this filter + + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + Діалог + + + Search: + Пошук: + + + Dissector Tables + + + + + DissectorTablesProxyModel + + Table Type + + + + String + Рядок + + + Dissector Description + + + + Integer + + + + Protocol + Протокол + + + Short Name + + + + Table Name + + + + Selector Name + + + + + EnabledProtocolsDialog + + Dialog + Діалог + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>Виключення протоколу припиняє відображення протоколів вищих рівнів</i></small> + + + Search: + Пошук: + + + in + + + + Enable All + Включити Всі + + + Disable All + Виключити Всі + + + Invert + Обернути + + + Enabled Protocols + Включені Протоколи + + + Everywhere + + + + Only Protocols + + + + Only Description + + + + Only enabled protocols + + + + Only disabled protocols + + + + any protocol + + + + non-heuristic protocols + + + + heuristic protocols + + + + + EnabledProtocolsModel + + Protocol + Протокол + + + Description + Опис + + + + EndpointDataModel + + Address + + + + Port + + + + Packets + Пакетів + + + Bytes + + + + Tx Packets + + + + Tx Bytes + + + + Rx Packets + + + + Rx Bytes + + + + Country + + + + City + + + + Latitude + + + + Longitude + + + + AS Number + + + + AS Organization + + + + Total Packets + + + + Percent Filtered + + + + + EndpointDialog + + Map + + + + Draw IPv4 or IPv6 endpoints on a map. + + + + Open in browser + + + + Save As… + + + + Map file error + + + + Save Endpoints Map + + + + Failed to save map file %1. + + + + + EthernetAddressModel + + Type + Тип + + + Name + + + + Address + + + + All entries + + + + Hosts + + + + Ethernet Addresses + Адреси Ethernet + + + Ethernet Manufacturers + Виробники Ethernet + + + Ethernet Well-Known Addresses + Відомі Адреси Ethernet + + + + ExpertInfoDialog + + Dialog + Діалог + + + <small><i>A hint.</i></small> + <small><i>Підказка</i></small> + + + Limit to Display Filter + + + + Group by summary + + + + Search expert summaries. + + + + Search: + Пошук: + + + Show… + Show... + Показати... + + + Error + Помилка + + + Show error packets. + Показати пакети з помилками. + + + Warning + Попередження + + + Show warning packets. + Показати пакети з попередженнями. + + + Note + Примітка + + + Show note packets. + Показати пакети з примітками. + + + Chat + Чат + + + Show chat packets. + Показати пакети з чатів. + + + Comment + Коментар + + + Show comment packets. + Показати пакети з коментарями. + + + Expert Information + Експертна Інформація + + + Collapse All + + + + Expand All + + + + Capture file closed. + Файл захоплення закрито. + + + No display filter + Фільтр відображення відсутній + + + No display filter set. + Не встановлено фільтр відображення. + + + Limit information to "%1". + + + + Display filter: "%1" + + + + + ExpertInfoProxyModel + + Packet + Пакет + + + Severity + + + + Summary + + + + Group + + + + Protocol + Протокол + + + Count + + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + Експортувати Розібрані Пакети + + + Export As: + Export as: + Експортувати Як: + + + Plain text (*.txt) + Звичайний текст (*.txt) + + + Comma Separated Values - summary (*.csv) + + + + PSML - summary (*.psml, *.xml) + + + + PDML - details (*.pdml, *.xml) + + + + JSON (*.json) + + + + C Arrays - bytes (*.c, *.h) + Масиви C - байти (*.c, *.h) + + + + ExportObjectDialog + + Dialog + Діалог + + + Content Type: + + + + Searching for objects + Пошук об'єктів + + + Text Filter: + + + + Only display entries containing this string + + + + Preview + + + + All Content-Types + + + + Export + Експортувати + + + %1 object list + + + + Save Object As… + + + + Save All Objects In… + + + + + ExportObjectModel + + Packet + Пакет + + + Hostname + + + + Content Type + + + + Size + + + + Filename + + + + + ExportPDUDialog + + Dialog + Діалог + + + Display filter: + Фільтр відображення: + + + + ExtArgSelector + + Reload data + + + + + ExtcapArgumentFileSelection + + Clear + + + + All Files ( + + + + Open File + Відкрити Файл + + + Select File + + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + + + + Start + + + + Save + + + + Default + + + + Restore default value of the item + + + + Extcap Help cannot be found + + + + The help for the extcap interface %1 cannot be found. Given file: %2 + + + + Save parameter(s) on capture start + + + + + FieldFilterEdit + + Display filter entry + + + + Enter a field %1 + + + + Invalid filter: + + + + + FileSetDialog + + Dialog + Діалог + + + Directory: + Каталог: + + + No files in Set + + + + No capture loaded + Немає завантаженого захоплення + + + %Ln File(s) in Set + %1 File%2 in Set + + + + + + + + + FilesetEntryModel + + Open this capture file + + + + Filename + + + + Created + + + + Modified + + + + Size + + + + + FilterAction + + Selected + + + + Not Selected + + + + …and Selected + + + + …or Selected + + + + …and not Selected + + + + …or not Selected + + + + + FilterDialog + + Dialog + Діалог + + + Create a new filter. + + + + Remove this filter. + Remove this profile. + + + + Copy this filter. + Copy this profile. + + + + Capture Filters + + + + Display Filters + + + + Open + Відкрити + + + New capture filter + This text is automatically filled in when a new filter is created + + + + New display filter + This text is automatically filled in when a new filter is created + + + + + FilterExpressionFrame + + Frame + Кадр + + + Filter Buttons Preferences… + + + + Label: + + + + Enter a description for the filter button + + + + Filter: + + + + Enter a filter expression to be applied + + + + Comment: + + + + Enter a comment for the filter button + + + + Missing label. + + + + Missing filter expression. + + + + Invalid filter expression. + + + + + FilterExpressionToolBar + + Filter Button Preferences... + + + + Edit + + + + Disable + + + + Remove + + + + + FilterListModel + + Filter Name + + + + Filter Expression + + + + + FindLineEdit + + Textual Find + + + + Regular Expression Find + + + + + FirewallRulesDialog + + Create rules for + + + + Inbound + + + + Deny + + + + Firewall ACL Rules + + + + Copy + Скопіювати + + + IPv4 source address. + + + + IPv4 destination address. + + + + Source port. + + + + Destination port. + + + + IPv4 source address and port. + + + + IPv4 destination address and port. + + + + MAC source address. + + + + MAC destination address. + + + + Text file (*.txt);;All Files ( + + + + Warning + Попередження + + + Unable to save %1 + + + + + FolderListModel + + "File" dialogs + + + + capture files + + + + Temp + + + + untitled capture files + + + + Personal configuration + + + + Global configuration + + + + dfilters, preferences, ethers, … + + + + dfilters, preferences, manuf, … + + + + System + + + + ethers, ipxnets + + + + Program + + + + program files + + + + Personal Plugins + + + + binary plugins + + + + Global Plugins + + + + Personal Lua Plugins + + + + Global Lua Plugins + + + + Lua scripts + + + + Personal Extcap path + + + + external capture (extcap) plugins + + + + Global Extcap path + + + + MaxMind DB path + + + + MaxMind DB database search path + + + + MIB/PIB path + + + + SMI MIB/PIB search path + + + + macOS Extras + + + + Extra macOS packages + + + + Name + + + + Location + + + + Typical Files + + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + + + + Print + Роздрукувати + + + ASCII + ASCII + + + C Arrays + Масиви C + + + EBCDIC + EBCDIC + + + Hex Dump + Шістнадцятковий Дамп + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + + + + Save as… + Зберегти як... + + + Back + + + + Packet %1. + Пакет %1. + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + + + + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + + + + + + + %Ln turn(s). + + + + + + + + Click to select. + Клікніть для вибору. + + + Regex Find: + + + + No capture file. + + + + Please make sure you have a capture file opened. + + + + Error following stream. + Помилка слідування за потоком. + + + Capture file invalid. + Файл захоплення пошкоджено. + + + Please make sure you have a %1 packet selected. + + + + %1 stream not found on the selected packet. + + + + Entire conversation (%1) + + + + Follow %1 Stream (%2) + + + + Error creating filter for this stream. + Помилка при створенні фільтру для цього потоку. + + + Save Stream Content As… + + + + [Stream output truncated] + + + + %Ln total stream(s). + + + + + + + + Max sub stream ID for the selected stream: %Ln + + + + + + + + File closed. + Файл закрито. + + + Follow Stream + Простежити за Потоком + + + Hint. + Підказка. + + + Show data as + Show and save data as + + + + Stream + Потік + + + Substream + + + + Find: + Знайти: + + + Find &Next + Знайти &Наступний + + + + FontColorPreferencesFrame + + Frame + Кадр + + + Main window font: + Шрифт основного вікна: + + + Select Font + Вибрати Шрифт + + + Colors: + Кольори: + + + System Default + + + + Solid + + + + Sample ignored packet text + Зразок тексту проігнорованого пакета + + + Sample marked packet text + Зразок тексту позначеного пакета + + + Sample active selected item + + + + Style: + + + + Gradient + + + + Sample inactive selected item + + + + Sample "Follow Stream" client text + Зразок тексту клієнта "Слідувати за потоком" + + + Sample "Follow Stream" server text + Зразок тексту сервера "Слідкувати за потоком" + + + Sample valid filter + Зразок дійсного фільтра + + + Sample invalid filter + Зразок недійсного фільтра + + + Sample warning filter + Sample deprecated filter + + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + + + + Lazy badgers move unique waxy jellyfish packets + + + + Font + Шрифт + + + + FunnelStringDialog + + Dialog + Діалог + + + + FunnelTextDialog + + Dialog + Діалог + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + + + + Highlight: + + + + + GsmMapSummaryDialog + + Dialog + Діалог + + + GSM MAP Summary + + + + File + Файл + + + Name + Ім'я + + + Length + Розмір + + + Format + Формат + + + Snapshot length + Розмір знімку + + + Data + + + + First packet + Перший пакет + + + Last packet + Останній пакет + + + Elapsed + Витрачено + + + Packets + Пакетів + + + Invokes + + + + Total number of Invokes + + + + Average number of Invokes per second + + + + Total number of bytes for Invokes + + + + Average number of bytes per Invoke + + + + Return Results + + + + Total number of Return Results + + + + Average number of Return Results per second + + + + Total number of bytes for Return Results + + + + Average number of bytes per Return Result + + + + Totals + + + + Total number of GSM MAP messages + + + + Average number of GSM MAP messages per second + + + + Total number of bytes for GSM MAP messages + + + + Average number of bytes per GSM MAP message + + + + + IOConsoleDialog + + Dialog + Діалог + + + Enter code + + + + Evaluate + + + + Clear + + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + Діалог + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + + Remove this graph. + Remove this dissection behavior. + Видалити цей графік. + + + Add a new graph. + Додати новий графік. + + + Duplicate this graph. + Продублювати цей графік. + + + Clear all graphs. + + + + Move this graph upwards. + + + + Move this graph downwards. + + + + Mouse + Миша + + + Drag using the mouse button. + Перетягнути, використовуючи кнопку миші. + + + drags + перетягує + + + Select using the mouse button. + Вибрати область, використовуючи кнопку миші. + + + zooms + масштабує + + + Interval + Інтервал + + + Time of day + Час доби + + + Log scale + Логарифмічна шкала + + + Automatic update + + + + Enable legend + + + + Reset + Скинути + + + Reset Graph + Скинути Зміни До Діаграми + + + Reset the graph to its initial state. + Відновити початковий стан графіку. + + + 0 + 0 + + + Zoom In + Наблизити + + + + + + + + + Zoom Out + Віддалити + + + - + - + + + Move Up 10 Pixels + Переміститися Вгору На 10 Пікселів + + + Up + Вгору + + + Move Left 10 Pixels + Переміститися Вліво На 10 Пікселів + + + Left + Вліво + + + Move Right 10 Pixels + Переміститися Вправо На 10 Пікселів + + + Right + Вправо + + + Move Down 10 Pixels + Переміститися Вниз На 10 Пікселів + + + Down + Вниз + + + Move Up 1 Pixel + Переміститися Вгору На 1 Піксель + + + Shift+Up + Shift+Вгору + + + Move Left 1 Pixel + Переміститися Вліво На 1 Піксель + + + Shift+Left + Shift+Вліво + + + Move Right 1 Pixel + Переміститися Вправо На 1 Піксель + + + Shift+Right + Shift+Вправо + + + Move Down 1 Pixel + Переміститися Вниз На 1 Піксель + + + Move down 1 Pixel + Move down 1 pixel + Переміститися вниз на 1 Піксель + + + Shift+Down + Shift+Вниз + + + Go To Packet Under Cursor + Перейти До Пакету Під Курсором + + + Go to packet currently under the cursor + Перейти до пакету, що в даний момент знаходиться під курсором + + + G + G + + + Drag / Zoom + Перетягування / Масштабування + + + Toggle mouse drag / zoom behavior + Встановити для миші поведінку перетягування / масштабування + + + Z + Z + + + Capture / Session Time Origin + Час Початку Захоплення / Сесії + + + Toggle capture / session time origin + Встановити початок часу захоплення / сессії + + + T + T + + + Crosshairs + Перехрестя + + + Toggle crosshairs + Показати / приховати перехрестя + + + Space + Прогалина + + + Zoom In X Axis + + + + X + + + + Zoom Out X Axis + + + + Shift+X + + + + Zoom In Y Axis + + + + Y + + + + Zoom Out Y Axis + + + + Shift+Y + + + + 1 sec + 1 сек + + + 10 sec + 10 сек + + + 1 min + 1 хв + + + 10 min + 10 хв + + + Time (s) + Час (сек) + + + I/O Graphs + + + + Save As… + + + + Copy + Скопіювати + + + Copy graphs from another profile. + + + + 1 ms + + + + 2 ms + + + + 5 ms + + + + 10 ms + + + + 20 ms + + + + 50 ms + + + + 100 ms + + + + 200 ms + + + + 500 ms + + + + 2 sec + 10 сек {2 ?} + + + 5 sec + 10 сек {5 ?} + + + Wireshark I/O Graphs: %1 + + + + Filtered packets + + + + Filtered events + + + + All Packets + + + + TCP Errors + + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + Наведіть курсор миші на графік для детальнішої інформації. + + + No packets in interval + Відсутні пакети в інтервалі + + + No events in interval + + + + Click to select packet + Клікніть, щоб вибрати пакет + + + Packet + Пакет + + + Click to select event + + + + Event + Подія + + + %1 (%2s%3). + %1 (%2с%3). + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Відпустіть для вибору області, x = від %1 до %2, y = від %3 до %4 + + + Unable to select range. + Неможливо вибрати область. + + + Click to select a portion of the graph. + Клікніть, щоб виділити частину графіку. + + + Portable Document Format (*.pdf) + Формат Переносних Документів (*.pdf) + + + Portable Network Graphics (*.png) + Переносна Мережева Графіка (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Формат Обміну Файлами JPEG (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + + + + Save Graph As… + + + + + Iax2AnalysisDialog + + Dialog + Діалог + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + + + + Forward + + + + Packet + Пакет + + + Delta (ms) + + + + Jitter (ms) + + + + Bandwidth + + + + Status + Статус + + + Length + Розмір + + + Reverse + + + + Graph + + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + + + + Forward Jitter + + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + + + + Forward Difference + + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + + + + Reverse Jitter + + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + + + + Reverse Difference + + + + <small><i>A hint.</i></small> + <small><i>Підказка</i></small> + + + Audio + + + + Save the audio data for both channels. + + + + Forward Stream Audio + + + + Save the forward stream audio data. + + + + Reverse Stream Audio + + + + Save the reverse stream audio data. + + + + CSV + CSV + + + Save both tables as CSV. + + + + Forward Stream CSV + + + + Save the forward table as CSV. + + + + Reverse Stream CSV + + + + Save the reverse table as CSV. + + + + Save Graph + Зберегти графік + + + Save the graph image. + + + + Go to Packet + Перейти до Пакету + + + Select the corresponding packet in the packet list. + Вибрати відповідний пакет у списку пакетів + + + G + G + + + Next Problem Packet + + + + Go to the next problem packet + + + + N + + + + IAX2 Stream Analysis + + + + Unable to save RTP data. + + + + Please select an IAX2 packet. + + + + G: Go to packet, N: Next problem packet + + + + Portable Document Format (*.pdf) + Формат Переносних Документів (*.pdf) + + + Portable Network Graphics (*.png) + Переносна Мережева Графіка (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Формат Обміну Файлами JPEG (*.jpeg *.jpg) + + + Save Graph As… + + + + Can't save in a file: Wrong length of captured packets. + + + + Can't save in a file: File I/O problem. + + + + Save forward stream audio + + + + Save reverse stream audio + + + + Save audio + + + + Sun Audio (*.au) + + + + ;;Raw (*.raw) + + + + Warning + Попередження + + + Unable to save in that format + + + + Unable to save %1 + + + + Saving %1… + + + + Analyzing IAX2 + + + + Save forward stream CSV + + + + Save reverse stream CSV + + + + Save CSV + + + + Comma-separated values (*.csv) + Значення, розділені комою (*.csv) + + + + ImportTextDialog + + File: + Файл: + + + Set name of text file to import + Встановити ім'я текстового файлу для імпорту + + + Browse for text file to import + Переглянути текстові файли для імпорту + + + Browse… + Browse... + Перегляд... + + + Hex Dump + Шістнадцятковий Дамп + + + Import a standard hex dump as exported by Wireshark + + + + Offsets in the text file are in octal notation + Зсуви в текстовому файлі задані у вісімковій нотації + + + Octal + Вісімкові + + + Offsets: + Зсуви: + + + Offsets in the text file are in hexadecimal notation + Зсуви в текстовому файлі задані в шістнадцятковій нотації + + + Hexadecimal + Шістнадцяткові + + + Offsets in the text file are in decimal notation + Зсуви в текстовому файлі задані в десятковій нотації + + + Decimal + Десяткові + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + + + + ASCII identification: + + + + Regular Expression + + + + Import a file formatted according to a custom regular expression + + + + Packet format regular expression + + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + + + + This is regexHintLabel, it will be set to default_regex_hint + + + + Data encoding: + + + + How data is encoded + + + + encodingRegexExample + + + + List of characters indicating incoming packets + + + + iI< + + + + List of characters indicating outgoing packets + + + + oO> + + + + Timestamp format: + Формат міток часу: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + Наявність в файлі інформації, що вказує напрямок (вхідний чи вихідний) пакету. + + + Direction indication: + Вказання напрямку: + + + ExportPDU + + + + IP version: + + + + Interface name: + + + + The name of the interface to write to the import capture file + + + + Fake IF, Import from Hex Dump + + + + Maximum frame length: + Максимальна довжина кадру: + + + Encapsulation + Інкапсуляція + + + The text file has no offset + + + + None + Відсутня + + + <small><i>recommended regex:</small></i> + + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + + + + %H:%M:%S.%f + + + + timestampExampleLabel + + + + Encapsulation Type: + Тип Інкапсуляції: + + + Encapsulation type of the frames in the import capture file + Тип інкапсуляції кадрів в імпортованому файлі захоплення + + + Prefix each frame with an Ethernet and IP header + + + + IP + + + + Prefix each frame with an Ethernet, IP and UDP header + + + + Prefix each frame with an Ethernet, IP and TCP header + + + + Prefix each frame with an Ethernet, IP and SCTP header + + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + + + + Source address: + + + + Destination address: + + + + Dissector + + + + The IP protocol ID for each frame + + + + The IP source address for each frame + + + + The IP destination address for each frame + + + + The UDP, TCP or SCTP source port for each frame + Порт джерела UDP, TCP чи SCTP для кожного кадру + + + The SCTP DATA payload protocol identifier for each frame + + + + The UDP, TCP or SCTP destination port for each frame + + + + Prefix each frame with an Ethernet header + + + + Ethernet + Ethernet + + + SCTP + SCTP + + + PPI: + + + + Protocol (dec): + + + + Leave frames unchanged + + + + No dummy header + + + + Tag: + Тег: + + + UDP + UDP + + + Source port: + Порт джерела: + + + The Ethertype value of each frame + + + + TCP + TCP + + + The SCTP verification tag for each frame + + + + Destination port: + Порт призначення: + + + Ethertype (hex): + Ethertype (hex): + + + SCTP (Data) + + + + The dissector to use for each frame + + + + The IP Version to use for the dummy IP header + + + + The maximum size of the frames to write to the import capture file (max 256kiB) + + + + Supported fields are data, dir, time, seqno + + + + Missing capturing group data (use (? + + + + Import From Hex Dump + Імпортувати З Шістнадцяткового Дампу + + + Import + + + + Import Text File + Імпортувати Текстовий Файл + + + + InterfaceFrame + + Frame + Кадр + + + Wired + + + + AirPCAP + + + + Pipe + + + + STDIN + + + + Bluetooth + + + + Wireless + + + + Dial-Up + + + + USB + + + + External Capture + + + + Virtual + + + + Remote interfaces + + + + Show hidden interfaces + + + + External capture interfaces disabled. + + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + + + + You don't have permission to capture on local interfaces. + + + + No interfaces found. + + + + Interfaces not loaded (due to preference). Go to Capture + + + + Start capture + + + + Hide Interface + + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + + + + + InterfaceToolbar + + Frame + Кадр + + + Select interface + + + + Interface + Інтерфейс + + + + InterfaceToolbarLineEdit + + Apply changes + Застосувати зміни + + + + InterfaceTreeModel + + Show + Показати + + + Friendly Name + + + + Interface Name + + + + No interfaces found. + + + + This version of Wireshark was built without packet capture support. + + + + Local Pipe Path + + + + Comment + Коментар + + + Link-Layer Header + + + + Promiscuous + + + + Snaplen (B) + + + + Buffer (MB) + + + + Monitor Mode + + + + Capture Filter + + + + Addresses + + + + Address + + + + Extcap interface: %1 + + + + No addresses + + + + No capture filter + + + + Capture filter + Фільтр захоплення + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + + + + Sources + Джерела + + + Address/Transport + + + + Data frames + + + + Data bytes + + + + Data frames/bytes + + + + Data rate + + + + RX data frames + + + + RX data bytes + + + + RX data frames/bytes + + + + RX data rate + + + + NCF frames + + + + NCF count + + + + NCF bytes + + + + NCF frames/bytes + + + + NCF count/bytes + + + + NCF frames/count + + + + NCF frames/count/bytes + + + + NCF rate + + + + SM frames + + + + SM bytes + + + + SM frames/bytes + + + + SM rate + + + + Show + Показати + + + Data + + + + RX Data + + + + NCF + Nak ConFirmation + + + + SM + Session Message + + + + sequence numbers for transport + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + + SQN + + + + Count + + + + Frame + Кадр + + + SQN/Reason + + + + Receivers + + + + NAK frames + + + + NAK count + + + + NAK bytes + + + + NAK rate + + + + NAK sequence numbers for transport + + + + Display filter: + Фільтр відображення: + + + Regenerate statistics using this display filter + + + + Apply + Застосувати + + + Copy as CSV + Скопіювати як CSV + + + Copy the tree as CSV + Скопіювати дерево як CSV + + + Copy as YAML + Скопіювати як YAML + + + Copy the tree as YAML + Скопіювати дерево як YAML + + + Show the data frames column + + + + Show the data bytes column + + + + Show the data frames/bytes column + + + + Show the RX data frames column + + + + Show the RX data bytes column + + + + Show the RX data frames/bytes column + + + + Show the NCF frames column + + + + Show the NCF bytes column + + + + Show the NCF count column + + + + Show the data rate column + + + + Show the RX data rate column + + + + Show the NCF frames/bytes column + + + + Show the NCF count/bytes column + + + + Show the NCF frames/count column + + + + Show the NCF frames/count/bytes column + + + + Show the NCF rate column + + + + Show the SM frames column + + + + Show the SM bytes column + + + + Show the SM frames/bytes column + + + + Show the SM rate column + + + + Auto-resize columns to content + + + + Resize columns to content size + + + + LBT-RM Statistics failed to attach to tap + + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + + + + Sources + Джерела + + + Address/Transport/Client + + + + Data frames + + + + Data bytes + + + + Data frames/bytes + + + + Data rate + + + + RX data frames + + + + RX data bytes + + + + RX data frames/bytes + + + + RX data rate + + + + NCF frames + + + + NCF count + + + + NCF bytes + + + + NCF frames/count + + + + NCF frames/bytes + + + + NCF count/bytes + + + + NCF frames/count/bytes + + + + NCF rate + + + + SM frames + + + + SM bytes + + + + SM frames/bytes + + + + SM rate + + + + RST frames + + + + RST bytes + + + + RST frames/bytes + + + + RST rate + + + + Show + Показати + + + Data SQN + + + + RX Data SQN + + + + NCF SQN + + + + SM SQN + + + + RST reason + + + + details for transport + + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + + SQN + + + + Count + + + + Frame + Кадр + + + Reason + Причина + + + SQN/Reason + + + + Receivers + + + + Address/Transport + + + + NAK frames + + + + NAK count + + + + NAK bytes + + + + NAK frames/count + + + + NAK count/bytes + + + + NAK frames/bytes + + + + NAK frames/count/bytes + + + + NAK rate + + + + ACK frames + + + + ACK bytes + + + + ACK frames/bytes + + + + ACK rate + + + + CREQ frames + + + + CREQ bytes + + + + CREQ frames/bytes + + + + CREQ rate + + + + NAK SQN + + + + ACK SQN + + + + CREQ request + + + + Display filter: + Фільтр відображення: + + + Regenerate statistics using this display filter + + + + Apply + Застосувати + + + Copy as CSV + Скопіювати як CSV + + + Copy the tree as CSV + Скопіювати дерево як CSV + + + Copy as YAML + Скопіювати як YAML + + + Copy the tree as YAML + Скопіювати дерево як YAML + + + Show the data frames column + + + + Show the data bytes column + + + + Show the data frames/bytes column + + + + Show the data rate column + + + + Show the RX data frames column + + + + Show the RX data bytes column + + + + Show the RX data frames/bytes column + + + + Show the RX data rate column + + + + Show the NCF frames column + + + + Show the NCF count column + + + + Show the NCF bytes column + + + + Show the NCF frames/bytes column + + + + Show the NCF count/bytes column + + + + Show the NCF frames/count column + + + + Show the NCF frames/count/bytes column + + + + Show the SM frames column + + + + Show the SM bytes column + + + + Show the SM frames/bytes column + + + + Show the SM rate column + + + + Show the RST frames column + + + + Show the RST bytes column + + + + Show the RST frames/bytes column + + + + Show the RST rate column + + + + Show the NAK frames column + + + + Show the NAK count column + + + + Show the NAK bytes column + + + + Show the NAK frames/count column + + + + Show the NAK count/bytes column + + + + Show the NAK frames/bytes column + + + + Show the NAK frames/count/bytes column + + + + Show the NAK rate column + + + + Show the ACK frames column + + + + Show the ACK bytes column + + + + Show the ACK frames/bytes column + + + + Show the ACK rate column + + + + Show the CREQ frames column + + + + Show the CREQ bytes column + + + + Show the CREQ frames/bytes column + + + + Show the CREQ rate column + + + + Auto-resize columns to content + + + + Resize columns to content size + + + + Show the NCF rate column + + + + LBT-RU Statistics failed to attach to tap + + + + + LBMStreamDialog + + Dialog + Діалог + + + Stream + Потік + + + Endpoint A + + + + Endpoint B + + + + Messages + + + + Bytes + + + + First Frame + + + + Last Frame + + + + Display filter: + Фільтр відображення: + + + Regenerate statistics using this display filter + + + + Apply + Застосувати + + + Copy as CSV + Скопіювати як CSV + + + Copy the tree as CSV + Скопіювати дерево як CSV + + + Copy as YAML + Скопіювати як YAML + + + Copy the tree as YAML + Скопіювати дерево як YAML + + + LBM Stream failed to attach to tap + + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + %Ln nodes + + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + + LayoutPreferencesFrame + + Frame + Кадр + + + Pane 1: + Панель 1: + + + Packet List + Список Пакетів + + + Packet Details + Складові Пакету + + + Packet Bytes + Байти Пакету + + + Packet Diagram + + + + None + Відсутня + + + Pane 2: + Панель 2: + + + Pane 3: + Панель 3: + + + Packet List settings: + + + + Show packet separator + + + + Show column definition in column context menu + + + + Allow the list to be sorted + + + + Maximum number of cached rows (affects sorting) + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + Enable mouse-over colorization + + + + Status Bar settings: + + + + Show selected packet number + + + + Show file load time + + + + + LteMacStatisticsDialog + + LTE Mac Statistics + + + + Include SR frames in filter + + + + Include RACH frames in filter + + + + MAC Statistics + + + + + LteRlcGraphDialog + + Dialog + Діалог + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + + + + Mouse + Миша + + + Drag using the mouse button. + Перетягнути, використовуючи кнопку миші. + + + drags + перетягує + + + Select using the mouse button. + Вибрати область, використовуючи кнопку миші. + + + zooms + масштабує + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Відновити початковий стан графіку.</p></body></html> + + + Reset + Скинути + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Змінити напрямок з'єднання (для огляду протилежного потоку).</p></body></html> + + + Switch Direction + Змінити Напрямок + + + Reset Graph + + + + Reset the graph to its initial state. + Відновити початковий стан графіку. + + + 0 + 0 + + + Zoom In + Наблизити + + + + + + + + + Zoom Out + + + + - + - + + + Move Up 10 Pixels + Переміститися Вгору На 10 Пікселів + + + Up + Вгору + + + Move Left 10 Pixels + Переміститися Вліво На 10 Пікселів + + + Left + Вліво + + + Move Right 10 Pixels + Переміститися Вправо На 10 Пікселів + + + Right + Вправо + + + Move Down 10 Pixels + Переміститися Вниз На 10 Пікселів + + + Down + Вниз + + + Move Up 1 Pixel + Переміститися Вгору На 1 Піксель + + + Shift+Up + Shift+Вгору + + + Move Left 1 Pixel + Переміститися Вліво На 1 Піксель + + + Shift+Left + Shift+Вліво + + + Move Right 1 Pixel + Переміститися Вправо На 1 Піксель + + + Shift+Right + Shift+Вправо + + + Move Down 1 Pixel + Переміститися Вниз На 1 Піксель + + + Move down 1 Pixel + Переміститися вниз на 1 Піксель + + + Shift+Down + Shift+Вниз + + + Drag / Zoom + Перетягування / Масштабування + + + Toggle mouse drag / zoom behavior + Встановити для миші поведінку перетягування / масштабування + + + Z + Z + + + Crosshairs + Перехрестя + + + Toggle crosshairs + Показати / приховати перехрестя + + + Space + Прогалина + + + Move Up 100 Pixels + Переміститися Вгору На 10 Пікселів {100 ?} + + + PgUp + PgUp + + + PgDown + PgDown + + + Go To Packet Under Cursor + Перейти До Пакету Під Курсором + + + Go to packet currently under the cursor + Перейти до пакету, що в даний момент знаходиться під курсором + + + G + G + + + Zoom In X Axis + + + + X + + + + Zoom Out Y Axis + + + + Shift+Y + + + + Zoom In Y Axis + + + + Y + + + + Zoom Out X Axis + + + + Shift+X + + + + Switch direction (swap between UL and DL) + + + + D + D + + + Time + Час + + + Sequence Number + + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + + + + LTE RLC Graph - no channel selected + + + + Save As… + + + + %1 %2 (%3s seq %4 len %5) + + + + Click to select packet + Клікніть, щоб вибрати пакет + + + Packet + Пакет + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Відпустіть для вибору області, x = від %1 до %2, y = від %3 до %4 + + + Unable to select range. + Неможливо вибрати область. + + + Click to select a portion of the graph. + Клікніть, щоб виділити частину графіку. + + + Portable Document Format (*.pdf) + Формат Переносних Документів (*.pdf) + + + Portable Network Graphics (*.png) + Переносна Мережева Графіка (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Формат Обміну Файлами JPEG (*.jpeg *.jpg) + + + Save Graph As… + + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + + + + Include SR frames in filter + + + + Include RACH frames in filter + + + + Use RLC frames only from MAC frames + + + + UL Frames + + + + UL Bytes + + + + UL MB/s + + + + UL ACKs + + + + UL NACKs + + + + UL Missing + + + + DL Frames + + + + DL Bytes + + + + DL MB/s + + + + DL ACKs + + + + DL NACKs + + + + DL Missing + + + + RLC Statistics + + + + + MainStatusBar + + Ready to load or capture + Готовий до завантаження або захоплення + + + Ready to load file + Готовий завантажити файл + + + Open the Capture File Properties dialog + + + + Profile: %1 + + + + Manage Profiles… + + + + New… + + + + Edit… + + + + Import + + + + Export + Експортувати + + + Delete + Видалити + + + Switch to + Переключитися на + + + is the highest expert information level + is the highest expert info level + є найвищим рівнем експертної інформації + + + ERROR + ПОМИЛКА + + + WARNING + ПОПЕРЕДЖЕННЯ + + + NOTE + ПРИМІТКА + + + CHAT + ЧАТ + + + No expert information + No expert info + Експертна інформація відсутня + + + %Ln byte(s) + , %1 bytes + + + + + + + + Byte %1 + + + + Bytes %1-%2 + + + + Selected Packet: %1 %2 + + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + + + + %1 Selected: %2 (%3%) + + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + + + + %1 Dropped: %2 (%3%) + + + + %1 Ignored: %2 (%3%) + + + + %1 Comments: %2 + + + + %1 Load time: %2:%3.%4 + %1 Час завантаження: %2:%3.%4 + + + No Packets + Пакети відсутні + + + From Zip File... + + + + From Directory... + + + + Selected Personal Profile... + + + + All Personal Profiles... + + + + Packets: %1 + Пакетів: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + + MainWindowPreferencesFrame + + Frame + Кадр + + + Checking this will save the size, position, and maximized state of the main window. + + + + Remember main window size and placement + + + + Open files in + + + + This folder: + + + + Browse… + Browse... + Перегляд... + + + The most recently used folder + + + + Show up to + + + + filter entries + + + + recent files + + + + Confirm unsaved capture files + + + + Display autocompletion for filter text + + + + Main toolbar style: + Стиль головної панелі: + + + Icons only + Тільки іконки + + + Text only + Тільки текст + + + Icons & Text + Іконки і Текст + + + Window title + + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + Prepend window title + + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + Language: + Мова: + + + Use system setting + Використовувати системні налаштування + + + Open Files In + + + + + ManageInterfacesDialog + + Manage Interfaces + Керування Інтерфейсами + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>Змініть стан прапорця для того, щоб приховати чи показати прихований інтерфейс.</p></body></html> + + + Local Interfaces + Локальні Інтерфейси + + + Show + Показати + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p>Додати канал для захоплення або прибрати існуючий канал зі списку.</p></body></html> + + + Pipes + Канали (Pipes) + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>Додати новий канал, використовуючи типові налаштування.</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>Видалити вибраний канал зі списку.</p></body></html> + + + Remote Interfaces + Віддалені Інтерфейси + + + Host / Device URL + URL хосту / пристрою + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>Додати віддалений хост та його інтерфейси</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>Видалити вибраний хост зі списку.</p></body></html> + + + Remote Settings + Віддалені Налаштування + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + Ця версія Wireshark’а не зберігає налаштування каналів. + + + This version of Wireshark does not save remote settings. + Ця версія Wireshark'а не підтримує віддалене налаштування + + + This version of Wireshark does not support remote interfaces. + Ця версія Wireshark'а не підтримує віддалені інтерфейси. + + + New Pipe + + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + + + + Copy + Скопіювати + + + Find + Знайти + + + Clear + + + + + ManufTableModel + + Address Block + + + + Short Name + + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + + + + + Mtp3SummaryDialog + + Dialog + Діалог + + + MTP3 Summary + + + + File + Файл + + + Name + + + + Length + Розмір + + + Format + Формат + + + Snapshot length + Розмір знімку + + + Data + + + + First packet + Перший пакет + + + Last packet + Останній пакет + + + Elapsed + Витрачено + + + Packets + Пакетів + + + Service Indicator (SI) Totals + + + + SI + + + + MSUs + + + + MSUs/s + + + + Bytes + + + + Bytes/MSU + + + + Bytes/s + + + + Totals + + + + Total MSUs + + + + Total Bytes + + + + Average Bytes/MSU + + + + Average Bytes/s + + + + + MulticastStatisticsDialog + + UDP Multicast Streams + + + + Source Address + Адреса Джерела + + + Source Port + Порт Джерела + + + Destination Address + Адреса Призначення + + + Destination Port + Порт Призначення + + + Packets + Пакетів + + + Packets/s + + + + Avg BW (bps) + + + + Max BW (bps) + + + + Max Burst + + + + Burst Alarms + + + + Max Buffers (B) + + + + Buffer Alarms + + + + Burst measurement interval (ms): + + + + Burst alarm threshold (packets): + + + + Buffer alarm threshold (B): + + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + + + + The burst interval must be between 1 and 1000. + + + + The burst alarm threshold isn't valid. + + + + The buffer alarm threshold isn't valid. + + + + The stream empty speed should be between 1 and 10000000. + + + + The total empty speed should be between 1 and 10000000. + + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + + + + + PacketCommentDialog + + Edit Packet Comment + + + + Add Packet Comment + + + + + PacketDiagram + + Packet diagram + + + + Show Field Values + + + + Save Diagram As… + + + + Copy as Raster Image + + + + …as SVG + + + + Portable Network Graphics (*.png) + Переносна Мережева Графіка (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Формат Обміну Файлами JPEG (*.jpeg *.jpg) + + + Scalable Vector Graphics (*.svg) + + + + Save Graph As… + + + + + PacketDialog + + Dialog + Діалог + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + + + + Packet %1 + + + + [%1 closed] + [%1 закрито] + + + Byte %1 + + + + Bytes %1-%2 + + + + %Ln byte(s) + + + + + + + + + PacketFormatGroupBox + + GroupBox + + + + Packet Format + Формат Пакету + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + + + + Summary line + + + + Include column headings + + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + + + + Details: + + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + + + + All co&llapsed + + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + + + + As displa&yed + + + + <html><head/><body><p>Export all packet detail items</p></body></html> + + + + All e&xpanded + + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + + + + Bytes + + + + Include secondary data sources + + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + + + + + PacketList + + Protocol Preferences + + + + Summary as Text + + + + …as CSV + + + + …as YAML + + + + Decode As… + + + + Frame %1: %2 + + + + + + [ Comment text exceeds %1. Stopping. ] + + + + + PacketListHeader + + Align Left + + + + Align Center + + + + Align Right + + + + Edit Column + + + + Resize to Contents + + + + Column Preferences… + + + + Resize Column to Width… + + + + Resolve Names + + + + Remove this Column + + + + Column %1 + + + + Width: + + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + + + + Sorting "%1"… + + + + Sorting … + + + + + PacketRangeGroupBox + + Form + + + + Packet Range + Діапазон Пакетів + + + - + - + + + Displayed + Відображені + + + &Marked packets only + Тільки &позначені пакети + + + &Range: + &Діапазон: + + + Remove &ignored packets + Видалити &проігноровані пакети + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + Від першого до &останнього позначеного + + + &All packets + &Всі пакети + + + &Selected packets only + Тільки &вибрані пакети + + + Captured + Захоплені + + + + PathSelectionDelegate + + Open a pipe + + + + + PathSelectionEdit + + Browse + + + + Select a path + + + + + PluginListModel + + Name + + + + Version + + + + Type + Тип + + + Path + + + + + PortsModel + + All entries + + + + tcp + + + + udp + + + + sctp + + + + dccp + + + + Name + + + + Port + + + + Type + Тип + + + + PreferenceEditorFrame + + Frame + Кадр + + + … + + + + a preference + + + + Browse… + Перегляд... + + + Open %1 preferences… + + + + Invalid value. + + + + + PreferencesDialog + + Search: + Пошук: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + + + + + PrefsModel + + Advanced + + + + Appearance + + + + Layout + + + + Columns + + + + Font and Colors + + + + Capture + Захоплення + + + Expert + + + + Filter Buttons + + + + RSA Keys + + + + + PrintDialog + + Packet Format + Формат Пакету + + + Print each packet on a new page + + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + + + + Capture information header + + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + + + + Packet Range + Діапазон Пакетів + + + Print + Роздрукувати + + + &Print… + &Роздрукувати... + + + Page &Setup… + + + + %1 %2 total packets, %3 shown + + + + Print Error + + + + Unable to print to %1. + + + + + ProfileDialog + + Search for profile … + + + + Create a new profile using default settings. + Створити новий профіль, використовуючи типові налаштування. + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + + + + Copy this profile. + Скопіювати цей профіль. + + + Configuration Profiles + Профілі Конфігурації + + + Import + noun + + + + Export + noun + Експортувати + + + From Zip File... + + + + From Directory... + + + + %Ln Selected Personal Profile(s)... + + + + + + + + All Personal Profiles... + + + + New profile + + + + Profile Error + Помилка Профілю + + + Exporting profiles + + + + No profiles found for export + + + + Select zip file for export + + + + An import of profiles is not allowed, while changes are pending + + + + An import is pending to be saved. Additional imports are not allowed + + + + An export of profiles is only allowed for personal profiles + + + + An export of profiles is not allowed, while changes are pending + + + + %Ln profile(s) exported + + + + + + + + Select zip file for import + + + + Select directory for import + + + + Zip File (*.zip) + + + + Error + Помилка + + + An error has occurred while exporting profiles + + + + No profiles found for import in %1 + + + + %Ln profile(s) imported + + + + + + + + , %Ln profile(s) skipped + + + + + + + + Importing profiles + + + + %Ln profile(s) selected + + %Ln profile selected + %Ln profiles selected + + + + + + ProfileModel + + Resetting to default + + + + Imported profile + + + + This is a system provided profile + + + + A profile change for this name is pending + + + + (See: %1) + + + + This is an invalid profile definition + + + + A profile already exists with this name + + + + A profile with this name is being deleted + + + + Created from default settings + + + + system provided + + + + deleted + + + + copy + noun + + + + Exporting profiles while changes are pending is not allowed + + + + No profiles found to export + + + + Can't delete profile directory + + + + A profile name cannot contain the following characters: %1 + + + + A profile name cannot contain the '/' character + + + + A profile cannot start or end with a period (.) + + + + Default + + + + Global + + + + Personal + + + + Renamed from: %1 + + + + Copied from: %1 + + + + renamed to %1 + + + + Profile + + + + Type + Тип + + + + ProfileSortModel + + All profiles + + + + Personal profiles + + + + Global profiles + + + + + ProgressFrame + + Frame + Кадр + + + Loading + + + + + ProtoTree + + Packet details + Складові пакету + + + Not a field or protocol + + + + No field reference available for text labels. + + + + Expand Subtrees + + + + Collapse Subtrees + + + + Expand All + + + + Collapse All + + + + Copy + Скопіювати + + + All Visible Items + + + + All Visible Selected Tree Items + + + + Description + Опис + + + Field Name + Назва Поля + + + Value + Значення + + + As Filter + Як Фільтр + + + Wiki Protocol Page + Вікі-Сторінка Протоколів + + + Filter Field Reference + + + + Copied + + + + Wiki Page for %1 + + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + + + + Colorize with Filter + + + + + ProtocolHierarchyDialog + + Dialog + Діалог + + + Protocol + Протокол + + + Percent Packets + + + + Packets + Пакетів + + + Percent Bytes + + + + Bytes + + + + Bits/s + + + + End Packets + + + + End Bytes + + + + End Bits/s + + + + PDUs + + + + <small><i>A hint.</i></small> + <small><i>Підказка</i></small> + + + Copy as CSV + Скопіювати як CSV + + + Copy stream list as CSV. + + + + Copy as YAML + Скопіювати як YAML + + + Copy stream list as YAML. + + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + + + + Copy + Скопіювати + + + as CSV + як CSV + + + as YAML + як YAML + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + Фільтр відображення відсутній. + + + Display filter: %1 + Фільтр відображення: %1 + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + + + + No protocol preferences available + + + + Disable %1 + + + + %1 has no preferences + + + + Open %1 preferences… + + + + + QObject + + Average Throughput (bits/s) + + + + Round Trip Time (ms) + Час Обороту (мс) + + + Segment Length (B) + + + + Sequence Number (B) + + + + Time (s) + Час (с) + + + Window Size (B) + Розмір Вікна (Б) + + + [no capture file] + + + + Conversation + + + + Bars show the relative timeline for each conversation. + + + + Endpoint + + + + Apply as Filter + Застосувати як Фільтр + + + Prepare as Filter + + + + Find + Знайти + + + Colorize + + + + Look Up + + + + Copy + Скопіювати + + + UNKNOWN + + + + Selected + + + + Not Selected + + + + …and Selected + + + + …or Selected + + + + …and not Selected + + + + …or not Selected + + + + A + + + + B + + + + Any + + + + Don't show this message again. + + + + Multiple problems found + + + + %1 (%L2%) + + + + No entries. + + + + %1 entries. + + + + Base station + + + + <Broadcast> + + + + <Hidden> + + + + BSSID + + + + Beacons + + + + Data Pkts + + + + Protection + + + + Address + + + + Pkts Sent + + + + Pkts Received + + + + Comment + Коментар + + + Wrong sequence number + + + + Payload changed to PT=%1 + + + + Incorrect timestamp + + + + Marker missing? + + + + C-RNTI + + + + SPS-RNTI + + + + RNTI + + + + Type + Тип + + + UEId + + + + UL Frames + + + + UL Bytes + + + + UL MB/s + + + + UL Padding % + + + + UL Re TX + + + + DL Frames + + + + DL Bytes + + + + DL MB/s + + + + DL Padding % + + + + DL CRC Failed + + + + DL ReTX + + + + LCID 1 + + + + LCID 2 + + + + LCID 3 + + + + LCID 4 + + + + LCID 5 + + + + LCID 6 + + + + LCID 7 + + + + LCID 8 + + + + LCID 9 + + + + LCID 10 + + + + LCID 32 + + + + LCID 33 + + + + LCID 34 + + + + LCID 35 + + + + LCID 36 + + + + LCID 37 + + + + LCID 38 + + + + TM + + + + UM + + + + AM + + + + Predef + + + + Unknown (%1) + + + + CCCH + + + + SRB-%1 + + + + DRB-%1 + + + + Unknown + Невідомо + + + UE Id + + + + Name + + + + Mode + + + + Priority + + + + default + + + + DLT %1 + + + + Invalid Display Filter + Некоректний Фільтр Відображення + + + The filter expression %1 isn't a valid display filter. (%2). + + + + Error + Помилка + + + No remote interfaces found. + + + + PCAP not found + + + + Unknown error + + + + Default + + + + Changed + + + + Has this preference been changed? + + + + Default value is empty + + + + Gap in dissection + + + + Edit… + + + + Browse… + Перегляд... + + + + QObject::QObject + + CCCH + + + + + RemoteCaptureDialog + + Remote Interface + + + + Host: + + + + Port: + Порт: + + + Authentication + + + + Null authentication + + + + Password authentication + + + + Username: + + + + Password: + + + + Clear list + + + + Error + Помилка + + + No remote interfaces found. + + + + PCAP not found + + + + + RemoteSettingsDialog + + Remote Capture Settings + + + + Capture Options + + + + Do not capture own RPCAP traffic + + + + Use UDP for data transfer + + + + Sampling Options + + + + None + Відсутня + + + 1 of + + + + packets + + + + 1 every + + + + milliseconds + + + + + ResolvedAddressesDialog + + Dialog + Діалог + + + Hosts + + + + Search for entry (min 3 characters) + + + + Ports + + + + Search for port or name + + + + Capture File Comments + + + + Comment + Коментар + + + Show the comment. + + + + IPv4 Hash Table + + + + Show the IPv4 hash table entries. + + + + IPv6 Hash Table + + + + Show the IPv6 hash table entries. + + + + Show All + + + + Show all address types. + + + + Hide All + + + + Hide all address types. + + + + IPv4 and IPv6 Addresses (hosts) + + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + + + + Port names (services) + + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + + + + Ethernet Addresses + Адреси Ethernet + + + Show resolved Ethernet addresses in "ethers" format. + Показувати визначені Ethernet-адреси в форматі «ethers». + + + Ethernet Well-Known Addresses + Відомі Адреси Ethernet + + + Show well-known Ethernet addresses in "ethers" format. + Показувати добре відомі адреси Ethernet в форматі «ethers». + + + Ethernet Manufacturers + Виробники Ethernet + + + Show Ethernet manufacturers in "ethers" format. + Показувати виробників Ethernet в форматі «ethers». + + + [no file] + + + + Resolved Addresses + Визначені Адреси + + + # Resolved addresses found in %1 + + + + # Comments +# +# + + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + + + + Type + Тип + + + Messages + + + + Min SRT + + + + Max SRT + + + + Avg SRT + + + + Min in Frame + + + + Max in Frame + + + + Open Requests + + + + Discarded Responses + + + + Repeated Requests + + + + Repeated Responses + + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + + + + Version: + + + + Program: + + + + DCE-RPC Service Response Times + + + + ONC-RPC Service Response Times + + + + + RsaKeysFrame + + RSA Keys + + + + RSA private keys are loaded from a file or PKCS #11 token. + + + + Add new keyfile… + + + + Add new token… + + + + Remove key + + + + PKCS #11 provider libraries. + + + + Add new provider… + + + + Remove provider + + + + Add PKCS #11 token or key + + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + + + + Select a new PKCS #11 token or key + + + + PKCS #11 token or key + + + + Enter PIN or password for %1 (it will be stored unencrypted) + + + + Enter PIN or password for key + + + + Key could not be added: %1 + + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + + + + Select RSA private key file + + + + Libraries (*.dll) + + + + Libraries (*.so) + + + + Select PKCS #11 Provider Library + + + + Changes will apply after a restart + + + + PKCS #11 provider %1 will be removed after the next restart. + + + + + RtpAnalysisDialog + + Dialog + Діалог + + + Packet + Пакет + + + Sequence + + + + Delta (ms) + + + + Jitter (ms) + Jitter + + + + Skew + + + + Bandwidth + + + + Marker + + + + Status + Статус + + + Stream %1 + + + + Stream %1 Jitter + + + + Stream %1 Difference + + + + Stream %1 Delta + + + + %1 streams, + + + + Save one stream CSV + + + + Save all stream's CSV + + + + &Analyze + &Аналіз + + + Open the analysis window for the selected stream(s) + + + + &Set List + + + + &Add to List + + + + &Remove from List + + + + Replace existing list in RTP Analysis Dialog with new one + + + + Add new set to existing list in RTP Analysis Dialog + + + + Remove selected streams from list in RTP Analysis Dialog + + + + Graph + + + + <small><i>A hint.</i></small> + <small><i>Підказка</i></small> + + + &Export + + + + Open export menu + + + + CSV + CSV + + + Save tables as CSV. + + + + Current Tab Stream CSV + + + + Save the table on the current tab as CSV. + + + + All Tab Streams CSV + + + + Save the table from all tabs as CSV. + + + + Save Graph + Зберегти графік + + + Save the graph image. + + + + Go to Packet + Перейти до Пакету + + + Select the corresponding packet in the packet list. + Вибрати відповідний пакет у списку пакетів + + + G + G + + + Next Problem Packet + + + + Go to the next problem packet + + + + N + + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + + + + &Current Tab + + + + Prepare a filter matching current tab. + + + + &All Tabs + + + + Prepare a filter matching all tabs. + + + + RTP Stream Analysis + + + + Save Graph As… + + + + G: Go to packet, N: Next problem packet + + + + Portable Document Format (*.pdf) + Формат Переносних Документів (*.pdf) + + + Portable Network Graphics (*.png) + Переносна Мережева Графіка (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Формат Обміну Файлами JPEG (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + Значення, розділені комою (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + + + + + RtpPlayerDialog + + RTP Player + + + + Play + + + + Source Address + Адреса Джерела + + + Source Port + Порт Джерела + + + Destination Address + Адреса Призначення + + + Destination Port + Порт Призначення + + + SSRC + SSRC + + + Setup Frame + + + + Packets + Пакетів + + + Time Span (s) + + + + Payloads + + + + <small><i>No audio</i></small> + + + + Start playback of all unmuted streams + + + + Pause/unpause playback + + + + Stop playback + + + + Enable/disable skipping of silence during playback + + + + Min silence: + + + + Minimum silence duration to skip in seconds + + + + Output Device: + + + + Output Audio Rate: + + + + Jitter Buffer: + + + + The simulated jitter buffer in milliseconds. + + + + Playback Timing: + + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + + + + Jitter Buffer + + + + RTP Timestamp + + + + Uninterrupted Mode + + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + + + + Time of Day + + + + &Export + + + + Export audio of all unmuted selected channels or export payload of one channel. + + + + From &cursor + + + + Save audio data started at the cursor + + + + &Stream Synchronized Audio + + + + Save audio data synchronized to start of the earliest stream. + + + + &File Synchronized Audio + + + + Save audio data synchronized to start of the capture file. + + + + &Payload + + + + Save RTP payload of selected stream. + + + + Reset Graph + + + + Reset the graph to its initial state. + Відновити початковий стан графіку. + + + Go To Setup Packet + + + + Go to setup packet of stream currently under the cursor + + + + Mute + + + + Mute selected streams + + + + Unmute + + + + Unmute selected streams + + + + Invert muting of selected streams + + + + Route audio to left channel of selected streams + + + + Route audio to left and right channel of selected streams + + + + Route audio to right channel of selected streams + + + + Remove Streams + + + + Remove selected streams from the list + + + + All + + + + Select all + + + + None + Відсутня + + + Clear selection + + + + Invert + Обернути + + + Invert selection + + + + Play/Pause + + + + Start playing or pause playing + + + + Stop + + + + Stop playing + + + + I&naudible streams + + + + Select/Deselect inaudible streams + + + + Inaudible streams + + + + &Select + + + + Select inaudible streams + + + + &Deselect + + + + Deselect inaudible streams + + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + + + + R&efresh streams + + + + Read captured packets from capture in progress to player + + + + Zoom In + Наблизити + + + SR (Hz) + + + + Sample rate of codec + + + + PR (Hz) + + + + Play rate of decoded audio (depends e. g. on selected sound card) + + + + Zoom Out + + + + Move Left 10 Pixels + Переміститися Вліво На 10 Пікселів + + + Move Right 10 Pixels + Переміститися Вправо На 10 Пікселів + + + Move Left 1 Pixels + Переміститися Вліво На 10 Пікселів {1 ?} + + + Move Right 1 Pixels + Переміститися Вправо На 10 Пікселів {1 ?} + + + Go To Packet Under Cursor + Перейти До Пакету Під Курсором + + + Go to packet currently under the cursor + Перейти до пакету, що в даний момент знаходиться під курсором + + + Play the stream + + + + To Left + + + + Left + Right + + + + To Right + + + + Invert Muting + + + + No devices available + + + + Select + + + + Audio Routing + + + + &Play Streams + + + + Open RTP player dialog + + + + &Set playlist + + + + Replace existing playlist in RTP Player with new one + + + + &Add to playlist + + + + Add new set to existing playlist in RTP Player + + + + &Remove from playlist + + + + Remove selected streams from playlist in RTP Player + + + + No Audio + + + + Decoding streams... + + + + Out of Sequence + + + + Jitter Drops + + + + Wrong Timestamps + + + + Inserted Silence + + + + Double click on cell to change audio routing + + + + %1 streams + + + + , %1 selected + + + + , %1 not muted + + + + , start: %1. Double click on graph to set start of playback. + + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + + + + Playback of stream %1 failed! + + + + Automatic + + + + WAV (*.wav) + + + + Sun Audio (*.au) + + + + Save audio + + + + Raw (*.raw) + + + + Save payload + + + + Warning + Попередження + + + No stream selected or none of selected streams provide audio + + + + Error + Помилка + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + + + + No streams are suitable for save + + + + Save failed! + + + + Can't write header of AU file + + + + Can't write header of WAV file + + + + Payload save works with just one audio stream. + + + + Double click to change audio routing + + + + Preparing to play... + + + + Unknown + Невідомо + + + + RtpStreamDialog + + Dialog + Діалог + + + Source Address + Адреса Джерела + + + Source Port + Порт Джерела + + + Destination Address + Адреса Призначення + + + Destination Port + Порт Призначення + + + SSRC + SSRC + + + Start Time + + + + Duration + + + + Payload + + + + Packets + Пакетів + + + Lost + + + + Max Delta (ms) + + + + Max Jitter + + + + Mean Jitter + + + + Status + Статус + + + <small><i>A hint.</i></small> + <small><i>Підказка</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + + Limit to display filter + + + + Time of Day + + + + Find &Reverse + + + + Prepare &Filter + + + + &Export + + + + &Analyze + &Аналіз + + + Open the analysis window for the selected stream(s) and add it to it + + + + Find the reverse stream matching the selected forward stream. + + + + Min Delta (ms) + + + + Mean Delta (ms) + + + + Min Jitter + + + + All forward/reverse stream actions + + + + R + + + + Find All &Pairs + + + + Select all streams which are paired in forward/reverse relation + + + + Shift+R + + + + Find Only &Singles + + + + Find all streams which don't have paired reverse stream + + + + Ctrl+R + + + + Mark Packets + + + + Mark the packets of the selected stream(s). + + + + M + + + + All + + + + Select all + + + + None + Відсутня + + + Clear selection + + + + Invert + Обернути + + + Invert selection + + + + Go To Setup + + + + Go to the setup packet for this stream. + + + + G + G + + + Prepare a filter matching the selected stream(s). + + + + P + + + + Export the stream payload as rtpdump + + + + E + + + + A + + + + Cop&y + + + + Open copy menu + + + + Copy as CSV + Скопіювати як CSV + + + Copy stream list as CSV. + + + + Copy as YAML + Скопіювати як YAML + + + Copy stream list as YAML. + + + + RTP Streams + + + + Select + + + + as CSV + як CSV + + + as YAML + як YAML + + + %1 streams + + + + , %1 selected, %2 total packets + + + + Save RTPDump As… + + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + + + + ID + + + + Port 1 + + + + Port 2 + + + + Number of Packets + + + + Number of DATA Chunks + + + + Number of Bytes + + + + Filter Selected Association + + + + Analyze + + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + + + + TabWidget + + + + Statistics + Статистика + + + Chunk Statistics + + + + Filter Association + + + + Number of Data Chunks from EP2 to EP1: + + + + Checksum Type: + + + + Number of Data Chunks from EP1 to EP2: + + + + Number of Data Bytes from EP1 to EP2: + + + + Number of Data Bytes from EP2 to EP1: + + + + Endpoint 1 + Кінцева точка 1 + + + Graph TSN + + + + Graph Bytes + + + + Requested Number of Inbound Streams: + + + + Port: + Порт: + + + Sent Verification Tag: + Надісланий верифікаційний тег: + + + Minimum Number of Inbound Streams: + + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + + + + Minimum Number of Outbound Streams: + + + + Graph Arwnd + + + + Endpoint 2 + Кінцева точка 2 + + + Complete List of IP addresses from INIT_ACK Chunk: + + + + Provided Number of Outbound Streams: + + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + + + + No Association found for this packet. + + + + Warning + Попередження + + + Could not find SCTP Association with id: %1 + + + + Complete list of IP addresses from INIT Chunk: + + + + Complete list of IP addresses from INIT_ACK Chunk: + + + + List of Used IP Addresses + + + + Used Number of Inbound Streams: + + + + Used Number of Outbound Streams: + + + + + SCTPChunkStatisticsDialog + + Dialog + Діалог + + + Association + + + + Endpoint 1 + Кінцева точка 1 + + + Endpoint 2 + Кінцева точка 2 + + + Save Chunk Type Order + + + + Hide Chunk Type + + + + Remove the chunk type from the table + + + + Chunk Type Preferences + + + + Go to the chunk type preferences dialog to show or hide other chunk types + + + + Show All Registered Chunk Types + + + + Show all chunk types with defined names + + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + + + + + SCTPGraphArwndDialog + + SCTP Graph + + + + Reset to full size + Відновити до повного розміру + + + Save Graph + Зберегти графік + + + goToPacket + перейтиДоПакету + + + Go to Packet + Перейти до Пакету + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + Arwnd + + + + time [secs] + час [сек.] + + + Advertised Receiver Window [Bytes] + + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + + + + + SCTPGraphByteDialog + + SCTP Graph + + + + Reset to full size + Відновити до повного розміру + + + Save Graph + Зберегти графік + + + goToPacket + перейтиДоПакету + + + Go to Packet + Перейти до Пакету + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + Bytes + Байти + + + time [secs] + час [сек.] + + + Received Bytes + Отримано байтів + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + + + + + SCTPGraphDialog + + SCTP Graph + + + + Relative TSNs + + + + Only SACKs + + + + Only TSNs + + + + Show both + + + + Reset to full size + Відновити до повного розміру + + + Save Graph + Зберегти графік + + + goToPacket + перейтиДоПакету + + + Go to Packet + Перейти до Пакету + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + + + + No Data Chunks sent + + + + CumTSNAck + + + + Gap Ack + + + + NR Gap Ack + + + + Duplicate Ack + + + + TSN + + + + time [secs] + час [сек.] + + + TSNs + + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 Час: %3 сек. </i></small> + + + Portable Document Format (*.pdf) + Формат Переносних Документів (*.pdf) + + + Portable Network Graphics (*.png) + Переносна Мережева Графіка (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Формат Обміну Файлами JPEG (*.jpeg *.jpg) + + + Save Graph As… + + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + + + + Command: + + + + SCSI Service Response Times + + + + + SearchFrame + + Frame + Кадр + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + + + + Packet list + Список пакетів + + + Packet details + Складові пакету + + + Packet bytes + Байти пакету + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>Шукати рядки, що містять звичайні (UTF-8 та ASCII) або розширені (UTF-16) символи.</p></body></html> + + + Narrow & Wide + Звичайні & Розширені + + + Narrow (UTF-8 / ASCII) + Звичайні (UTF-8 / ASCII) + + + Wide (UTF-16) + Розширені (UTF-16) + + + Case sensitive + Враховувати регістр + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + + + + Display filter + Фільтр відображення + + + Hex value + Шістнадцяткове значення + + + String + Рядок + + + Regular Expression + + + + Find + Знайти + + + Cancel + Скасувати + + + No valid search type selected. Please report this to the development team. + Вибрано некоректний тип пошуку. Будь ласка, повідомте про це команду розробників. + + + Invalid filter. + Некоректний фільтр. + + + That filter doesn't test anything. + Цей фільтр нічого не перевіряє. + + + That's not a valid hex string. + Вказано некоректний шістнадцятковий рядок. + + + You didn't specify any text for which to search. + Ви не вказали текст для пошуку. + + + No valid character set selected. Please report this to the development team. + Вибрано некоректний набір символів. Будь ласка, повідомте про це команду розробників. + + + No valid search area selected. Please report this to the development team. + Вказано некоректну область пошуку. Будь ласка, повідомте про це команду розробників. + + + Searching for %1… + + + + No packet contained those bytes. + Жоден пакет не містить таких байтів. + + + No packet contained that string in its Info column. + Жоден пакет не містить цього рядка в колонці «Інформація». + + + No packet contained that string in its dissected display. + + + + No packet contained that string in its converted data. + + + + No packet matched that filter. + Жоден пакет не співпав з цим фільтром. + + + + SequenceDialog + + Call Flow + + + + Time + Час + + + Comment + Коментар + + + No data + + + + %Ln node(s) + + %Ln node + %Ln nodes + + + + + %Ln item(s) + + %Ln item + %Ln items + + + + + Portable Document Format (*.pdf) + Формат Переносних Документів (*.pdf) + + + Portable Network Graphics (*.png) + Переносна Мережева Графіка (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Формат Обміну Файлами JPEG (*.jpeg *.jpg) + + + ASCII (*.txt) + Текст ASCII (*.txt) + + + Save Graph As… + + + + Flow + Потік + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + + + + <small><i>A hint</i></small> + <small><i>Підказка</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + + + + Limit to display filter + + + + Flow type: + + + + Addresses: + Адреси: + + + Any + Будь-який + + + Network + Мережа + + + Reset Diagram + Скинути Зміни до Діаграми + + + Reset &Diagram + + + + Reset the diagram to its initial state. + Відновити початковий стан діаграми. + + + 0 + 0 + + + &Reset Diagram + + + + Reset the diagram to its initial state + + + + &Export + + + + Export diagram + + + + Zoom In + Наблизити + + + + + + + + + Zoom Out + + + + - + - + + + Move Up 10 Pixels + Переміститися Вгору На 10 Пікселів + + + Up + Вгору + + + Move Left 10 Pixels + Переміститися Вліво На 10 Пікселів + + + Left + Вліво + + + Move Right 10 Pixels + Переміститися Вправо На 10 Пікселів + + + Right + Вправо + + + Move Down 10 Pixels + Переміститися Вниз На 10 Пікселів + + + Down + Вниз + + + Move Up 1 Pixel + Переміститися Вгору На 1 Піксель + + + Shift+Up + Shift+Вгору + + + Move Left 1 Pixel + Переміститися Вліво На 1 Піксель + + + Shift+Left + Shift+Вліво + + + Move Right 1 Pixel + Переміститися Вправо На 1 Піксель + + + Shift+Right + Shift+Вправо + + + Move Down 1 Pixel + Переміститися Вниз На 1 Піксель + + + Shift+Down + Shift+Вниз + + + Go To Packet Under Cursor + Перейти До Пакету Під Курсором + + + Go to packet currently under the cursor + Перейти до пакету, що в даний момент знаходиться під курсором + + + G + G + + + All Flows + Усі потоки + + + Show flows for all packets + + + + 1 + 1 + + + TCP Flows + + + + Show only TCP flow information + + + + Go To Next Packet + + + + Go to the next packet + Перейти до наступного пакету + + + N + + + + Go To Previous Packet + + + + Go to the previous packet + Перейти до попереднього пакету + + + P + + + + Select RTP Stream + + + + Select RTP stream in RTP Streams dialog + + + + S + S + + + Deselect RTP Stream + + + + Deselect RTP stream in RTP Streams dialog + + + + D + D + + + + ShortcutListModel + + Shortcut + + + + Name + + + + Description + Опис + + + + ShowPacketBytesDialog + + Show Packet Bytes + + + + Hint. + Підказка. + + + Decode as + + + + Show as + + + + Start + + + + End + + + + Find: + Знайти: + + + Find &Next + Знайти &Наступний + + + Frame %1, %2, %Ln byte(s). + + + + + + + + None + Відсутня + + + Base64 + + + + Compressed + + + + Hex Digits + + + + Percent-Encoding + + + + Quoted-Printable + + + + ROT13 + + + + ASCII + ASCII + + + ASCII & Control + + + + C Array + + + + EBCDIC + EBCDIC + + + Hex Dump + Шістнадцятковий Дамп + + + HTML + + + + Image + + + + Raw + + + + Rust Array + + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + Роздрукувати + + + Copy + Скопіювати + + + Save as… + Зберегти як... + + + Save Selected Packet Bytes As… + + + + Displaying %Ln byte(s). + + + + + + + + JSON + + + + Regex Find: + + + + + ShowPacketBytesTextEdit + + Show Selected + + + + Show All + + + + + SplashOverlay + + Initializing dissectors + + + + Initializing tap listeners + + + + Initializing external capture plugins + + + + Registering dissectors + + + + Registering plugins + Registering dissector + + + + Handing off dissectors + + + + Handing off plugins + + + + Loading Lua plugins + + + + Removing Lua plugins + + + + Loading module preferences + + + + Finding local interfaces + + + + Applying changed preferences + + + + (Unknown action) + (Невідома дія) + + + + StatsTreeDialog + + Configuration not found + + + + Unable to find configuration for %1. + + + + + StripHeadersDialog + + Dialog + Діалог + + + Display filter: + Фільтр відображення: + + + + SupportedProtocolsDialog + + Dialog + Діалог + + + <html><head/><body><p>Search the list of field names.</p></body></html> + + + + Search: + Пошук: + + + <small><i>Gathering protocol information…</i></small> + + + + Supported Protocols + + + + %1 protocols, %2 fields. + + + + + SupportedProtocolsModel + + Name + + + + Filter + + + + Type + Тип + + + Description + Опис + + + + SyntaxLineEdit + + Invalid filter: %1 + + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + + + + %1 + + + + + TCPStreamDialog + + Dialog + Діалог + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + + + + <small><i>Mouse over for shortcuts</i></small> + + + + Type + Тип + + + MA Window (s) + + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + + + + Select SACKs + select SACKs + + + + Stream + Потік + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>Змінити напрямок з'єднання (для огляду протилежного потоку).</p></body></html> + + + Switch Direction + Змінити Напрямок + + + Mouse + Миша + + + Drag using the mouse button. + Перетягнути, використовуючи кнопку миші. + + + drags + перетягує + + + Select using the mouse button. + Вибрати область, використовуючи кнопку миші. + + + zooms + масштабує + + + Display Round Trip Time vs Sequence Number + + + + RTT By Sequence Number + + + + Display graph of Segment Length vs Time + + + + Segment Length + + + + Display graph of Mean Transmitted Bytes vs Time + + + + Display graph of Mean ACKed Bytes vs Time + + + + Goodput + + + + Display graph of Receive Window Size vs Time + + + + Rcv Win + + + + Display graph of Outstanding Bytes vs Time + + + + Bytes Out + + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>Відновити початковий стан графіку.</p></body></html> + + + Reset + Скинути + + + Reset Graph + Скинути Зміни до Графіку + + + Reset the graph to its initial state. + Відновити початковий стан графіку. + + + 0 + 0 + + + Zoom In + Наблизити + + + + + + + + + Zoom Out + Віддалити + + + - + - + + + Move Up 10 Pixels + Переміститися Вгору На 10 Пікселів + + + Up + Вгору + + + Move Left 10 Pixels + Переміститися Вліво На 10 Пікселів + + + Left + Вліво + + + Move Right 10 Pixels + Переміститися Вправо На 10 Пікселів + + + Right + Вправо + + + Move Down 10 Pixels + Переміститися Вниз На 10 Пікселів + + + Down + Вниз + + + Move Up 1 Pixel + Переміститися Вгору На 1 Піксель + + + Shift+Up + Shift+Вгору + + + Move Left 1 Pixel + Переміститися Вліво На 1 Піксель + + + Shift+Left + Shift+Вліво + + + Move Right 1 Pixel + Переміститися Вправо На 1 Піксель + + + Shift+Right + Shift+Вправо + + + Move Down 1 Pixel + Переміститися Вниз На 1 Піксель + + + Shift+Down + Shift+Вниз + + + Next Stream + Наступний Потік + + + Go to the next stream in the capture + Перейти до наступного захопленого потоку + + + PgUp + PgUp + + + Previous Stream + Попередній Потік + + + Go to the previous stream in the capture + Перейти до попереднього захопленого потоку + + + PgDown + PgDown + + + Switch direction (swap TCP endpoints) + Змінити напрямок (поміняти місцями кінцеві точки TCP) + + + D + D + + + Go To Packet Under Cursor + Перейти До Пакету Під Курсором + + + Go to packet currently under the cursor + Перейти до пакету, що в даний момент знаходиться під курсором + + + G + G + + + Drag / Zoom + Перетягування / Масштабування + + + Toggle mouse drag / zoom behavior + Встановити для миші поведінку перетягування / масштабування + + + Z + Z + + + Relative / Absolute Sequence Numbers + Відносні / Абсолютні Порядкові Номери + + + Toggle relative / absolute sequence numbers + Встановити відносні / абсолютні порядкові номери + + + S + S + + + Capture / Session Time Origin + Час Початку Захоплення / Сесії + + + Toggle capture / session time origin + Встановити початок часу захоплення / сесії + + + T + T + + + Crosshairs + Перехрестя + + + Toggle crosshairs + Показати / приховати перехрестя + + + Space + Прогалина + + + Round Trip Time + Час Обороту + + + Switch to the Round Trip Time graph + Переключитися на графік Часу Обороту + + + 1 + 1 + + + Throughput + Пропускна Здатність + + + Switch to the Throughput graph + Переключитися на графік Пропускної Здатності + + + 2 + 2 + + + Time / Sequence (Stevens) + Час / Послідовність (Стівенс) + + + Switch to the Stevens-style Time / Sequence graph + Переключитися на графік Часу / Послідовності (версія Стівенса) + + + 3 + 3 + + + Window Scaling + Масштабування Вікна + + + Switch to the Window Scaling graph + Переключитися на графік Масштабування Вікна + + + 5 + 5 + + + Time / Sequence (tcptrace) + Час / Послідовність (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + Переключитися на графік Часу / Послідовності (tcptrace) + + + 4 + 4 + + + Zoom In X Axis + + + + X + + + + Zoom Out X Axis + + + + Shift+X + + + + Zoom In Y Axis + + + + Y + + + + Zoom Out Y Axis + + + + Shift+Y + + + + Save As… + + + + No Capture Data + Немає завантаженого захоплення + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + + + + Sequence Numbers (Stevens) + Порядкові Номери (Стівенс) + + + Sequence Numbers (tcptrace) + Порядкові Номери (tcptrace) + + + (MA) + + + + (%1 Segment MA) + + + + [not enough data] + [недостатньо даних] + + + for %1:%2 %3 %4:%5 + + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + + Click to select packet + Клікніть, щоб вибрати пакет + + + Packet + Пакет + + + Release to zoom, x = %1 to %2, y = %3 to %4 + Відпустіть для вибору області, x = від %1 до %2, y = від %3 до %4 + + + Unable to select range. + Неможливо вибрати область. + + + Click to select a portion of the graph. + Клікніть, щоб виділити частину графіку. + + + Portable Document Format (*.pdf) + Формат Переносних Документів (*.pdf) + + + Portable Network Graphics (*.png) + Переносна Мережева Графіка (*.png) + + + Windows Bitmap (*.bmp) + Windows Bitmap (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + Формат Обміну Файлами JPEG (*.jpeg *.jpg) + + + Save Graph As… + + + + + TLSKeylogDialog + + Dialog + Діалог + + + Browse… + Перегляд... + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + Діалог + + + Item + + + + <small><i>A hint.</i></small> + <small><i>Підказка</i></small> + + + Display filter: + Фільтр відображення: + + + Regenerate statistics using this display filter + + + + Apply + Застосувати + + + Copy + Скопіювати + + + Copy a text representation of the tree to the clipboard + + + + Save as… + Save as... + Зберегти як... + + + Save the displayed data in various formats + + + + Collapse All + + + + Expand All + + + + Save Statistics As… + + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + Звичайний текстовий файл (*.txt);;Значення, розділені комою (*.csv);;Документ XML (*.xml);;Документ YAML (*.yaml) + + + Plain text file (*.txt) + Звичайний текстовий файл (*.txt) + + + Error saving file %1 + Помилка при зберіганні файлу %1 + + + + TimeShiftDialog + + Shift all packets by + Зсунути всі пакети на + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[гг:]хх:]сс[.ммм] </span></p></body></html> + + + Set the time for packet + Встановити час для пакету + + + to + в + + + …then set packet + ...then set packet + ...потім для пакету + + + and extrapolate the time for all other packets + та екстраполювати час на всі інші пакети + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[РРРР-ММ-ДД] гг:хх:сс[.ммм] </span></p></body></html> + + + Undo all shifts + Скасувати всі зсуви + + + Time Shift + Зсув Часу + + + Frame numbers must be between 1 and %1. + Номери кадрів повинні входити в діапазон від 1 до %1. + + + Invalid frame number. + Некоректний номер кадру. + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + + + + Could not open base file %1 for reading: %2 + + + + No endpoints available to map + + + + Unable to create temporary file + + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + + + + Name resolution + Визначення імен + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + + Limit to display filter + + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + + + + Filter list for specific type + + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + + + + GroupBox + + + + Absolute start time + + + + Copy + Скопіювати + + + Unknown + Невідомо + + + + TrafficTree + + Resize all columns to content + + + + Filter on stream id + + + + Copy %1 table + + + + as CSV + як CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + + + + as YAML + як YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + + + + as JSON + + + + Copy all values of this page to the clipboard in the JSON data serialization format. + + + + Save data as raw + + + + Disable data formatting for export/clipboard and save as raw data + + + + + TrafficTreeHeaderView + + Less than + + + + Greater than + + + + Equal + + + + Columns to display + + + + Filter %1 by + + + + Enter filter value + + + + + TrafficTypesModel + + Protocol + Протокол + + + + UatDialog + + Create a new entry. + + + + Remove this entry. + Remove this profile. + + + + Copy this entry. + Copy this profile. + + + + Move entry up. + + + + Move entry down. + + + + Clear all entries. + + + + Unknown User Accessible Table + + + + Open + Відкрити + + + + UatFrame + + Frame + Кадр + + + Create a new entry. + + + + Remove this entry. + + + + Copy this entry. + + + + Move entry up. + + + + Move entry down. + + + + Clear all entries. + + + + Copy entries from another profile. + + + + Copy from + + + + Unknown User Accessible Table + + + + Open + Відкрити + + + + VoipCallsDialog + + <small></small> + + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + + + + Limit to display filter + + + + Time of Day + + + + Flow &Sequence + + + + Show flow sequence for selected call(s). + + + + Prepare &Filter + + + + Prepare a filter matching the selected calls(s). + + + + Cop&y + + + + Open copy menu + + + + All + + + + Select all + + + + None + Відсутня + + + Invert + Обернути + + + Invert selection + + + + Select related RTP streams + + + + Select RTP streams related to selected calls in RTP Streams dialog + + + + S + S + + + Deselect related RTP Streams + + + + D + D + + + Clear selection + + + + Display time as time of day + + + + Copy as CSV + Скопіювати як CSV + + + Copy stream list as CSV. + + + + Copy as YAML + Скопіювати як YAML + + + Copy stream list as YAML. + + + + SIP Flows + + + + VoIP Calls + + + + as CSV + як CSV + + + as YAML + як YAML + + + Select + + + + + VoipCallsInfoModel + + On + + + + Off + + + + Tunneling: %1 Fast Start: %2 + + + + Start Time + + + + Stop Time + + + + Initial Speaker + + + + From + + + + To + + + + Protocol + Протокол + + + Duration + + + + Packets + Пакетів + + + State + + + + Comments + + + + + WelcomePage + + Form + + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + + + + <html><head/><body><p>Open a file on your file system</p></body></html> + + + + <h2>Open</h2> + + + + Recent capture files + + + + Capture files that have been opened previously + + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + + + + <h2>Capture</h2> + + + + …using this filter: + + + + Interface list + + + + List of available capture interfaces + + + + <h2>Learn</h2> + + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + + + + Show in Finder + Показати у Finder'і + + + Show in Folder + Показати у Папці + + + Welcome to %1 + + + + All interfaces shown + + + + %n interface(s) shown, %1 hidden + + %n interface shown, %1 hidden + %n interfaces shown, %1 hidden + + + + + You are sniffing the glue that holds the Internet together using Wireshark + + + + You are running Wireshark + + + + You receive automatic updates. + + + + You have disabled automatic updates. + + + + not found + + + + Copy file path + + + + Remove from list + + + + + WirelessFrame + + Frame + Кадр + + + Interface + Інтерфейс + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + + + + Channel + + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + + + + FCS Filter + + + + All Frames + + + + Valid Frames + + + + Invalid Frames + + + + Wireless controls are not supported in this version of Wireshark. + + + + External Helper + + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + + + + 802.11 Preferences + + + + AirPcap Control Panel + + + + Open the AirPcap Control Panel + + + + Unable to set channel or offset. + + + + Unable to set FCS validation behavior. + + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + + + + + WiresharkDialog + + Failed to attach to tap "%1" + + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + Перейти до пакету + + + Cancel + Скасувати + + + File Set + Набір Файлів + + + Export Packet Dissections + Експортувати Розібрані Пакети + + + Export Objects + Експортувати Об'єкти + + + &Zoom + &Масштабування + + + &Time Display Format + &Формат Відображення Часу + + + Copy + Скопіювати + + + Manual pages + Сторінки довідника + + + Apply as Filter + Застосувати як Фільтр + + + Prepare as Filter + + + + SCTP + SCTP + + + TCP Stream Graphs + Графіки Потоків TCP + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + &Файл + + + &Capture + &Захоплення + + + &Help + &Довідка + + + &Go + &Перехід + + + &View + &Вигляд + + + &Analyze + &Аналіз + + + Follow + Простежити + + + &Statistics + &Статистика + + + 29West + 29West + + + Topics + + + + Queues + + + + UIM + + + + Telephon&y + Телефон&ія + + + RTSP + RTSP + + + &Edit + &Правка + + + Packet Comments + + + + Main Toolbar + Головна Панель + + + Display Filter Toolbar + Показувати Панель Фільтрації + + + Open a capture file + Відкрити файл захоплення + + + Quit Wireshark + Вийти з Wireshark + + + &Start + &Почати + + + Start capturing packets + Почати захоплення пакетів + + + S&top + З&упинити + + + Stop capturing packets + Зупинити захоплення пакетів + + + No files found + Не знайдено жодного файлу + + + &Contents + &Зміст + + + Wireshark Filter + Фільтр Wireshark’а + + + TShark + TShark + + + Rawshark + + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + + + + Website + Веб-сайт + + + Downloads + Завантаження + + + Wiki + Вікі + + + Sample Captures + Зразки Захоплень + + + &About Wireshark + &Про Wireshark + + + Ask (Q&&A) + Запитати (Q&&A) + + + Next Packet + Наступний Пакет + + + Go to the next packet + Перейти до наступного пакету + + + Previous Packet + Попередній Пакет + + + Go to the previous packet + Перейти до попереднього пакету + + + First Packet + Перший Пакет + + + Go to the first packet + Перейти до першого пакету + + + Last Packet + Останній Пакет + + + Go to the last packet + Перейти до останнього пакету + + + E&xpand Subtrees + Р&озгорнути Піддерева + + + Expand the current packet detail + Розкрити властивості вибраного пакету + + + &Expand All + &Розгорнути Все + + + Expand packet details + Розкрити складові пакету + + + Collapse &All + Згорнути &Все + + + Collapse all packet details + Згорнути всі складові пакету + + + Go to specified packet + Перейти до вказаного пакету + + + Merge one or more files + Об'єднати один або більше файлів + + + Import a file + Імпортувати з файлу + + + &Save + &Зберегти + + + Save as a different file + Зберегти в іншому файлі + + + Export specified packets + Експортувати вказані пакети + + + Export TLS Session Keys… + Експортувати Сеансові Ключі TLS... + + + List Files + Список Файлів + + + Next File + Наступний Файл + + + Previous File + Попередній Файл + + + &Reload + &Перезавантажити + + + Options + Опції + + + Capture options + Опції захоплення + + + Capture filters + Фільтри захоплення + + + Refresh Interfaces + Оновити Інтерфейси + + + Refresh interfaces + Оновити інтерфейси + + + &Restart + &Перезапустити + + + Restart current capture + Перезапустити сеанс захоплення + + + As &CSV… + Як &CSV... + + + As "C" &Arrays… + Як М&асиви "C"... + + + As P&SML XML… + Як P&SML XML... + + + As P&DML XML… + Як P&DML XML... + + + As &JSON… + Як &JSON... + + + Description + Опис + + + Field Name + Назва Поля + + + Value + Значення + + + As Filter + Як Фільтр + + + Close this capture file + Закрити цей файл захоплення + + + Packet: + Пакет: + + + Interface Toolbars + + + + Colorize Conversation + Розфарбувати Взаємодії + + + Internals + + + + Additional Toolbars + Додаткові Панелі + + + Conversation Filter + Фільтр Взаємодій + + + Reliable Server Pooling (RSerPool) + + + + SOME/IP + + + + &DTN + + + + Osmux + + + + &Tools + Tools + &Інструменти + + + Wireless Toolbar + Панель Бездротової Мережі + + + Help contents + + + + FAQs + + + + Next Packet in Conversation + + + + Go to the next packet in this conversation + + + + Previous Packet in Conversation + + + + Go to the previous packet in this conversation + + + + Next Packet In History + + + + Go to the next packet in your selection history + + + + Previous Packet In History + + + + Go to the previous packet in your selection history + + + + Collapse Subtrees + + + + Collapse the current packet detail + + + + Go to Packet… + Перейти до Пакету... + + + &Merge… + &Об'єднати... + + + &Import from Hex Dump… + &Імпортувати з Шістнадцяткового Дампу... + + + Save this capture file + Зберегти цей файл захоплення + + + Save &As… + Зберегти &Як... + + + Export Specified Packets… + Експортувати Вказані Пакети... + + + Export Packet &Bytes… + Експортувати &Байти з Пакету... + + + &Print… + &Роздрукувати... + + + Reload this file + Перезавантажити цей файл + + + Reload as File Format/Capture + + + + Copy this item's description + + + + Copy this item's field name + + + + Copy this item's value + + + + Copy this item as a display filter + + + + Apply as Column + Зробити Колонкою + + + Create a packet list column from the selected field. + Створити колонку для пакетів з вибраним полем. + + + Find a packet + Знайти пакет + + + Find the next packet + Знайти наступний пакет + + + Find the previous packet + Знайти попередній пакет + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + + + + Mark All Displayed + Позначити Всі Відображені + + + Mark all displayed packets + Позначити всі відображені пакети + + + Unmark all displayed packets + Зняти позначки з усіх відображених пакетів + + + Next Mark + Наступна Позначка + + + Go to the next marked packet + Перейти до наступного позначеного пакету + + + Previous Mark + Попередня Позначка + + + Go to the previous marked packet + Перейти до попереднього позначеного пакету + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + + + + Ignore All Displayed + Прибрати Всі Відображені + + + Ignore all displayed packets + Прибрати всі відображені пакети + + + Set/Unset Time Reference + Встановити/Зняти Точку Відліку Часу + + + Set or unset a time reference for this packet + Встановити або зняти точку відліку часу для цього пакету + + + Unset All Time References + Зняти Всі Точки Відліку Часу + + + Remove all time references + Прибрати всі точки відліку часу + + + Next Time Reference + Наступна Точку Відліку Часу + + + Go to the next time reference + Перейти до наступної точки відліку часу + + + Previous Time Reference + Попередня Точку Відліку Часу + + + Go to the previous time reference + Перейти до попередньої точки відліку часу + + + Shift or change packet timestamps + Зсунути або змінити мітку часу для пакету + + + Delete All Packet Comments + + + + Remove all packet comments in the capture file + + + + &Configuration Profiles… + + + + Configuration profiles + Профілі конфігурації + + + Manage your configuration profiles + Налаштуйте ваші профілі конфігурації + + + Manage Wireshark's preferences + Керування налаштуваннями Wireshark'а + + + Capture File Properties + Властивості Файлу Захоплення + + + Capture file properties + Властивості файлу захоплення + + + &Protocol Hierarchy + &Ієрархія протоколів + + + Show a summary of protocols present in the capture file. + Показати перелік протоколів, що наявні в файлі захоплення + + + Capinfos + + + + Reordercap + + + + Time Sequence (Stevens) + + + + TCP time sequence graph (Stevens) + + + + Throughput + Пропускна Здатність + + + Round Trip Time + Час Обороту + + + TCP round trip time + Час обороту TCP + + + Window Scaling + Масштабування Вікна + + + TCP window scaling + Масштабування TCP-вікна + + + HTTP/2 Stream + + + + SIP Call + + + + Time Sequence (tcptrace) + + + + TCP time sequence graph (tcptrace) + + + + Analyse this Association + + + + Show All Associations + + + + Flow Graph + + + + Flow sequence diagram + + + + ANCP + ANCP + + + ANCP statistics + Статистика ANCP + + + Packets sorted by Instance ID + + + + BACapp statistics sorted by instance ID + + + + Packets sorted by IP + + + + BACapp statistics sorted by IP + + + + Packets sorted by object type + + + + BACapp statistics sorted by object type + + + + Packets sorted by service + + + + BACapp statistics sorted by service + + + + Collectd + Collectd + + + Collectd statistics + Статистика collectd + + + DNS + DNS + + + DNS statistics + Статистика DNS + + + HART-IP + HART-IP + + + HART-IP statistics + Статистика HART-IP + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + Статистика hpfeeds + + + HTTP2 + HTTP2 + + + HTTP2 statistics + Статистика HTTP2 + + + Packet Counter + Лічильник Пакетів + + + HTTP packet counter + Лічильник пакетів HTTP + + + Requests + Запити + + + HTTP requests + HTTP-запити + + + Load Distribution + + + + HTTP load distribution + + + + Packet Lengths + + + + Packet length statistics + + + + Sametime + + + + Sametime statistics + + + + SOME/IP Messages + + + + SOME/IP Message statistics + + + + SOME/IP-SD Entries + + + + SOME/IP-SD Entries statistics + + + + &LTP + + + + LTP segment and block statistics + + + + &ISUP Messages + Повідомлення &ISUP + + + ISUP message statistics + Статистика повідомлень ISUP + + + Osmux packet counts + + + + RTSP packet counts + + + + SM&PP Operations + + + + SMPP operation statistics + + + + &UCP Messages + + + + UCP message statistics + + + + F1AP + + + + F1AP Messages + + + + NGAP + + + + NGAP Messages + + + + Change the way packets are dissected + + + + Reload Lua Plugins + + + + Reload Lua plugins + + + + Advertisements by Topic + + + + Advertisements by Source + + + + Advertisements by Transport + + + + Queries by Topic + + + + Queries by Receiver + + + + Wildcard Queries by Pattern + + + + Wildcard Queries by Receiver + + + + Advertisements by Queue + + + + Queries by Queue + + + + Streams + + + + LBT-RM + + + + LBT-RU + + + + Filter this Association + + + + Strip Headers… + + + + Strip headers and export higher level encapsulations to file + + + + &I/O Graphs + + + + &Conversations + + + + &Endpoints + + + + Shrink the main window text + + + + Return the main window text to its normal size + + + + Reset Layout + + + + Reset appearance layout to default size + + + + Seconds Since First Captured Packet + + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + + + + Show or hide the packet diagram + + + + Show each conversation hash table + + + + Show each dissector table and its entries + + + + Show the currently supported protocols and display filter fields + + + + MAC Statistics + + + + LTE MAC statistics + + + + RLC Statistics + + + + LTE RLC statistics + + + + LTE RLC graph + + + + MTP3 Summary + + + + MTP3 summary statistics + + + + Bluetooth Devices + Пристрої Bluetooth + + + Bluetooth HCI Summary + Зведена інформація по Bluetooth HCI + + + Display Filter &Expression… + + + + Display Filter Expression… + + + + REGISTER_STAT_GROUP_RSERPOOL + + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + + + + No GSM statistics registered + + + + No LTE statistics registered + + + + No MTP3 statistics registered + + + + IAX2 Stream Analysis + + + + Show Packet Bytes… + + + + Go to &Linked Packet + + + + UDP Multicast Streams + + + + Show UTP multicast stream statistics. + + + + WLAN Traffic + + + + Show IEEE 802.11 wireless LAN statistics. + + + + Add a display filter button. + + + + Firewall ACL Rules + + + + Create firewall ACL rules + + + + &Full Screen + + + + Credentials + + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + &Опції... + + + &Wireless + + + + Capture &Filters… + Фільтри &Захоплення... + + + As Plain &Text… + Як Звичайний &Текст... + + + As Plain &Text + + + + As &CSV + + + + As &YAML + + + + All Visible Items + + + + All Visible Selected Tree Items + + + + Display Filter &Macros… + &Макроси Фільтру Відображення... + + + &Find Packet… + &Знайти Пакет... + + + Find Ne&xt + Знайти На&ступний + + + Find Pre&vious + Знайти Поп&ередній + + + Mark or unmark each selected packet + + + + Ignore or unignore each selected packet + + + + U&nignore All Displayed + + + + Unignore all displayed packets + + + + Time Shift… + Зсув Часу... + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + &Налаштування... + + + TCP throughput + + + + Request Sequences + + + + HTTP Request Sequences + + + + Decode &As… + Декодувати &Як... + + + Export PDUs to File… + + + + Create graphs based on display filter fields + + + + &Main Toolbar + &Головна панель + + + Show or hide the main toolbar + Показати чи приховати головну панель + + + &Filter Toolbar + Панель &Фільтрації + + + Show or hide the display filter toolbar + Показати чи приховати панель фільтру відображення + + + Conversations at different protocol levels + Взаємодії по протоколам різних рівнів + + + Endpoints at different protocol levels + Кінцеві точки в протоколах різних рівнів + + + Colorize Packet List + Розфарбувати Список Пакетів + + + Draw packets using your coloring rules + Розмалювати пакети згідно зі встановленими правилами + + + &Zoom In + &Наблизити + + + Enlarge the main window text + Збільшити текст в головному вікні + + + Zoom Out + &Віддалити + + + Normal Size + Нормальний Розмір + + + Resize Columns + Змінити Розмір Колонок + + + Resize packet list columns to fit contents + Підлаштувати розмір колонок списку пакетів під їхній зміст + + + Date and Time of Day (1970-01-01 01:02:03.123456) + Дата та Час Доби (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + Представити час пакетів у вигляді дати та часу доби. + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Рік, День Року, та Час Доби (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + Представити час пакетів у вигляді року, дня року та часу доби. + + + Time of Day (01:02:03.123456) + Час Доби (01:02:03.123456) + + + Seconds Since 1970-01-01 + Секунди Від 1970-01-01 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + Представити час пакетів у вигляді секунд, що минули з початку епохи UNIX / POSIX (1970-01-01). + + + Seconds Since Previous Captured Packet + Секунди Від Попереднього Захопленого Пакету + + + Show packet times as the seconds since the previous captured packet. + Представити час пакетів у вигляді секунд, що минули з моменту захоплення попереднього пакету. + + + Seconds Since Previous Displayed Packet + Секунди Від Попереднього Відображеного Пакету + + + Show packet times as the seconds since the previous displayed packet. + Представити час пакетів у вигляді секунд, що минули з моменту відображення попереднього пакету . + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + Дата та Час Доби по ВКЧ (UTC) (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + Представити час пакетів у вигляді дати та часу доби по ВКЧ (UTC) + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + Рік, День Року, та Час Доби по ВКЧ (UTC) (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + Представити час пакетів у вигляді року, дня року та часу доби по ВКЧ (UTC) + + + UTC Time of Day (01:02:03.123456) + Час Доби по ВКЧ (UTC) (01:02:03.123456) + + + Show packet times as the UTC time of day. + Представити час пакетів у вигляді час доби по ВКЧ (UTC). + + + Automatic (from capture file) + Автоматична (з файлу захоплення) + + + Use the time precision indicated in the capture file. + Використовувати вказану у файлі захоплення точність часу. + + + Seconds + Секунди + + + Tenths of a second + Десяті частки секунди + + + Hundredths of a second + Соті частки секунди + + + Milliseconds + Мілісекунди + + + Microseconds + Мікросекунди + + + Nanoseconds + Наносекунди + + + Display Seconds With Hours and Minutes + Відображати Секунди З Годинами та Хвилинами + + + Display seconds with hours and minutes + Відображати секунди з годинами та хвилинами + + + Resolve &Physical Addresses + Визначати &Фізичні Адреси + + + Show names for known MAC addresses. Lookups use a local database. + Показувати імена для відомих MAC-адрес. Для пошуку використовуватиметься локальна база даних + + + Resolve &Network Addresses + Визначати &Мережеві Адреси + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + Показувати імена для відомих адрес IPv4, IPv6 та IPX. Пошук може згенерувати мережевий трафік. + + + Resolve &Transport Addresses + Визначати &Транспортні Адреси + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + Показувати імена для відомих сервісів TCP, UDP та SCTP. Пошук може згенерувати трафік на деяких системах. + + + Wire&less Toolbar + Панель Без&дротової Мережі + + + Show or hide the wireless toolbar + Показати чи приховати панель бездротової мережі + + + &Status Bar + &Рядок стану + + + Show or hide the status bar + Показати чи приховати рядок стану + + + Packet &List + &Список Пакетів + + + Show or hide the packet list + Показати чи приховати список пакетів + + + Packet &Details + &Складові Пакету + + + Show or hide the packet details + Показати чи приховати складові пакету + + + Packet &Bytes + &Байти Пакету + + + Show or hide the packet bytes + Показати чи приховати байти пакету + + + &Conversation Hash Tables + + + + &Dissector Tables + + + + &Supported Protocols + + + + MAP Summary + Зведені дані MAP + + + GSM MAP summary statistics + Загальна статистика GSM MAP + + + RLC &Graph + + + + &Coloring Rules… + &Правила Розфарбовування... + + + Show Linked Packet in New Window + + + + New Coloring Rule… + New Conversation Rule… + + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + + + + RTP Player + + + + Play selected stream. Press CTRL key for playing reverse stream too. + + + + IA&X2 Stream Analysis + + + + Enabled Protocols… + Enable Protocols… + + + + Wiki Protocol Page + Вікі-Сторінка Протоколів + + + Open the Wireshark wiki page for this protocol. + + + + Filter Field Reference + + + + Open the display filter reference page for this filter field. + + + + Go to the packet referenced by the selected field. + + + + &VoIP Calls + &Виклики VoIP + + + Open &Recent + + + + Name Resol&ution + + + + Service &Response Time + + + + &RTP + + + + S&CTP + + + + &ANSI + + + + &GSM + + + + &LTE + + + + &MTP3 + + + + &Open + + + + &Quit + + + + &Close + + + + Display &Filters… + + + + &Unmark All Displayed + + + + All VoIP Calls + Всі Виклики VoIP + + + SIP &Flows + + + + SIP Flows + + + + RTP Streams + + + + Edit the packet list coloring rules. + + + + Bluetooth ATT Server Attributes + ATT Server Attributes + Атрибути Серверу (Bluetooth ATT) + + + Show Packet in New &Window + + + + Show this packet in a separate window. + + + + Show the linked packet in a separate window. + + + + Auto Scroll in Li&ve Capture + Автопрокрутка Під &Час Захоплення + + + Automatically scroll to the last packet during a live capture. + Автоматично прокручувати до останнього пакету під час захоплення + + + Expert Information + Експертна Інформація + + + Show expert notifications + + + + Add an expression to the display filter. + + + + REGISTER_STAT_GROUP_UNSORTED + + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + + + + No ANSI statistics registered + No tools registered + + + + Resolved Addresses + Визначені Адреси + + + Show each table of resolved addresses as copyable text. + + + + Color &1 + Колір &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + + + + Color &2 + Колір &2 + + + Color &3 + Колір &3 + + + Color &4 + Колір &4 + + + Color &5 + Колір &5 + + + Color &6 + Колір &6 + + + Color &7 + Колір &7 + + + Color &8 + Колір &8 + + + Color &9 + Колір &9 + + + Color 1&0 + Колір 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + + + + Reset Colorization + + + + Reset colorized conversations. + + + + RTP Stream Analysis + + + + Edit Resolved Name + + + + Manually edit a name resolution entry. + + + + Enable and disable specific protocols + + + + before quitting + + + + Save packets before merging? + Зберегти пакети перед об'єднанням? + + + A temporary capture file can't be merged. + Тимчасовий файл захоплення не може бути використаний для об'єднання. + + + Save changes in "%1" before merging? + Зберегти зміни до «%1» перед об'єднанням? + + + Changes must be saved before the files can be merged. + Перед об'єднанням файлів зміни необхідно зберегти. + + + Invalid Display Filter + Некоректний Фільтр Відображення + + + Invalid Read Filter + + + + The filter expression %1 isn't a valid read filter. (%2). + + + + before importing a capture + before importing a new capture + + + + Unable to export to "%1". + + + + You cannot export packets to the current capture file. + Ви не можете експортувати пакети до поточного файлу захоплення. + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + + + + Your captured packets will be lost if you don't save them. + Ваші захоплені пакети буде втрачено, якщо ви їх не збережете. + + + Do you want to save the changes you've made to the capture file "%1"%2? + Чи ви хочете зберегти внесені вами зміни до файлу захоплення "%1"%2? + + + Your changes will be lost if you don't save them. + Ваші зміни буде втрачено, якщо ви їх не збережете + + + Check for Updates… + + + + Unable to drop files during capture. + + + + Unknown file type returned by merge dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + Unknown file type returned by export dialog. + + + + Do you want to stop the capture and save the captured packets%1? + + + + Do you want to save the captured packets%1? + + + + Save before Continue + + + + Stop and Save + Зупинити та Зберегти + + + Stop and Quit &without Saving + Stop and Quit without Saving + + + + Quit &without Saving + Quit without Saving + + + + There is no "rtp.ssrc" field in this version of Wireshark. + + + + Please select an RTPv2 packet with an SSRC value + + + + SSRC value not found. + + + + Show or hide the toolbar + + + + Continue &without Saving + Continue without Saving + + + + Stop and Continue &without Saving + Stop and Continue without Saving + + + + The Wireshark Network Analyzer + + + + Capturing from %1 + + + + before opening another file + + + + Merging files. + + + + %1: %2 + %1: %2 + + + Clear Menu + Очистити Меню + + + before closing the file + + + + Export Selected Packet Bytes + Експортувати Вибрані Байти з Пакету + + + No Keys + Ключі Відсутні + + + Raw data (*.bin *.dat *.raw);;All Files ( + + + + Couldn't copy text. Try another item. + + + + Are you sure you want to remove all packet comments? + + + + Unable to build conversation filter. + + + + before reloading the file + + + + Error compiling filter for this conversation. + + + + No previous/next packet in conversation. + + + + No interface selected. + + + + Saving %1… + + + + Configure all extcaps before start of capture. + + + + Invalid capture filter. + + + + (empty comment) + placeholder for empty comment + + + + Add New Comment… + + + + Edit "%1" + edit packet comment + + + + Delete "%1" + delete packet comment + + + + Delete packet comments + + + + Delete comments from %n packet(s) + + + + + + + + before starting a new capture + + + + before reloading Lua plugins + + + + Please wait while Wireshark is initializing… + + + + before updating + + + + There are no TLS Session Keys to save. + + + + Export TLS Session Keys (%Ln key(s)) + + + + + + + + TLS Session Keys (*.keys *.txt);;All Files ( + + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + + + + column + + + + item + + + + The "%1" column already exists. + + + + The "%1" column already exists as "%2". + + + + RTP packet search failed + + + + No Interface Selected. + + + + before restarting the capture + + + + Wiki Page for %1 + + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + + + + Loading + + + + Reloading + + + + Rescanning + + + + + WlanStatisticsDialog + + Wireless LAN Statistics + + + + Channel + + + + SSID + + + + Percent Packets + + + + Percent Retry + + + + Probe Reqs + + + + Probe Resp + + + + Auths + + + + Retry + + + + Deauths + + + + Other + + + + diff --git a/ui/qt/wireshark_zh_CN.ts b/ui/qt/wireshark_zh_CN.ts new file mode 100644 index 00000000..cd0cdd37 --- /dev/null +++ b/ui/qt/wireshark_zh_CN.ts @@ -0,0 +1,14707 @@ + + + + + Abbreviation + + + for "not applicable" + + + + + AboutDialog + + About Wireshark + 关于 Wireshark + + + Wireshark + Wireshark + + + <span size=\"x-large\" weight=\"bold\">Network Protocol Analyzer</span> + <span size=\"x-large\" weight=\"bold\">网络协议分析器</span> + + + Copy the version information to the clipboard + 复制版本信息到剪贴板 + + + Copy to Clipboard + + + + Authors + 作者 + + + Search Authors + 搜索作者 + + + Folders + 文件夹 + + + Filter by path + 按路径过滤 + + + Plugins + 插件 + + + No plugins found. + 未找到插件。 + + + Search Plugins + 搜索插件 + + + Filter by type: + 按类型过滤: + + + Keyboard Shortcuts + 快捷键 + + + Search Shortcuts + 搜索快捷方式 + + + Acknowledgments + 致谢 + + + License + 许可 + + + The directory does not exist + 该目录不存在 + + + Should the directory %1 be created? + 是否应创建目录 %1? + + + The directory could not be created + 无法创建目录 + + + The directory %1 could not be created. + 无法创建目录 %1! + + + Show in Finder + 在 Finder 中显示 + + + Show in Folder + 在文件夹中显示 + + + Copy + 复制 + + + Copy Row(s) + + 复制行 + + + + + AddressEditorFrame + + Frame + + + + Name Resolution Preferences… + Name Resolution Preferences... + 名称解析首选项… + + + Address: + 地址: + + + Name: + 名称: + + + Can't assign %1 to %2. + 无法将 %1 赋值给 %2。 + + + + AdvancedPrefsModel + + Name + 名称 + + + Status + 状态 + + + Type + 类型 + + + Value + + + + + ApplyLineEdit + + Apply changes + 应用更改 + + + + AuthorListModel + + Name + 名称 + + + Email + 电子邮件 + + + + BluetoothAttServerAttributesDialog + + Bluetooth ATT Server Attributes + 蓝牙ATT服务器属性 + + + Handle + 句柄 + + + UUID + UUID + + + UUID Name + UUID 名称 + + + All Interfaces + 所有接口 + + + All Devices + 所有设备 + + + Remove duplicates + 去除重复 + + + Copy Cell + 复制单元 + + + Copy Rows + 复制行 + + + Copy All + 复制所有 + + + Save as image + 另存为图像 + + + Mark/Unmark Row + 标记/取消标记行 + + + Ctrl-M + Ctrl+M + + + Mark/Unmark Cell + 标记/取消标记单元格 + + + Save Table Image + 保存表格图像 + + + PNG Image (*.png) + PNG 图像 (*.png) + + + + BluetoothDeviceDialog + + Bluetooth Device + 蓝牙设备 + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + 名称 + + + Class of Device + 设备类别 + + + LMP Version + LMP 版本 + + + LMP Subversion + LMP 子版本 + + + Manufacturer + 制造商 + + + HCI Version + HCI 版本 + + + HCI Revision + HCI 修订 + + + Scan + 扫描 + + + Authentication + 认证 + + + Encryption + 加密 + + + ACL MTU + ACL MTU + + + ACL Total Packets + ACL 总分组 + + + SCO MTU + SCO MTU + + + SCO Total Packets + SCO 总分组 + + + LE ACL MTU + LE ACL MTU + + + LE ACL Total Packets + LE ACL 总分组 + + + LE ISO MTU + + + + LE ISO Total Packets + + + + Inquiry Mode + 调查模式 + + + Page Timeout + 页超时 + + + Simple Pairing Mode + 简单匹配模式 + + + Voice Setting + 语音设置 + + + Value + + + + Changes + 个更改 + + + %1 changes + %1 个更改 + + + Copy Cell + 复制单元 + + + Copy Rows + 复制行 + + + Copy All + 复制所有 + + + Save as image + 另存为图像 + + + Mark/Unmark Row + 标记/取消标记行 + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + 标记/取消标记单元格 + + + Unknown + 未知 + + + Bluetooth Device - %1%2 + 蓝牙设备 - %1%2 + + + enabled + 已启用 + + + disabled + 已禁用 + + + %1 ms (%2 slots) + %1 ms (%2 槽) + + + Save Table Image + 保存表格图像 + + + PNG Image (*.png) + PNG 图像 (*.png) + + + + BluetoothDevicesDialog + + Bluetooth Devices + 蓝牙设备 + + + BD_ADDR + BD_ADDR + + + OUI + OUI + + + Name + 名称 + + + LMP Version + LMP 版本 + + + LMP Subversion + LMP 子版本 + + + Manufacturer + 制造商 + + + HCI Version + HCI 版本 + + + HCI Revision + HCI 修订 + + + Is Local Adapter + 是本地适配器 + + + All Interfaces + 所有接口 + + + Show information steps + 显示信息的步骤 + + + %1 items; Right click for more option; Double click for device details + %1 项;右击查看更多选项;双击了解设备详情 + + + Copy Cell + 复制单元 + + + Copy Rows + 复制行 + + + Copy All + 复制所有 + + + Save as image + 另存为图像 + + + Mark/Unmark Row + 标记/取消标记行 + + + Ctrl-M + Ctrl+M + + + Mark/Unmark Cell + 标记/取消标记单元格 + + + true + + + + Save Table Image + 保存表格图像 + + + PNG Image (*.png) + PNG 图像 (*.png) + + + + BluetoothHciSummaryDialog + + Bluetooth HCI Summary + 蓝牙 HCI 摘要 + + + Name + 名称 + + + OGF + OGF + + + OCF + OCF + + + Opcode + 操作码 + + + Event + 事件 + + + Subevent + 子事件 + + + Status + 状态 + + + Reason + 原因 + + + Hardware Error + 硬件错误 + + + Occurrence + 发生 + + + Link Control Commands + 链路控制命令 + + + 0x01 + 0x01 + + + 0 + 0 + + + Link Policy Commands + 链路策略命令 + + + 0x02 + 0x02 + + + Controller & Baseband Commands + 控制器和基带命令 + + + 0x03 + 0x03 + + + Informational Parameters + 信息参数 + + + 0x04 + 0x04 + + + Status Parameters + 状态参数 + + + 0x05 + 0x05 + + + Testing Commands + 测试参数 + + + 0x06 + 0x06 + + + LE Controller Commands + LE 控制器命令 + + + 0x08 + 0x08 + + + Bluetooth Logo Testing Commands + 蓝牙日志测试命令 + + + 0x3E + 0x3E + + + Vendor-Specific Commands + 供应商制定的命令 + + + 0x3F + 0x3F + + + Unknown OGF + 未知 OGF + + + Events + 事件 + + + Hardware Errors + 硬件错误 + + + Results filter: + 结果过滤器: + + + Display filter: + 显示过滤器: + + + All Interfaces + 所有接口 + + + All Adapters + 所有适配器 + + + Copy Cell + 复制单元 + + + Copy Rows + 复制行 + + + Copy All + 复制全部 + + + Save as image + 另存为图像 + + + Mark/Unmark Row + 标记/取消标记行 + + + Ctrl+M + Ctrl+M + + + Mark/Unmark Cell + 标记/取消标记单元格 + + + Unknown + 未知 + + + Adapter %1 + 适配器 %1 + + + Frame %1 + 帧 %1 + + + Pending + 等待中 + + + Save Table Image + 保存表格图像 + + + PNG Image (*.png) + PNG 图像 (*.png) + + + + ByteViewTab + + Packet bytes + 分组字节流 + + + + ByteViewText + + Allow hover highlighting + + + + Show bytes as hexadecimal + 将字节显示为十六进制 + + + …as decimal + + + + …as octal + + + + …as bits + + + + Show text based on packet + 根据分组显示文本 + + + …as ASCII + + + + …as EBCDIC + + + + + CaptureFile + + [closing] + [关闭中] + + + [closed] + [已关闭] + + + + CaptureFileDialog + + This capture file contains comments. + 此捕获文件含有注释内容。 + + + The file format you chose doesn't support comments. Do you want to save the capture in a format that supports comments or discard the comments and save in the format you chose? + 您所选择的文件格式不支持注释。您希望将捕获内容保存为支持注释的格式,还是希望丢弃注释,直接保存为您所选择的格式? + + + Discard comments and save + 丢弃注释并保存 + + + Save in another format + 保存为其他格式 + + + No file format in which it can be saved supports comments. Do you want to discard the comments and save in the format you chose? + 可以保存的文件格式中,没有支持注释的格式。您是否要丢弃注释并以您选择的格式保存? + + + All Files ( + 所有文件 ( + + + All Capture Files + 所有捕获文件 + + + Format: + 格式: + + + Size: + 大小: + + + Start / elapsed: + 开始/经过: + + + Automatically detect file type + 自动检测文件类型 + + + Prepend packets + 前置分组 + + + Insert packets from the selected file before the current file. Packet timestamps will be ignored. + 将选中文件中的分组插入到当前文件之前。分组时间戳将被忽略。 + + + Merge chronologically + 按时间戳合并 + + + Insert packets in chronological order. + 按照时间顺序插入分组。 + + + Append packets + 追加分组 + + + Insert packets from the selected file after the current file. Packet timestamps will be ignored. + 将选中文件中的分组插入到当前文件之后。分组时间戳将被忽略。 + + + Read filter: + 读取过滤器: + + + Compress with g&zip + 用 gzip 压缩(&Z) + + + Open Capture File + Wireshark: Open Capture File + 打开捕获文件 + + + Save Capture File As + Wireshark: Save Capture File As + 保存捕获文件为 + + + Save as: + 另存为: + + + Export Specified Packets + Wireshark: Export Specified Packets + 导出特定分组 + + + Export as: + 导出为: + + + Merge Capture File + Wireshark: Merge Capture File + 合并捕获文件 + + + Unknown file type returned by save as dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + directory + 目录 + + + unknown file format + 未知文件格式 + + + error opening file + 打开文件出错 + + + %1, error after %Ln data record(s) + %1, error after %Ln record(s) + + %1,%Ln 数据记录后的错误 + + + + %1, timed out at %Ln data record(s) + + %1,在 %Ln 数据记录处超时 + + + + %1, %Ln data record(s) + + %1,%Ln 数据记录 + + + + unknown + 未知 + + + + CaptureFilePropertiesDialog + + Details + 细节 + + + Capture file comments + 捕获文件描述 + + + Refresh + 刷新 + + + Copy To Clipboard + 复制到剪贴板 + + + Save Comments + 保存注释 + + + Capture File Properties + 捕获文件属性 + + + Unknown + 未知 + + + File + 文件 + + + Name + 名称 + + + Length + 长度 + + + Hash (SHA256) + 哈希 (SHA256) + + + Hash (SHA1) + 哈希 (SHA1) + + + Format + 格式 + + + Encapsulation + 封装 + + + Snapshot length + 快照长度 + + + Time + 时间 + + + First packet + 第一个分组 + + + Last packet + 最后分组 + + + Elapsed + 经过时间 + + + Section %1 + + + + Capture + 捕获 + + + Hardware + 硬件 + + + OS + OS + + + Application + 应用 + + + Interfaces + 接口 + + + Interface + 接口 + + + Dropped packets + 丢弃分组 + + + Capture filter + 捕获过滤器 + + + Link type + 链路类型 + + + Packet size limit (snaplen) + + + + none + + + + %1 bytes + %1 字节 + + + Statistics + 统计 + + + Measurement + 测量 + + + Captured + 已捕获 + + + Displayed + 已显示 + + + Marked + 标记 + + + Packets + 分组 + + + Time span, s + 时间跨度,s + + + Average pps + 平均 pps + + + Average packet size, B + 平均分组大小,B + + + Bytes + 字节 + + + Average bytes/s + 平均 字节/秒 + + + Average bits/s + 平均 比特/秒 + + + Section Comment + + + + Packet Comments + 分组注释 + + + <p>Frame %1: + <p>帧 %1: + + + Created by Wireshark %1 + + + 通过 Wireshark 创建 %1 + + + + CaptureFilterCombo + + Capture filter selector + 捕获过滤器选择器 + + + + CaptureFilterEdit + + Capture filter entry + 捕获过滤器条目 + + + Manage saved bookmarks. + 管理保存的书签。 + + + Apply this filter string to the display. + 将此过滤字符串应用于显示。 + + + Multiple filters selected. Override them here or leave this blank to preserve them. + This is a very long concept that needs to fit into a short space. + 已选择多个过滤器。在此处覆盖它们,或者保留为空以保留它们。 + + + <p>The interfaces you have selected have different capture filters. Typing a filter here will override them. Doing nothing will preserve them.</p> + <p>您选择的接口具有不同的捕获过滤器。在此处键入过滤器将覆盖它们。不做任何事保留它们。</p> + + + Enter a capture filter %1 + 输入捕获过滤器 %1 + + + Save this filter + 保存此过滤器 + + + Remove this filter + 删除此过滤器 + + + Manage Capture Filters + 管理捕获过滤器 + + + + CaptureInfoDialog + + Capture Information + 捕获信息 + + + Stop Capture + 停止捕获 + + + %1 packets, %2:%3:%4 + %1 个分组,%2:%3:%4 + + + + CaptureInfoModel + + Other + 其他 + + + + CaptureOptionsDialog + + Input + + + + Interface + 接口 + + + Traffic + 流量 + + + Link-layer Header + 链路层 + + + Promiscuous + 混杂 + + + Snaplen (B) + 捕获长度 (B) + + + Buffer (MB) + 缓冲区 (MB) + + + Monitor Mode + 监控模式 + + + Capture Filter + 捕获过滤器 + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>您可能希望启用此项。网卡通常只会捕获那些发给它自身地址的流量。如果您希望捕获网卡能够“看到”的所有流量,请启用此选项。查看FAQ中关于从交换网络中捕获分组的详细信息。</p></body></html> + + + Enable promiscuous mode on all interfaces + 在所有接口上使用混杂模式 + + + Show and hide interfaces, add comments, and manage pipes and remote interfaces. + + + + Manage Interfaces… + + + + Capture filter for selected interfaces: + + + + Compile BPFs + + + + Output + 输出 + + + <html><head/><body><p>Enter the file name to which captured data will be written. By default, a temporary file will be used.</p></body></html> + + + + Capture to a permanent file + 抓取并保存到一个永久文件 + + + File: + 文件: + + + Browse… + 浏览… + + + Output format: + + + + pcapng + + + + pcap + + + + <html><head/><body><p>Instead of using a single capture file, multiple files will be created.</p><p>The generated file names will contain an incrementing number and the start time of the capture.</p><p>NOTE: If enabled, at least one of the new-file criteria MUST be selected.</p></body></html> + <html><head/><body><p>相对于使用单个捕获文件,多个文件将会被创建。</p><p>生成的文件名会包含一个递增的序号和捕获开始时间。</p><p>注意:如果启用,则必须至少选择一个创建文件的规则。</p></body></html> + + + Create a new file automatically… + 自动创建新文件,如果… + + + after + 已经 + + + Switch to the next file after the specified number of packets have been captured. + + + + packets + 分组 + + + Switch to the next file after the file size exceeds the specified file size. + 当文件大小超过指定的文件大小后切换到下一个文件。 + + + kilobytes + KB + + + megabytes + MB + + + gigabytes + GB + + + Switch to the next file when the time capturing to the current file exceeds the specified time. + 当捕获时间超过指定时间时,切换到下一个文件。 + + + seconds + + + + minutes + + + + hours + + + + when time is a multiple of + 当前时刻是整数倍于 + + + Switch to the next file when the (wall clock) time is an even multiple of the specified interval. +For example, use 1 hour to have a new file created every hour on the hour. + 当当前时间为指定时间的整数倍时切换到下一个文件。 +例如,如果设置成1小时,则每个整点将创建一个文件。 + + + compression + 压缩 + + + None + + + + gzip + gzip + + + <html><head/><body><p>After capturing has switched to the next file and the given number of files has exceeded, the oldest file will be removed.</p></body></html> + <html><head/><body><p>当捕获切换到下一个文件并且给定的文件数已经达到时,则最早的文件将会被删除。</p></body></html> + + + Use a ring buffer with + + + + files + + + + Options + 选项 + + + Display Options + + + + <html><head/><body><p>Using this option will show the captured packets immediately on the main screen. Please note: this will slow down capturing, so increased packet drops might appear.</p></body></html> + + + + Update list of packets in real-time + + + + <html><head/><body><p>This will scroll the &quot;Packet List&quot; automatically to the latest captured packet, when the &quot;Update list of packets in real-time&quot; option is used.</p></body></html> + + + + Automatically scroll during live capture + + + + <html><head/><body><p>Show the capture info dialog while capturing.</p></body></html> + + + + Show capture information during live capture + + + + Name Resolution + + + + Perform MAC layer name resolution while capturing. + + + + Resolve MAC addresses + + + + <html><head/><body><p>Perform network layer name resolution while capturing.</p></body></html> + + + + Resolve network names + + + + Perform transport layer name resolution while capturing. + + + + Resolve transport names + + + + Stop capture automatically after… + + + + <html><head/><body><p>Stop capturing after the specified number of packets have been captured.</p></body></html> + + + + Stop capturing after the specified number of packets have been captured. + + + + <html><head/><body><p>Stop capturing after the specified number of files have been created.</p></body></html> + + + + <html><head/><body><p>Stop capturing after the specified amount of data has been captured.</p></body></html> + + + + Stop capturing after the specified amount of data has been captured. + + + + Stop capturing after the specified amount of time has passed. + + + + <html><head/><body><p>Optionally specify a temporary directory for unnamed capture files.</p></body></html> + + + + Directory for temporary files + + + + Capture Options + 捕获选项 + + + Start + 开始 + + + Leave blank to use a temporary file + + + + Specify a Capture File + + + + Specify temporary directory + + + + %1: %2 + %1: %2 + + + Addresses + 地址 + + + Address + 地址 + + + no addresses + + + + Error + 错误 + + + Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB. + + + + Multiple files: No capture file name given. You must specify a filename if you want to use multiple files. + + + + Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file. + + + + + CapturePreferencesFrame + + Frame + + + + Default interface + 默认接口 + + + <html><head/><body><p>You probably want to enable this. Usually a network card will only capture the traffic sent to its own network address. If you want to capture all traffic that the network card can &quot;see&quot;, mark this option. See the FAQ for some more details of capturing packets from a switched network.</p></body></html> + <html><head/><body><p>您可能希望启用此项。网卡通常只会捕获那些发给它自身地址的流量。如果您希望捕获网卡能够“看到”的所有流量,请启用此选项。查看FAQ中关于从交换网络中捕获分组的详细信息。</p></body></html> + + + Capture packets in promiscuous mode + 使用混杂模式捕获分组 + + + <html><head/><body><p>Capture packets in the next-generation capture file format.</p></body></html> + <html><head/><body><p>使用下一代捕获文件格式来捕获分组。</p></body></html> + + + Capture packets in pcapng format + 以 pcapng 格式捕获分组 + + + <html><head/><body><p>Update the list of packets while capture is in progress. This can result in dropped packets on high-speed networks.</p></body></html> + <html><head/><body><p>在捕获进行时更新分组列表。这可能会导致在高速网络中丢弃分组。</p></body></html> + + + Update list of packets in real time + 实时更新分组列表 + + + Interval between updates (ms) + + + + <html><head/><body><p>How often the capture notifies the GUI of new packets. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + <html><head/><body><p>The interval between new packet updates. Affects how often the GUI updates and the granularity of timers.</p></body></html> + + + + Don't load interfaces on startup + 启动时不加载接口 + + + Disable external capture interfaces + 禁用外部捕获接口 + + + + ColoringRulesDelegate + + the "@" symbol will be ignored. + “@”符号将被忽略。 + + + + ColoringRulesDialog + + Dialog + 对话框 + + + <small><i>A hint.</i></small> + <small><i>提示</i></small> + + + Add a new coloring rule. + 增加着色规则。 + + + Delete this coloring rule. + 删除着色规则。 + + + Duplicate this coloring rule. + 复制着色规则。 + + + Clear all coloring rules. + 清除所有着色规则。 + + + Set the foreground color for this rule. + 为规则设置前景色。 + + + Foreground + 前景 + + + Set the background color for this rule. + 为规则设置背景色。 + + + Background + 背景 + + + Set the display filter using this rule. + 使用此规则设置显示过滤器。 + + + Apply as filter + 作为过滤器应用 + + + Select a file and add its filters to the end of the list. + 选择文件并添加到列表末尾。 + + + Save filters in a file. + 保存过滤器到文件。 + + + Coloring Rules %1 + 着色规则 %1 + + + Import… + + + + Export… + + + + Copy coloring rules from another profile. + 从另一个配置文件复制着色规则。 + + + Open + 打开 + + + Double click to edit. Drag to move. Rules are processed in order until a match is found. + 双击编辑。拖拽移动。规则按顺序进行处理,直到找到一个匹配的。 + + + Import Coloring Rules + 导入着色规则 + + + Export %1 Coloring Rules + 导出 %1 着色规则 + + + + ColoringRulesModel + + New coloring rule + + + + Unable to save coloring rules: %1 + 无法保存着色规则:%1 + + + Name + 名称 + + + Filter + 过滤器 + + + + ColumnEditorFrame + + Frame + + + + Title: + Title + 标题: + + + Type: + Type + 类型: + + + Fields: + Fields + 字段: + + + Occurrence: + Occurrence + 发生: + + + Resolve Names: + + + + <html><head/><p>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</p></body></html> + + + + Missing fields. + 缺少字段。 + + + Invalid fields. + 无效字段。 + + + Invalid occurrence value. + 无效的发生值。 + + + + ColumnListModel + + Displayed + 已显示 + + + Title + + + + Type + 类型 + + + Fields + + + + Field Occurrence + + + + Resolved + + + + <html>Show human-readable strings instead of raw values for fields. Only applicable to custom columns with fields that have value strings.</html> + + + + New Column + + + + + ColumnPreferencesFrame + + Frame + + + + Add a new column + + + + Delete selected column + + + + Show displayed columns only + + + + Reset all changes + + + + + CompiledFilterOutput + + Compiled Filter Output + 编译的过滤器输出 + + + Copy + 复制 + + + Copy filter text to the clipboard. + 复制过滤器文本到剪贴板。 + + + + ConversationDataModel + + Address A + + + + Port A + + + + Address B + + + + Port B + + + + Packets + 分组 + + + Bytes + + + + Stream ID + + + + Packets A + + + + Bytes A + + + + Packets B + + + + Bytes B + + + + Abs Start + + + + Rel Start + + + + Duration + 持续时间 + + + Bits/s A + + + + Bits/s B + + + + Total Packets + + + + Percent Filtered + + + + + ConversationDialog + + Follow Stream… + + + + Follow a TCP or UDP stream. + 追踪 TCP 或 UDP 流。 + + + Graph… + + + + Graph a TCP conversation. + 绘制 TCP 对话图形。 + + + + ConversationHashTablesDialog + + Dialog + 对话框 + + + Conversation Hash Tables + 对话哈希表 + + + + CopyFromProfileButton + + Copy from + 复制自 + + + Copy entries from another profile. + 从其他配置文件复制条目。 + + + System default + + + + + CredentialsDialog + + Wireshark - Credentials + + + + Credentials + + + + + CredentialsModel + + Click to select the packet + + + + Click to select the packet with username + + + + Username not available + + + + Packet No. + 分组编号. + + + Protocol + 协议 + + + Username + + + + Additional Info + + + + + DataPrinter + + Copy Bytes as Hex + ASCII Dump + 将字节复制为十六进制 + ASCII 转储 + + + Copy packet bytes as a hex and ASCII dump. + 将分组字节复制为十六进制和 ASCII 转储。 + + + …as Hex Dump + + + + Copy packet bytes as a hex dump. + 将分组字节复制为十六进制转储。 + + + …as MIME Data + + + + …as C String + + + + Copy packet bytes as printable ASCII characters and escape sequences. + + + + …as a Hex Stream + + + + Copy packet bytes as a stream of hex. + 将分组字节复制为十六进制流。 + + + …as a Base64 String + + + + Copy packet bytes as a base64 encoded string. + + + + Copy packet bytes as application/octet-stream MIME data. + 将分组字节复制为 application/octet-stream MIME 数据。 + + + + DecodeAsDialog + + Change the dissection behavior for a protocol. + 修改协议的解析行为。 + + + Remove this dissection behavior. + 删除解析行为。 + + + Copy this dissection behavior. + 复制解析行为。 + + + Clear all dissection behaviors. + 清除所有解析行为。 + + + Decode As… + + + + Open + 打开 + + + + DecodeAsModel + + Match using this field + 使用此字段匹配 + + + Change behavior when the field matches this value + + + + Field value type (and base, if Integer) + + + + Current"Decode As" behavior + 当前“解码为”行为 + + + Default "Decode As" behavior + 默认“解码为”行为 + + + String + 字符串 + + + Integer, base + 整数,基数 + + + unknown + 未知 + + + <none> + <无> + + + GUID + GUID + + + Field + 字段 + + + Value + + + + Type + 类型 + + + Default + 默认 + + + Current + 当前 + + + + DisplayFilterCombo + + Display filter selector + 显示过滤选择器 + + + Select from previously used filters. + 从以前使用的过滤器中选择. + + + + DisplayFilterEdit + + Display filter entry + 显示过滤器条目 + + + Manage saved bookmarks. + 管理保存的书签。 + + + Display Filter Expression… + + + + Apply a display filter %1 <%2/> + 应用显示过滤器 %1 <%2/> + + + Enter a display filter %1 + 输入显示过滤器 %1 + + + Clear display filter + + + + Apply display filter + + + + Left align buttons + + + + Apply a read filter %1 + 应用读取过滤器 %1 + + + Current filter: %1 + + + + Invalid filter: + 无效的过滤器: + + + Save this filter + 保存此过滤器 + + + Remove this filter + 删除此过滤器 + + + Manage Display Filters + 管理显示过滤器 + + + Filter Button Preferences... + 筛选器按钮首选项... + + + + DisplayFilterExpressionDialog + + Dialog + 对话框 + + + Select a field to start building a display filter. + 选择一个字段开始建立一个显示过滤器。 + + + Field Name + 字段名称 + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>搜索字段名称列表。</p></body></html> + + + Search: + 搜索: + + + <html><head/><body><p>Relations can be used to restrict fields to specific values. Each relation does the following:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">is present</span></p></td><td><p>Match any packet that contains this field</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==, !=, etc.</span></p></td><td><p>Compare the field to a specific value.</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">contains, matches</span></p></td><td><p>Check the field against a string (contains) or a regular expression (matches)</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">in</span></p></td><td><p>Compare the field to a specific set of values</p></td></tr></table></body></html> + + + <html><head/><body><p>关系可用于将字段限制为特定值。每个关系可执行以下操作:</p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p align="center"><span style=" font-weight:600;">存在</span></p></td><td><p>匹配包含此字段的任何分组</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">==,!=,等</span></p></td><td><p>将字段与特定值进行比较。</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">包含,匹配</span></p></td><td><p>根据字符串(包含)或正则表达式(匹配)检查字段</p></td></tr><tr><td><p align="center"><span style=" font-weight:600;">属于</span></p></td><td><p>将字段与特定值集合进行比较</p></td></tr></table></body></html> + + + Relation + 关系 + + + By default order comparisons and contains/matches/in relations are true if any value matches. The quantifier "all" can be used to apply the test to all values in a frame. + + + + Quantifier + + + + Any + 任何 + + + All + + + + Match against this value. + 匹配对应此值。 + + + Value + + + + If the field you have selected has a known set of valid values they will be listed here. + 如果您已选择的字段有一个已知的有效值的集,它将被在这里列出。 + + + Predefined Values + 预定义的值 + + + If the field you have selected covers a range of bytes (e.g. you have selected a protocol) you can restrict the match to a range of bytes here. + 如果您已选择的字段涵盖范围字节(例如您选择了一个协议),您可以在这里对匹配进行字节范围的限制。 + + + Range (offset:length) + 范围 (偏移:长度) + + + No display filter + 无显示过滤器 + + + <small><i>A hint.</i></small> + <small><i>提示</i></small> + + + Display Filter Expression + 显示过滤器表达式 + + + Select a field name to get started + 选择一个字段名称以开始 + + + Click OK to insert this filter + 点击确定插入此过滤器 + + + + DissectorSyntaxLineEdit + + Dissector entry + + + + Enter a dissector %1 + + + + + DissectorTablesDialog + + Dialog + 对话框 + + + Search: + 搜索: + + + Dissector Tables + 解析器表 + + + + DissectorTablesProxyModel + + Table Type + 表类型 + + + String + 字符串 + + + Dissector Description + + + + Integer + 整数 + + + Protocol + 协议 + + + Short Name + 简称 + + + Table Name + 表名称 + + + Selector Name + 选择器名称 + + + + EnabledProtocolsDialog + + Dialog + 对话框 + + + <small><i>Disabling a protocol prevents higher layer protocols from being displayed</i></small> + <small><i>禁用一个协议将阻止显示更高层级的协议</i></small> + + + Search: + 搜索: + + + in + + + + Enable All + 全部启用 + + + Disable All + 全部禁用 + + + Invert + 反转 + + + Enabled Protocols + 已启用的协议 + + + Everywhere + + + + Only Protocols + + + + Only Description + + + + Only enabled protocols + + + + Only disabled protocols + + + + any protocol + + + + non-heuristic protocols + + + + heuristic protocols + + + + + EnabledProtocolsModel + + Protocol + 协议 + + + Description + 描述 + + + + EndpointDataModel + + Address + 地址 + + + Port + + + + Packets + 分组 + + + Bytes + + + + Tx Packets + + + + Tx Bytes + + + + Rx Packets + + + + Rx Bytes + + + + Country + + + + City + + + + Latitude + + + + Longitude + + + + AS Number + + + + AS Organization + + + + Total Packets + + + + Percent Filtered + + + + + EndpointDialog + + Map + + + + Draw IPv4 or IPv6 endpoints on a map. + + + + Open in browser + 在浏览器中打开 + + + Save As… + + + + Map file error + + + + Save Endpoints Map + + + + Failed to save map file %1. + + + + + EthernetAddressModel + + Type + 类型 + + + Name + 名称 + + + Address + 地址 + + + All entries + + + + Hosts + + + + Ethernet Addresses + 以太网地址簿 + + + Ethernet Manufacturers + 以太网制造商 + + + Ethernet Well-Known Addresses + 以太网著名地址簿 + + + + ExpertInfoDialog + + Dialog + 对话框 + + + <small><i>A hint.</i></small> + <small><i>提示</i></small> + + + Limit to Display Filter + 显示过滤器的限制 + + + Group by summary + 按摘要分组 + + + Search expert summaries. + 搜索专家概要。 + + + Search: + 搜索: + + + Show… + Show... + 显示… + + + Error + 错误 + + + Show error packets. + 显示错误分组。 + + + Warning + 警告 + + + Show warning packets. + 显示警告分组。 + + + Note + 注意 + + + Show note packets. + 显示注意分组。 + + + Chat + 聊天 + + + Show chat packets. + 显示聊天分组。 + + + Comment + 注释 + + + Show comment packets. + 显示注释分组。 + + + Expert Information + 专家信息 + + + Collapse All + 全部折叠 + + + Expand All + 全部展开 + + + Capture file closed. + 捕获文件已关闭。 + + + No display filter + 无显示过滤器 + + + No display filter set. + 没有设置显示过滤器。 + + + Limit information to "%1". + 限制信息至“%1”。 + + + Display filter: "%1" + 显示过滤器:“%1” + + + + ExpertInfoProxyModel + + Packet + 分组 + + + Severity + 严重 + + + Summary + 概要 + + + Group + + + + Protocol + 协议 + + + Count + 计数 + + + + ExportDissectionDialog + + Export Packet Dissections + Wireshark: Export Packet Dissections + 导出分组解析结果 + + + Export As: + Export as: + 导出为: + + + Plain text (*.txt) + 纯文本 (*.txt) + + + Comma Separated Values - summary (*.csv) + 逗号分隔值 - 概要 (*.csv) + + + PSML - summary (*.psml, *.xml) + PSML - 概要 (*.psml, *.xml) + + + PDML - details (*.pdml, *.xml) + PDML - 详细 (*.pdml, *.xml) + + + JSON (*.json) + JSON (*.json) + + + C Arrays - bytes (*.c, *.h) + C 数组 - 字节流 (*.c, *.h) + + + + ExportObjectDialog + + Dialog + 对话框 + + + Content Type: + + + + Searching for objects + 正在搜索对象 + + + Text Filter: + 文本过滤器: + + + Only display entries containing this string + 仅显示包含此字符串的条目 + + + Preview + + + + All Content-Types + + + + Export + 导出 + + + %1 object list + %1 对象列表 + + + Save Object As… + + + + Save All Objects In… + + + + + ExportObjectModel + + Packet + 分组 + + + Hostname + 主机名 + + + Content Type + 内容类型 + + + Size + 大小 + + + Filename + 文件名 + + + + ExportPDUDialog + + Dialog + 对话框 + + + Display filter: + 显示过滤器: + + + + ExtArgSelector + + Reload data + 重新加载数据 + + + + ExtcapArgumentFileSelection + + Clear + + + + All Files ( + 所有文件 ( + + + Open File + 打开文件 + + + Select File + + + + + ExtcapOptionsDialog + + Interface Options + Extcap Interface Options + 接口选项 + + + Start + 开始 + + + Save + + + + Default + 默认 + + + Restore default value of the item + + + + Extcap Help cannot be found + 无法找到 Extcap 帮助 + + + The help for the extcap interface %1 cannot be found. Given file: %2 + 无法找到 extcap接口 %1 的帮助。文件:%2 + + + Save parameter(s) on capture start + + + + + FieldFilterEdit + + Display filter entry + 显示过滤器条目 + + + Enter a field %1 + 输入字段 %1 + + + Invalid filter: + 无效的过滤器: + + + + FileSetDialog + + Dialog + 对话框 + + + Directory: + 目录: + + + No files in Set + 集合中没有文件 + + + No capture loaded + 未加载捕获 + + + %Ln File(s) in Set + %1 File%2 in Set + + %Ln 个文件在集合中 + + + + + FilesetEntryModel + + Open this capture file + 打开此捕获文件 + + + Filename + 文件名 + + + Created + 创建 + + + Modified + 修改 + + + Size + 大小 + + + + FilterAction + + Selected + 选中 + + + Not Selected + 非选中 + + + …and Selected + …且选中 + + + …or Selected + ...或选中 + + + …and not Selected + ...且不选中 + + + …or not Selected + ...或不选中 + + + + FilterDialog + + Dialog + 对话框 + + + Create a new filter. + 创建一个新的过滤器。 + + + Remove this filter. + Remove this profile. + 移除此过滤器。 + + + Copy this filter. + Copy this profile. + 复制此过滤器。 + + + Capture Filters + 捕获过滤器 + + + Display Filters + 显示过滤器 + + + Open + 打开 + + + New capture filter + This text is automatically filled in when a new filter is created + 新建捕获过滤器 + + + New display filter + This text is automatically filled in when a new filter is created + 新建显示过滤器 + + + + FilterExpressionFrame + + Frame + + + + Filter Buttons Preferences… + 过滤器按钮首选项… + + + Label: + 标签: + + + Enter a description for the filter button + 为过滤器按钮输入描述 + + + Filter: + 过滤器: + + + Enter a filter expression to be applied + 输入要应用的过滤器表达式 + + + Comment: + 注释: + + + Enter a comment for the filter button + 输入过滤器按钮的注释 + + + Missing label. + 缺少标签。 + + + Missing filter expression. + 缺少过滤器表达式。 + + + Invalid filter expression. + 过滤器表达式无效。 + + + + FilterExpressionToolBar + + Filter Button Preferences... + 筛选器按钮首选项... + + + Edit + 编辑 + + + Disable + 禁用 + + + Remove + 移除 + + + + FilterListModel + + Filter Name + + + + Filter Expression + + + + + FindLineEdit + + Textual Find + 文本查找 + + + Regular Expression Find + 正则表达式查找 + + + + FirewallRulesDialog + + Create rules for + 创建规则为 + + + Inbound + 入站 + + + Deny + 拒绝 + + + Firewall ACL Rules + 防火墙 ACL 规则 + + + Copy + 复制 + + + IPv4 source address. + IPv4 源地址。 + + + IPv4 destination address. + IPv4 目标地址。 + + + Source port. + 源端口。 + + + Destination port. + 目标端口。 + + + IPv4 source address and port. + IPv4 源地址和端口。 + + + IPv4 destination address and port. + IPv4 目标地址和端口。 + + + MAC source address. + MAC 源地址。 + + + MAC destination address. + MAC 目标地址。 + + + Text file (*.txt);;All Files ( + 文本文件 (*.txt);;所有文件 ( + + + Warning + 警告 + + + Unable to save %1 + 无法保存 %1 + + + + FolderListModel + + "File" dialogs + “文件”对话框 + + + capture files + 捕获文件 + + + Temp + 临时 + + + untitled capture files + 无标题捕获文件 + + + Personal configuration + 个人配置 + + + Global configuration + 全局配置 + + + dfilters, preferences, ethers, … + + + + dfilters, preferences, manuf, … + + + + System + 系统 + + + ethers, ipxnets + ethers, ipxnets + + + Program + 程序 + + + program files + 程序文件 + + + Personal Plugins + 个人插件 + + + binary plugins + 二进制插件 + + + Global Plugins + 全局插件 + + + Personal Lua Plugins + 个人 Lua 插件 + + + Global Lua Plugins + 全局 Lua 插件 + + + Lua scripts + + + + Personal Extcap path + + + + external capture (extcap) plugins + + + + Global Extcap path + + + + MaxMind DB path + MaxMind DB 路径 + + + MaxMind DB database search path + MaxMind DB 数据库搜索路径 + + + MIB/PIB path + MIB/PIB 路径 + + + SMI MIB/PIB search path + SMI MIB/PIB 搜索路径 + + + macOS Extras + + + + Extra macOS packages + + + + Name + 名称 + + + Location + 位置 + + + Typical Files + 典型文件 + + + + FollowStreamAction + + %1 Stream + + + + + FollowStreamDialog + + Filter Out This Stream + Hide this stream + 滤掉此流 + + + Print + 打印 + + + ASCII + ASCII + + + C Arrays + C Arrays + + + EBCDIC + EBCDIC + + + Hex Dump + Hex 转储 + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Raw + 原始数据 + + + Save as… + 另存为… + + + Back + 返回 + + + Packet %1. + 分组 %1。 + + + %Ln <span style="color: %1; background-color:%2">client</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">客户端</span> 分组, + + + + %Ln <span style="color: %1; background-color:%2">server</span> pkt(s), + + %Ln <span style="color: %1; background-color:%2">服务器</span> 分组, + + + + %Ln turn(s). + + %Ln turn(s). + + + + Click to select. + 点击选择。 + + + Regex Find: + 正则表达式查找: + + + No capture file. + 无捕获文件。 + + + Please make sure you have a capture file opened. + 请确认您已经打开一个捕获文件。 + + + Error following stream. + 追踪流错误。 + + + Capture file invalid. + 捕获文件无效。 + + + Please make sure you have a %1 packet selected. + 请确保您已选中一个 %1 分组。 + + + %1 stream not found on the selected packet. + + + + Entire conversation (%1) + 整个对话(%1) + + + Follow %1 Stream (%2) + 追踪 %1 流 (%2) + + + Error creating filter for this stream. + 为这个流创建过滤器时出错。 + + + Save Stream Content As… + + + + [Stream output truncated] + [流输出被截断] + + + %Ln total stream(s). + + %Ln 全部流。 + + + + Max sub stream ID for the selected stream: %Ln + + + + + + File closed. + 文件关闭。 + + + Follow Stream + 追踪数据流 + + + Hint. + 提示。 + + + Show data as + Show and save data as + + + + Stream + + + + Substream + + + + Find: + 查找: + + + Find &Next + 查找下一个(&N) + + + + FontColorPreferencesFrame + + Frame + + + + Main window font: + 主窗口字体: + + + Select Font + 选择字体 + + + Colors: + 颜色: + + + System Default + + + + Solid + + + + Sample ignored packet text + 忽略的分组 + + + Sample marked packet text + 标记的分组 + + + Sample active selected item + 示例活动选定项目 + + + Style: + 样式: + + + Gradient + 渐变 + + + Sample inactive selected item + 示例非活动选定项目 + + + Sample "Follow Stream" client text + “追踪数据流”客户端示例文本 + + + Sample "Follow Stream" server text + “追踪数据流”服务器示例文本 + + + Sample valid filter + 有效过滤器示例 + + + Sample invalid filter + 无效过滤器示例 + + + Sample warning filter + Sample deprecated filter + 样本警告过滤器 + + + Example GIF query packets have jumbo window sizes + These are pangrams. Feel free to replace with nonsense text that spans your alphabet. https://en.wikipedia.org/wiki/Pangram + + + + Lazy badgers move unique waxy jellyfish packets + + + + Font + 字体 + + + + FunnelStringDialog + + Dialog + 对话框 + + + + FunnelTextDialog + + Dialog + 对话框 + + + <html><head/><body><p>Enter some text or a regular expression. It will be highlighted above.</p></body></html> + <html><head/><body><p>请输入要在上方高亮的文本或者正则表达式。</p></body></html> + + + Highlight: + 高亮: + + + + GsmMapSummaryDialog + + Dialog + 对话框 + + + GSM MAP Summary + GSM MAP 摘要 + + + File + 文件 + + + Name + 名称 + + + Length + 长度 + + + Format + 格式 + + + Snapshot length + 快照长度 + + + Data + 数据 + + + First packet + 首个分组 + + + Last packet + 末个分组 + + + Elapsed + 经过时间 + + + Packets + 分组 + + + Invokes + 调用 + + + Total number of Invokes + 调用总数 + + + Average number of Invokes per second + 每秒平均调用 + + + Total number of bytes for Invokes + 调用总字节 + + + Average number of bytes per Invoke + 调用平均字节 + + + Return Results + 返回结果 + + + Total number of Return Results + 返回结果总数 + + + Average number of Return Results per second + 每秒平均返回结果 + + + Total number of bytes for Return Results + 返回结果总字节 + + + Average number of bytes per Return Result + 返回结果平均字节 + + + Totals + 总计 + + + Total number of GSM MAP messages + GSM MAP 消息总数 + + + Average number of GSM MAP messages per second + GSM MAP 消息每秒平均数量 + + + Total number of bytes for GSM MAP messages + GSM MAP 消息总字节 + + + Average number of bytes per GSM MAP message + GSM MAP 消息平均字节 + + + + IOConsoleDialog + + Dialog + 对话框 + + + Enter code + + + + Evaluate + + + + Clear + + + + Use %1 to evaluate. + + + + + IOGraphDialog + + Dialog + 对话框 + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>有价值的和惊人的时间节省键盘快捷方式</h3> +<table><tbody> + +<tr><th>+</th><td>放大</td></th> +<tr><th>-</th><td>缩小</td></th> +<tr><th>x</th><td>放大 X 轴</td></th> +<tr><th>X</th><td>缩小 X 轴</td></th> +<tr><th>y</th><td>放大 Y 轴</td></th> +<tr><th>Y</th><td>缩小 Y 轴</td></th> +<tr><th>0</th><td>图形重置为初始状态</td></th> + +<tr><th>→</th><td>右移 10 个像素</td></th> +<tr><th>←</th><td>左移 10 个像素</td></th> +<tr><th>↑</th><td>上移 10 个像素</td></th> +<tr><th>↓</th><td>下移 10 个像素</td></th> +<tr><th><i>Shift+</i>→</th><td>右移 1 个像素</td></th> +<tr><th><i>Shift+</i>←</th><td>左移 1 个像素</td></th> +<tr><th><i>Shift+</i>↑</th><td>上移 1 个像素</td></th> +<tr><th><i>Shift+</i>↓</th><td>下移 1 个像素</td></th> + +<tr><th>g</th><td>转到光标下的分组</td></th> + +<tr><th>z</th><td>切换鼠标 拖动/缩放</td></th> +<tr><th>t</th><td>切换 捕获/会话 时间起源</td></th> +<tr><th>Space</th><td>切换十字线</td></th> + +</tbody></table> +</body></html> + + + Remove this graph. + Remove this dissection behavior. + 删除图形。 + + + Add a new graph. + 增加新图形。 + + + Duplicate this graph. + 复制图形。 + + + Clear all graphs. + 清除所有图形。 + + + Move this graph upwards. + + + + Move this graph downwards. + + + + Mouse + 鼠标 + + + Drag using the mouse button. + 使用鼠标按键进行拖拽。 + + + drags + 拖拽 + + + Select using the mouse button. + 使用鼠标按键进行选择。 + + + zooms + 缩放 + + + Interval + 间隔 + + + Time of day + 一天时钟 + + + Log scale + 对数刻度 + + + Automatic update + + + + Enable legend + + + + Reset + 复位 + + + Reset Graph + 复位图形 + + + Reset the graph to its initial state. + 复位图形到初始状态。 + + + 0 + 0 + + + Zoom In + 放大 + + + + + + + + + Zoom Out + 缩小 + + + - + - + + + Move Up 10 Pixels + 上移10像素 + + + Up + 上键 + + + Move Left 10 Pixels + 左移10像素 + + + Left + 左键 + + + Move Right 10 Pixels + 右移10像素 + + + Right + 右键 + + + Move Down 10 Pixels + 下移10像素 + + + Down + 下键 + + + Move Up 1 Pixel + 上移1像素 + + + Shift+Up + Shift+上键 + + + Move Left 1 Pixel + 左移1像素 + + + Shift+Left + Shift+右键 + + + Move Right 1 Pixel + 右移1像素 + + + Shift+Right + Shift+右键 + + + Move Down 1 Pixel + 下移1像素 + + + Move down 1 Pixel + Move down 1 pixel + 下移1像素 + + + Shift+Down + Shift+下键 + + + Go To Packet Under Cursor + 跳转到光标所在的分组 + + + Go to packet currently under the cursor + 跳转到当前光标所在的分组 + + + G + G + + + Drag / Zoom + 拖拽/缩放 + + + Toggle mouse drag / zoom behavior + 切换鼠标拖拽/缩放行为 + + + Z + Z + + + Capture / Session Time Origin + 捕获/会话时间起源 + + + Toggle capture / session time origin + 触发捕获/会话时间起源 + + + T + T + + + Crosshairs + 十字线 + + + Toggle crosshairs + 切换十字线 + + + Space + 空格键 + + + Zoom In X Axis + 放大 X 轴 + + + X + X + + + Zoom Out X Axis + 缩小 X 轴 + + + Shift+X + Shift+X + + + Zoom In Y Axis + 放大 Y 轴 + + + Y + Y + + + Zoom Out Y Axis + 缩小 Y 轴 + + + Shift+Y + Shift+Y + + + 1 sec + 1秒 + + + 10 sec + 10秒 + + + 1 min + 1分钟 + + + 10 min + 10分钟 + + + Time (s) + 时间 (秒) + + + I/O Graphs + + + + Save As… + + + + Copy + 复制 + + + Copy graphs from another profile. + 从另一个配置文件复制图形。 + + + 1 ms + 1毫秒 + + + 2 ms + 100毫秒 {2 ?} + + + 5 ms + 100毫秒 {5 ?} + + + 10 ms + 10毫秒 + + + 20 ms + 100毫秒 {20 ?} + + + 50 ms + 100毫秒 {50 ?} + + + 100 ms + 100毫秒 + + + 200 ms + 100毫秒 {200 ?} + + + 500 ms + 100毫秒 {500 ?} + + + 2 sec + 10秒 {2 ?} + + + 5 sec + 10秒 {5 ?} + + + Wireshark I/O Graphs: %1 + + + + Filtered packets + + + + Filtered events + + + + All Packets + + + + TCP Errors + + + + All Events + + + + Access Denied + + + + Hover over the graph for details. + 悬停在图片上来查看详情。 + + + No packets in interval + 间隔期没有分组 + + + No events in interval + + + + Click to select packet + 点击选取分组 + + + Packet + 分组 + + + Click to select event + + + + Event + 事件 + + + %1 (%2s%3). + %1 (%2s%3)。 + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 释放缩放, x = %1 到 %2, y = %3 到 %4 + + + Unable to select range. + 无法选择范围。 + + + Click to select a portion of the graph. + 点击选择图形的一部分。 + + + Portable Document Format (*.pdf) + 便携式文档格式 (*.pdf) + + + Portable Network Graphics (*.png) + 便携式网络图形格式 (*.png) + + + Windows Bitmap (*.bmp) + Windows 位图 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 文件交换格式 (*.jpeg *.jpg) + + + Comma Separated Values (*.csv) + 逗号分隔值 (*.csv) + + + Save Graph As… + + + + + Iax2AnalysisDialog + + Dialog + 对话框 + + + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html> + <html><head/><body><p><span style=" font-size:medium; font-weight:600;">正向</span></p><p><span style=" font-size:medium; font-weight:600;">反向</span></p></body></html> + + + Forward + 正向 + + + Packet + 分组 + + + Delta (ms) + 延迟 (ms) + + + Jitter (ms) + 抖动 (ms) + + + Bandwidth + 带宽 + + + Status + 状态 + + + Length + 长度 + + + Reverse + 反向 + + + Graph + 图表 + + + <html><head/><body><p>Show or hide forward jitter values.</p></body></html> + <html><head/><body><p>显示或隐藏正向抖动值。</p></body></html> + + + Forward Jitter + 正向抖动 + + + <html><head/><body><p>Show or hide forward difference values.</p></body></html> + <html><head/><body><p>显示或隐藏正向差异值。</p></body></html> + + + Forward Difference + 正向差异 + + + <html><head/><body><p>Show or hide reverse jitter values.</p></body></html> + <html><head/><body><p>显示或隐藏反向抖动值。</p></body></html> + + + Reverse Jitter + 反向抖动 + + + <html><head/><body><p>Show or hide reverse difference values.</p></body></html> + <html><head/><body><p>显示或隐藏反向差异值。</p></body></html> + + + Reverse Difference + 反向差异 + + + <small><i>A hint.</i></small> + <small><i>一个提示。</i></small> + + + Audio + 音频 + + + Save the audio data for both channels. + 保存两个频道的音频数据。 + + + Forward Stream Audio + 正向音频流 + + + Save the forward stream audio data. + 保存正向音频流数据。 + + + Reverse Stream Audio + 反向音频流 + + + Save the reverse stream audio data. + 保存反向音频流数据。 + + + CSV + CSV + + + Save both tables as CSV. + 两个表另存为 CSV。 + + + Forward Stream CSV + 正向流 CSV + + + Save the forward table as CSV. + 正向表另存为 CSV。 + + + Reverse Stream CSV + 反向流 CSV + + + Save the reverse table as CSV. + 反向表另存为 CSV。 + + + Save Graph + 保存图表 + + + Save the graph image. + 保存图表图像。 + + + Go to Packet + 跳转到分组 + + + Select the corresponding packet in the packet list. + 选择分组列表中对应的分组。 + + + G + G + + + Next Problem Packet + 下一个问题分组 + + + Go to the next problem packet + 转至下一个问题分组 + + + N + N + + + IAX2 Stream Analysis + IAX2 流分析 + + + Unable to save RTP data. + 无法保存 RTP 数据。 + + + Please select an IAX2 packet. + 请选择一个 IAX2 分组。 + + + G: Go to packet, N: Next problem packet + G: 转到分组,N: 转到问题分组 + + + Portable Document Format (*.pdf) + 便携式文档格式 (*.pdf) + + + Portable Network Graphics (*.png) + 便携式网络图形格式 (*.png) + + + Windows Bitmap (*.bmp) + Windows 位图 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 文件交换格式 (*.jpeg *.jpg) + + + Save Graph As… + + + + Can't save in a file: Wrong length of captured packets. + 无法保存到一个文件:捕获到的分组长度错误。 + + + Can't save in a file: File I/O problem. + 无法保存到一个文件:文件 I/O 问题。 + + + Save forward stream audio + 保存正向音频流 + + + Save reverse stream audio + 保存反向音频流 + + + Save audio + 保存音频 + + + Sun Audio (*.au) + Sun 音频 (*.au) + + + ;;Raw (*.raw) + ;;原始数据 (*.raw) + + + Warning + 警告 + + + Unable to save in that format + 无法保存到该格式 + + + Unable to save %1 + 无法保存 %1 + + + Saving %1… + + + + Analyzing IAX2 + + + + Save forward stream CSV + 保存正向流 CSV + + + Save reverse stream CSV + 保存反向流 CSV + + + Save CSV + 保存 CSV + + + Comma-separated values (*.csv) + 逗号分隔值 (*.csv) + + + + ImportTextDialog + + File: + 文件: + + + Set name of text file to import + 设置要导入的文本文件名称 + + + Browse for text file to import + 浏览要导入的文本文件 + + + Browse… + Browse... + 浏览… + + + Hex Dump + Hex 转储 + + + Import a standard hex dump as exported by Wireshark + + + + Offsets in the text file are in octal notation + 八进制表示的文本文件偏移量 + + + Octal + 八进制 + + + Offsets: + 偏移量: + + + Offsets in the text file are in hexadecimal notation + 十六进制表示的文本文件偏移量 + + + Hexadecimal + 十六进制 + + + Offsets in the text file are in decimal notation + 十进制表示的文本文件偏移量 + + + Decimal + 十进制 + + + <html><head/><body><p>Whether to do extra processing detecting the start of the ASCII representation at the end of a hex+ASCII line even if it looks like hex bytes.</p><p>Do not enable if the hex dump does not contain ASCII.</p></body></html> + + + + ASCII identification: + + + + Regular Expression + 正则表达式 + + + Import a file formatted according to a custom regular expression + + + + Packet format regular expression + + + + <html><head/><body><p>Perl compatible regular expression capturing a single packet in the file with named groups identifieing data to import. Anchors ^ and $ also match before/after newlines </p><p>Required is only a data group, also supported are time, dir and seqno.</p><p>Regex flags: DUPNAMES, MULTILINE and NOEMPTY</p></body></html> + + + + This is regexHintLabel, it will be set to default_regex_hint + + + + Data encoding: + + + + How data is encoded + + + + encodingRegexExample + + + + List of characters indicating incoming packets + + + + iI< + + + + List of characters indicating outgoing packets + + + + oO> + + + + Timestamp format: + 时间戳格式: + + + Whether or not the file contains information indicating the direction (inbound or outbound) of the packet. + 文件是否包含信息来指示分组的方向(入或出方向)。 + + + Direction indication: + 方向指示: + + + ExportPDU + + + + IP version: + + + + Interface name: + + + + The name of the interface to write to the import capture file + + + + Fake IF, Import from Hex Dump + + + + Maximum frame length: + 最大帧长: + + + Encapsulation + 封装 + + + The text file has no offset + 该文本文件没有偏移量 + + + None + + + + <small><i>recommended regex:</small></i> + + + + The format in which to parse timestamps in the text file (e.g. %H:%M:%S.). Format specifiers are based on strptime(3) + + + + <html><head/><body><p>The format in which to parse timestamps in the text file (e.g. %H:%M:%S.%f).</p><p>Format specifiers are based on strptime(3) with the addition of %f for second fractions. The precision of %f is determined from its length.</p></body></html> + + + + %H:%M:%S.%f + + + + timestampExampleLabel + + + + Encapsulation Type: + 封装类型: + + + Encapsulation type of the frames in the import capture file + 导入捕获文件中帧的封装类型 + + + Prefix each frame with an Ethernet and IP header + + + + IP + + + + Prefix each frame with an Ethernet, IP and UDP header + + + + Prefix each frame with an Ethernet, IP and TCP header + + + + Prefix each frame with an Ethernet, IP and SCTP header + + + + Prefix each frame with an Ethernet, IP and SCTP (DATA) header + + + + Source address: + + + + Destination address: + + + + Dissector + + + + The IP protocol ID for each frame + + + + The IP source address for each frame + + + + The IP destination address for each frame + + + + The UDP, TCP or SCTP source port for each frame + 每一帧的 UDP、TCP 或 SCTP 源端口 + + + The SCTP DATA payload protocol identifier for each frame + 每一帧的 SCTP DATA 有效载荷协议标识符 + + + The UDP, TCP or SCTP destination port for each frame + 每一帧的 UDP、TCP 或 SCTP 目标端口 + + + Prefix each frame with an Ethernet header + 每一帧前添加以太网头部 + + + Ethernet + 以太网 + + + SCTP + SCTP + + + PPI: + PPI: + + + Protocol (dec): + 协议 (十进制): + + + Leave frames unchanged + 保持帧不变化 + + + No dummy header + 无虚头部 + + + Tag: + 标签: + + + UDP + UDP + + + Source port: + 源端口: + + + The Ethertype value of each frame + 每一帧的以太类型值 + + + TCP + TCP + + + The SCTP verification tag for each frame + 每一帧的 SCTP 验证标签 + + + Destination port: + 目的端口: + + + Ethertype (hex): + 以太类型 (十六进制): + + + SCTP (Data) + SCTP (Data) + + + The dissector to use for each frame + + + + The IP Version to use for the dummy IP header + + + + The maximum size of the frames to write to the import capture file (max 256kiB) + 要写入导入捕获文件的帧的最大大小(最大 256 kiB) + + + Supported fields are data, dir, time, seqno + + + + Missing capturing group data (use (? + + + + Import From Hex Dump + 从 Hex 转储文件导入 + + + Import + 导入 + + + Import Text File + 导入文本文件 + + + + InterfaceFrame + + Frame + + + + Wired + 有线 + + + AirPCAP + AirPCAP + + + Pipe + 管道 + + + STDIN + STDIN + + + Bluetooth + 蓝牙 + + + Wireless + 无线 + + + Dial-Up + 拨号 + + + USB + USB + + + External Capture + 外部捕获 + + + Virtual + 虚拟 + + + Remote interfaces + 远程接口 + + + Show hidden interfaces + 显示隐藏接口 + + + External capture interfaces disabled. + 已禁用外部捕获接口。 + + + <p>Local interfaces are unavailable because no packet capture driver is installed.</p><p>You can fix this by installing <a href="https://npcap.com/">Npcap</a>.</p> + + + + <p>Local interfaces are unavailable because the packet capture driver isn't loaded.</p><p>You can fix this by running <pre>net start npcap</pre> if you have Npcap installed or <pre>net start npf</pre> if you have WinPcap installed. Both commands must be run as Administrator.</p> + + + + <p>You don't have permission to capture on local interfaces.</p><p>You can fix this by <a href="file://%1">installing ChmodBPF</a>.</p> + + + + You don't have permission to capture on local interfaces. + + + + No interfaces found. + + + + Interfaces not loaded (due to preference). Go to Capture + + + + Start capture + + + + Hide Interface + + + + + InterfaceSortFilterModel + + No interfaces to be displayed. %1 interfaces hidden. + 没有要显示的接口。 %1 接口隐藏。 + + + + InterfaceToolbar + + Frame + + + + Select interface + 选择接口 + + + Interface + 接口 + + + + InterfaceToolbarLineEdit + + Apply changes + 应用更改 + + + + InterfaceTreeModel + + Show + 显示 + + + Friendly Name + 易记名称 + + + Interface Name + 接口名称 + + + No interfaces found. + + + + This version of Wireshark was built without packet capture support. + + + + Local Pipe Path + 局部管道路径 + + + Comment + 注释 + + + Link-Layer Header + 链路层标头 + + + Promiscuous + 混杂 + + + Snaplen (B) + 捕获长度 (B) + + + Buffer (MB) + 缓冲区 (MB) + + + Monitor Mode + 监控模式 + + + Capture Filter + 捕获过滤器 + + + Addresses + 地址 + + + Address + 地址 + + + Extcap interface: %1 + ExtCap 接口:%1 + + + No addresses + 无地址 + + + No capture filter + 无捕获过滤器 + + + Capture filter + 捕获过滤器 + + + + LBMLBTRMTransportDialog + + LBT-RM Transport Statistics + LBT-RM 传输统计 + + + Sources + + + + Address/Transport + 地址/端口 + + + Data frames + 数据帧 + + + Data bytes + 数据字节 + + + Data frames/bytes + 数据帧/字节 + + + Data rate + 数据速率 + + + RX data frames + RX 数据帧 + + + RX data bytes + RX 数据字节 + + + RX data frames/bytes + RX 数据帧/字节 + + + RX data rate + RX 数据速率 + + + NCF frames + NCF 帧 + + + NCF count + NCF 计数 + + + NCF bytes + NCF 字节 + + + NCF frames/bytes + NCF 数据帧/字节 + + + NCF count/bytes + NCF 计数/字节 + + + NCF frames/count + NCF 帧/计数 + + + NCF frames/count/bytes + NCF 帧/计数/字节 + + + NCF rate + NCF 速率 + + + SM frames + SM 帧 + + + SM bytes + SM 字节 + + + SM frames/bytes + SM 帧/字节 + + + SM rate + SM 速率 + + + Show + 显示 + + + Data + 数据 + + + RX Data + RX 数据 + + + NCF + Nak ConFirmation + NCF + + + SM + Session Message + SM + + + sequence numbers for transport + 传输层序列号 + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + 计数 + + + Frame + 帧帧 + + + SQN/Reason + SQN/原因 + + + Receivers + 接收者 + + + NAK frames + NAK 帧 + + + NAK count + NAK 计数 + + + NAK bytes + NAK 字节 + + + NAK rate + NAK 速率 + + + NAK sequence numbers for transport + NAK 传输层序列号 + + + Display filter: + 显示过滤器: + + + Regenerate statistics using this display filter + 使用这个显示过滤器重新生成统计 + + + Apply + 应用 + + + Copy as CSV + 复制为 CSV + + + Copy the tree as CSV + 复制树为 CSV + + + Copy as YAML + 复制为YAML + + + Copy the tree as YAML + 复制树为YAML + + + Show the data frames column + 显示"数据帧"列 + + + Show the data bytes column + 显示"数据字节"列 + + + Show the data frames/bytes column + 显示"数据帧/字节"列 + + + Show the RX data frames column + 显示"RX数据帧"列 + + + Show the RX data bytes column + 显示"RX数据字节"列 + + + Show the RX data frames/bytes column + 显示"RX数据帧/字节"列 + + + Show the NCF frames column + 显示"NCF帧"列 + + + Show the NCF bytes column + 显示"NCF字节"列 + + + Show the NCF count column + 显示"NCF计数"列 + + + Show the data rate column + 显示"数据速率"列 + + + Show the RX data rate column + 显示"RX数据速率"列 + + + Show the NCF frames/bytes column + 显示"NCF帧/字节"列 + + + Show the NCF count/bytes column + 显示"NCF计数/字节"列 + + + Show the NCF frames/count column + 显示"NCF帧/计数"列 + + + Show the NCF frames/count/bytes column + 显示"NCF帧/计数/字节"列 + + + Show the NCF rate column + 显示"NCF速率"列 + + + Show the SM frames column + 显示"SM帧"列 + + + Show the SM bytes column + 显示"SM字节"列 + + + Show the SM frames/bytes column + 显示"SM帧/字节"列 + + + Show the SM rate column + 显示"SM速率"列 + + + Auto-resize columns to content + 根据内容自动调整列宽 + + + Resize columns to content size + 调整列宽以适应内容 + + + LBT-RM Statistics failed to attach to tap + LBT-RM统计附加到 tap 失败 + + + + LBMLBTRUTransportDialog + + LBT-RU Transport Statistics + LBT-RU 传输统计 + + + Sources + + + + Address/Transport/Client + 地址/端口/客户端 + + + Data frames + 数据帧 + + + Data bytes + 数据字节 + + + Data frames/bytes + 数据 帧/字节 + + + Data rate + 数据速率 + + + RX data frames + RX 数据帧 + + + RX data bytes + RX 数据字节 + + + RX data frames/bytes + RX 数据帧/字节 + + + RX data rate + RX 数据速率 + + + NCF frames + NCF 帧 + + + NCF count + NCF 计数 + + + NCF bytes + NCF 字节 + + + NCF frames/count + NCF 帧/计数 + + + NCF frames/bytes + NCF 帧/字节 + + + NCF count/bytes + NCF 计数/字节 + + + NCF frames/count/bytes + NCF 帧/计数/字节 + + + NCF rate + NCF 速率 + + + SM frames + SM帧 + + + SM bytes + SM字节 + + + SM frames/bytes + SM帧/字节 + + + SM rate + SM速率 + + + RST frames + RST 帧 + + + RST bytes + RST 字节 + + + RST frames/bytes + RST 帧/字节 + + + RST rate + RST 速率 + + + Show + 显示 + + + Data SQN + 数据 SQN + + + RX Data SQN + RX 数据 SQN + + + NCF SQN + NCF SQN + + + SM SQN + SM SQN + + + RST reason + RST 原因 + + + details for transport + 传输层详情 + + + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + XXXXX:XXX.XXX.XXX.XXX:XXXXX:XXXXXXXX:XXX.XXX.XXX.XXX:XXXXX + + + SQN + SQN + + + Count + 计数 + + + Frame + + + + Reason + 原因 + + + SQN/Reason + SQN/原因 + + + Receivers + 接收者 + + + Address/Transport + 地址/端口 + + + NAK frames + NAK 帧 + + + NAK count + NAK 计数 + + + NAK bytes + NAK 字节 + + + NAK frames/count + NAK 帧/计数 + + + NAK count/bytes + NAK 计数/字节 + + + NAK frames/bytes + NAK 帧/字节 + + + NAK frames/count/bytes + NAK 帧/计数/字节 + + + NAK rate + NAK 速率 + + + ACK frames + ACK 帧 + + + ACK bytes + ACK 字节 + + + ACK frames/bytes + ACK 帧/字节 + + + ACK rate + ACK 速率 + + + CREQ frames + CREQ 帧 + + + CREQ bytes + CREQ 字节 + + + CREQ frames/bytes + CREQ 帧/字节 + + + CREQ rate + CREQ 速率 + + + NAK SQN + NAK SQN + + + ACK SQN + ACK SQN + + + CREQ request + CREQ 请求 + + + Display filter: + 显示过滤器: + + + Regenerate statistics using this display filter + 使用这个显示过滤器重新生成统计 + + + Apply + 应用 + + + Copy as CSV + 复制为 CSV + + + Copy the tree as CSV + 复制树为 CSV + + + Copy as YAML + 复制为YAML + + + Copy the tree as YAML + 复制树为YAML + + + Show the data frames column + 显示"数据帧"列 + + + Show the data bytes column + 显示"数据字节"列 + + + Show the data frames/bytes column + 显示"数据帧/字节"列 + + + Show the data rate column + 显示"数据速率"列 + + + Show the RX data frames column + 显示"RX数据帧"列 + + + Show the RX data bytes column + 显示"RX数据字节"列 + + + Show the RX data frames/bytes column + 显示"RX数据帧/字节"列 + + + Show the RX data rate column + 显示"RX数据速率"列 + + + Show the NCF frames column + 显示"NCF帧"列 + + + Show the NCF count column + 显示"NCF计数"列 + + + Show the NCF bytes column + 显示"NCF字节"列 + + + Show the NCF frames/bytes column + 显示"NCF帧/字节"列 + + + Show the NCF count/bytes column + 显示"NCF计数/字节"列 + + + Show the NCF frames/count column + 显示"NCF帧/计数"列 + + + Show the NCF frames/count/bytes column + 显示"NCF帧/计数/字节"列 + + + Show the SM frames column + 显示"SM帧"列 + + + Show the SM bytes column + 显示"SM字节"列 + + + Show the SM frames/bytes column + 显示"SM帧/字节"列 + + + Show the SM rate column + 显示"SM速率"列 + + + Show the RST frames column + 显示"RST帧"列 + + + Show the RST bytes column + 显示"RST字节"列 + + + Show the RST frames/bytes column + 显示"RST帧/字节"列 + + + Show the RST rate column + 显示"RST速率"列 + + + Show the NAK frames column + 显示"NAK帧"列 + + + Show the NAK count column + 显示"NAK计数"列 + + + Show the NAK bytes column + 显示"NAK字节"列 + + + Show the NAK frames/count column + 显示"NAK帧/计数"列 + + + Show the NAK count/bytes column + 显示"NAK计数/字节"列 + + + Show the NAK frames/bytes column + 显示"NAK帧/字节"列 + + + Show the NAK frames/count/bytes column + 显示"NAK帧/计数/字节"列 + + + Show the NAK rate column + 显示"NAK速率"列 + + + Show the ACK frames column + 显示"ACK帧"列 + + + Show the ACK bytes column + 显示"ACK字节"列 + + + Show the ACK frames/bytes column + 显示"ACK帧/字节"列 + + + Show the ACK rate column + 显示"ACK速率"列 + + + Show the CREQ frames column + 显示"CREQ帧"列 + + + Show the CREQ bytes column + 显示"CREQ字节"列 + + + Show the CREQ frames/bytes column + 显示"CREQ帧/字节"列 + + + Show the CREQ rate column + 显示"CREQ速率"列 + + + Auto-resize columns to content + 根据内容自动调整列宽 + + + Resize columns to content size + 调整列宽以适应内容 + + + Show the NCF rate column + 显示"NCF速率"列 + + + LBT-RU Statistics failed to attach to tap + LBT-RU 统计附加到 tap 失败 + + + + LBMStreamDialog + + Dialog + 对话框 + + + Stream + + + + Endpoint A + 端点A + + + Endpoint B + 端点B + + + Messages + 消息 + + + Bytes + 字节 + + + First Frame + 第一个帧 + + + Last Frame + 最后一帧 + + + Display filter: + 显示过滤器: + + + Regenerate statistics using this display filter + 使用这个显示过滤器重新生成统计 + + + Apply + 应用 + + + Copy as CSV + 复制为 CSV + + + Copy the tree as CSV + 复制树为 CSV + + + Copy as YAML + 复制为YAML + + + Copy the tree as YAML + 复制树为YAML + + + LBM Stream failed to attach to tap + LBM 流附加到 tap 失败 + + + + LBMUIMFlowDialog + + %Ln node(s) + + %Ln node + + + + %Ln item(s) + + %Ln item + + + + + LayoutPreferencesFrame + + Frame + + + + Pane 1: + 窗格 1: + + + Packet List + 分组列表 + + + Packet Details + 分组详情 + + + Packet Bytes + 分组字节流 + + + Packet Diagram + + + + None + + + + Pane 2: + 窗格 2: + + + Pane 3: + 窗格 2: + + + Packet List settings: + 分组列表设置: + + + Show packet separator + 显示分组分隔符 + + + Show column definition in column context menu + + + + Allow the list to be sorted + + + + Maximum number of cached rows (affects sorting) + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + Enable mouse-over colorization + + + + Status Bar settings: + 状态栏设置: + + + Show selected packet number + 显示所选分组编号 + + + Show file load time + 显示文件加载时间 + + + + LteMacStatisticsDialog + + LTE Mac Statistics + LTE MAC 统计 + + + Include SR frames in filter + 过滤器中包括 SR 帧 + + + Include RACH frames in filter + 过滤器中包括 RACH 帧 + + + MAC Statistics + MAC 统计 + + + + LteRlcGraphDialog + + Dialog + 对话框 + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>有价值且惊人的节省时间的快捷键</h3> +<table><tbody> + +<tr><th>+</th><td>放大</td></th> +<tr><th>-</th><td>缩小</td></th> +<tr><th>0</th><td>复位图形到初始状态</td></th> + +<tr><th>→</th><td>右移10个像素</td></th> +<tr><th>←</th><td>左移10个像素</td></th> +<tr><th>↑</th><td>上移10个像素</td></th> +<tr><th>↓</th><td>下移10个像素</td></th> +<tr><th><i>Shift+</i>→</th><td>右移1个像素</td></th> +<tr><th><i>Shift+</i>←</th><td>左移1个像素</td></th> +<tr><th><i>Shift+</i>↑</th><td>上移1个像素</td></th> +<tr><th><i>Shift+</i>↓</th><td>下移1个像素</td></th> + +<tr><th>g</th><td>跳转到光标所在分组</td></th> + +<tr><th>z</th><td>切换鼠标拖拽/缩放</td></th> +<tr><th>t</th><td>切换捕获/会话时间起源</td></th> +<tr><th>Space</th><td>切换十字线</td></th> + +</tbody></table> +</body></html> + + + Mouse + 鼠标 + + + Drag using the mouse button. + 使用鼠标按键进行拖拽。 + + + drags + 拖拽 + + + Select using the mouse button. + 使用鼠标按键进行选择。 + + + zooms + 缩放 + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>复位图形到初始状态。</p></body></html> + + + Reset + 复位 + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>切换连接方向(查看相反的流程)。</p></body></html> + + + Switch Direction + 切换方向 + + + Reset Graph + 复位图形 + + + Reset the graph to its initial state. + 复位图形到初始状态。 + + + 0 + 0 + + + Zoom In + 放大 + + + + + + + + + Zoom Out + 缩小 + + + - + - + + + Move Up 10 Pixels + 上移10像素 + + + Up + 上键 + + + Move Left 10 Pixels + 左移10像素 + + + Left + 左方向键 + + + Move Right 10 Pixels + 右移10像素 + + + Right + 右方向键 + + + Move Down 10 Pixels + 下移10像素 + + + Down + 下方向键 + + + Move Up 1 Pixel + 上移1像素 + + + Shift+Up + Shift+上方向键 + + + Move Left 1 Pixel + 左移1像素 + + + Shift+Left + Shift+左方向键 + + + Move Right 1 Pixel + 右移1像素 + + + Shift+Right + Shift+右方向键 + + + Move Down 1 Pixel + 下移1像素 + + + Move down 1 Pixel + 下移1像素 + + + Shift+Down + Shift+下方向键 + + + Drag / Zoom + 拖拽/缩放 + + + Toggle mouse drag / zoom behavior + 切换鼠标拖拽/缩放行为 + + + Z + Z + + + Crosshairs + 十字线 + + + Toggle crosshairs + 切换十字线 + + + Space + 空格键 + + + Move Up 100 Pixels + 上移100像素 + + + PgUp + PgUp + + + PgDown + PgDown + + + Go To Packet Under Cursor + 跳转到光标所在的分组 + + + Go to packet currently under the cursor + 跳转到光标当前所在的分组 + + + G + G + + + Zoom In X Axis + 放大 X 轴 + + + X + X + + + Zoom Out Y Axis + 缩小 Y 轴 + + + Shift+Y + Shift+Y + + + Zoom In Y Axis + 放大 Y 轴 + + + Y + Y + + + Zoom Out X Axis + 缩小 X 轴 + + + Shift+X + Shift+X + + + Switch direction (swap between UL and DL) + 切换方向(UL 和 DL 之间的交换) + + + D + D + + + Time + 时间 + + + Sequence Number + 序列号 + + + LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5) + LTE RLC 图表 (UE=%1 chan=%2%3 %4 - %5) + + + LTE RLC Graph - no channel selected + LTE RLC 图表 - 没有选择信道 + + + Save As… + + + + %1 %2 (%3s seq %4 len %5) + %1 %2 (%3s seq %4 len %5) + + + Click to select packet + 点击选取分组 + + + Packet + 分组 + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 释放缩放, x = %1 到 %2, y = %3 到 %4 + + + Unable to select range. + 无法选择范围。 + + + Click to select a portion of the graph. + 点击选择图形的一部分。 + + + Portable Document Format (*.pdf) + 便携式文档格式 (*.pdf) + + + Portable Network Graphics (*.png) + 便携式网络图形 (*.png) + + + Windows Bitmap (*.bmp) + Windows 位图 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 文件交换格式 (*.jpeg *.jpg) + + + Save Graph As… + + + + + LteRlcStatisticsDialog + + LTE RLC Statistics + LTE RLC 统计 + + + Include SR frames in filter + 过滤器中包括 SR 帧 + + + Include RACH frames in filter + 过滤器中包括 RACH 帧 + + + Use RLC frames only from MAC frames + 只使用来自 MAC 帧的 RLC 帧 + + + UL Frames + UL 帧 + + + UL Bytes + UL 字节 + + + UL MB/s + UL MB/s + + + UL ACKs + UL ACK + + + UL NACKs + UL NACK + + + UL Missing + UL 缺失 + + + DL Frames + DL 帧 + + + DL Bytes + DL 字节 + + + DL MB/s + DL MB/s + + + DL ACKs + DL ACK + + + DL NACKs + DL NACK + + + DL Missing + DL 缺失 + + + RLC Statistics + RLC 统计 + + + + MainStatusBar + + Ready to load or capture + 已准备好加载或捕获 + + + Ready to load file + 已准备好加载文件 + + + Open the Capture File Properties dialog + 打开捕获文件属性对话框 + + + Profile: %1 + 配置:%1 + + + Manage Profiles… + + + + New… + + + + Edit… + + + + Import + 导入 + + + Export + 导出 + + + Delete + 删除 + + + Switch to + 切换到 + + + is the highest expert information level + is the highest expert info level + 为最高专家信息级别 + + + ERROR + 错误 + + + WARNING + 警告 + + + NOTE + 通知 + + + CHAT + 会话 + + + No expert information + No expert info + 无专家信息 + + + %Ln byte(s) + , %1 bytes + + + + + + Byte %1 + 字节 %1 + + + Bytes %1-%2 + 字节 %1-%2 + + + Selected Packet: %1 %2 + 所选分组:%1 %2 + + + Packets: %1 %4 Displayed: %2 (%3%) + Packets: %1 %4 Displayed: %2 %4 Marked: %3 + 分组: %1 %4 已显示: %2 (%3%) + + + %1 Selected: %2 (%3%) + + + + %1 Marked: %2 (%3%) + %1 Dropped: %2 + %1 已标记: %2 (%3%) + + + %1 Dropped: %2 (%3%) + %1 已丢弃: %2 (%3%) + + + %1 Ignored: %2 (%3%) + %1 已忽略: %2 (%3%) + + + %1 Comments: %2 + + + + %1 Load time: %2:%3.%4 + %1 加载时间: %2:%3.%4 + + + No Packets + 无分组 + + + From Zip File... + + + + From Directory... + + + + Selected Personal Profile... + + + + All Personal Profiles... + + + + Packets: %1 + 分组: %1 + + + + MainWelcome + + %n interface(s) shown, %1 hidden + %Ln interface(s) shown + + %n interface shown, %1 hidden + + + + + MainWindowPreferencesFrame + + Frame + + + + Checking this will save the size, position, and maximized state of the main window. + 选中此项将保持主窗口的大小、位置及最大化状态。 + + + Remember main window size and placement + 记住主窗口的大小及位置 + + + Open files in + 打开文件夹中的文件 + + + This folder: + 该文件夹: + + + Browse… + Browse... + 浏览… + + + The most recently used folder + 最近使用的文件夹 + + + Show up to + 显示最多 + + + filter entries + 过滤器项目 + + + recent files + 个最近文件 + + + Confirm unsaved capture files + 确认未保存的捕获文件 + + + Display autocompletion for filter text + 显示过滤器文本的自动完成 + + + Main toolbar style: + 主工具栏样式: + + + Icons only + 只有图标 + + + Text only + 只有文本 + + + Icons & Text + 图标和文本 + + + Window title + + + + <html><head/><body><p>Custom window title to be appended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + Prepend window title + + + + <html><head/><body><p>Custom window title to be prepended to the existing title<br/>%F = file path of the capture file<br/>%P = profile name<br/>%S = a conditional separator (&quot; - &quot;) that only shows when surrounded by variables with values or static text<br/>%V = version info</p></body></html> + + + + Language: + 语言: + + + Use system setting + 使用系统设置 + + + Open Files In + 打开文件夹中的文件 + + + + ManageInterfacesDialog + + Manage Interfaces + 管理接口 + + + <html><head/><body><p>Click the checkbox to hide or show a hidden interface.</p></body></html> + <html><head/><body><p>点击复选框来隐藏或者显示隐藏接口。</p></body></html> + + + Local Interfaces + 本地接口 + + + Show + 显示 + + + <html><head/><body><p>Add a pipe to capture from or remove an existing pipe from the list.</p></body></html> + <html><head/><body><p></p></body></html> + + + Pipes + 管道 + + + <html><head/><body><p>Add a new pipe using default settings.</p></body></html> + <html><head/><body><p>使用默认设置来添加新管道</p></body></html> + + + <html><head/><body><p>Remove the selected pipe from the list.</p></body></html> + <html><head/><body><p>从列表中移除选中的管道。</p></body></html> + + + Remote Interfaces + 远程接口 + + + Host / Device URL + 主机/设备 URL + + + <html><head/><body><p>Add a remote host and its interfaces</p></body></html> + <html><head/><body><p>添加远程主机和它的接口</p></body></html> + + + <html><head/><body><p>Remove the selected host from the list.</p></body></html> + <html><head/><body><p>从列表中删除选择的主机。</p></body></html> + + + Remote Settings + 远程设置 + + + <small><i></i></small> + <small><i></i></small> + + + This version of Wireshark does not save pipe settings. + 这个版本的 Wireshark 无法保存管道设置。 + + + This version of Wireshark does not save remote settings. + 这个版本的 Wireshark 无法保存远端设置。 + + + This version of Wireshark does not support remote interfaces. + 这个版本的 Wireshark 不支持远端接口。 + + + New Pipe + 新建管道 + + + + ManufDialog + + MAC Address Blocks + + + + Search MAC address or address prefix. Special purpose bits are masked. + + + + MAC Address + + + + Search vendor name using a case-insentitive regular expression. + + + + Vendor Name + + + + Show short name column. + + + + Short name + + + + Select all + + + + Copy + 复制 + + + Find + 查找 + + + Clear + + + + + ManufTableModel + + Address Block + + + + Short Name + 简称 + + + Vendor Name + + + + + ModulePreferencesScrollArea + + ScrollArea + 滚动区 + + + + Mtp3SummaryDialog + + Dialog + 对话框 + + + MTP3 Summary + MTP3 汇总 + + + File + 文件 + + + Name + 名称 + + + Length + 长度 + + + Format + 格式 + + + Snapshot length + 快照长度 + + + Data + 数据 + + + First packet + 首个分组 + + + Last packet + 最后分组 + + + Elapsed + 经过时间 + + + Packets + 分组 + + + Service Indicator (SI) Totals + 服务标识器 (SI) 总计 + + + SI + SI + + + MSUs + MSU + + + MSUs/s + MSU/s + + + Bytes + 字节 + + + Bytes/MSU + 字节/MSU + + + Bytes/s + 字节/s + + + Totals + 总计 + + + Total MSUs + 总计 MSU + + + Total Bytes + 总计字节 + + + Average Bytes/MSU + 平均 字节/MSU + + + Average Bytes/s + 平均 字节/s + + + + MulticastStatisticsDialog + + UDP Multicast Streams + UDP 多播流 + + + Source Address + 源地址 + + + Source Port + 源端口 + + + Destination Address + 目的地址 + + + Destination Port + 目的端口 + + + Packets + 分组 + + + Packets/s + 包/秒 + + + Avg BW (bps) + 平均带宽 (bps) + + + Max BW (bps) + 最大带宽 (bps) + + + Max Burst + 最大突发 + + + Burst Alarms + 突发警报 + + + Max Buffers (B) + 最大缓冲区 (B) + + + Buffer Alarms + 缓冲区警报 + + + Burst measurement interval (ms): + 突发测量间隔 (ms): + + + Burst alarm threshold (packets): + 突发警报门槛 (包): + + + Buffer alarm threshold (B): + 突发警报门槛 (B): + + + Stream empty speed (Kb/s): + Stream empty speed (Kb/s: + 流清空速度 (Kb/s: + + + Total empty speed (Kb/s): + Total empty speed (Kb/s: + 总清空速度 (Kb/s: + + + The burst interval must be between 1 and 1000. + 突发间隔必须介于 1 与 1000 之间。 + + + The burst alarm threshold isn't valid. + 突发警报门槛无效。 + + + The buffer alarm threshold isn't valid. + 缓冲区警报门槛无效。 + + + The stream empty speed should be between 1 and 10000000. + 流清空速度应该介于 1 与 10000000 之间。 + + + The total empty speed should be between 1 and 10000000. + 总清空速度应该介于 1 与 10000000 之间。 + + + %1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B + %1 流,平均带宽: %2bps,最大带宽: %3bps,最大突发: %4 / %5ms,最大缓冲区: %6B + + + + PacketCommentDialog + + Edit Packet Comment + + + + Add Packet Comment + + + + + PacketDiagram + + Packet diagram + + + + Show Field Values + + + + Save Diagram As… + + + + Copy as Raster Image + + + + …as SVG + + + + Portable Network Graphics (*.png) + + + + Windows Bitmap (*.bmp) + Windows 位图 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + + + + Scalable Vector Graphics (*.svg) + + + + Save Graph As… + + + + + PacketDialog + + Dialog + 对话框 + + + <small><i></i></small> + <small><i></i></small> + + + Show packet bytes + + + + Packet %1 + 分组 %1 + + + [%1 closed] + [%1 已关闭] + + + Byte %1 + 字节 %1 + + + Bytes %1-%2 + 字节 %1-%2 + + + %Ln byte(s) + + + + + + + PacketFormatGroupBox + + GroupBox + 分组框 + + + Packet Format + 分组格式 + + + <html><head/><body><p>Packet summary lines similar to the packet list</p></body></html> + <html><head/><body><p>分组概要行,与分组列表类似</p></body></html> + + + Summary line + 概要行 + + + Include column headings + 包括列标题 + + + <html><head/><body><p>Packet details similar to the protocol tree</p></body></html> + <html><head/><body><p>分组详情,与协议树类似</p></body></html> + + + Details: + 详情: + + + <html><head/><body><p>Export only top-level packet detail items</p></body></html> + <html><head/><body><p>只导出最高层分组详情项目</p></body></html> + + + All co&llapsed + 全部收起(&L) + + + <html><head/><body><p>Expand and collapse packet details as they are currently displayed.</p></body></html> + <html><head/><body><p>按当前显示状态展开与收起分组报文详情。</p></body></html> + + + As displa&yed + 按当前显示(&Y) + + + <html><head/><body><p>Export all packet detail items</p></body></html> + <html><head/><body><p>导出所有分组详情项目</p></body></html> + + + All e&xpanded + 全部展开(&X) + + + <html><head/><body><p>Export a hexdump of the packet data similar to the packet bytes view</p></body></html> + <html><head/><body><p>导出分组数据的十六进制转储,类似于分组字节视图</p></body></html> + + + Bytes + 字节流 + + + Include secondary data sources + + + + <html><head/><body><p>Generate hexdumps for secondary data sources like reassembled or decrypted buffers in addition to the frame</p></body></html> + + + + + PacketList + + Protocol Preferences + 协议首选项 + + + Summary as Text + 摘要为文本 + + + …as CSV + + + + …as YAML + + + + Decode As… + + + + Frame %1: %2 + + + 分组 %1: %2 + + + + [ Comment text exceeds %1. Stopping. ] + [ 注释文本超过 %1。正在停止。 ] + + + + PacketListHeader + + Align Left + + + + Align Center + + + + Align Right + + + + Edit Column + + + + Resize to Contents + + + + Column Preferences… + 列首选项... + + + Resize Column to Width… + + + + Resolve Names + + + + Remove this Column + + + + Column %1 + + + + Width: + + + + + PacketListModel + + Column + + + + %1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences + + + + Sorting "%1"… + + + + Sorting … + + + + + PacketRangeGroupBox + + Form + 表单 + + + Packet Range + 分组范围 + + + - + - + + + Displayed + 已显示 + + + &Marked packets only + 仅已标记分组(&M) + + + &Range: + 范围(&R): + + + Remove &ignored packets + 移除已忽略分组(&I) + + + Include &depended upon packets + + + + Also include packets depended upon, such as those used to reassemble displayed packets + + + + First &to last marked + 第一个到标记的最后一个(&T) + + + &All packets + 所有分组(&A) + + + &Selected packets only + 仅选中分组(&S) + + + Captured + 已捕获 + + + + PathSelectionDelegate + + Open a pipe + + + + + PathSelectionEdit + + Browse + + + + Select a path + + + + + PluginListModel + + Name + 名称 + + + Version + 版本 + + + Type + 类型 + + + Path + 路径 + + + + PortsModel + + All entries + + + + tcp + + + + udp + + + + sctp + + + + dccp + + + + Name + 名称 + + + Port + + + + Type + 类型 + + + + PreferenceEditorFrame + + Frame + + + + … + + + + a preference + 一个首选项 + + + Browse… + 浏览… + + + Open %1 preferences… + 打开 %1 首选项… + + + Invalid value. + 无效值。 + + + + PreferencesDialog + + Search: + 搜索: + + + Checking this will show only changed preferences. + + + + Show changed values + + + + Preferences + 首选项 + + + + PrefsModel + + Advanced + 高级 + + + Appearance + 外观 + + + Layout + 布局 + + + Columns + + + + Font and Colors + 字体和颜色 + + + Capture + 捕获 + + + Expert + 专家 + + + Filter Buttons + 过滤器按钮 + + + RSA Keys + RSA 密钥 + + + + PrintDialog + + Packet Format + 分组格式 + + + Print each packet on a new page + 每个分组另起一页打印 + + + <html><head/><body><p>Print capture file information on each page</p></body></html> + + + + Capture information header + + + + <html><head/><body><p>Use the &quot;+&quot; and &quot;-&quot; keys to zoom the preview in and out. Use the &quot;0&quot; key to reset the zoom level.</p></body></html> + <html><head/><body><p>使用“+”和“-”键可放大与缩小预览。用“0”键可重置缩放级别。</p></body></html> + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ and - zoom, 0 resets</span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">+ 与 - 缩放,0 重置</span></p></body></html> + + + Packet Range + 分组范围 + + + Print + 打印 + + + &Print… + 打印(&P)… + + + Page &Setup… + + + + %1 %2 total packets, %3 shown + %1 %2 总分组数,%3 已显示 + + + Print Error + 打印错误 + + + Unable to print to %1. + 无法打印至 %1。 + + + + ProfileDialog + + Search for profile … + + + + Create a new profile using default settings. + 用默认设置创建新的配置文件。 + + + <html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be reset upon deletion.</p></body></html> + + + + Copy this profile. + 复制该配置文件。 + + + Configuration Profiles + 配置文件 + + + Import + noun + 导入 + + + Export + noun + 导出 + + + From Zip File... + + + + From Directory... + + + + %Ln Selected Personal Profile(s)... + + + + + + All Personal Profiles... + + + + New profile + + + + Profile Error + 配置文件错误 + + + Exporting profiles + + + + No profiles found for export + + + + Select zip file for export + + + + An import of profiles is not allowed, while changes are pending + + + + An import is pending to be saved. Additional imports are not allowed + + + + An export of profiles is only allowed for personal profiles + + + + An export of profiles is not allowed, while changes are pending + + + + %Ln profile(s) exported + + + + + + Select zip file for import + + + + Select directory for import + + + + Zip File (*.zip) + + + + Error + 错误 + + + An error has occurred while exporting profiles + + + + No profiles found for import in %1 + + + + %Ln profile(s) imported + + + + + + , %Ln profile(s) skipped + + + + + + Importing profiles + + + + %Ln profile(s) selected + + %Ln profile selected + + + + + ProfileModel + + Resetting to default + + + + Imported profile + + + + This is a system provided profile + + + + A profile change for this name is pending + + + + (See: %1) + + + + This is an invalid profile definition + + + + A profile already exists with this name + + + + A profile with this name is being deleted + + + + Created from default settings + + + + system provided + + + + deleted + + + + copy + noun + + + + Exporting profiles while changes are pending is not allowed + + + + No profiles found to export + + + + Can't delete profile directory + + + + A profile name cannot contain the following characters: %1 + + + + A profile name cannot contain the '/' character + + + + A profile cannot start or end with a period (.) + + + + Default + 默认 + + + Global + + + + Personal + + + + Renamed from: %1 + + + + Copied from: %1 + + + + renamed to %1 + + + + Profile + + + + Type + 类型 + + + + ProfileSortModel + + All profiles + + + + Personal profiles + + + + Global profiles + + + + + ProgressFrame + + Frame + + + + Loading + 正在载入 + + + + ProtoTree + + Packet details + 分组详情 + + + Not a field or protocol + + + + No field reference available for text labels. + + + + Expand Subtrees + + + + Collapse Subtrees + 折叠子树 + + + Expand All + 全部展开 + + + Collapse All + 全部折叠 + + + Copy + 复制 + + + All Visible Items + 所有可见项目 + + + All Visible Selected Tree Items + 选中树的所有可见项目 + + + Description + 描述 + + + Field Name + 字段名称 + + + Value + + + + As Filter + 作为过滤器 + + + Wiki Protocol Page + Wiki 协议页面 + + + Filter Field Reference + 过滤器字段参考 + + + Copied + + + + Wiki Page for %1 + %1 的 Wiki 页面 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Wireshark Wiki 由社区进行维护。</p><p>您即将加载的页面可能是良好的、不完整的、有错误的,甚至不存在的。</p><p>继续访问该 Wiki?</p> + + + Colorize with Filter + 用过滤器着色 + + + + ProtocolHierarchyDialog + + Dialog + 对话框 + + + Protocol + 协议 + + + Percent Packets + 按分组百分比 + + + Packets + 分组 + + + Percent Bytes + 按字节百分比 + + + Bytes + 字节 + + + Bits/s + 比特/秒 + + + End Packets + 结束 分组 + + + End Bytes + 结束 字节 + + + End Bits/s + 结束 位/秒 + + + PDUs + + + + <small><i>A hint.</i></small> + <small><i>提示</i></small> + + + Copy as CSV + 复制为 CSV + + + Copy stream list as CSV. + 复制流列表为 CSV。 + + + Copy as YAML + 复制为YAML + + + Copy stream list as YAML. + 复制流列表为YAML。 + + + Copy short names + + + + Copy short protocol names in use. + + + + Disable unused protocols + + + + Disable all protocols but those listed. + + + + Re-enable unused protocols + + + + Re-enable protocols that were disabled in this dialog. + + + + Protocol Hierarchy Statistics + 协议分级统计 + + + Copy + 复制 + + + as CSV + 作为 CSV + + + as YAML + 作为YAML + + + protocol short names + + + + Protocols + + + + Disable unused + + + + Revert changes + + + + No display filter. + 无显示过滤器。 + + + Display filter: %1 + 显示过滤器: %1 + + + Unused protocols have been disabled. + + + + Protocol changes have been reverted. + + + + + ProtocolPreferencesMenu + + Protocol Preferences + 协议首选项 + + + No protocol preferences available + 没有可用的协议首选项 + + + Disable %1 + 禁用 %1 + + + %1 has no preferences + %1 没有首选项 + + + Open %1 preferences… + 打开 %1 首选项… + + + + QObject + + Average Throughput (bits/s) + 平均吞吐量 (比特/秒) + + + Round Trip Time (ms) + 往返时间 (毫秒) + + + Segment Length (B) + 段长度 (B) + + + Sequence Number (B) + 序列号 (B) + + + Time (s) + 时间 (秒) + + + Window Size (B) + 窗口大小 (B) + + + [no capture file] + [无捕获文件] + + + Conversation + 对话 + + + Bars show the relative timeline for each conversation. + 条形图显示每个对话的相对时间线。 + + + Endpoint + 端点 + + + Apply as Filter + 作为过滤器应用 + + + Prepare as Filter + 准备作为过滤器 + + + Find + 查找 + + + Colorize + 着色 + + + Look Up + 查找 + + + Copy + 复制 + + + UNKNOWN + 未知 + + + Selected + 选中 + + + Not Selected + 非选中 + + + …and Selected + ...且选中 + + + …or Selected + ...或选中 + + + …and not Selected + ...且不选中 + + + …or not Selected + ...或不选中 + + + A + A + + + B + B + + + Any + 任何 + + + Don't show this message again. + 不再显示此消息。 + + + Multiple problems found + 发现多个问题 + + + %1 (%L2%) + %1 (%L2%) + + + No entries. + 没有项目。 + + + %1 entries. + %1 项。 + + + Base station + 基础设施 + + + <Broadcast> + <广播> + + + <Hidden> + <隐藏> + + + BSSID + BSSID + + + Beacons + 信标 + + + Data Pkts + 数据分组 + + + Protection + 保护 + + + Address + 地址 + + + Pkts Sent + 已发分组 + + + Pkts Received + 已收分组 + + + Comment + 注释 + + + Wrong sequence number + 错误的序列号 + + + Payload changed to PT=%1 + 载荷更改为 PT=%1 + + + Incorrect timestamp + 不正确的时间戳 + + + Marker missing? + 标记缺失? + + + C-RNTI + C-RNTI + + + SPS-RNTI + SPS-RNTI + + + RNTI + RNTI + + + Type + 类型 + + + UEId + UEId + + + UL Frames + UL 帧 + + + UL Bytes + UL 字节 + + + UL MB/s + UL MB/s + + + UL Padding % + UL 填充 % + + + UL Re TX + UL Re TX + + + DL Frames + DL 帧 + + + DL Bytes + DL 字节 + + + DL MB/s + DL MB/s + + + DL Padding % + DL 填充 % + + + DL CRC Failed + DL CRC 失败 + + + DL ReTX + DL ReTX + + + LCID 1 + LCID 1 + + + LCID 2 + LCID 2 + + + LCID 3 + LCID 3 + + + LCID 4 + LCID 4 + + + LCID 5 + LCID 5 + + + LCID 6 + LCID 6 + + + LCID 7 + LCID 7 + + + LCID 8 + LCID 8 + + + LCID 9 + LCID 9 + + + LCID 10 + LCID 10 + + + LCID 32 + LCID 32 + + + LCID 33 + LCID 33 + + + LCID 34 + LCID 34 + + + LCID 35 + LCID 35 + + + LCID 36 + LCID 36 + + + LCID 37 + LCID 37 + + + LCID 38 + LCID 38 + + + TM + TM + + + UM + UM + + + AM + AM + + + Predef + 预定义 + + + Unknown (%1) + 未知 (%1) + + + CCCH + CCCH + + + SRB-%1 + SRB-%1 + + + DRB-%1 + DRB-%1 + + + Unknown + 未知 + + + UE Id + UE ID + + + Name + 名称 + + + Mode + 模式 + + + Priority + 优先级 + + + default + 默认 + + + DLT %1 + DLT %1 + + + Invalid Display Filter + 无效显示过滤器 + + + The filter expression %1 isn't a valid display filter. (%2). + 过滤器表达式 %1 不是有效的显示过滤器 (%2)。 + + + Error + 错误 + + + No remote interfaces found. + 找不到远程接口。 + + + PCAP not found + 找不到 PCAP + + + Unknown error + 未知错误 + + + Default + 默认 + + + Changed + 已更改 + + + Has this preference been changed? + 此首选项是否已更改? + + + Default value is empty + 默认值为空 + + + Gap in dissection + + + + Edit… + + + + Browse… + 浏览… + + + + QObject::QObject + + CCCH + CCCH + + + + RemoteCaptureDialog + + Remote Interface + 远程接口 + + + Host: + 主机: + + + Port: + 端口: + + + Authentication + 认证 + + + Null authentication + 无认证 + + + Password authentication + 密码认证 + + + Username: + 用户名: + + + Password: + 密码: + + + Clear list + 清除列表 + + + Error + 错误 + + + No remote interfaces found. + 远程接口没有发现。 + + + PCAP not found + PCAP没有发现 + + + + RemoteSettingsDialog + + Remote Capture Settings + 远程捕获设置 + + + Capture Options + 捕获选项 + + + Do not capture own RPCAP traffic + 不捕获自身的RPCAP流量 + + + Use UDP for data transfer + 使用 UDP 进行数据传输 + + + Sampling Options + 采样选项 + + + None + + + + 1 of + 1 共 + + + packets + 分组 + + + 1 every + 1 每 + + + milliseconds + 毫秒 + + + + ResolvedAddressesDialog + + Dialog + 对话框 + + + Hosts + + + + Search for entry (min 3 characters) + + + + Ports + + + + Search for port or name + + + + Capture File Comments + + + + Comment + 注释 + + + Show the comment. + 显示注释。 + + + IPv4 Hash Table + IPv4 哈希表 + + + Show the IPv4 hash table entries. + 显示 IPv4 哈希表项目。 + + + IPv6 Hash Table + IPv6 哈希表 + + + Show the IPv6 hash table entries. + 显示 IPv6 哈希表项目。 + + + Show All + 显示全部 + + + Show all address types. + 显示全部地址类型。 + + + Hide All + 隐藏全部 + + + Hide all address types. + 隐藏全部地址类型。 + + + IPv4 and IPv6 Addresses (hosts) + IPv4 和 IPv6 地址簿 (hosts) + + + Show resolved IPv4 and IPv6 host names in "hosts" format. + 在 "hosts" 格式下显示解析后的 IPv4 和 IPv6 主机名。 + + + Port names (services) + 端口名 (服务) + + + Show resolved port names in "services" format. + Show resolved port names names in "servies" format. + 在“服务”格式下显示解析后的端口名称。 + + + Ethernet Addresses + 以太网地址簿 + + + Show resolved Ethernet addresses in "ethers" format. + 在 "ethers" 格式下显示解析后的以太网地址簿。 + + + Ethernet Well-Known Addresses + 以太网著名地址簿 + + + Show well-known Ethernet addresses in "ethers" format. + 在 "ethers" 格式下显示的著名以太网地址地址簿。 + + + Ethernet Manufacturers + 以太网制造商 + + + Show Ethernet manufacturers in "ethers" format. + 在 "ethers" 格式下显示以太网制造商。 + + + [no file] + [无文件] + + + Resolved Addresses + 解析后的地址 + + + # Resolved addresses found in %1 + # 在 %1 中找到的解析后的地址 + + + # Comments +# +# + # 注释 +# +# + + + + ResponseTimeDelayDialog + + %1 Response Time Delay Statistics + %1 响应时间延迟统计 + + + Type + 类型 + + + Messages + 消息 + + + Min SRT + 最小 SRT + + + Max SRT + 最大 SRT + + + Avg SRT + 平均 SRT + + + Min in Frame + 帧中最小 + + + Max in Frame + 帧中最大 + + + Open Requests + 打开请求 + + + Discarded Responses + 已废弃响应 + + + Repeated Requests + 重发的请求 + + + Repeated Responses + 重发的响应 + + + + RpcServiceResponseTimeDialog + + <small><i>Select a program and version and enter a filter if desired, then press Apply.</i></small> + <small><i>请选择一个程序和版本,并输入一个过滤器(如有必要),然后按“应用”。</i></small> + + + Version: + 版本: + + + Program: + 程序: + + + DCE-RPC Service Response Times + DCE-RPC 服务响应时间 + + + ONC-RPC Service Response Times + ONC-RPC 服务响应时间 + + + + RsaKeysFrame + + RSA Keys + RSA 密钥 + + + RSA private keys are loaded from a file or PKCS #11 token. + + + + Add new keyfile… + + + + Add new token… + + + + Remove key + 移除密钥 + + + PKCS #11 provider libraries. + + + + Add new provider… + + + + Remove provider + + + + Add PKCS #11 token or key + + + + No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider. + + + + Select a new PKCS #11 token or key + 选择一个新的 PKCS #11 令牌或密钥 + + + PKCS #11 token or key + PKCS #11 令牌或密钥 + + + Enter PIN or password for %1 (it will be stored unencrypted) + + + + Enter PIN or password for key + + + + Key could not be added: %1 + 密钥未能添加:%1 + + + RSA private key (*.pem *.p12 *.pfx *.key);;All Files ( + + + + Select RSA private key file + + + + Libraries (*.dll) + 程序库 (*.dll) + + + Libraries (*.so) + 程序库 (*.so) + + + Select PKCS #11 Provider Library + + + + Changes will apply after a restart + 更改将在软件重启后应用 + + + PKCS #11 provider %1 will be removed after the next restart. + + + + + RtpAnalysisDialog + + Dialog + 对话框 + + + Packet + 分组 + + + Sequence + 序列 + + + Delta (ms) + Delta (ms) + + + Jitter (ms) + Jitter + 抖动 (ms) + + + Skew + 扭曲 + + + Bandwidth + 带宽 + + + Marker + 标记 + + + Status + 状态 + + + Stream %1 + + + + Stream %1 Jitter + + + + Stream %1 Difference + + + + Stream %1 Delta + + + + %1 streams, + + + + Save one stream CSV + + + + Save all stream's CSV + + + + &Analyze + 分析(&A) + + + Open the analysis window for the selected stream(s) + + + + &Set List + + + + &Add to List + + + + &Remove from List + + + + Replace existing list in RTP Analysis Dialog with new one + + + + Add new set to existing list in RTP Analysis Dialog + + + + Remove selected streams from list in RTP Analysis Dialog + + + + Graph + 图形 + + + <small><i>A hint.</i></small> + <small><i>提示</i></small> + + + &Export + + + + Open export menu + + + + CSV + CSV + + + Save tables as CSV. + + + + Current Tab Stream CSV + + + + Save the table on the current tab as CSV. + + + + All Tab Streams CSV + + + + Save the table from all tabs as CSV. + + + + Save Graph + 保存图表 + + + Save the graph image. + 保存图表图像。 + + + Go to Packet + 转至分组 + + + Select the corresponding packet in the packet list. + 选择分组列表中对应的分组。 + + + G + G + + + Next Problem Packet + 下一个问题分组 + + + Go to the next problem packet + 转至下一个问题分组 + + + N + N + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + 将匹配上的选中的流作为过滤器。 + + + &Current Tab + + + + Prepare a filter matching current tab. + + + + &All Tabs + + + + Prepare a filter matching all tabs. + + + + RTP Stream Analysis + RTP 流分析 + + + Save Graph As… + + + + G: Go to packet, N: Next problem packet + G: 转到分组,N: 转到问题分组 + + + Portable Document Format (*.pdf) + 便携式文档格式 (*.pdf) + + + Portable Network Graphics (*.png) + 便携式网络图形格式 (*.png) + + + Windows Bitmap (*.bmp) + Windows 位图 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 文件交换格式 (*.jpeg *.jpg) + + + Comma-separated values (*.csv) + 逗号分隔值 (*.csv) + + + + RtpAudioStream + + %1 does not support PCM at %2. Preferred format is %3 + %1 不支持 %2 上的 PCM。首选格式为 %3 + + + + RtpPlayerDialog + + RTP Player + RTP 播放器 + + + Play + + + + Source Address + 源地址 + + + Source Port + 源端口 + + + Destination Address + 目的地址 + + + Destination Port + 目的端口 + + + SSRC + SSRC + + + Setup Frame + 设置帧 + + + Packets + 分组 + + + Time Span (s) + 时间跨度 (s) + + + Payloads + 载荷 + + + <small><i>No audio</i></small> + <small><i>无音频</i></small> + + + Start playback of all unmuted streams + + + + Pause/unpause playback + + + + Stop playback + + + + Enable/disable skipping of silence during playback + + + + Min silence: + + + + Minimum silence duration to skip in seconds + + + + Output Device: + 输出设备: + + + Output Audio Rate: + + + + Jitter Buffer: + 抖动缓冲区: + + + The simulated jitter buffer in milliseconds. + 以毫秒为单位的模拟的抖动缓冲区。 + + + Playback Timing: + 播放时间: + + + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uninterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user. +<br/> +<strong>RTP Timestamp</strong>: Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing. +<br/> +<strong>Uniterrupted Mode</strong>: Ignore the RTP Timestamp. Play the stream as it is completed. This is useful when the RTP timestamp is missing. + <strong>Jitter Buffer</strong>: 使用 jitter buffer 模拟终端用户收听 RTP 流。 +<br/> +<strong>RTP 时间戳</strong>: 使用 RTP 时间戳而不是到达的分组的时间。当用户收听时不能重新生成 RTP 流,但是当 RTP 经过隧道并且原始的分组时序缺失时非常有用。 +<br/> +<strong>不间断模式</strong>: 忽略 RTP 时间戳。播放流认为它是完整的。 这在 RTP 时间戳缺失时非常有用。 + + + Jitter Buffer + 抖动缓冲区 + + + RTP Timestamp + RTP 时间戳 + + + Uninterrupted Mode + 不间断模式 + + + <html><head/><body><p>View the timestamps as time of day (checked) or seconds since beginning of capture (unchecked).</p></body></html> + <html><head/><body><p>以当天时间形式查看时间戳(选中状态)或者以自捕捉以来的秒数(未选中状态)</p></body></html> + + + Time of Day + 当天时间 + + + &Export + + + + Export audio of all unmuted selected channels or export payload of one channel. + + + + From &cursor + + + + Save audio data started at the cursor + + + + &Stream Synchronized Audio + + + + Save audio data synchronized to start of the earliest stream. + + + + &File Synchronized Audio + + + + Save audio data synchronized to start of the capture file. + + + + &Payload + + + + Save RTP payload of selected stream. + + + + Reset Graph + 复位图形 + + + Reset the graph to its initial state. + 复位图形到初始状态。 + + + Go To Setup Packet + + + + Go to setup packet of stream currently under the cursor + + + + Mute + + + + Mute selected streams + + + + Unmute + + + + Unmute selected streams + + + + Invert muting of selected streams + + + + Route audio to left channel of selected streams + + + + Route audio to left and right channel of selected streams + + + + Route audio to right channel of selected streams + + + + Remove Streams + + + + Remove selected streams from the list + + + + All + + + + Select all + + + + None + + + + Clear selection + + + + Invert + 反转 + + + Invert selection + + + + Play/Pause + + + + Start playing or pause playing + + + + Stop + + + + Stop playing + + + + I&naudible streams + + + + Select/Deselect inaudible streams + + + + Inaudible streams + + + + &Select + + + + Select inaudible streams + + + + &Deselect + + + + Deselect inaudible streams + + + + Prepare &Filter + + + + Prepare a filter matching the selected stream(s). + 将匹配上的选中的流作为过滤器。 + + + R&efresh streams + + + + Read captured packets from capture in progress to player + + + + Zoom In + 放大 + + + SR (Hz) + + + + Sample rate of codec + + + + PR (Hz) + + + + Play rate of decoded audio (depends e. g. on selected sound card) + + + + Zoom Out + 缩小 + + + Move Left 10 Pixels + 左移10像素 + + + Move Right 10 Pixels + 右移10像素 + + + Move Left 1 Pixels + 左移10像素 {1 ?} + + + Move Right 1 Pixels + 右移1像素 + + + Go To Packet Under Cursor + 跳转到光标所在的分组 + + + Go to packet currently under the cursor + 跳转到当前光标所在的分组 + + + Play the stream + + + + To Left + + + + Left + Right + + + + To Right + + + + Invert Muting + + + + No devices available + 无可用设备 + + + Select + + + + Audio Routing + + + + &Play Streams + + + + Open RTP player dialog + + + + &Set playlist + + + + Replace existing playlist in RTP Player with new one + + + + &Add to playlist + + + + Add new set to existing playlist in RTP Player + + + + &Remove from playlist + + + + Remove selected streams from playlist in RTP Player + + + + No Audio + + + + Decoding streams... + + + + Out of Sequence + 乱序 + + + Jitter Drops + 抖动过滤 + + + Wrong Timestamps + 错误的时间戳 + + + Inserted Silence + 插入的静默 + + + Double click on cell to change audio routing + + + + %1 streams + %1 流 + + + , %1 selected + + + + , %1 not muted + + + + , start: %1. Double click on graph to set start of playback. + + + + , start: %1, cursor: %2. Press "G" to go to packet %3. Double click on graph to set start of playback. + + + + Playback of stream %1 failed! + + + + Automatic + + + + WAV (*.wav) + + + + Sun Audio (*.au) + Sun 音频 (*.au) + + + Save audio + 保存音频 + + + Raw (*.raw) + + + + Save payload + + + + Warning + 警告 + + + No stream selected or none of selected streams provide audio + + + + Error + 错误 + + + All selected streams must use same play rate. Manual set of Output Audio Rate might help. + + + + No streams are suitable for save + + + + Save failed! + + + + Can't write header of AU file + + + + Can't write header of WAV file + + + + Payload save works with just one audio stream. + + + + Double click to change audio routing + + + + Preparing to play... + + + + Unknown + 未知 + + + + RtpStreamDialog + + Dialog + 对话框 + + + Source Address + 源地址 + + + Source Port + 源端口 + + + Destination Address + 目的地址 + + + Destination Port + 目的端口 + + + SSRC + SSRC + + + Start Time + 开始时间 + + + Duration + 持续时间 + + + Payload + 载荷 + + + Packets + 分组 + + + Lost + 丢弃 + + + Max Delta (ms) + 最大Delta (ms) + + + Max Jitter + 最大抖动 + + + Mean Jitter + 平均抖动 + + + Status + 状态 + + + <small><i>A hint.</i></small> + <small><i>提示</i></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>只显示匹配当前显示过滤器的对话</p></body></html> + + + Limit to display filter + + + + Time of Day + 当天时间 + + + Find &Reverse + + + + Prepare &Filter + + + + &Export + + + + &Analyze + 分析(&A) + + + Open the analysis window for the selected stream(s) and add it to it + + + + Find the reverse stream matching the selected forward stream. + 查找与所选正向流匹配的反向流。 + + + Min Delta (ms) + + + + Mean Delta (ms) + + + + Min Jitter + + + + All forward/reverse stream actions + + + + R + R + + + Find All &Pairs + + + + Select all streams which are paired in forward/reverse relation + + + + Shift+R + + + + Find Only &Singles + + + + Find all streams which don't have paired reverse stream + + + + Ctrl+R + + + + Mark Packets + 标记分组 + + + Mark the packets of the selected stream(s). + 标记选中流量的分组。 + + + M + M + + + All + + + + Select all + + + + None + + + + Clear selection + + + + Invert + 反转 + + + Invert selection + + + + Go To Setup + 跳转到Setup + + + Go to the setup packet for this stream. + 跳转到这条流的设置分组。 + + + G + G + + + Prepare a filter matching the selected stream(s). + 将匹配上的选中的流作为过滤器。 + + + P + P + + + Export the stream payload as rtpdump + 导出流载荷为 rtpdump + + + E + E + + + A + + + + Cop&y + + + + Open copy menu + + + + Copy as CSV + 复制为 CSV + + + Copy stream list as CSV. + 复制流列表为 CSV。 + + + Copy as YAML + 复制为YAML + + + Copy stream list as YAML. + 复制流列表为YAML。 + + + RTP Streams + RTP 流 + + + Select + + + + as CSV + 作为 CSV + + + as YAML + 作为YAML + + + %1 streams + %1 流 + + + , %1 selected, %2 total packets + , %1 项已选中, 总计 %2 项分组 + + + Save RTPDump As… + + + + + SCTPAllAssocsDialog + + Wireshark - SCTP Associations + Wireshark - SCTP 分析助手 + + + ID + ID + + + Port 1 + 端口1 + + + Port 2 + 端口2 + + + Number of Packets + 分组数量 + + + Number of DATA Chunks + 数据块的数量 + + + Number of Bytes + 分组数量 + + + Filter Selected Association + 过滤选择的助手 + + + Analyze + 分析 + + + + SCTPAssocAnalyseDialog + + Wireshark - Analyse Association + Wireshark - 分析助手 + + + TabWidget + 选项卡 + + + Statistics + 统计 + + + Chunk Statistics + 块统计 + + + Filter Association + 过滤器关联 + + + Number of Data Chunks from EP2 to EP1: + 从端点2到端点1的数据块数量: + + + Checksum Type: + 校验类型: + + + Number of Data Chunks from EP1 to EP2: + 从端点1到端点2的数据块数量: + + + Number of Data Bytes from EP1 to EP2: + 从端点1到端点2的数据字节数量: + + + Number of Data Bytes from EP2 to EP1: + 从端点2到端点1的数据字节数量: + + + Endpoint 1 + 端点1 + + + Graph TSN + TSN图 + + + Graph Bytes + 字节图 + + + Requested Number of Inbound Streams: + 输入流的请求数量: + + + Port: + 端口: + + + Sent Verification Tag: + 发送验证标签: + + + Minimum Number of Inbound Streams: + 输入流的最小数量: + + + - + - + + + <small><i>For complete analysis check SCTP preference Enable Association indexing</i></small> + + + + Complete List of IP addresses from INIT Chunk: + + + + Minimum Number of Outbound Streams: + 输出流的最小数量: + + + Graph Arwnd + Arwnd图 + + + Endpoint 2 + 端点2 + + + Complete List of IP addresses from INIT_ACK Chunk: + + + + Provided Number of Outbound Streams: + 输出流的提供数量: + + + SCTP Analyse Association: %1 Port1 %2 Port2 %3 + SCTP分析助手:%1 端口1 %2 端口2 %3 + + + No Association found for this packet. + 没找到该分组的助手。 + + + Warning + 警告 + + + Could not find SCTP Association with id: %1 + 找不到 ID 为 %1 的 SCTP 关联 + + + Complete list of IP addresses from INIT Chunk: + + + + Complete list of IP addresses from INIT_ACK Chunk: + + + + List of Used IP Addresses + + + + Used Number of Inbound Streams: + 输入流的使用数量: + + + Used Number of Outbound Streams: + 输出流的使用数量: + + + + SCTPChunkStatisticsDialog + + Dialog + 对话框 + + + Association + 关联 + + + Endpoint 1 + 端点1 + + + Endpoint 2 + 端点2 + + + Save Chunk Type Order + 保存块类型顺序 + + + Hide Chunk Type + 隐藏块类型 + + + Remove the chunk type from the table + 从表中删除块类型 + + + Chunk Type Preferences + 块类型首选项 + + + Go to the chunk type preferences dialog to show or hide other chunk types + 跳转到块类型首选项对话框来设置显示或隐藏其他的块类型 + + + Show All Registered Chunk Types + 显示所有注册的块类型 + + + Show all chunk types with defined names + 显示所有的定义名称的块类型 + + + SCTP Chunk Statistics: %1 Port1 %2 Port2 %3 + SCTP 数据统计:%1 端口1 %2 端口2 %3 + + + + SCTPGraphArwndDialog + + SCTP Graph + SCTP图 + + + Reset to full size + 复位为完整尺寸 + + + Save Graph + 保存图形 + + + goToPacket + 转到分组 + + + Go to Packet + 跳转到分组 + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP数据和通告接收窗口经过时间:%1 端口1 %2 端口2 %3 + + + No Data Chunks sent + 无数据块发送 + + + Arwnd + Arwnd + + + time [secs] + 时间 [秒] + + + Advertised Receiver Window [Bytes] + 通告的接受窗口 [字节] + + + <small><i>Graph %1: a_rwnd=%2 Time=%3 secs </i></small> + <small><i>图 %1: a_rwnd=%2 时间=%3 秒 </i></small> + + + + SCTPGraphByteDialog + + SCTP Graph + SCTP图形 + + + Reset to full size + 复位为完整尺寸 + + + Save Graph + 保存图形 + + + goToPacket + 跳转到分组 + + + Go to Packet + 跳转到分组 + + + SCTP Data and Adv. Rec. Window over Time: %1 Port1 %2 Port2 %3 + SCTP 数据和通告接收端口经过时间: %1 端口1 %2 端口2 %3 + + + No Data Chunks sent + 无数据块发送 + + + Bytes + 字节 + + + time [secs] + 时间 [秒] + + + Received Bytes + 收到字节 + + + <small><i>Graph %1: Received bytes=%2 Time=%3 secs </i></small> + <small><i>图 %1: 收到字节=%2 时间=%3 秒 </i></small> + + + + SCTPGraphDialog + + SCTP Graph + SCTP 图 + + + Relative TSNs + 相对 TSN + + + Only SACKs + 只有SACK + + + Only TSNs + 只有TSN + + + Show both + 都显示 + + + Reset to full size + 复位为完整尺寸 + + + Save Graph + 保存图形 + + + goToPacket + 跳转到分组 + + + Go to Packet + 跳转到分组 + + + SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3 + SCTP TSN和SACK经过时间: %1 端口1 %2 端口2 %3 + + + No Data Chunks sent + 无数据 + + + CumTSNAck + CumTSNAck + + + Gap Ack + Gap 确认 + + + NR Gap Ack + NR Gap确认 + + + Duplicate Ack + 重复ACK + + + TSN + TSN + + + time [secs] + 时间 [秒] + + + TSNs + TSNs + + + <small><i>%1: %2 Time: %3 secs </i></small> + <small><i>%1: %2 时间: %3 秒 </i></small> + + + Portable Document Format (*.pdf) + 便携式文档格式 (*.pdf) + + + Portable Network Graphics (*.png) + 便携式网络图形格式 (*.png) + + + Windows Bitmap (*.bmp) + Windows 位图 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG文件交换格式 (*.jpeg *.jpg) + + + Save Graph As… + + + + + ScsiServiceResponseTimeDialog + + <small><i>Select a command and enter a filter if desired, then press Apply.</i></small> + <small><i>选择一个命令并根据需要输入过滤器,然后按 应用。</i></small> + + + Command: + 命令: + + + SCSI Service Response Times + SCSI 服务响应时间 + + + + SearchFrame + + Frame + + + + <html><head/><body><p>Search the Info column of the packet list (summary pane), decoded packet display labels (tree view pane) or the ASCII-converted packet data (hex view pane).</p></body></html> + <html><head/><body><p>搜索分组列表 (概要窗格) 的“信息”列、解码分组显示标签 (树形视图窗格) 或转换为 ASCII 字符的分组数据 (十六进制视图窗格)。</p></body></html> + + + Packet list + 分组列表 + + + Packet details + 分组详情 + + + Packet bytes + 分组字节流 + + + <html><head/><body><p>Search for strings containing narrow (UTF-8 and ASCII) or wide (UTF-16) characters.</p></body></html> + <html><head/><body><p>搜索包含窄字符集 (UTF-8 与 ASCII) 或宽字符集 (UTF-16) 的字符串。</p></body></html> + + + Narrow & Wide + 宽窄 + + + Narrow (UTF-8 / ASCII) + 窄 (UTF-8 / ASCII) + + + Wide (UTF-16) + 宽 (UTF-16) + + + Case sensitive + 区分大小写 + + + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5), a plain string (e.g. My String) or a regular expression (e.g. colou?r).</p></body></html> + <html><head/><body><p>Search for data using display filter syntax (e.g. ip.addr==10.1.1.1), a hexadecimal string (e.g. fffffda5) or a plain string (e.g. My String).</p></body></html> + <html><head/><body><p>以“显示过滤器”语法 (如 ip.addr==10.1.1.1)、十六进制字符串 (如 fffffda5) 或纯字符串 (如 My String) 搜索数据。</p></body></html> + + + Display filter + 显示过滤器 + + + Hex value + 十六进制值 + + + String + 字符串 + + + Regular Expression + 正则表达式 + + + Find + 查找 + + + Cancel + 取消 + + + No valid search type selected. Please report this to the development team. + 未选择有效的搜索类型。请将此问题报告给开发团队。 + + + Invalid filter. + 无效过滤器。 + + + That filter doesn't test anything. + 该过滤器未测试任何项目。 + + + That's not a valid hex string. + 不是有效的十六进制字符串。 + + + You didn't specify any text for which to search. + 您未指定任何要搜索的文本。 + + + No valid character set selected. Please report this to the development team. + 未选择有效的字符集。请将此问题报告给开发团队。 + + + No valid search area selected. Please report this to the development team. + 未选择有效的搜索范围。请将此问题报告给开发团队。 + + + Searching for %1… + + + + No packet contained those bytes. + 无分组包含这些字节流。 + + + No packet contained that string in its Info column. + 无分组的“信息”列包含该字符串。 + + + No packet contained that string in its dissected display. + 无分组的解析视图包含该字符串。 + + + No packet contained that string in its converted data. + 无分组转换后的分组含该字符串。 + + + No packet matched that filter. + 无分组与该过滤器匹配。 + + + + SequenceDialog + + Call Flow + 呼叫流 + + + Time + 时间 + + + Comment + 注释 + + + No data + 无数据 + + + %Ln node(s) + + %Ln 节点 + + + + %Ln item(s) + + %Ln 项目 + + + + Portable Document Format (*.pdf) + 便携式文档格式 (*.pdf) + + + Portable Network Graphics (*.png) + 便携式网络图形格式 (*.png) + + + Windows Bitmap (*.bmp) + Windows 位图 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 文件交换格式 (*.jpeg *.jpg) + + + ASCII (*.txt) + ASCII (*.txt) + + + Save Graph As… + + + + Flow + + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> +<tr><th>n</th><td>Go to the next packet</td></th> +<tr><th>p</th><td>Go to the previous packet</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>有价值的和惊人的时间节省键盘快捷方式</h3> +<table><tbody> + +<tr><th>0</th><td>图形重置为初始状态</td></th> + +<tr><th>→</th><td>右移 10 个像素</td></th> +<tr><th>←</th><td>左移 10 个像素</td></th> +<tr><th>↑</th><td>上移 10 个像素</td></th> +<tr><th>↓</th><td>下移 10 个像素</td></th> +<tr><th><i>Shift+</i>→</th><td>右移 1 个像素</td></th> +<tr><th><i>Shift+</i>←</th><td>左移 1 个像素</td></th> +<tr><th><i>Shift+</i>↑</th><td>上移 1 个像素</td></th> +<tr><th><i>Shift+</i>↓</th><td>下移 1 个像素</td></th> + +<tr><th>g</th><td>转到光标下的分组</td></th> +<tr><th>n</th><td>转到下一个分组</td></th> +<tr><th>p</th><td>转到上一个分组</td></th> + +</tbody></table> +</body></html> +</tbody></table> +</body></html> + + + <small><i>A hint</i></small> + <small><i>提示</i></small> + + + <html><head/><body><p>Only show flows matching the current display filter</p></body></html> + <html><head/><body><p>仅显示与当前显示筛选器匹配的流</p></body></html> + + + Limit to display filter + 限制显示过滤器 + + + Flow type: + 流类型: + + + Addresses: + 地址: + + + Any + 任何 + + + Network + 网络 + + + Reset Diagram + 复位图表 + + + Reset &Diagram + + + + Reset the diagram to its initial state. + 复位图表到初始状态。 + + + 0 + 0 + + + &Reset Diagram + + + + Reset the diagram to its initial state + + + + &Export + + + + Export diagram + + + + Zoom In + 放大 + + + + + + + + + Zoom Out + 缩小 + + + - + - + + + Move Up 10 Pixels + 上移10像素 + + + Up + 上键 + + + Move Left 10 Pixels + 左移10像素 + + + Left + 左键 + + + Move Right 10 Pixels + 右移10像素 + + + Right + 右键 + + + Move Down 10 Pixels + 下移10像素 + + + Down + 下键 + + + Move Up 1 Pixel + 上移1像素 + + + Shift+Up + Shift+上键 + + + Move Left 1 Pixel + 左移1像素 + + + Shift+Left + Shift+右键 + + + Move Right 1 Pixel + 右移1像素 + + + Shift+Right + Shift+右键 + + + Move Down 1 Pixel + 下移1像素 + + + Shift+Down + Shift+下键 + + + Go To Packet Under Cursor + 跳转到光标所在的分组 + + + Go to packet currently under the cursor + 跳转到当前光标所在的分组 + + + G + G + + + All Flows + 所有流 + + + Show flows for all packets + 显示所有分组 + + + 1 + 1 + + + TCP Flows + TCP流 + + + Show only TCP flow information + 只显示 TCP 流的信息 + + + Go To Next Packet + 转到下一分组 + + + Go to the next packet + 转到下一分组 + + + N + N + + + Go To Previous Packet + 转到上一分组 + + + Go to the previous packet + 转到上一分组 + + + P + P + + + Select RTP Stream + + + + Select RTP stream in RTP Streams dialog + + + + S + S + + + Deselect RTP Stream + + + + Deselect RTP stream in RTP Streams dialog + + + + D + D + + + + ShortcutListModel + + Shortcut + 快捷方式 + + + Name + 名称 + + + Description + 描述 + + + + ShowPacketBytesDialog + + Show Packet Bytes + 显示分组字节 + + + Hint. + 提示。 + + + Decode as + 解码为 + + + Show as + 显示为 + + + Start + 开始 + + + End + 结束 + + + Find: + 查找: + + + Find &Next + 查找下一个(&N) + + + Frame %1, %2, %Ln byte(s). + + 帧 %1, %2, %Ln 字节。 + + + + None + + + + Base64 + Base64 + + + Compressed + 压缩 + + + Hex Digits + + + + Percent-Encoding + + + + Quoted-Printable + 可打印引用 + + + ROT13 + ROT13 + + + ASCII + ASCII + + + ASCII & Control + ASCII与控制符 + + + C Array + C 数组 + + + EBCDIC + EBCDIC + + + Hex Dump + Hex 转储 + + + HTML + HTML + + + Image + 图像 + + + Raw + 原始数据 + + + Rust Array + + + + UTF-8 + UTF-8 + + + YAML + YAML + + + Print + 打印 + + + Copy + 复制 + + + Save as… + 另存为… + + + Save Selected Packet Bytes As… + + + + Displaying %Ln byte(s). + + 已显示 %Ln 字节。 + + + + JSON + + + + Regex Find: + 正则表达式查找: + + + + ShowPacketBytesTextEdit + + Show Selected + 显示选中 + + + Show All + 显示全部 + + + + SplashOverlay + + Initializing dissectors + 正在初始化解析器 + + + Initializing tap listeners + 正在初始化 tap 监听器 + + + Initializing external capture plugins + 初始化外部捕获插件 + + + Registering dissectors + 注册解析器 + + + Registering plugins + Registering dissector + 正在注册插件 + + + Handing off dissectors + 正在卸载解析器 + + + Handing off plugins + 正在卸载插件 + + + Loading Lua plugins + 正在加载 Lua 插件 + + + Removing Lua plugins + 移除 Lua 插件 + + + Loading module preferences + 正在加载模块首选项 + + + Finding local interfaces + 正在查找本地接口 + + + Applying changed preferences + + + + (Unknown action) + (未知操作) + + + + StatsTreeDialog + + Configuration not found + 配置未找到 + + + Unable to find configuration for %1. + 不能为 %1找到配置。 + + + + StripHeadersDialog + + Dialog + 对话框 + + + Display filter: + + + + + SupportedProtocolsDialog + + Dialog + 对话框 + + + <html><head/><body><p>Search the list of field names.</p></body></html> + <html><head/><body><p>搜索字段名称列表。</p></body></html> + + + Search: + 搜索: + + + <small><i>Gathering protocol information…</i></small> + <small><i>正在汇聚协议信息…</i></small> + + + Supported Protocols + 支持的协议 + + + %1 protocols, %2 fields. + %1 个协议,%2 个字段。 + + + + SupportedProtocolsModel + + Name + 名称 + + + Filter + 过滤器 + + + Type + 类型 + + + Description + 描述 + + + + SyntaxLineEdit + + Invalid filter: %1 + + + + "%1" is deprecated in favour of "%2". See Help section 6.4.8 for details. + + + + %1 + + + + + TCPStreamDialog + + Dialog + 对话框 + + + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>x</th><td>Zoom in X axis</td></th> +<tr><th>X</th><td>Zoom out X axis</td></th> +<tr><th>y</th><td>Zoom in Y axis</td></th> +<tr><th>Y</th><td>Zoom out Y axis</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th><i>Pg Up</i></th><td>Next stream</td></th> +<tr><th><i>Pg Dn</i></th><td>Previous stream</td></th> +<tr><th>d</th><td>Switch direction (swap TCP endpoints)</td></th> +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>s</th><td>Toggle relative / absolute sequence numbers</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +<tr><th>1</th><td>Round Trip Time graph</td></th> +<tr><th>2</th><td>Throughput graph</td></th> +<tr><th>3</th><td>Stevens-style Time / Sequence graph</td></th> +<tr><th>4</th><td>tcptrace-style Time / Sequence graph</td></th> +<tr><th>5</th><td>Window Scaling graph</td></th> + +</tbody></table> +</body></html> + <html><head/><body> + +<h3>有价值的和惊人的节省时间的快捷键</h3> +<table><tbody> + +<tr><th>+</th><td>放大</td></th> +<tr><th>-</th><td>缩小</td></th> +<tr><th>0</th><td>图形恢复到初始状态</td></th> + +<tr><th>→</th><td>右移 10 个像素</td></th> +<tr><th>←</th><td>左移 10 个像素</td></th> +<tr><th>↑</th><td>上移 10 个像素</td></th> +<tr><th>↓</th><td>下移 10 个像素</td></th> +<tr><th><i>Shift+</i>→</th><td>右移 1 个像素</td></th> +<tr><th><i>Shift+</i>←</th><td>右移 1 个像素</td></th> +<tr><th><i>Shift+</i>↑</th><td>上移 1 个像素</td></th> +<tr><th><i>Shift+</i>↓</th><td>下移 1 个像素</td></th> + +<tr><th><i>Pg Up</i></th><td>下一条流</td></th> +<tr><th><i>Pg Dn</i></th><td>前一条流</td></th> +<tr><th>d</th><td>切换方向(交换TCP端点)</td></th> +<tr><th>g</th><td>跳转到光标所在包</td></th> + +<tr><th>z</th><td>切换鼠标 拖拽/缩放</td></th> +<tr><th>s</th><td>切换 相对/绝对 顺序号</td></th> +<tr><th>t</th><td>切换 捕获/会话 时间起源</td></th> +<tr><th>Space</th><td>切换十字线</td></th> + +<tr><th>1</th><td>往返时间图</td></th> +<tr><th>2</th><td>吞吐量图</td></th> +<tr><th>3</th><td>Stevens-风格 时间/序列图</td></th> +<tr><th>4</th><td>tcptrace-风格 时间/序列图</td></th> +<tr><th>5</th><td>窗口缩放图</td></th> + +</tbody></table> +</body></html> + + + <small><i>Mouse over for shortcuts</i></small> + <small><i>鼠标悬停显示快捷键</i></small> + + + Type + 类型 + + + MA Window (s) + MA 窗口 + + + Allow SACK segments as well as data packets to be selected by clicking on the graph + 允许通过点击图表来选择 SACK 段及分组 + + + Select SACKs + select SACKs + 选择 SACKs + + + Stream + + + + <html><head/><body><p>Switch the direction of the connection (view the opposite flow).</p></body></html> + <html><head/><body><p>切换连接的方向(查看反向流)。</p></body></html> + + + Switch Direction + 切换方向 + + + Mouse + 鼠标 + + + Drag using the mouse button. + 使用鼠标按键进行拖拽。 + + + drags + 拖拽 + + + Select using the mouse button. + 使用鼠标按键进行选择。 + + + zooms + 缩放 + + + Display Round Trip Time vs Sequence Number + 显示往返时间与序列号 + + + RTT By Sequence Number + RTT 按序列号 + + + Display graph of Segment Length vs Time + 显示段长度与时间的图表 + + + Segment Length + 段长度 + + + Display graph of Mean Transmitted Bytes vs Time + 显示平均传输字节与时间的图表 + + + Display graph of Mean ACKed Bytes vs Time + 显示平均确认字节与时间的图表 + + + Goodput + 实际吞吐量 + + + Display graph of Receive Window Size vs Time + 显示接收窗口大小与时间的图表 + + + Rcv Win + 接收窗口 + + + Display graph of Outstanding Bytes vs Time + 显示未完成字节与时间的图表 + + + Bytes Out + 发出字节 + + + <html><head/><body><p>Reset the graph to its initial state.</p></body></html> + <html><head/><body><p>复位图形到初始状态。</p></body></html> + + + Reset + 复位 + + + Reset Graph + 复位图形 + + + Reset the graph to its initial state. + 复位图形到初始状态。 + + + 0 + 0 + + + Zoom In + 放大 + + + + + + + + + Zoom Out + 缩小 + + + - + - + + + Move Up 10 Pixels + 上移10像素 + + + Up + 上键 + + + Move Left 10 Pixels + 左移10像素 + + + Left + 左键 + + + Move Right 10 Pixels + 右移10像素 + + + Right + 右键 + + + Move Down 10 Pixels + 下移10像素 + + + Down + 下键 + + + Move Up 1 Pixel + 上移1像素 + + + Shift+Up + Shift+上键 + + + Move Left 1 Pixel + 左移1像素 + + + Shift+Left + Shift+右键 + + + Move Right 1 Pixel + 右移1像素 + + + Shift+Right + Shift+右键 + + + Move Down 1 Pixel + 下移1像素 + + + Shift+Down + Shift+下键 + + + Next Stream + 下一条流 + + + Go to the next stream in the capture + 跳转到捕获的下一条流 + + + PgUp + PgUp + + + Previous Stream + 前一条流 + + + Go to the previous stream in the capture + 跳转到捕获的前一条流 + + + PgDown + PgDown + + + Switch direction (swap TCP endpoints) + 切换方向(交换TCP端点) + + + D + D + + + Go To Packet Under Cursor + 跳转到光标所在的分组 + + + Go to packet currently under the cursor + 跳转到当前光标所在的分组 + + + G + G + + + Drag / Zoom + 拖拽/缩放 + + + Toggle mouse drag / zoom behavior + 触发鼠标拖拽/缩放行为 + + + Z + Z + + + Relative / Absolute Sequence Numbers + 相对/绝对 序列号 + + + Toggle relative / absolute sequence numbers + 触发相对/绝对序列号 + + + S + S + + + Capture / Session Time Origin + 捕获/会话时间起源 + + + Toggle capture / session time origin + 触发捕获/会话时间起源 + + + T + T + + + Crosshairs + 十字线 + + + Toggle crosshairs + 切换十字线 + + + Space + 空格键 + + + Round Trip Time + 往返时间 + + + Switch to the Round Trip Time graph + 切换到往返时间图 + + + 1 + 1 + + + Throughput + 吞吐量 + + + Switch to the Throughput graph + 切换到吞吐量图 + + + 2 + 2 + + + Time / Sequence (Stevens) + 时间/序列 (Stevens) + + + Switch to the Stevens-style Time / Sequence graph + 切换到Stevens样式的时间/序列图 + + + 3 + 3 + + + Window Scaling + 窗口尺寸 + + + Switch to the Window Scaling graph + 切换到窗口尺寸图 + + + 5 + 5 + + + Time / Sequence (tcptrace) + 时间/序列 (tcptrace) + + + Switch to the tcptrace-style Time / Sequence graph + 切换到tcptrace样式的时间/序列图 + + + 4 + 4 + + + Zoom In X Axis + 放大 X 轴 + + + X + X + + + Zoom Out X Axis + 缩小 X 轴 + + + Shift+X + Shift+X + + + Zoom In Y Axis + 放大 Y 轴 + + + Y + Y + + + Zoom Out Y Axis + 缩小 Y 轴 + + + Shift+Y + Shift+Y + + + Save As… + + + + No Capture Data + 无捕获数据 + + + %1 %2 pkts, %3 %4 %5 pkts, %6 + %1 %2 分组, %3 %4 %5 分组, %6 + + + Sequence Numbers (Stevens) + 序列号 (Stevens) + + + Sequence Numbers (tcptrace) + 序列号 (tcptrace) + + + (MA) + (MA) + + + (%1 Segment MA) + (%1 Segment MA) + + + [not enough data] + [没有足够数据] + + + for %1:%2 %3 %4:%5 + 对于 %1:%2 %3 %4:%5 + + + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + %1 %2 (%3s len %4 seq %5 ack %6 win %7) + + + Click to select packet + 点击选取分组 + + + Packet + 分组 + + + Release to zoom, x = %1 to %2, y = %3 to %4 + 释放缩放, x = % 到 %2, y = %3 到 %4 + + + Unable to select range. + 无法选择范围。 + + + Click to select a portion of the graph. + 点击选择图形的一部分。 + + + Portable Document Format (*.pdf) + 便携式文档格式 (*.pdf) + + + Portable Network Graphics (*.png) + 便携式网络图形格式 (*.png) + + + Windows Bitmap (*.bmp) + Windows 位图 (*.bmp) + + + JPEG File Interchange Format (*.jpeg *.jpg) + JPEG 文件交换格式 (*.jpeg *.jpg) + + + Save Graph As… + + + + + TLSKeylogDialog + + Dialog + 对话框 + + + Browse… + 浏览… + + + Command line + + + + Run an application with the SSLKEYLOGFILE environment variable set to the file specified by the TLS key log filename preference. This enables TLS decryption in Wireshark. Set the key log file and start the capture before launching the application to ensure that the initial TLS handshakes are captured. + + + + <span style=" font-size:small;">Firefox and Chrome are known to work. If your desired browser is currently running, close it first before launching it below. Command line options are supported.</span> + + + + TLS (Pre)-Master-Secret log file path (tls.keylog_file) + + + + <span style=" font-size:small;">TLS session secrets will be logged to this file. If you change this field, hit the Save button to update the TLS protocol preferences.</span> + + + + Launch application with SSLKEYLOGFILE + + + + Launch + + + + Save + + + + TLS Keylog file + + + + Program to start with SSLKEYLOGFILE + + + + + TapParameterDialog + + Dialog + 对话框 + + + Item + 项目 + + + <small><i>A hint.</i></small> + <small><i>一个提示。</i></small> + + + Display filter: + 显示过滤器: + + + Regenerate statistics using this display filter + 使用此显示过滤器重新生成统计 + + + Apply + 应用 + + + Copy + 复制 + + + Copy a text representation of the tree to the clipboard + 复制表示树的文本到剪贴板 + + + Save as… + Save as... + 另存为… + + + Save the displayed data in various formats + 使用不同的格式保存显示的数据 + + + Collapse All + 全部折叠 + + + Expand All + 全部展开 + + + Save Statistics As… + + + + Plain text file (*.txt);;Comma separated values (*.csv);;XML document (*.xml);;YAML document (*.yaml) + 纯文本文件 (*.txt);;逗号分隔值 (*.csv);;XML 文档 (*.xml);;YAML 文档 (*.yaml) + + + Plain text file (*.txt) + 纯文本文件 (*.txt) + + + Error saving file %1 + 保存文件 %1 时出错 + + + + TimeShiftDialog + + Shift all packets by + 平移所有分组 + + + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + <html><head/><body><p><span style=" font-size:small; font-style:italic;">[-][[hh:]mm:]ss[.ddd] </span></p></body></html> + + + Set the time for packet + 设置分组时间 + + + to + + + + …then set packet + ...then set packet + …然后设置分组 + + + and extrapolate the time for all other packets + 并推算所有其他分组的时间 + + + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + <html><head/><body><p align="right"><span style=" font-size:small; font-style:italic;">[YYYY-MM-DD] hh:mm:ss[.ddd] </span></p></body></html> + + + Undo all shifts + 撤销所有平移 + + + Time Shift + 时间平移 + + + Frame numbers must be between 1 and %1. + 帧序号必须介于 1 与 %1。 + + + Invalid frame number. + 帧序号无效。 + + + Time shifting is not available while capturing packets. + + + + + TrafficTab + + Map file error + + + + Could not open base file %1 for reading: %2 + + + + No endpoints available to map + + + + Unable to create temporary file + + + + + TrafficTableDialog + + <html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html> + <html><head/><body><p>显示解析后地址和端口号代替纯文本。对应的名称解析首选项必须先开启。</p></body></html> + + + Name resolution + 解析名称 + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>只显示匹配当前显示过滤器的对话</p></body></html> + + + Limit to display filter + 显示过滤器的限制 + + + <html><head/><body><p>Only show types matching the filter value</p></body></html> + + + + Filter list for specific type + + + + <html><head/><body><p>Show absolute times in the start time column.</p></body></html> + <html><head/><body><p>在开始时间列中显示绝对时间。</p></body></html> + + + GroupBox + 分组框 + + + Absolute start time + 绝对开始时间 + + + Copy + 复制 + + + Unknown + 未知 + + + + TrafficTree + + Resize all columns to content + + + + Filter on stream id + + + + Copy %1 table + + + + as CSV + 作为 CSV + + + Copy all values of this page to the clipboard in CSV (Comma Separated Values) format. + + + + as YAML + 作为YAML + + + Copy all values of this page to the clipboard in the YAML data serialization format. + + + + as JSON + + + + Copy all values of this page to the clipboard in the JSON data serialization format. + + + + Save data as raw + + + + Disable data formatting for export/clipboard and save as raw data + + + + + TrafficTreeHeaderView + + Less than + + + + Greater than + + + + Equal + + + + Columns to display + + + + Filter %1 by + + + + Enter filter value + + + + + TrafficTypesModel + + Protocol + 协议 + + + + UatDialog + + Create a new entry. + 创建一个新项。 + + + Remove this entry. + Remove this profile. + 移除此项。 + + + Copy this entry. + Copy this profile. + 复制此项。 + + + Move entry up. + 向上移动条目。 + + + Move entry down. + 向下移动条目。 + + + Clear all entries. + 清除所有条目。 + + + Unknown User Accessible Table + 未知用户可访问表 + + + Open + 打开 + + + + UatFrame + + Frame + + + + Create a new entry. + 创建一个新条目。 + + + Remove this entry. + 删除此条目。 + + + Copy this entry. + 复制此条目。 + + + Move entry up. + 向上移动条目。 + + + Move entry down. + 向下移动条目。 + + + Clear all entries. + 清除所有条目。 + + + Copy entries from another profile. + 从其他配置文件复制条目。 + + + Copy from + 复制自 + + + Unknown User Accessible Table + 未知用户可访问表 + + + Open + 打开 + + + + VoipCallsDialog + + <small></small> + <small></small> + + + <html><head/><body><p>Only show conversations matching the current display filter</p></body></html> + <html><head/><body><p>只显示匹配当前显示过滤器的对话</p></body></html> + + + Limit to display filter + + + + Time of Day + 当天时间 + + + Flow &Sequence + + + + Show flow sequence for selected call(s). + + + + Prepare &Filter + + + + Prepare a filter matching the selected calls(s). + + + + Cop&y + + + + Open copy menu + + + + All + + + + Select all + + + + None + + + + Invert + 反转 + + + Invert selection + + + + Select related RTP streams + + + + Select RTP streams related to selected calls in RTP Streams dialog + + + + S + S + + + Deselect related RTP Streams + + + + D + D + + + Clear selection + + + + Display time as time of day + + + + Copy as CSV + 复制为 CSV + + + Copy stream list as CSV. + 复制流列表为 CSV。 + + + Copy as YAML + 复制为 YAML + + + Copy stream list as YAML. + 复制流列表为 YAML。 + + + SIP Flows + SIP流 + + + VoIP Calls + VoIP 呼叫 + + + as CSV + 作为 CSV + + + as YAML + 作为YAML + + + Select + + + + + VoipCallsInfoModel + + On + + + + Off + + + + Tunneling: %1 Fast Start: %2 + 隧道:%1 快速启动:%2 + + + Start Time + 开始时间 + + + Stop Time + 停止时间 + + + Initial Speaker + 初始化扬声器 + + + From + + + + To + + + + Protocol + 协议 + + + Duration + 持续时间 + + + Packets + 分组 + + + State + 状态 + + + Comments + 注释 + + + + WelcomePage + + Form + 表单 + + + <html><head/><body><p><span style=" font-size:large;">Welcome to Wireshark</span></p></body></html> + <html><head/><body><p><span style=" font-size:large;">欢迎使用 Wireshark</span></p></body></html> + + + <html><head/><body><p>Open a file on your file system</p></body></html> + <html><head/><body><p>打开在您的文件系统上的文件</p></body></html> + + + <h2>Open</h2> + <h2>打开</h2> + + + Recent capture files + 最近的捕获文件 + + + Capture files that have been opened previously + 不久前打开过的捕获文件 + + + <html><head/><body><p>Capture live packets from your network.</p></body></html> + <html><head/><body><p>从您的网络中捕获实时分组。</p></body></html> + + + <h2>Capture</h2> + <h2>捕获</h2> + + + …using this filter: + …使用这个过滤器: + + + Interface list + 接口列表 + + + List of available capture interfaces + 可用捕获接口的列表 + + + <h2>Learn</h2> + <h2>学习</h2> + + + <html><head> +<style> +a:link { + color: palette(text); + text-decoration: none; +} +a:hover { + color: palette(text); + text-decoration: underline; +} +</style> +</head> +<body> + +<table><tr> +<th><a href="https://www.wireshark.org/docs/wsug_html_chunked/">User's Guide</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://gitlab.com/wireshark/wireshark/-/wikis/">Wiki</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://ask.wireshark.org/">Questions and Answers</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://www.wireshark.org/lists/">Mailing Lists</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://sharkfest.wireshark.org/">SharkFest</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://discord.com/invite/ts9GZCjGj5">Wireshark Discord</a></th> + +<td style="padding-left: 8px; padding-right: 8px;">·</td> + +<th><a href="https://wiresharkfoundation.org/donate/">Donate</a></th> + +</tr></table> +</body></html> + + + + Show in Finder + 在 Finder 中显示 + + + Show in Folder + 在 Finder 中显示 + + + Welcome to %1 + + + + All interfaces shown + 显示所有接口 + + + %n interface(s) shown, %1 hidden + + 显示了 %n 个接口,隐藏了 %1 个 + + + + You are sniffing the glue that holds the Internet together using Wireshark + 你正在使用 Wireshark 嗅探互联网的联机 + + + You are running Wireshark + 正在运行 Wireshark + + + You receive automatic updates. + 接受自动更新。 + + + You have disabled automatic updates. + 禁止自动更新。 + + + not found + 未找到 + + + Copy file path + 复制文件路径 + + + Remove from list + 从列表中移除 + + + + WirelessFrame + + Frame + + + + Interface + 接口 + + + <html><head/><body><p>Set the 802.11 channel.</p></body></html> + <html><head/><body><p>设置 802.11 信道。</p></body></html> + + + Channel + 信道 + + + <html><head/><body><p>When capturing, show all frames, ones that have a valid frame check sequence (FCS), or ones with an invalid FCS.</p></body></html> + <html><head/><body><p>在捕捉时,显示所有具有有效的帧校验序列(FCS)的帧,或者也显示具有无效的 FCS 的帧。</p></body></html> + + + FCS Filter + FCS 过滤器 + + + All Frames + 所有帧 + + + Valid Frames + 有效帧 + + + Invalid Frames + 无效帧 + + + Wireless controls are not supported in this version of Wireshark. + 此版本的 Wireshark 不支持无线控件。 + + + External Helper + 外部助手 + + + <html><head/><body><p>Show the IEEE 802.11 preferences, including decryption keys.</p></body></html> + <html><head/><body><p>显示 IEEE 802.11 首选项。包括解密密钥。</p></body></html> + + + 802.11 Preferences + 802.11 首选项 + + + AirPcap Control Panel + AirPcap 控制面板 + + + Open the AirPcap Control Panel + 打开 AirPcap 控制面板 + + + Unable to set channel or offset. + 无法设置信道或偏移。 + + + Unable to set FCS validation behavior. + 无法设置 FCS 验证行为。 + + + + WirelessTimeline + + Packet number %1 does not include TSF timestamp, not showing timeline. + + + + Packet number %u has large negative jump in TSF, not showing timeline. Perhaps TSF reference point is set wrong? + + + + + WiresharkDialog + + Failed to attach to tap "%1" + 附加到 Tap “%1” 失败 + + + + WiresharkMainWindow + + Wireshark + Wireshark + + + Go to packet + 转到分组 + + + Cancel + 取消 + + + File Set + 文件集合 + + + Export Packet Dissections + 导出分组解析结果 + + + Export Objects + 导出对象 + + + &Zoom + 缩放(&Z) + + + &Time Display Format + 时间显示格式(&T) + + + Copy + 复制 + + + Manual pages + 说明文档 + + + Apply as Filter + 作为过滤器应用 + + + Prepare as Filter + 准备作为过滤器 + + + SCTP + SCTP + + + TCP Stream Graphs + TCP 流图形 + + + BACnet + BACnet + + + HTTP + HTTP + + + &File + 文件(&F) + + + &Capture + 捕获(&C) + + + &Help + 帮助(&H) + + + &Go + 跳转(&G) + + + &View + 视图(&V) + + + &Analyze + 分析(&A) + + + Follow + 追踪流 + + + &Statistics + 统计(&S) + + + 29West + 29West + + + Topics + 主题 + + + Queues + 队列 + + + UIM + UIM + + + Telephon&y + 电话(&Y) + + + RTSP + RTSP + + + &Edit + 编辑(&E) + + + Packet Comments + 分组注释 + + + Main Toolbar + 主工具栏 + + + Display Filter Toolbar + 显示过滤器工具栏 + + + Open a capture file + 打开已保存的捕获文件 + + + Quit Wireshark + 退出 Wireshark + + + &Start + 开始(&S) + + + Start capturing packets + 开始捕获分组 + + + S&top + 停止(&T) + + + Stop capturing packets + 停止捕获分组 + + + No files found + 未找到文件 + + + &Contents + 内容(&C) + + + Wireshark Filter + Wireshark 过滤器 + + + TShark + TShark + + + Rawshark + + + + Dumpcap + Dumpcap + + + Mergecap + Mergecap + + + Editcap + Editcap + + + Text2pcap + + + + Website + 网站 + + + Downloads + 下载 + + + Wiki + Wiki + + + Sample Captures + 捕获示例 + + + &About Wireshark + 关于 Wireshark (&A) + + + Ask (Q&&A) + 提问 (问答平台) + + + Next Packet + 下一分组 + + + Go to the next packet + 转到下一分组 + + + Previous Packet + 前一分组 + + + Go to the previous packet + 转到前一分组 + + + First Packet + 首个分组 + + + Go to the first packet + 转到首个分组 + + + Last Packet + 最新分组 + + + Go to the last packet + 转到最新分组 + + + E&xpand Subtrees + 展开子树(&X) + + + Expand the current packet detail + 展开当前分组详情 + + + &Expand All + 展开全部(&E) + + + Expand packet details + 展开分组详情 + + + Collapse &All + 收起全部(&A) + + + Collapse all packet details + 收起所有分组详情 + + + Go to specified packet + 转到特定分组 + + + Merge one or more files + 合并一或多个文件 + + + Import a file + 导入文件 + + + &Save + 保存(&S) + + + Save as a different file + 另存为不同的文件 + + + Export specified packets + 导出指定分组 + + + Export TLS Session Keys… + 导出 TLS 会话密钥… + + + List Files + 列出文件 + + + Next File + 下一文件 + + + Previous File + 上一文件 + + + &Reload + 重新加载(&R) + + + Options + 选项 + + + Capture options + 捕获选项 + + + Capture filters + 捕获过滤器 + + + Refresh Interfaces + 刷新接口列表 + + + Refresh interfaces + 刷新接口列表 + + + &Restart + 重新开始(&R) + + + Restart current capture + 重新开始当前捕获 + + + As &CSV… + + + + As "C" &Arrays… + + + + As P&SML XML… + + + + As P&DML XML… + + + + As &JSON… + + + + Description + 描述 + + + Field Name + 字段名称 + + + Value + + + + As Filter + 作为过滤器 + + + Close this capture file + 关闭捕获文件 + + + Packet: + 分组: + + + Interface Toolbars + 接口工具栏 + + + Colorize Conversation + 对话着色 + + + Internals + 内部 + + + Additional Toolbars + 额外工具栏 + + + Conversation Filter + 对话过滤器 + + + Reliable Server Pooling (RSerPool) + + + + SOME/IP + + + + &DTN + + + + Osmux + + + + &Tools + Tools + 工具(&T) + + + Wireless Toolbar + 无线工具栏 + + + Help contents + 帮助内容 + + + FAQs + + + + Next Packet in Conversation + 对话中的下一个分组 + + + Go to the next packet in this conversation + 转至此对话中的下一个分组 + + + Previous Packet in Conversation + 对话中的上一个分组 + + + Go to the previous packet in this conversation + 转至此对话中的上一个分组 + + + Next Packet In History + 历史中的下一个分组 + + + Go to the next packet in your selection history + 转至您的选定历史中的下一个分组 + + + Previous Packet In History + 历史中的上一个分组 + + + Go to the previous packet in your selection history + 转至您的选定历史中的上一个分组 + + + Collapse Subtrees + 折叠子树 + + + Collapse the current packet detail + 折叠当前分组详细信息 + + + Go to Packet… + 转至分组… + + + &Merge… + 合并(&M)… + + + &Import from Hex Dump… + 从 Hex 转储导入(&I)… + + + Save this capture file + 保存捕获文件 + + + Save &As… + 另存为(&A)… + + + Export Specified Packets… + 导出特定分组… + + + Export Packet &Bytes… + 导出分组字节流(&B)… + + + &Print… + 打印(&P)… + + + Reload this file + 重新加载文件 + + + Reload as File Format/Capture + 重新载入为文件格式/捕获 + + + Copy this item's description + 复制此项的描述 + + + Copy this item's field name + 复制此项的字段名称 + + + Copy this item's value + 复制此项的值 + + + Copy this item as a display filter + 复制此项为显示过滤器 + + + Apply as Column + 应用为列 + + + Create a packet list column from the selected field. + 从选择的字段创建分组列表列。 + + + Find a packet + 查找一个分组 + + + Find the next packet + 查找下一分组 + + + Find the previous packet + 查找上一分组 + + + &Mark/Unmark Packet(s) + &Mark/Unmark Packet + 标记/取消标记 分组(&M) + + + Mark All Displayed + 标记所有显示的分组 + + + Mark all displayed packets + 标记所有已经显示的分组 + + + Unmark all displayed packets + 取消标记所有已经显示的分组 + + + Next Mark + 下一标记 + + + Go to the next marked packet + 转到下一个已标记的分组 + + + Previous Mark + 前一标记 + + + Go to the previous marked packet + 转到前一个已标记的分组 + + + &Ignore/Unignore Packet(s) + &Ignore/Unignore Packet + 忽略/取消忽略 分组(&I) + + + Ignore All Displayed + 忽略所有显示的分组 + + + Ignore all displayed packets + 忽略所有已经显示的分组 + + + Set/Unset Time Reference + 设置/取消设置 时间参考 + + + Set or unset a time reference for this packet + 设置或取消设置该分组的时间参考 + + + Unset All Time References + 取消设置所有时间参考 + + + Remove all time references + 移除所有时间参考 + + + Next Time Reference + 下一时间参考 + + + Go to the next time reference + 转到下一个时间参考 + + + Previous Time Reference + 前一时间参考 + + + Go to the previous time reference + 转到前一时间参考 + + + Shift or change packet timestamps + 平移或更改分组时间戳 + + + Delete All Packet Comments + 删除所有分组注释 + + + Remove all packet comments in the capture file + 删除捕获文件中的所有分组注释 + + + &Configuration Profiles… + 配置文件(&C)… + + + Configuration profiles + 配置文件 + + + Manage your configuration profiles + 管理配置文件 + + + Manage Wireshark's preferences + 管理 Wireshark 的首选项设置 + + + Capture File Properties + 捕获文件属性 + + + Capture file properties + 捕获文件属性 + + + &Protocol Hierarchy + 协议分级(&P) + + + Show a summary of protocols present in the capture file. + 显示捕获文件中存在协议的概要。 + + + Capinfos + 捕获信息 + + + Reordercap + Reordercap + + + Time Sequence (Stevens) + 时间序列 (Stevens) + + + TCP time sequence graph (Stevens) + TCP 时间序列图 (Stevens) + + + Throughput + 吞吐量 + + + Round Trip Time + 往返时间 + + + TCP round trip time + TCP 往返时间 + + + Window Scaling + 窗口尺寸 + + + TCP window scaling + TCP 窗口尺寸 + + + HTTP/2 Stream + + + + SIP Call + + + + Time Sequence (tcptrace) + 时间序列 (tcptrace) + + + TCP time sequence graph (tcptrace) + TCP 时间序列图 (tcptrace) + + + Analyse this Association + 分析关联 + + + Show All Associations + 显示所有助手 + + + Flow Graph + 流量图 + + + Flow sequence diagram + 流序列图 + + + ANCP + ANCP + + + ANCP statistics + ANCP统计 + + + Packets sorted by Instance ID + 分组按实例ID排序 + + + BACapp statistics sorted by instance ID + BACapp 统计按实例ID排序 + + + Packets sorted by IP + 分组按IP排序 + + + BACapp statistics sorted by IP + BACapp 统计按实例IP排序 + + + Packets sorted by object type + 分组按对象类型排序 + + + BACapp statistics sorted by object type + BACapp 统计按实例对象类型排序 + + + Packets sorted by service + 分组按服务排序 + + + BACapp statistics sorted by service + BACapp 统计按实例服务排序 + + + Collectd + Collectd + + + Collectd statistics + Collectd协议统计 + + + DNS + DNS + + + DNS statistics + DNS统计 + + + HART-IP + HART-IP + + + HART-IP statistics + HART-IP 统计 + + + HPFEEDS + HPFEEDS + + + hpfeeds statistics + hpfeeds 统计 + + + HTTP2 + HTTP2 + + + HTTP2 statistics + HTTP2 统计 + + + Packet Counter + 分组计数器 + + + HTTP packet counter + HTTP 分组计数器 + + + Requests + 请求 + + + HTTP requests + HTTP 请求 + + + Load Distribution + 负载分配 + + + HTTP load distribution + HTTP 负载分配 + + + Packet Lengths + 分组长度 + + + Packet length statistics + 分组长度统计 + + + Sametime + Sametime + + + Sametime statistics + Sametime协议统计 + + + SOME/IP Messages + + + + SOME/IP Message statistics + + + + SOME/IP-SD Entries + + + + SOME/IP-SD Entries statistics + + + + &LTP + + + + LTP segment and block statistics + + + + &ISUP Messages + ISUP 消息(&I) + + + ISUP message statistics + ISUP 消息统计 + + + Osmux packet counts + OSMUX 分组计数 + + + RTSP packet counts + RTSP分组计数 + + + SM&PP Operations + SMPP 操作(&P) + + + SMPP operation statistics + SMPP操作统计 + + + &UCP Messages + UCP 消息(&U) + + + UCP message statistics + UCP 消息统计 + + + F1AP + + + + F1AP Messages + + + + NGAP + + + + NGAP Messages + + + + Change the way packets are dissected + 修改分组解析方式 + + + Reload Lua Plugins + 重新载入 Lua 插件 + + + Reload Lua plugins + 重新载入 Lua 插件 + + + Advertisements by Topic + 根据主题的通告 + + + Advertisements by Source + 根据源的通告 + + + Advertisements by Transport + 根据传输层的通告 + + + Queries by Topic + 根据主题的查询 + + + Queries by Receiver + 根据接收者的查询 + + + Wildcard Queries by Pattern + 根据模式的通配查询 + + + Wildcard Queries by Receiver + 根据接收者的通配查询 + + + Advertisements by Queue + 根据队列的通告 + + + Queries by Queue + 根据队列的查询 + + + Streams + + + + LBT-RM + LBT-RM + + + LBT-RU + LBT-RU + + + Filter this Association + 过滤该助手 + + + Strip Headers… + + + + Strip headers and export higher level encapsulations to file + + + + &I/O Graphs + I/O 图表(&I) + + + &Conversations + 会话(&C) + + + &Endpoints + 端点(&E) + + + Shrink the main window text + 收缩主窗口文字 + + + Return the main window text to its normal size + 使主窗口文字返回正常大小 + + + Reset Layout + 重置布局 + + + Reset appearance layout to default size + 重置外观布局为默认尺寸 + + + Seconds Since First Captured Packet + + + + Show packet times as the seconds since the first captured packet. + + + + Tenths of a millisecond + + + + Hundredths of a millisecond + + + + Tenths of a microsecond + + + + Hundredths of a microsecond + + + + Packet &Diagram + 分组图(&D) + + + Show or hide the packet diagram + + + + Show each conversation hash table + 显示每个对话哈希表 + + + Show each dissector table and its entries + 显示每个解析器表及其项 + + + Show the currently supported protocols and display filter fields + 显示当前支持的协议和显示过滤器字段 + + + MAC Statistics + MAC 统计 + + + LTE MAC statistics + LTE MAC 统计 + + + RLC Statistics + RLC 统计 + + + LTE RLC statistics + LTE RLC 统计 + + + LTE RLC graph + LTE RLC 图表 + + + MTP3 Summary + MTP3 汇总 + + + MTP3 summary statistics + MTP3 汇总统计 + + + Bluetooth Devices + 蓝牙设备 + + + Bluetooth HCI Summary + 蓝牙 HCI 摘要 + + + Display Filter &Expression… + 显示过滤器表达式(&E)... + + + Display Filter Expression… + + + + REGISTER_STAT_GROUP_RSERPOOL + + + + Start of "REGISTER_STAT_GROUP_RSERPOOL" + + + + No GSM statistics registered + 没有已注册的 GSM 统计 + + + No LTE statistics registered + 没有 LTE 统计已注册 + + + No MTP3 statistics registered + 没有已注册的 MTP3 统计 + + + IAX2 Stream Analysis + IAX2 流分析 + + + Show Packet Bytes… + 显示分组字节… + + + Go to &Linked Packet + 转至链接的分组(&L) + + + UDP Multicast Streams + UDP 多播流 + + + Show UTP multicast stream statistics. + 显示 UTP 多播流统计。 + + + WLAN Traffic + WLAN 流量 + + + Show IEEE 802.11 wireless LAN statistics. + 显示 IEEE 802.11 无线 LAN 统计。 + + + Add a display filter button. + 添加一个显示过滤器按钮。 + + + Firewall ACL Rules + 防火墙 ACL 规则 + + + Create firewall ACL rules + 创建防火墙 ACL 规则 + + + &Full Screen + 全屏(&F) + + + Credentials + + + + MAC Address Blocks + + + + TLS Keylog Launcher + + + + Release Notes + + + + &Options… + 选项(&O)… + + + &Wireless + 无线(&W) + + + Capture &Filters… + 捕获过滤器(&F)… + + + As Plain &Text… + 为纯文本(&T)… + + + As Plain &Text + + + + As &CSV + + + + As &YAML + + + + All Visible Items + 所有可见项目 + + + All Visible Selected Tree Items + 选中树的所有可见项目 + + + Display Filter &Macros… + 显示过滤器宏(&M)… + + + &Find Packet… + 查找分组(&F)… + + + Find Ne&xt + 查找下一个(&N) + + + Find Pre&vious + 查找上一个(&v) + + + Mark or unmark each selected packet + + + + Ignore or unignore each selected packet + + + + U&nignore All Displayed + 取消忽略所有显示的分组(&n) + + + Unignore all displayed packets + + + + Time Shift… + 时间平移… + + + Inject TLS Secrets + + + + Embed used TLS secrets in the capture file + + + + Discard All Secrets + + + + Discard all decryption secrets in the capture file + + + + &Preferences… + 首选项(&P)… + + + TCP throughput + + + + Request Sequences + 请求序列 + + + HTTP Request Sequences + HTTP 请求序列 + + + Decode &As… + 解码为(&A)… + + + Export PDUs to File… + 导出 PDU 到文件… + + + Create graphs based on display filter fields + 基于显示过滤器字段创建图形 + + + &Main Toolbar + 主工具栏(&M) + + + Show or hide the main toolbar + 显示或隐藏主工具栏 + + + &Filter Toolbar + 过滤器工具栏(&F) + + + Show or hide the display filter toolbar + 显示或隐藏显示过滤器工具栏 + + + Conversations at different protocol levels + 对话在不同的协议层 + + + Endpoints at different protocol levels + 端点在不同的协议层 + + + Colorize Packet List + 着色分组列表 + + + Draw packets using your coloring rules + 使用您的着色规则来绘制分组 + + + &Zoom In + 放大(&Z) + + + Enlarge the main window text + 放大主窗口文本 + + + Zoom Out + 缩小 + + + Normal Size + 普通大小 + + + Resize Columns + 调整列宽 + + + Resize packet list columns to fit contents + 调整分组列表列以适应内容 + + + Date and Time of Day (1970-01-01 01:02:03.123456) + 日期和时间 (1970-01-01 01:02:03.123456) + + + Show packet times as the date and time of day. + 使用日期和时间来显示分组时间。 + + + Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + 年、年积日、时间 (1970/001 01:02:03.123456) + + + Show packet times as the year, day of the year and time of day. + 使用年、年积日和时间来显示分组时间。 + + + Time of Day (01:02:03.123456) + 时间 (01:02:03.123456) + + + Seconds Since 1970-01-01 + 自1970-01-01经过的秒数 + + + Show packet times as the seconds since the UNIX / POSIX epoch (1970-01-01). + 使用自 UNIX / POSIX 时间戳 (1970-01-01) 以来的秒数来显示分组时间。 + + + Seconds Since Previous Captured Packet + 自上一个捕获分组经过的秒数 + + + Show packet times as the seconds since the previous captured packet. + 使用自上一个被捕获分组的秒数来显示分组时间。 + + + Seconds Since Previous Displayed Packet + 自上一个显示分组经过的秒数 + + + Show packet times as the seconds since the previous displayed packet. + 使用自上一个被显示分组的秒数来显示分组时间。 + + + UTC Date and Time of Day (1970-01-01 01:02:03.123456) + UTC 日期和时间 (1970-01-01 01:02:03.123456) + + + Show packet times as the UTC date and time of day. + 使用 UTC 日期和时间来显示分组时间。 + + + UTC Year, Day of Year, and Time of Day (1970/001 01:02:03.123456) + UTC 年、年积日、时间 (1970/001 01:02:03.123456) + + + Show packet times as the UTC year, day of the year and time of day. + 使用 UTC 年、年积日和时间来显示分组时间。 + + + UTC Time of Day (01:02:03.123456) + UTC 时间 (01:02:03.123456) + + + Show packet times as the UTC time of day. + 使用 UTC 时间来显示分组时间。 + + + Automatic (from capture file) + 自动 (根据捕获文件) + + + Use the time precision indicated in the capture file. + 使用捕获文件中指示的时间精度。 + + + Seconds + + + + Tenths of a second + 十分之一秒 + + + Hundredths of a second + 百分之一秒 + + + Milliseconds + 毫秒 + + + Microseconds + 微秒 + + + Nanoseconds + 纳秒 + + + Display Seconds With Hours and Minutes + 显示小时、分钟和秒 + + + Display seconds with hours and minutes + 显示小时、分钟、秒 + + + Resolve &Physical Addresses + 解析物理地址(&P) + + + Show names for known MAC addresses. Lookups use a local database. + 显示已知 MAC 地址的名称。使用本地数据库查询。 + + + Resolve &Network Addresses + 解析网络地址(&N) + + + Show names for known IPv4, IPv6, and IPX addresses. Lookups can generate network traffic. + 显示已知的IPv4、IPv6和IPX地址的名称。查找会产生网络流量。 + + + Resolve &Transport Addresses + 解析传输层地址(&T) + + + Show names for known TCP, UDP, and SCTP services. Lookups can generate traffic on some systems. + 显示已知的 TCP、UDP 和 SCTP 服务的名称。在一些系统中,查找会产生流量。 + + + Wire&less Toolbar + 无线工具栏(&l) + + + Show or hide the wireless toolbar + 显示或隐藏无线工具栏 + + + &Status Bar + 状态栏(&S) + + + Show or hide the status bar + 显示或隐藏状态栏 + + + Packet &List + 分组列表(&L) + + + Show or hide the packet list + 显示或隐藏分组列表 + + + Packet &Details + 分组详情(&D) + + + Show or hide the packet details + 显示或隐藏分组详情 + + + Packet &Bytes + 分组字节流(&B) + + + Show or hide the packet bytes + 显示或隐藏分组字节流 + + + &Conversation Hash Tables + + + + &Dissector Tables + + + + &Supported Protocols + + + + MAP Summary + MAP 摘要 + + + GSM MAP summary statistics + GSM MAP 摘要统计 + + + RLC &Graph + + + + &Coloring Rules… + 着色规则(&C)… + + + Show Linked Packet in New Window + 在新窗口中显示已链接的分组 + + + New Coloring Rule… + New Conversation Rule… + 新建着色规则… + + + RTP Stream Analysis for selected stream. Press CTRL key for adding reverse stream too. + + + + RTP Player + RTP 播放器 + + + Play selected stream. Press CTRL key for playing reverse stream too. + + + + IA&X2 Stream Analysis + + + + Enabled Protocols… + Enable Protocols… + 启用的协议… + + + Wiki Protocol Page + Wiki 协议页面 + + + Open the Wireshark wiki page for this protocol. + 打开针对此协议的 Wireshark Wiki 页面。 + + + Filter Field Reference + 过滤器字段参考 + + + Open the display filter reference page for this filter field. + 打开此过滤器字段的显示过滤器参考。 + + + Go to the packet referenced by the selected field. + 转至选定字段引用的分组。 + + + &VoIP Calls + &VoIP 通话 + + + Open &Recent + 打开最近的文件(&R) + + + Name Resol&ution + 名称解析(&U) + + + Service &Response Time + 服务响应时间(&R) + + + &RTP + + + + S&CTP + + + + &ANSI + + + + &GSM + + + + &LTE + + + + &MTP3 + + + + &Open + 打开(&O) + + + &Quit + 退出(&Q) + + + &Close + 关闭(&C) + + + Display &Filters… + 显示过滤器(&F)… + + + &Unmark All Displayed + 取消标记所有显示的分组(&U) + + + All VoIP Calls + 全部VoIP呼叫 + + + SIP &Flows + SIP 流(&F) + + + SIP Flows + SIP流 + + + RTP Streams + RTP 流 + + + Edit the packet list coloring rules. + 编辑分组列表着色规则。 + + + Bluetooth ATT Server Attributes + ATT Server Attributes + 蓝牙 ATT 服务器属性 + + + Show Packet in New &Window + 在新窗口显示分组(&W) + + + Show this packet in a separate window. + 在单独窗口中显示此分组。 + + + Show the linked packet in a separate window. + 在独立窗口中显示链接分组。 + + + Auto Scroll in Li&ve Capture + 实时捕获时自动滚动(&V) + + + Automatically scroll to the last packet during a live capture. + 在实时捕获时,自动滚动屏幕到最新的分组。 + + + Expert Information + 专家信息 + + + Show expert notifications + 显示专家通知 + + + Add an expression to the display filter. + 添加一个表达式到显示过滤器。 + + + REGISTER_STAT_GROUP_UNSORTED + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + "REGISTER_STAT_GROUP_UNSORTED" 的启动 + + + No ANSI statistics registered + No tools registered + 没有 ANSI 统计已注册 + + + Resolved Addresses + 已解析的地址 + + + Show each table of resolved addresses as copyable text. + 将每个已解析地址的表格表示为可复制的文本。 + + + Color &1 + 颜色 &1 + + + Mark the current conversation with its own color. + Mark the current coversation with its own color. + 让当前的对话使用自己的颜色。 + + + Color &2 + 颜色 &2 + + + Color &3 + 颜色 &3 + + + Color &4 + 颜色 &4 + + + Color &5 + 颜色 &5 + + + Color &6 + 颜色 &6 + + + Color &7 + 颜色 &7 + + + Color &8 + 颜色 &8 + + + Color &9 + 颜色 &9 + + + Color 1&0 + 颜色 1&0 + + + Create a new coloring rule based on this field. + Create a new coloring rule based on this conversation. + 基于此栏创建一个新的着色规则。 + + + Reset Colorization + 重置着色 + + + Reset colorized conversations. + 重置着色的对话。 + + + RTP Stream Analysis + RTP 流分析 + + + Edit Resolved Name + 编辑解析的名称 + + + Manually edit a name resolution entry. + 手动编辑一个名称解析项。 + + + Enable and disable specific protocols + 启用和禁用特定的协议 + + + before quitting + 在退出前 + + + Save packets before merging? + 是否在合并之前保存分组? + + + A temporary capture file can't be merged. + 无法合并临时捕获文件。 + + + Save changes in "%1" before merging? + 是否在合并前保存对“%1”的更改? + + + Changes must be saved before the files can be merged. + 在文件合并之前,其更改必须先保存。 + + + Invalid Display Filter + 无效显示过滤器 + + + Invalid Read Filter + 无效读取过滤器 + + + The filter expression %1 isn't a valid read filter. (%2). + 过滤器表达式 %1 不是有效的读取过滤器 (%2)。 + + + before importing a capture + before importing a new capture + 导入新捕获前 + + + Unable to export to "%1". + 无法导出“%1”。 + + + You cannot export packets to the current capture file. + 您不能将分组导出到当前捕获文件。 + + + Do you want to save the changes you've made%1? + Do you want to save the captured packets + 您是否要保存已做出的更改%1? + + + Your captured packets will be lost if you don't save them. + 若不保存,您已经捕获的分组将会丢失。 + + + Do you want to save the changes you've made to the capture file "%1"%2? + 是否希望保存对捕获文件“%1”%2 的更改? + + + Your changes will be lost if you don't save them. + 若不保存,您的更改将会丢失。 + + + Check for Updates… + 检查更新... + + + Unable to drop files during capture. + 无法在捕获时拖放文件。 + + + Unknown file type returned by merge dialog. + + + + Please report this as a Wireshark issue at https://gitlab.com/wireshark/wireshark/-/issues. + + + + Unknown file type returned by export dialog. + + + + Do you want to stop the capture and save the captured packets%1? + 您是否要停止捕获,并保存已捕获的分组%1? + + + Do you want to save the captured packets%1? + 您是否要保存已捕获的分组%1? + + + Save before Continue + 继续前保存 + + + Stop and Save + 停止并保存 + + + Stop and Quit &without Saving + Stop and Quit without Saving + 停止并退出,不保存(&W) + + + Quit &without Saving + Quit without Saving + 直接退出,不保存(&W) + + + There is no "rtp.ssrc" field in this version of Wireshark. + + + + Please select an RTPv2 packet with an SSRC value + + + + SSRC value not found. + + + + Show or hide the toolbar + 显示或隐藏该工具栏 + + + Continue &without Saving + Continue without Saving + 继续,不保存(&W) + + + Stop and Continue &without Saving + Stop and Continue without Saving + 停止并继续,不保存(&W) + + + The Wireshark Network Analyzer + Wireshark 网络分析器 + + + Capturing from %1 + 正在捕获 %1 + + + before opening another file + 打开另一个文件前 + + + Merging files. + + + + %1: %2 + %1: %2 + + + Clear Menu + 清除菜单 + + + before closing the file + 关闭文件前 + + + Export Selected Packet Bytes + 导出选择分组字节流 + + + No Keys + 无密钥 + + + Raw data (*.bin *.dat *.raw);;All Files ( + 原始数据 (*.bin *.dat *.raw);;所有文件 ( + + + Couldn't copy text. Try another item. + 无法复制文本。请尝试其他对象。 + + + Are you sure you want to remove all packet comments? + 确实要删除所有分组注释吗? + + + Unable to build conversation filter. + 无法建立对话过滤器。 + + + before reloading the file + 重新载入文件前 + + + Error compiling filter for this conversation. + 为此对话编译过滤器时出错。 + + + No previous/next packet in conversation. + 此对话中没有上一个/下一个分组。 + + + No interface selected. + + + + Saving %1… + + + + Configure all extcaps before start of capture. + + + + Invalid capture filter. + + + + (empty comment) + placeholder for empty comment + + + + Add New Comment… + + + + Edit "%1" + edit packet comment + + + + Delete "%1" + delete packet comment + + + + Delete packet comments + + + + Delete comments from %n packet(s) + + + + + + before starting a new capture + 在开始新捕获前 + + + before reloading Lua plugins + + + + Please wait while Wireshark is initializing… + + + + before updating + + + + There are no TLS Session Keys to save. + 没有要保存的 TLS 会话密钥。 + + + Export TLS Session Keys (%Ln key(s)) + + 导出 TLS 会话密钥(%Ln 个密钥) + + + + TLS Session Keys (*.keys *.txt);;All Files ( + TLS 会话密钥 (*.keys *.txt);;所有文件( + + + No TLS Secrets + + + + There are no available secrets used to decrypt TLS traffic in the capture file. Would you like to view information about how to decrypt TLS traffic on the wiki? + + + + Are you sure you want to discard all decryption secrets? + + + + No filter available. Try another %1. + + + + column + + + + item + + + + The "%1" column already exists. + + + + The "%1" column already exists as "%2". + + + + RTP packet search failed + + + + No Interface Selected. + + + + before restarting the capture + 重新开始捕获前 + + + Wiki Page for %1 + %1 的 Wiki 页面 + + + <p>The Wireshark Wiki is maintained by the community.</p><p>The page you are about to load might be wonderful, incomplete, wrong, or nonexistent.</p><p>Proceed to the wiki?</p> + <p>Wireshark Wiki 由社区进行维护。</p><p>您即将加载的页面可能是良好的、不完整的、有错误的,甚至不存在的。</p><p>继续访问该 Wiki?</p> + + + Loading + 正在载入 + + + Reloading + 正在重新加载 + + + Rescanning + 正在重新扫描 + + + + WlanStatisticsDialog + + Wireless LAN Statistics + 无线 LAN 统计 + + + Channel + 信道 + + + SSID + SSID + + + Percent Packets + 按分组百分比 + + + Percent Retry + 重试百分比 + + + Probe Reqs + Probe 请求 + + + Probe Resp + Probe 响应 + + + Auths + 验证 + + + Retry + 重试 + + + Deauths + 反验证 + + + Other + 其他 + + + diff --git a/ui/qt/wlan_statistics_dialog.cpp b/ui/qt/wlan_statistics_dialog.cpp new file mode 100644 index 00000000..0fc529ea --- /dev/null +++ b/ui/qt/wlan_statistics_dialog.cpp @@ -0,0 +1,757 @@ +/* wlan_statistics_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "wlan_statistics_dialog.h" + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include "main_application.h" + +// To do: +// - Add the name resolution checkbox +// - Add the "Only show defined networks" checkbox + +enum { + col_bssid_, + col_channel_, + col_ssid_, + col_pct_packets_, + col_pct_retry_, + col_retry_packets_, + col_beacons_, + col_data_packets_, + col_probe_reqs_, + col_probe_resps_, + col_auths_, + col_deauths_, + col_others_, + col_protection_ +}; + +enum { + wlan_network_row_type_ = 1000, + wlan_station_row_type_ +}; + +class WlanStationTreeWidgetItem : public QTreeWidgetItem +{ +public: + WlanStationTreeWidgetItem(const address *addr) : + QTreeWidgetItem (wlan_station_row_type_), + packets_(0), + retry_(0), + sent_(0), + received_(0), + probe_req_(0), + probe_resp_(0), + auth_(0), + deauth_(0), + other_(0) + { + copy_address(&addr_, addr); + setText(col_bssid_, address_to_qstring(&addr_)); + } + bool isMatch(const address *addr) { + return addresses_equal(&addr_, addr); + } + void update(const wlan_hdr_t *wlan_hdr) { + bool is_sender = addresses_equal(&addr_, &wlan_hdr->src); + + if (wlan_hdr->stats.fc_retry != 0) { + retry_++; + } + + // XXX Should we count received probes and auths? This is what the + // GTK+ UI does, but it seems odd. + switch (wlan_hdr->type) { + case MGT_PROBE_REQ: + probe_req_++; + break; + case MGT_PROBE_RESP: + probe_resp_++; + break; + case MGT_BEACON: + // Skip + break; + case MGT_AUTHENTICATION: + auth_++; + break; + case MGT_DEAUTHENTICATION: + deauth_++; + break; + case DATA: + case DATA_CF_ACK: + case DATA_CF_POLL: + case DATA_CF_ACK_POLL: + case DATA_QOS_DATA: + case DATA_QOS_DATA_CF_ACK: + case DATA_QOS_DATA_CF_POLL: + case DATA_QOS_DATA_CF_ACK_POLL: + if (is_sender) { + sent_++; + } else { + received_++; + } + break; + default: + other_++; + break; + } + if (wlan_hdr->type != MGT_BEACON) packets_++; + } + void draw(address *bssid, int num_packets) { + if (packets_ && num_packets > 0) { + setData(col_pct_packets_, Qt::UserRole, QVariant::fromValue(packets_ * 100.0 / num_packets)); + setData(col_pct_retry_, Qt::UserRole, QVariant::fromValue(retry_ * 100.0 / packets_)); + } else { + setData(col_pct_packets_, Qt::UserRole, QVariant::fromValue(0)); + setData(col_pct_retry_, Qt::UserRole, QVariant::fromValue(0)); + } + setText(col_beacons_, QString::number(sent_)); + setText(col_data_packets_, QString::number(received_)); + setText(col_retry_packets_, QString::number(retry_)); + setText(col_probe_reqs_, QString::number(probe_req_)); + setText(col_probe_resps_, QString::number(probe_resp_)); + setText(col_auths_, QString::number(auth_)); + setText(col_deauths_, QString::number(deauth_)); + setText(col_others_, QString::number(other_)); + + if (!is_broadcast_bssid(bssid) && addresses_data_equal(&addr_, bssid)) { + setText(col_protection_, QObject::tr("Base station")); + } + } + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != wlan_station_row_type_) return QTreeWidgetItem::operator< (other); + const WlanStationTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case col_bssid_: + return cmp_address(&addr_, &other_row->addr_) < 0; + case col_pct_packets_: + return packets_ < other_row->packets_; + case col_beacons_: + return sent_ < other_row->sent_; + case col_data_packets_: + return received_ < other_row->received_; + case col_probe_reqs_: + return probe_req_ < other_row->probe_req_; + case col_probe_resps_: + return probe_resp_ < other_row->probe_resp_; + case col_auths_: + return auth_ < other_row->auth_; + case col_deauths_: + return deauth_ < other_row->deauth_; + case col_others_: + return other_ < other_row->other_; + case col_retry_packets_: + case col_pct_retry_: + return retry_ < other_row->retry_; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList rowData() { + return QList() + << address_to_qstring(&addr_) + << data(col_pct_packets_, Qt::UserRole).toDouble() + << data(col_pct_retry_, Qt::UserRole).toDouble() << retry_ + << sent_ << received_ << probe_req_ << probe_resp_ + << auth_ << deauth_ << other_ << text(col_protection_); + } + const QString filterExpression() { + QString filter_expr = QString("wlan.addr==%1") + .arg(address_to_qstring(&addr_)); + return filter_expr; + } + +private: + address addr_; + int packets_; + int retry_; + int sent_; + int received_; + int probe_req_; + int probe_resp_; + int auth_; + int deauth_; + int other_; + +}; + +class WlanNetworkTreeWidgetItem : public QTreeWidgetItem +{ +public: + WlanNetworkTreeWidgetItem(QTreeWidget *parent, const wlan_hdr_t *wlan_hdr) : + QTreeWidgetItem (parent, wlan_network_row_type_), + beacon_(0), + data_packet_(0), + retry_packet_(0), + probe_req_(0), + probe_resp_(0), + auth_(0), + deauth_(0), + other_(0), + packets_(0) + { + updateBssid(wlan_hdr); + channel_ = wlan_hdr->stats.channel; + ssid_ = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + QString ssid_text; + + if (wlan_hdr->stats.ssid_len == 0) { + ssid_text = QObject::tr(""); + } else if (wlan_hdr->stats.ssid_len == 1 && wlan_hdr->stats.ssid[0] == 0) { + ssid_text = QObject::tr(""); + } else { + gchar *str = format_text(NULL, (const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + ssid_text = str; + wmem_free(NULL, str); + } + + setText(col_ssid_, ssid_text); + } + + bool isMatch(const wlan_hdr_t *wlan_hdr) { + bool is_bssid_match = false; + bool is_ssid_match = false; + bool update_bssid = false; + bool update_ssid = false; + // We want (but might not have) a unicast BSSID and a named SSID. Try + // to match the current packet and update our information if possible. + + if (addresses_equal(&bssid_, &wlan_hdr->bssid)) { + is_bssid_match = true; + } + + if ((wlan_hdr->stats.ssid_len > 0) && (wlan_hdr->stats.ssid[0] != 0)) { + QByteArray hdr_ssid = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + if (ssid_ == hdr_ssid) { + is_ssid_match = true; + } + } + + if (is_bssid_match && is_ssid_match) return true; + + // Probe requests. + if (wlan_hdr->type == MGT_PROBE_REQ) { + // Probes with visible SSIDs. Unicast or broadcast. + if (is_ssid_match) { + if (is_broadcast_ && !is_broadcast_bssid(&wlan_hdr->bssid)) { + update_bssid = true; + } + // Probes with hidden SSIDs. Unicast. + } else if ((wlan_hdr->stats.ssid_len == 1) && (wlan_hdr->stats.ssid[0] == 0)) { + if (!is_broadcast_ && addresses_equal(&bssid_, &wlan_hdr->bssid)) { + is_bssid_match = true; + update_ssid = true; + } + // Probes with no SSID. Broadcast. + } else if (ssid_.isEmpty() && wlan_hdr->stats.ssid_len < 1) { + if (is_broadcast_ && is_broadcast_bssid(&wlan_hdr->bssid)) { + return true; + } + } + // Non-probe requests (responses, beacons, etc) + } else { + if (is_ssid_match) { + if (is_broadcast_ && !is_broadcast_bssid(&wlan_hdr->bssid)) { + update_bssid = true; + } + } else if (wlan_hdr->stats.ssid_len < 1) { + // No SSID. + is_ssid_match = true; + } + if (is_bssid_match) { + if ((ssid_.isEmpty() || ssid_[0] == '\0') && (wlan_hdr->stats.ssid_len > 0) && (wlan_hdr->stats.ssid[0] != 0)) { + update_ssid = true; + } + } + } + + if (update_bssid) { + updateBssid(wlan_hdr); + is_bssid_match = true; + } + + if (update_ssid) { + gchar* str; + ssid_ = QByteArray::fromRawData((const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + str = format_text(NULL, (const char *)wlan_hdr->stats.ssid, wlan_hdr->stats.ssid_len); + setText(col_ssid_, str); + wmem_free(NULL, str); + is_ssid_match = true; + } + + return is_bssid_match && is_ssid_match; + } + + void update(const wlan_hdr_t *wlan_hdr) { + if (channel_ == 0 && wlan_hdr->stats.channel != 0) { + channel_ = wlan_hdr->stats.channel; + } + if (text(col_protection_).isEmpty() && wlan_hdr->stats.protection[0] != 0) { + setText(col_protection_, wlan_hdr->stats.protection); + } + if (wlan_hdr->stats.fc_retry != 0) { + retry_packet_++; + } + + switch (wlan_hdr->type) { + case MGT_PROBE_REQ: + probe_req_++; + break; + case MGT_PROBE_RESP: + probe_resp_++; + break; + case MGT_BEACON: + beacon_++; + break; + case MGT_AUTHENTICATION: + auth_++; + break; + case MGT_DEAUTHENTICATION: + deauth_++; + break; + case DATA: + case DATA_CF_ACK: + case DATA_CF_POLL: + case DATA_CF_ACK_POLL: + case DATA_QOS_DATA: + case DATA_QOS_DATA_CF_ACK: + case DATA_QOS_DATA_CF_POLL: + case DATA_QOS_DATA_CF_ACK_POLL: + data_packet_++; + break; + default: + other_++; + break; + } + packets_++; + + WlanStationTreeWidgetItem* sender_ws_ti = NULL; + WlanStationTreeWidgetItem* receiver_ws_ti = NULL; + foreach (QTreeWidgetItem *cur_ti, stations_) { + WlanStationTreeWidgetItem *cur_ws_ti = dynamic_cast(cur_ti); + if (cur_ws_ti && (cur_ws_ti->isMatch(&wlan_hdr->src))) sender_ws_ti = cur_ws_ti; + if (cur_ws_ti && (cur_ws_ti->isMatch(&wlan_hdr->dst))) receiver_ws_ti = cur_ws_ti; + if (sender_ws_ti && receiver_ws_ti) break; + } + if (!sender_ws_ti) { + sender_ws_ti = new WlanStationTreeWidgetItem(&wlan_hdr->src); + stations_ << sender_ws_ti; + } + if (!receiver_ws_ti) { + receiver_ws_ti = new WlanStationTreeWidgetItem(&wlan_hdr->dst); + stations_ << receiver_ws_ti; + } + sender_ws_ti->update(wlan_hdr); + receiver_ws_ti->update(wlan_hdr); + } + + void draw(int num_packets) { + if (channel_ > 0) setText(col_channel_, QString::number(channel_)); + setData(col_pct_packets_, Qt::UserRole, QVariant::fromValue(packets_ * 100.0 / num_packets)); + setData(col_pct_retry_, Qt::UserRole, QVariant::fromValue(retry_packet_ * 100.0 / packets_)); + setText(col_retry_packets_, QString::number(retry_packet_)); + setText(col_beacons_, QString::number(beacon_)); + setText(col_data_packets_, QString::number(data_packet_)); + setText(col_probe_reqs_, QString::number(probe_req_)); + setText(col_probe_resps_, QString::number(probe_resp_)); + setText(col_auths_, QString::number(auth_)); + setText(col_deauths_, QString::number(deauth_)); + setText(col_others_, QString::number(other_)); + } + + void addStations() { + foreach (QTreeWidgetItem *cur_ti, stations_) { + WlanStationTreeWidgetItem *cur_ws_ti = dynamic_cast(cur_ti); + cur_ws_ti->draw(&bssid_, packets_ - beacon_); + for (int col = 0; col < treeWidget()->columnCount(); col++) { + // int QTreeWidgetItem::textAlignment(int column) const + // Returns the text alignment for the label in the given column. + // Note: This function returns an int for historical reasons. It will be corrected to return Qt::Alignment in Qt 7. + cur_ws_ti->setTextAlignment(col, static_cast(treeWidget()->headerItem()->textAlignment(col))); + } + } + + addChildren(stations_); + stations_.clear(); + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != wlan_network_row_type_) return QTreeWidgetItem::operator< (other); + const WlanNetworkTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case col_bssid_: + return cmp_address(&bssid_, &other_row->bssid_) < 0; + case col_channel_: + return channel_ < other_row->channel_; + case col_ssid_: + return ssid_ < other_row->ssid_; + case col_pct_packets_: + return packets_ < other_row->packets_; + case col_beacons_: + return beacon_ < other_row->beacon_; + case col_data_packets_: + return data_packet_ < other_row->data_packet_; + case col_probe_reqs_: + return probe_req_ < other_row->probe_req_; + case col_probe_resps_: + return probe_resp_ < other_row->probe_resp_; + case col_auths_: + return auth_ < other_row->auth_; + case col_deauths_: + return deauth_ < other_row->deauth_; + case col_others_: + return other_ < other_row->other_; + case col_protection_: + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList rowData() { + return QList() + << address_to_qstring(&bssid_) << channel_ << text(col_ssid_) + << data(col_pct_packets_, Qt::UserRole).toDouble() + << data(col_pct_retry_, Qt::UserRole).toDouble() + << retry_packet_ << beacon_ << data_packet_ << probe_req_ + << probe_resp_ << auth_ << deauth_ << other_ + << text(col_protection_); + } + + const QString filterExpression() { + QString filter_expr = QString("(wlan.bssid==%1") + .arg(address_to_qstring(&bssid_)); + if (!ssid_.isEmpty() && ssid_[0] != '\0') { + filter_expr += QString(" || wlan.ssid==\"%1\"") + .arg(ssid_.constData()); + } + filter_expr += ")"; + return filter_expr; + } + +private: + address bssid_; + bool is_broadcast_; + int channel_; + QByteArray ssid_; + int beacon_; + int data_packet_; + int retry_packet_; + int probe_req_; + int probe_resp_; + int auth_; + int deauth_; + int other_; + int packets_; + + // Adding items one at a time is slow. Gather up the stations in a list + // and add them all at once later. + QListstations_; + + void updateBssid(const wlan_hdr_t *wlan_hdr) { + copy_address(&bssid_, &wlan_hdr->bssid); + is_broadcast_ = is_broadcast_bssid(&bssid_); + setText(col_bssid_, address_to_qstring(&bssid_)); + } +}; + +static const QString network_col_0_title_ = QObject::tr("BSSID"); +static const QString network_col_6_title_ = QObject::tr("Beacons"); +static const QString network_col_7_title_ = QObject::tr("Data Pkts"); +static const QString network_col_13_title_ = QObject::tr("Protection"); + +static const QString node_col_0_title_ = QObject::tr("Address"); +static const QString node_col_4_title_ = QObject::tr("Pkts Sent"); +static const QString node_col_5_title_ = QObject::tr("Pkts Received"); +static const QString node_col_11_title_ = QObject::tr("Comment"); + +WlanStatisticsDialog::WlanStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) : + TapParameterDialog(parent, cf, HELP_STATS_WLAN_TRAFFIC_DIALOG), + packet_count_(0), + cur_network_(0), + add_station_timer_(0) +{ + setWindowSubtitle(tr("Wireless LAN Statistics")); + loadGeometry(parent.width() * 4 / 5, parent.height() * 3 / 4, "WlanStatisticsDialog"); + + QStringList header_labels = QStringList() + << "" << tr("Channel") << tr("SSID") << tr("Percent Packets") << tr("Percent Retry") + << tr("Retry") << "" << "" << tr("Probe Reqs") << tr("Probe Resp") << tr("Auths") + << tr("Deauths") << tr("Other"); + statsTreeWidget()->setHeaderLabels(header_labels); + updateHeaderLabels(); + packets_delegate_ = new PercentBarDelegate(); + statsTreeWidget()->setItemDelegateForColumn(col_pct_packets_, packets_delegate_); + retry_delegate_ = new PercentBarDelegate(); + statsTreeWidget()->setItemDelegateForColumn(col_pct_retry_, retry_delegate_); + statsTreeWidget()->sortByColumn(col_bssid_, Qt::AscendingOrder); + + // resizeColumnToContents doesn't work well here, so set sizes manually. + int one_em = fontMetrics().height(); + for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) { + switch (col) { + case col_bssid_: + statsTreeWidget()->setColumnWidth(col, one_em * 11); + break; + case col_ssid_: + statsTreeWidget()->setColumnWidth(col, one_em * 8); + break; + case col_pct_packets_: + case col_pct_retry_: + case col_protection_: + statsTreeWidget()->setColumnWidth(col, one_em * 6); + break; + default: + // The rest are numeric + statsTreeWidget()->setColumnWidth(col, one_em * 4); + statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); + break; + } + } + + addFilterActions(); + + if (filter) { + setDisplayFilter(filter); + } + + add_station_timer_ = new QElapsedTimer(); + + connect(statsTreeWidget(), SIGNAL(itemSelectionChanged()), + this, SLOT(updateHeaderLabels())); + + // Set handler for when display filter string is changed. + connect(this, SIGNAL(updateFilter(QString)), + this, SLOT(filterUpdated(QString))); +} + +WlanStatisticsDialog::~WlanStatisticsDialog() +{ + delete packets_delegate_; + delete retry_delegate_; + delete add_station_timer_; +} + +void WlanStatisticsDialog::tapReset(void *ws_dlg_ptr) +{ + WlanStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + if (!ws_dlg) return; + + ws_dlg->statsTreeWidget()->clear(); + ws_dlg->packet_count_ = 0; +} + +tap_packet_status WlanStatisticsDialog::tapPacket(void *ws_dlg_ptr, _packet_info *, epan_dissect *, const void *wlan_hdr_ptr, tap_flags_t) +{ + WlanStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + const wlan_hdr_t *wlan_hdr = (const wlan_hdr_t *)wlan_hdr_ptr; + if (!ws_dlg || !wlan_hdr) return TAP_PACKET_DONT_REDRAW; + + guint16 frame_type = wlan_hdr->type & 0xff0; + if (!((frame_type == 0x0) || (frame_type == 0x20) || (frame_type == 0x30)) + || ((frame_type == 0x20) && DATA_FRAME_IS_NULL(wlan_hdr->type))) { + /* Not a management or non null data or extension frame; let's skip it */ + return TAP_PACKET_DONT_REDRAW; + } + + ws_dlg->packet_count_++; + + // XXX This is very slow for large numbers of networks. We might be + // able to store networks in a cache keyed on BSSID+SSID instead. + WlanNetworkTreeWidgetItem *wn_ti = NULL; + for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { + QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); + if (ti->type() != wlan_network_row_type_) continue; + WlanNetworkTreeWidgetItem *cur_wn_ti = static_cast(ti); + + if (cur_wn_ti->isMatch(wlan_hdr)) { + wn_ti = cur_wn_ti; + break; + } + } + + if (!wn_ti) { + wn_ti = new WlanNetworkTreeWidgetItem(ws_dlg->statsTreeWidget(), wlan_hdr); + for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) { + // int QTreeWidgetItem::textAlignment(int column) const + // Returns the text alignment for the label in the given column. + // Note: This function returns an int for historical reasons. It will be corrected to return Qt::Alignment in Qt 7. + wn_ti->setTextAlignment(col, static_cast(ws_dlg->statsTreeWidget()->headerItem()->textAlignment(col))); + } + } + + wn_ti->update(wlan_hdr); + return TAP_PACKET_REDRAW; +} + +void WlanStatisticsDialog::tapDraw(void *ws_dlg_ptr) +{ + WlanStatisticsDialog *ws_dlg = static_cast(ws_dlg_ptr); + if (!ws_dlg) return; + + for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { + QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); + if (ti->type() != wlan_network_row_type_) continue; + + WlanNetworkTreeWidgetItem *wn_ti = static_cast(ti); + wn_ti->draw(ws_dlg->packet_count_); + } +} + +const QString WlanStatisticsDialog::filterExpression() +{ + QString filter_expr; + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + + if (ti->type() == wlan_network_row_type_) { + WlanNetworkTreeWidgetItem *wn_ti = static_cast(ti); + filter_expr = wn_ti->filterExpression(); + } else if (ti->type() == wlan_station_row_type_) { + WlanStationTreeWidgetItem *ws_ti = static_cast(ti); + filter_expr = ws_ti->filterExpression(); + } + } + return filter_expr; +} + +void WlanStatisticsDialog::fillTree() +{ + if (!registerTapListener("wlan", + this, + displayFilter_.toLatin1().data(), + TL_REQUIRES_NOTHING, + tapReset, + tapPacket, + tapDraw)) { + reject(); + return; + } + + statsTreeWidget()->setSortingEnabled(false); + cap_file_.retapPackets(); + tapDraw(this); + removeTapListeners(); + statsTreeWidget()->setSortingEnabled(true); + + // Don't freeze if we have a large number of stations. + cur_network_ = 0; + QTimer::singleShot(0, this, SLOT(addStationTreeItems())); +} + +static const int add_station_interval_ = 5; // ms +void WlanStatisticsDialog::addStationTreeItems() +{ + add_station_timer_->start(); + while (add_station_timer_->elapsed() < add_station_interval_ && cur_network_ < statsTreeWidget()->topLevelItemCount()) { + QTreeWidgetItem *ti = statsTreeWidget()->topLevelItem(cur_network_); + if (ti->type() != wlan_network_row_type_) continue; + + WlanNetworkTreeWidgetItem *wn_ti = static_cast(ti); + wn_ti->addStations(); + ++cur_network_; + } + + if (cur_network_ < statsTreeWidget()->topLevelItemCount()) { + QTimer::singleShot(0, this, SLOT(addStationTreeItems())); + } +} + +void WlanStatisticsDialog::updateHeaderLabels() +{ + if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == wlan_station_row_type_) { + statsTreeWidget()->headerItem()->setText(col_bssid_, node_col_0_title_); + statsTreeWidget()->headerItem()->setText(col_beacons_, node_col_4_title_); + statsTreeWidget()->headerItem()->setText(col_data_packets_, node_col_5_title_); + statsTreeWidget()->headerItem()->setText(col_protection_, node_col_11_title_); + } else { + statsTreeWidget()->headerItem()->setText(col_bssid_, network_col_0_title_); + statsTreeWidget()->headerItem()->setText(col_beacons_, network_col_6_title_); + statsTreeWidget()->headerItem()->setText(col_data_packets_, network_col_7_title_); + statsTreeWidget()->headerItem()->setText(col_protection_, network_col_13_title_); + } +} + +void WlanStatisticsDialog::captureFileClosing() +{ + remove_tap_listener(this); + + WiresharkDialog::captureFileClosing(); +} + +// Store filter from signal. +void WlanStatisticsDialog::filterUpdated(QString filter) +{ + displayFilter_ = filter; +} + +// This is how an item is represented for exporting. +QList WlanStatisticsDialog::treeItemData(QTreeWidgetItem *it) const +{ + // Cast up to our type. + WlanNetworkTreeWidgetItem *nit = dynamic_cast(it); + if (nit) { + return nit->rowData(); + } + // TODO: not going to cast to WlanStationTreeWidgetItem* and do the same as + // some of the columns are different... + + return QList(); +} + +// Stat command + args + +static void +wlan_statistics_init(const char *args, void*) { + QStringList args_l = QString(args).split(','); + QByteArray filter; + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(",").toUtf8(); + } + mainApp->emitStatCommandSignal("WlanStatistics", filter.constData(), NULL); +} + +static stat_tap_ui wlan_statistics_ui = { + REGISTER_STAT_GROUP_GENERIC, + NULL, + "wlan,stat", + wlan_statistics_init, + 0, + NULL +}; + +extern "C" { + +void register_tap_listener_qt_wlan_statistics(void); + +void +register_tap_listener_qt_wlan_statistics(void) +{ + register_stat_tap_ui(&wlan_statistics_ui, NULL); +} + +} diff --git a/ui/qt/wlan_statistics_dialog.h b/ui/qt/wlan_statistics_dialog.h new file mode 100644 index 00000000..ad5c7006 --- /dev/null +++ b/ui/qt/wlan_statistics_dialog.h @@ -0,0 +1,53 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WLANSTATISTICSDIALOG_H +#define WLANSTATISTICSDIALOG_H + +#include "tap_parameter_dialog.h" +#include + +class QElapsedTimer; + +class WlanStatisticsDialog : public TapParameterDialog +{ + Q_OBJECT + +public: + WlanStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter); + ~WlanStatisticsDialog(); + +protected: + void captureFileClosing(); + +private: + int packet_count_; + int cur_network_; + PercentBarDelegate *packets_delegate_, *retry_delegate_; + QElapsedTimer *add_station_timer_; + QString displayFilter_; + + // Callbacks for register_tap_listener + static void tapReset(void *ws_dlg_ptr); + static tap_packet_status tapPacket(void *ws_dlg_ptr, struct _packet_info *, struct epan_dissect *, const void *wlan_hdr_ptr, tap_flags_t flags); + static void tapDraw(void *ws_dlg_ptr); + + virtual const QString filterExpression(); + + // How each item will be exported + virtual QList treeItemData(QTreeWidgetItem *) const; + +private slots: + virtual void fillTree(); + void addStationTreeItems(); + void updateHeaderLabels(); + void filterUpdated(QString filter); +}; + +#endif // WLANSTATISTICSDIALOG_H -- cgit v1.2.3