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 <epan/prefs.h>
29 #include <epan/stats_tree_priv.h>
30 #include <epan/plugin_if.h>
33 #include "ui/capture.h"
34 #include <capchild/capture_session.h>
37 #include "ui/alert_box.h"
39 #include "ui/capture_ui_utils.h"
41 #include "ui/capture_globals.h"
42 #include "ui/main_statusbar.h"
43 #include "ui/recent.h"
45 #include "ui/preference_utils.h"
47 #include "byte_view_tab.h"
48 #include "display_filter_edit.h"
49 #include "export_dissection_dialog.h"
50 #include "funnel_statistics.h"
51 #include "import_text_dialog.h"
52 #include "packet_list.h"
53 #include "proto_tree.h"
54 #include "simple_dialog.h"
55 #include "stock_icon.h"
56 #include "tap_parameter_dialog.h"
57 #include "wireless_frame.h"
58 #include "wireshark_application.h"
60 #include "qt_ui_utils.h"
63 #include <QActionGroup>
64 #include <QDesktopWidget>
66 #include <QMessageBox>
67 #include <QMetaObject>
70 #include <QToolButton>
71 #include <QTreeWidget>
74 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
75 #include <QtMacExtras/QMacNativeToolBar>
79 //menu_recent_file_write_all
81 // If we ever add support for multiple windows this will need to be replaced.
82 static MainWindow *gbl_cur_main_window_ = NULL;
84 void pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
86 gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
89 static void plugin_if_mainwindow_apply_filter(gconstpointer user_data)
91 if ( gbl_cur_main_window_ != NULL && user_data != NULL )
93 GHashTable * dataSet = (GHashTable *) user_data;
95 if ( g_hash_table_lookup_extended(dataSet, "filter_string", NULL, NULL ) )
97 QString filter((const char *)g_hash_table_lookup(dataSet, "filter_string"));
98 gbl_cur_main_window_->filterPackets(filter);
103 static void plugin_if_mainwindow_preference(gconstpointer user_data)
105 if ( gbl_cur_main_window_ != NULL && user_data != NULL )
107 GHashTable * dataSet = (GHashTable *) user_data;
108 const char * module_name;
109 const char * pref_name;
110 const char * pref_value;
111 if ( g_hash_table_lookup_extended(dataSet, "pref_module", NULL, (void**)&module_name ) &&
112 g_hash_table_lookup_extended(dataSet, "pref_key", NULL, (void**)&pref_name ) &&
113 g_hash_table_lookup_extended(dataSet, "pref_value", NULL, (void**)&pref_value ) )
115 if ( prefs_store_ext(module_name, pref_name, pref_value) )
117 wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
118 wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
124 static void plugin_if_mainwindow_gotoframe(gconstpointer user_data)
126 if ( gbl_cur_main_window_ != NULL && user_data != NULL )
128 GHashTable * dataSet = (GHashTable *) user_data;
131 if ( g_hash_table_lookup_extended(dataSet, "frame_nr", NULL, &framenr ) )
133 if ( GPOINTER_TO_UINT(framenr) != 0 )
134 gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr));
141 static void plugin_if_mainwindow_get_ws_info(gconstpointer user_data)
143 if (gbl_cur_main_window_ != NULL && user_data != NULL)
145 GHashTable * dataSet = (GHashTable *)user_data;
146 ws_info_t *ws_info = NULL;
148 if (g_hash_table_lookup_extended(dataSet, "ws_info", NULL, (void**)&ws_info))
150 CaptureFile *cfWrap = gbl_cur_main_window_->captureFile();
151 capture_file *cf = cfWrap->capFile();
153 ws_info->ws_info_supported = true;
157 ws_info->cf_state = cf->state;
158 ws_info->cf_count = cf->count;
160 if (ws_info->cf_filename != NULL)
161 g_free(ws_info->cf_filename);
162 ws_info->cf_filename = g_strdup(cf->filename);
164 if (cf->state == FILE_READ_DONE)
166 ws_info->cf_framenr = (cf->current_frame)->num;
167 ws_info->frame_passed_dfilter = ((cf->current_frame)->flags.passed_dfilter) == 0 ? FALSE : TRUE;
171 ws_info->cf_framenr = 0;
172 ws_info->frame_passed_dfilter = FALSE;
175 else if (ws_info->cf_state != FILE_CLOSED)
177 /* Initialise the ws_info structure */
178 ws_info->cf_count = 0;
180 if (ws_info->cf_filename != NULL)
181 g_free(ws_info->cf_filename);
182 ws_info->cf_filename = NULL;
184 ws_info->cf_framenr = 0;
185 ws_info->frame_passed_dfilter = false;
186 ws_info->cf_state = FILE_CLOSED;
192 #endif /* HAVE_LIBPCAP */
195 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
199 va_start(ap, msg_format);
200 SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
208 * Alert box, with optional "don't show this message again" variable
209 * and checkbox, and optional secondary text.
212 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
213 const char *secondary_msg, const char *msg_format, ...)
215 if (notagain && *notagain) {
221 va_start(ap, msg_format);
222 SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
225 sd.setDetailedText(secondary_msg);
227 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
228 QCheckBox *cb = NULL;
230 cb = new QCheckBox();
231 cb->setChecked(true);
232 cb->setText(QObject::tr("Don't show this message again."));
239 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
240 if (notagain && cb) {
241 *notagain = cb->isChecked();
247 * Error alert box, taking a format and a va_list argument.
250 vsimple_error_message_box(const char *msg_format, va_list ap)
252 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
257 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
258 QList<QAction *> actions = parent_menu->actions();
259 QList<QAction *>::const_iterator i;
260 for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
261 if ((*i)->text()==menu_text) {
265 // If we get here there menu entry was not found, add a sub menu
266 return parent_menu->addMenu(menu_text);
269 MainWindow::MainWindow(QWidget *parent) :
271 main_ui_(new Ui::MainWindow),
272 cur_layout_(QVector<unsigned>()),
273 df_combo_box_(new DisplayFilterCombo()),
276 previous_focus_(NULL),
277 show_hide_actions_(NULL),
278 time_display_actions_(NULL),
279 time_precision_actions_(NULL),
280 funnel_statistics_(new FunnelStatistics(this, capture_file_)),
282 capture_stopping_(false),
283 capture_filter_valid_(false),
293 if (!gbl_cur_main_window_) {
294 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
295 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
296 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
297 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
299 gbl_cur_main_window_ = this;
301 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
303 main_ui_->setupUi(this);
304 setWindowIcon(wsApp->normalIcon());
305 setTitlebarForCaptureFile();
306 setMenusForCaptureFile();
307 setForCapturedPackets(false);
308 setMenusForFileSet(false);
309 interfaceSelectionChanged();
310 loadWindowGeometry();
313 main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
316 //To prevent users use features before initialization complete
317 //Otherwise unexpected problems may occur
318 setFeaturesEnabled(false);
319 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
320 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
321 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
322 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
323 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
324 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
326 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
327 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
328 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
329 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
330 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
331 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
333 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
337 connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
338 connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
341 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
342 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
343 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
344 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
345 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
346 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
347 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
348 connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
349 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
350 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
352 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
353 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
354 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
355 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
356 this, SLOT(openCaptureFile(QString,QString)));
357 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
359 initMainToolbarIcons();
361 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
362 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
363 // https://bugreports.qt-project.org/browse/QTBUG-22433
364 // This property is obsolete in Qt5 so this issue may be fixed in that version.
365 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
367 wireless_frame_ = new WirelessFrame(this);
368 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
369 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
370 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
371 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
372 this, SLOT(showPreferencesDialog(QString)));
374 main_ui_->goToFrame->hide();
375 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
376 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
378 // XXX For some reason the cursor is drawn funny with an input mask set
379 // https://bugreports.qt-project.org/browse/QTBUG-7174
381 main_ui_->searchFrame->hide();
382 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
383 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
384 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
385 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
387 main_ui_->addressEditorFrame->hide();
388 main_ui_->columnEditorFrame->hide();
389 main_ui_->preferenceEditorFrame->hide();
390 main_ui_->filterExpressionFrame->hide();
393 main_ui_->menuCapture->setEnabled(false);
396 #if defined(Q_OS_MAC)
397 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
398 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
399 ntb->setIconSize(QSize(24, 24));
400 #endif // QT_MACEXTRAS_LIB
402 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
403 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
404 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
406 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
410 #ifdef HAVE_SOFTWARE_UPDATE
411 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
412 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
413 main_ui_->menuHelp->insertAction(update_sep, update_action);
414 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
416 master_split_.setObjectName("splitterMaster");
417 extra_split_.setObjectName("splitterExtra");
418 main_ui_->mainStack->addWidget(&master_split_);
420 empty_pane_.setObjectName("emptyPane");
422 packet_list_ = new PacketList(&master_split_);
424 proto_tree_ = new ProtoTree(&master_split_);
425 proto_tree_->installEventFilter(this);
427 byte_view_tab_ = new ByteViewTab(&master_split_);
429 packet_list_->setProtoTree(proto_tree_);
430 packet_list_->setByteViewTab(byte_view_tab_);
431 packet_list_->installEventFilter(this);
433 main_welcome_ = main_ui_->welcomePage;
435 // Packet list and proto tree must exist before these are called.
436 setMenusForSelectedPacket();
437 setMenusForSelectedTreeRow();
439 initShowHideMainWidgets();
440 initTimeDisplayFormatMenu();
441 initTimePrecisionFormatMenu();
443 updatePreferenceActions();
444 updateRecentActions();
445 setForCaptureInProgress(false);
447 setTabOrder(df_combo_box_, packet_list_);
449 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
450 this, SLOT(captureCapturePrepared(capture_session *)));
451 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
452 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
453 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
454 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
455 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
456 this, SLOT(captureCaptureFixedStarted(capture_session *)));
457 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
458 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
459 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
460 this, SLOT(captureCaptureFixedFinished(capture_session *)));
461 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
462 this, SLOT(captureCaptureStopping(capture_session *)));
463 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
464 this, SLOT(captureCaptureFailed(capture_session *)));
465 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
466 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
468 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
469 wsApp, SLOT(captureStarted()));
470 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
471 wsApp, SLOT(captureFinished()));
472 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
473 wsApp, SLOT(captureStarted()));
474 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
475 wsApp, SLOT(captureFinished()));
477 connect(&capture_file_, SIGNAL(captureFileOpened()),
478 this, SLOT(captureFileOpened()));
479 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
480 this, SLOT(captureFileReadStarted()));
481 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
482 this, SLOT(captureFileReadFinished()));
483 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
484 this, SLOT(captureFileReloadStarted()));
485 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
486 this, SLOT(captureFileReadFinished()));
487 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
488 this, SLOT(captureFileRescanStarted()));
489 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
490 this, SLOT(captureFileReadFinished()));
491 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
492 this, SLOT(captureFileRetapStarted()));
493 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
494 this, SLOT(captureFileRetapFinished()));
495 connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
496 this, SLOT(captureFileFlushTapsData()));
497 connect(&capture_file_, SIGNAL(captureFileClosing()),
498 this, SLOT(captureFileClosing()));
499 connect(&capture_file_, SIGNAL(captureFileClosed()),
500 this, SLOT(captureFileClosed()));
502 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
503 this, SLOT(captureFileSaveStarted(QString)));
504 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
505 main_ui_->statusBar, SLOT(popFileStatus()));
506 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
507 main_ui_->statusBar, SLOT(popFileStatus()));
508 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
509 main_ui_->statusBar, SLOT(popFileStatus()));
511 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
512 wsApp, SLOT(captureFileReadStarted()));
513 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
514 wsApp, SLOT(updateTaps()));
516 connect(wsApp, SIGNAL(columnsChanged()),
517 packet_list_, SLOT(columnsChanged()));
518 connect(wsApp, SIGNAL(preferencesChanged()),
519 packet_list_, SLOT(preferencesChanged()));
520 connect(wsApp, SIGNAL(recentFilesRead()),
521 this, SLOT(applyRecentPaneGeometry()));
522 connect(wsApp, SIGNAL(recentFilesRead()),
523 this, SLOT(updateRecentActions()));
524 connect(wsApp, SIGNAL(packetDissectionChanged()),
525 this, SLOT(redissectPackets()), Qt::QueuedConnection);
526 connect(wsApp, SIGNAL(appInitialized()),
527 this, SLOT(filterExpressionsChanged()));
528 connect(wsApp, SIGNAL(filterExpressionsChanged()),
529 this, SLOT(filterExpressionsChanged()));
530 connect(wsApp, SIGNAL(checkDisplayFilter()),
531 this, SLOT(checkDisplayFilter()));
532 connect(wsApp, SIGNAL(fieldsChanged()),
533 this, SLOT(fieldsChanged()));
534 connect(wsApp, SIGNAL(reloadLuaPlugins()),
535 this, SLOT(reloadLuaPlugins()));
537 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
538 this, SLOT(mainStackChanged(int)));
540 connect(main_welcome_, SIGNAL(startCapture()),
541 this, SLOT(startCapture()));
542 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
543 this, SLOT(openCaptureFile(QString)));
544 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
545 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
546 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
547 main_ui_->statusBar, SLOT(popFilterStatus()));
549 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
550 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
551 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
552 this, SLOT(redissectPackets()));
553 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
554 this, SLOT(showPreferencesDialog(QString)));
555 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
556 this, SLOT(showPreferencesDialog(QString)));
557 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
558 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
559 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
560 this, SLOT(filterExpressionsChanged()));
562 connect(this, SIGNAL(setCaptureFile(capture_file*)),
563 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
564 connect(this, SIGNAL(setCaptureFile(capture_file*)),
565 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
566 connect(this, SIGNAL(setCaptureFile(capture_file*)),
567 packet_list_, SLOT(setCaptureFile(capture_file*)));
568 connect(this, SIGNAL(setCaptureFile(capture_file*)),
569 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
571 connect(this, SIGNAL(monospaceFontChanged(QFont)),
572 packet_list_, SLOT(setMonospaceFont(QFont)));
573 connect(this, SIGNAL(monospaceFontChanged(QFont)),
574 proto_tree_, SLOT(setMonospaceFont(QFont)));
575 connect(this, SIGNAL(monospaceFontChanged(QFont)),
576 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
578 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
579 packet_list_, SLOT(goNextPacket()));
580 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
581 packet_list_, SLOT(goPreviousPacket()));
582 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
583 packet_list_, SLOT(goFirstPacket()));
584 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
585 packet_list_, SLOT(goLastPacket()));
587 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
588 proto_tree_, SLOT(expandSubtrees()));
589 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
590 proto_tree_, SLOT(expandAll()));
591 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
592 proto_tree_, SLOT(collapseAll()));
594 connect(packet_list_, SIGNAL(packetSelectionChanged()),
595 this, SLOT(setMenusForSelectedPacket()));
596 connect(packet_list_, SIGNAL(packetDissectionChanged()),
597 this, SLOT(redissectPackets()));
598 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
599 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
600 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
601 this, SLOT(showPreferencesDialog(QString)));
602 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
603 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
604 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
605 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
606 packet_list_, SLOT(columnsChanged()));
607 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
608 this, SLOT(openPacketDialog()));
609 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
610 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
611 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
612 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
613 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
614 main_ui_->statusBar, SLOT(popBusyStatus()));
615 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
616 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
617 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
618 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
619 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
620 main_ui_->statusBar, SLOT(popProgressStatus()));
622 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
623 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
624 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
625 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
626 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
627 this, SLOT(openPacketDialog(bool)));
628 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
629 this, SLOT(showPreferencesDialog(QString)));
630 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
631 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
633 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
634 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
636 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
637 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
639 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
640 &capture_file_, SLOT(stopLoading()));
642 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
643 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
645 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
646 this, SLOT(openCaptureFile(QString)));
649 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
651 connect(iface_tree, SIGNAL(itemSelectionChanged()),
652 this, SLOT(interfaceSelectionChanged()));
654 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
655 this, SLOT(captureFilterSyntaxChanged(bool)));
658 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
659 this, SLOT(showExtcapOptionsDialog(QString&)));
662 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
663 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
664 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
665 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
666 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
667 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
670 /* Create plugin_if hooks */
671 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter );
672 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter );
673 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
674 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
676 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
679 main_ui_->mainStack->setCurrentWidget(main_welcome_);
682 MainWindow::~MainWindow()
687 QString MainWindow::getFilter()
689 return df_combo_box_->currentText();
692 QMenu *MainWindow::createPopupMenu()
694 QMenu *menu = new QMenu();
695 menu->addAction(main_ui_->actionViewMainToolbar);
696 menu->addAction(main_ui_->actionViewFilterToolbar);
697 menu->addAction(main_ui_->actionViewWirelessToolbar);
698 menu->addAction(main_ui_->actionViewStatusBar);
699 menu->addSeparator();
700 menu->addAction(main_ui_->actionViewPacketList);
701 menu->addAction(main_ui_->actionViewPacketDetails);
702 menu->addAction(main_ui_->actionViewPacketBytes);
706 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
708 pipe_source_ = source;
709 pipe_child_process_ = child_process;
710 pipe_user_data_ = user_data;
711 pipe_input_cb_ = input_cb;
714 /* Tricky to use pipes in win9x, as no concept of wait. NT can
715 do this but that doesn't cover all win32 platforms. GTK can do
716 this but doesn't seem to work over processes. Attempt to do
717 something similar here, start a timer and check for data on every
719 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
722 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
726 pipe_timer_ = new QTimer(this);
727 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
728 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
729 pipe_timer_->start(200);
731 if (pipe_notifier_) {
732 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
733 delete pipe_notifier_;
736 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
737 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
738 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
739 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
743 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
745 // The user typed some text. Start filling in a filter.
746 // We may need to be more choosy here. We just need to catch events for the packet list,
747 // proto tree, and main welcome widgets.
748 if (event->type() == QEvent::KeyPress) {
749 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
750 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
751 df_combo_box_->lineEdit()->insert(kevt->text());
752 df_combo_box_->lineEdit()->setFocus();
757 return QMainWindow::eventFilter(obj, event);
760 void MainWindow::keyPressEvent(QKeyEvent *event) {
762 // Explicitly focus on the display filter combo.
763 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
764 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
768 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
769 if (event->modifiers() == Qt::NoModifier) {
770 if (event->key() == Qt::Key_Escape) {
771 on_goToCancel_clicked();
772 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
776 return; // goToLineEdit didn't want it and we don't either.
779 // Move up & down the packet list.
780 if (event->key() == Qt::Key_F7) {
781 packet_list_->goPreviousPacket();
782 } else if (event->key() == Qt::Key_F8) {
783 packet_list_->goNextPacket();
786 // Move along, citizen.
787 QMainWindow::keyPressEvent(event);
790 void MainWindow::closeEvent(QCloseEvent *event) {
791 saveWindowGeometry();
793 /* If we're in the middle of stopping a capture, don't do anything;
794 the user can try deleting the window after the capture stops. */
795 if (capture_stopping_) {
800 QString before_what(tr(" before quitting"));
801 if (!testCaptureFileClose(before_what, QuitButtons)) {
807 capture_interfaces_dialog_.close();
809 // Make sure we kill any open dumpcap processes.
810 delete main_welcome_;
812 // One of the many places we assume one main window.
813 if(!wsApp->isInitialized()) {
814 // If we're still initializing, QCoreApplication::quit() won't
815 // exit properly because we are not in the event loop. This
816 // means that the application won't clean up after itself. We
817 // might want to call wsApp->processEvents() during startup
818 // instead so that we can do a normal exit here.
824 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
827 foreach (QUrl drag_url, event->mimeData()->urls()) {
828 if (!drag_url.toLocalFile().isEmpty()) {
833 if (accept) event->acceptProposedAction();
836 void MainWindow::dropEvent(QDropEvent *event)
838 foreach (QUrl drop_url, event->mimeData()->urls()) {
839 QString local_file = drop_url.toLocalFile();
840 if (!local_file.isEmpty()) {
841 event->acceptProposedAction();
842 openCaptureFile(local_file);
848 // Apply recent settings to the main window geometry.
849 // We haven't loaded the preferences at this point so we assume that the
850 // position and size preference are enabled.
851 void MainWindow::loadWindowGeometry()
853 int min_sensible_dimension = 200;
856 if (recent.gui_geometry_main_maximized) {
857 setWindowState(Qt::WindowMaximized);
861 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
862 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
863 if (!rect_on_screen(recent_geom)) {
864 // We're not visible on any screens. Give up and use the default geometry.
868 // if (prefs.gui_geometry_save_position) {
869 move(recent_geom.topLeft());
872 if (// prefs.gui_geometry_save_size &&
873 recent_geom.width() > min_sensible_dimension &&
874 recent_geom.height() > min_sensible_dimension) {
875 resize(recent_geom.size());
880 void MainWindow::saveWindowGeometry()
882 if (prefs.gui_geometry_save_position) {
883 recent.gui_geometry_main_x = pos().x();
884 recent.gui_geometry_main_y = pos().y();
887 if (prefs.gui_geometry_save_size) {
888 recent.gui_geometry_main_width = size().width();
889 recent.gui_geometry_main_height = size().height();
892 if (prefs.gui_geometry_save_maximized) {
893 // On OS X this is false when it shouldn't be
894 recent.gui_geometry_main_maximized = isMaximized();
897 if (master_split_.sizes().length() > 0) {
898 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
901 if (master_split_.sizes().length() > 2) {
902 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
903 } else if (extra_split_.sizes().length() > 0) {
904 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
908 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
910 case layout_pane_content_none:
912 case layout_pane_content_plist:
914 case layout_pane_content_pdetails:
916 case layout_pane_content_pbytes:
917 return byte_view_tab_;
919 g_assert_not_reached();
924 // Our event loop becomes nested whenever we call update_progress_dlg, which
925 // includes several places in file.c. The GTK+ UI stays out of trouble by
926 // showing a modal progress dialog. We attempt to do the equivalent below by
927 // disabling parts of the main window. At a minumum the ProgressFrame in the
928 // main status bar must remain accessible.
930 // We might want to do this any time the main status bar progress frame is
932 void MainWindow::freeze()
934 freeze_focus_ = wsApp->focusWidget();
936 // XXX Alternatively we could just disable and enable the main menu.
937 for (int i = 0; i < freeze_actions_.size(); i++) {
938 QAction *action = freeze_actions_[i].first;
939 freeze_actions_[i].second = action->isEnabled();
940 action->setEnabled(false);
942 main_ui_->centralWidget->setEnabled(false);
945 void MainWindow::thaw()
947 main_ui_->centralWidget->setEnabled(true);
948 for (int i = 0; i < freeze_actions_.size(); i++) {
949 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
952 if (freeze_focus_) freeze_focus_->setFocus();
953 freeze_focus_ = NULL;
956 void MainWindow::mergeCaptureFile()
958 QString file_name = "";
959 QString read_filter = "";
960 dfilter_t *rfcode = NULL;
963 if (!capture_file_.capFile())
966 if (prefs.gui_ask_unsaved) {
967 if (cf_has_unsaved_data(capture_file_.capFile())) {
968 QMessageBox msg_dialog;
969 gchar *display_basename;
972 msg_dialog.setIcon(QMessageBox::Question);
973 /* This file has unsaved data; ask the user whether to save
975 if (capture_file_.capFile()->is_tempfile) {
976 msg_dialog.setText(tr("Save packets before merging?"));
977 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
980 * Format the message.
982 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
983 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
984 g_free(display_basename);
985 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
988 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
989 msg_dialog.setDefaultButton(QMessageBox::Save);
991 response = msg_dialog.exec();
995 case QMessageBox::Save:
996 /* Save the file but don't close it */
997 saveCaptureFile(capture_file_.capFile(), FALSE);
1000 case QMessageBox::Cancel:
1002 /* Don't do the merge. */
1009 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1011 cf_status_t merge_status;
1012 char *in_filenames[2];
1015 switch (prefs.gui_fileopen_style) {
1017 case FO_STYLE_LAST_OPENED:
1018 /* The user has specified that we should start out in the last directory
1019 we looked in. If we've already opened a file, use its containing
1020 directory, if we could determine it, as the directory, otherwise
1021 use the "last opened" directory saved in the preferences file if
1023 /* This is now the default behaviour in file_selection_new() */
1026 case FO_STYLE_SPECIFIED:
1027 /* The user has specified that we should always start out in a
1028 specified directory; if they've specified that directory,
1029 start out by showing the files in that dir. */
1030 if (prefs.gui_fileopen_dir[0] != '\0')
1031 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
1035 if (merge_dlg.merge(file_name)) {
1038 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1039 /* Not valid. Tell the user, and go back and run the file
1040 selection box again once they dismiss the alert. */
1041 //bad_dfilter_alert_box(top_level, read_filter->str);
1042 QMessageBox::warning(this, tr("Invalid Read Filter"),
1043 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1052 file_type = capture_file_.capFile()->cd_t;
1054 /* Try to merge or append the two files */
1056 if (merge_dlg.mergeType() == 0) {
1057 /* chronological order */
1058 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1059 in_filenames[1] = qstring_strdup(file_name);
1060 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
1061 } else if (merge_dlg.mergeType() <= 0) {
1063 in_filenames[0] = qstring_strdup(file_name);
1064 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1065 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1068 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1069 in_filenames[1] = qstring_strdup(file_name);
1070 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1073 g_free(in_filenames[0]);
1074 g_free(in_filenames[1]);
1076 if (merge_status != CF_OK) {
1078 dfilter_free(rfcode);
1083 cf_close(capture_file_.capFile());
1085 /* Try to open the merged capture file. */
1086 CaptureFile::globalCapFile()->window = this;
1087 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1088 /* We couldn't open it; fail. */
1089 CaptureFile::globalCapFile()->window = NULL;
1091 dfilter_free(rfcode);
1096 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1097 it closed the previous capture file, and thus destroyed any
1098 previous read filter attached to "cf"). */
1099 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1101 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1105 /* Just because we got an error, that doesn't mean we were unable
1106 to read any of the file; we handle what we could get from the
1110 case CF_READ_ABORTED:
1111 /* The user bailed out of re-reading the capture file; the
1112 capture file has been closed - just free the capture file name
1113 string and return (without changing the last containing
1119 /* Save the name of the containing directory specified in the path name,
1120 if any; we can write over cf_merged_name, which is a good thing, given that
1121 "get_dirname()" does write over its argument. */
1122 wsApp->setLastOpenDir(get_dirname(tmpname));
1124 main_ui_->statusBar->showExpert();
1130 void MainWindow::importCaptureFile() {
1131 ImportTextDialog import_dlg;
1133 QString before_what(tr(" before importing a new capture"));
1134 if (!testCaptureFileClose(before_what))
1139 if (import_dlg.result() != QDialog::Accepted) {
1140 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1144 openCaptureFile(import_dlg.capfileName());
1147 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1149 gboolean discard_comments;
1151 if (cf->is_tempfile) {
1152 /* This is a temporary capture file, so saving it means saving
1153 it to a permanent file. Prompt the user for a location
1154 to which to save it. Don't require that the file format
1155 support comments - if it's a temporary capture file, it's
1156 probably pcap-ng, which supports comments and, if it's
1157 not pcap-ng, let the user decide what they want to do
1158 if they've added comments. */
1159 saveAsCaptureFile(cf, FALSE, dont_reopen);
1161 if (cf->unsaved_changes) {
1162 cf_write_status_t status;
1164 /* This is not a temporary capture file, but it has unsaved
1165 changes, so saving it means doing a "safe save" on top
1166 of the existing file, in the same format - no UI needed
1167 unless the file has comments and the file's format doesn't
1170 If the file has comments, does the file's format support them?
1171 If not, ask the user whether they want to discard the comments
1172 or choose a different format. */
1173 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1176 /* The file can be saved in the specified format as is;
1177 just drive on and save in the format they selected. */
1178 discard_comments = FALSE;
1181 case SAVE_WITHOUT_COMMENTS:
1182 /* The file can't be saved in the specified format as is,
1183 but it can be saved without the comments, and the user
1184 said "OK, discard the comments", so save it in the
1185 format they specified without the comments. */
1186 discard_comments = TRUE;
1189 case SAVE_IN_ANOTHER_FORMAT:
1190 /* There are file formats in which we can save this that
1191 support comments, and the user said not to delete the
1192 comments. Do a "Save As" so the user can select
1193 one of those formats and choose a file name. */
1194 saveAsCaptureFile(cf, TRUE, dont_reopen);
1198 /* The user said "forget it". Just return. */
1202 /* Squelch warnings that discard_comments is being used
1204 g_assert_not_reached();
1208 /* XXX - cf->filename might get freed out from under us, because
1209 the code path through which cf_save_records() goes currently
1210 closes the current file and then opens and reloads the saved file,
1211 so make a copy and free it later. */
1212 file_name = cf->filename;
1213 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1214 discard_comments, dont_reopen);
1218 /* The save succeeded; we're done.
1219 If we discarded comments, redraw the packet list to reflect
1220 any packets that no longer have comments. */
1221 if (discard_comments)
1222 packet_list_queue_draw();
1224 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1225 updateForUnsavedChanges(); // we update the title bar to remove the *
1228 case CF_WRITE_ERROR:
1229 /* The write failed.
1230 XXX - OK, what do we do now? Let them try a
1231 "Save As", in case they want to try to save to a
1232 different directory r file system? */
1235 case CF_WRITE_ABORTED:
1236 /* The write was aborted; just drive on. */
1240 /* Otherwise just do nothing. */
1244 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1245 QString file_name = "";
1247 gboolean compressed;
1248 cf_write_status_t status;
1250 gboolean discard_comments = FALSE;
1257 CaptureFileDialog save_as_dlg(this, cf);
1259 switch (prefs.gui_fileopen_style) {
1261 case FO_STYLE_LAST_OPENED:
1262 /* The user has specified that we should start out in the last directory
1263 we looked in. If we've already opened a file, use its containing
1264 directory, if we could determine it, as the directory, otherwise
1265 use the "last opened" directory saved in the preferences file if
1267 /* This is now the default behaviour in file_selection_new() */
1270 case FO_STYLE_SPECIFIED:
1271 /* The user has specified that we should always start out in a
1272 specified directory; if they've specified that directory,
1273 start out by showing the files in that dir. */
1274 if (prefs.gui_fileopen_dir[0] != '\0')
1275 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1279 /* If the file has comments, does the format the user selected
1280 support them? If not, ask the user whether they want to
1281 discard the comments or choose a different format. */
1282 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1285 /* The file can be saved in the specified format as is;
1286 just drive on and save in the format they selected. */
1287 discard_comments = FALSE;
1290 case SAVE_WITHOUT_COMMENTS:
1291 /* The file can't be saved in the specified format as is,
1292 but it can be saved without the comments, and the user
1293 said "OK, discard the comments", so save it in the
1294 format they specified without the comments. */
1295 discard_comments = TRUE;
1298 case SAVE_IN_ANOTHER_FORMAT:
1299 /* There are file formats in which we can save this that
1300 support comments, and the user said not to delete the
1301 comments. The combo box of file formats has had the
1302 formats that don't support comments trimmed from it,
1303 so run the dialog again, to let the user decide
1304 whether to save in one of those formats or give up. */
1305 must_support_comments = TRUE;
1309 /* The user said "forget it". Just get rid of the dialog box
1313 file_type = save_as_dlg.selectedFileType();
1314 compressed = save_as_dlg.isCompressed();
1316 fileAddExtension(file_name, file_type, compressed);
1319 // /* If the file exists and it's user-immutable or not writable,
1320 // ask the user whether they want to override that. */
1321 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1322 // /* They don't. Let them try another file name or cancel. */
1327 /* Attempt to save the file */
1328 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1329 discard_comments, dont_reopen);
1333 /* The save succeeded; we're done. */
1334 /* Save the directory name for future file dialogs. */
1335 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1336 set_last_open_dir(dirname);
1338 /* If we discarded comments, redraw the packet list to reflect
1339 any packets that no longer have comments. */
1340 if (discard_comments)
1341 packet_list_queue_draw();
1343 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1344 updateForUnsavedChanges(); // we update the title bar to remove the *
1347 case CF_WRITE_ERROR:
1348 /* The save failed; let the user try again. */
1351 case CF_WRITE_ABORTED:
1352 /* The user aborted the save; just return. */
1359 void MainWindow::exportSelectedPackets() {
1360 QString file_name = "";
1362 gboolean compressed;
1363 packet_range_t range;
1364 cf_write_status_t status;
1366 gboolean discard_comments = FALSE;
1368 if (!capture_file_.capFile())
1371 /* Init the packet range */
1372 packet_range_init(&range, capture_file_.capFile());
1373 range.process_filtered = TRUE;
1374 range.include_dependents = TRUE;
1377 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1379 switch (prefs.gui_fileopen_style) {
1381 case FO_STYLE_LAST_OPENED:
1382 /* The user has specified that we should start out in the last directory
1383 we looked in. If we've already opened a file, use its containing
1384 directory, if we could determine it, as the directory, otherwise
1385 use the "last opened" directory saved in the preferences file if
1387 /* This is now the default behaviour in file_selection_new() */
1390 case FO_STYLE_SPECIFIED:
1391 /* The user has specified that we should always start out in a
1392 specified directory; if they've specified that directory,
1393 start out by showing the files in that dir. */
1394 if (prefs.gui_fileopen_dir[0] != '\0')
1395 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1399 /* If the file has comments, does the format the user selected
1400 support them? If not, ask the user whether they want to
1401 discard the comments or choose a different format. */
1402 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1405 /* The file can be saved in the specified format as is;
1406 just drive on and save in the format they selected. */
1407 discard_comments = FALSE;
1410 case SAVE_WITHOUT_COMMENTS:
1411 /* The file can't be saved in the specified format as is,
1412 but it can be saved without the comments, and the user
1413 said "OK, discard the comments", so save it in the
1414 format they specified without the comments. */
1415 discard_comments = TRUE;
1418 case SAVE_IN_ANOTHER_FORMAT:
1419 /* There are file formats in which we can save this that
1420 support comments, and the user said not to delete the
1421 comments. The combo box of file formats has had the
1422 formats that don't support comments trimmed from it,
1423 so run the dialog again, to let the user decide
1424 whether to save in one of those formats or give up. */
1428 /* The user said "forget it". Just get rid of the dialog box
1434 * Check that we're not going to save on top of the current
1436 * We do it here so we catch all cases ...
1437 * Unfortunately, the file requester gives us an absolute file
1438 * name and the read file name may be relative (if supplied on
1439 * the command line). From Joerg Mayer.
1441 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1442 QMessageBox msg_box;
1443 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1445 msg_box.setIcon(QMessageBox::Critical);
1446 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1447 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1448 msg_box.setStandardButtons(QMessageBox::Ok);
1449 msg_box.setDefaultButton(QMessageBox::Ok);
1451 g_free(display_basename);
1455 file_type = esp_dlg.selectedFileType();
1456 compressed = esp_dlg.isCompressed();
1457 fileAddExtension(file_name, file_type, compressed);
1460 // /* If the file exists and it's user-immutable or not writable,
1461 // ask the user whether they want to override that. */
1462 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1463 // /* They don't. Let them try another file name or cancel. */
1468 /* Attempt to save the file */
1469 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1473 /* The save succeeded; we're done. */
1474 /* Save the directory name for future file dialogs. */
1475 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1476 set_last_open_dir(dirname);
1478 /* If we discarded comments, redraw the packet list to reflect
1479 any packets that no longer have comments. */
1480 if (discard_comments)
1481 packet_list_queue_draw();
1484 case CF_WRITE_ERROR:
1485 /* The save failed; let the user try again. */
1488 case CF_WRITE_ABORTED:
1489 /* The user aborted the save; just return. */
1496 void MainWindow::exportDissections(export_type_e export_type) {
1497 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1498 packet_range_t range;
1500 if (!capture_file_.capFile())
1503 /* Init the packet range */
1504 packet_range_init(&range, capture_file_.capFile());
1505 range.process_filtered = TRUE;
1506 range.include_dependents = TRUE;
1511 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1512 QString file_name_lower;
1513 QString file_suffix;
1514 GSList *extensions_list;
1515 gboolean add_extension;
1518 * Append the default file extension if there's none given by
1519 * the user or if they gave one that's not one of the valid
1520 * extensions for the file type.
1522 file_name_lower = file_name.toLower();
1523 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1524 if (extensions_list != NULL) {
1527 /* We have one or more extensions for this file type.
1528 Start out assuming we need to add the default one. */
1529 add_extension = TRUE;
1531 /* OK, see if the file has one of those extensions. */
1532 for (extension = extensions_list; extension != NULL;
1533 extension = g_slist_next(extension)) {
1534 file_suffix += tr(".") + (char *)extension->data;
1535 if (file_name_lower.endsWith(file_suffix)) {
1537 * The file name has one of the extensions for
1540 add_extension = FALSE;
1543 file_suffix += ".gz";
1544 if (file_name_lower.endsWith(file_suffix)) {
1546 * The file name has one of the extensions for
1549 add_extension = FALSE;
1554 /* We have no extensions for this file type. Don't add one. */
1555 add_extension = FALSE;
1557 if (add_extension) {
1558 if (wtap_default_file_extension(file_type) != NULL) {
1559 file_name += tr(".") + wtap_default_file_extension(file_type);
1567 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseButtons buttons) {
1568 bool capture_in_progress = false;
1569 bool do_close_file = false;
1571 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1572 return true; /* Already closed, nothing to do */
1575 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1576 /* This is true if we're reading a capture file *or* if we're doing
1577 a live capture. If we're reading a capture file, the main loop
1578 is busy reading packets, and only accepting input from the
1579 progress dialog, so we can't get here, so this means we're
1581 capture_in_progress = true;
1585 if (prefs.gui_ask_unsaved) {
1586 if (cf_has_unsaved_data(capture_file_.capFile()) ||
1587 (capture_in_progress && capture_file_.capFile()->count > 0))
1589 QMessageBox msg_dialog;
1591 QPushButton *saveButton;
1592 QPushButton *discardButton;
1594 msg_dialog.setIcon(QMessageBox::Question);
1595 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1596 /* This file has unsaved data or there's a capture in
1597 progress; ask the user whether to save the data. */
1598 if (capture_file_.capFile()->is_tempfile) {
1600 msg_dialog.setText(tr("You have unsaved packets"));
1601 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1603 if (capture_in_progress) {
1604 question.append(tr("Do you want to stop the capture and save the captured packets"));
1606 question.append(tr("Do you want to save the captured packets"));
1608 question.append(before_what).append(tr("?"));
1609 msg_dialog.setInformativeText(question);
1614 * Format the message.
1616 if (capture_in_progress) {
1617 question.append(tr("Do you want to stop the capture and save the captured packets"));
1618 question.append(before_what).append(tr("?"));
1619 msg_dialog.setText(question);
1620 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1622 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1623 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1624 .arg(display_basename)
1627 g_free(display_basename);
1628 msg_dialog.setText(question);
1629 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1633 // XXX Text comes from ui/gtk/stock_icons.[ch]
1634 // Note that the button roles differ from the GTK+ version.
1635 // Cancel = RejectRole
1636 // Save = AcceptRole
1637 // Don't Save = DestructiveRole
1638 msg_dialog.addButton(QMessageBox::Cancel);
1640 if (capture_in_progress) {
1641 QString saveButtonText;
1642 if (buttons == RestartButtons) {
1643 saveButtonText = tr("Save before Continue");
1645 saveButtonText = tr("Stop and Save");
1647 saveButton = msg_dialog.addButton(saveButtonText, QMessageBox::AcceptRole);
1649 saveButton = msg_dialog.addButton(QMessageBox::Save);
1651 msg_dialog.setDefaultButton(saveButton);
1653 QString discardButtonText;
1654 if (capture_in_progress) {
1657 discardButtonText = tr("Stop and Quit without Saving");
1659 case RestartButtons:
1660 discardButtonText = tr("Continue without Saving");
1663 discardButtonText = tr("Stop and Continue without Saving");
1669 discardButtonText = tr("Quit without Saving");
1671 case RestartButtons:
1673 discardButtonText = tr("Continue without Saving");
1677 discardButton = msg_dialog.addButton(discardButtonText, QMessageBox::DestructiveRole);
1680 /* According to the Qt doc:
1681 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1683 * Therefore we should use clickedButton() to determine which button was clicked. */
1685 if (msg_dialog.clickedButton() == saveButton) {
1687 /* If there's a capture in progress, we have to stop the capture
1688 and then do the save. */
1689 if (capture_in_progress)
1692 /* Save the file and close it */
1693 saveCaptureFile(capture_file_.capFile(), true);
1694 } else if(msg_dialog.clickedButton() == discardButton) {
1695 /* Just close the file, discarding changes */
1696 do_close_file = true;
1698 // cancelButton or some other unspecified button
1702 /* Unchanged file or capturing with no packets */
1703 do_close_file = true;
1706 /* User asked not to be bothered by those prompts, just close it.
1707 XXX - should that apply only to saving temporary files? */
1708 do_close_file = true;
1711 if (do_close_file) {
1713 /* If there's a capture in progress, we have to stop the capture
1714 and then do the close. */
1715 if (capture_in_progress)
1718 /* captureStop() will close the file if not having any packets */
1719 if (capture_file_.capFile())
1720 cf_close(capture_file_.capFile());
1723 return true; /* File closed */
1726 void MainWindow::captureStop() {
1729 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1730 WiresharkApplication::processEvents();
1734 void MainWindow::initMainToolbarIcons()
1736 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1737 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1738 #if !defined(Q_OS_WIN)
1739 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1740 // The OS X HIG specifies 32-pixel icons but they're a little too
1742 icon_size = icon_size * 3 / 2;
1744 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1746 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1747 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1749 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1750 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1751 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1752 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1754 // Menu icons are disabled in main_window.ui for these items.
1755 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1756 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1757 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1758 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1760 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1761 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1762 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1763 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1764 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1765 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1766 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1767 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1768 #if defined(Q_OS_MAC)
1769 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1770 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1772 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1774 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1775 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1777 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1778 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1779 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1780 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1781 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1782 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1783 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1786 void MainWindow::initShowHideMainWidgets()
1788 if (show_hide_actions_) {
1792 show_hide_actions_ = new QActionGroup(this);
1793 QMap<QAction *, QWidget *> shmw_actions;
1795 show_hide_actions_->setExclusive(false);
1796 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1797 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1798 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1799 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1800 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1801 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1802 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1804 foreach (QAction *shmwa, shmw_actions.keys()) {
1805 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1806 show_hide_actions_->addAction(shmwa);
1807 showHideMainWidgets(shmwa);
1810 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1813 Q_DECLARE_METATYPE(ts_type)
1815 void MainWindow::initTimeDisplayFormatMenu()
1817 if (time_display_actions_) {
1821 time_display_actions_ = new QActionGroup(this);
1823 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1824 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1825 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1826 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1827 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1828 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1829 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1830 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1831 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1832 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1834 foreach (QAction* tda, td_actions.keys()) {
1835 tda->setData(qVariantFromValue(td_actions[tda]));
1836 time_display_actions_->addAction(tda);
1839 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1842 Q_DECLARE_METATYPE(ts_precision)
1844 void MainWindow::initTimePrecisionFormatMenu()
1846 if (time_precision_actions_) {
1850 time_precision_actions_ = new QActionGroup(this);
1852 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1853 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1854 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1855 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1856 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1857 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1858 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1860 foreach (QAction* tpa, tp_actions.keys()) {
1861 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1862 time_precision_actions_->addAction(tpa);
1865 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1868 // Menu items which will be disabled when we freeze() and whose state will
1869 // be restored when we thaw(). Add to the list as needed.
1870 void MainWindow::initFreezeActions()
1872 QList<QAction *> freeze_actions = QList<QAction *>()
1873 << main_ui_->actionFileClose
1874 << main_ui_->actionViewReload
1875 << main_ui_->actionEditMarkPacket
1876 << main_ui_->actionEditMarkAllDisplayed
1877 << main_ui_->actionEditUnmarkAllDisplayed
1878 << main_ui_->actionEditIgnorePacket
1879 << main_ui_->actionEditIgnoreAllDisplayed
1880 << main_ui_->actionEditUnignoreAllDisplayed
1881 << main_ui_->actionEditSetTimeReference
1882 << main_ui_->actionEditUnsetAllTimeReferences;
1884 foreach (QAction *action, freeze_actions) {
1885 freeze_actions_ << QPair<QAction *, bool>(action, false);
1890 void MainWindow::setTitlebarForCaptureFile()
1892 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1893 if (capture_file_.capFile()->is_tempfile) {
1895 // For a temporary file, put the source of the data
1896 // in the window title, not whatever random pile
1897 // of characters is the last component of the path
1900 // XXX - on non-Mac platforms, put in the application
1903 setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1906 // For a user file, set the full path; that way,
1907 // for OS X, it'll set the "proxy icon". Qt
1908 // handles extracting the last component.
1910 // Sadly, some UN*Xes don't necessarily use UTF-8
1911 // for their file names, so we have to map the
1912 // file path to UTF-8. If that fails, we're somewhat
1915 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1920 if (utf8_filename) {
1921 QFileInfo fi(utf8_filename);
1922 setWSWindowTitle(fi.fileName());
1923 setWindowFilePath(utf8_filename);
1924 g_free(utf8_filename);
1926 // So what the heck else can we do here?
1927 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1930 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1932 /* We have no capture file. */
1937 void MainWindow::setWSWindowTitle(QString title)
1939 if (title.isEmpty()) {
1940 title = tr("The Wireshark Network Analyzer");
1943 if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
1944 title.prepend(QString("[%1] ").arg(prefs.gui_prepend_window_title));
1947 if (prefs.gui_window_title && prefs.gui_window_title[0]) {
1949 // On OS X we separate the titles with a unicode em dash
1950 title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(prefs.gui_window_title));
1952 title.append(QString(" [%1]").arg(prefs.gui_window_title));
1956 setWindowTitle(title);
1957 setWindowFilePath(NULL);
1960 void MainWindow::setTitlebarForCaptureInProgress()
1962 if (capture_file_.capFile()) {
1963 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1965 /* We have no capture in progress. */
1972 /* Enable or disable menu items based on whether you have a capture file
1973 you've finished reading and, if you have one, whether it's been saved
1974 and whether it could be saved except by copying the raw packet data. */
1975 void MainWindow::setMenusForCaptureFile(bool force_disable)
1977 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1978 /* We have no capture file or we're currently reading a file */
1979 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(false);
1980 main_ui_->actionFileMerge->setEnabled(false);
1981 main_ui_->actionFileClose->setEnabled(false);
1982 main_ui_->actionFileSave->setEnabled(false);
1983 main_ui_->actionFileSaveAs->setEnabled(false);
1984 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1985 main_ui_->actionFileExportPackets->setEnabled(false);
1986 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1987 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1988 main_ui_->actionFileExportPDU->setEnabled(false);
1989 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1990 main_ui_->menuFileExportObjects->setEnabled(false);
1991 main_ui_->actionViewReload->setEnabled(false);
1993 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(true);
1994 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1996 main_ui_->actionFileClose->setEnabled(true);
1997 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1998 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1999 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
2001 * "Export Specified Packets..." should be available only if
2002 * we can write the file out in at least one format.
2004 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
2005 main_ui_->menuFileExportPacketDissections->setEnabled(true);
2006 main_ui_->actionFileExportPacketBytes->setEnabled(true);
2007 main_ui_->actionFileExportPDU->setEnabled(true);
2008 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
2009 main_ui_->menuFileExportObjects->setEnabled(true);
2010 main_ui_->actionViewReload->setEnabled(true);
2014 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
2015 /* Either a capture was started or stopped; in either case, it's not
2016 in the process of stopping, so allow quitting. */
2018 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
2019 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
2020 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
2021 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
2022 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
2023 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
2024 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
2025 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
2026 main_ui_->actionFileQuit->setEnabled(true);
2028 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
2030 // XXX Fix packet list heading menu sensitivity
2031 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2032 // !capture_in_progress);
2033 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2034 // !capture_in_progress);
2035 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2036 // !capture_in_progress);
2039 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
2040 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
2041 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
2042 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
2043 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
2044 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
2045 #endif /* HAVE_LIBPCAP */
2049 void MainWindow::setMenusForCaptureStopping() {
2050 main_ui_->actionFileQuit->setEnabled(false);
2051 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
2053 main_ui_->actionCaptureStart->setChecked(false);
2054 main_ui_->actionCaptureStop->setEnabled(false);
2055 main_ui_->actionCaptureRestart->setEnabled(false);
2056 #endif /* HAVE_LIBPCAP */
2059 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2061 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2063 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2064 // have_captured_packets);
2066 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2067 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2068 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2070 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2071 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2072 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2073 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2074 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2075 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2076 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2078 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2079 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2080 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2081 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2083 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2084 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2085 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2088 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2089 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2090 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2092 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2093 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2094 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2097 void MainWindow::setWindowIcon(const QIcon &icon) {
2098 wsApp->setWindowIcon(icon);
2099 QMainWindow::setWindowIcon(icon);
2102 void MainWindow::updateForUnsavedChanges() {
2103 setTitlebarForCaptureFile();
2104 setMenusForCaptureFile();
2107 void MainWindow::changeEvent(QEvent* event)
2111 switch (event->type())
2113 case QEvent::LanguageChange:
2114 main_ui_->retranslateUi(this);
2115 // make sure that the "Clear Menu" item is retranslated
2116 updateRecentFiles();
2118 case QEvent::LocaleChange:{
2119 QString locale = QLocale::system().name();
2120 locale.truncate(locale.lastIndexOf('_'));
2121 wsApp->loadLanguage(locale);
2128 QMainWindow::changeEvent(event);
2131 /* Update main window items based on whether there's a capture in progress. */
2132 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2134 setMenusForCaptureInProgress(capture_in_progress);
2136 wireless_frame_->setCaptureInProgress(capture_in_progress);
2139 packet_list_->setCaptureInProgress(capture_in_progress);
2140 // set_toolbar_for_capture_in_progress(capture_in_progress);
2142 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2146 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2147 << REGISTER_ANALYZE_GROUP_UNSORTED
2148 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2149 << REGISTER_STAT_GROUP_UNSORTED
2150 << REGISTER_STAT_GROUP_GENERIC
2151 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2152 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2153 << REGISTER_STAT_GROUP_RESPONSE_TIME
2154 << REGISTER_STAT_GROUP_TELEPHONY
2155 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2156 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2157 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2158 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2159 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2160 << REGISTER_TOOLS_GROUP_UNSORTED;
2162 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2164 foreach (QAction *action, actions) {
2165 switch (menu_group) {
2166 case REGISTER_ANALYZE_GROUP_UNSORTED:
2167 case REGISTER_STAT_GROUP_UNSORTED:
2168 main_ui_->menuStatistics->insertAction(
2169 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2172 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2173 main_ui_->menuServiceResponseTime->addAction(action);
2175 case REGISTER_STAT_GROUP_TELEPHONY:
2176 main_ui_->menuTelephony->addAction(action);
2178 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2179 main_ui_->menuANSI->addAction(action);
2181 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2182 main_ui_->menuGSM->addAction(action);
2184 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2185 main_ui_->menuLTE->addAction(action);
2187 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2188 main_ui_->menuMTP3->addAction(action);
2190 case REGISTER_TOOLS_GROUP_UNSORTED:
2192 // Allow the creation of submenus. Mimics the behavor of
2193 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2194 // and GtkUIManager.
2196 // For now we limit the insanity to the "Tools" menu.
2197 QStringList menu_path = action->text().split('/');
2198 QMenu *cur_menu = main_ui_->menuTools;
2199 while (menu_path.length() > 1) {
2200 QString menu_title = menu_path.takeFirst();
2201 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2202 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2204 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2205 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2208 submenu = cur_menu->addMenu(menu_title);
2209 submenu->setObjectName(menu_title.toLower());
2213 action->setText(menu_path.last());
2214 cur_menu->addAction(action);
2218 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2222 // Connect each action type to its corresponding slot. We to
2223 // distinguish various types of actions. Setting their objectName
2224 // seems to work OK.
2225 if (action->objectName() == TapParameterDialog::actionName()) {
2226 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2227 } else if (action->objectName() == FunnelStatistics::actionName()) {
2228 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2232 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2234 foreach (QAction *action, actions) {
2235 switch (menu_group) {
2236 case REGISTER_ANALYZE_GROUP_UNSORTED:
2237 case REGISTER_STAT_GROUP_UNSORTED:
2238 main_ui_->menuStatistics->removeAction(action);
2240 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2241 main_ui_->menuServiceResponseTime->removeAction(action);
2243 case REGISTER_STAT_GROUP_TELEPHONY:
2244 main_ui_->menuTelephony->removeAction(action);
2246 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2247 main_ui_->menuANSI->removeAction(action);
2249 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2250 main_ui_->menuGSM->removeAction(action);
2252 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2253 main_ui_->menuLTE->removeAction(action);
2255 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2256 main_ui_->menuMTP3->removeAction(action);
2258 case REGISTER_TOOLS_GROUP_UNSORTED:
2260 // Allow removal of submenus.
2261 // For now we limit the insanity to the "Tools" menu.
2262 QStringList menu_path = action->text().split('/');
2263 QMenu *cur_menu = main_ui_->menuTools;
2264 while (menu_path.length() > 1) {
2265 QString menu_title = menu_path.takeFirst();
2266 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2267 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2269 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2270 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2274 cur_menu->removeAction(action);
2278 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2284 void MainWindow::addDynamicMenus()
2287 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2288 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2289 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2290 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2291 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2292 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2294 // Fill in each menu
2295 foreach (register_stat_group_t menu_group, menu_groups) {
2296 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2297 addMenuActions(actions, menu_group);
2300 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2301 // We've added a placeholder in order to make sure the "Tools" menu is
2302 // visible. Hide it as needed.
2303 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2304 main_ui_->actionToolsPlaceholder->setVisible(false);
2306 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2307 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2309 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2310 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2312 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2313 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2315 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2316 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2320 void MainWindow::reloadDynamicMenus()
2322 foreach (register_stat_group_t menu_group, menu_groups) {
2323 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2324 removeMenuActions(actions, menu_group);
2326 actions = wsApp->addedMenuGroupItems(menu_group);
2327 addMenuActions(actions, menu_group);
2330 wsApp->clearAddedMenuGroupItems();
2331 wsApp->clearRemovedMenuGroupItems();
2334 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2336 QAction * itemAction = NULL;
2337 ext_menubar_t * item = NULL;
2338 GList * children = NULL;
2340 /* There must exists an xpath parent */
2341 g_assert(subMenu != NULL);
2343 /* If the depth counter exceeds, something must have gone wrong */
2344 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2346 children = menu->children;
2347 /* Iterate the child entries */
2348 while ( children != NULL && children->data != NULL )
2350 item = (ext_menubar_t *) children->data;
2352 if ( item->type == EXT_MENUBAR_MENU )
2354 /* Handle Submenu entry */
2355 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++ );
2357 else if ( item->type == EXT_MENUBAR_SEPARATOR )
2359 subMenu->addSeparator();
2361 else if ( item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL )
2363 itemAction = subMenu->addAction(item->name);
2364 itemAction->setData(QVariant::fromValue((void *)item));
2365 itemAction->setText(item->label);
2366 connect(itemAction, SIGNAL(triggered()),
2367 this, SLOT(externalMenuItem_triggered()));
2371 children = g_list_next(children);
2375 QMenu * MainWindow::searchSubMenu(QString objectName)
2379 if ( objectName.length() > 0 )
2381 QString searchName = QString("menu") + objectName;
2383 lst = main_ui_->menuBar->findChildren<QMenu*>();
2384 foreach (QMenu* m, lst)
2386 if ( QString::compare( m->objectName(), searchName ) == 0 )
2394 void MainWindow::addExternalMenus()
2396 QMenu * subMenu = NULL;
2397 GList * user_menu = NULL;
2398 ext_menu_t * menu = NULL;
2400 user_menu = ext_menubar_get_entries();
2402 while ( ( user_menu != NULL ) && ( user_menu->data != NULL ) )
2404 menu = (ext_menu_t *) user_menu->data;
2406 /* On this level only menu items should exist. Not doing an assert here,
2407 * as it could be an honest mistake */
2408 if ( menu->type != EXT_MENUBAR_MENU )
2410 user_menu = g_list_next(user_menu);
2414 /* Create main submenu and add it to the menubar */
2415 if ( menu->parent_menu != NULL )
2417 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2418 if ( sortUnderneath != NULL)
2419 subMenu = sortUnderneath->addMenu(menu->label);
2422 if ( subMenu == NULL )
2423 subMenu = main_ui_->menuBar->addMenu(menu->label);
2425 /* This will generate the action structure for each menu. It is recursive,
2426 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2427 this->externalMenuHelper(menu, subMenu, 0);
2430 user_menu = g_list_next (user_menu);
2440 * indent-tabs-mode: nil
2443 * ex: set shiftwidth=4 tabstop=8 expandtab:
2444 * :indentSize=4:tabSize=8:noTabs=true: