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 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
657 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
658 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
659 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
662 /* Create plugin_if hooks */
663 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
664 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
665 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
666 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
668 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
671 main_ui_->mainStack->setCurrentWidget(main_welcome_);
674 MainWindow::~MainWindow()
679 QString MainWindow::getFilter()
681 return df_combo_box_->currentText();
684 QMenu *MainWindow::createPopupMenu()
686 QMenu *menu = new QMenu();
687 menu->addAction(main_ui_->actionViewMainToolbar);
688 menu->addAction(main_ui_->actionViewFilterToolbar);
689 menu->addAction(main_ui_->actionViewWirelessToolbar);
690 menu->addAction(main_ui_->actionViewStatusBar);
691 menu->addSeparator();
692 menu->addAction(main_ui_->actionViewPacketList);
693 menu->addAction(main_ui_->actionViewPacketDetails);
694 menu->addAction(main_ui_->actionViewPacketBytes);
698 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
700 pipe_source_ = source;
701 pipe_child_process_ = child_process;
702 pipe_user_data_ = user_data;
703 pipe_input_cb_ = input_cb;
706 /* Tricky to use pipes in win9x, as no concept of wait. NT can
707 do this but that doesn't cover all win32 platforms. GTK can do
708 this but doesn't seem to work over processes. Attempt to do
709 something similar here, start a timer and check for data on every
711 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
714 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
718 pipe_timer_ = new QTimer(this);
719 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
720 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
721 pipe_timer_->start(200);
723 if (pipe_notifier_) {
724 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
725 delete pipe_notifier_;
728 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
729 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
730 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
731 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
735 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
737 // The user typed some text. Start filling in a filter.
738 // We may need to be more choosy here. We just need to catch events for the packet list,
739 // proto tree, and main welcome widgets.
740 if (event->type() == QEvent::KeyPress) {
741 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
742 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
743 df_combo_box_->lineEdit()->insert(kevt->text());
744 df_combo_box_->lineEdit()->setFocus();
749 return QMainWindow::eventFilter(obj, event);
752 void MainWindow::keyPressEvent(QKeyEvent *event) {
754 // Explicitly focus on the display filter combo.
755 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
756 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
760 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
761 if (event->modifiers() == Qt::NoModifier) {
762 if (event->key() == Qt::Key_Escape) {
763 on_goToCancel_clicked();
764 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
768 return; // goToLineEdit didn't want it and we don't either.
771 // Move up & down the packet list.
772 if (event->key() == Qt::Key_F7) {
773 packet_list_->goPreviousPacket();
774 } else if (event->key() == Qt::Key_F8) {
775 packet_list_->goNextPacket();
778 // Move along, citizen.
779 QMainWindow::keyPressEvent(event);
782 void MainWindow::closeEvent(QCloseEvent *event) {
783 saveWindowGeometry();
785 /* If we're in the middle of stopping a capture, don't do anything;
786 the user can try deleting the window after the capture stops. */
787 if (capture_stopping_) {
792 QString before_what(tr(" before quitting"));
793 if (!testCaptureFileClose(before_what, Quit)) {
799 capture_interfaces_dialog_.close();
801 // Make sure we kill any open dumpcap processes.
802 delete main_welcome_;
804 // One of the many places we assume one main window.
805 if(!wsApp->isInitialized()) {
806 // If we're still initializing, QCoreApplication::quit() won't
807 // exit properly because we are not in the event loop. This
808 // means that the application won't clean up after itself. We
809 // might want to call wsApp->processEvents() during startup
810 // instead so that we can do a normal exit here.
816 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
819 foreach (QUrl drag_url, event->mimeData()->urls()) {
820 if (!drag_url.toLocalFile().isEmpty()) {
825 if (accept) event->acceptProposedAction();
828 void MainWindow::dropEvent(QDropEvent *event)
830 foreach (QUrl drop_url, event->mimeData()->urls()) {
831 QString local_file = drop_url.toLocalFile();
832 if (!local_file.isEmpty()) {
833 event->acceptProposedAction();
834 openCaptureFile(local_file);
840 // Apply recent settings to the main window geometry.
841 // We haven't loaded the preferences at this point so we assume that the
842 // position and size preference are enabled.
843 void MainWindow::loadWindowGeometry()
845 int min_sensible_dimension = 200;
848 if (recent.gui_geometry_main_maximized) {
849 setWindowState(Qt::WindowMaximized);
853 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
854 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
855 if (!rect_on_screen(recent_geom)) {
856 // We're not visible on any screens. Give up and use the default geometry.
860 // if (prefs.gui_geometry_save_position) {
861 move(recent_geom.topLeft());
864 if (// prefs.gui_geometry_save_size &&
865 recent_geom.width() > min_sensible_dimension &&
866 recent_geom.height() > min_sensible_dimension) {
867 resize(recent_geom.size());
872 void MainWindow::saveWindowGeometry()
874 if (prefs.gui_geometry_save_position) {
875 recent.gui_geometry_main_x = pos().x();
876 recent.gui_geometry_main_y = pos().y();
879 if (prefs.gui_geometry_save_size) {
880 recent.gui_geometry_main_width = size().width();
881 recent.gui_geometry_main_height = size().height();
884 if (prefs.gui_geometry_save_maximized) {
885 // On OS X this is false when it shouldn't be
886 recent.gui_geometry_main_maximized = isMaximized();
889 if (master_split_.sizes().length() > 0) {
890 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
893 if (master_split_.sizes().length() > 2) {
894 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
895 } else if (extra_split_.sizes().length() > 0) {
896 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
900 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
902 case layout_pane_content_none:
904 case layout_pane_content_plist:
906 case layout_pane_content_pdetails:
908 case layout_pane_content_pbytes:
909 return byte_view_tab_;
911 g_assert_not_reached();
916 // Our event loop becomes nested whenever we call update_progress_dlg, which
917 // includes several places in file.c. The GTK+ UI stays out of trouble by
918 // showing a modal progress dialog. We attempt to do the equivalent below by
919 // disabling parts of the main window. At a minumum the ProgressFrame in the
920 // main status bar must remain accessible.
922 // We might want to do this any time the main status bar progress frame is
924 void MainWindow::freeze()
926 freeze_focus_ = wsApp->focusWidget();
928 // XXX Alternatively we could just disable and enable the main menu.
929 for (int i = 0; i < freeze_actions_.size(); i++) {
930 QAction *action = freeze_actions_[i].first;
931 freeze_actions_[i].second = action->isEnabled();
932 action->setEnabled(false);
934 main_ui_->centralWidget->setEnabled(false);
937 void MainWindow::thaw()
939 main_ui_->centralWidget->setEnabled(true);
940 for (int i = 0; i < freeze_actions_.size(); i++) {
941 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
944 if (freeze_focus_) freeze_focus_->setFocus();
945 freeze_focus_ = NULL;
948 void MainWindow::mergeCaptureFile()
950 QString file_name = "";
951 QString read_filter = "";
952 dfilter_t *rfcode = NULL;
955 if (!capture_file_.capFile())
958 if (prefs.gui_ask_unsaved) {
959 if (cf_has_unsaved_data(capture_file_.capFile())) {
960 QMessageBox msg_dialog;
961 gchar *display_basename;
964 msg_dialog.setIcon(QMessageBox::Question);
965 /* This file has unsaved data; ask the user whether to save
967 if (capture_file_.capFile()->is_tempfile) {
968 msg_dialog.setText(tr("Save packets before merging?"));
969 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
972 * Format the message.
974 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
975 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
976 g_free(display_basename);
977 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
980 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
981 msg_dialog.setDefaultButton(QMessageBox::Save);
983 response = msg_dialog.exec();
987 case QMessageBox::Save:
988 /* Save the file but don't close it */
989 saveCaptureFile(capture_file_.capFile(), FALSE);
992 case QMessageBox::Cancel:
994 /* Don't do the merge. */
1001 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1003 cf_status_t merge_status;
1004 char *in_filenames[2];
1007 switch (prefs.gui_fileopen_style) {
1009 case FO_STYLE_LAST_OPENED:
1010 /* The user has specified that we should start out in the last directory
1011 we looked in. If we've already opened a file, use its containing
1012 directory, if we could determine it, as the directory, otherwise
1013 use the "last opened" directory saved in the preferences file if
1015 /* This is now the default behaviour in file_selection_new() */
1018 case FO_STYLE_SPECIFIED:
1019 /* The user has specified that we should always start out in a
1020 specified directory; if they've specified that directory,
1021 start out by showing the files in that dir. */
1022 if (prefs.gui_fileopen_dir[0] != '\0')
1023 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
1027 if (merge_dlg.merge(file_name)) {
1030 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1031 /* Not valid. Tell the user, and go back and run the file
1032 selection box again once they dismiss the alert. */
1033 //bad_dfilter_alert_box(top_level, read_filter->str);
1034 QMessageBox::warning(this, tr("Invalid Read Filter"),
1035 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1044 file_type = capture_file_.capFile()->cd_t;
1046 /* Try to merge or append the two files */
1048 if (merge_dlg.mergeType() == 0) {
1049 /* chronological order */
1050 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1051 in_filenames[1] = qstring_strdup(file_name);
1052 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
1053 } else if (merge_dlg.mergeType() <= 0) {
1055 in_filenames[0] = qstring_strdup(file_name);
1056 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1057 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1060 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1061 in_filenames[1] = qstring_strdup(file_name);
1062 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1065 g_free(in_filenames[0]);
1066 g_free(in_filenames[1]);
1068 if (merge_status != CF_OK) {
1070 dfilter_free(rfcode);
1075 cf_close(capture_file_.capFile());
1077 /* Try to open the merged capture file. */
1078 CaptureFile::globalCapFile()->window = this;
1079 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1080 /* We couldn't open it; fail. */
1081 CaptureFile::globalCapFile()->window = NULL;
1083 dfilter_free(rfcode);
1088 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1089 it closed the previous capture file, and thus destroyed any
1090 previous read filter attached to "cf"). */
1091 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1093 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1097 /* Just because we got an error, that doesn't mean we were unable
1098 to read any of the file; we handle what we could get from the
1102 case CF_READ_ABORTED:
1103 /* The user bailed out of re-reading the capture file; the
1104 capture file has been closed - just free the capture file name
1105 string and return (without changing the last containing
1111 /* Save the name of the containing directory specified in the path name,
1112 if any; we can write over cf_merged_name, which is a good thing, given that
1113 "get_dirname()" does write over its argument. */
1114 wsApp->setLastOpenDir(get_dirname(tmpname));
1116 main_ui_->statusBar->showExpert();
1122 void MainWindow::importCaptureFile() {
1123 ImportTextDialog import_dlg;
1125 QString before_what(tr(" before importing a capture"));
1126 if (!testCaptureFileClose(before_what))
1131 if (import_dlg.result() != QDialog::Accepted) {
1132 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1136 openCaptureFile(import_dlg.capfileName());
1139 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1141 gboolean discard_comments;
1143 if (cf->is_tempfile) {
1144 /* This is a temporary capture file, so saving it means saving
1145 it to a permanent file. Prompt the user for a location
1146 to which to save it. Don't require that the file format
1147 support comments - if it's a temporary capture file, it's
1148 probably pcap-ng, which supports comments and, if it's
1149 not pcap-ng, let the user decide what they want to do
1150 if they've added comments. */
1151 saveAsCaptureFile(cf, FALSE, dont_reopen);
1153 if (cf->unsaved_changes) {
1154 cf_write_status_t status;
1156 /* This is not a temporary capture file, but it has unsaved
1157 changes, so saving it means doing a "safe save" on top
1158 of the existing file, in the same format - no UI needed
1159 unless the file has comments and the file's format doesn't
1162 If the file has comments, does the file's format support them?
1163 If not, ask the user whether they want to discard the comments
1164 or choose a different format. */
1165 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1168 /* The file can be saved in the specified format as is;
1169 just drive on and save in the format they selected. */
1170 discard_comments = FALSE;
1173 case SAVE_WITHOUT_COMMENTS:
1174 /* The file can't be saved in the specified format as is,
1175 but it can be saved without the comments, and the user
1176 said "OK, discard the comments", so save it in the
1177 format they specified without the comments. */
1178 discard_comments = TRUE;
1181 case SAVE_IN_ANOTHER_FORMAT:
1182 /* There are file formats in which we can save this that
1183 support comments, and the user said not to delete the
1184 comments. Do a "Save As" so the user can select
1185 one of those formats and choose a file name. */
1186 saveAsCaptureFile(cf, TRUE, dont_reopen);
1190 /* The user said "forget it". Just return. */
1194 /* Squelch warnings that discard_comments is being used
1196 g_assert_not_reached();
1200 /* XXX - cf->filename might get freed out from under us, because
1201 the code path through which cf_save_records() goes currently
1202 closes the current file and then opens and reloads the saved file,
1203 so make a copy and free it later. */
1204 file_name = cf->filename;
1205 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1206 discard_comments, dont_reopen);
1210 /* The save succeeded; we're done.
1211 If we discarded comments, redraw the packet list to reflect
1212 any packets that no longer have comments. */
1213 if (discard_comments)
1214 packet_list_queue_draw();
1216 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1217 updateForUnsavedChanges(); // we update the title bar to remove the *
1220 case CF_WRITE_ERROR:
1221 /* The write failed.
1222 XXX - OK, what do we do now? Let them try a
1223 "Save As", in case they want to try to save to a
1224 different directory r file system? */
1227 case CF_WRITE_ABORTED:
1228 /* The write was aborted; just drive on. */
1232 /* Otherwise just do nothing. */
1236 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1237 QString file_name = "";
1239 gboolean compressed;
1240 cf_write_status_t status;
1242 gboolean discard_comments = FALSE;
1249 CaptureFileDialog save_as_dlg(this, cf);
1251 switch (prefs.gui_fileopen_style) {
1253 case FO_STYLE_LAST_OPENED:
1254 /* The user has specified that we should start out in the last directory
1255 we looked in. If we've already opened a file, use its containing
1256 directory, if we could determine it, as the directory, otherwise
1257 use the "last opened" directory saved in the preferences file if
1259 /* This is now the default behaviour in file_selection_new() */
1262 case FO_STYLE_SPECIFIED:
1263 /* The user has specified that we should always start out in a
1264 specified directory; if they've specified that directory,
1265 start out by showing the files in that dir. */
1266 if (prefs.gui_fileopen_dir[0] != '\0')
1267 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1271 /* If the file has comments, does the format the user selected
1272 support them? If not, ask the user whether they want to
1273 discard the comments or choose a different format. */
1274 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1277 /* The file can be saved in the specified format as is;
1278 just drive on and save in the format they selected. */
1279 discard_comments = FALSE;
1282 case SAVE_WITHOUT_COMMENTS:
1283 /* The file can't be saved in the specified format as is,
1284 but it can be saved without the comments, and the user
1285 said "OK, discard the comments", so save it in the
1286 format they specified without the comments. */
1287 discard_comments = TRUE;
1290 case SAVE_IN_ANOTHER_FORMAT:
1291 /* There are file formats in which we can save this that
1292 support comments, and the user said not to delete the
1293 comments. The combo box of file formats has had the
1294 formats that don't support comments trimmed from it,
1295 so run the dialog again, to let the user decide
1296 whether to save in one of those formats or give up. */
1297 must_support_comments = TRUE;
1301 /* The user said "forget it". Just get rid of the dialog box
1305 file_type = save_as_dlg.selectedFileType();
1306 compressed = save_as_dlg.isCompressed();
1308 fileAddExtension(file_name, file_type, compressed);
1311 // /* If the file exists and it's user-immutable or not writable,
1312 // ask the user whether they want to override that. */
1313 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1314 // /* They don't. Let them try another file name or cancel. */
1319 /* Attempt to save the file */
1320 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1321 discard_comments, dont_reopen);
1325 /* The save succeeded; we're done. */
1326 /* Save the directory name for future file dialogs. */
1327 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1328 set_last_open_dir(get_dirname(dirname));
1330 /* If we discarded comments, redraw the packet list to reflect
1331 any packets that no longer have comments. */
1332 if (discard_comments)
1333 packet_list_queue_draw();
1335 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1336 updateForUnsavedChanges(); // we update the title bar to remove the *
1339 case CF_WRITE_ERROR:
1340 /* The save failed; let the user try again. */
1343 case CF_WRITE_ABORTED:
1344 /* The user aborted the save; just return. */
1351 void MainWindow::exportSelectedPackets() {
1352 QString file_name = "";
1354 gboolean compressed;
1355 packet_range_t range;
1356 cf_write_status_t status;
1358 gboolean discard_comments = FALSE;
1360 if (!capture_file_.capFile())
1363 /* Init the packet range */
1364 packet_range_init(&range, capture_file_.capFile());
1365 range.process_filtered = TRUE;
1366 range.include_dependents = TRUE;
1369 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1371 switch (prefs.gui_fileopen_style) {
1373 case FO_STYLE_LAST_OPENED:
1374 /* The user has specified that we should start out in the last directory
1375 we looked in. If we've already opened a file, use its containing
1376 directory, if we could determine it, as the directory, otherwise
1377 use the "last opened" directory saved in the preferences file if
1379 /* This is now the default behaviour in file_selection_new() */
1382 case FO_STYLE_SPECIFIED:
1383 /* The user has specified that we should always start out in a
1384 specified directory; if they've specified that directory,
1385 start out by showing the files in that dir. */
1386 if (prefs.gui_fileopen_dir[0] != '\0')
1387 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1391 /* If the file has comments, does the format the user selected
1392 support them? If not, ask the user whether they want to
1393 discard the comments or choose a different format. */
1394 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1397 /* The file can be saved in the specified format as is;
1398 just drive on and save in the format they selected. */
1399 discard_comments = FALSE;
1402 case SAVE_WITHOUT_COMMENTS:
1403 /* The file can't be saved in the specified format as is,
1404 but it can be saved without the comments, and the user
1405 said "OK, discard the comments", so save it in the
1406 format they specified without the comments. */
1407 discard_comments = TRUE;
1410 case SAVE_IN_ANOTHER_FORMAT:
1411 /* There are file formats in which we can save this that
1412 support comments, and the user said not to delete the
1413 comments. The combo box of file formats has had the
1414 formats that don't support comments trimmed from it,
1415 so run the dialog again, to let the user decide
1416 whether to save in one of those formats or give up. */
1420 /* The user said "forget it". Just get rid of the dialog box
1426 * Check that we're not going to save on top of the current
1428 * We do it here so we catch all cases ...
1429 * Unfortunately, the file requester gives us an absolute file
1430 * name and the read file name may be relative (if supplied on
1431 * the command line). From Joerg Mayer.
1433 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1434 QMessageBox msg_box;
1435 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1437 msg_box.setIcon(QMessageBox::Critical);
1438 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1439 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1440 msg_box.setStandardButtons(QMessageBox::Ok);
1441 msg_box.setDefaultButton(QMessageBox::Ok);
1443 g_free(display_basename);
1447 file_type = esp_dlg.selectedFileType();
1448 compressed = esp_dlg.isCompressed();
1449 fileAddExtension(file_name, file_type, compressed);
1452 // /* If the file exists and it's user-immutable or not writable,
1453 // ask the user whether they want to override that. */
1454 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1455 // /* They don't. Let them try another file name or cancel. */
1460 /* Attempt to save the file */
1461 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1465 /* The save succeeded; we're done. */
1466 /* Save the directory name for future file dialogs. */
1467 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1468 set_last_open_dir(get_dirname(dirname));
1470 /* If we discarded comments, redraw the packet list to reflect
1471 any packets that no longer have comments. */
1472 if (discard_comments)
1473 packet_list_queue_draw();
1476 case CF_WRITE_ERROR:
1477 /* The save failed; let the user try again. */
1480 case CF_WRITE_ABORTED:
1481 /* The user aborted the save; just return. */
1488 void MainWindow::exportDissections(export_type_e export_type) {
1489 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1490 packet_range_t range;
1492 if (!capture_file_.capFile())
1495 /* Init the packet range */
1496 packet_range_init(&range, capture_file_.capFile());
1497 range.process_filtered = TRUE;
1498 range.include_dependents = TRUE;
1503 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1504 QString file_name_lower;
1505 QString file_suffix;
1506 GSList *extensions_list;
1507 gboolean add_extension;
1510 * Append the default file extension if there's none given by
1511 * the user or if they gave one that's not one of the valid
1512 * extensions for the file type.
1514 file_name_lower = file_name.toLower();
1515 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1516 if (extensions_list != NULL) {
1519 /* We have one or more extensions for this file type.
1520 Start out assuming we need to add the default one. */
1521 add_extension = TRUE;
1523 /* OK, see if the file has one of those extensions. */
1524 for (extension = extensions_list; extension != NULL;
1525 extension = g_slist_next(extension)) {
1526 file_suffix += tr(".") + (char *)extension->data;
1527 if (file_name_lower.endsWith(file_suffix)) {
1529 * The file name has one of the extensions for
1532 add_extension = FALSE;
1535 file_suffix += ".gz";
1536 if (file_name_lower.endsWith(file_suffix)) {
1538 * The file name has one of the extensions for
1541 add_extension = FALSE;
1546 /* We have no extensions for this file type. Don't add one. */
1547 add_extension = FALSE;
1549 if (add_extension) {
1550 if (wtap_default_file_extension(file_type) != NULL) {
1551 file_name += tr(".") + wtap_default_file_extension(file_type);
1559 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1560 bool capture_in_progress = false;
1561 bool do_close_file = false;
1563 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1564 return true; /* Already closed, nothing to do */
1567 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1568 /* This is true if we're reading a capture file *or* if we're doing
1569 a live capture. If we're reading a capture file, the main loop
1570 is busy reading packets, and only accepting input from the
1571 progress dialog, so we can't get here, so this means we're
1573 capture_in_progress = true;
1577 if (prefs.gui_ask_unsaved) {
1578 if (cf_has_unsaved_data(capture_file_.capFile()) ||
1579 (capture_in_progress && capture_file_.capFile()->count > 0))
1581 QMessageBox msg_dialog;
1584 QPushButton *save_button;
1585 QPushButton *discard_button;
1587 msg_dialog.setIcon(QMessageBox::Question);
1588 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1590 /* This file has unsaved data or there's a capture in
1591 progress; ask the user whether to save the data. */
1592 if (capture_in_progress && context != Restart) {
1593 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1594 infotext = tr("Your captured packets will be lost if you don't save them.");
1595 } else if (capture_file_.capFile()->is_tempfile) {
1596 if (context == Reload) {
1597 // Reloading a tempfile will keep the packets, so this is not unsaved packets
1598 question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1599 infotext = tr("Your changes will be lost if you don't save them.");
1601 question = tr("Do you want to save the captured packets%1?").arg(before_what);
1602 infotext = tr("Your captured packets will be lost if you don't save them.");
1605 // No capture in progress and not a tempfile, so this is not unsaved packets
1606 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1607 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1608 infotext = tr("Your changes will be lost if you don't save them.");
1609 g_free(display_basename);
1612 msg_dialog.setText(question);
1613 msg_dialog.setInformativeText(infotext);
1615 // XXX Text comes from ui/gtk/stock_icons.[ch]
1616 // Note that the button roles differ from the GTK+ version.
1617 // Cancel = RejectRole
1618 // Save = AcceptRole
1619 // Don't Save = DestructiveRole
1620 msg_dialog.addButton(QMessageBox::Cancel);
1622 if (capture_in_progress) {
1623 QString save_button_text;
1624 if (context == Restart) {
1625 save_button_text = tr("Save before Continue");
1627 save_button_text = tr("Stop and Save");
1629 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1631 save_button = msg_dialog.addButton(QMessageBox::Save);
1633 msg_dialog.setDefaultButton(save_button);
1635 QString discard_button_text;
1636 if (capture_in_progress) {
1639 discard_button_text = tr("Stop and Quit without Saving");
1642 discard_button_text = tr("Continue without Saving");
1645 discard_button_text = tr("Stop and Continue without Saving");
1651 discard_button_text = tr("Quit without Saving");
1655 discard_button_text = tr("Continue without Saving");
1659 discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1662 /* According to the Qt doc:
1663 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1665 * Therefore we should use clickedButton() to determine which button was clicked. */
1667 if (msg_dialog.clickedButton() == save_button) {
1669 /* If there's a capture in progress, we have to stop the capture
1670 and then do the save. */
1671 if (capture_in_progress)
1674 /* Save the file and close it */
1675 saveCaptureFile(capture_file_.capFile(), true);
1676 } else if(msg_dialog.clickedButton() == discard_button) {
1677 /* Just close the file, discarding changes */
1678 do_close_file = true;
1680 // cancelButton or some other unspecified button
1684 /* Unchanged file or capturing with no packets */
1685 do_close_file = true;
1688 /* User asked not to be bothered by those prompts, just close it.
1689 XXX - should that apply only to saving temporary files? */
1690 do_close_file = true;
1693 if (do_close_file) {
1695 /* If there's a capture in progress, we have to stop the capture
1696 and then do the close. */
1697 if (capture_in_progress)
1700 /* captureStop() will close the file if not having any packets */
1701 if (capture_file_.capFile() && context != Restart && context != Reload)
1702 // Don't really close if Restart or Reload
1703 cf_close(capture_file_.capFile());
1706 return true; /* File closed */
1709 void MainWindow::captureStop() {
1712 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1713 WiresharkApplication::processEvents();
1717 void MainWindow::initMainToolbarIcons()
1719 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1720 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1721 #if !defined(Q_OS_WIN)
1722 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1723 // The OS X HIG specifies 32-pixel icons but they're a little too
1725 icon_size = icon_size * 3 / 2;
1727 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1729 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1730 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1732 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1733 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1734 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1735 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1737 // Menu icons are disabled in main_window.ui for these items.
1738 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1739 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1740 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1741 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1743 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1744 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1745 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1746 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1747 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1748 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1749 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1750 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1751 #if defined(Q_OS_MAC)
1752 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1753 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1755 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1757 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1758 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1760 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1761 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1762 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1763 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1764 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1765 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1766 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1769 void MainWindow::initShowHideMainWidgets()
1771 if (show_hide_actions_) {
1775 show_hide_actions_ = new QActionGroup(this);
1776 QMap<QAction *, QWidget *> shmw_actions;
1778 show_hide_actions_->setExclusive(false);
1779 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1780 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1781 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1782 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1783 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1784 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1785 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1787 foreach (QAction *shmwa, shmw_actions.keys()) {
1788 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1789 show_hide_actions_->addAction(shmwa);
1790 showHideMainWidgets(shmwa);
1793 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1796 Q_DECLARE_METATYPE(ts_type)
1798 void MainWindow::initTimeDisplayFormatMenu()
1800 if (time_display_actions_) {
1804 time_display_actions_ = new QActionGroup(this);
1806 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1807 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1808 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1809 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1810 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1811 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1812 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1813 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1814 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1815 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1817 foreach (QAction* tda, td_actions.keys()) {
1818 tda->setData(qVariantFromValue(td_actions[tda]));
1819 time_display_actions_->addAction(tda);
1822 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1825 Q_DECLARE_METATYPE(ts_precision)
1827 void MainWindow::initTimePrecisionFormatMenu()
1829 if (time_precision_actions_) {
1833 time_precision_actions_ = new QActionGroup(this);
1835 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1836 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1837 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1838 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1839 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1840 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1841 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1843 foreach (QAction* tpa, tp_actions.keys()) {
1844 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1845 time_precision_actions_->addAction(tpa);
1848 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1851 // Menu items which will be disabled when we freeze() and whose state will
1852 // be restored when we thaw(). Add to the list as needed.
1853 void MainWindow::initFreezeActions()
1855 QList<QAction *> freeze_actions = QList<QAction *>()
1856 << main_ui_->actionFileClose
1857 << main_ui_->actionViewReload
1858 << main_ui_->actionEditMarkPacket
1859 << main_ui_->actionEditMarkAllDisplayed
1860 << main_ui_->actionEditUnmarkAllDisplayed
1861 << main_ui_->actionEditIgnorePacket
1862 << main_ui_->actionEditIgnoreAllDisplayed
1863 << main_ui_->actionEditUnignoreAllDisplayed
1864 << main_ui_->actionEditSetTimeReference
1865 << main_ui_->actionEditUnsetAllTimeReferences;
1867 foreach (QAction *action, freeze_actions) {
1868 freeze_actions_ << QPair<QAction *, bool>(action, false);
1873 void MainWindow::setTitlebarForCaptureFile()
1875 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1876 if (capture_file_.capFile()->is_tempfile) {
1878 // For a temporary file, put the source of the data
1879 // in the window title, not whatever random pile
1880 // of characters is the last component of the path
1883 // XXX - on non-Mac platforms, put in the application
1886 setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1889 // For a user file, set the full path; that way,
1890 // for OS X, it'll set the "proxy icon". Qt
1891 // handles extracting the last component.
1893 // Sadly, some UN*Xes don't necessarily use UTF-8
1894 // for their file names, so we have to map the
1895 // file path to UTF-8. If that fails, we're somewhat
1898 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1903 if (utf8_filename) {
1904 QFileInfo fi(utf8_filename);
1905 setWSWindowTitle(fi.fileName());
1906 setWindowFilePath(utf8_filename);
1907 g_free(utf8_filename);
1909 // So what the heck else can we do here?
1910 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1913 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1915 /* We have no capture file. */
1920 QString MainWindow::replaceWindowTitleVariables(QString title)
1922 title.replace ("%P", get_profile_name());
1923 title.replace ("%V", get_ws_vcs_version_info());
1928 void MainWindow::setWSWindowTitle(QString title)
1930 if (title.isEmpty()) {
1931 title = tr("The Wireshark Network Analyzer");
1934 if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
1935 QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
1936 title.prepend(QString("[%1] ").arg(custom_title));
1939 if (prefs.gui_window_title && prefs.gui_window_title[0]) {
1940 QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
1942 // On OS X we separate the titles with a unicode em dash
1943 title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
1945 title.append(QString(" [%1]").arg(custom_title));
1949 setWindowTitle(title);
1950 setWindowFilePath(NULL);
1953 void MainWindow::setTitlebarForCaptureInProgress()
1955 if (capture_file_.capFile()) {
1956 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1958 /* We have no capture in progress. */
1965 /* Enable or disable menu items based on whether you have a capture file
1966 you've finished reading and, if you have one, whether it's been saved
1967 and whether it could be saved except by copying the raw packet data. */
1968 void MainWindow::setMenusForCaptureFile(bool force_disable)
1970 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1971 /* We have no capture file or we're currently reading a file */
1972 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(false);
1973 main_ui_->actionFileMerge->setEnabled(false);
1974 main_ui_->actionFileClose->setEnabled(false);
1975 main_ui_->actionFileSave->setEnabled(false);
1976 main_ui_->actionFileSaveAs->setEnabled(false);
1977 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1978 main_ui_->actionFileExportPackets->setEnabled(false);
1979 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1980 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1981 main_ui_->actionFileExportPDU->setEnabled(false);
1982 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1983 main_ui_->menuFileExportObjects->setEnabled(false);
1984 main_ui_->actionViewReload->setEnabled(false);
1986 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(true);
1987 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1989 main_ui_->actionFileClose->setEnabled(true);
1990 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1991 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1992 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1994 * "Export Specified Packets..." should be available only if
1995 * we can write the file out in at least one format.
1997 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1998 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1999 main_ui_->actionFileExportPacketBytes->setEnabled(true);
2000 main_ui_->actionFileExportPDU->setEnabled(true);
2001 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
2002 main_ui_->menuFileExportObjects->setEnabled(true);
2003 main_ui_->actionViewReload->setEnabled(true);
2007 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
2008 /* Either a capture was started or stopped; in either case, it's not
2009 in the process of stopping, so allow quitting. */
2011 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
2012 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
2013 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
2014 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
2015 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
2016 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
2017 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
2018 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
2019 main_ui_->actionFileQuit->setEnabled(true);
2021 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
2023 // XXX Fix packet list heading menu sensitivity
2024 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2025 // !capture_in_progress);
2026 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2027 // !capture_in_progress);
2028 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2029 // !capture_in_progress);
2032 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
2033 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
2034 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
2035 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
2036 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
2037 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
2038 #endif /* HAVE_LIBPCAP */
2042 void MainWindow::setMenusForCaptureStopping() {
2043 main_ui_->actionFileQuit->setEnabled(false);
2044 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
2046 main_ui_->actionCaptureStart->setChecked(false);
2047 main_ui_->actionCaptureStop->setEnabled(false);
2048 main_ui_->actionCaptureRestart->setEnabled(false);
2049 #endif /* HAVE_LIBPCAP */
2052 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2054 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2056 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2057 // have_captured_packets);
2059 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2060 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2061 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2063 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2064 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2065 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2066 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2067 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2068 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2069 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2071 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2072 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2073 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2074 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2076 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2077 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2078 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2081 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2082 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2083 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2085 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2086 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2087 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2090 void MainWindow::setWindowIcon(const QIcon &icon) {
2091 wsApp->setWindowIcon(icon);
2092 QMainWindow::setWindowIcon(icon);
2095 void MainWindow::updateForUnsavedChanges() {
2096 setTitlebarForCaptureFile();
2097 setMenusForCaptureFile();
2100 void MainWindow::changeEvent(QEvent* event)
2104 switch (event->type())
2106 case QEvent::LanguageChange:
2107 main_ui_->retranslateUi(this);
2108 // make sure that the "Clear Menu" item is retranslated
2109 updateRecentFiles();
2111 case QEvent::LocaleChange:{
2112 QString locale = QLocale::system().name();
2113 locale.truncate(locale.lastIndexOf('_'));
2114 wsApp->loadLanguage(locale);
2121 QMainWindow::changeEvent(event);
2124 /* Update main window items based on whether there's a capture in progress. */
2125 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2127 setMenusForCaptureInProgress(capture_in_progress);
2129 wireless_frame_->setCaptureInProgress(capture_in_progress);
2132 packet_list_->setCaptureInProgress(capture_in_progress);
2133 // set_toolbar_for_capture_in_progress(capture_in_progress);
2135 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2139 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2140 << REGISTER_ANALYZE_GROUP_UNSORTED
2141 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2142 << REGISTER_STAT_GROUP_UNSORTED
2143 << REGISTER_STAT_GROUP_GENERIC
2144 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2145 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2146 << REGISTER_STAT_GROUP_RESPONSE_TIME
2147 << REGISTER_STAT_GROUP_TELEPHONY
2148 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2149 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2150 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2151 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2152 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2153 << REGISTER_TOOLS_GROUP_UNSORTED;
2155 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2157 foreach (QAction *action, actions) {
2158 switch (menu_group) {
2159 case REGISTER_ANALYZE_GROUP_UNSORTED:
2160 case REGISTER_STAT_GROUP_UNSORTED:
2161 main_ui_->menuStatistics->insertAction(
2162 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2165 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2166 main_ui_->menuServiceResponseTime->addAction(action);
2168 case REGISTER_STAT_GROUP_TELEPHONY:
2169 main_ui_->menuTelephony->addAction(action);
2171 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2172 main_ui_->menuANSI->addAction(action);
2174 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2175 main_ui_->menuGSM->addAction(action);
2177 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2178 main_ui_->menuLTE->addAction(action);
2180 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2181 main_ui_->menuMTP3->addAction(action);
2183 case REGISTER_TOOLS_GROUP_UNSORTED:
2185 // Allow the creation of submenus. Mimics the behavor of
2186 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2187 // and GtkUIManager.
2189 // For now we limit the insanity to the "Tools" menu.
2190 QStringList menu_path = action->text().split('/');
2191 QMenu *cur_menu = main_ui_->menuTools;
2192 while (menu_path.length() > 1) {
2193 QString menu_title = menu_path.takeFirst();
2194 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2195 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2197 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2198 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2201 submenu = cur_menu->addMenu(menu_title);
2202 submenu->setObjectName(menu_title.toLower());
2206 action->setText(menu_path.last());
2207 cur_menu->addAction(action);
2211 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2215 // Connect each action type to its corresponding slot. We to
2216 // distinguish various types of actions. Setting their objectName
2217 // seems to work OK.
2218 if (action->objectName() == TapParameterDialog::actionName()) {
2219 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2220 } else if (action->objectName() == FunnelStatistics::actionName()) {
2221 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2225 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2227 foreach (QAction *action, actions) {
2228 switch (menu_group) {
2229 case REGISTER_ANALYZE_GROUP_UNSORTED:
2230 case REGISTER_STAT_GROUP_UNSORTED:
2231 main_ui_->menuStatistics->removeAction(action);
2233 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2234 main_ui_->menuServiceResponseTime->removeAction(action);
2236 case REGISTER_STAT_GROUP_TELEPHONY:
2237 main_ui_->menuTelephony->removeAction(action);
2239 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2240 main_ui_->menuANSI->removeAction(action);
2242 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2243 main_ui_->menuGSM->removeAction(action);
2245 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2246 main_ui_->menuLTE->removeAction(action);
2248 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2249 main_ui_->menuMTP3->removeAction(action);
2251 case REGISTER_TOOLS_GROUP_UNSORTED:
2253 // Allow removal of submenus.
2254 // For now we limit the insanity to the "Tools" menu.
2255 QStringList menu_path = action->text().split('/');
2256 QMenu *cur_menu = main_ui_->menuTools;
2257 while (menu_path.length() > 1) {
2258 QString menu_title = menu_path.takeFirst();
2259 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2260 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2262 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2263 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2267 cur_menu->removeAction(action);
2271 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2277 void MainWindow::addDynamicMenus()
2280 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2281 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2282 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2283 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2284 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2285 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2287 // Fill in each menu
2288 foreach (register_stat_group_t menu_group, menu_groups) {
2289 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2290 addMenuActions(actions, menu_group);
2293 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2294 // We've added a placeholder in order to make sure the "Tools" menu is
2295 // visible. Hide it as needed.
2296 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2297 main_ui_->actionToolsPlaceholder->setVisible(false);
2299 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2300 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2302 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2303 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2305 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2306 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2308 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2309 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2313 void MainWindow::reloadDynamicMenus()
2315 foreach (register_stat_group_t menu_group, menu_groups) {
2316 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2317 removeMenuActions(actions, menu_group);
2319 actions = wsApp->addedMenuGroupItems(menu_group);
2320 addMenuActions(actions, menu_group);
2323 wsApp->clearAddedMenuGroupItems();
2324 wsApp->clearRemovedMenuGroupItems();
2327 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2329 QAction * itemAction = NULL;
2330 ext_menubar_t * item = NULL;
2331 GList * children = NULL;
2333 /* There must exists an xpath parent */
2334 g_assert(subMenu != NULL);
2336 /* If the depth counter exceeds, something must have gone wrong */
2337 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2339 children = menu->children;
2340 /* Iterate the child entries */
2341 while (children && children->data) {
2342 item = (ext_menubar_t *) children->data;
2344 if (item->type == EXT_MENUBAR_MENU) {
2345 /* Handle Submenu entry */
2346 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2347 } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2348 subMenu->addSeparator();
2349 } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2350 itemAction = subMenu->addAction(item->name);
2351 itemAction->setData(QVariant::fromValue((void *)item));
2352 itemAction->setText(item->label);
2353 connect(itemAction, SIGNAL(triggered()),
2354 this, SLOT(externalMenuItem_triggered()));
2358 children = g_list_next(children);
2362 QMenu * MainWindow::searchSubMenu(QString objectName)
2366 if (objectName.length() > 0) {
2367 QString searchName = QString("menu") + objectName;
2369 lst = main_ui_->menuBar->findChildren<QMenu*>();
2370 foreach (QMenu* m, lst) {
2371 if (QString::compare(m->objectName(), searchName) == 0)
2379 void MainWindow::addExternalMenus()
2381 QMenu * subMenu = NULL;
2382 GList * user_menu = NULL;
2383 ext_menu_t * menu = NULL;
2385 user_menu = ext_menubar_get_entries();
2387 while (user_menu && user_menu->data) {
2388 menu = (ext_menu_t *) user_menu->data;
2390 /* On this level only menu items should exist. Not doing an assert here,
2391 * as it could be an honest mistake */
2392 if (menu->type != EXT_MENUBAR_MENU) {
2393 user_menu = g_list_next(user_menu);
2397 /* Create main submenu and add it to the menubar */
2398 if (menu->parent_menu) {
2399 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2401 subMenu = sortUnderneath->addMenu(menu->label);
2405 subMenu = main_ui_->menuBar->addMenu(menu->label);
2407 /* This will generate the action structure for each menu. It is recursive,
2408 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2409 this->externalMenuHelper(menu, subMenu, 0);
2412 user_menu = g_list_next (user_menu);
2422 * indent-tabs-mode: nil
2425 * ex: set shiftwidth=4 tabstop=8 expandtab:
2426 * :indentSize=4:tabSize=8:noTabs=true: