3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "main_window.h"
25 * The generated Ui_MainWindow::setupUi() can grow larger than our configured limit,
26 * so turn off -Wframe-larger-than= for ui_main_window.h.
28 DIAG_OFF(frame-larger-than=)
29 #include <ui_main_window.h>
30 DIAG_ON(frame-larger-than=)
32 #include <epan/addr_resolv.h>
33 #include "epan/dissector_filters.h"
34 #include <epan/epan_dissect.h>
35 #include <wsutil/filesystem.h>
36 #include <ws_version_info.h>
37 #include <epan/prefs.h>
38 #include <epan/stats_tree_priv.h>
39 #include <epan/plugin_if.h>
40 #include <epan/export_object.h>
42 #include "ui/commandline.h"
43 #include "ui/iface_toolbar.h"
46 #include "ui/capture.h"
47 #include <capchild/capture_session.h>
50 #include "ui/alert_box.h"
52 #include "ui/capture_ui_utils.h"
54 #include "ui/capture_globals.h"
55 #include "ui/main_statusbar.h"
56 #include "ui/recent.h"
57 #include "ui/recent_utils.h"
59 #include "ui/preference_utils.h"
61 #include "byte_view_tab.h"
63 #include "capture_interfaces_dialog.h"
65 #include "conversation_colorize_action.h"
66 #include "export_object_action.h"
67 #include "display_filter_edit.h"
68 #include "export_dissection_dialog.h"
69 #include "file_set_dialog.h"
70 #include "funnel_statistics.h"
71 #include "import_text_dialog.h"
72 #include "interface_toolbar.h"
73 #include "packet_list.h"
74 #include "wireless_timeline.h"
75 #include "proto_tree.h"
76 #include "simple_dialog.h"
77 #include "stock_icon.h"
78 #include "tap_parameter_dialog.h"
79 #include "wireless_frame.h"
80 #include "wireshark_application.h"
82 #include "additional_toolbar.h"
83 #include "variant_pointer.h"
85 #include "qt_ui_utils.h"
87 #include <ui/qt/widgets/drag_drop_toolbar.h>
90 #include <QActionGroup>
91 #include <QDesktopWidget>
93 #include <QMessageBox>
94 #include <QMetaObject>
97 #include <QToolButton>
98 #include <QTreeWidget>
101 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
102 #include <QtMacExtras/QMacNativeToolBar>
106 //menu_recent_file_write_all
108 // If we ever add support for multiple windows this will need to be replaced.
109 static MainWindow *gbl_cur_main_window_ = NULL;
111 void pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
113 gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
116 static void plugin_if_mainwindow_apply_filter(gconstpointer user_data)
118 if (!gbl_cur_main_window_ || !user_data)
121 GHashTable * data_set = (GHashTable *) user_data;
123 if (g_hash_table_lookup_extended(data_set, "filter_string", NULL, NULL)) {
124 QString filter((const char *)g_hash_table_lookup(data_set, "filter_string"));
125 gbl_cur_main_window_->filterPackets(filter);
129 static void plugin_if_mainwindow_preference(gconstpointer user_data)
131 if (!gbl_cur_main_window_ || !user_data)
134 GHashTable * data_set = (GHashTable *) user_data;
135 const char * module_name;
136 const char * pref_name;
137 const char * pref_value;
139 if (g_hash_table_lookup_extended(data_set, "pref_module", NULL, (void**)&module_name) &&
140 g_hash_table_lookup_extended(data_set, "pref_key", NULL, (void**)&pref_name) &&
141 g_hash_table_lookup_extended(data_set, "pref_value", NULL, (void**)&pref_value))
143 if (prefs_store_ext(module_name, pref_name, pref_value)) {
144 wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
145 wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
150 static void plugin_if_mainwindow_gotoframe(gconstpointer user_data)
152 if (!gbl_cur_main_window_ || !user_data)
155 GHashTable * data_set = (GHashTable *) user_data;
158 if (g_hash_table_lookup_extended(data_set, "frame_nr", NULL, &framenr)) {
159 if (GPOINTER_TO_UINT(framenr) != 0)
160 gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr));
166 static void plugin_if_mainwindow_get_ws_info(gconstpointer user_data)
168 if (!gbl_cur_main_window_ || !user_data)
171 GHashTable * data_set = (GHashTable *)user_data;
172 ws_info_t *ws_info = NULL;
174 if (!g_hash_table_lookup_extended(data_set, "ws_info", NULL, (void**)&ws_info))
177 CaptureFile *cfWrap = gbl_cur_main_window_->captureFile();
178 capture_file *cf = cfWrap->capFile();
180 ws_info->ws_info_supported = true;
183 ws_info->cf_state = cf->state;
184 ws_info->cf_count = cf->count;
186 g_free(ws_info->cf_filename);
187 ws_info->cf_filename = g_strdup(cf->filename);
189 if (cf->state == FILE_READ_DONE && cf->current_frame) {
190 ws_info->cf_framenr = cf->current_frame->num;
191 ws_info->frame_passed_dfilter = (cf->current_frame->flags.passed_dfilter == 1);
193 ws_info->cf_framenr = 0;
194 ws_info->frame_passed_dfilter = FALSE;
196 } else if (ws_info->cf_state != FILE_CLOSED) {
197 /* Initialise the ws_info structure */
198 ws_info->cf_count = 0;
200 g_free(ws_info->cf_filename);
201 ws_info->cf_filename = NULL;
203 ws_info->cf_framenr = 0;
204 ws_info->frame_passed_dfilter = FALSE;
205 ws_info->cf_state = FILE_CLOSED;
209 #endif /* HAVE_LIBPCAP */
211 static void plugin_if_mainwindow_update_toolbars(gconstpointer user_data)
213 if (!gbl_cur_main_window_ || ! user_data)
216 GHashTable * data_set = (GHashTable *)user_data;
217 if (g_hash_table_lookup_extended(data_set, "toolbar_name", NULL, NULL)) {
218 QString toolbarName((const char *)g_hash_table_lookup(data_set, "toolbar_name"));
219 gbl_cur_main_window_->removeAdditionalToolbar(toolbarName);
224 static void mainwindow_add_toolbar(const iface_toolbar *toolbar_entry)
226 if (gbl_cur_main_window_ && toolbar_entry)
228 gbl_cur_main_window_->addInterfaceToolbar(toolbar_entry);
232 static void mainwindow_remove_toolbar(const gchar *menu_title)
234 if (gbl_cur_main_window_ && menu_title)
236 gbl_cur_main_window_->removeInterfaceToolbar(menu_title);
241 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
245 va_start(ap, msg_format);
246 SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
254 * Alert box, with optional "don't show this message again" variable
255 * and checkbox, and optional secondary text.
258 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
259 const char *secondary_msg, const char *msg_format, ...)
261 if (notagain && *notagain) {
267 va_start(ap, msg_format);
268 SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
271 sd.setDetailedText(secondary_msg);
273 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
274 QCheckBox *cb = NULL;
276 cb = new QCheckBox();
277 cb->setChecked(true);
278 cb->setText(QObject::tr("Don't show this message again."));
285 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
286 if (notagain && cb) {
287 *notagain = cb->isChecked();
293 * Error alert box, taking a format and a va_list argument.
296 vsimple_error_message_box(const char *msg_format, va_list ap)
299 // We want to quit after reading the capture file, hence
300 // we don't actually open the error dialog.
301 if (global_commandline_info.quit_after_cap)
305 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
310 * Warning alert box, taking a format and a va_list argument.
313 vsimple_warning_message_box(const char *msg_format, va_list ap)
316 // We want to quit after reading the capture file, hence
317 // we don't actually open the error dialog.
318 if (global_commandline_info.quit_after_cap)
322 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_WARN, ESD_BTN_OK, msg_format, ap);
326 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
327 QList<QAction *> actions = parent_menu->actions();
328 QList<QAction *>::const_iterator i;
329 for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
330 if ((*i)->text()==menu_text) {
334 // If we get here there menu entry was not found, add a sub menu
335 return parent_menu->addMenu(menu_text);
338 MainWindow::MainWindow(QWidget *parent) :
340 main_ui_(new Ui::MainWindow),
341 cur_layout_(QVector<unsigned>()),
345 previous_focus_(NULL),
346 file_set_dialog_(NULL),
347 show_hide_actions_(NULL),
348 time_display_actions_(NULL),
349 time_precision_actions_(NULL),
350 funnel_statistics_(NULL),
352 was_maximized_(false),
353 capture_stopping_(false),
354 capture_filter_valid_(false)
356 , capture_interfaces_dialog_(NULL)
362 , pipe_notifier_(NULL)
364 #if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
368 if (!gbl_cur_main_window_) {
369 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
370 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
371 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
372 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
374 gbl_cur_main_window_ = this;
376 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
379 // setpUi calls QMetaObject::connectSlotsByName(this). connectSlotsByName
380 // iterates over *all* of our children, looking for matching "on_" slots.
381 // The fewer children we have at this point the better.
382 main_ui_->setupUi(this);
383 #ifdef HAVE_SOFTWARE_UPDATE
384 update_action_ = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
386 setWindowIcon(wsApp->normalIcon());
387 setTitlebarForCaptureFile();
388 setMenusForCaptureFile();
389 setForCapturedPackets(false);
390 setMenusForFileSet(false);
391 interfaceSelectionChanged();
392 loadWindowGeometry();
395 main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
398 qRegisterMetaType<FilterAction::Action>("FilterAction::Action");
399 qRegisterMetaType<FilterAction::ActionType>("FilterAction::ActionType");
400 connect(this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)),
401 this, SLOT(queuedFilterAction(QString,FilterAction::Action,FilterAction::ActionType)),
402 Qt::QueuedConnection);
404 //To prevent users use features before initialization complete
405 //Otherwise unexpected problems may occur
406 setFeaturesEnabled(false);
407 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
408 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(applyGlobalCommandLineOptions()));
409 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
410 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
411 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
412 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
413 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addPluginIFStructures()));
414 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initConversationMenus()));
415 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initExportObjectsMenus()));
417 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
418 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
419 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
420 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
421 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
422 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
424 connect(wsApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures()));
425 updateRecentCaptures();
427 #ifdef HAVE_SOFTWARE_UPDATE
428 connect(wsApp, SIGNAL(softwareUpdateRequested()), this, SLOT(softwareUpdateRequested()),
429 Qt::BlockingQueuedConnection);
430 connect(wsApp, SIGNAL(softwareUpdateClose()), this, SLOT(close()),
431 Qt::BlockingQueuedConnection);
434 df_combo_box_ = new DisplayFilterCombo();
435 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
436 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
437 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
438 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
439 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
440 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
441 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
442 connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
443 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
444 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
446 funnel_statistics_ = new FunnelStatistics(this, capture_file_);
447 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
448 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
449 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
450 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
451 this, SLOT(openCaptureFile(QString,QString)));
452 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
454 file_set_dialog_ = new FileSetDialog(this);
455 connect(file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
456 this, SLOT(openCaptureFile(QString)));
458 initMainToolbarIcons();
460 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
462 // Make sure filter expressions overflow into a menu instead of a
463 // larger toolbar. We do this by adding them to a child toolbar.
464 // https://bugreports.qt.io/browse/QTBUG-2472
465 filter_expression_toolbar_ = new DragDropToolBar();
466 filter_expression_toolbar_->setStyleSheet("QToolBar { background: none; border: none; }");
467 filter_expression_toolbar_->setContextMenuPolicy(Qt::CustomContextMenu);
468 connect(filter_expression_toolbar_, SIGNAL(customContextMenuRequested(QPoint)),
469 this, SLOT(filterToolbarCustomMenuHandler(QPoint)));
470 connect(filter_expression_toolbar_, SIGNAL(actionMoved(QAction*, int, int)),
471 this, SLOT(filterToolbarActionMoved(QAction*, int, int)));
473 main_ui_->displayFilterToolBar->addWidget(filter_expression_toolbar_);
475 wireless_frame_ = new WirelessFrame(this);
476 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
477 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
478 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
479 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
480 this, SLOT(showPreferencesDialog(QString)));
482 main_ui_->goToFrame->hide();
483 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
484 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
486 // XXX For some reason the cursor is drawn funny with an input mask set
487 // https://bugreports.qt-project.org/browse/QTBUG-7174
489 main_ui_->searchFrame->hide();
490 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
491 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
492 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
493 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
495 main_ui_->addressEditorFrame->hide();
496 main_ui_->columnEditorFrame->hide();
497 main_ui_->preferenceEditorFrame->hide();
498 main_ui_->filterExpressionFrame->hide();
501 main_ui_->menuCapture->setEnabled(false);
504 // Set OS specific shortcuts for fullscreen mode
505 #if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
506 main_ui_->actionViewFullScreen->setShortcut(QKeySequence::FullScreen);
508 main_ui_->actionViewFullScreen->setShortcut(QKeySequence(Qt::Key_F11));
511 #if defined(Q_OS_MAC)
512 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
513 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
514 ntb->setIconSize(QSize(24, 24));
515 #endif // QT_MACEXTRAS_LIB
517 main_ui_->goToPacketLabel->setAttribute(Qt::WA_MacSmallSize, true);
518 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
519 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
520 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
522 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
526 #ifdef HAVE_SOFTWARE_UPDATE
527 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
528 main_ui_->menuHelp->insertAction(update_sep, update_action_);
529 connect(update_action_, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
531 master_split_.setObjectName("splitterMaster");
532 extra_split_.setObjectName("splitterExtra");
533 main_ui_->mainStack->addWidget(&master_split_);
535 empty_pane_.setObjectName("emptyPane");
537 packet_list_ = new PacketList(&master_split_);
538 main_ui_->wirelessTimelineWidget->setPacketList(packet_list_);
540 proto_tree_ = new ProtoTree(&master_split_);
541 proto_tree_->installEventFilter(this);
543 byte_view_tab_ = new ByteViewTab(&master_split_);
545 packet_list_->setProtoTree(proto_tree_);
546 packet_list_->setByteViewTab(byte_view_tab_);
547 packet_list_->installEventFilter(this);
549 main_welcome_ = main_ui_->welcomePage;
551 // Packet list and proto tree must exist before these are called.
552 setMenusForSelectedPacket();
553 setMenusForSelectedTreeRow();
555 initShowHideMainWidgets();
556 initTimeDisplayFormatMenu();
557 initTimePrecisionFormatMenu();
559 updatePreferenceActions();
560 updateRecentActions();
561 setForCaptureInProgress(false);
563 setTabOrder(df_combo_box_->lineEdit(), packet_list_);
564 setTabOrder(packet_list_, proto_tree_);
566 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
567 this, SLOT(captureCapturePrepared(capture_session *)));
568 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
569 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
570 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
571 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
572 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
573 this, SLOT(captureCaptureFixedStarted(capture_session *)));
574 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
575 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
576 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
577 this, SLOT(captureCaptureFixedFinished(capture_session *)));
578 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
579 this, SLOT(captureCaptureStopping(capture_session *)));
580 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
581 this, SLOT(captureCaptureFailed(capture_session *)));
582 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
583 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
585 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
586 wsApp, SLOT(captureStarted()));
587 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
588 wsApp, SLOT(captureFinished()));
589 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
590 wsApp, SLOT(captureStarted()));
591 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
592 wsApp, SLOT(captureFinished()));
594 connect(&capture_file_, SIGNAL(captureFileOpened()),
595 this, SLOT(captureFileOpened()));
596 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
597 this, SLOT(captureFileReadStarted()));
598 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
599 this, SLOT(captureFileReadFinished()));
600 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
601 this, SLOT(captureFileReloadStarted()));
602 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
603 this, SLOT(captureFileReadFinished()));
604 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
605 this, SLOT(captureFileRescanStarted()));
606 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
607 this, SLOT(captureFileReadFinished()));
608 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
609 this, SLOT(captureFileRetapStarted()));
610 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
611 this, SLOT(captureFileRetapFinished()));
612 connect(&capture_file_, SIGNAL(captureFileMergeStarted()),
613 this, SLOT(captureFileMergeStarted()));
614 connect(&capture_file_, SIGNAL(captureFileMergeFinished()),
615 this, SLOT(captureFileMergeFinished()));
616 connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
617 this, SLOT(captureFileFlushTapsData()));
618 connect(&capture_file_, SIGNAL(captureFileClosing()),
619 this, SLOT(captureFileClosing()));
620 connect(&capture_file_, SIGNAL(captureFileClosed()),
621 this, SLOT(captureFileClosed()));
623 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
624 this, SLOT(captureFileSaveStarted(QString)));
625 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
626 main_ui_->statusBar, SLOT(popFileStatus()));
627 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
628 main_ui_->statusBar, SLOT(popFileStatus()));
629 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
630 main_ui_->statusBar, SLOT(popFileStatus()));
632 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
633 wsApp, SLOT(captureFileReadStarted()));
634 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
635 wsApp, SLOT(updateTaps()));
637 connect(wsApp, SIGNAL(columnsChanged()),
638 packet_list_, SLOT(columnsChanged()));
639 connect(wsApp, SIGNAL(preferencesChanged()),
640 packet_list_, SLOT(preferencesChanged()));
641 connect(wsApp, SIGNAL(recentPreferencesRead()),
642 this, SLOT(applyRecentPaneGeometry()));
643 connect(wsApp, SIGNAL(recentPreferencesRead()),
644 this, SLOT(updateRecentActions()));
645 connect(wsApp, SIGNAL(packetDissectionChanged()),
646 this, SLOT(redissectPackets()), Qt::QueuedConnection);
647 connect(wsApp, SIGNAL(appInitialized()),
648 this, SLOT(filterExpressionsChanged()));
649 connect(wsApp, SIGNAL(filterExpressionsChanged()),
650 this, SLOT(filterExpressionsChanged()));
651 connect(wsApp, SIGNAL(checkDisplayFilter()),
652 this, SLOT(checkDisplayFilter()));
653 connect(wsApp, SIGNAL(fieldsChanged()),
654 this, SLOT(fieldsChanged()));
655 connect(wsApp, SIGNAL(reloadLuaPlugins()),
656 this, SLOT(reloadLuaPlugins()));
658 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
659 this, SLOT(mainStackChanged(int)));
661 connect(main_welcome_, SIGNAL(startCapture()),
662 this, SLOT(startCapture()));
663 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
664 this, SLOT(openCaptureFile(QString)));
665 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
666 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
667 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
668 main_ui_->statusBar, SLOT(popFilterStatus()));
670 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
671 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
672 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
673 this, SLOT(redissectPackets()));
674 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
675 this, SLOT(showPreferencesDialog(QString)));
676 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
677 this, SLOT(showPreferencesDialog(QString)));
678 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
679 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
680 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
681 this, SLOT(filterExpressionsChanged()));
683 connect(this, SIGNAL(setCaptureFile(capture_file*)),
684 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
685 connect(this, SIGNAL(setCaptureFile(capture_file*)),
686 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
687 connect(this, SIGNAL(setCaptureFile(capture_file*)),
688 packet_list_, SLOT(setCaptureFile(capture_file*)));
689 connect(this, SIGNAL(setCaptureFile(capture_file*)),
690 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
692 connect(this, SIGNAL(monospaceFontChanged(QFont)),
693 packet_list_, SLOT(setMonospaceFont(QFont)));
694 connect(this, SIGNAL(monospaceFontChanged(QFont)),
695 proto_tree_, SLOT(setMonospaceFont(QFont)));
696 connect(this, SIGNAL(monospaceFontChanged(QFont)),
697 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
699 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
700 packet_list_, SLOT(goNextPacket()));
701 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
702 packet_list_, SLOT(goPreviousPacket()));
703 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
704 packet_list_, SLOT(goFirstPacket()));
705 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
706 packet_list_, SLOT(goLastPacket()));
707 connect(main_ui_->actionGoNextHistoryPacket, SIGNAL(triggered()),
708 packet_list_, SLOT(goNextHistoryPacket()));
709 connect(main_ui_->actionGoPreviousHistoryPacket, SIGNAL(triggered()),
710 packet_list_, SLOT(goPreviousHistoryPacket()));
712 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
713 proto_tree_, SLOT(expandSubtrees()));
714 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
715 proto_tree_, SLOT(expandAll()));
716 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
717 proto_tree_, SLOT(collapseAll()));
719 connect(packet_list_, SIGNAL(packetSelectionChanged()),
720 this, SLOT(setMenusForSelectedPacket()));
721 connect(packet_list_, SIGNAL(packetDissectionChanged()),
722 this, SLOT(redissectPackets()));
723 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
724 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
725 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
726 this, SLOT(showPreferencesDialog(QString)));
727 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
728 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
729 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
730 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
731 packet_list_, SLOT(columnsChanged()));
732 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
733 this, SLOT(openPacketDialog()));
734 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
735 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
736 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
737 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
738 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
739 main_ui_->statusBar, SLOT(popBusyStatus()));
740 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
741 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
742 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
743 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
744 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
745 main_ui_->statusBar, SLOT(popProgressStatus()));
747 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
748 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
749 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
750 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
751 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
752 this, SLOT(openPacketDialog(bool)));
753 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
754 this, SLOT(showPreferencesDialog(QString)));
755 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
756 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
758 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
759 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
760 connect(byte_view_tab_, SIGNAL(currentChanged(int)),
761 this, SLOT(byteViewTabChanged(int)));
763 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
764 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
766 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
767 &capture_file_, SLOT(stopLoading()));
769 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
770 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
773 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
775 connect(iface_tree, SIGNAL(itemSelectionChanged()),
776 this, SLOT(interfaceSelectionChanged()));
778 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
779 this, SLOT(captureFilterSyntaxChanged(bool)));
782 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
783 this, SLOT(showExtcapOptionsDialog(QString&)));
786 #endif // HAVE_LIBPCAP
788 /* Create plugin_if hooks */
789 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
790 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
791 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
792 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
794 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
796 plugin_if_register_gui_cb(PLUGIN_IF_REMOVE_TOOLBAR, plugin_if_mainwindow_update_toolbars);
798 #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) && !defined(_WIN32)
799 // Register Interface Toolbar callbacks
801 // Qt version must be 5.2 or higher because the use of
802 // QThread::requestInterruption() in interface_toolbar.cpp and
803 // QThread::isInterruptionRequested() in interface_toolbar_reader.cpp
805 // The toolbar in/out control pipes are not supported on WIN32 yet.
806 iface_toolbar_register_cb(mainwindow_add_toolbar, mainwindow_remove_toolbar);
809 main_ui_->mainStack->setCurrentWidget(main_welcome_);
812 MainWindow::~MainWindow()
817 QString MainWindow::getFilter()
819 return df_combo_box_->currentText();
822 QMenu *MainWindow::createPopupMenu()
824 QMenu *menu = new QMenu();
825 menu->addAction(main_ui_->actionViewMainToolbar);
826 menu->addAction(main_ui_->actionViewFilterToolbar);
827 menu->addAction(main_ui_->actionViewWirelessToolbar);
829 if (!main_ui_->menuInterfaceToolbars->actions().isEmpty()) {
830 QMenu *submenu = menu->addMenu(main_ui_->menuInterfaceToolbars->title());
831 foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
832 submenu->addAction(action);
836 if (!main_ui_->menuAdditionalToolbars->actions().isEmpty()) {
837 QMenu *subMenu = menu->addMenu(main_ui_->menuAdditionalToolbars->title());
838 foreach (QAction *action, main_ui_->menuAdditionalToolbars->actions()) {
839 subMenu->addAction(action);
843 menu->addAction(main_ui_->actionViewStatusBar);
845 menu->addSeparator();
846 menu->addAction(main_ui_->actionViewPacketList);
847 menu->addAction(main_ui_->actionViewPacketDetails);
848 menu->addAction(main_ui_->actionViewPacketBytes);
852 void MainWindow::addInterfaceToolbar(const iface_toolbar *toolbar_entry)
854 QMenu *menu = main_ui_->menuInterfaceToolbars;
855 bool visible = g_list_find_custom(recent.interface_toolbars, toolbar_entry->menu_title, (GCompareFunc) strcmp) ? true : false;
857 QString title = QString().fromUtf8(toolbar_entry->menu_title);
858 QAction *action = new QAction(title, menu);
859 action->setEnabled(true);
860 action->setCheckable(true);
861 action->setChecked(visible);
862 action->setToolTip(tr("Show or hide the toolbar"));
864 QAction *before = NULL;
865 foreach (QAction *action, menu->actions()) {
866 // Ensure we add the menu entries in sorted order
867 if (action->text().compare(title, Qt::CaseInsensitive) > 0) {
872 menu->insertAction(before, action);
874 InterfaceToolbar *interface_toolbar = new InterfaceToolbar(this, toolbar_entry);
875 connect(wsApp, SIGNAL(appInitialized()), interface_toolbar, SLOT(interfaceListChanged()));
876 connect(wsApp, SIGNAL(localInterfaceListChanged()), interface_toolbar, SLOT(interfaceListChanged()));
878 QToolBar *toolbar = new QToolBar(this);
879 toolbar->addWidget(interface_toolbar);
880 toolbar->setMovable(false);
881 toolbar->setVisible(visible);
883 action->setData(qVariantFromValue(toolbar));
885 addToolBar(Qt::TopToolBarArea, toolbar);
886 insertToolBarBreak(toolbar);
888 if (show_hide_actions_) {
889 show_hide_actions_->addAction(action);
892 menu->menuAction()->setVisible(true);
895 void MainWindow::removeInterfaceToolbar(const gchar *menu_title)
897 QMenu *menu = main_ui_->menuInterfaceToolbars;
898 QAction *action = NULL;
899 QMap<QAction *, QWidget *>::iterator i;
901 QString title = QString().fromUtf8(menu_title);
902 foreach (action, menu->actions()) {
903 if (title.compare(action->text()) == 0) {
909 if (show_hide_actions_) {
910 show_hide_actions_->removeAction(action);
912 menu->removeAction(action);
914 QToolBar *toolbar = action->data().value<QToolBar *>();
915 removeToolBar(toolbar);
921 menu->menuAction()->setVisible(!menu->actions().isEmpty());
924 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
926 pipe_source_ = source;
927 pipe_child_process_ = child_process;
928 pipe_user_data_ = user_data;
929 pipe_input_cb_ = input_cb;
932 /* Tricky to use pipes in win9x, as no concept of wait. NT can
933 do this but that doesn't cover all win32 platforms. GTK can do
934 this but doesn't seem to work over processes. Attempt to do
935 something similar here, start a timer and check for data on every
937 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
940 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
944 pipe_timer_ = new QTimer(this);
945 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
946 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
947 pipe_timer_->start(200);
949 if (pipe_notifier_) {
950 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
951 delete pipe_notifier_;
954 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
955 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
956 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
957 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
961 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
963 // The user typed some text. Start filling in a filter.
964 // We may need to be more choosy here. We just need to catch events for the packet list,
965 // proto tree, and main welcome widgets.
966 if (event->type() == QEvent::KeyPress) {
967 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
968 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
969 df_combo_box_->lineEdit()->insert(kevt->text());
970 df_combo_box_->lineEdit()->setFocus();
975 return QMainWindow::eventFilter(obj, event);
978 void MainWindow::keyPressEvent(QKeyEvent *event) {
980 // Explicitly focus on the display filter combo.
981 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
982 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
986 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
987 if (event->modifiers() == Qt::NoModifier) {
988 if (event->key() == Qt::Key_Escape) {
989 on_goToCancel_clicked();
990 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
994 return; // goToLineEdit didn't want it and we don't either.
997 // Move up & down the packet list.
998 if (event->key() == Qt::Key_F7) {
999 packet_list_->goPreviousPacket();
1000 } else if (event->key() == Qt::Key_F8) {
1001 packet_list_->goNextPacket();
1004 // Move along, citizen.
1005 QMainWindow::keyPressEvent(event);
1008 void MainWindow::closeEvent(QCloseEvent *event) {
1009 saveWindowGeometry();
1011 /* If we're in the middle of stopping a capture, don't do anything;
1012 the user can try deleting the window after the capture stops. */
1013 if (capture_stopping_) {
1018 QString before_what(tr(" before quitting"));
1019 if (!testCaptureFileClose(before_what, Quit)) {
1025 if (capture_interfaces_dialog_) capture_interfaces_dialog_->close();
1027 // Make sure we kill any open dumpcap processes.
1028 delete main_welcome_;
1030 // One of the many places we assume one main window.
1031 if(!wsApp->isInitialized()) {
1032 // If we're still initializing, QCoreApplication::quit() won't
1033 // exit properly because we are not in the event loop. This
1034 // means that the application won't clean up after itself. We
1035 // might want to call wsApp->processEvents() during startup
1036 // instead so that we can do a normal exit here.
1040 // When the main loop is not yet running (i.e. when openCaptureFile is
1041 // executing in wireshark-qt.cpp), the above quit action has no effect.
1042 // Schedule a quit action for the next execution of the main loop.
1043 QMetaObject::invokeMethod(wsApp, "quit", Qt::QueuedConnection);
1046 // XXX On windows the drag description is "Copy". It should be "Open" or
1047 // "Merge" as appropriate. It looks like we need access to IDataObject in
1048 // order to set DROPDESCRIPTION.
1049 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
1051 if (!main_ui_->actionFileOpen->isEnabled()) {
1052 // We could alternatively call setAcceptDrops(!capture_in_progress)
1053 // in setMenusForCaptureInProgress but that wouldn't provide feedback.
1055 main_ui_->statusBar->pushTemporaryStatus(tr("Unable to drop files during capture."));
1056 event->setDropAction(Qt::IgnoreAction);
1061 bool have_files = false;
1062 foreach (QUrl drag_url, event->mimeData()->urls()) {
1063 if (!drag_url.toLocalFile().isEmpty()) {
1070 event->acceptProposedAction();
1074 void MainWindow::dropEvent(QDropEvent *event)
1076 QList<QByteArray> local_files;
1078 foreach (QUrl drop_url, event->mimeData()->urls()) {
1079 QString drop_file = drop_url.toLocalFile();
1080 if (!drop_file.isEmpty()) {
1081 local_files << drop_file.toUtf8();
1085 if (local_files.size() < 1) {
1088 event->acceptProposedAction();
1091 if (local_files.size() == 1) {
1092 openCaptureFile(local_files.at(0));
1096 char **in_filenames = (char **)g_malloc(sizeof(char*) * local_files.size());
1097 char *tmpname = NULL;
1099 for (int i = 0; i < local_files.size(); i++) {
1100 in_filenames[i] = (char *) local_files.at(i).constData();
1103 /* merge the files in chronological order */
1104 if (cf_merge_files_to_tempfile(this, &tmpname, local_files.size(),
1105 in_filenames, WTAP_FILE_TYPE_SUBTYPE_PCAPNG,
1107 /* Merge succeeded; close the currently-open file and try
1108 to open the merged capture file. */
1109 openCaptureFile(tmpname, QString(), WTAP_TYPE_AUTO, TRUE);
1113 g_free(in_filenames);
1117 // Apply recent settings to the main window geometry.
1118 // We haven't loaded the preferences at this point so we assume that the
1119 // position and size preference are enabled.
1120 // Note we might end up with unexpected screen geometries if the user
1121 // unplugs or plugs in a monitor:
1122 // https://bugreports.qt.io/browse/QTBUG-44213
1123 void MainWindow::loadWindowGeometry()
1125 int min_sensible_dimension = 200;
1128 if (recent.gui_geometry_main_maximized) {
1129 setWindowState(Qt::WindowMaximized);
1133 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
1134 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
1135 if (!rect_on_screen(recent_geom)) {
1136 // We're not visible on any screens. See if we can move onscreen
1137 // without resizing.
1138 recent_geom.moveTo(50, 50); // recent.c defaults to 20.
1141 if (!rect_on_screen(recent_geom)) {
1142 // Give up and use the default geometry.
1146 // if (prefs.gui_geometry_save_position) {
1147 move(recent_geom.topLeft());
1150 if (// prefs.gui_geometry_save_size &&
1151 recent_geom.width() > min_sensible_dimension &&
1152 recent_geom.height() > min_sensible_dimension) {
1153 resize(recent_geom.size());
1158 void MainWindow::saveWindowGeometry()
1160 if (prefs.gui_geometry_save_position) {
1161 recent.gui_geometry_main_x = pos().x();
1162 recent.gui_geometry_main_y = pos().y();
1165 if (prefs.gui_geometry_save_size) {
1166 recent.gui_geometry_main_width = size().width();
1167 recent.gui_geometry_main_height = size().height();
1170 if (prefs.gui_geometry_save_maximized) {
1171 // On macOS this is false when it shouldn't be
1172 recent.gui_geometry_main_maximized = isMaximized();
1175 if (master_split_.sizes().length() > 0) {
1176 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
1179 if (master_split_.sizes().length() > 2) {
1180 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
1181 } else if (extra_split_.sizes().length() > 0) {
1182 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
1186 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
1188 case layout_pane_content_none:
1189 return &empty_pane_;
1190 case layout_pane_content_plist:
1191 return packet_list_;
1192 case layout_pane_content_pdetails:
1194 case layout_pane_content_pbytes:
1195 return byte_view_tab_;
1197 g_assert_not_reached();
1202 // Our event loop becomes nested whenever we call update_progress_dlg, which
1203 // includes several places in file.c. The GTK+ UI stays out of trouble by
1204 // showing a modal progress dialog. We attempt to do the equivalent below by
1205 // disabling parts of the main window. At a minumum the ProgressFrame in the
1206 // main status bar must remain accessible.
1208 // We might want to do this any time the main status bar progress frame is
1209 // shown and hidden.
1210 void MainWindow::freeze()
1212 freeze_focus_ = wsApp->focusWidget();
1214 // XXX Alternatively we could just disable and enable the main menu.
1215 for (int i = 0; i < freeze_actions_.size(); i++) {
1216 QAction *action = freeze_actions_[i].first;
1217 freeze_actions_[i].second = action->isEnabled();
1218 action->setEnabled(false);
1220 main_ui_->centralWidget->setEnabled(false);
1223 void MainWindow::thaw()
1225 main_ui_->centralWidget->setEnabled(true);
1226 for (int i = 0; i < freeze_actions_.size(); i++) {
1227 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
1230 if (freeze_focus_) freeze_focus_->setFocus();
1231 freeze_focus_ = NULL;
1234 void MainWindow::mergeCaptureFile()
1236 QString file_name = "";
1237 QString read_filter = "";
1238 dfilter_t *rfcode = NULL;
1241 if (!capture_file_.capFile())
1244 if (prefs.gui_ask_unsaved) {
1245 if (cf_has_unsaved_data(capture_file_.capFile())) {
1246 QMessageBox msg_dialog;
1247 gchar *display_basename;
1250 msg_dialog.setIcon(QMessageBox::Question);
1251 /* This file has unsaved data; ask the user whether to save
1253 if (capture_file_.capFile()->is_tempfile) {
1254 msg_dialog.setText(tr("Save packets before merging?"));
1255 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
1258 * Format the message.
1260 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1261 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
1262 g_free(display_basename);
1263 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
1266 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
1267 msg_dialog.setDefaultButton(QMessageBox::Save);
1269 response = msg_dialog.exec();
1273 case QMessageBox::Save:
1274 /* Save the file but don't close it */
1275 saveCaptureFile(capture_file_.capFile(), false);
1278 case QMessageBox::Cancel:
1280 /* Don't do the merge. */
1287 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1289 cf_status_t merge_status;
1290 char *in_filenames[2];
1293 if (merge_dlg.merge(file_name)) {
1296 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1297 /* Not valid. Tell the user, and go back and run the file
1298 selection box again once they dismiss the alert. */
1299 // Similar to commandline_info.jfilter section in main().
1300 QMessageBox::warning(this, tr("Invalid Read Filter"),
1301 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1310 file_type = capture_file_.capFile()->cd_t;
1312 /* Try to merge or append the two files */
1313 if (merge_dlg.mergeType() == 0) {
1314 /* chronological order */
1315 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1316 in_filenames[1] = qstring_strdup(file_name);
1317 merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, FALSE);
1318 } else if (merge_dlg.mergeType() <= 0) {
1320 in_filenames[0] = qstring_strdup(file_name);
1321 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1322 merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, TRUE);
1325 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1326 in_filenames[1] = qstring_strdup(file_name);
1327 merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, TRUE);
1330 g_free(in_filenames[0]);
1331 g_free(in_filenames[1]);
1333 if (merge_status != CF_OK) {
1335 dfilter_free(rfcode);
1340 cf_close(capture_file_.capFile());
1342 /* Try to open the merged capture file. */
1343 CaptureFile::globalCapFile()->window = this;
1344 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1345 /* We couldn't open it; fail. */
1346 CaptureFile::globalCapFile()->window = NULL;
1348 dfilter_free(rfcode);
1353 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1354 it closed the previous capture file, and thus destroyed any
1355 previous read filter attached to "cf"). */
1356 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1358 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1362 /* Just because we got an error, that doesn't mean we were unable
1363 to read any of the file; we handle what we could get from the
1367 case CF_READ_ABORTED:
1368 /* The user bailed out of re-reading the capture file; the
1369 capture file has been closed - just free the capture file name
1370 string and return (without changing the last containing
1376 /* Save the name of the containing directory specified in the path name,
1377 if any; we can write over cf_merged_name, which is a good thing, given that
1378 "get_dirname()" does write over its argument. */
1379 wsApp->setLastOpenDir(get_dirname(tmpname));
1381 main_ui_->statusBar->showExpert();
1387 void MainWindow::importCaptureFile() {
1388 ImportTextDialog import_dlg;
1390 QString before_what(tr(" before importing a capture"));
1391 if (!testCaptureFileClose(before_what))
1396 if (import_dlg.result() != QDialog::Accepted) {
1397 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1401 openCaptureFile(import_dlg.capfileName());
1404 bool MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1406 gboolean discard_comments;
1408 if (cf->is_tempfile) {
1409 /* This is a temporary capture file, so saving it means saving
1410 it to a permanent file. Prompt the user for a location
1411 to which to save it. Don't require that the file format
1412 support comments - if it's a temporary capture file, it's
1413 probably pcap-ng, which supports comments and, if it's
1414 not pcap-ng, let the user decide what they want to do
1415 if they've added comments. */
1416 return saveAsCaptureFile(cf, FALSE, dont_reopen);
1418 if (cf->unsaved_changes) {
1419 cf_write_status_t status;
1421 /* This is not a temporary capture file, but it has unsaved
1422 changes, so saving it means doing a "safe save" on top
1423 of the existing file, in the same format - no UI needed
1424 unless the file has comments and the file's format doesn't
1427 If the file has comments, does the file's format support them?
1428 If not, ask the user whether they want to discard the comments
1429 or choose a different format. */
1430 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1433 /* The file can be saved in the specified format as is;
1434 just drive on and save in the format they selected. */
1435 discard_comments = FALSE;
1438 case SAVE_WITHOUT_COMMENTS:
1439 /* The file can't be saved in the specified format as is,
1440 but it can be saved without the comments, and the user
1441 said "OK, discard the comments", so save it in the
1442 format they specified without the comments. */
1443 discard_comments = TRUE;
1446 case SAVE_IN_ANOTHER_FORMAT:
1447 /* There are file formats in which we can save this that
1448 support comments, and the user said not to delete the
1449 comments. Do a "Save As" so the user can select
1450 one of those formats and choose a file name. */
1451 return saveAsCaptureFile(cf, TRUE, dont_reopen);
1454 /* The user said "forget it". Just return. */
1458 /* Squelch warnings that discard_comments is being used
1460 g_assert_not_reached();
1464 /* XXX - cf->filename might get freed out from under us, because
1465 the code path through which cf_save_records() goes currently
1466 closes the current file and then opens and reloads the saved file,
1467 so make a copy and free it later. */
1468 file_name = cf->filename;
1469 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1470 discard_comments, dont_reopen);
1474 /* The save succeeded; we're done.
1475 If we discarded comments, redraw the packet list to reflect
1476 any packets that no longer have comments. */
1477 if (discard_comments)
1478 packet_list_queue_draw();
1480 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1481 updateForUnsavedChanges(); // we update the title bar to remove the *
1484 case CF_WRITE_ERROR:
1485 /* The write failed.
1486 XXX - OK, what do we do now? Let them try a
1487 "Save As", in case they want to try to save to a
1488 different directory or file system? */
1491 case CF_WRITE_ABORTED:
1492 /* The write was aborted; just drive on. */
1496 /* Otherwise just do nothing. */
1502 bool MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1503 QString file_name = "";
1505 gboolean compressed;
1506 cf_write_status_t status;
1508 gboolean discard_comments = FALSE;
1515 CaptureFileDialog save_as_dlg(this, cf);
1517 /* If the file has comments, does the format the user selected
1518 support them? If not, ask the user whether they want to
1519 discard the comments or choose a different format. */
1520 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1523 /* The file can be saved in the specified format as is;
1524 just drive on and save in the format they selected. */
1525 discard_comments = FALSE;
1528 case SAVE_WITHOUT_COMMENTS:
1529 /* The file can't be saved in the specified format as is,
1530 but it can be saved without the comments, and the user
1531 said "OK, discard the comments", so save it in the
1532 format they specified without the comments. */
1533 discard_comments = TRUE;
1536 case SAVE_IN_ANOTHER_FORMAT:
1537 /* There are file formats in which we can save this that
1538 support comments, and the user said not to delete the
1539 comments. The combo box of file formats has had the
1540 formats that don't support comments trimmed from it,
1541 so run the dialog again, to let the user decide
1542 whether to save in one of those formats or give up. */
1543 must_support_comments = TRUE;
1547 /* The user said "forget it". Just get rid of the dialog box
1551 file_type = save_as_dlg.selectedFileType();
1552 compressed = save_as_dlg.isCompressed();
1554 fileAddExtension(file_name, file_type, compressed);
1557 // /* If the file exists and it's user-immutable or not writable,
1558 // ask the user whether they want to override that. */
1559 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1560 // /* They don't. Let them try another file name or cancel. */
1565 /* Attempt to save the file */
1566 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1567 discard_comments, dont_reopen);
1571 /* The save succeeded; we're done. */
1572 /* Save the directory name for future file dialogs. */
1573 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1574 set_last_open_dir(get_dirname(dirname));
1576 /* If we discarded comments, redraw the packet list to reflect
1577 any packets that no longer have comments. */
1578 if (discard_comments)
1579 packet_list_queue_draw();
1581 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1582 updateForUnsavedChanges(); // we update the title bar to remove the *
1583 /* Add this filename to the list of recent files in the "Recent Files" submenu */
1584 add_menu_recent_capture_file(file_name.toUtf8().constData());
1587 case CF_WRITE_ERROR:
1588 /* The save failed; let the user try again. */
1591 case CF_WRITE_ABORTED:
1592 /* The user aborted the save; just return. */
1599 void MainWindow::exportSelectedPackets() {
1600 QString file_name = "";
1602 gboolean compressed;
1603 packet_range_t range;
1604 cf_write_status_t status;
1606 gboolean discard_comments = FALSE;
1608 if (!capture_file_.capFile())
1611 /* Init the packet range */
1612 packet_range_init(&range, capture_file_.capFile());
1613 range.process_filtered = TRUE;
1614 range.include_dependents = TRUE;
1617 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1619 /* If the file has comments, does the format the user selected
1620 support them? If not, ask the user whether they want to
1621 discard the comments or choose a different format. */
1622 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1625 /* The file can be saved in the specified format as is;
1626 just drive on and save in the format they selected. */
1627 discard_comments = FALSE;
1630 case SAVE_WITHOUT_COMMENTS:
1631 /* The file can't be saved in the specified format as is,
1632 but it can be saved without the comments, and the user
1633 said "OK, discard the comments", so save it in the
1634 format they specified without the comments. */
1635 discard_comments = TRUE;
1638 case SAVE_IN_ANOTHER_FORMAT:
1639 /* There are file formats in which we can save this that
1640 support comments, and the user said not to delete the
1641 comments. The combo box of file formats has had the
1642 formats that don't support comments trimmed from it,
1643 so run the dialog again, to let the user decide
1644 whether to save in one of those formats or give up. */
1648 /* The user said "forget it". Just get rid of the dialog box
1654 * Check that we're not going to save on top of the current
1656 * We do it here so we catch all cases ...
1657 * Unfortunately, the file requester gives us an absolute file
1658 * name and the read file name may be relative (if supplied on
1659 * the command line). From Joerg Mayer.
1661 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1662 QMessageBox msg_box;
1663 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1665 msg_box.setIcon(QMessageBox::Critical);
1666 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1667 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1668 msg_box.setStandardButtons(QMessageBox::Ok);
1669 msg_box.setDefaultButton(QMessageBox::Ok);
1671 g_free(display_basename);
1675 file_type = esp_dlg.selectedFileType();
1676 compressed = esp_dlg.isCompressed();
1677 fileAddExtension(file_name, file_type, compressed);
1680 // /* If the file exists and it's user-immutable or not writable,
1681 // ask the user whether they want to override that. */
1682 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1683 // /* They don't. Let them try another file name or cancel. */
1688 /* Attempt to save the file */
1689 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1693 /* The save succeeded; we're done. */
1694 /* Save the directory name for future file dialogs. */
1695 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1696 set_last_open_dir(get_dirname(dirname));
1698 /* If we discarded comments, redraw the packet list to reflect
1699 any packets that no longer have comments. */
1700 if (discard_comments)
1701 packet_list_queue_draw();
1702 /* Add this filename to the list of recent files in the "Recent Files" submenu */
1703 add_menu_recent_capture_file(file_name.toUtf8().constData());
1706 case CF_WRITE_ERROR:
1707 /* The save failed; let the user try again. */
1710 case CF_WRITE_ABORTED:
1711 /* The user aborted the save; just return. */
1718 void MainWindow::exportDissections(export_type_e export_type) {
1719 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1720 packet_range_t range;
1722 if (!capture_file_.capFile())
1725 /* Init the packet range */
1726 packet_range_init(&range, capture_file_.capFile());
1727 range.process_filtered = TRUE;
1728 range.include_dependents = TRUE;
1733 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1734 QString file_name_lower;
1735 GSList *extensions_list;
1736 gboolean add_extension;
1739 * Append the default file extension if there's none given by
1740 * the user or if they gave one that's not one of the valid
1741 * extensions for the file type.
1743 file_name_lower = file_name.toLower();
1744 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1745 if (extensions_list != NULL) {
1748 /* We have one or more extensions for this file type.
1749 Start out assuming we need to add the default one. */
1750 add_extension = TRUE;
1752 /* OK, see if the file has one of those extensions. */
1753 for (extension = extensions_list; extension != NULL;
1754 extension = g_slist_next(extension)) {
1755 QString file_suffix = tr(".") + (char *)extension->data;
1756 if (file_name_lower.endsWith(file_suffix)) {
1758 * The file name has one of the extensions for
1761 add_extension = FALSE;
1764 file_suffix += ".gz";
1765 if (file_name_lower.endsWith(file_suffix)) {
1767 * The file name has one of the extensions for
1770 add_extension = FALSE;
1775 /* We have no extensions for this file type. Don't add one. */
1776 add_extension = FALSE;
1778 if (add_extension) {
1779 if (wtap_default_file_extension(file_type) != NULL) {
1780 file_name += tr(".") + wtap_default_file_extension(file_type);
1788 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1789 bool capture_in_progress = false;
1790 bool do_close_file = false;
1792 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1793 return true; /* Already closed, nothing to do */
1796 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1798 * This (FILE_READ_IN_PROGRESS) is true if we're reading a capture file
1799 * *or* if we're doing a live capture. From the capture file itself we
1800 * cannot differentiate the cases, so check the current capture session.
1802 capture_in_progress = captureSession()->state != CAPTURE_STOPPED;
1806 if (prefs.gui_ask_unsaved) {
1807 if (cf_has_unsaved_data(capture_file_.capFile())) {
1808 QMessageBox msg_dialog;
1811 QPushButton *save_button;
1812 QPushButton *discard_button;
1814 msg_dialog.setIcon(QMessageBox::Question);
1815 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1817 /* This file has unsaved data or there's a capture in
1818 progress; ask the user whether to save the data. */
1819 if (capture_in_progress && context != Restart) {
1820 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1821 infotext = tr("Your captured packets will be lost if you don't save them.");
1822 } else if (capture_file_.capFile()->is_tempfile) {
1823 if (context == Reload) {
1824 // Reloading a tempfile will keep the packets, so this is not unsaved packets
1825 question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1826 infotext = tr("Your changes will be lost if you don't save them.");
1828 question = tr("Do you want to save the captured packets%1?").arg(before_what);
1829 infotext = tr("Your captured packets will be lost if you don't save them.");
1832 // No capture in progress and not a tempfile, so this is not unsaved packets
1833 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1834 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1835 infotext = tr("Your changes will be lost if you don't save them.");
1836 g_free(display_basename);
1839 msg_dialog.setText(question);
1840 msg_dialog.setInformativeText(infotext);
1842 // XXX Text comes from ui/gtk/stock_icons.[ch]
1843 // Note that the button roles differ from the GTK+ version.
1844 // Cancel = RejectRole
1845 // Save = AcceptRole
1846 // Don't Save = DestructiveRole
1847 msg_dialog.addButton(QMessageBox::Cancel);
1849 if (capture_in_progress) {
1850 QString save_button_text;
1851 if (context == Restart) {
1852 save_button_text = tr("Save before Continue");
1854 save_button_text = tr("Stop and Save");
1856 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1858 save_button = msg_dialog.addButton(QMessageBox::Save);
1860 msg_dialog.setDefaultButton(save_button);
1862 QString discard_button_text;
1863 if (capture_in_progress) {
1866 discard_button_text = tr("Stop and Quit &without Saving");
1869 discard_button_text = tr("Continue &without Saving");
1872 discard_button_text = tr("Stop and Continue &without Saving");
1878 discard_button_text = tr("Quit &without Saving");
1882 discard_button_text = tr("Continue &without Saving");
1886 discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1888 discard_button->setAutoDefault(false);
1889 discard_button->setFocus();
1892 /* According to the Qt doc:
1893 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1895 * Therefore we should use clickedButton() to determine which button was clicked. */
1897 if (msg_dialog.clickedButton() == save_button) {
1899 /* If there's a capture in progress, we have to stop the capture
1900 and then do the save. */
1901 if (capture_in_progress)
1904 /* Save the file and close it */
1905 // XXX if no packets were captured, any unsaved comments set by
1906 // the user are silently discarded because capFile() is null.
1907 if (capture_file_.capFile() && saveCaptureFile(capture_file_.capFile(), true) == false)
1909 do_close_file = true;
1910 } else if(msg_dialog.clickedButton() == discard_button) {
1911 /* Just close the file, discarding changes */
1912 do_close_file = true;
1914 // cancelButton or some other unspecified button
1918 /* Unchanged file or capturing with no packets */
1919 do_close_file = true;
1922 /* User asked not to be bothered by those prompts, just close it.
1923 XXX - should that apply only to saving temporary files? */
1924 do_close_file = true;
1928 * Are we done with this file and should we close the file?
1930 if (do_close_file) {
1932 /* If there's a capture in progress, we have to stop the capture
1933 and then do the close. */
1934 if (capture_in_progress)
1936 else if (capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1938 * When an offline capture is being read, mark it as aborted.
1939 * cf_read will be responsible for actually closing the capture.
1941 * We cannot just invoke cf_close here since cf_read is up in the
1942 * call chain. (update_progress_dlg can end up processing the Quit
1943 * event from the user which then ends up here.)
1945 capture_file_.capFile()->state = FILE_READ_ABORTED;
1949 /* captureStop() will close the file if not having any packets */
1950 if (capture_file_.capFile() && context != Restart && context != Reload)
1951 // Don't really close if Restart or Reload
1952 cf_close(capture_file_.capFile());
1955 return true; /* File closed */
1958 void MainWindow::captureStop() {
1961 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1962 WiresharkApplication::processEvents();
1966 void MainWindow::initMainToolbarIcons()
1968 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1969 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1970 #if !defined(Q_OS_WIN)
1971 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1972 // The macOS HIG specifies 32-pixel icons but they're a little too
1974 icon_size = icon_size * 3 / 2;
1976 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1978 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1979 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1981 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1982 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1983 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1984 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1986 // Menu icons are disabled in main_window.ui for these items.
1987 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1988 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1989 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1990 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1992 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1993 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1994 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1995 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1996 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1997 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1998 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1999 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
2000 #if defined(Q_OS_MAC)
2001 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
2002 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
2004 main_ui_->actionGoPreviousHistoryPacket->setIcon(StockIcon("go-previous"));
2005 main_ui_->actionGoNextHistoryPacket->setIcon(StockIcon("go-next"));
2006 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
2008 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
2010 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
2011 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
2012 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
2013 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
2014 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
2015 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
2016 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
2019 void MainWindow::initShowHideMainWidgets()
2021 if (show_hide_actions_) {
2025 show_hide_actions_ = new QActionGroup(this);
2026 QMap<QAction *, QWidget *> shmw_actions;
2028 show_hide_actions_->setExclusive(false);
2029 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
2030 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
2031 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
2032 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
2033 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
2034 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
2035 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
2037 foreach (QAction *shmwa, shmw_actions.keys()) {
2038 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
2039 show_hide_actions_->addAction(shmwa);
2040 showHideMainWidgets(shmwa);
2043 // Initial hide the Interface Toolbar submenu
2044 main_ui_->menuInterfaceToolbars->menuAction()->setVisible(false);
2046 /* Initially hide the additional toolbars menus */
2047 main_ui_->menuAdditionalToolbars->menuAction()->setVisible(false);
2049 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
2052 void MainWindow::initTimeDisplayFormatMenu()
2054 if (time_display_actions_) {
2058 time_display_actions_ = new QActionGroup(this);
2060 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
2061 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
2062 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
2063 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
2064 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
2065 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
2066 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
2067 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
2068 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
2069 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
2071 foreach (QAction* tda, td_actions.keys()) {
2072 tda->setData(qVariantFromValue(td_actions[tda]));
2073 time_display_actions_->addAction(tda);
2076 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
2079 void MainWindow::initTimePrecisionFormatMenu()
2081 if (time_precision_actions_) {
2085 time_precision_actions_ = new QActionGroup(this);
2087 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
2088 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
2089 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
2090 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
2091 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
2092 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
2093 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
2095 foreach (QAction* tpa, tp_actions.keys()) {
2096 tpa->setData(qVariantFromValue(tp_actions[tpa]));
2097 time_precision_actions_->addAction(tpa);
2100 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
2103 // Menu items which will be disabled when we freeze() and whose state will
2104 // be restored when we thaw(). Add to the list as needed.
2105 void MainWindow::initFreezeActions()
2107 QList<QAction *> freeze_actions = QList<QAction *>()
2108 << main_ui_->actionFileClose
2109 << main_ui_->actionViewReload
2110 << main_ui_->actionEditMarkPacket
2111 << main_ui_->actionEditMarkAllDisplayed
2112 << main_ui_->actionEditUnmarkAllDisplayed
2113 << main_ui_->actionEditIgnorePacket
2114 << main_ui_->actionEditIgnoreAllDisplayed
2115 << main_ui_->actionEditUnignoreAllDisplayed
2116 << main_ui_->actionEditSetTimeReference
2117 << main_ui_->actionEditUnsetAllTimeReferences;
2119 foreach (QAction *action, freeze_actions) {
2120 freeze_actions_ << QPair<QAction *, bool>(action, false);
2124 void MainWindow::initConversationMenus()
2128 QList<QAction *> cc_actions = QList<QAction *>()
2129 << main_ui_->actionViewColorizeConversation1 << main_ui_->actionViewColorizeConversation2
2130 << main_ui_->actionViewColorizeConversation3 << main_ui_->actionViewColorizeConversation4
2131 << main_ui_->actionViewColorizeConversation5 << main_ui_->actionViewColorizeConversation6
2132 << main_ui_->actionViewColorizeConversation7 << main_ui_->actionViewColorizeConversation8
2133 << main_ui_->actionViewColorizeConversation9 << main_ui_->actionViewColorizeConversation10;
2135 for (GList *conv_filter_list_entry = conv_filter_list; conv_filter_list_entry; conv_filter_list_entry = g_list_next(conv_filter_list_entry)) {
2137 conversation_filter_t* conv_filter = (conversation_filter_t *)conv_filter_list_entry->data;
2138 ConversationAction *conv_action = new ConversationAction(main_ui_->menuConversationFilter, conv_filter);
2139 main_ui_->menuConversationFilter->addAction(conv_action);
2141 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2142 connect(conv_action, SIGNAL(triggered()), this, SLOT(applyConversationFilter()));
2144 // Packet list context menu items
2145 packet_list_->conversationMenu()->addAction(conv_action);
2147 QMenu *submenu = packet_list_->colorizeMenu()->addMenu(conv_action->text());
2150 foreach (QAction *cc_action, cc_actions) {
2151 conv_action = new ConversationAction(submenu, conv_filter);
2152 conv_action->setText(cc_action->text());
2153 conv_action->setIcon(cc_action->icon());
2154 conv_action->setColorNumber(i++);
2155 submenu->addAction(conv_action);
2156 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2157 connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2160 conv_action = new ConversationAction(submenu, conv_filter);
2161 conv_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
2162 submenu->addAction(conv_action);
2163 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2164 connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2166 // Proto tree conversation menu is filled in in ProtoTree::contextMenuEvent.
2167 // We should probably do that here.
2170 // Proto tree colorization items
2172 ColorizeAction *colorize_action;
2173 foreach (QAction *cc_action, cc_actions) {
2174 colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
2175 colorize_action->setText(cc_action->text());
2176 colorize_action->setIcon(cc_action->icon());
2177 colorize_action->setColorNumber(i++);
2178 proto_tree_->colorizeMenu()->addAction(colorize_action);
2179 connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
2180 connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2183 colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
2184 colorize_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
2185 proto_tree_->colorizeMenu()->addAction(colorize_action);
2186 connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
2187 connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2190 gboolean MainWindow::addExportObjectsMenuItem(const void *, void *value, void *userdata)
2192 register_eo_t *eo = (register_eo_t*)value;
2193 MainWindow *window = (MainWindow*)userdata;
2195 ExportObjectAction *export_action = new ExportObjectAction(window->main_ui_->menuFileExportObjects, eo);
2196 window->main_ui_->menuFileExportObjects->addAction(export_action);
2198 //initially disable until a file is loaded (then file signals will take over)
2199 export_action->setEnabled(false);
2201 connect(&window->capture_file_, SIGNAL(captureFileOpened()), export_action, SLOT(captureFileOpened()));
2202 connect(&window->capture_file_, SIGNAL(captureFileClosed()), export_action, SLOT(captureFileClosed()));
2203 connect(export_action, SIGNAL(triggered()), window, SLOT(applyExportObject()));
2207 void MainWindow::initExportObjectsMenus()
2209 eo_iterate_tables(addExportObjectsMenuItem, this);
2213 void MainWindow::setTitlebarForCaptureFile()
2215 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
2216 if (capture_file_.capFile()->is_tempfile) {
2218 // For a temporary file, put the source of the data
2219 // in the window title, not whatever random pile
2220 // of characters is the last component of the path
2223 // XXX - on non-Mac platforms, put in the application
2226 setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
2229 // For a user file, set the full path; that way,
2230 // for macOS, it'll set the "proxy icon". Qt
2231 // handles extracting the last component.
2233 // Sadly, some UN*Xes don't necessarily use UTF-8
2234 // for their file names, so we have to map the
2235 // file path to UTF-8. If that fails, we're somewhat
2238 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
2243 if (utf8_filename) {
2244 QFileInfo fi(utf8_filename);
2245 setWSWindowTitle(QString("[*]%1").arg(fi.fileName()));
2246 setWindowFilePath(utf8_filename);
2247 g_free(utf8_filename);
2249 // So what the heck else can we do here?
2250 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
2253 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
2255 /* We have no capture file. */
2260 QString MainWindow::replaceWindowTitleVariables(QString title)
2262 title.replace ("%P", get_profile_name());
2263 title.replace ("%V", get_ws_vcs_version_info());
2268 void MainWindow::setWSWindowTitle(QString title)
2270 if (title.isEmpty()) {
2271 title = tr("The Wireshark Network Analyzer");
2274 if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
2275 QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
2276 title.prepend(QString("[%1] ").arg(custom_title));
2279 if (prefs.gui_window_title && prefs.gui_window_title[0]) {
2280 QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
2282 // On macOS we separate the titles with a unicode em dash
2283 title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
2285 title.append(QString(" [%1]").arg(custom_title));
2289 setWindowTitle(title);
2290 setWindowFilePath(NULL);
2293 void MainWindow::setTitlebarForCaptureInProgress()
2295 if (capture_file_.capFile()) {
2296 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
2298 /* We have no capture in progress. */
2305 /* Enable or disable menu items based on whether you have a capture file
2306 you've finished reading and, if you have one, whether it's been saved
2307 and whether it could be saved except by copying the raw packet data. */
2308 void MainWindow::setMenusForCaptureFile(bool force_disable)
2311 bool can_write = false;
2312 bool can_save = false;
2313 bool can_save_as = false;
2315 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
2316 /* We have no capture file or we're currently reading a file */
2319 /* We have a capture file. Can we write or save? */
2320 can_write = cf_can_write_with_wiretap(capture_file_.capFile());
2321 can_save = cf_can_save(capture_file_.capFile());
2322 can_save_as = cf_can_save_as(capture_file_.capFile());
2325 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(enable);
2326 main_ui_->actionFileMerge->setEnabled(can_write);
2327 main_ui_->actionFileClose->setEnabled(enable);
2328 main_ui_->actionFileSave->setEnabled(can_save);
2329 main_ui_->actionFileSaveAs->setEnabled(can_save_as);
2330 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(enable);
2332 * "Export Specified Packets..." should be available only if
2333 * we can write the file out in at least one format.
2335 main_ui_->actionFileExportPackets->setEnabled(can_write);
2337 main_ui_->actionFileExportAsCArrays->setEnabled(enable);
2338 main_ui_->actionFileExportAsCSV->setEnabled(enable);
2339 main_ui_->actionFileExportAsPDML->setEnabled(enable);
2340 main_ui_->actionFileExportAsPlainText->setEnabled(enable);
2341 main_ui_->actionFileExportAsPSML->setEnabled(enable);
2342 main_ui_->actionFileExportAsJSON->setEnabled(enable);
2344 main_ui_->actionFileExportPacketBytes->setEnabled(enable);
2345 main_ui_->actionFileExportPDU->setEnabled(enable);
2346 main_ui_->actionFileExportSSLSessionKeys->setEnabled(enable);
2348 foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2349 eo_action->setEnabled(enable);
2352 main_ui_->actionViewReload->setEnabled(enable);
2354 #ifdef HAVE_SOFTWARE_UPDATE
2355 // We might want to enable or disable automatic checks here as well.
2356 update_action_->setEnabled(!can_save);
2360 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
2361 /* Either a capture was started or stopped; in either case, it's not
2362 in the process of stopping, so allow quitting. */
2364 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
2365 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
2367 main_ui_->actionFileExportAsCArrays->setEnabled(capture_in_progress);
2368 main_ui_->actionFileExportAsCSV->setEnabled(capture_in_progress);
2369 main_ui_->actionFileExportAsPDML->setEnabled(capture_in_progress);
2370 main_ui_->actionFileExportAsPlainText->setEnabled(capture_in_progress);
2371 main_ui_->actionFileExportAsPSML->setEnabled(capture_in_progress);
2372 main_ui_->actionFileExportAsJSON->setEnabled(capture_in_progress);
2374 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
2375 main_ui_->actionFileExportPDU->setEnabled(!capture_in_progress);
2376 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
2378 foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2379 eo_action->setEnabled(capture_in_progress);
2382 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
2383 main_ui_->actionFileQuit->setEnabled(true);
2384 #ifdef HAVE_SOFTWARE_UPDATE
2385 // We might want to enable or disable automatic checks here as well.
2386 update_action_->setEnabled(!capture_in_progress);
2389 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
2391 // XXX Fix packet list heading menu sensitivity
2392 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2393 // !capture_in_progress);
2394 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2395 // !capture_in_progress);
2396 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2397 // !capture_in_progress);
2400 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
2401 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
2402 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
2403 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
2404 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
2405 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
2406 #endif /* HAVE_LIBPCAP */
2410 void MainWindow::setMenusForCaptureStopping() {
2411 main_ui_->actionFileQuit->setEnabled(false);
2412 #ifdef HAVE_SOFTWARE_UPDATE
2413 update_action_->setEnabled(false);
2415 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
2417 main_ui_->actionCaptureStart->setChecked(false);
2418 main_ui_->actionCaptureStop->setEnabled(false);
2419 main_ui_->actionCaptureRestart->setEnabled(false);
2420 #endif /* HAVE_LIBPCAP */
2423 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2425 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2427 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2428 // have_captured_packets);
2430 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2431 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2432 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2434 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2435 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2436 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2437 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2438 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2439 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2440 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2442 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2443 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2444 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2445 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2447 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2448 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2449 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2452 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2453 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2454 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2456 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2457 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2458 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2461 void MainWindow::setWindowIcon(const QIcon &icon) {
2462 wsApp->setWindowIcon(icon);
2463 QMainWindow::setWindowIcon(icon);
2466 void MainWindow::updateForUnsavedChanges() {
2467 setTitlebarForCaptureFile();
2468 setMenusForCaptureFile();
2471 void MainWindow::changeEvent(QEvent* event)
2475 switch (event->type())
2477 case QEvent::LanguageChange:
2478 main_ui_->retranslateUi(this);
2479 // make sure that the "Clear Menu" item is retranslated
2480 wsApp->emitAppSignal(WiresharkApplication::RecentCapturesChanged);
2482 case QEvent::LocaleChange:{
2483 QString locale = QLocale::system().name();
2484 locale.truncate(locale.lastIndexOf('_'));
2485 wsApp->loadLanguage(locale);
2488 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
2489 case QEvent::WindowStateChange:
2490 main_ui_->actionViewFullScreen->setChecked(this->isFullScreen());
2497 QMainWindow::changeEvent(event);
2500 void MainWindow::resizeEvent(QResizeEvent *event)
2502 df_combo_box_->setMinimumWidth(width() * 2 / 3); // Arbitrary
2503 QMainWindow::resizeEvent(event);
2506 /* Update main window items based on whether there's a capture in progress. */
2507 void MainWindow::setForCaptureInProgress(bool capture_in_progress, GArray *ifaces)
2509 setMenusForCaptureInProgress(capture_in_progress);
2511 wireless_frame_->setCaptureInProgress(capture_in_progress);
2514 packet_list_->setCaptureInProgress(capture_in_progress);
2515 packet_list_->setVerticalAutoScroll(capture_in_progress && main_ui_->actionGoAutoScroll->isChecked());
2517 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2521 QList<InterfaceToolbar *> toolbars = findChildren<InterfaceToolbar *>();
2522 foreach (InterfaceToolbar *toolbar, toolbars) {
2523 if (capture_in_progress && ifaces) {
2524 toolbar->startCapture(ifaces);
2526 toolbar->stopCapture();
2534 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2535 << REGISTER_ANALYZE_GROUP_UNSORTED
2536 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2537 << REGISTER_STAT_GROUP_UNSORTED
2538 << REGISTER_STAT_GROUP_GENERIC
2539 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2540 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2541 << REGISTER_STAT_GROUP_RESPONSE_TIME
2542 << REGISTER_STAT_GROUP_TELEPHONY
2543 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2544 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2545 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2546 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2547 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2548 << REGISTER_TOOLS_GROUP_UNSORTED;
2550 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2552 foreach (QAction *action, actions) {
2553 switch (menu_group) {
2554 case REGISTER_ANALYZE_GROUP_UNSORTED:
2555 case REGISTER_STAT_GROUP_UNSORTED:
2556 main_ui_->menuStatistics->insertAction(
2557 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2560 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2561 main_ui_->menuServiceResponseTime->addAction(action);
2563 case REGISTER_STAT_GROUP_TELEPHONY:
2564 main_ui_->menuTelephony->addAction(action);
2566 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2567 main_ui_->menuANSI->addAction(action);
2569 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2570 main_ui_->menuGSM->addAction(action);
2572 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2573 main_ui_->menuLTE->addAction(action);
2575 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2576 main_ui_->menuMTP3->addAction(action);
2578 case REGISTER_TOOLS_GROUP_UNSORTED:
2580 // Allow the creation of submenus. Mimics the behavor of
2581 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2582 // and GtkUIManager.
2584 // For now we limit the insanity to the "Tools" menu.
2585 QStringList menu_path = action->text().split('/');
2586 QMenu *cur_menu = main_ui_->menuTools;
2587 while (menu_path.length() > 1) {
2588 QString menu_title = menu_path.takeFirst();
2589 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2590 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2592 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2593 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2596 submenu = cur_menu->addMenu(menu_title);
2597 submenu->setObjectName(menu_title.toLower());
2601 action->setText(menu_path.last());
2602 cur_menu->addAction(action);
2606 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2610 // Connect each action type to its corresponding slot. We to
2611 // distinguish various types of actions. Setting their objectName
2612 // seems to work OK.
2613 if (action->objectName() == TapParameterDialog::actionName()) {
2614 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2615 } else if (action->objectName() == FunnelStatistics::actionName()) {
2616 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2620 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2622 foreach (QAction *action, actions) {
2623 switch (menu_group) {
2624 case REGISTER_ANALYZE_GROUP_UNSORTED:
2625 case REGISTER_STAT_GROUP_UNSORTED:
2626 main_ui_->menuStatistics->removeAction(action);
2628 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2629 main_ui_->menuServiceResponseTime->removeAction(action);
2631 case REGISTER_STAT_GROUP_TELEPHONY:
2632 main_ui_->menuTelephony->removeAction(action);
2634 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2635 main_ui_->menuANSI->removeAction(action);
2637 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2638 main_ui_->menuGSM->removeAction(action);
2640 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2641 main_ui_->menuLTE->removeAction(action);
2643 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2644 main_ui_->menuMTP3->removeAction(action);
2646 case REGISTER_TOOLS_GROUP_UNSORTED:
2648 // Allow removal of submenus.
2649 // For now we limit the insanity to the "Tools" menu.
2650 QStringList menu_path = action->text().split('/');
2651 QMenu *cur_menu = main_ui_->menuTools;
2652 while (menu_path.length() > 1) {
2653 QString menu_title = menu_path.takeFirst();
2654 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2655 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2657 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2658 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2662 cur_menu->removeAction(action);
2666 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2672 void MainWindow::addDynamicMenus()
2675 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2676 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2677 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2678 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2679 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2680 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2682 // Fill in each menu
2683 foreach (register_stat_group_t menu_group, menu_groups) {
2684 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2685 addMenuActions(actions, menu_group);
2688 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2689 // We've added a placeholder in order to make sure some menus are visible.
2690 // Hide them as needed.
2691 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2692 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2694 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2695 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2697 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2698 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2700 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2701 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2705 void MainWindow::reloadDynamicMenus()
2707 foreach (register_stat_group_t menu_group, menu_groups) {
2708 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2709 removeMenuActions(actions, menu_group);
2711 actions = wsApp->addedMenuGroupItems(menu_group);
2712 addMenuActions(actions, menu_group);
2715 wsApp->clearAddedMenuGroupItems();
2716 wsApp->clearRemovedMenuGroupItems();
2719 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2721 QAction * itemAction = NULL;
2722 ext_menubar_t * item = NULL;
2723 GList * children = NULL;
2725 /* There must exists an xpath parent */
2726 g_assert(subMenu != NULL);
2728 /* If the depth counter exceeds, something must have gone wrong */
2729 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2731 children = menu->children;
2732 /* Iterate the child entries */
2733 while (children && children->data) {
2734 item = (ext_menubar_t *) children->data;
2736 if (item->type == EXT_MENUBAR_MENU) {
2737 /* Handle Submenu entry */
2738 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2739 } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2740 subMenu->addSeparator();
2741 } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2742 itemAction = subMenu->addAction(item->name);
2743 itemAction->setData(QVariant::fromValue((void *)item));
2744 itemAction->setText(item->label);
2745 connect(itemAction, SIGNAL(triggered()),
2746 this, SLOT(externalMenuItem_triggered()));
2750 children = g_list_next(children);
2754 QMenu * MainWindow::searchSubMenu(QString objectName)
2758 if (objectName.length() > 0) {
2759 QString searchName = QString("menu") + objectName;
2761 lst = main_ui_->menuBar->findChildren<QMenu*>();
2762 foreach (QMenu* m, lst) {
2763 if (QString::compare(m->objectName(), searchName) == 0)
2771 void MainWindow::addPluginIFStructures()
2773 GList *user_menu = ext_menubar_get_entries();
2775 while (user_menu && user_menu->data) {
2776 QMenu *subMenu = NULL;
2777 ext_menu_t *menu = (ext_menu_t *) user_menu->data;
2779 /* On this level only menu items should exist. Not doing an assert here,
2780 * as it could be an honest mistake */
2781 if (menu->type != EXT_MENUBAR_MENU) {
2782 user_menu = g_list_next(user_menu);
2786 /* Create main submenu and add it to the menubar */
2787 if (menu->parent_menu) {
2788 QMenu *sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2790 subMenu = sortUnderneath->addMenu(menu->label);
2794 subMenu = main_ui_->menuBar->addMenu(menu->label);
2796 /* This will generate the action structure for each menu. It is recursive,
2797 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2798 this->externalMenuHelper(menu, subMenu, 0);
2801 user_menu = g_list_next (user_menu);
2804 int cntToolbars = 0;
2806 QMenu *tbMenu = main_ui_->menuAdditionalToolbars;
2807 GList *if_toolbars = ext_toolbar_get_entries();
2808 while (if_toolbars && if_toolbars->data) {
2809 ext_toolbar_t *toolbar = (ext_toolbar_t*) if_toolbars->data;
2811 if (toolbar->type != EXT_TOOLBAR_BAR) {
2812 if_toolbars = g_list_next (if_toolbars);
2816 bool visible = g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, (GCompareFunc) strcmp) ? true : false;
2818 AdditionalToolBar *ifToolBar = AdditionalToolBar::create(this, toolbar);
2821 ifToolBar->setVisible(visible);
2823 QAction *iftbAction = new QAction(QString(toolbar->name), this);
2824 iftbAction->setToolTip(toolbar->tooltip);
2825 iftbAction->setEnabled(true);
2826 iftbAction->setCheckable(true);
2827 iftbAction->setChecked(visible);
2828 iftbAction->setToolTip(tr("Show or hide the toolbar"));
2829 iftbAction->setData(VariantPointer<ext_toolbar_t>::asQVariant(toolbar));
2831 QAction *before = 0;
2833 foreach (QAction *action, tbMenu->actions()) {
2834 /* Ensure we add the menu entries in sorted order */
2835 if (action->text().compare(toolbar->name, Qt::CaseInsensitive) > 0) {
2841 tbMenu->insertAction(before, iftbAction);
2843 addToolBar(Qt::TopToolBarArea, ifToolBar);
2844 insertToolBarBreak(ifToolBar);
2846 if (show_hide_actions_)
2847 show_hide_actions_->addAction(iftbAction);
2852 if_toolbars = g_list_next (if_toolbars);
2856 tbMenu->menuAction()->setVisible(true);
2859 void MainWindow::removeAdditionalToolbar(QString toolbarName)
2861 if (toolbarName.length() == 0)
2864 QList<QToolBar *> toolbars = findChildren<QToolBar *>();
2865 foreach(QToolBar *tb, toolbars) {
2866 AdditionalToolBar *ifToolBar = dynamic_cast<AdditionalToolBar *>(tb);
2868 if (ifToolBar && ifToolBar->menuName().compare(toolbarName)) {
2869 GList *entry = g_list_find_custom(recent.gui_additional_toolbars, ifToolBar->menuName().toStdString().c_str(), (GCompareFunc) strcmp);
2871 recent.gui_additional_toolbars = g_list_remove(recent.gui_additional_toolbars, entry->data);
2873 QList<QAction *> actions = main_ui_->menuAdditionalToolbars->actions();
2874 foreach(QAction *action, actions) {
2875 ext_toolbar_t *item = VariantPointer<ext_toolbar_t>::asPtr(action->data());
2876 if (item && ifToolBar->menuName().compare(item->name)) {
2877 if (show_hide_actions_)
2878 show_hide_actions_->removeAction(action);
2879 main_ui_->menuAdditionalToolbars->removeAction(action);
2895 * indent-tabs-mode: nil
2898 * ex: set shiftwidth=4 tabstop=8 expandtab:
2899 * :indentSize=4:tabSize=8:noTabs=true: