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"
23 #include <ui_main_window.h>
25 #include <epan/addr_resolv.h>
26 #include "epan/dissector_filters.h"
27 #include <epan/epan_dissect.h>
28 #include <wsutil/filesystem.h>
29 #include <ws_version_info.h>
30 #include <epan/prefs.h>
31 #include <epan/stats_tree_priv.h>
32 #include <epan/plugin_if.h>
33 #include <epan/export_object.h>
35 #include "ui/commandline.h"
36 #include "ui/iface_toolbar.h"
39 #include "ui/capture.h"
40 #include <capchild/capture_session.h>
43 #include "ui/alert_box.h"
45 #include "ui/capture_ui_utils.h"
47 #include "ui/capture_globals.h"
48 #include "ui/main_statusbar.h"
49 #include "ui/recent.h"
50 #include "ui/recent_utils.h"
52 #include "ui/preference_utils.h"
54 #include "byte_view_tab.h"
56 #include "capture_interfaces_dialog.h"
58 #include "conversation_colorize_action.h"
59 #include "export_object_action.h"
60 #include "display_filter_edit.h"
61 #include "export_dissection_dialog.h"
62 #include "file_set_dialog.h"
63 #include "funnel_statistics.h"
64 #include "import_text_dialog.h"
65 #include "interface_toolbar.h"
66 #include "packet_list.h"
67 #include "proto_tree.h"
68 #include "simple_dialog.h"
69 #include "stock_icon.h"
70 #include "tap_parameter_dialog.h"
71 #include "wireless_frame.h"
72 #include "wireshark_application.h"
74 #include "additional_toolbar.h"
75 #include "variant_pointer.h"
77 #include "qt_ui_utils.h"
80 #include <QActionGroup>
81 #include <QDesktopWidget>
83 #include <QMessageBox>
84 #include <QMetaObject>
87 #include <QToolButton>
88 #include <QTreeWidget>
91 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
92 #include <QtMacExtras/QMacNativeToolBar>
96 //menu_recent_file_write_all
98 // If we ever add support for multiple windows this will need to be replaced.
99 static MainWindow *gbl_cur_main_window_ = NULL;
101 void pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
103 gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
106 static void plugin_if_mainwindow_apply_filter(gconstpointer user_data)
108 if (!gbl_cur_main_window_ || !user_data)
111 GHashTable * data_set = (GHashTable *) user_data;
113 if (g_hash_table_lookup_extended(data_set, "filter_string", NULL, NULL)) {
114 QString filter((const char *)g_hash_table_lookup(data_set, "filter_string"));
115 gbl_cur_main_window_->filterPackets(filter);
119 static void plugin_if_mainwindow_preference(gconstpointer user_data)
121 if (!gbl_cur_main_window_ || !user_data)
124 GHashTable * data_set = (GHashTable *) user_data;
125 const char * module_name;
126 const char * pref_name;
127 const char * pref_value;
129 if (g_hash_table_lookup_extended(data_set, "pref_module", NULL, (void**)&module_name) &&
130 g_hash_table_lookup_extended(data_set, "pref_key", NULL, (void**)&pref_name) &&
131 g_hash_table_lookup_extended(data_set, "pref_value", NULL, (void**)&pref_value))
133 if (prefs_store_ext(module_name, pref_name, pref_value)) {
134 wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
135 wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
140 static void plugin_if_mainwindow_gotoframe(gconstpointer user_data)
142 if (!gbl_cur_main_window_ || !user_data)
145 GHashTable * data_set = (GHashTable *) user_data;
148 if (g_hash_table_lookup_extended(data_set, "frame_nr", NULL, &framenr)) {
149 if (GPOINTER_TO_UINT(framenr) != 0)
150 gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr));
156 static void plugin_if_mainwindow_get_ws_info(gconstpointer user_data)
158 if (!gbl_cur_main_window_ || !user_data)
161 GHashTable * data_set = (GHashTable *)user_data;
162 ws_info_t *ws_info = NULL;
164 if (!g_hash_table_lookup_extended(data_set, "ws_info", NULL, (void**)&ws_info))
167 CaptureFile *cfWrap = gbl_cur_main_window_->captureFile();
168 capture_file *cf = cfWrap->capFile();
170 ws_info->ws_info_supported = true;
173 ws_info->cf_state = cf->state;
174 ws_info->cf_count = cf->count;
176 g_free(ws_info->cf_filename);
177 ws_info->cf_filename = g_strdup(cf->filename);
179 if (cf->state == FILE_READ_DONE && cf->current_frame) {
180 ws_info->cf_framenr = cf->current_frame->num;
181 ws_info->frame_passed_dfilter = (cf->current_frame->flags.passed_dfilter == 1);
183 ws_info->cf_framenr = 0;
184 ws_info->frame_passed_dfilter = FALSE;
186 } else if (ws_info->cf_state != FILE_CLOSED) {
187 /* Initialise the ws_info structure */
188 ws_info->cf_count = 0;
190 g_free(ws_info->cf_filename);
191 ws_info->cf_filename = NULL;
193 ws_info->cf_framenr = 0;
194 ws_info->frame_passed_dfilter = FALSE;
195 ws_info->cf_state = FILE_CLOSED;
199 #endif /* HAVE_LIBPCAP */
201 static void plugin_if_mainwindow_update_toolbars(gconstpointer user_data)
203 if (!gbl_cur_main_window_ || ! user_data)
206 GHashTable * data_set = (GHashTable *)user_data;
207 if (g_hash_table_lookup_extended(data_set, "toolbar_name", NULL, NULL)) {
208 QString toolbarName((const char *)g_hash_table_lookup(data_set, "toolbar_name"));
209 gbl_cur_main_window_->removeAdditionalToolbar(toolbarName);
214 static void mainwindow_add_toolbar(const iface_toolbar *toolbar_entry)
216 if (gbl_cur_main_window_ && toolbar_entry)
218 gbl_cur_main_window_->addInterfaceToolbar(toolbar_entry);
222 static void mainwindow_remove_toolbar(const gchar *menu_title)
224 if (gbl_cur_main_window_ && menu_title)
226 gbl_cur_main_window_->removeInterfaceToolbar(menu_title);
231 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
235 va_start(ap, msg_format);
236 SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
244 * Alert box, with optional "don't show this message again" variable
245 * and checkbox, and optional secondary text.
248 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
249 const char *secondary_msg, const char *msg_format, ...)
251 if (notagain && *notagain) {
257 va_start(ap, msg_format);
258 SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
261 sd.setDetailedText(secondary_msg);
263 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
264 QCheckBox *cb = NULL;
266 cb = new QCheckBox();
267 cb->setChecked(true);
268 cb->setText(QObject::tr("Don't show this message again."));
275 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
276 if (notagain && cb) {
277 *notagain = cb->isChecked();
283 * Error alert box, taking a format and a va_list argument.
286 vsimple_error_message_box(const char *msg_format, va_list ap)
289 // We want to quit after reading the capture file, hence
290 // we don't actually open the error dialog.
291 if (global_commandline_info.quit_after_cap)
295 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
300 * Warning alert box, taking a format and a va_list argument.
303 vsimple_warning_message_box(const char *msg_format, va_list ap)
306 // We want to quit after reading the capture file, hence
307 // we don't actually open the error dialog.
308 if (global_commandline_info.quit_after_cap)
312 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_WARN, ESD_BTN_OK, msg_format, ap);
316 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
317 QList<QAction *> actions = parent_menu->actions();
318 QList<QAction *>::const_iterator i;
319 for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
320 if ((*i)->text()==menu_text) {
324 // If we get here there menu entry was not found, add a sub menu
325 return parent_menu->addMenu(menu_text);
328 MainWindow::MainWindow(QWidget *parent) :
330 main_ui_(new Ui::MainWindow),
331 cur_layout_(QVector<unsigned>()),
335 previous_focus_(NULL),
336 file_set_dialog_(NULL),
337 show_hide_actions_(NULL),
338 time_display_actions_(NULL),
339 time_precision_actions_(NULL),
340 funnel_statistics_(NULL),
342 was_maximized_(false),
343 capture_stopping_(false),
344 capture_filter_valid_(false)
346 , capture_interfaces_dialog_(NULL)
352 , pipe_notifier_(NULL)
354 #if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
358 if (!gbl_cur_main_window_) {
359 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
360 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
361 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
362 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
364 gbl_cur_main_window_ = this;
366 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
369 // setpUi calls QMetaObject::connectSlotsByName(this). connectSlotsByName
370 // iterates over *all* of our children, looking for matching "on_" slots.
371 // The fewer children we have at this point the better.
372 main_ui_->setupUi(this);
373 #ifdef HAVE_SOFTWARE_UPDATE
374 update_action_ = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
376 setWindowIcon(wsApp->normalIcon());
377 setTitlebarForCaptureFile();
378 setMenusForCaptureFile();
379 setForCapturedPackets(false);
380 setMenusForFileSet(false);
381 interfaceSelectionChanged();
382 loadWindowGeometry();
385 main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
388 qRegisterMetaType<FilterAction::Action>("FilterAction::Action");
389 qRegisterMetaType<FilterAction::ActionType>("FilterAction::ActionType");
390 connect(this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)),
391 this, SLOT(queuedFilterAction(QString,FilterAction::Action,FilterAction::ActionType)),
392 Qt::QueuedConnection);
394 //To prevent users use features before initialization complete
395 //Otherwise unexpected problems may occur
396 setFeaturesEnabled(false);
397 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
398 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(applyGlobalCommandLineOptions()));
399 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
400 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
401 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
402 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
403 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addPluginIFStructures()));
404 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initConversationMenus()));
405 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initExportObjectsMenus()));
407 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
408 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
409 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
410 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
411 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
412 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
414 connect(wsApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures()));
415 updateRecentCaptures();
417 #ifdef HAVE_SOFTWARE_UPDATE
418 connect(wsApp, SIGNAL(softwareUpdateRequested()), this, SLOT(softwareUpdateRequested()),
419 Qt::BlockingQueuedConnection);
420 connect(wsApp, SIGNAL(softwareUpdateClose()), this, SLOT(close()),
421 Qt::BlockingQueuedConnection);
424 df_combo_box_ = new DisplayFilterCombo();
425 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
426 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
427 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
428 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
429 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
430 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
431 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
432 connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
433 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
434 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
436 funnel_statistics_ = new FunnelStatistics(this, capture_file_);
437 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
438 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
439 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
440 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
441 this, SLOT(openCaptureFile(QString,QString)));
442 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
444 file_set_dialog_ = new FileSetDialog(this);
445 connect(file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
446 this, SLOT(openCaptureFile(QString)));
448 initMainToolbarIcons();
450 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
452 // Make sure filter expressions overflow into a menu instead of a
453 // larger toolbar. We do this by adding them to a child toolbar.
454 // https://bugreports.qt.io/browse/QTBUG-2472
455 filter_expression_toolbar_ = new QToolBar();
456 filter_expression_toolbar_->setStyleSheet("QToolBar { background: none; border: none; }");
457 main_ui_->displayFilterToolBar->addWidget(filter_expression_toolbar_);
459 wireless_frame_ = new WirelessFrame(this);
460 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
461 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
462 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
463 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
464 this, SLOT(showPreferencesDialog(QString)));
466 main_ui_->goToFrame->hide();
467 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
468 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
470 // XXX For some reason the cursor is drawn funny with an input mask set
471 // https://bugreports.qt-project.org/browse/QTBUG-7174
473 main_ui_->searchFrame->hide();
474 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
475 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
476 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
477 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
479 main_ui_->addressEditorFrame->hide();
480 main_ui_->columnEditorFrame->hide();
481 main_ui_->preferenceEditorFrame->hide();
482 main_ui_->filterExpressionFrame->hide();
485 main_ui_->menuCapture->setEnabled(false);
488 // Set OS specific shortcuts for fullscreen mode
489 #if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
490 main_ui_->actionViewFullScreen->setShortcut(QKeySequence::FullScreen);
492 main_ui_->actionViewFullScreen->setShortcut(QKeySequence(Qt::Key_F11));
495 #if defined(Q_OS_MAC)
496 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
497 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
498 ntb->setIconSize(QSize(24, 24));
499 #endif // QT_MACEXTRAS_LIB
501 main_ui_->goToPacketLabel->setAttribute(Qt::WA_MacSmallSize, true);
502 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
503 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
504 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
506 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
510 #ifdef HAVE_SOFTWARE_UPDATE
511 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
512 main_ui_->menuHelp->insertAction(update_sep, update_action_);
513 connect(update_action_, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
515 master_split_.setObjectName("splitterMaster");
516 extra_split_.setObjectName("splitterExtra");
517 main_ui_->mainStack->addWidget(&master_split_);
519 empty_pane_.setObjectName("emptyPane");
521 packet_list_ = new PacketList(&master_split_);
523 proto_tree_ = new ProtoTree(&master_split_);
524 proto_tree_->installEventFilter(this);
526 byte_view_tab_ = new ByteViewTab(&master_split_);
528 packet_list_->setProtoTree(proto_tree_);
529 packet_list_->setByteViewTab(byte_view_tab_);
530 packet_list_->installEventFilter(this);
532 main_welcome_ = main_ui_->welcomePage;
534 // Packet list and proto tree must exist before these are called.
535 setMenusForSelectedPacket();
536 setMenusForSelectedTreeRow();
538 initShowHideMainWidgets();
539 initTimeDisplayFormatMenu();
540 initTimePrecisionFormatMenu();
542 updatePreferenceActions();
543 updateRecentActions();
544 setForCaptureInProgress(false);
546 setTabOrder(df_combo_box_->lineEdit(), packet_list_);
547 setTabOrder(packet_list_, proto_tree_);
549 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
550 this, SLOT(captureCapturePrepared(capture_session *)));
551 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
552 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
553 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
554 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
555 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
556 this, SLOT(captureCaptureFixedStarted(capture_session *)));
557 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
558 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
559 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
560 this, SLOT(captureCaptureFixedFinished(capture_session *)));
561 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
562 this, SLOT(captureCaptureStopping(capture_session *)));
563 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
564 this, SLOT(captureCaptureFailed(capture_session *)));
565 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
566 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
568 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
569 wsApp, SLOT(captureStarted()));
570 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
571 wsApp, SLOT(captureFinished()));
572 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
573 wsApp, SLOT(captureStarted()));
574 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
575 wsApp, SLOT(captureFinished()));
577 connect(&capture_file_, SIGNAL(captureFileOpened()),
578 this, SLOT(captureFileOpened()));
579 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
580 this, SLOT(captureFileReadStarted()));
581 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
582 this, SLOT(captureFileReadFinished()));
583 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
584 this, SLOT(captureFileReloadStarted()));
585 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
586 this, SLOT(captureFileReadFinished()));
587 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
588 this, SLOT(captureFileRescanStarted()));
589 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
590 this, SLOT(captureFileReadFinished()));
591 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
592 this, SLOT(captureFileRetapStarted()));
593 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
594 this, SLOT(captureFileRetapFinished()));
595 connect(&capture_file_, SIGNAL(captureFileMergeStarted()),
596 this, SLOT(captureFileMergeStarted()));
597 connect(&capture_file_, SIGNAL(captureFileMergeFinished()),
598 this, SLOT(captureFileMergeFinished()));
599 connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
600 this, SLOT(captureFileFlushTapsData()));
601 connect(&capture_file_, SIGNAL(captureFileClosing()),
602 this, SLOT(captureFileClosing()));
603 connect(&capture_file_, SIGNAL(captureFileClosed()),
604 this, SLOT(captureFileClosed()));
606 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
607 this, SLOT(captureFileSaveStarted(QString)));
608 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
609 main_ui_->statusBar, SLOT(popFileStatus()));
610 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
611 main_ui_->statusBar, SLOT(popFileStatus()));
612 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
613 main_ui_->statusBar, SLOT(popFileStatus()));
615 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
616 wsApp, SLOT(captureFileReadStarted()));
617 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
618 wsApp, SLOT(updateTaps()));
620 connect(wsApp, SIGNAL(columnsChanged()),
621 packet_list_, SLOT(columnsChanged()));
622 connect(wsApp, SIGNAL(preferencesChanged()),
623 packet_list_, SLOT(preferencesChanged()));
624 connect(wsApp, SIGNAL(recentPreferencesRead()),
625 this, SLOT(applyRecentPaneGeometry()));
626 connect(wsApp, SIGNAL(recentPreferencesRead()),
627 this, SLOT(updateRecentActions()));
628 connect(wsApp, SIGNAL(packetDissectionChanged()),
629 this, SLOT(redissectPackets()), Qt::QueuedConnection);
630 connect(wsApp, SIGNAL(appInitialized()),
631 this, SLOT(filterExpressionsChanged()));
632 connect(wsApp, SIGNAL(filterExpressionsChanged()),
633 this, SLOT(filterExpressionsChanged()));
634 connect(wsApp, SIGNAL(checkDisplayFilter()),
635 this, SLOT(checkDisplayFilter()));
636 connect(wsApp, SIGNAL(fieldsChanged()),
637 this, SLOT(fieldsChanged()));
638 connect(wsApp, SIGNAL(reloadLuaPlugins()),
639 this, SLOT(reloadLuaPlugins()));
641 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
642 this, SLOT(mainStackChanged(int)));
644 connect(main_welcome_, SIGNAL(startCapture()),
645 this, SLOT(startCapture()));
646 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
647 this, SLOT(openCaptureFile(QString)));
648 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
649 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
650 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
651 main_ui_->statusBar, SLOT(popFilterStatus()));
653 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
654 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
655 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
656 this, SLOT(redissectPackets()));
657 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
658 this, SLOT(showPreferencesDialog(QString)));
659 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
660 this, SLOT(showPreferencesDialog(QString)));
661 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
662 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
663 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
664 this, SLOT(filterExpressionsChanged()));
666 connect(this, SIGNAL(setCaptureFile(capture_file*)),
667 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
668 connect(this, SIGNAL(setCaptureFile(capture_file*)),
669 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
670 connect(this, SIGNAL(setCaptureFile(capture_file*)),
671 packet_list_, SLOT(setCaptureFile(capture_file*)));
672 connect(this, SIGNAL(setCaptureFile(capture_file*)),
673 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
675 connect(this, SIGNAL(monospaceFontChanged(QFont)),
676 packet_list_, SLOT(setMonospaceFont(QFont)));
677 connect(this, SIGNAL(monospaceFontChanged(QFont)),
678 proto_tree_, SLOT(setMonospaceFont(QFont)));
679 connect(this, SIGNAL(monospaceFontChanged(QFont)),
680 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
682 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
683 packet_list_, SLOT(goNextPacket()));
684 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
685 packet_list_, SLOT(goPreviousPacket()));
686 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
687 packet_list_, SLOT(goFirstPacket()));
688 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
689 packet_list_, SLOT(goLastPacket()));
690 connect(main_ui_->actionGoNextHistoryPacket, SIGNAL(triggered()),
691 packet_list_, SLOT(goNextHistoryPacket()));
692 connect(main_ui_->actionGoPreviousHistoryPacket, SIGNAL(triggered()),
693 packet_list_, SLOT(goPreviousHistoryPacket()));
695 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
696 proto_tree_, SLOT(expandSubtrees()));
697 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
698 proto_tree_, SLOT(expandAll()));
699 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
700 proto_tree_, SLOT(collapseAll()));
702 connect(packet_list_, SIGNAL(packetSelectionChanged()),
703 this, SLOT(setMenusForSelectedPacket()));
704 connect(packet_list_, SIGNAL(packetDissectionChanged()),
705 this, SLOT(redissectPackets()));
706 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
707 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
708 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
709 this, SLOT(showPreferencesDialog(QString)));
710 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
711 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
712 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
713 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
714 packet_list_, SLOT(columnsChanged()));
715 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
716 this, SLOT(openPacketDialog()));
717 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
718 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
719 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
720 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
721 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
722 main_ui_->statusBar, SLOT(popBusyStatus()));
723 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
724 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
725 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
726 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
727 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
728 main_ui_->statusBar, SLOT(popProgressStatus()));
730 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
731 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
732 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
733 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
734 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
735 this, SLOT(openPacketDialog(bool)));
736 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
737 this, SLOT(showPreferencesDialog(QString)));
738 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
739 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
741 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
742 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
743 connect(byte_view_tab_, SIGNAL(currentChanged(int)),
744 this, SLOT(byteViewTabChanged(int)));
746 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
747 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
749 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
750 &capture_file_, SLOT(stopLoading()));
752 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
753 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
756 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
758 connect(iface_tree, SIGNAL(itemSelectionChanged()),
759 this, SLOT(interfaceSelectionChanged()));
761 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
762 this, SLOT(captureFilterSyntaxChanged(bool)));
765 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
766 this, SLOT(showExtcapOptionsDialog(QString&)));
769 #endif // HAVE_LIBPCAP
771 /* Create plugin_if hooks */
772 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
773 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
774 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
775 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
777 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
779 plugin_if_register_gui_cb(PLUGIN_IF_REMOVE_TOOLBAR, plugin_if_mainwindow_update_toolbars);
781 #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) && !defined(_WIN32)
782 // Register Interface Toolbar callbacks
784 // Qt version must be 5.2 or higher because the use of
785 // QThread::requestInterruption() in interface_toolbar.cpp and
786 // QThread::isInterruptionRequested() in interface_toolbar_reader.cpp
788 // The toolbar in/out control pipes are not supported on WIN32 yet.
789 iface_toolbar_register_cb(mainwindow_add_toolbar, mainwindow_remove_toolbar);
792 main_ui_->mainStack->setCurrentWidget(main_welcome_);
795 MainWindow::~MainWindow()
800 QString MainWindow::getFilter()
802 return df_combo_box_->currentText();
805 QMenu *MainWindow::createPopupMenu()
807 QMenu *menu = new QMenu();
808 menu->addAction(main_ui_->actionViewMainToolbar);
809 menu->addAction(main_ui_->actionViewFilterToolbar);
810 menu->addAction(main_ui_->actionViewWirelessToolbar);
812 if (!main_ui_->menuInterfaceToolbars->actions().isEmpty()) {
813 QMenu *submenu = menu->addMenu(main_ui_->menuInterfaceToolbars->title());
814 foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
815 submenu->addAction(action);
819 if (!main_ui_->menuAdditionalToolbars->actions().isEmpty()) {
820 QMenu *subMenu = menu->addMenu(main_ui_->menuAdditionalToolbars->title());
821 foreach (QAction *action, main_ui_->menuAdditionalToolbars->actions()) {
822 subMenu->addAction(action);
826 menu->addAction(main_ui_->actionViewStatusBar);
828 menu->addSeparator();
829 menu->addAction(main_ui_->actionViewPacketList);
830 menu->addAction(main_ui_->actionViewPacketDetails);
831 menu->addAction(main_ui_->actionViewPacketBytes);
835 void MainWindow::addInterfaceToolbar(const iface_toolbar *toolbar_entry)
837 QMenu *menu = main_ui_->menuInterfaceToolbars;
838 bool visible = g_list_find_custom(recent.interface_toolbars, toolbar_entry->menu_title, (GCompareFunc) strcmp) ? true : false;
840 QString title = QString().fromUtf8(toolbar_entry->menu_title);
841 QAction *action = new QAction(title, menu);
842 action->setEnabled(true);
843 action->setCheckable(true);
844 action->setChecked(visible);
845 action->setToolTip(tr("Show or hide the toolbar"));
847 QAction *before = NULL;
848 foreach (QAction *action, menu->actions()) {
849 // Ensure we add the menu entries in sorted order
850 if (action->text().compare(title, Qt::CaseInsensitive) > 0) {
855 menu->insertAction(before, action);
857 InterfaceToolbar *interface_toolbar = new InterfaceToolbar(this, toolbar_entry);
859 QToolBar *toolbar = new QToolBar(this);
860 toolbar->addWidget(interface_toolbar);
861 toolbar->setMovable(false);
862 toolbar->setVisible(visible);
864 action->setData(qVariantFromValue(toolbar));
866 addToolBar(Qt::TopToolBarArea, toolbar);
867 insertToolBarBreak(toolbar);
869 if (show_hide_actions_) {
870 show_hide_actions_->addAction(action);
873 menu->menuAction()->setVisible(true);
876 void MainWindow::removeInterfaceToolbar(const gchar *menu_title)
878 QMenu *menu = main_ui_->menuInterfaceToolbars;
879 QAction *action = NULL;
880 QMap<QAction *, QWidget *>::iterator i;
882 QString title = QString().fromUtf8(menu_title);
883 foreach (action, menu->actions()) {
884 if (title.compare(action->text()) == 0) {
890 if (show_hide_actions_) {
891 show_hide_actions_->removeAction(action);
893 menu->removeAction(action);
895 QToolBar *toolbar = action->data().value<QToolBar *>();
896 removeToolBar(toolbar);
902 menu->menuAction()->setVisible(!menu->actions().isEmpty());
905 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
907 pipe_source_ = source;
908 pipe_child_process_ = child_process;
909 pipe_user_data_ = user_data;
910 pipe_input_cb_ = input_cb;
913 /* Tricky to use pipes in win9x, as no concept of wait. NT can
914 do this but that doesn't cover all win32 platforms. GTK can do
915 this but doesn't seem to work over processes. Attempt to do
916 something similar here, start a timer and check for data on every
918 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
921 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
925 pipe_timer_ = new QTimer(this);
926 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
927 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
928 pipe_timer_->start(200);
930 if (pipe_notifier_) {
931 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
932 delete pipe_notifier_;
935 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
936 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
937 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
938 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
942 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
944 // The user typed some text. Start filling in a filter.
945 // We may need to be more choosy here. We just need to catch events for the packet list,
946 // proto tree, and main welcome widgets.
947 if (event->type() == QEvent::KeyPress) {
948 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
949 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
950 df_combo_box_->lineEdit()->insert(kevt->text());
951 df_combo_box_->lineEdit()->setFocus();
956 return QMainWindow::eventFilter(obj, event);
959 void MainWindow::keyPressEvent(QKeyEvent *event) {
961 // Explicitly focus on the display filter combo.
962 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
963 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
967 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
968 if (event->modifiers() == Qt::NoModifier) {
969 if (event->key() == Qt::Key_Escape) {
970 on_goToCancel_clicked();
971 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
975 return; // goToLineEdit didn't want it and we don't either.
978 // Move up & down the packet list.
979 if (event->key() == Qt::Key_F7) {
980 packet_list_->goPreviousPacket();
981 } else if (event->key() == Qt::Key_F8) {
982 packet_list_->goNextPacket();
985 // Move along, citizen.
986 QMainWindow::keyPressEvent(event);
989 void MainWindow::closeEvent(QCloseEvent *event) {
990 saveWindowGeometry();
992 /* If we're in the middle of stopping a capture, don't do anything;
993 the user can try deleting the window after the capture stops. */
994 if (capture_stopping_) {
999 QString before_what(tr(" before quitting"));
1000 if (!testCaptureFileClose(before_what, Quit)) {
1006 if (capture_interfaces_dialog_) capture_interfaces_dialog_->close();
1008 // Make sure we kill any open dumpcap processes.
1009 delete main_welcome_;
1011 // One of the many places we assume one main window.
1012 if(!wsApp->isInitialized()) {
1013 // If we're still initializing, QCoreApplication::quit() won't
1014 // exit properly because we are not in the event loop. This
1015 // means that the application won't clean up after itself. We
1016 // might want to call wsApp->processEvents() during startup
1017 // instead so that we can do a normal exit here.
1023 // XXX On windows the drag description is "Copy". It should be "Open" or
1024 // "Merge" as appropriate. It looks like we need access to IDataObject in
1025 // order to set DROPDESCRIPTION.
1026 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
1028 if (!main_ui_->actionFileOpen->isEnabled()) {
1029 // We could alternatively call setAcceptDrops(!capture_in_progress)
1030 // in setMenusForCaptureInProgress but that wouldn't provide feedback.
1032 main_ui_->statusBar->pushTemporaryStatus(tr("Unable to drop files during capture."));
1033 event->setDropAction(Qt::IgnoreAction);
1038 bool have_files = false;
1039 foreach (QUrl drag_url, event->mimeData()->urls()) {
1040 if (!drag_url.toLocalFile().isEmpty()) {
1047 event->acceptProposedAction();
1051 void MainWindow::dropEvent(QDropEvent *event)
1053 QList<QByteArray> local_files;
1055 foreach (QUrl drop_url, event->mimeData()->urls()) {
1056 QString drop_file = drop_url.toLocalFile();
1057 if (!drop_file.isEmpty()) {
1058 local_files << drop_file.toUtf8();
1062 if (local_files.size() < 1) {
1065 event->acceptProposedAction();
1068 if (local_files.size() == 1) {
1069 openCaptureFile(local_files.at(0));
1073 char **in_filenames = (char **)g_malloc(sizeof(char*) * local_files.size());
1074 char *tmpname = NULL;
1076 for (int i = 0; i < local_files.size(); i++) {
1077 in_filenames[i] = (char *) local_files.at(i).constData();
1080 /* merge the files in chronological order */
1081 if (cf_merge_files_to_tempfile(this, &tmpname, local_files.size(),
1082 in_filenames, WTAP_FILE_TYPE_SUBTYPE_PCAPNG,
1084 /* Merge succeeded; close the currently-open file and try
1085 to open the merged capture file. */
1086 openCaptureFile(tmpname, QString(), WTAP_TYPE_AUTO, TRUE);
1090 g_free(in_filenames);
1094 // Apply recent settings to the main window geometry.
1095 // We haven't loaded the preferences at this point so we assume that the
1096 // position and size preference are enabled.
1097 // Note we might end up with unexpected screen geometries if the user
1098 // unplugs or plugs in a monitor:
1099 // https://bugreports.qt.io/browse/QTBUG-44213
1100 void MainWindow::loadWindowGeometry()
1102 int min_sensible_dimension = 200;
1105 if (recent.gui_geometry_main_maximized) {
1106 setWindowState(Qt::WindowMaximized);
1110 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
1111 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
1112 if (!rect_on_screen(recent_geom)) {
1113 // We're not visible on any screens. See if we can move onscreen
1114 // without resizing.
1115 recent_geom.moveTo(50, 50); // recent.c defaults to 20.
1118 if (!rect_on_screen(recent_geom)) {
1119 // Give up and use the default geometry.
1123 // if (prefs.gui_geometry_save_position) {
1124 move(recent_geom.topLeft());
1127 if (// prefs.gui_geometry_save_size &&
1128 recent_geom.width() > min_sensible_dimension &&
1129 recent_geom.height() > min_sensible_dimension) {
1130 resize(recent_geom.size());
1135 void MainWindow::saveWindowGeometry()
1137 if (prefs.gui_geometry_save_position) {
1138 recent.gui_geometry_main_x = pos().x();
1139 recent.gui_geometry_main_y = pos().y();
1142 if (prefs.gui_geometry_save_size) {
1143 recent.gui_geometry_main_width = size().width();
1144 recent.gui_geometry_main_height = size().height();
1147 if (prefs.gui_geometry_save_maximized) {
1148 // On macOS this is false when it shouldn't be
1149 recent.gui_geometry_main_maximized = isMaximized();
1152 if (master_split_.sizes().length() > 0) {
1153 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
1156 if (master_split_.sizes().length() > 2) {
1157 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
1158 } else if (extra_split_.sizes().length() > 0) {
1159 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
1163 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
1165 case layout_pane_content_none:
1166 return &empty_pane_;
1167 case layout_pane_content_plist:
1168 return packet_list_;
1169 case layout_pane_content_pdetails:
1171 case layout_pane_content_pbytes:
1172 return byte_view_tab_;
1174 g_assert_not_reached();
1179 // Our event loop becomes nested whenever we call update_progress_dlg, which
1180 // includes several places in file.c. The GTK+ UI stays out of trouble by
1181 // showing a modal progress dialog. We attempt to do the equivalent below by
1182 // disabling parts of the main window. At a minumum the ProgressFrame in the
1183 // main status bar must remain accessible.
1185 // We might want to do this any time the main status bar progress frame is
1186 // shown and hidden.
1187 void MainWindow::freeze()
1189 freeze_focus_ = wsApp->focusWidget();
1191 // XXX Alternatively we could just disable and enable the main menu.
1192 for (int i = 0; i < freeze_actions_.size(); i++) {
1193 QAction *action = freeze_actions_[i].first;
1194 freeze_actions_[i].second = action->isEnabled();
1195 action->setEnabled(false);
1197 main_ui_->centralWidget->setEnabled(false);
1200 void MainWindow::thaw()
1202 main_ui_->centralWidget->setEnabled(true);
1203 for (int i = 0; i < freeze_actions_.size(); i++) {
1204 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
1207 if (freeze_focus_) freeze_focus_->setFocus();
1208 freeze_focus_ = NULL;
1211 void MainWindow::mergeCaptureFile()
1213 QString file_name = "";
1214 QString read_filter = "";
1215 dfilter_t *rfcode = NULL;
1218 if (!capture_file_.capFile())
1221 if (prefs.gui_ask_unsaved) {
1222 if (cf_has_unsaved_data(capture_file_.capFile())) {
1223 QMessageBox msg_dialog;
1224 gchar *display_basename;
1227 msg_dialog.setIcon(QMessageBox::Question);
1228 /* This file has unsaved data; ask the user whether to save
1230 if (capture_file_.capFile()->is_tempfile) {
1231 msg_dialog.setText(tr("Save packets before merging?"));
1232 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
1235 * Format the message.
1237 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1238 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
1239 g_free(display_basename);
1240 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
1243 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
1244 msg_dialog.setDefaultButton(QMessageBox::Save);
1246 response = msg_dialog.exec();
1250 case QMessageBox::Save:
1251 /* Save the file but don't close it */
1252 saveCaptureFile(capture_file_.capFile(), false);
1255 case QMessageBox::Cancel:
1257 /* Don't do the merge. */
1264 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1266 cf_status_t merge_status;
1267 char *in_filenames[2];
1270 if (merge_dlg.merge(file_name)) {
1273 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1274 /* Not valid. Tell the user, and go back and run the file
1275 selection box again once they dismiss the alert. */
1276 // Similar to commandline_info.jfilter section in main().
1277 QMessageBox::warning(this, tr("Invalid Read Filter"),
1278 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1287 file_type = capture_file_.capFile()->cd_t;
1289 /* Try to merge or append the two files */
1290 if (merge_dlg.mergeType() == 0) {
1291 /* chronological order */
1292 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1293 in_filenames[1] = qstring_strdup(file_name);
1294 merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, FALSE);
1295 } else if (merge_dlg.mergeType() <= 0) {
1297 in_filenames[0] = qstring_strdup(file_name);
1298 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1299 merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, TRUE);
1302 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1303 in_filenames[1] = qstring_strdup(file_name);
1304 merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, TRUE);
1307 g_free(in_filenames[0]);
1308 g_free(in_filenames[1]);
1310 if (merge_status != CF_OK) {
1312 dfilter_free(rfcode);
1317 cf_close(capture_file_.capFile());
1319 /* Try to open the merged capture file. */
1320 CaptureFile::globalCapFile()->window = this;
1321 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1322 /* We couldn't open it; fail. */
1323 CaptureFile::globalCapFile()->window = NULL;
1325 dfilter_free(rfcode);
1330 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1331 it closed the previous capture file, and thus destroyed any
1332 previous read filter attached to "cf"). */
1333 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1335 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1339 /* Just because we got an error, that doesn't mean we were unable
1340 to read any of the file; we handle what we could get from the
1344 case CF_READ_ABORTED:
1345 /* The user bailed out of re-reading the capture file; the
1346 capture file has been closed - just free the capture file name
1347 string and return (without changing the last containing
1353 /* Save the name of the containing directory specified in the path name,
1354 if any; we can write over cf_merged_name, which is a good thing, given that
1355 "get_dirname()" does write over its argument. */
1356 wsApp->setLastOpenDir(get_dirname(tmpname));
1358 main_ui_->statusBar->showExpert();
1364 void MainWindow::importCaptureFile() {
1365 ImportTextDialog import_dlg;
1367 QString before_what(tr(" before importing a capture"));
1368 if (!testCaptureFileClose(before_what))
1373 if (import_dlg.result() != QDialog::Accepted) {
1374 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1378 openCaptureFile(import_dlg.capfileName());
1381 bool MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1383 gboolean discard_comments;
1385 if (cf->is_tempfile) {
1386 /* This is a temporary capture file, so saving it means saving
1387 it to a permanent file. Prompt the user for a location
1388 to which to save it. Don't require that the file format
1389 support comments - if it's a temporary capture file, it's
1390 probably pcap-ng, which supports comments and, if it's
1391 not pcap-ng, let the user decide what they want to do
1392 if they've added comments. */
1393 return saveAsCaptureFile(cf, FALSE, dont_reopen);
1395 if (cf->unsaved_changes) {
1396 cf_write_status_t status;
1398 /* This is not a temporary capture file, but it has unsaved
1399 changes, so saving it means doing a "safe save" on top
1400 of the existing file, in the same format - no UI needed
1401 unless the file has comments and the file's format doesn't
1404 If the file has comments, does the file's format support them?
1405 If not, ask the user whether they want to discard the comments
1406 or choose a different format. */
1407 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1410 /* The file can be saved in the specified format as is;
1411 just drive on and save in the format they selected. */
1412 discard_comments = FALSE;
1415 case SAVE_WITHOUT_COMMENTS:
1416 /* The file can't be saved in the specified format as is,
1417 but it can be saved without the comments, and the user
1418 said "OK, discard the comments", so save it in the
1419 format they specified without the comments. */
1420 discard_comments = TRUE;
1423 case SAVE_IN_ANOTHER_FORMAT:
1424 /* There are file formats in which we can save this that
1425 support comments, and the user said not to delete the
1426 comments. Do a "Save As" so the user can select
1427 one of those formats and choose a file name. */
1428 return saveAsCaptureFile(cf, TRUE, dont_reopen);
1431 /* The user said "forget it". Just return. */
1435 /* Squelch warnings that discard_comments is being used
1437 g_assert_not_reached();
1441 /* XXX - cf->filename might get freed out from under us, because
1442 the code path through which cf_save_records() goes currently
1443 closes the current file and then opens and reloads the saved file,
1444 so make a copy and free it later. */
1445 file_name = cf->filename;
1446 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1447 discard_comments, dont_reopen);
1451 /* The save succeeded; we're done.
1452 If we discarded comments, redraw the packet list to reflect
1453 any packets that no longer have comments. */
1454 if (discard_comments)
1455 packet_list_queue_draw();
1457 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1458 updateForUnsavedChanges(); // we update the title bar to remove the *
1461 case CF_WRITE_ERROR:
1462 /* The write failed.
1463 XXX - OK, what do we do now? Let them try a
1464 "Save As", in case they want to try to save to a
1465 different directory or file system? */
1468 case CF_WRITE_ABORTED:
1469 /* The write was aborted; just drive on. */
1473 /* Otherwise just do nothing. */
1479 bool MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1480 QString file_name = "";
1482 gboolean compressed;
1483 cf_write_status_t status;
1485 gboolean discard_comments = FALSE;
1492 CaptureFileDialog save_as_dlg(this, cf);
1494 /* If the file has comments, does the format the user selected
1495 support them? If not, ask the user whether they want to
1496 discard the comments or choose a different format. */
1497 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1500 /* The file can be saved in the specified format as is;
1501 just drive on and save in the format they selected. */
1502 discard_comments = FALSE;
1505 case SAVE_WITHOUT_COMMENTS:
1506 /* The file can't be saved in the specified format as is,
1507 but it can be saved without the comments, and the user
1508 said "OK, discard the comments", so save it in the
1509 format they specified without the comments. */
1510 discard_comments = TRUE;
1513 case SAVE_IN_ANOTHER_FORMAT:
1514 /* There are file formats in which we can save this that
1515 support comments, and the user said not to delete the
1516 comments. The combo box of file formats has had the
1517 formats that don't support comments trimmed from it,
1518 so run the dialog again, to let the user decide
1519 whether to save in one of those formats or give up. */
1520 must_support_comments = TRUE;
1524 /* The user said "forget it". Just get rid of the dialog box
1528 file_type = save_as_dlg.selectedFileType();
1529 compressed = save_as_dlg.isCompressed();
1531 fileAddExtension(file_name, file_type, compressed);
1534 // /* If the file exists and it's user-immutable or not writable,
1535 // ask the user whether they want to override that. */
1536 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1537 // /* They don't. Let them try another file name or cancel. */
1542 /* Attempt to save the file */
1543 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1544 discard_comments, dont_reopen);
1548 /* The save succeeded; we're done. */
1549 /* Save the directory name for future file dialogs. */
1550 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1551 set_last_open_dir(get_dirname(dirname));
1553 /* If we discarded comments, redraw the packet list to reflect
1554 any packets that no longer have comments. */
1555 if (discard_comments)
1556 packet_list_queue_draw();
1558 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1559 updateForUnsavedChanges(); // we update the title bar to remove the *
1560 /* Add this filename to the list of recent files in the "Recent Files" submenu */
1561 add_menu_recent_capture_file(file_name.toUtf8().constData());
1564 case CF_WRITE_ERROR:
1565 /* The save failed; let the user try again. */
1568 case CF_WRITE_ABORTED:
1569 /* The user aborted the save; just return. */
1576 void MainWindow::exportSelectedPackets() {
1577 QString file_name = "";
1579 gboolean compressed;
1580 packet_range_t range;
1581 cf_write_status_t status;
1583 gboolean discard_comments = FALSE;
1585 if (!capture_file_.capFile())
1588 /* Init the packet range */
1589 packet_range_init(&range, capture_file_.capFile());
1590 range.process_filtered = TRUE;
1591 range.include_dependents = TRUE;
1594 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1596 /* If the file has comments, does the format the user selected
1597 support them? If not, ask the user whether they want to
1598 discard the comments or choose a different format. */
1599 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1602 /* The file can be saved in the specified format as is;
1603 just drive on and save in the format they selected. */
1604 discard_comments = FALSE;
1607 case SAVE_WITHOUT_COMMENTS:
1608 /* The file can't be saved in the specified format as is,
1609 but it can be saved without the comments, and the user
1610 said "OK, discard the comments", so save it in the
1611 format they specified without the comments. */
1612 discard_comments = TRUE;
1615 case SAVE_IN_ANOTHER_FORMAT:
1616 /* There are file formats in which we can save this that
1617 support comments, and the user said not to delete the
1618 comments. The combo box of file formats has had the
1619 formats that don't support comments trimmed from it,
1620 so run the dialog again, to let the user decide
1621 whether to save in one of those formats or give up. */
1625 /* The user said "forget it". Just get rid of the dialog box
1631 * Check that we're not going to save on top of the current
1633 * We do it here so we catch all cases ...
1634 * Unfortunately, the file requester gives us an absolute file
1635 * name and the read file name may be relative (if supplied on
1636 * the command line). From Joerg Mayer.
1638 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1639 QMessageBox msg_box;
1640 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1642 msg_box.setIcon(QMessageBox::Critical);
1643 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1644 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1645 msg_box.setStandardButtons(QMessageBox::Ok);
1646 msg_box.setDefaultButton(QMessageBox::Ok);
1648 g_free(display_basename);
1652 file_type = esp_dlg.selectedFileType();
1653 compressed = esp_dlg.isCompressed();
1654 fileAddExtension(file_name, file_type, compressed);
1657 // /* If the file exists and it's user-immutable or not writable,
1658 // ask the user whether they want to override that. */
1659 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1660 // /* They don't. Let them try another file name or cancel. */
1665 /* Attempt to save the file */
1666 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1670 /* The save succeeded; we're done. */
1671 /* Save the directory name for future file dialogs. */
1672 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1673 set_last_open_dir(get_dirname(dirname));
1675 /* If we discarded comments, redraw the packet list to reflect
1676 any packets that no longer have comments. */
1677 if (discard_comments)
1678 packet_list_queue_draw();
1679 /* Add this filename to the list of recent files in the "Recent Files" submenu */
1680 add_menu_recent_capture_file(file_name.toUtf8().constData());
1683 case CF_WRITE_ERROR:
1684 /* The save failed; let the user try again. */
1687 case CF_WRITE_ABORTED:
1688 /* The user aborted the save; just return. */
1695 void MainWindow::exportDissections(export_type_e export_type) {
1696 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1697 packet_range_t range;
1699 if (!capture_file_.capFile())
1702 /* Init the packet range */
1703 packet_range_init(&range, capture_file_.capFile());
1704 range.process_filtered = TRUE;
1705 range.include_dependents = TRUE;
1710 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1711 QString file_name_lower;
1712 GSList *extensions_list;
1713 gboolean add_extension;
1716 * Append the default file extension if there's none given by
1717 * the user or if they gave one that's not one of the valid
1718 * extensions for the file type.
1720 file_name_lower = file_name.toLower();
1721 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1722 if (extensions_list != NULL) {
1725 /* We have one or more extensions for this file type.
1726 Start out assuming we need to add the default one. */
1727 add_extension = TRUE;
1729 /* OK, see if the file has one of those extensions. */
1730 for (extension = extensions_list; extension != NULL;
1731 extension = g_slist_next(extension)) {
1732 QString file_suffix = tr(".") + (char *)extension->data;
1733 if (file_name_lower.endsWith(file_suffix)) {
1735 * The file name has one of the extensions for
1738 add_extension = FALSE;
1741 file_suffix += ".gz";
1742 if (file_name_lower.endsWith(file_suffix)) {
1744 * The file name has one of the extensions for
1747 add_extension = FALSE;
1752 /* We have no extensions for this file type. Don't add one. */
1753 add_extension = FALSE;
1755 if (add_extension) {
1756 if (wtap_default_file_extension(file_type) != NULL) {
1757 file_name += tr(".") + wtap_default_file_extension(file_type);
1765 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1766 bool capture_in_progress = false;
1767 bool do_close_file = false;
1769 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1770 return true; /* Already closed, nothing to do */
1773 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1774 /* This is true if we're reading a capture file *or* if we're doing
1775 a live capture. If we're reading a capture file, the main loop
1776 is busy reading packets, and only accepting input from the
1777 progress dialog, so we can't get here, so this means we're
1779 capture_in_progress = true;
1783 if (prefs.gui_ask_unsaved) {
1784 if (cf_has_unsaved_data(capture_file_.capFile()) ||
1785 (capture_in_progress && capture_file_.capFile()->count > 0))
1787 QMessageBox msg_dialog;
1790 QPushButton *save_button;
1791 QPushButton *discard_button;
1793 msg_dialog.setIcon(QMessageBox::Question);
1794 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1796 /* This file has unsaved data or there's a capture in
1797 progress; ask the user whether to save the data. */
1798 if (capture_in_progress && context != Restart) {
1799 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1800 infotext = tr("Your captured packets will be lost if you don't save them.");
1801 } else if (capture_file_.capFile()->is_tempfile) {
1802 if (context == Reload) {
1803 // Reloading a tempfile will keep the packets, so this is not unsaved packets
1804 question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1805 infotext = tr("Your changes will be lost if you don't save them.");
1807 question = tr("Do you want to save the captured packets%1?").arg(before_what);
1808 infotext = tr("Your captured packets will be lost if you don't save them.");
1811 // No capture in progress and not a tempfile, so this is not unsaved packets
1812 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1813 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1814 infotext = tr("Your changes will be lost if you don't save them.");
1815 g_free(display_basename);
1818 msg_dialog.setText(question);
1819 msg_dialog.setInformativeText(infotext);
1821 // XXX Text comes from ui/gtk/stock_icons.[ch]
1822 // Note that the button roles differ from the GTK+ version.
1823 // Cancel = RejectRole
1824 // Save = AcceptRole
1825 // Don't Save = DestructiveRole
1826 msg_dialog.addButton(QMessageBox::Cancel);
1828 if (capture_in_progress) {
1829 QString save_button_text;
1830 if (context == Restart) {
1831 save_button_text = tr("Save before Continue");
1833 save_button_text = tr("Stop and Save");
1835 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1837 save_button = msg_dialog.addButton(QMessageBox::Save);
1839 msg_dialog.setDefaultButton(save_button);
1841 QString discard_button_text;
1842 if (capture_in_progress) {
1845 discard_button_text = tr("Stop and Quit &without Saving");
1848 discard_button_text = tr("Continue &without Saving");
1851 discard_button_text = tr("Stop and Continue &without Saving");
1857 discard_button_text = tr("Quit &without Saving");
1861 discard_button_text = tr("Continue &without Saving");
1865 discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1867 discard_button->setAutoDefault(false);
1868 discard_button->setFocus();
1871 /* According to the Qt doc:
1872 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1874 * Therefore we should use clickedButton() to determine which button was clicked. */
1876 if (msg_dialog.clickedButton() == save_button) {
1878 /* If there's a capture in progress, we have to stop the capture
1879 and then do the save. */
1880 if (capture_in_progress)
1883 /* Save the file and close it */
1884 if (saveCaptureFile(capture_file_.capFile(), true) == false)
1886 do_close_file = true;
1887 } else if(msg_dialog.clickedButton() == discard_button) {
1888 /* Just close the file, discarding changes */
1889 do_close_file = true;
1891 // cancelButton or some other unspecified button
1895 /* Unchanged file or capturing with no packets */
1896 do_close_file = true;
1899 /* User asked not to be bothered by those prompts, just close it.
1900 XXX - should that apply only to saving temporary files? */
1901 do_close_file = true;
1904 if (do_close_file) {
1906 /* If there's a capture in progress, we have to stop the capture
1907 and then do the close. */
1908 if (capture_in_progress)
1911 /* captureStop() will close the file if not having any packets */
1912 if (capture_file_.capFile() && context != Restart && context != Reload)
1913 // Don't really close if Restart or Reload
1914 cf_close(capture_file_.capFile());
1917 return true; /* File closed */
1920 void MainWindow::captureStop() {
1923 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1924 WiresharkApplication::processEvents();
1928 void MainWindow::initMainToolbarIcons()
1930 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1931 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1932 #if !defined(Q_OS_WIN)
1933 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1934 // The macOS HIG specifies 32-pixel icons but they're a little too
1936 icon_size = icon_size * 3 / 2;
1938 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1940 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1941 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1943 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1944 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1945 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1946 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1948 // Menu icons are disabled in main_window.ui for these items.
1949 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1950 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1951 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1952 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1954 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1955 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1956 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1957 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1958 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1959 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1960 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1961 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1962 #if defined(Q_OS_MAC)
1963 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1964 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1966 main_ui_->actionGoPreviousHistoryPacket->setIcon(StockIcon("go-previous"));
1967 main_ui_->actionGoNextHistoryPacket->setIcon(StockIcon("go-next"));
1968 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1970 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1972 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1973 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1974 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1975 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1976 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1977 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1978 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1981 void MainWindow::initShowHideMainWidgets()
1983 if (show_hide_actions_) {
1987 show_hide_actions_ = new QActionGroup(this);
1988 QMap<QAction *, QWidget *> shmw_actions;
1990 show_hide_actions_->setExclusive(false);
1991 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1992 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1993 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1994 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1995 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1996 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1997 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1999 foreach (QAction *shmwa, shmw_actions.keys()) {
2000 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
2001 show_hide_actions_->addAction(shmwa);
2002 showHideMainWidgets(shmwa);
2005 // Initial hide the Interface Toolbar submenu
2006 main_ui_->menuInterfaceToolbars->menuAction()->setVisible(false);
2008 /* Initially hide the additional toolbars menus */
2009 main_ui_->menuAdditionalToolbars->menuAction()->setVisible(false);
2011 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
2014 void MainWindow::initTimeDisplayFormatMenu()
2016 if (time_display_actions_) {
2020 time_display_actions_ = new QActionGroup(this);
2022 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
2023 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
2024 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
2025 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
2026 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
2027 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
2028 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
2029 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
2030 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
2031 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
2033 foreach (QAction* tda, td_actions.keys()) {
2034 tda->setData(qVariantFromValue(td_actions[tda]));
2035 time_display_actions_->addAction(tda);
2038 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
2041 void MainWindow::initTimePrecisionFormatMenu()
2043 if (time_precision_actions_) {
2047 time_precision_actions_ = new QActionGroup(this);
2049 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
2050 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
2051 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
2052 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
2053 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
2054 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
2055 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
2057 foreach (QAction* tpa, tp_actions.keys()) {
2058 tpa->setData(qVariantFromValue(tp_actions[tpa]));
2059 time_precision_actions_->addAction(tpa);
2062 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
2065 // Menu items which will be disabled when we freeze() and whose state will
2066 // be restored when we thaw(). Add to the list as needed.
2067 void MainWindow::initFreezeActions()
2069 QList<QAction *> freeze_actions = QList<QAction *>()
2070 << main_ui_->actionFileClose
2071 << main_ui_->actionViewReload
2072 << main_ui_->actionEditMarkPacket
2073 << main_ui_->actionEditMarkAllDisplayed
2074 << main_ui_->actionEditUnmarkAllDisplayed
2075 << main_ui_->actionEditIgnorePacket
2076 << main_ui_->actionEditIgnoreAllDisplayed
2077 << main_ui_->actionEditUnignoreAllDisplayed
2078 << main_ui_->actionEditSetTimeReference
2079 << main_ui_->actionEditUnsetAllTimeReferences;
2081 foreach (QAction *action, freeze_actions) {
2082 freeze_actions_ << QPair<QAction *, bool>(action, false);
2086 void MainWindow::initConversationMenus()
2090 QList<QAction *> cc_actions = QList<QAction *>()
2091 << main_ui_->actionViewColorizeConversation1 << main_ui_->actionViewColorizeConversation2
2092 << main_ui_->actionViewColorizeConversation3 << main_ui_->actionViewColorizeConversation4
2093 << main_ui_->actionViewColorizeConversation5 << main_ui_->actionViewColorizeConversation6
2094 << main_ui_->actionViewColorizeConversation7 << main_ui_->actionViewColorizeConversation8
2095 << main_ui_->actionViewColorizeConversation9 << main_ui_->actionViewColorizeConversation10;
2097 for (GList *conv_filter_list_entry = conv_filter_list; conv_filter_list_entry; conv_filter_list_entry = g_list_next(conv_filter_list_entry)) {
2099 conversation_filter_t* conv_filter = (conversation_filter_t *)conv_filter_list_entry->data;
2100 ConversationAction *conv_action = new ConversationAction(main_ui_->menuConversationFilter, conv_filter);
2101 main_ui_->menuConversationFilter->addAction(conv_action);
2103 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2104 connect(conv_action, SIGNAL(triggered()), this, SLOT(applyConversationFilter()));
2106 // Packet list context menu items
2107 packet_list_->conversationMenu()->addAction(conv_action);
2109 QMenu *submenu = packet_list_->colorizeMenu()->addMenu(conv_action->text());
2112 foreach (QAction *cc_action, cc_actions) {
2113 conv_action = new ConversationAction(submenu, conv_filter);
2114 conv_action->setText(cc_action->text());
2115 conv_action->setIcon(cc_action->icon());
2116 conv_action->setColorNumber(i++);
2117 submenu->addAction(conv_action);
2118 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2119 connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2122 conv_action = new ConversationAction(submenu, conv_filter);
2123 conv_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
2124 submenu->addAction(conv_action);
2125 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
2126 connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2128 // Proto tree conversation menu is filled in in ProtoTree::contextMenuEvent.
2129 // We should probably do that here.
2132 // Proto tree colorization items
2134 ColorizeAction *colorize_action;
2135 foreach (QAction *cc_action, cc_actions) {
2136 colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
2137 colorize_action->setText(cc_action->text());
2138 colorize_action->setIcon(cc_action->icon());
2139 colorize_action->setColorNumber(i++);
2140 proto_tree_->colorizeMenu()->addAction(colorize_action);
2141 connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
2142 connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2145 colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
2146 colorize_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
2147 proto_tree_->colorizeMenu()->addAction(colorize_action);
2148 connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
2149 connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
2152 gboolean MainWindow::addExportObjectsMenuItem(const void *, void *value, void *userdata)
2154 register_eo_t *eo = (register_eo_t*)value;
2155 MainWindow *window = (MainWindow*)userdata;
2157 ExportObjectAction *export_action = new ExportObjectAction(window->main_ui_->menuFileExportObjects, eo);
2158 window->main_ui_->menuFileExportObjects->addAction(export_action);
2160 //initially disable until a file is loaded (then file signals will take over)
2161 export_action->setEnabled(false);
2163 connect(&window->capture_file_, SIGNAL(captureFileOpened()), export_action, SLOT(captureFileOpened()));
2164 connect(&window->capture_file_, SIGNAL(captureFileClosed()), export_action, SLOT(captureFileClosed()));
2165 connect(export_action, SIGNAL(triggered()), window, SLOT(applyExportObject()));
2169 void MainWindow::initExportObjectsMenus()
2171 eo_iterate_tables(addExportObjectsMenuItem, this);
2175 void MainWindow::setTitlebarForCaptureFile()
2177 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
2178 if (capture_file_.capFile()->is_tempfile) {
2180 // For a temporary file, put the source of the data
2181 // in the window title, not whatever random pile
2182 // of characters is the last component of the path
2185 // XXX - on non-Mac platforms, put in the application
2188 setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
2191 // For a user file, set the full path; that way,
2192 // for macOS, it'll set the "proxy icon". Qt
2193 // handles extracting the last component.
2195 // Sadly, some UN*Xes don't necessarily use UTF-8
2196 // for their file names, so we have to map the
2197 // file path to UTF-8. If that fails, we're somewhat
2200 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
2205 if (utf8_filename) {
2206 QFileInfo fi(utf8_filename);
2207 setWSWindowTitle(QString("[*]%1").arg(fi.fileName()));
2208 setWindowFilePath(utf8_filename);
2209 g_free(utf8_filename);
2211 // So what the heck else can we do here?
2212 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
2215 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
2217 /* We have no capture file. */
2222 QString MainWindow::replaceWindowTitleVariables(QString title)
2224 title.replace ("%P", get_profile_name());
2225 title.replace ("%V", get_ws_vcs_version_info());
2230 void MainWindow::setWSWindowTitle(QString title)
2232 if (title.isEmpty()) {
2233 title = tr("The Wireshark Network Analyzer");
2236 if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
2237 QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
2238 title.prepend(QString("[%1] ").arg(custom_title));
2241 if (prefs.gui_window_title && prefs.gui_window_title[0]) {
2242 QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
2244 // On macOS we separate the titles with a unicode em dash
2245 title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
2247 title.append(QString(" [%1]").arg(custom_title));
2251 setWindowTitle(title);
2252 setWindowFilePath(NULL);
2255 void MainWindow::setTitlebarForCaptureInProgress()
2257 if (capture_file_.capFile()) {
2258 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
2260 /* We have no capture in progress. */
2267 /* Enable or disable menu items based on whether you have a capture file
2268 you've finished reading and, if you have one, whether it's been saved
2269 and whether it could be saved except by copying the raw packet data. */
2270 void MainWindow::setMenusForCaptureFile(bool force_disable)
2273 bool can_write = false;
2274 bool can_save = false;
2275 bool can_save_as = false;
2277 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
2278 /* We have no capture file or we're currently reading a file */
2281 /* We have a capture file. Can we write or save? */
2282 can_write = cf_can_write_with_wiretap(capture_file_.capFile());
2283 can_save = cf_can_save(capture_file_.capFile());
2284 can_save_as = cf_can_save_as(capture_file_.capFile());
2287 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(enable);
2288 main_ui_->actionFileMerge->setEnabled(can_write);
2289 main_ui_->actionFileClose->setEnabled(enable);
2290 main_ui_->actionFileSave->setEnabled(can_save);
2291 main_ui_->actionFileSaveAs->setEnabled(can_save_as);
2292 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(enable);
2294 * "Export Specified Packets..." should be available only if
2295 * we can write the file out in at least one format.
2297 main_ui_->actionFileExportPackets->setEnabled(can_write);
2299 main_ui_->actionFileExportAsCArrays->setEnabled(enable);
2300 main_ui_->actionFileExportAsCSV->setEnabled(enable);
2301 main_ui_->actionFileExportAsPDML->setEnabled(enable);
2302 main_ui_->actionFileExportAsPlainText->setEnabled(enable);
2303 main_ui_->actionFileExportAsPSML->setEnabled(enable);
2304 main_ui_->actionFileExportAsJSON->setEnabled(enable);
2306 main_ui_->actionFileExportPacketBytes->setEnabled(enable);
2307 main_ui_->actionFileExportPDU->setEnabled(enable);
2308 main_ui_->actionFileExportSSLSessionKeys->setEnabled(enable);
2310 foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2311 eo_action->setEnabled(enable);
2314 main_ui_->actionViewReload->setEnabled(enable);
2316 #ifdef HAVE_SOFTWARE_UPDATE
2317 // We might want to enable or disable automatic checks here as well.
2318 update_action_->setEnabled(!can_save);
2322 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
2323 /* Either a capture was started or stopped; in either case, it's not
2324 in the process of stopping, so allow quitting. */
2326 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
2327 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
2329 main_ui_->actionFileExportAsCArrays->setEnabled(capture_in_progress);
2330 main_ui_->actionFileExportAsCSV->setEnabled(capture_in_progress);
2331 main_ui_->actionFileExportAsPDML->setEnabled(capture_in_progress);
2332 main_ui_->actionFileExportAsPlainText->setEnabled(capture_in_progress);
2333 main_ui_->actionFileExportAsPSML->setEnabled(capture_in_progress);
2334 main_ui_->actionFileExportAsJSON->setEnabled(capture_in_progress);
2336 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
2337 main_ui_->actionFileExportPDU->setEnabled(!capture_in_progress);
2338 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
2340 foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2341 eo_action->setEnabled(capture_in_progress);
2344 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
2345 main_ui_->actionFileQuit->setEnabled(true);
2346 #ifdef HAVE_SOFTWARE_UPDATE
2347 // We might want to enable or disable automatic checks here as well.
2348 update_action_->setEnabled(!capture_in_progress);
2351 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
2353 // XXX Fix packet list heading menu sensitivity
2354 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2355 // !capture_in_progress);
2356 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2357 // !capture_in_progress);
2358 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2359 // !capture_in_progress);
2362 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
2363 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
2364 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
2365 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
2366 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
2367 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
2368 #endif /* HAVE_LIBPCAP */
2372 void MainWindow::setMenusForCaptureStopping() {
2373 main_ui_->actionFileQuit->setEnabled(false);
2374 #ifdef HAVE_SOFTWARE_UPDATE
2375 update_action_->setEnabled(false);
2377 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
2379 main_ui_->actionCaptureStart->setChecked(false);
2380 main_ui_->actionCaptureStop->setEnabled(false);
2381 main_ui_->actionCaptureRestart->setEnabled(false);
2382 #endif /* HAVE_LIBPCAP */
2385 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2387 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2389 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2390 // have_captured_packets);
2392 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2393 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2394 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2396 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2397 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2398 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2399 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2400 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2401 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2402 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2404 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2405 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2406 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2407 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2409 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2410 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2411 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2414 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2415 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2416 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2418 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2419 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2420 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2423 void MainWindow::setWindowIcon(const QIcon &icon) {
2424 wsApp->setWindowIcon(icon);
2425 QMainWindow::setWindowIcon(icon);
2428 void MainWindow::updateForUnsavedChanges() {
2429 setTitlebarForCaptureFile();
2430 setMenusForCaptureFile();
2433 void MainWindow::changeEvent(QEvent* event)
2437 switch (event->type())
2439 case QEvent::LanguageChange:
2440 main_ui_->retranslateUi(this);
2441 // make sure that the "Clear Menu" item is retranslated
2442 wsApp->emitAppSignal(WiresharkApplication::RecentCapturesChanged);
2444 case QEvent::LocaleChange:{
2445 QString locale = QLocale::system().name();
2446 locale.truncate(locale.lastIndexOf('_'));
2447 wsApp->loadLanguage(locale);
2450 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
2451 case QEvent::WindowStateChange:
2452 main_ui_->actionViewFullScreen->setChecked(this->isFullScreen());
2459 QMainWindow::changeEvent(event);
2462 void MainWindow::resizeEvent(QResizeEvent *event)
2464 df_combo_box_->setMinimumWidth(width() * 2 / 3); // Arbitrary
2465 QMainWindow::resizeEvent(event);
2468 /* Update main window items based on whether there's a capture in progress. */
2469 void MainWindow::setForCaptureInProgress(bool capture_in_progress, GArray *ifaces)
2471 setMenusForCaptureInProgress(capture_in_progress);
2473 wireless_frame_->setCaptureInProgress(capture_in_progress);
2476 packet_list_->setCaptureInProgress(capture_in_progress);
2477 packet_list_->setVerticalAutoScroll(capture_in_progress && main_ui_->actionGoAutoScroll->isChecked());
2479 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2483 QList<InterfaceToolbar *> toolbars = findChildren<InterfaceToolbar *>();
2484 foreach (InterfaceToolbar *toolbar, toolbars) {
2485 if (capture_in_progress && ifaces) {
2486 for (guint i = 0; i < ifaces->len; i++) {
2487 interface_options interface_opts = g_array_index(ifaces, interface_options, i);
2488 toolbar->startCapture(interface_opts.name, interface_opts.extcap_control_in, interface_opts.extcap_control_out);
2491 toolbar->stopCapture();
2499 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2500 << REGISTER_ANALYZE_GROUP_UNSORTED
2501 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2502 << REGISTER_STAT_GROUP_UNSORTED
2503 << REGISTER_STAT_GROUP_GENERIC
2504 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2505 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2506 << REGISTER_STAT_GROUP_RESPONSE_TIME
2507 << REGISTER_STAT_GROUP_TELEPHONY
2508 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2509 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2510 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2511 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2512 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2513 << REGISTER_TOOLS_GROUP_UNSORTED;
2515 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2517 foreach (QAction *action, actions) {
2518 switch (menu_group) {
2519 case REGISTER_ANALYZE_GROUP_UNSORTED:
2520 case REGISTER_STAT_GROUP_UNSORTED:
2521 main_ui_->menuStatistics->insertAction(
2522 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2525 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2526 main_ui_->menuServiceResponseTime->addAction(action);
2528 case REGISTER_STAT_GROUP_TELEPHONY:
2529 main_ui_->menuTelephony->addAction(action);
2531 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2532 main_ui_->menuANSI->addAction(action);
2534 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2535 main_ui_->menuGSM->addAction(action);
2537 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2538 main_ui_->menuLTE->addAction(action);
2540 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2541 main_ui_->menuMTP3->addAction(action);
2543 case REGISTER_TOOLS_GROUP_UNSORTED:
2545 // Allow the creation of submenus. Mimics the behavor of
2546 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2547 // and GtkUIManager.
2549 // For now we limit the insanity to the "Tools" menu.
2550 QStringList menu_path = action->text().split('/');
2551 QMenu *cur_menu = main_ui_->menuTools;
2552 while (menu_path.length() > 1) {
2553 QString menu_title = menu_path.takeFirst();
2554 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2555 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2557 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2558 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2561 submenu = cur_menu->addMenu(menu_title);
2562 submenu->setObjectName(menu_title.toLower());
2566 action->setText(menu_path.last());
2567 cur_menu->addAction(action);
2571 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2575 // Connect each action type to its corresponding slot. We to
2576 // distinguish various types of actions. Setting their objectName
2577 // seems to work OK.
2578 if (action->objectName() == TapParameterDialog::actionName()) {
2579 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2580 } else if (action->objectName() == FunnelStatistics::actionName()) {
2581 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2585 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2587 foreach (QAction *action, actions) {
2588 switch (menu_group) {
2589 case REGISTER_ANALYZE_GROUP_UNSORTED:
2590 case REGISTER_STAT_GROUP_UNSORTED:
2591 main_ui_->menuStatistics->removeAction(action);
2593 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2594 main_ui_->menuServiceResponseTime->removeAction(action);
2596 case REGISTER_STAT_GROUP_TELEPHONY:
2597 main_ui_->menuTelephony->removeAction(action);
2599 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2600 main_ui_->menuANSI->removeAction(action);
2602 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2603 main_ui_->menuGSM->removeAction(action);
2605 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2606 main_ui_->menuLTE->removeAction(action);
2608 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2609 main_ui_->menuMTP3->removeAction(action);
2611 case REGISTER_TOOLS_GROUP_UNSORTED:
2613 // Allow removal of submenus.
2614 // For now we limit the insanity to the "Tools" menu.
2615 QStringList menu_path = action->text().split('/');
2616 QMenu *cur_menu = main_ui_->menuTools;
2617 while (menu_path.length() > 1) {
2618 QString menu_title = menu_path.takeFirst();
2619 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2620 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2622 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2623 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2627 cur_menu->removeAction(action);
2631 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2637 void MainWindow::addDynamicMenus()
2640 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2641 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2642 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2643 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2644 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2645 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2647 // Fill in each menu
2648 foreach (register_stat_group_t menu_group, menu_groups) {
2649 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2650 addMenuActions(actions, menu_group);
2653 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2654 // We've added a placeholder in order to make sure some menus are visible.
2655 // Hide them as needed.
2656 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2657 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2659 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2660 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2662 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2663 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2665 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2666 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2670 void MainWindow::reloadDynamicMenus()
2672 foreach (register_stat_group_t menu_group, menu_groups) {
2673 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2674 removeMenuActions(actions, menu_group);
2676 actions = wsApp->addedMenuGroupItems(menu_group);
2677 addMenuActions(actions, menu_group);
2680 wsApp->clearAddedMenuGroupItems();
2681 wsApp->clearRemovedMenuGroupItems();
2684 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2686 QAction * itemAction = NULL;
2687 ext_menubar_t * item = NULL;
2688 GList * children = NULL;
2690 /* There must exists an xpath parent */
2691 g_assert(subMenu != NULL);
2693 /* If the depth counter exceeds, something must have gone wrong */
2694 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2696 children = menu->children;
2697 /* Iterate the child entries */
2698 while (children && children->data) {
2699 item = (ext_menubar_t *) children->data;
2701 if (item->type == EXT_MENUBAR_MENU) {
2702 /* Handle Submenu entry */
2703 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2704 } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2705 subMenu->addSeparator();
2706 } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2707 itemAction = subMenu->addAction(item->name);
2708 itemAction->setData(QVariant::fromValue((void *)item));
2709 itemAction->setText(item->label);
2710 connect(itemAction, SIGNAL(triggered()),
2711 this, SLOT(externalMenuItem_triggered()));
2715 children = g_list_next(children);
2719 QMenu * MainWindow::searchSubMenu(QString objectName)
2723 if (objectName.length() > 0) {
2724 QString searchName = QString("menu") + objectName;
2726 lst = main_ui_->menuBar->findChildren<QMenu*>();
2727 foreach (QMenu* m, lst) {
2728 if (QString::compare(m->objectName(), searchName) == 0)
2736 void MainWindow::addPluginIFStructures()
2738 GList *user_menu = ext_menubar_get_entries();
2740 while (user_menu && user_menu->data) {
2741 QMenu *subMenu = NULL;
2742 ext_menu_t *menu = (ext_menu_t *) user_menu->data;
2744 /* On this level only menu items should exist. Not doing an assert here,
2745 * as it could be an honest mistake */
2746 if (menu->type != EXT_MENUBAR_MENU) {
2747 user_menu = g_list_next(user_menu);
2751 /* Create main submenu and add it to the menubar */
2752 if (menu->parent_menu) {
2753 QMenu *sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2755 subMenu = sortUnderneath->addMenu(menu->label);
2759 subMenu = main_ui_->menuBar->addMenu(menu->label);
2761 /* This will generate the action structure for each menu. It is recursive,
2762 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2763 this->externalMenuHelper(menu, subMenu, 0);
2766 user_menu = g_list_next (user_menu);
2769 int cntToolbars = 0;
2771 QMenu *tbMenu = main_ui_->menuAdditionalToolbars;
2772 GList *if_toolbars = ext_toolbar_get_entries();
2773 while (if_toolbars && if_toolbars->data) {
2774 ext_toolbar_t *toolbar = (ext_toolbar_t*) if_toolbars->data;
2776 if (toolbar->type != EXT_TOOLBAR_BAR) {
2777 if_toolbars = g_list_next (if_toolbars);
2781 bool visible = g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, (GCompareFunc) strcmp) ? true : false;
2783 AdditionalToolBar *ifToolBar = AdditionalToolBar::create(this, toolbar);
2786 ifToolBar->setVisible(visible);
2788 QAction *iftbAction = new QAction(QString(toolbar->name), this);
2789 iftbAction->setToolTip(toolbar->tooltip);
2790 iftbAction->setEnabled(true);
2791 iftbAction->setCheckable(true);
2792 iftbAction->setChecked(visible);
2793 iftbAction->setToolTip(tr("Show or hide the toolbar"));
2794 iftbAction->setData(VariantPointer<ext_toolbar_t>::asQVariant(toolbar));
2796 QAction *before = 0;
2798 foreach (QAction *action, tbMenu->actions()) {
2799 /* Ensure we add the menu entries in sorted order */
2800 if (action->text().compare(toolbar->name, Qt::CaseInsensitive) > 0) {
2806 tbMenu->insertAction(before, iftbAction);
2808 addToolBar(Qt::TopToolBarArea, ifToolBar);
2809 insertToolBarBreak(ifToolBar);
2811 if (show_hide_actions_)
2812 show_hide_actions_->addAction(iftbAction);
2817 if_toolbars = g_list_next (if_toolbars);
2821 tbMenu->menuAction()->setVisible(true);
2824 void MainWindow::removeAdditionalToolbar(QString toolbarName)
2826 if (toolbarName.length() == 0)
2829 QList<QToolBar *> toolbars = findChildren<QToolBar *>();
2830 foreach(QToolBar *tb, toolbars) {
2831 AdditionalToolBar *ifToolBar = dynamic_cast<AdditionalToolBar *>(tb);
2833 if (ifToolBar && ifToolBar->menuName().compare(toolbarName)) {
2834 GList *entry = g_list_find_custom(recent.gui_additional_toolbars, ifToolBar->menuName().toStdString().c_str(), (GCompareFunc) strcmp);
2836 recent.gui_additional_toolbars = g_list_remove(recent.gui_additional_toolbars, entry->data);
2838 QList<QAction *> actions = main_ui_->menuAdditionalToolbars->actions();
2839 foreach(QAction *action, actions) {
2840 ext_toolbar_t *item = VariantPointer<ext_toolbar_t>::asPtr(action->data());
2841 if (item && ifToolBar->menuName().compare(item->name)) {
2842 if (show_hide_actions_)
2843 show_hide_actions_->removeAction(action);
2844 main_ui_->menuAdditionalToolbars->removeAction(action);
2860 * indent-tabs-mode: nil
2863 * ex: set shiftwidth=4 tabstop=8 expandtab:
2864 * :indentSize=4:tabSize=8:noTabs=true: