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_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
394 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
395 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
397 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
401 #ifdef HAVE_SOFTWARE_UPDATE
402 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
403 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
404 main_ui_->menuHelp->insertAction(update_sep, update_action);
405 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
407 master_split_.setObjectName("splitterMaster");
408 extra_split_.setObjectName("splitterExtra");
409 main_ui_->mainStack->addWidget(&master_split_);
411 empty_pane_.setObjectName("emptyPane");
413 packet_list_ = new PacketList(&master_split_);
415 proto_tree_ = new ProtoTree(&master_split_);
416 proto_tree_->installEventFilter(this);
418 byte_view_tab_ = new ByteViewTab(&master_split_);
420 packet_list_->setProtoTree(proto_tree_);
421 packet_list_->setByteViewTab(byte_view_tab_);
422 packet_list_->installEventFilter(this);
424 main_welcome_ = main_ui_->welcomePage;
426 // Packet list and proto tree must exist before these are called.
427 setMenusForSelectedPacket();
428 setMenusForSelectedTreeRow();
430 initShowHideMainWidgets();
431 initTimeDisplayFormatMenu();
432 initTimePrecisionFormatMenu();
434 updatePreferenceActions();
435 updateRecentActions();
436 setForCaptureInProgress(false);
438 setTabOrder(df_combo_box_, packet_list_);
440 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
441 this, SLOT(captureCapturePrepared(capture_session *)));
442 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
443 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
444 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
445 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
446 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
447 this, SLOT(captureCaptureFixedStarted(capture_session *)));
448 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
449 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
450 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
451 this, SLOT(captureCaptureFixedFinished(capture_session *)));
452 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
453 this, SLOT(captureCaptureStopping(capture_session *)));
454 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
455 this, SLOT(captureCaptureFailed(capture_session *)));
456 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
457 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
459 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
460 wsApp, SLOT(captureStarted()));
461 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
462 wsApp, SLOT(captureFinished()));
463 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
464 wsApp, SLOT(captureStarted()));
465 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
466 wsApp, SLOT(captureFinished()));
468 connect(&capture_file_, SIGNAL(captureFileOpened()),
469 this, SLOT(captureFileOpened()));
470 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
471 this, SLOT(captureFileReadStarted()));
472 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
473 this, SLOT(captureFileReadFinished()));
474 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
475 this, SLOT(captureFileReloadStarted()));
476 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
477 this, SLOT(captureFileReadFinished()));
478 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
479 this, SLOT(captureFileRescanStarted()));
480 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
481 this, SLOT(captureFileReadFinished()));
482 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
483 this, SLOT(captureFileRetapStarted()));
484 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
485 this, SLOT(captureFileRetapFinished()));
486 connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
487 this, SLOT(captureFileFlushTapsData()));
488 connect(&capture_file_, SIGNAL(captureFileClosing()),
489 this, SLOT(captureFileClosing()));
490 connect(&capture_file_, SIGNAL(captureFileClosed()),
491 this, SLOT(captureFileClosed()));
493 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
494 this, SLOT(captureFileSaveStarted(QString)));
495 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
496 main_ui_->statusBar, SLOT(popFileStatus()));
497 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
498 main_ui_->statusBar, SLOT(popFileStatus()));
499 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
500 main_ui_->statusBar, SLOT(popFileStatus()));
502 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
503 wsApp, SLOT(captureFileReadStarted()));
504 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
505 wsApp, SLOT(updateTaps()));
507 connect(wsApp, SIGNAL(columnsChanged()),
508 packet_list_, SLOT(columnsChanged()));
509 connect(wsApp, SIGNAL(preferencesChanged()),
510 packet_list_, SLOT(preferencesChanged()));
511 connect(wsApp, SIGNAL(recentFilesRead()),
512 this, SLOT(applyRecentPaneGeometry()));
513 connect(wsApp, SIGNAL(recentFilesRead()),
514 this, SLOT(updateRecentActions()));
515 connect(wsApp, SIGNAL(packetDissectionChanged()),
516 this, SLOT(redissectPackets()), Qt::QueuedConnection);
517 connect(wsApp, SIGNAL(appInitialized()),
518 this, SLOT(filterExpressionsChanged()));
519 connect(wsApp, SIGNAL(filterExpressionsChanged()),
520 this, SLOT(filterExpressionsChanged()));
521 connect(wsApp, SIGNAL(checkDisplayFilter()),
522 this, SLOT(checkDisplayFilter()));
523 connect(wsApp, SIGNAL(fieldsChanged()),
524 this, SLOT(fieldsChanged()));
525 connect(wsApp, SIGNAL(reloadLuaPlugins()),
526 this, SLOT(reloadLuaPlugins()));
528 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
529 this, SLOT(mainStackChanged(int)));
531 connect(main_welcome_, SIGNAL(startCapture()),
532 this, SLOT(startCapture()));
533 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
534 this, SLOT(openCaptureFile(QString)));
535 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
536 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
537 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
538 main_ui_->statusBar, SLOT(popFilterStatus()));
540 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
541 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
542 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
543 this, SLOT(redissectPackets()));
544 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
545 this, SLOT(showPreferencesDialog(QString)));
546 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
547 this, SLOT(showPreferencesDialog(QString)));
548 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
549 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
550 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
551 this, SLOT(filterExpressionsChanged()));
553 connect(this, SIGNAL(setCaptureFile(capture_file*)),
554 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
555 connect(this, SIGNAL(setCaptureFile(capture_file*)),
556 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
557 connect(this, SIGNAL(setCaptureFile(capture_file*)),
558 packet_list_, SLOT(setCaptureFile(capture_file*)));
559 connect(this, SIGNAL(setCaptureFile(capture_file*)),
560 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
562 connect(this, SIGNAL(monospaceFontChanged(QFont)),
563 packet_list_, SLOT(setMonospaceFont(QFont)));
564 connect(this, SIGNAL(monospaceFontChanged(QFont)),
565 proto_tree_, SLOT(setMonospaceFont(QFont)));
566 connect(this, SIGNAL(monospaceFontChanged(QFont)),
567 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
569 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
570 packet_list_, SLOT(goNextPacket()));
571 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
572 packet_list_, SLOT(goPreviousPacket()));
573 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
574 packet_list_, SLOT(goFirstPacket()));
575 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
576 packet_list_, SLOT(goLastPacket()));
578 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
579 proto_tree_, SLOT(expandSubtrees()));
580 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
581 proto_tree_, SLOT(expandAll()));
582 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
583 proto_tree_, SLOT(collapseAll()));
585 connect(packet_list_, SIGNAL(packetSelectionChanged()),
586 this, SLOT(setMenusForSelectedPacket()));
587 connect(packet_list_, SIGNAL(packetDissectionChanged()),
588 this, SLOT(redissectPackets()));
589 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
590 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
591 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
592 this, SLOT(showPreferencesDialog(QString)));
593 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
594 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
595 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
596 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
597 packet_list_, SLOT(columnsChanged()));
598 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
599 this, SLOT(openPacketDialog()));
600 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
601 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
602 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
603 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
604 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
605 main_ui_->statusBar, SLOT(popBusyStatus()));
606 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
607 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
608 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
609 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
610 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
611 main_ui_->statusBar, SLOT(popProgressStatus()));
613 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
614 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
615 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
616 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
617 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
618 this, SLOT(openPacketDialog(bool)));
619 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
620 this, SLOT(showPreferencesDialog(QString)));
621 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
622 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
624 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
625 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
627 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
628 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
630 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
631 &capture_file_, SLOT(stopLoading()));
633 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
634 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
636 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
637 this, SLOT(openCaptureFile(QString)));
640 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
642 connect(iface_tree, SIGNAL(itemSelectionChanged()),
643 this, SLOT(interfaceSelectionChanged()));
645 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
646 this, SLOT(captureFilterSyntaxChanged(bool)));
649 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
650 this, SLOT(showExtcapOptionsDialog(QString&)));
653 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
654 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
655 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
656 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
657 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
658 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
661 /* Create plugin_if hooks */
662 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
663 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
664 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
665 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
667 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
670 main_ui_->mainStack->setCurrentWidget(main_welcome_);
673 MainWindow::~MainWindow()
678 QString MainWindow::getFilter()
680 return df_combo_box_->currentText();
683 QMenu *MainWindow::createPopupMenu()
685 QMenu *menu = new QMenu();
686 menu->addAction(main_ui_->actionViewMainToolbar);
687 menu->addAction(main_ui_->actionViewFilterToolbar);
688 menu->addAction(main_ui_->actionViewWirelessToolbar);
689 menu->addAction(main_ui_->actionViewStatusBar);
690 menu->addSeparator();
691 menu->addAction(main_ui_->actionViewPacketList);
692 menu->addAction(main_ui_->actionViewPacketDetails);
693 menu->addAction(main_ui_->actionViewPacketBytes);
697 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
699 pipe_source_ = source;
700 pipe_child_process_ = child_process;
701 pipe_user_data_ = user_data;
702 pipe_input_cb_ = input_cb;
705 /* Tricky to use pipes in win9x, as no concept of wait. NT can
706 do this but that doesn't cover all win32 platforms. GTK can do
707 this but doesn't seem to work over processes. Attempt to do
708 something similar here, start a timer and check for data on every
710 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
713 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
717 pipe_timer_ = new QTimer(this);
718 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
719 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
720 pipe_timer_->start(200);
722 if (pipe_notifier_) {
723 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
724 delete pipe_notifier_;
727 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
728 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
729 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
730 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
734 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
736 // The user typed some text. Start filling in a filter.
737 // We may need to be more choosy here. We just need to catch events for the packet list,
738 // proto tree, and main welcome widgets.
739 if (event->type() == QEvent::KeyPress) {
740 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
741 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
742 df_combo_box_->lineEdit()->insert(kevt->text());
743 df_combo_box_->lineEdit()->setFocus();
748 return QMainWindow::eventFilter(obj, event);
751 void MainWindow::keyPressEvent(QKeyEvent *event) {
753 // Explicitly focus on the display filter combo.
754 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
755 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
759 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
760 if (event->modifiers() == Qt::NoModifier) {
761 if (event->key() == Qt::Key_Escape) {
762 on_goToCancel_clicked();
763 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
767 return; // goToLineEdit didn't want it and we don't either.
770 // Move up & down the packet list.
771 if (event->key() == Qt::Key_F7) {
772 packet_list_->goPreviousPacket();
773 } else if (event->key() == Qt::Key_F8) {
774 packet_list_->goNextPacket();
777 // Move along, citizen.
778 QMainWindow::keyPressEvent(event);
781 void MainWindow::closeEvent(QCloseEvent *event) {
782 saveWindowGeometry();
784 /* If we're in the middle of stopping a capture, don't do anything;
785 the user can try deleting the window after the capture stops. */
786 if (capture_stopping_) {
791 QString before_what(tr(" before quitting"));
792 if (!testCaptureFileClose(before_what, Quit)) {
798 capture_interfaces_dialog_.close();
800 // Make sure we kill any open dumpcap processes.
801 delete main_welcome_;
803 // One of the many places we assume one main window.
804 if(!wsApp->isInitialized()) {
805 // If we're still initializing, QCoreApplication::quit() won't
806 // exit properly because we are not in the event loop. This
807 // means that the application won't clean up after itself. We
808 // might want to call wsApp->processEvents() during startup
809 // instead so that we can do a normal exit here.
815 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
818 foreach (QUrl drag_url, event->mimeData()->urls()) {
819 if (!drag_url.toLocalFile().isEmpty()) {
824 if (accept) event->acceptProposedAction();
827 void MainWindow::dropEvent(QDropEvent *event)
829 foreach (QUrl drop_url, event->mimeData()->urls()) {
830 QString local_file = drop_url.toLocalFile();
831 if (!local_file.isEmpty()) {
832 event->acceptProposedAction();
833 openCaptureFile(local_file);
839 // Apply recent settings to the main window geometry.
840 // We haven't loaded the preferences at this point so we assume that the
841 // position and size preference are enabled.
842 void MainWindow::loadWindowGeometry()
844 int min_sensible_dimension = 200;
847 if (recent.gui_geometry_main_maximized) {
848 setWindowState(Qt::WindowMaximized);
852 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
853 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
854 if (!rect_on_screen(recent_geom)) {
855 // We're not visible on any screens. Give up and use the default geometry.
859 // if (prefs.gui_geometry_save_position) {
860 move(recent_geom.topLeft());
863 if (// prefs.gui_geometry_save_size &&
864 recent_geom.width() > min_sensible_dimension &&
865 recent_geom.height() > min_sensible_dimension) {
866 resize(recent_geom.size());
871 void MainWindow::saveWindowGeometry()
873 if (prefs.gui_geometry_save_position) {
874 recent.gui_geometry_main_x = pos().x();
875 recent.gui_geometry_main_y = pos().y();
878 if (prefs.gui_geometry_save_size) {
879 recent.gui_geometry_main_width = size().width();
880 recent.gui_geometry_main_height = size().height();
883 if (prefs.gui_geometry_save_maximized) {
884 // On OS X this is false when it shouldn't be
885 recent.gui_geometry_main_maximized = isMaximized();
888 if (master_split_.sizes().length() > 0) {
889 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
892 if (master_split_.sizes().length() > 2) {
893 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
894 } else if (extra_split_.sizes().length() > 0) {
895 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
899 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
901 case layout_pane_content_none:
903 case layout_pane_content_plist:
905 case layout_pane_content_pdetails:
907 case layout_pane_content_pbytes:
908 return byte_view_tab_;
910 g_assert_not_reached();
915 // Our event loop becomes nested whenever we call update_progress_dlg, which
916 // includes several places in file.c. The GTK+ UI stays out of trouble by
917 // showing a modal progress dialog. We attempt to do the equivalent below by
918 // disabling parts of the main window. At a minumum the ProgressFrame in the
919 // main status bar must remain accessible.
921 // We might want to do this any time the main status bar progress frame is
923 void MainWindow::freeze()
925 freeze_focus_ = wsApp->focusWidget();
927 // XXX Alternatively we could just disable and enable the main menu.
928 for (int i = 0; i < freeze_actions_.size(); i++) {
929 QAction *action = freeze_actions_[i].first;
930 freeze_actions_[i].second = action->isEnabled();
931 action->setEnabled(false);
933 main_ui_->centralWidget->setEnabled(false);
936 void MainWindow::thaw()
938 main_ui_->centralWidget->setEnabled(true);
939 for (int i = 0; i < freeze_actions_.size(); i++) {
940 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
943 if (freeze_focus_) freeze_focus_->setFocus();
944 freeze_focus_ = NULL;
947 void MainWindow::mergeCaptureFile()
949 QString file_name = "";
950 QString read_filter = "";
951 dfilter_t *rfcode = NULL;
954 if (!capture_file_.capFile())
957 if (prefs.gui_ask_unsaved) {
958 if (cf_has_unsaved_data(capture_file_.capFile())) {
959 QMessageBox msg_dialog;
960 gchar *display_basename;
963 msg_dialog.setIcon(QMessageBox::Question);
964 /* This file has unsaved data; ask the user whether to save
966 if (capture_file_.capFile()->is_tempfile) {
967 msg_dialog.setText(tr("Save packets before merging?"));
968 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
971 * Format the message.
973 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
974 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
975 g_free(display_basename);
976 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
979 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
980 msg_dialog.setDefaultButton(QMessageBox::Save);
982 response = msg_dialog.exec();
986 case QMessageBox::Save:
987 /* Save the file but don't close it */
988 saveCaptureFile(capture_file_.capFile(), FALSE);
991 case QMessageBox::Cancel:
993 /* Don't do the merge. */
1000 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1002 cf_status_t merge_status;
1003 char *in_filenames[2];
1006 switch (prefs.gui_fileopen_style) {
1008 case FO_STYLE_LAST_OPENED:
1009 /* The user has specified that we should start out in the last directory
1010 we looked in. If we've already opened a file, use its containing
1011 directory, if we could determine it, as the directory, otherwise
1012 use the "last opened" directory saved in the preferences file if
1014 /* This is now the default behaviour in file_selection_new() */
1017 case FO_STYLE_SPECIFIED:
1018 /* The user has specified that we should always start out in a
1019 specified directory; if they've specified that directory,
1020 start out by showing the files in that dir. */
1021 if (prefs.gui_fileopen_dir[0] != '\0')
1022 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
1026 if (merge_dlg.merge(file_name)) {
1029 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1030 /* Not valid. Tell the user, and go back and run the file
1031 selection box again once they dismiss the alert. */
1032 //bad_dfilter_alert_box(top_level, read_filter->str);
1033 QMessageBox::warning(this, tr("Invalid Read Filter"),
1034 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1043 file_type = capture_file_.capFile()->cd_t;
1045 /* Try to merge or append the two files */
1047 if (merge_dlg.mergeType() == 0) {
1048 /* chronological order */
1049 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1050 in_filenames[1] = qstring_strdup(file_name);
1051 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
1052 } else if (merge_dlg.mergeType() <= 0) {
1054 in_filenames[0] = qstring_strdup(file_name);
1055 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1056 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1059 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1060 in_filenames[1] = qstring_strdup(file_name);
1061 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1064 g_free(in_filenames[0]);
1065 g_free(in_filenames[1]);
1067 if (merge_status != CF_OK) {
1069 dfilter_free(rfcode);
1074 cf_close(capture_file_.capFile());
1076 /* Try to open the merged capture file. */
1077 CaptureFile::globalCapFile()->window = this;
1078 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1079 /* We couldn't open it; fail. */
1080 CaptureFile::globalCapFile()->window = NULL;
1082 dfilter_free(rfcode);
1087 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1088 it closed the previous capture file, and thus destroyed any
1089 previous read filter attached to "cf"). */
1090 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1092 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1096 /* Just because we got an error, that doesn't mean we were unable
1097 to read any of the file; we handle what we could get from the
1101 case CF_READ_ABORTED:
1102 /* The user bailed out of re-reading the capture file; the
1103 capture file has been closed - just free the capture file name
1104 string and return (without changing the last containing
1110 /* Save the name of the containing directory specified in the path name,
1111 if any; we can write over cf_merged_name, which is a good thing, given that
1112 "get_dirname()" does write over its argument. */
1113 wsApp->setLastOpenDir(get_dirname(tmpname));
1115 main_ui_->statusBar->showExpert();
1121 void MainWindow::importCaptureFile() {
1122 ImportTextDialog import_dlg;
1124 QString before_what(tr(" before importing a capture"));
1125 if (!testCaptureFileClose(before_what))
1130 if (import_dlg.result() != QDialog::Accepted) {
1131 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1135 openCaptureFile(import_dlg.capfileName());
1138 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1140 gboolean discard_comments;
1142 if (cf->is_tempfile) {
1143 /* This is a temporary capture file, so saving it means saving
1144 it to a permanent file. Prompt the user for a location
1145 to which to save it. Don't require that the file format
1146 support comments - if it's a temporary capture file, it's
1147 probably pcap-ng, which supports comments and, if it's
1148 not pcap-ng, let the user decide what they want to do
1149 if they've added comments. */
1150 saveAsCaptureFile(cf, FALSE, dont_reopen);
1152 if (cf->unsaved_changes) {
1153 cf_write_status_t status;
1155 /* This is not a temporary capture file, but it has unsaved
1156 changes, so saving it means doing a "safe save" on top
1157 of the existing file, in the same format - no UI needed
1158 unless the file has comments and the file's format doesn't
1161 If the file has comments, does the file's format support them?
1162 If not, ask the user whether they want to discard the comments
1163 or choose a different format. */
1164 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1167 /* The file can be saved in the specified format as is;
1168 just drive on and save in the format they selected. */
1169 discard_comments = FALSE;
1172 case SAVE_WITHOUT_COMMENTS:
1173 /* The file can't be saved in the specified format as is,
1174 but it can be saved without the comments, and the user
1175 said "OK, discard the comments", so save it in the
1176 format they specified without the comments. */
1177 discard_comments = TRUE;
1180 case SAVE_IN_ANOTHER_FORMAT:
1181 /* There are file formats in which we can save this that
1182 support comments, and the user said not to delete the
1183 comments. Do a "Save As" so the user can select
1184 one of those formats and choose a file name. */
1185 saveAsCaptureFile(cf, TRUE, dont_reopen);
1189 /* The user said "forget it". Just return. */
1193 /* Squelch warnings that discard_comments is being used
1195 g_assert_not_reached();
1199 /* XXX - cf->filename might get freed out from under us, because
1200 the code path through which cf_save_records() goes currently
1201 closes the current file and then opens and reloads the saved file,
1202 so make a copy and free it later. */
1203 file_name = cf->filename;
1204 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1205 discard_comments, dont_reopen);
1209 /* The save succeeded; we're done.
1210 If we discarded comments, redraw the packet list to reflect
1211 any packets that no longer have comments. */
1212 if (discard_comments)
1213 packet_list_queue_draw();
1215 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1216 updateForUnsavedChanges(); // we update the title bar to remove the *
1219 case CF_WRITE_ERROR:
1220 /* The write failed.
1221 XXX - OK, what do we do now? Let them try a
1222 "Save As", in case they want to try to save to a
1223 different directory r file system? */
1226 case CF_WRITE_ABORTED:
1227 /* The write was aborted; just drive on. */
1231 /* Otherwise just do nothing. */
1235 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1236 QString file_name = "";
1238 gboolean compressed;
1239 cf_write_status_t status;
1241 gboolean discard_comments = FALSE;
1248 CaptureFileDialog save_as_dlg(this, cf);
1250 switch (prefs.gui_fileopen_style) {
1252 case FO_STYLE_LAST_OPENED:
1253 /* The user has specified that we should start out in the last directory
1254 we looked in. If we've already opened a file, use its containing
1255 directory, if we could determine it, as the directory, otherwise
1256 use the "last opened" directory saved in the preferences file if
1258 /* This is now the default behaviour in file_selection_new() */
1261 case FO_STYLE_SPECIFIED:
1262 /* The user has specified that we should always start out in a
1263 specified directory; if they've specified that directory,
1264 start out by showing the files in that dir. */
1265 if (prefs.gui_fileopen_dir[0] != '\0')
1266 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1270 /* If the file has comments, does the format the user selected
1271 support them? If not, ask the user whether they want to
1272 discard the comments or choose a different format. */
1273 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1276 /* The file can be saved in the specified format as is;
1277 just drive on and save in the format they selected. */
1278 discard_comments = FALSE;
1281 case SAVE_WITHOUT_COMMENTS:
1282 /* The file can't be saved in the specified format as is,
1283 but it can be saved without the comments, and the user
1284 said "OK, discard the comments", so save it in the
1285 format they specified without the comments. */
1286 discard_comments = TRUE;
1289 case SAVE_IN_ANOTHER_FORMAT:
1290 /* There are file formats in which we can save this that
1291 support comments, and the user said not to delete the
1292 comments. The combo box of file formats has had the
1293 formats that don't support comments trimmed from it,
1294 so run the dialog again, to let the user decide
1295 whether to save in one of those formats or give up. */
1296 must_support_comments = TRUE;
1300 /* The user said "forget it". Just get rid of the dialog box
1304 file_type = save_as_dlg.selectedFileType();
1305 compressed = save_as_dlg.isCompressed();
1307 fileAddExtension(file_name, file_type, compressed);
1310 // /* If the file exists and it's user-immutable or not writable,
1311 // ask the user whether they want to override that. */
1312 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1313 // /* They don't. Let them try another file name or cancel. */
1318 /* Attempt to save the file */
1319 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1320 discard_comments, dont_reopen);
1324 /* The save succeeded; we're done. */
1325 /* Save the directory name for future file dialogs. */
1326 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1327 set_last_open_dir(get_dirname(dirname));
1329 /* If we discarded comments, redraw the packet list to reflect
1330 any packets that no longer have comments. */
1331 if (discard_comments)
1332 packet_list_queue_draw();
1334 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1335 updateForUnsavedChanges(); // we update the title bar to remove the *
1338 case CF_WRITE_ERROR:
1339 /* The save failed; let the user try again. */
1342 case CF_WRITE_ABORTED:
1343 /* The user aborted the save; just return. */
1350 void MainWindow::exportSelectedPackets() {
1351 QString file_name = "";
1353 gboolean compressed;
1354 packet_range_t range;
1355 cf_write_status_t status;
1357 gboolean discard_comments = FALSE;
1359 if (!capture_file_.capFile())
1362 /* Init the packet range */
1363 packet_range_init(&range, capture_file_.capFile());
1364 range.process_filtered = TRUE;
1365 range.include_dependents = TRUE;
1368 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1370 switch (prefs.gui_fileopen_style) {
1372 case FO_STYLE_LAST_OPENED:
1373 /* The user has specified that we should start out in the last directory
1374 we looked in. If we've already opened a file, use its containing
1375 directory, if we could determine it, as the directory, otherwise
1376 use the "last opened" directory saved in the preferences file if
1378 /* This is now the default behaviour in file_selection_new() */
1381 case FO_STYLE_SPECIFIED:
1382 /* The user has specified that we should always start out in a
1383 specified directory; if they've specified that directory,
1384 start out by showing the files in that dir. */
1385 if (prefs.gui_fileopen_dir[0] != '\0')
1386 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1390 /* If the file has comments, does the format the user selected
1391 support them? If not, ask the user whether they want to
1392 discard the comments or choose a different format. */
1393 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1396 /* The file can be saved in the specified format as is;
1397 just drive on and save in the format they selected. */
1398 discard_comments = FALSE;
1401 case SAVE_WITHOUT_COMMENTS:
1402 /* The file can't be saved in the specified format as is,
1403 but it can be saved without the comments, and the user
1404 said "OK, discard the comments", so save it in the
1405 format they specified without the comments. */
1406 discard_comments = TRUE;
1409 case SAVE_IN_ANOTHER_FORMAT:
1410 /* There are file formats in which we can save this that
1411 support comments, and the user said not to delete the
1412 comments. The combo box of file formats has had the
1413 formats that don't support comments trimmed from it,
1414 so run the dialog again, to let the user decide
1415 whether to save in one of those formats or give up. */
1419 /* The user said "forget it". Just get rid of the dialog box
1425 * Check that we're not going to save on top of the current
1427 * We do it here so we catch all cases ...
1428 * Unfortunately, the file requester gives us an absolute file
1429 * name and the read file name may be relative (if supplied on
1430 * the command line). From Joerg Mayer.
1432 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1433 QMessageBox msg_box;
1434 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1436 msg_box.setIcon(QMessageBox::Critical);
1437 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1438 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1439 msg_box.setStandardButtons(QMessageBox::Ok);
1440 msg_box.setDefaultButton(QMessageBox::Ok);
1442 g_free(display_basename);
1446 file_type = esp_dlg.selectedFileType();
1447 compressed = esp_dlg.isCompressed();
1448 fileAddExtension(file_name, file_type, compressed);
1451 // /* If the file exists and it's user-immutable or not writable,
1452 // ask the user whether they want to override that. */
1453 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1454 // /* They don't. Let them try another file name or cancel. */
1459 /* Attempt to save the file */
1460 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1464 /* The save succeeded; we're done. */
1465 /* Save the directory name for future file dialogs. */
1466 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1467 set_last_open_dir(get_dirname(dirname));
1469 /* If we discarded comments, redraw the packet list to reflect
1470 any packets that no longer have comments. */
1471 if (discard_comments)
1472 packet_list_queue_draw();
1475 case CF_WRITE_ERROR:
1476 /* The save failed; let the user try again. */
1479 case CF_WRITE_ABORTED:
1480 /* The user aborted the save; just return. */
1487 void MainWindow::exportDissections(export_type_e export_type) {
1488 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1489 packet_range_t range;
1491 if (!capture_file_.capFile())
1494 /* Init the packet range */
1495 packet_range_init(&range, capture_file_.capFile());
1496 range.process_filtered = TRUE;
1497 range.include_dependents = TRUE;
1502 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1503 QString file_name_lower;
1504 QString file_suffix;
1505 GSList *extensions_list;
1506 gboolean add_extension;
1509 * Append the default file extension if there's none given by
1510 * the user or if they gave one that's not one of the valid
1511 * extensions for the file type.
1513 file_name_lower = file_name.toLower();
1514 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1515 if (extensions_list != NULL) {
1518 /* We have one or more extensions for this file type.
1519 Start out assuming we need to add the default one. */
1520 add_extension = TRUE;
1522 /* OK, see if the file has one of those extensions. */
1523 for (extension = extensions_list; extension != NULL;
1524 extension = g_slist_next(extension)) {
1525 file_suffix += tr(".") + (char *)extension->data;
1526 if (file_name_lower.endsWith(file_suffix)) {
1528 * The file name has one of the extensions for
1531 add_extension = FALSE;
1534 file_suffix += ".gz";
1535 if (file_name_lower.endsWith(file_suffix)) {
1537 * The file name has one of the extensions for
1540 add_extension = FALSE;
1545 /* We have no extensions for this file type. Don't add one. */
1546 add_extension = FALSE;
1548 if (add_extension) {
1549 if (wtap_default_file_extension(file_type) != NULL) {
1550 file_name += tr(".") + wtap_default_file_extension(file_type);
1558 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1559 bool capture_in_progress = false;
1560 bool do_close_file = false;
1562 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1563 return true; /* Already closed, nothing to do */
1566 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1567 /* This is true if we're reading a capture file *or* if we're doing
1568 a live capture. If we're reading a capture file, the main loop
1569 is busy reading packets, and only accepting input from the
1570 progress dialog, so we can't get here, so this means we're
1572 capture_in_progress = true;
1576 if (prefs.gui_ask_unsaved) {
1577 if (cf_has_unsaved_data(capture_file_.capFile()) ||
1578 (capture_in_progress && capture_file_.capFile()->count > 0))
1580 QMessageBox msg_dialog;
1583 QPushButton *save_button;
1584 QPushButton *discard_button;
1586 msg_dialog.setIcon(QMessageBox::Question);
1587 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1589 /* This file has unsaved data or there's a capture in
1590 progress; ask the user whether to save the data. */
1591 if (capture_in_progress && context != Restart) {
1592 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1593 infotext = tr("Your captured packets will be lost if you don't save them.");
1594 } else if (capture_file_.capFile()->is_tempfile) {
1595 if (context == Reload) {
1596 // Reloading a tempfile will keep the packets, so this is not unsaved packets
1597 question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1598 infotext = tr("Your changes will be lost if you don't save them.");
1600 question = tr("Do you want to save the captured packets%1?").arg(before_what);
1601 infotext = tr("Your captured packets will be lost if you don't save them.");
1604 // No capture in progress and not a tempfile, so this is not unsaved packets
1605 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1606 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1607 infotext = tr("Your changes will be lost if you don't save them.");
1608 g_free(display_basename);
1611 msg_dialog.setText(question);
1612 msg_dialog.setInformativeText(infotext);
1614 // XXX Text comes from ui/gtk/stock_icons.[ch]
1615 // Note that the button roles differ from the GTK+ version.
1616 // Cancel = RejectRole
1617 // Save = AcceptRole
1618 // Don't Save = DestructiveRole
1619 msg_dialog.addButton(QMessageBox::Cancel);
1621 if (capture_in_progress) {
1622 QString save_button_text;
1623 if (context == Restart) {
1624 save_button_text = tr("Save before Continue");
1626 save_button_text = tr("Stop and Save");
1628 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1630 save_button = msg_dialog.addButton(QMessageBox::Save);
1632 msg_dialog.setDefaultButton(save_button);
1634 QString discard_button_text;
1635 if (capture_in_progress) {
1638 discard_button_text = tr("Stop and Quit without Saving");
1641 discard_button_text = tr("Continue without Saving");
1644 discard_button_text = tr("Stop and Continue without Saving");
1650 discard_button_text = tr("Quit without Saving");
1654 discard_button_text = tr("Continue without Saving");
1658 discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1661 /* According to the Qt doc:
1662 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1664 * Therefore we should use clickedButton() to determine which button was clicked. */
1666 if (msg_dialog.clickedButton() == save_button) {
1668 /* If there's a capture in progress, we have to stop the capture
1669 and then do the save. */
1670 if (capture_in_progress)
1673 /* Save the file and close it */
1674 saveCaptureFile(capture_file_.capFile(), true);
1675 } else if(msg_dialog.clickedButton() == discard_button) {
1676 /* Just close the file, discarding changes */
1677 do_close_file = true;
1679 // cancelButton or some other unspecified button
1683 /* Unchanged file or capturing with no packets */
1684 do_close_file = true;
1687 /* User asked not to be bothered by those prompts, just close it.
1688 XXX - should that apply only to saving temporary files? */
1689 do_close_file = true;
1692 if (do_close_file) {
1694 /* If there's a capture in progress, we have to stop the capture
1695 and then do the close. */
1696 if (capture_in_progress)
1699 /* captureStop() will close the file if not having any packets */
1700 if (capture_file_.capFile() && context != Restart && context != Reload)
1701 // Don't really close if Restart or Reload
1702 cf_close(capture_file_.capFile());
1705 return true; /* File closed */
1708 void MainWindow::captureStop() {
1711 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1712 WiresharkApplication::processEvents();
1716 void MainWindow::initMainToolbarIcons()
1718 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1719 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1720 #if !defined(Q_OS_WIN)
1721 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1722 // The OS X HIG specifies 32-pixel icons but they're a little too
1724 icon_size = icon_size * 3 / 2;
1726 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1728 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1729 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1731 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1732 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1733 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1734 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1736 // Menu icons are disabled in main_window.ui for these items.
1737 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1738 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1739 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1740 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1742 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1743 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1744 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1745 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1746 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1747 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1748 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1749 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1750 #if defined(Q_OS_MAC)
1751 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1752 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1754 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1756 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1757 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1759 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1760 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1761 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1762 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1763 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1764 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1765 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1768 void MainWindow::initShowHideMainWidgets()
1770 if (show_hide_actions_) {
1774 show_hide_actions_ = new QActionGroup(this);
1775 QMap<QAction *, QWidget *> shmw_actions;
1777 show_hide_actions_->setExclusive(false);
1778 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1779 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1780 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1781 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1782 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1783 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1784 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1786 foreach (QAction *shmwa, shmw_actions.keys()) {
1787 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1788 show_hide_actions_->addAction(shmwa);
1789 showHideMainWidgets(shmwa);
1792 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1795 Q_DECLARE_METATYPE(ts_type)
1797 void MainWindow::initTimeDisplayFormatMenu()
1799 if (time_display_actions_) {
1803 time_display_actions_ = new QActionGroup(this);
1805 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1806 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1807 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1808 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1809 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1810 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1811 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1812 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1813 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1814 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1816 foreach (QAction* tda, td_actions.keys()) {
1817 tda->setData(qVariantFromValue(td_actions[tda]));
1818 time_display_actions_->addAction(tda);
1821 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1824 Q_DECLARE_METATYPE(ts_precision)
1826 void MainWindow::initTimePrecisionFormatMenu()
1828 if (time_precision_actions_) {
1832 time_precision_actions_ = new QActionGroup(this);
1834 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1835 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1836 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1837 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1838 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1839 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1840 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1842 foreach (QAction* tpa, tp_actions.keys()) {
1843 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1844 time_precision_actions_->addAction(tpa);
1847 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1850 // Menu items which will be disabled when we freeze() and whose state will
1851 // be restored when we thaw(). Add to the list as needed.
1852 void MainWindow::initFreezeActions()
1854 QList<QAction *> freeze_actions = QList<QAction *>()
1855 << main_ui_->actionFileClose
1856 << main_ui_->actionViewReload
1857 << main_ui_->actionEditMarkPacket
1858 << main_ui_->actionEditMarkAllDisplayed
1859 << main_ui_->actionEditUnmarkAllDisplayed
1860 << main_ui_->actionEditIgnorePacket
1861 << main_ui_->actionEditIgnoreAllDisplayed
1862 << main_ui_->actionEditUnignoreAllDisplayed
1863 << main_ui_->actionEditSetTimeReference
1864 << main_ui_->actionEditUnsetAllTimeReferences;
1866 foreach (QAction *action, freeze_actions) {
1867 freeze_actions_ << QPair<QAction *, bool>(action, false);
1872 void MainWindow::setTitlebarForCaptureFile()
1874 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1875 if (capture_file_.capFile()->is_tempfile) {
1877 // For a temporary file, put the source of the data
1878 // in the window title, not whatever random pile
1879 // of characters is the last component of the path
1882 // XXX - on non-Mac platforms, put in the application
1885 setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1888 // For a user file, set the full path; that way,
1889 // for OS X, it'll set the "proxy icon". Qt
1890 // handles extracting the last component.
1892 // Sadly, some UN*Xes don't necessarily use UTF-8
1893 // for their file names, so we have to map the
1894 // file path to UTF-8. If that fails, we're somewhat
1897 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1902 if (utf8_filename) {
1903 QFileInfo fi(utf8_filename);
1904 setWSWindowTitle(fi.fileName());
1905 setWindowFilePath(utf8_filename);
1906 g_free(utf8_filename);
1908 // So what the heck else can we do here?
1909 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1912 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1914 /* We have no capture file. */
1919 QString MainWindow::replaceWindowTitleVariables(QString title)
1921 title.replace ("%P", get_profile_name());
1922 title.replace ("%V", get_ws_vcs_version_info());
1927 void MainWindow::setWSWindowTitle(QString title)
1929 if (title.isEmpty()) {
1930 title = tr("The Wireshark Network Analyzer");
1933 if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
1934 QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
1935 title.prepend(QString("[%1] ").arg(custom_title));
1938 if (prefs.gui_window_title && prefs.gui_window_title[0]) {
1939 QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
1941 // On OS X we separate the titles with a unicode em dash
1942 title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
1944 title.append(QString(" [%1]").arg(custom_title));
1948 setWindowTitle(title);
1949 setWindowFilePath(NULL);
1952 void MainWindow::setTitlebarForCaptureInProgress()
1954 if (capture_file_.capFile()) {
1955 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1957 /* We have no capture in progress. */
1964 /* Enable or disable menu items based on whether you have a capture file
1965 you've finished reading and, if you have one, whether it's been saved
1966 and whether it could be saved except by copying the raw packet data. */
1967 void MainWindow::setMenusForCaptureFile(bool force_disable)
1969 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1970 /* We have no capture file or we're currently reading a file */
1971 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(false);
1972 main_ui_->actionFileMerge->setEnabled(false);
1973 main_ui_->actionFileClose->setEnabled(false);
1974 main_ui_->actionFileSave->setEnabled(false);
1975 main_ui_->actionFileSaveAs->setEnabled(false);
1976 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1977 main_ui_->actionFileExportPackets->setEnabled(false);
1978 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1979 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1980 main_ui_->actionFileExportPDU->setEnabled(false);
1981 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1982 main_ui_->menuFileExportObjects->setEnabled(false);
1983 main_ui_->actionViewReload->setEnabled(false);
1985 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(true);
1986 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1988 main_ui_->actionFileClose->setEnabled(true);
1989 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1990 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1991 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1993 * "Export Specified Packets..." should be available only if
1994 * we can write the file out in at least one format.
1996 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1997 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1998 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1999 main_ui_->actionFileExportPDU->setEnabled(true);
2000 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
2001 main_ui_->menuFileExportObjects->setEnabled(true);
2002 main_ui_->actionViewReload->setEnabled(true);
2006 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
2007 /* Either a capture was started or stopped; in either case, it's not
2008 in the process of stopping, so allow quitting. */
2010 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
2011 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
2012 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
2013 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
2014 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
2015 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
2016 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
2017 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
2018 main_ui_->actionFileQuit->setEnabled(true);
2020 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
2022 // XXX Fix packet list heading menu sensitivity
2023 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2024 // !capture_in_progress);
2025 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2026 // !capture_in_progress);
2027 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2028 // !capture_in_progress);
2031 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
2032 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
2033 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
2034 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
2035 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
2036 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
2037 #endif /* HAVE_LIBPCAP */
2041 void MainWindow::setMenusForCaptureStopping() {
2042 main_ui_->actionFileQuit->setEnabled(false);
2043 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
2045 main_ui_->actionCaptureStart->setChecked(false);
2046 main_ui_->actionCaptureStop->setEnabled(false);
2047 main_ui_->actionCaptureRestart->setEnabled(false);
2048 #endif /* HAVE_LIBPCAP */
2051 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2053 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2055 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2056 // have_captured_packets);
2058 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2059 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2060 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2062 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2063 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2064 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2065 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2066 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2067 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2068 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2070 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2071 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2072 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2073 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2075 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2076 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2077 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2080 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2081 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2082 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2084 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2085 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2086 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2089 void MainWindow::setWindowIcon(const QIcon &icon) {
2090 wsApp->setWindowIcon(icon);
2091 QMainWindow::setWindowIcon(icon);
2094 void MainWindow::updateForUnsavedChanges() {
2095 setTitlebarForCaptureFile();
2096 setMenusForCaptureFile();
2099 void MainWindow::changeEvent(QEvent* event)
2103 switch (event->type())
2105 case QEvent::LanguageChange:
2106 main_ui_->retranslateUi(this);
2107 // make sure that the "Clear Menu" item is retranslated
2108 updateRecentFiles();
2110 case QEvent::LocaleChange:{
2111 QString locale = QLocale::system().name();
2112 locale.truncate(locale.lastIndexOf('_'));
2113 wsApp->loadLanguage(locale);
2120 QMainWindow::changeEvent(event);
2123 /* Update main window items based on whether there's a capture in progress. */
2124 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2126 setMenusForCaptureInProgress(capture_in_progress);
2128 wireless_frame_->setCaptureInProgress(capture_in_progress);
2131 packet_list_->setCaptureInProgress(capture_in_progress);
2132 // set_toolbar_for_capture_in_progress(capture_in_progress);
2134 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2138 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2139 << REGISTER_ANALYZE_GROUP_UNSORTED
2140 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2141 << REGISTER_STAT_GROUP_UNSORTED
2142 << REGISTER_STAT_GROUP_GENERIC
2143 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2144 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2145 << REGISTER_STAT_GROUP_RESPONSE_TIME
2146 << REGISTER_STAT_GROUP_TELEPHONY
2147 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2148 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2149 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2150 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2151 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2152 << REGISTER_TOOLS_GROUP_UNSORTED;
2154 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2156 foreach (QAction *action, actions) {
2157 switch (menu_group) {
2158 case REGISTER_ANALYZE_GROUP_UNSORTED:
2159 case REGISTER_STAT_GROUP_UNSORTED:
2160 main_ui_->menuStatistics->insertAction(
2161 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2164 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2165 main_ui_->menuServiceResponseTime->addAction(action);
2167 case REGISTER_STAT_GROUP_TELEPHONY:
2168 main_ui_->menuTelephony->addAction(action);
2170 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2171 main_ui_->menuANSI->addAction(action);
2173 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2174 main_ui_->menuGSM->addAction(action);
2176 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2177 main_ui_->menuLTE->addAction(action);
2179 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2180 main_ui_->menuMTP3->addAction(action);
2182 case REGISTER_TOOLS_GROUP_UNSORTED:
2184 // Allow the creation of submenus. Mimics the behavor of
2185 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2186 // and GtkUIManager.
2188 // For now we limit the insanity to the "Tools" menu.
2189 QStringList menu_path = action->text().split('/');
2190 QMenu *cur_menu = main_ui_->menuTools;
2191 while (menu_path.length() > 1) {
2192 QString menu_title = menu_path.takeFirst();
2193 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2194 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2196 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2197 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2200 submenu = cur_menu->addMenu(menu_title);
2201 submenu->setObjectName(menu_title.toLower());
2205 action->setText(menu_path.last());
2206 cur_menu->addAction(action);
2210 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2214 // Connect each action type to its corresponding slot. We to
2215 // distinguish various types of actions. Setting their objectName
2216 // seems to work OK.
2217 if (action->objectName() == TapParameterDialog::actionName()) {
2218 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2219 } else if (action->objectName() == FunnelStatistics::actionName()) {
2220 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2224 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2226 foreach (QAction *action, actions) {
2227 switch (menu_group) {
2228 case REGISTER_ANALYZE_GROUP_UNSORTED:
2229 case REGISTER_STAT_GROUP_UNSORTED:
2230 main_ui_->menuStatistics->removeAction(action);
2232 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2233 main_ui_->menuServiceResponseTime->removeAction(action);
2235 case REGISTER_STAT_GROUP_TELEPHONY:
2236 main_ui_->menuTelephony->removeAction(action);
2238 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2239 main_ui_->menuANSI->removeAction(action);
2241 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2242 main_ui_->menuGSM->removeAction(action);
2244 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2245 main_ui_->menuLTE->removeAction(action);
2247 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2248 main_ui_->menuMTP3->removeAction(action);
2250 case REGISTER_TOOLS_GROUP_UNSORTED:
2252 // Allow removal of submenus.
2253 // For now we limit the insanity to the "Tools" menu.
2254 QStringList menu_path = action->text().split('/');
2255 QMenu *cur_menu = main_ui_->menuTools;
2256 while (menu_path.length() > 1) {
2257 QString menu_title = menu_path.takeFirst();
2258 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2259 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2261 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2262 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2266 cur_menu->removeAction(action);
2270 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2276 void MainWindow::addDynamicMenus()
2279 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2280 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2281 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2282 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2283 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2284 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2286 // Fill in each menu
2287 foreach (register_stat_group_t menu_group, menu_groups) {
2288 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2289 addMenuActions(actions, menu_group);
2292 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2293 // We've added a placeholder in order to make sure the "Tools" menu is
2294 // visible. Hide it as needed.
2295 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2296 main_ui_->actionToolsPlaceholder->setVisible(false);
2298 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2299 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2301 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2302 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2304 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2305 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2307 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2308 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2312 void MainWindow::reloadDynamicMenus()
2314 foreach (register_stat_group_t menu_group, menu_groups) {
2315 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2316 removeMenuActions(actions, menu_group);
2318 actions = wsApp->addedMenuGroupItems(menu_group);
2319 addMenuActions(actions, menu_group);
2322 wsApp->clearAddedMenuGroupItems();
2323 wsApp->clearRemovedMenuGroupItems();
2326 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2328 QAction * itemAction = NULL;
2329 ext_menubar_t * item = NULL;
2330 GList * children = NULL;
2332 /* There must exists an xpath parent */
2333 g_assert(subMenu != NULL);
2335 /* If the depth counter exceeds, something must have gone wrong */
2336 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2338 children = menu->children;
2339 /* Iterate the child entries */
2340 while (children && children->data) {
2341 item = (ext_menubar_t *) children->data;
2343 if (item->type == EXT_MENUBAR_MENU) {
2344 /* Handle Submenu entry */
2345 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2346 } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2347 subMenu->addSeparator();
2348 } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2349 itemAction = subMenu->addAction(item->name);
2350 itemAction->setData(QVariant::fromValue((void *)item));
2351 itemAction->setText(item->label);
2352 connect(itemAction, SIGNAL(triggered()),
2353 this, SLOT(externalMenuItem_triggered()));
2357 children = g_list_next(children);
2361 QMenu * MainWindow::searchSubMenu(QString objectName)
2365 if (objectName.length() > 0) {
2366 QString searchName = QString("menu") + objectName;
2368 lst = main_ui_->menuBar->findChildren<QMenu*>();
2369 foreach (QMenu* m, lst) {
2370 if (QString::compare(m->objectName(), searchName) == 0)
2378 void MainWindow::addExternalMenus()
2380 QMenu * subMenu = NULL;
2381 GList * user_menu = NULL;
2382 ext_menu_t * menu = NULL;
2384 user_menu = ext_menubar_get_entries();
2386 while (user_menu && user_menu->data) {
2387 menu = (ext_menu_t *) user_menu->data;
2389 /* On this level only menu items should exist. Not doing an assert here,
2390 * as it could be an honest mistake */
2391 if (menu->type != EXT_MENUBAR_MENU) {
2392 user_menu = g_list_next(user_menu);
2396 /* Create main submenu and add it to the menubar */
2397 if (menu->parent_menu) {
2398 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2400 subMenu = sortUnderneath->addMenu(menu->label);
2404 subMenu = main_ui_->menuBar->addMenu(menu->label);
2406 /* This will generate the action structure for each menu. It is recursive,
2407 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2408 this->externalMenuHelper(menu, subMenu, 0);
2411 user_menu = g_list_next (user_menu);
2421 * indent-tabs-mode: nil
2424 * ex: set shiftwidth=4 tabstop=8 expandtab:
2425 * :indentSize=4:tabSize=8:noTabs=true: