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/epan_dissect.h>
27 #include <wsutil/filesystem.h>
28 #include <wsutil/ws_version_info.h>
29 #include <epan/prefs.h>
30 #include <epan/stats_tree_priv.h>
31 #include <epan/plugin_if.h>
34 #include "ui/capture.h"
35 #include <capchild/capture_session.h>
38 #include "ui/alert_box.h"
40 #include "ui/capture_ui_utils.h"
42 #include "ui/capture_globals.h"
43 #include "ui/main_statusbar.h"
44 #include "ui/recent.h"
46 #include "ui/preference_utils.h"
48 #include "byte_view_tab.h"
49 #include "display_filter_edit.h"
50 #include "export_dissection_dialog.h"
51 #include "funnel_statistics.h"
52 #include "import_text_dialog.h"
53 #include "packet_list.h"
54 #include "proto_tree.h"
55 #include "simple_dialog.h"
56 #include "stock_icon.h"
57 #include "tap_parameter_dialog.h"
58 #include "wireless_frame.h"
59 #include "wireshark_application.h"
61 #include "qt_ui_utils.h"
64 #include <QActionGroup>
65 #include <QDesktopWidget>
67 #include <QMessageBox>
68 #include <QMetaObject>
71 #include <QToolButton>
72 #include <QTreeWidget>
75 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
76 #include <QtMacExtras/QMacNativeToolBar>
80 //menu_recent_file_write_all
82 // If we ever add support for multiple windows this will need to be replaced.
83 static MainWindow *gbl_cur_main_window_ = NULL;
85 void pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
87 gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
90 static void plugin_if_mainwindow_apply_filter(gconstpointer user_data)
92 if (!gbl_cur_main_window_ || !user_data)
95 GHashTable * data_set = (GHashTable *) user_data;
97 if (g_hash_table_lookup_extended(data_set, "filter_string", NULL, NULL)) {
98 QString filter((const char *)g_hash_table_lookup(data_set, "filter_string"));
99 gbl_cur_main_window_->filterPackets(filter);
103 static void plugin_if_mainwindow_preference(gconstpointer user_data)
105 if (!gbl_cur_main_window_ || !user_data)
108 GHashTable * data_set = (GHashTable *) user_data;
109 const char * module_name;
110 const char * pref_name;
111 const char * pref_value;
113 if (g_hash_table_lookup_extended(data_set, "pref_module", NULL, (void**)&module_name) &&
114 g_hash_table_lookup_extended(data_set, "pref_key", NULL, (void**)&pref_name) &&
115 g_hash_table_lookup_extended(data_set, "pref_value", NULL, (void**)&pref_value))
117 if (prefs_store_ext(module_name, pref_name, pref_value)) {
118 wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
119 wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
124 static void plugin_if_mainwindow_gotoframe(gconstpointer user_data)
126 if (!gbl_cur_main_window_ || !user_data)
129 GHashTable * data_set = (GHashTable *) user_data;
132 if (g_hash_table_lookup_extended(data_set, "frame_nr", NULL, &framenr)) {
133 if (GPOINTER_TO_UINT(framenr) != 0)
134 gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr));
140 static void plugin_if_mainwindow_get_ws_info(gconstpointer user_data)
142 if (!gbl_cur_main_window_ || !user_data)
145 GHashTable * data_set = (GHashTable *)user_data;
146 ws_info_t *ws_info = NULL;
148 if (!g_hash_table_lookup_extended(data_set, "ws_info", NULL, (void**)&ws_info))
151 CaptureFile *cfWrap = gbl_cur_main_window_->captureFile();
152 capture_file *cf = cfWrap->capFile();
154 ws_info->ws_info_supported = true;
157 ws_info->cf_state = cf->state;
158 ws_info->cf_count = cf->count;
160 g_free(ws_info->cf_filename);
161 ws_info->cf_filename = g_strdup(cf->filename);
163 if (cf->state == FILE_READ_DONE) {
164 ws_info->cf_framenr = cf->current_frame->num;
165 ws_info->frame_passed_dfilter = (cf->current_frame->flags.passed_dfilter == 1);
167 ws_info->cf_framenr = 0;
168 ws_info->frame_passed_dfilter = FALSE;
170 } else if (ws_info->cf_state != FILE_CLOSED) {
171 /* Initialise the ws_info structure */
172 ws_info->cf_count = 0;
174 g_free(ws_info->cf_filename);
175 ws_info->cf_filename = NULL;
177 ws_info->cf_framenr = 0;
178 ws_info->frame_passed_dfilter = FALSE;
179 ws_info->cf_state = FILE_CLOSED;
183 #endif /* HAVE_LIBPCAP */
186 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
190 va_start(ap, msg_format);
191 SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
199 * Alert box, with optional "don't show this message again" variable
200 * and checkbox, and optional secondary text.
203 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
204 const char *secondary_msg, const char *msg_format, ...)
206 if (notagain && *notagain) {
212 va_start(ap, msg_format);
213 SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
216 sd.setDetailedText(secondary_msg);
218 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
219 QCheckBox *cb = NULL;
221 cb = new QCheckBox();
222 cb->setChecked(true);
223 cb->setText(QObject::tr("Don't show this message again."));
230 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
231 if (notagain && cb) {
232 *notagain = cb->isChecked();
238 * Error alert box, taking a format and a va_list argument.
241 vsimple_error_message_box(const char *msg_format, va_list ap)
243 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
248 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
249 QList<QAction *> actions = parent_menu->actions();
250 QList<QAction *>::const_iterator i;
251 for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
252 if ((*i)->text()==menu_text) {
256 // If we get here there menu entry was not found, add a sub menu
257 return parent_menu->addMenu(menu_text);
260 MainWindow::MainWindow(QWidget *parent) :
262 main_ui_(new Ui::MainWindow),
263 cur_layout_(QVector<unsigned>()),
264 df_combo_box_(new DisplayFilterCombo()),
267 previous_focus_(NULL),
268 show_hide_actions_(NULL),
269 time_display_actions_(NULL),
270 time_precision_actions_(NULL),
271 funnel_statistics_(new FunnelStatistics(this, capture_file_)),
273 capture_stopping_(false),
274 capture_filter_valid_(false),
284 if (!gbl_cur_main_window_) {
285 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
286 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
287 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
288 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
290 gbl_cur_main_window_ = this;
292 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
294 main_ui_->setupUi(this);
295 setWindowIcon(wsApp->normalIcon());
296 setTitlebarForCaptureFile();
297 setMenusForCaptureFile();
298 setForCapturedPackets(false);
299 setMenusForFileSet(false);
300 interfaceSelectionChanged();
301 loadWindowGeometry();
304 main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
307 //To prevent users use features before initialization complete
308 //Otherwise unexpected problems may occur
309 setFeaturesEnabled(false);
310 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
311 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
312 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
313 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
314 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
315 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
317 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
318 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
319 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
320 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
321 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
322 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
324 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
328 connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
329 connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
332 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
333 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
334 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
335 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
336 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
337 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
338 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
339 connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
340 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
341 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
343 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
344 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
345 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
346 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
347 this, SLOT(openCaptureFile(QString,QString)));
348 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
350 initMainToolbarIcons();
352 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
353 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
354 // https://bugreports.qt-project.org/browse/QTBUG-22433
355 // This property is obsolete in Qt5 so this issue may be fixed in that version.
356 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
358 wireless_frame_ = new WirelessFrame(this);
359 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
360 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
361 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
362 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
363 this, SLOT(showPreferencesDialog(QString)));
365 main_ui_->goToFrame->hide();
366 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
367 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
369 // XXX For some reason the cursor is drawn funny with an input mask set
370 // https://bugreports.qt-project.org/browse/QTBUG-7174
372 main_ui_->searchFrame->hide();
373 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
374 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
375 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
376 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
378 main_ui_->addressEditorFrame->hide();
379 main_ui_->columnEditorFrame->hide();
380 main_ui_->preferenceEditorFrame->hide();
381 main_ui_->filterExpressionFrame->hide();
384 main_ui_->menuCapture->setEnabled(false);
387 #if defined(Q_OS_MAC)
388 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
389 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
390 ntb->setIconSize(QSize(24, 24));
391 #endif // QT_MACEXTRAS_LIB
393 main_ui_->goToPacketLabel->setAttribute(Qt::WA_MacSmallSize, true);
394 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
395 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
396 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
398 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
402 #ifdef HAVE_SOFTWARE_UPDATE
403 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
404 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
405 main_ui_->menuHelp->insertAction(update_sep, update_action);
406 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
408 master_split_.setObjectName("splitterMaster");
409 extra_split_.setObjectName("splitterExtra");
410 main_ui_->mainStack->addWidget(&master_split_);
412 empty_pane_.setObjectName("emptyPane");
414 packet_list_ = new PacketList(&master_split_);
416 proto_tree_ = new ProtoTree(&master_split_);
417 proto_tree_->installEventFilter(this);
419 byte_view_tab_ = new ByteViewTab(&master_split_);
421 packet_list_->setProtoTree(proto_tree_);
422 packet_list_->setByteViewTab(byte_view_tab_);
423 packet_list_->installEventFilter(this);
425 main_welcome_ = main_ui_->welcomePage;
427 // Packet list and proto tree must exist before these are called.
428 setMenusForSelectedPacket();
429 setMenusForSelectedTreeRow();
431 initShowHideMainWidgets();
432 initTimeDisplayFormatMenu();
433 initTimePrecisionFormatMenu();
435 updatePreferenceActions();
436 updateRecentActions();
437 setForCaptureInProgress(false);
439 setTabOrder(df_combo_box_, packet_list_);
441 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
442 this, SLOT(captureCapturePrepared(capture_session *)));
443 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
444 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
445 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
446 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
447 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
448 this, SLOT(captureCaptureFixedStarted(capture_session *)));
449 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
450 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
451 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
452 this, SLOT(captureCaptureFixedFinished(capture_session *)));
453 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
454 this, SLOT(captureCaptureStopping(capture_session *)));
455 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
456 this, SLOT(captureCaptureFailed(capture_session *)));
457 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
458 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
460 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
461 wsApp, SLOT(captureStarted()));
462 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
463 wsApp, SLOT(captureFinished()));
464 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
465 wsApp, SLOT(captureStarted()));
466 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
467 wsApp, SLOT(captureFinished()));
469 connect(&capture_file_, SIGNAL(captureFileOpened()),
470 this, SLOT(captureFileOpened()));
471 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
472 this, SLOT(captureFileReadStarted()));
473 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
474 this, SLOT(captureFileReadFinished()));
475 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
476 this, SLOT(captureFileReloadStarted()));
477 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
478 this, SLOT(captureFileReadFinished()));
479 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
480 this, SLOT(captureFileRescanStarted()));
481 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
482 this, SLOT(captureFileReadFinished()));
483 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
484 this, SLOT(captureFileRetapStarted()));
485 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
486 this, SLOT(captureFileRetapFinished()));
487 connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
488 this, SLOT(captureFileFlushTapsData()));
489 connect(&capture_file_, SIGNAL(captureFileClosing()),
490 this, SLOT(captureFileClosing()));
491 connect(&capture_file_, SIGNAL(captureFileClosed()),
492 this, SLOT(captureFileClosed()));
494 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
495 this, SLOT(captureFileSaveStarted(QString)));
496 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
497 main_ui_->statusBar, SLOT(popFileStatus()));
498 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
499 main_ui_->statusBar, SLOT(popFileStatus()));
500 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
501 main_ui_->statusBar, SLOT(popFileStatus()));
503 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
504 wsApp, SLOT(captureFileReadStarted()));
505 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
506 wsApp, SLOT(updateTaps()));
508 connect(wsApp, SIGNAL(columnsChanged()),
509 packet_list_, SLOT(columnsChanged()));
510 connect(wsApp, SIGNAL(preferencesChanged()),
511 packet_list_, SLOT(preferencesChanged()));
512 connect(wsApp, SIGNAL(recentFilesRead()),
513 this, SLOT(applyRecentPaneGeometry()));
514 connect(wsApp, SIGNAL(recentFilesRead()),
515 this, SLOT(updateRecentActions()));
516 connect(wsApp, SIGNAL(packetDissectionChanged()),
517 this, SLOT(redissectPackets()), Qt::QueuedConnection);
518 connect(wsApp, SIGNAL(appInitialized()),
519 this, SLOT(filterExpressionsChanged()));
520 connect(wsApp, SIGNAL(filterExpressionsChanged()),
521 this, SLOT(filterExpressionsChanged()));
522 connect(wsApp, SIGNAL(checkDisplayFilter()),
523 this, SLOT(checkDisplayFilter()));
524 connect(wsApp, SIGNAL(fieldsChanged()),
525 this, SLOT(fieldsChanged()));
526 connect(wsApp, SIGNAL(reloadLuaPlugins()),
527 this, SLOT(reloadLuaPlugins()));
529 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
530 this, SLOT(mainStackChanged(int)));
532 connect(main_welcome_, SIGNAL(startCapture()),
533 this, SLOT(startCapture()));
534 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
535 this, SLOT(openCaptureFile(QString)));
536 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
537 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
538 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
539 main_ui_->statusBar, SLOT(popFilterStatus()));
541 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
542 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
543 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
544 this, SLOT(redissectPackets()));
545 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
546 this, SLOT(showPreferencesDialog(QString)));
547 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
548 this, SLOT(showPreferencesDialog(QString)));
549 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
550 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
551 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
552 this, SLOT(filterExpressionsChanged()));
554 connect(this, SIGNAL(setCaptureFile(capture_file*)),
555 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
556 connect(this, SIGNAL(setCaptureFile(capture_file*)),
557 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
558 connect(this, SIGNAL(setCaptureFile(capture_file*)),
559 packet_list_, SLOT(setCaptureFile(capture_file*)));
560 connect(this, SIGNAL(setCaptureFile(capture_file*)),
561 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
563 connect(this, SIGNAL(monospaceFontChanged(QFont)),
564 packet_list_, SLOT(setMonospaceFont(QFont)));
565 connect(this, SIGNAL(monospaceFontChanged(QFont)),
566 proto_tree_, SLOT(setMonospaceFont(QFont)));
567 connect(this, SIGNAL(monospaceFontChanged(QFont)),
568 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
570 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
571 packet_list_, SLOT(goNextPacket()));
572 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
573 packet_list_, SLOT(goPreviousPacket()));
574 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
575 packet_list_, SLOT(goFirstPacket()));
576 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
577 packet_list_, SLOT(goLastPacket()));
579 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
580 proto_tree_, SLOT(expandSubtrees()));
581 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
582 proto_tree_, SLOT(expandAll()));
583 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
584 proto_tree_, SLOT(collapseAll()));
586 connect(packet_list_, SIGNAL(packetSelectionChanged()),
587 this, SLOT(setMenusForSelectedPacket()));
588 connect(packet_list_, SIGNAL(packetDissectionChanged()),
589 this, SLOT(redissectPackets()));
590 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
591 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
592 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
593 this, SLOT(showPreferencesDialog(QString)));
594 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
595 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
596 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
597 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
598 packet_list_, SLOT(columnsChanged()));
599 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
600 this, SLOT(openPacketDialog()));
601 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
602 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
603 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
604 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
605 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
606 main_ui_->statusBar, SLOT(popBusyStatus()));
607 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
608 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
609 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
610 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
611 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
612 main_ui_->statusBar, SLOT(popProgressStatus()));
614 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
615 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
616 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
617 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
618 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
619 this, SLOT(openPacketDialog(bool)));
620 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
621 this, SLOT(showPreferencesDialog(QString)));
622 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
623 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
625 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
626 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
628 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
629 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
631 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
632 &capture_file_, SLOT(stopLoading()));
634 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
635 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
637 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
638 this, SLOT(openCaptureFile(QString)));
641 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
643 connect(iface_tree, SIGNAL(itemSelectionChanged()),
644 this, SLOT(interfaceSelectionChanged()));
646 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
647 this, SLOT(captureFilterSyntaxChanged(bool)));
650 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
651 this, SLOT(showExtcapOptionsDialog(QString&)));
654 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
655 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
656 // Changes in interface selections or capture filters should be propagated
657 // to the main welcome screen where they will be applied to the global
659 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
660 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
661 connect(&capture_interfaces_dialog_, SIGNAL(interfacesChanged()),
662 this->main_welcome_, SLOT(interfaceSelected()));
663 connect(&capture_interfaces_dialog_, SIGNAL(interfacesChanged()),
664 this->main_welcome_->getInterfaceTree(), SLOT(updateSelectedInterfaces()));
665 connect(&capture_interfaces_dialog_, SIGNAL(interfacesChanged()),
666 this->main_welcome_->getInterfaceTree(), SLOT(updateToolTips()));
667 connect(&capture_interfaces_dialog_, SIGNAL(captureFilterTextEdited(QString)),
668 this->main_welcome_, SLOT(setCaptureFilterText(QString)));
671 /* Create plugin_if hooks */
672 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
673 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
674 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
675 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
677 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
680 main_ui_->mainStack->setCurrentWidget(main_welcome_);
683 MainWindow::~MainWindow()
688 QString MainWindow::getFilter()
690 return df_combo_box_->currentText();
693 QMenu *MainWindow::createPopupMenu()
695 QMenu *menu = new QMenu();
696 menu->addAction(main_ui_->actionViewMainToolbar);
697 menu->addAction(main_ui_->actionViewFilterToolbar);
698 menu->addAction(main_ui_->actionViewWirelessToolbar);
699 menu->addAction(main_ui_->actionViewStatusBar);
700 menu->addSeparator();
701 menu->addAction(main_ui_->actionViewPacketList);
702 menu->addAction(main_ui_->actionViewPacketDetails);
703 menu->addAction(main_ui_->actionViewPacketBytes);
707 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
709 pipe_source_ = source;
710 pipe_child_process_ = child_process;
711 pipe_user_data_ = user_data;
712 pipe_input_cb_ = input_cb;
715 /* Tricky to use pipes in win9x, as no concept of wait. NT can
716 do this but that doesn't cover all win32 platforms. GTK can do
717 this but doesn't seem to work over processes. Attempt to do
718 something similar here, start a timer and check for data on every
720 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
723 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
727 pipe_timer_ = new QTimer(this);
728 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
729 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
730 pipe_timer_->start(200);
732 if (pipe_notifier_) {
733 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
734 delete pipe_notifier_;
737 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
738 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
739 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
740 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
744 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
746 // The user typed some text. Start filling in a filter.
747 // We may need to be more choosy here. We just need to catch events for the packet list,
748 // proto tree, and main welcome widgets.
749 if (event->type() == QEvent::KeyPress) {
750 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
751 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
752 df_combo_box_->lineEdit()->insert(kevt->text());
753 df_combo_box_->lineEdit()->setFocus();
758 return QMainWindow::eventFilter(obj, event);
761 void MainWindow::keyPressEvent(QKeyEvent *event) {
763 // Explicitly focus on the display filter combo.
764 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
765 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
769 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
770 if (event->modifiers() == Qt::NoModifier) {
771 if (event->key() == Qt::Key_Escape) {
772 on_goToCancel_clicked();
773 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
777 return; // goToLineEdit didn't want it and we don't either.
780 // Move up & down the packet list.
781 if (event->key() == Qt::Key_F7) {
782 packet_list_->goPreviousPacket();
783 } else if (event->key() == Qt::Key_F8) {
784 packet_list_->goNextPacket();
787 // Move along, citizen.
788 QMainWindow::keyPressEvent(event);
791 void MainWindow::closeEvent(QCloseEvent *event) {
792 saveWindowGeometry();
794 /* If we're in the middle of stopping a capture, don't do anything;
795 the user can try deleting the window after the capture stops. */
796 if (capture_stopping_) {
801 QString before_what(tr(" before quitting"));
802 if (!testCaptureFileClose(before_what, Quit)) {
808 capture_interfaces_dialog_.close();
810 // Make sure we kill any open dumpcap processes.
811 delete main_welcome_;
813 // One of the many places we assume one main window.
814 if(!wsApp->isInitialized()) {
815 // If we're still initializing, QCoreApplication::quit() won't
816 // exit properly because we are not in the event loop. This
817 // means that the application won't clean up after itself. We
818 // might want to call wsApp->processEvents() during startup
819 // instead so that we can do a normal exit here.
825 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
828 foreach (QUrl drag_url, event->mimeData()->urls()) {
829 if (!drag_url.toLocalFile().isEmpty()) {
834 if (accept) event->acceptProposedAction();
837 void MainWindow::dropEvent(QDropEvent *event)
839 foreach (QUrl drop_url, event->mimeData()->urls()) {
840 QString local_file = drop_url.toLocalFile();
841 if (!local_file.isEmpty()) {
842 event->acceptProposedAction();
843 openCaptureFile(local_file);
849 // Apply recent settings to the main window geometry.
850 // We haven't loaded the preferences at this point so we assume that the
851 // position and size preference are enabled.
852 void MainWindow::loadWindowGeometry()
854 int min_sensible_dimension = 200;
857 if (recent.gui_geometry_main_maximized) {
858 setWindowState(Qt::WindowMaximized);
862 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
863 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
864 if (!rect_on_screen(recent_geom)) {
865 // We're not visible on any screens. Give up and use the default geometry.
869 // if (prefs.gui_geometry_save_position) {
870 move(recent_geom.topLeft());
873 if (// prefs.gui_geometry_save_size &&
874 recent_geom.width() > min_sensible_dimension &&
875 recent_geom.height() > min_sensible_dimension) {
876 resize(recent_geom.size());
881 void MainWindow::saveWindowGeometry()
883 if (prefs.gui_geometry_save_position) {
884 recent.gui_geometry_main_x = pos().x();
885 recent.gui_geometry_main_y = pos().y();
888 if (prefs.gui_geometry_save_size) {
889 recent.gui_geometry_main_width = size().width();
890 recent.gui_geometry_main_height = size().height();
893 if (prefs.gui_geometry_save_maximized) {
894 // On OS X this is false when it shouldn't be
895 recent.gui_geometry_main_maximized = isMaximized();
898 if (master_split_.sizes().length() > 0) {
899 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
902 if (master_split_.sizes().length() > 2) {
903 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
904 } else if (extra_split_.sizes().length() > 0) {
905 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
909 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
911 case layout_pane_content_none:
913 case layout_pane_content_plist:
915 case layout_pane_content_pdetails:
917 case layout_pane_content_pbytes:
918 return byte_view_tab_;
920 g_assert_not_reached();
925 // Our event loop becomes nested whenever we call update_progress_dlg, which
926 // includes several places in file.c. The GTK+ UI stays out of trouble by
927 // showing a modal progress dialog. We attempt to do the equivalent below by
928 // disabling parts of the main window. At a minumum the ProgressFrame in the
929 // main status bar must remain accessible.
931 // We might want to do this any time the main status bar progress frame is
933 void MainWindow::freeze()
935 freeze_focus_ = wsApp->focusWidget();
937 // XXX Alternatively we could just disable and enable the main menu.
938 for (int i = 0; i < freeze_actions_.size(); i++) {
939 QAction *action = freeze_actions_[i].first;
940 freeze_actions_[i].second = action->isEnabled();
941 action->setEnabled(false);
943 main_ui_->centralWidget->setEnabled(false);
946 void MainWindow::thaw()
948 main_ui_->centralWidget->setEnabled(true);
949 for (int i = 0; i < freeze_actions_.size(); i++) {
950 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
953 if (freeze_focus_) freeze_focus_->setFocus();
954 freeze_focus_ = NULL;
957 void MainWindow::mergeCaptureFile()
959 QString file_name = "";
960 QString read_filter = "";
961 dfilter_t *rfcode = NULL;
964 if (!capture_file_.capFile())
967 if (prefs.gui_ask_unsaved) {
968 if (cf_has_unsaved_data(capture_file_.capFile())) {
969 QMessageBox msg_dialog;
970 gchar *display_basename;
973 msg_dialog.setIcon(QMessageBox::Question);
974 /* This file has unsaved data; ask the user whether to save
976 if (capture_file_.capFile()->is_tempfile) {
977 msg_dialog.setText(tr("Save packets before merging?"));
978 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
981 * Format the message.
983 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
984 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
985 g_free(display_basename);
986 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
989 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
990 msg_dialog.setDefaultButton(QMessageBox::Save);
992 response = msg_dialog.exec();
996 case QMessageBox::Save:
997 /* Save the file but don't close it */
998 saveCaptureFile(capture_file_.capFile(), FALSE);
1001 case QMessageBox::Cancel:
1003 /* Don't do the merge. */
1010 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1012 cf_status_t merge_status;
1013 char *in_filenames[2];
1016 if (merge_dlg.merge(file_name)) {
1019 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1020 /* Not valid. Tell the user, and go back and run the file
1021 selection box again once they dismiss the alert. */
1022 //bad_dfilter_alert_box(top_level, read_filter->str);
1023 QMessageBox::warning(this, tr("Invalid Read Filter"),
1024 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1033 file_type = capture_file_.capFile()->cd_t;
1035 /* Try to merge or append the two files */
1037 if (merge_dlg.mergeType() == 0) {
1038 /* chronological order */
1039 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1040 in_filenames[1] = qstring_strdup(file_name);
1041 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
1042 } else if (merge_dlg.mergeType() <= 0) {
1044 in_filenames[0] = qstring_strdup(file_name);
1045 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1046 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1049 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1050 in_filenames[1] = qstring_strdup(file_name);
1051 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1054 g_free(in_filenames[0]);
1055 g_free(in_filenames[1]);
1057 if (merge_status != CF_OK) {
1059 dfilter_free(rfcode);
1064 cf_close(capture_file_.capFile());
1066 /* Try to open the merged capture file. */
1067 CaptureFile::globalCapFile()->window = this;
1068 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1069 /* We couldn't open it; fail. */
1070 CaptureFile::globalCapFile()->window = NULL;
1072 dfilter_free(rfcode);
1077 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1078 it closed the previous capture file, and thus destroyed any
1079 previous read filter attached to "cf"). */
1080 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1082 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1086 /* Just because we got an error, that doesn't mean we were unable
1087 to read any of the file; we handle what we could get from the
1091 case CF_READ_ABORTED:
1092 /* The user bailed out of re-reading the capture file; the
1093 capture file has been closed - just free the capture file name
1094 string and return (without changing the last containing
1100 /* Save the name of the containing directory specified in the path name,
1101 if any; we can write over cf_merged_name, which is a good thing, given that
1102 "get_dirname()" does write over its argument. */
1103 wsApp->setLastOpenDir(get_dirname(tmpname));
1105 main_ui_->statusBar->showExpert();
1111 void MainWindow::importCaptureFile() {
1112 ImportTextDialog import_dlg;
1114 QString before_what(tr(" before importing a capture"));
1115 if (!testCaptureFileClose(before_what))
1120 if (import_dlg.result() != QDialog::Accepted) {
1121 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1125 openCaptureFile(import_dlg.capfileName());
1128 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1130 gboolean discard_comments;
1132 if (cf->is_tempfile) {
1133 /* This is a temporary capture file, so saving it means saving
1134 it to a permanent file. Prompt the user for a location
1135 to which to save it. Don't require that the file format
1136 support comments - if it's a temporary capture file, it's
1137 probably pcap-ng, which supports comments and, if it's
1138 not pcap-ng, let the user decide what they want to do
1139 if they've added comments. */
1140 saveAsCaptureFile(cf, FALSE, dont_reopen);
1142 if (cf->unsaved_changes) {
1143 cf_write_status_t status;
1145 /* This is not a temporary capture file, but it has unsaved
1146 changes, so saving it means doing a "safe save" on top
1147 of the existing file, in the same format - no UI needed
1148 unless the file has comments and the file's format doesn't
1151 If the file has comments, does the file's format support them?
1152 If not, ask the user whether they want to discard the comments
1153 or choose a different format. */
1154 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1157 /* The file can be saved in the specified format as is;
1158 just drive on and save in the format they selected. */
1159 discard_comments = FALSE;
1162 case SAVE_WITHOUT_COMMENTS:
1163 /* The file can't be saved in the specified format as is,
1164 but it can be saved without the comments, and the user
1165 said "OK, discard the comments", so save it in the
1166 format they specified without the comments. */
1167 discard_comments = TRUE;
1170 case SAVE_IN_ANOTHER_FORMAT:
1171 /* There are file formats in which we can save this that
1172 support comments, and the user said not to delete the
1173 comments. Do a "Save As" so the user can select
1174 one of those formats and choose a file name. */
1175 saveAsCaptureFile(cf, TRUE, dont_reopen);
1179 /* The user said "forget it". Just return. */
1183 /* Squelch warnings that discard_comments is being used
1185 g_assert_not_reached();
1189 /* XXX - cf->filename might get freed out from under us, because
1190 the code path through which cf_save_records() goes currently
1191 closes the current file and then opens and reloads the saved file,
1192 so make a copy and free it later. */
1193 file_name = cf->filename;
1194 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1195 discard_comments, dont_reopen);
1199 /* The save succeeded; we're done.
1200 If we discarded comments, redraw the packet list to reflect
1201 any packets that no longer have comments. */
1202 if (discard_comments)
1203 packet_list_queue_draw();
1205 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1206 updateForUnsavedChanges(); // we update the title bar to remove the *
1209 case CF_WRITE_ERROR:
1210 /* The write failed.
1211 XXX - OK, what do we do now? Let them try a
1212 "Save As", in case they want to try to save to a
1213 different directory r file system? */
1216 case CF_WRITE_ABORTED:
1217 /* The write was aborted; just drive on. */
1221 /* Otherwise just do nothing. */
1225 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1226 QString file_name = "";
1228 gboolean compressed;
1229 cf_write_status_t status;
1231 gboolean discard_comments = FALSE;
1238 CaptureFileDialog save_as_dlg(this, cf);
1240 /* If the file has comments, does the format the user selected
1241 support them? If not, ask the user whether they want to
1242 discard the comments or choose a different format. */
1243 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1246 /* The file can be saved in the specified format as is;
1247 just drive on and save in the format they selected. */
1248 discard_comments = FALSE;
1251 case SAVE_WITHOUT_COMMENTS:
1252 /* The file can't be saved in the specified format as is,
1253 but it can be saved without the comments, and the user
1254 said "OK, discard the comments", so save it in the
1255 format they specified without the comments. */
1256 discard_comments = TRUE;
1259 case SAVE_IN_ANOTHER_FORMAT:
1260 /* There are file formats in which we can save this that
1261 support comments, and the user said not to delete the
1262 comments. The combo box of file formats has had the
1263 formats that don't support comments trimmed from it,
1264 so run the dialog again, to let the user decide
1265 whether to save in one of those formats or give up. */
1266 must_support_comments = TRUE;
1270 /* The user said "forget it". Just get rid of the dialog box
1274 file_type = save_as_dlg.selectedFileType();
1275 compressed = save_as_dlg.isCompressed();
1277 fileAddExtension(file_name, file_type, compressed);
1280 // /* If the file exists and it's user-immutable or not writable,
1281 // ask the user whether they want to override that. */
1282 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1283 // /* They don't. Let them try another file name or cancel. */
1288 /* Attempt to save the file */
1289 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1290 discard_comments, dont_reopen);
1294 /* The save succeeded; we're done. */
1295 /* Save the directory name for future file dialogs. */
1296 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1297 set_last_open_dir(get_dirname(dirname));
1299 /* If we discarded comments, redraw the packet list to reflect
1300 any packets that no longer have comments. */
1301 if (discard_comments)
1302 packet_list_queue_draw();
1304 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1305 updateForUnsavedChanges(); // we update the title bar to remove the *
1308 case CF_WRITE_ERROR:
1309 /* The save failed; let the user try again. */
1312 case CF_WRITE_ABORTED:
1313 /* The user aborted the save; just return. */
1320 void MainWindow::exportSelectedPackets() {
1321 QString file_name = "";
1323 gboolean compressed;
1324 packet_range_t range;
1325 cf_write_status_t status;
1327 gboolean discard_comments = FALSE;
1329 if (!capture_file_.capFile())
1332 /* Init the packet range */
1333 packet_range_init(&range, capture_file_.capFile());
1334 range.process_filtered = TRUE;
1335 range.include_dependents = TRUE;
1338 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1340 /* If the file has comments, does the format the user selected
1341 support them? If not, ask the user whether they want to
1342 discard the comments or choose a different format. */
1343 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1346 /* The file can be saved in the specified format as is;
1347 just drive on and save in the format they selected. */
1348 discard_comments = FALSE;
1351 case SAVE_WITHOUT_COMMENTS:
1352 /* The file can't be saved in the specified format as is,
1353 but it can be saved without the comments, and the user
1354 said "OK, discard the comments", so save it in the
1355 format they specified without the comments. */
1356 discard_comments = TRUE;
1359 case SAVE_IN_ANOTHER_FORMAT:
1360 /* There are file formats in which we can save this that
1361 support comments, and the user said not to delete the
1362 comments. The combo box of file formats has had the
1363 formats that don't support comments trimmed from it,
1364 so run the dialog again, to let the user decide
1365 whether to save in one of those formats or give up. */
1369 /* The user said "forget it". Just get rid of the dialog box
1375 * Check that we're not going to save on top of the current
1377 * We do it here so we catch all cases ...
1378 * Unfortunately, the file requester gives us an absolute file
1379 * name and the read file name may be relative (if supplied on
1380 * the command line). From Joerg Mayer.
1382 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1383 QMessageBox msg_box;
1384 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1386 msg_box.setIcon(QMessageBox::Critical);
1387 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1388 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1389 msg_box.setStandardButtons(QMessageBox::Ok);
1390 msg_box.setDefaultButton(QMessageBox::Ok);
1392 g_free(display_basename);
1396 file_type = esp_dlg.selectedFileType();
1397 compressed = esp_dlg.isCompressed();
1398 fileAddExtension(file_name, file_type, compressed);
1401 // /* If the file exists and it's user-immutable or not writable,
1402 // ask the user whether they want to override that. */
1403 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1404 // /* They don't. Let them try another file name or cancel. */
1409 /* Attempt to save the file */
1410 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1414 /* The save succeeded; we're done. */
1415 /* Save the directory name for future file dialogs. */
1416 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1417 set_last_open_dir(get_dirname(dirname));
1419 /* If we discarded comments, redraw the packet list to reflect
1420 any packets that no longer have comments. */
1421 if (discard_comments)
1422 packet_list_queue_draw();
1425 case CF_WRITE_ERROR:
1426 /* The save failed; let the user try again. */
1429 case CF_WRITE_ABORTED:
1430 /* The user aborted the save; just return. */
1437 void MainWindow::exportDissections(export_type_e export_type) {
1438 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1439 packet_range_t range;
1441 if (!capture_file_.capFile())
1444 /* Init the packet range */
1445 packet_range_init(&range, capture_file_.capFile());
1446 range.process_filtered = TRUE;
1447 range.include_dependents = TRUE;
1452 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1453 QString file_name_lower;
1454 QString file_suffix;
1455 GSList *extensions_list;
1456 gboolean add_extension;
1459 * Append the default file extension if there's none given by
1460 * the user or if they gave one that's not one of the valid
1461 * extensions for the file type.
1463 file_name_lower = file_name.toLower();
1464 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1465 if (extensions_list != NULL) {
1468 /* We have one or more extensions for this file type.
1469 Start out assuming we need to add the default one. */
1470 add_extension = TRUE;
1472 /* OK, see if the file has one of those extensions. */
1473 for (extension = extensions_list; extension != NULL;
1474 extension = g_slist_next(extension)) {
1475 file_suffix += tr(".") + (char *)extension->data;
1476 if (file_name_lower.endsWith(file_suffix)) {
1478 * The file name has one of the extensions for
1481 add_extension = FALSE;
1484 file_suffix += ".gz";
1485 if (file_name_lower.endsWith(file_suffix)) {
1487 * The file name has one of the extensions for
1490 add_extension = FALSE;
1495 /* We have no extensions for this file type. Don't add one. */
1496 add_extension = FALSE;
1498 if (add_extension) {
1499 if (wtap_default_file_extension(file_type) != NULL) {
1500 file_name += tr(".") + wtap_default_file_extension(file_type);
1508 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1509 bool capture_in_progress = false;
1510 bool do_close_file = false;
1512 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1513 return true; /* Already closed, nothing to do */
1516 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1517 /* This is true if we're reading a capture file *or* if we're doing
1518 a live capture. If we're reading a capture file, the main loop
1519 is busy reading packets, and only accepting input from the
1520 progress dialog, so we can't get here, so this means we're
1522 capture_in_progress = true;
1526 if (prefs.gui_ask_unsaved) {
1527 if (cf_has_unsaved_data(capture_file_.capFile()) ||
1528 (capture_in_progress && capture_file_.capFile()->count > 0))
1530 QMessageBox msg_dialog;
1533 QPushButton *save_button;
1534 QPushButton *discard_button;
1536 msg_dialog.setIcon(QMessageBox::Question);
1537 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1539 /* This file has unsaved data or there's a capture in
1540 progress; ask the user whether to save the data. */
1541 if (capture_in_progress && context != Restart) {
1542 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1543 infotext = tr("Your captured packets will be lost if you don't save them.");
1544 } else if (capture_file_.capFile()->is_tempfile) {
1545 if (context == Reload) {
1546 // Reloading a tempfile will keep the packets, so this is not unsaved packets
1547 question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1548 infotext = tr("Your changes will be lost if you don't save them.");
1550 question = tr("Do you want to save the captured packets%1?").arg(before_what);
1551 infotext = tr("Your captured packets will be lost if you don't save them.");
1554 // No capture in progress and not a tempfile, so this is not unsaved packets
1555 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1556 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1557 infotext = tr("Your changes will be lost if you don't save them.");
1558 g_free(display_basename);
1561 msg_dialog.setText(question);
1562 msg_dialog.setInformativeText(infotext);
1564 // XXX Text comes from ui/gtk/stock_icons.[ch]
1565 // Note that the button roles differ from the GTK+ version.
1566 // Cancel = RejectRole
1567 // Save = AcceptRole
1568 // Don't Save = DestructiveRole
1569 msg_dialog.addButton(QMessageBox::Cancel);
1571 if (capture_in_progress) {
1572 QString save_button_text;
1573 if (context == Restart) {
1574 save_button_text = tr("Save before Continue");
1576 save_button_text = tr("Stop and Save");
1578 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1580 save_button = msg_dialog.addButton(QMessageBox::Save);
1582 msg_dialog.setDefaultButton(save_button);
1584 QString discard_button_text;
1585 if (capture_in_progress) {
1588 discard_button_text = tr("Stop and Quit without Saving");
1591 discard_button_text = tr("Continue without Saving");
1594 discard_button_text = tr("Stop and Continue without Saving");
1600 discard_button_text = tr("Quit without Saving");
1604 discard_button_text = tr("Continue without Saving");
1608 discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1611 /* According to the Qt doc:
1612 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1614 * Therefore we should use clickedButton() to determine which button was clicked. */
1616 if (msg_dialog.clickedButton() == save_button) {
1618 /* If there's a capture in progress, we have to stop the capture
1619 and then do the save. */
1620 if (capture_in_progress)
1623 /* Save the file and close it */
1624 saveCaptureFile(capture_file_.capFile(), true);
1625 } else if(msg_dialog.clickedButton() == discard_button) {
1626 /* Just close the file, discarding changes */
1627 do_close_file = true;
1629 // cancelButton or some other unspecified button
1633 /* Unchanged file or capturing with no packets */
1634 do_close_file = true;
1637 /* User asked not to be bothered by those prompts, just close it.
1638 XXX - should that apply only to saving temporary files? */
1639 do_close_file = true;
1642 if (do_close_file) {
1644 /* If there's a capture in progress, we have to stop the capture
1645 and then do the close. */
1646 if (capture_in_progress)
1649 /* captureStop() will close the file if not having any packets */
1650 if (capture_file_.capFile() && context != Restart && context != Reload)
1651 // Don't really close if Restart or Reload
1652 cf_close(capture_file_.capFile());
1655 return true; /* File closed */
1658 void MainWindow::captureStop() {
1661 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1662 WiresharkApplication::processEvents();
1666 void MainWindow::initMainToolbarIcons()
1668 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1669 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1670 #if !defined(Q_OS_WIN)
1671 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1672 // The OS X HIG specifies 32-pixel icons but they're a little too
1674 icon_size = icon_size * 3 / 2;
1676 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1678 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1679 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1681 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1682 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1683 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1684 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1686 // Menu icons are disabled in main_window.ui for these items.
1687 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1688 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1689 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1690 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1692 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1693 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1694 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1695 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1696 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1697 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1698 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1699 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1700 #if defined(Q_OS_MAC)
1701 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1702 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1704 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1706 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1707 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1709 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1710 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1711 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1712 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1713 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1714 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1715 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1718 void MainWindow::initShowHideMainWidgets()
1720 if (show_hide_actions_) {
1724 show_hide_actions_ = new QActionGroup(this);
1725 QMap<QAction *, QWidget *> shmw_actions;
1727 show_hide_actions_->setExclusive(false);
1728 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1729 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1730 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1731 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1732 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1733 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1734 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1736 foreach (QAction *shmwa, shmw_actions.keys()) {
1737 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1738 show_hide_actions_->addAction(shmwa);
1739 showHideMainWidgets(shmwa);
1742 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1745 Q_DECLARE_METATYPE(ts_type)
1747 void MainWindow::initTimeDisplayFormatMenu()
1749 if (time_display_actions_) {
1753 time_display_actions_ = new QActionGroup(this);
1755 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1756 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1757 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1758 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1759 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1760 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1761 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1762 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1763 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1764 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1766 foreach (QAction* tda, td_actions.keys()) {
1767 tda->setData(qVariantFromValue(td_actions[tda]));
1768 time_display_actions_->addAction(tda);
1771 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1774 Q_DECLARE_METATYPE(ts_precision)
1776 void MainWindow::initTimePrecisionFormatMenu()
1778 if (time_precision_actions_) {
1782 time_precision_actions_ = new QActionGroup(this);
1784 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1785 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1786 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1787 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1788 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1789 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1790 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1792 foreach (QAction* tpa, tp_actions.keys()) {
1793 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1794 time_precision_actions_->addAction(tpa);
1797 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1800 // Menu items which will be disabled when we freeze() and whose state will
1801 // be restored when we thaw(). Add to the list as needed.
1802 void MainWindow::initFreezeActions()
1804 QList<QAction *> freeze_actions = QList<QAction *>()
1805 << main_ui_->actionFileClose
1806 << main_ui_->actionViewReload
1807 << main_ui_->actionEditMarkPacket
1808 << main_ui_->actionEditMarkAllDisplayed
1809 << main_ui_->actionEditUnmarkAllDisplayed
1810 << main_ui_->actionEditIgnorePacket
1811 << main_ui_->actionEditIgnoreAllDisplayed
1812 << main_ui_->actionEditUnignoreAllDisplayed
1813 << main_ui_->actionEditSetTimeReference
1814 << main_ui_->actionEditUnsetAllTimeReferences;
1816 foreach (QAction *action, freeze_actions) {
1817 freeze_actions_ << QPair<QAction *, bool>(action, false);
1822 void MainWindow::setTitlebarForCaptureFile()
1824 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1825 if (capture_file_.capFile()->is_tempfile) {
1827 // For a temporary file, put the source of the data
1828 // in the window title, not whatever random pile
1829 // of characters is the last component of the path
1832 // XXX - on non-Mac platforms, put in the application
1835 setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1838 // For a user file, set the full path; that way,
1839 // for OS X, it'll set the "proxy icon". Qt
1840 // handles extracting the last component.
1842 // Sadly, some UN*Xes don't necessarily use UTF-8
1843 // for their file names, so we have to map the
1844 // file path to UTF-8. If that fails, we're somewhat
1847 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1852 if (utf8_filename) {
1853 QFileInfo fi(utf8_filename);
1854 setWSWindowTitle(QString("[*]%1").arg(fi.fileName()));
1855 setWindowFilePath(utf8_filename);
1856 g_free(utf8_filename);
1858 // So what the heck else can we do here?
1859 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1862 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1864 /* We have no capture file. */
1869 QString MainWindow::replaceWindowTitleVariables(QString title)
1871 title.replace ("%P", get_profile_name());
1872 title.replace ("%V", get_ws_vcs_version_info());
1877 void MainWindow::setWSWindowTitle(QString title)
1879 if (title.isEmpty()) {
1880 title = tr("The Wireshark Network Analyzer");
1883 if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
1884 QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
1885 title.prepend(QString("[%1] ").arg(custom_title));
1888 if (prefs.gui_window_title && prefs.gui_window_title[0]) {
1889 QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
1891 // On OS X we separate the titles with a unicode em dash
1892 title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
1894 title.append(QString(" [%1]").arg(custom_title));
1898 setWindowTitle(title);
1899 setWindowFilePath(NULL);
1902 void MainWindow::setTitlebarForCaptureInProgress()
1904 if (capture_file_.capFile()) {
1905 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1907 /* We have no capture in progress. */
1914 /* Enable or disable menu items based on whether you have a capture file
1915 you've finished reading and, if you have one, whether it's been saved
1916 and whether it could be saved except by copying the raw packet data. */
1917 void MainWindow::setMenusForCaptureFile(bool force_disable)
1919 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1920 /* We have no capture file or we're currently reading a file */
1921 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(false);
1922 main_ui_->actionFileMerge->setEnabled(false);
1923 main_ui_->actionFileClose->setEnabled(false);
1924 main_ui_->actionFileSave->setEnabled(false);
1925 main_ui_->actionFileSaveAs->setEnabled(false);
1926 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1927 main_ui_->actionFileExportPackets->setEnabled(false);
1928 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1929 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1930 main_ui_->actionFileExportPDU->setEnabled(false);
1931 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1932 main_ui_->menuFileExportObjects->setEnabled(false);
1933 main_ui_->actionViewReload->setEnabled(false);
1935 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(true);
1936 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1938 main_ui_->actionFileClose->setEnabled(true);
1939 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1940 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1941 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1943 * "Export Specified Packets..." should be available only if
1944 * we can write the file out in at least one format.
1946 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1947 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1948 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1949 main_ui_->actionFileExportPDU->setEnabled(true);
1950 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1951 main_ui_->menuFileExportObjects->setEnabled(true);
1952 main_ui_->actionViewReload->setEnabled(true);
1956 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1957 /* Either a capture was started or stopped; in either case, it's not
1958 in the process of stopping, so allow quitting. */
1960 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1961 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1962 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1963 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1964 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1965 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1966 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1967 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1968 main_ui_->actionFileQuit->setEnabled(true);
1970 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1972 // XXX Fix packet list heading menu sensitivity
1973 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1974 // !capture_in_progress);
1975 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1976 // !capture_in_progress);
1977 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1978 // !capture_in_progress);
1981 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1982 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1983 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1984 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1985 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1986 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1987 #endif /* HAVE_LIBPCAP */
1991 void MainWindow::setMenusForCaptureStopping() {
1992 main_ui_->actionFileQuit->setEnabled(false);
1993 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1995 main_ui_->actionCaptureStart->setChecked(false);
1996 main_ui_->actionCaptureStop->setEnabled(false);
1997 main_ui_->actionCaptureRestart->setEnabled(false);
1998 #endif /* HAVE_LIBPCAP */
2001 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2003 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2005 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2006 // have_captured_packets);
2008 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2009 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2010 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2012 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2013 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2014 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2015 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2016 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2017 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2018 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2020 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2021 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2022 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2023 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2025 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2026 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2027 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2030 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2031 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2032 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2034 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2035 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2036 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2039 void MainWindow::setWindowIcon(const QIcon &icon) {
2040 wsApp->setWindowIcon(icon);
2041 QMainWindow::setWindowIcon(icon);
2044 void MainWindow::updateForUnsavedChanges() {
2045 setTitlebarForCaptureFile();
2046 setMenusForCaptureFile();
2049 void MainWindow::changeEvent(QEvent* event)
2053 switch (event->type())
2055 case QEvent::LanguageChange:
2056 main_ui_->retranslateUi(this);
2057 // make sure that the "Clear Menu" item is retranslated
2058 updateRecentFiles();
2060 case QEvent::LocaleChange:{
2061 QString locale = QLocale::system().name();
2062 locale.truncate(locale.lastIndexOf('_'));
2063 wsApp->loadLanguage(locale);
2070 QMainWindow::changeEvent(event);
2073 /* Update main window items based on whether there's a capture in progress. */
2074 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2076 setMenusForCaptureInProgress(capture_in_progress);
2078 wireless_frame_->setCaptureInProgress(capture_in_progress);
2081 packet_list_->setCaptureInProgress(capture_in_progress);
2082 // set_toolbar_for_capture_in_progress(capture_in_progress);
2084 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2088 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2089 << REGISTER_ANALYZE_GROUP_UNSORTED
2090 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2091 << REGISTER_STAT_GROUP_UNSORTED
2092 << REGISTER_STAT_GROUP_GENERIC
2093 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2094 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2095 << REGISTER_STAT_GROUP_RESPONSE_TIME
2096 << REGISTER_STAT_GROUP_TELEPHONY
2097 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2098 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2099 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2100 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2101 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2102 << REGISTER_TOOLS_GROUP_UNSORTED;
2104 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2106 foreach (QAction *action, actions) {
2107 switch (menu_group) {
2108 case REGISTER_ANALYZE_GROUP_UNSORTED:
2109 case REGISTER_STAT_GROUP_UNSORTED:
2110 main_ui_->menuStatistics->insertAction(
2111 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2114 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2115 main_ui_->menuServiceResponseTime->addAction(action);
2117 case REGISTER_STAT_GROUP_TELEPHONY:
2118 main_ui_->menuTelephony->addAction(action);
2120 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2121 main_ui_->menuANSI->addAction(action);
2123 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2124 main_ui_->menuGSM->addAction(action);
2126 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2127 main_ui_->menuLTE->addAction(action);
2129 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2130 main_ui_->menuMTP3->addAction(action);
2132 case REGISTER_TOOLS_GROUP_UNSORTED:
2134 // Allow the creation of submenus. Mimics the behavor of
2135 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2136 // and GtkUIManager.
2138 // For now we limit the insanity to the "Tools" menu.
2139 QStringList menu_path = action->text().split('/');
2140 QMenu *cur_menu = main_ui_->menuTools;
2141 while (menu_path.length() > 1) {
2142 QString menu_title = menu_path.takeFirst();
2143 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2144 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2146 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2147 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2150 submenu = cur_menu->addMenu(menu_title);
2151 submenu->setObjectName(menu_title.toLower());
2155 action->setText(menu_path.last());
2156 cur_menu->addAction(action);
2160 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2164 // Connect each action type to its corresponding slot. We to
2165 // distinguish various types of actions. Setting their objectName
2166 // seems to work OK.
2167 if (action->objectName() == TapParameterDialog::actionName()) {
2168 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2169 } else if (action->objectName() == FunnelStatistics::actionName()) {
2170 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2174 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2176 foreach (QAction *action, actions) {
2177 switch (menu_group) {
2178 case REGISTER_ANALYZE_GROUP_UNSORTED:
2179 case REGISTER_STAT_GROUP_UNSORTED:
2180 main_ui_->menuStatistics->removeAction(action);
2182 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2183 main_ui_->menuServiceResponseTime->removeAction(action);
2185 case REGISTER_STAT_GROUP_TELEPHONY:
2186 main_ui_->menuTelephony->removeAction(action);
2188 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2189 main_ui_->menuANSI->removeAction(action);
2191 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2192 main_ui_->menuGSM->removeAction(action);
2194 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2195 main_ui_->menuLTE->removeAction(action);
2197 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2198 main_ui_->menuMTP3->removeAction(action);
2200 case REGISTER_TOOLS_GROUP_UNSORTED:
2202 // Allow removal of submenus.
2203 // For now we limit the insanity to the "Tools" menu.
2204 QStringList menu_path = action->text().split('/');
2205 QMenu *cur_menu = main_ui_->menuTools;
2206 while (menu_path.length() > 1) {
2207 QString menu_title = menu_path.takeFirst();
2208 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2209 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2211 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2212 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2216 cur_menu->removeAction(action);
2220 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2226 void MainWindow::addDynamicMenus()
2229 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2230 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2231 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2232 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2233 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2234 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2236 // Fill in each menu
2237 foreach (register_stat_group_t menu_group, menu_groups) {
2238 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2239 addMenuActions(actions, menu_group);
2242 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2243 // We've added a placeholder in order to make sure the "Tools" menu is
2244 // visible. Hide it as needed.
2245 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2246 main_ui_->actionToolsPlaceholder->setVisible(false);
2248 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2249 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2251 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2252 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2254 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2255 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2257 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2258 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2262 void MainWindow::reloadDynamicMenus()
2264 foreach (register_stat_group_t menu_group, menu_groups) {
2265 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2266 removeMenuActions(actions, menu_group);
2268 actions = wsApp->addedMenuGroupItems(menu_group);
2269 addMenuActions(actions, menu_group);
2272 wsApp->clearAddedMenuGroupItems();
2273 wsApp->clearRemovedMenuGroupItems();
2276 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2278 QAction * itemAction = NULL;
2279 ext_menubar_t * item = NULL;
2280 GList * children = NULL;
2282 /* There must exists an xpath parent */
2283 g_assert(subMenu != NULL);
2285 /* If the depth counter exceeds, something must have gone wrong */
2286 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2288 children = menu->children;
2289 /* Iterate the child entries */
2290 while (children && children->data) {
2291 item = (ext_menubar_t *) children->data;
2293 if (item->type == EXT_MENUBAR_MENU) {
2294 /* Handle Submenu entry */
2295 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2296 } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2297 subMenu->addSeparator();
2298 } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2299 itemAction = subMenu->addAction(item->name);
2300 itemAction->setData(QVariant::fromValue((void *)item));
2301 itemAction->setText(item->label);
2302 connect(itemAction, SIGNAL(triggered()),
2303 this, SLOT(externalMenuItem_triggered()));
2307 children = g_list_next(children);
2311 QMenu * MainWindow::searchSubMenu(QString objectName)
2315 if (objectName.length() > 0) {
2316 QString searchName = QString("menu") + objectName;
2318 lst = main_ui_->menuBar->findChildren<QMenu*>();
2319 foreach (QMenu* m, lst) {
2320 if (QString::compare(m->objectName(), searchName) == 0)
2328 void MainWindow::addExternalMenus()
2330 QMenu * subMenu = NULL;
2331 GList * user_menu = NULL;
2332 ext_menu_t * menu = NULL;
2334 user_menu = ext_menubar_get_entries();
2336 while (user_menu && user_menu->data) {
2337 menu = (ext_menu_t *) user_menu->data;
2339 /* On this level only menu items should exist. Not doing an assert here,
2340 * as it could be an honest mistake */
2341 if (menu->type != EXT_MENUBAR_MENU) {
2342 user_menu = g_list_next(user_menu);
2346 /* Create main submenu and add it to the menubar */
2347 if (menu->parent_menu) {
2348 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2350 subMenu = sortUnderneath->addMenu(menu->label);
2354 subMenu = main_ui_->menuBar->addMenu(menu->label);
2356 /* This will generate the action structure for each menu. It is recursive,
2357 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2358 this->externalMenuHelper(menu, subMenu, 0);
2361 user_menu = g_list_next (user_menu);
2371 * indent-tabs-mode: nil
2374 * ex: set shiftwidth=4 tabstop=8 expandtab:
2375 * :indentSize=4:tabSize=8:noTabs=true: