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));
140 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
144 va_start(ap, msg_format);
145 SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
153 * Alert box, with optional "don't show this message again" variable
154 * and checkbox, and optional secondary text.
157 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
158 const char *secondary_msg, const char *msg_format, ...)
160 if (notagain && *notagain) {
166 va_start(ap, msg_format);
167 SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
170 sd.setDetailedText(secondary_msg);
172 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
173 QCheckBox *cb = NULL;
175 cb = new QCheckBox();
176 cb->setChecked(true);
177 cb->setText(QObject::tr("Don't show this message again."));
184 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
185 if (notagain && cb) {
186 *notagain = cb->isChecked();
192 * Error alert box, taking a format and a va_list argument.
195 vsimple_error_message_box(const char *msg_format, va_list ap)
197 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
202 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
203 QList<QAction *> actions = parent_menu->actions();
204 QList<QAction *>::const_iterator i;
205 for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
206 if ((*i)->text()==menu_text) {
210 // If we get here there menu entry was not found, add a sub menu
211 return parent_menu->addMenu(menu_text);
214 MainWindow::MainWindow(QWidget *parent) :
216 main_ui_(new Ui::MainWindow),
217 cur_layout_(QVector<unsigned>()),
218 df_combo_box_(new DisplayFilterCombo()),
221 previous_focus_(NULL),
222 show_hide_actions_(NULL),
223 time_display_actions_(NULL),
224 time_precision_actions_(NULL),
225 funnel_statistics_(new FunnelStatistics(this, capture_file_)),
227 capture_stopping_(false),
228 capture_filter_valid_(false),
235 if (!gbl_cur_main_window_) {
236 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
237 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
238 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
239 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
241 gbl_cur_main_window_ = this;
243 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
245 main_ui_->setupUi(this);
246 setWindowIcon(wsApp->normalIcon());
247 setTitlebarForCaptureFile();
248 setMenusForCaptureFile();
249 setForCapturedPackets(false);
250 setMenusForFileSet(false);
251 interfaceSelectionChanged();
252 loadWindowGeometry();
255 main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
258 //To prevent users use features before initialization complete
259 //Otherwise unexpected problems may occur
260 setFeaturesEnabled(false);
261 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
262 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
263 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
264 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
265 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
266 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
268 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
269 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
270 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
271 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
272 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
274 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
278 connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
279 connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
282 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
283 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
284 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
285 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
286 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
287 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
288 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
289 connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
290 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
291 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
293 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
294 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
295 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
296 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
297 this, SLOT(openCaptureFile(QString,QString)));
298 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
300 initMainToolbarIcons();
302 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
303 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
304 // https://bugreports.qt-project.org/browse/QTBUG-22433
305 // This property is obsolete in Qt5 so this issue may be fixed in that version.
306 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
308 wireless_frame_ = new WirelessFrame(this);
309 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
310 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
311 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
312 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
313 this, SLOT(showPreferencesDialog(QString)));
315 main_ui_->goToFrame->hide();
316 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
317 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
319 // XXX For some reason the cursor is drawn funny with an input mask set
320 // https://bugreports.qt-project.org/browse/QTBUG-7174
322 main_ui_->searchFrame->hide();
323 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
324 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
325 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
326 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
328 main_ui_->addressEditorFrame->hide();
329 main_ui_->columnEditorFrame->hide();
330 main_ui_->preferenceEditorFrame->hide();
331 main_ui_->filterExpressionFrame->hide();
334 main_ui_->menuCapture->setEnabled(false);
337 #if defined(Q_OS_MAC)
338 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
339 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
340 ntb->setIconSize(QSize(24, 24));
341 #endif // QT_MACEXTRAS_LIB
343 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
344 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
345 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
347 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
351 #ifdef HAVE_SOFTWARE_UPDATE
352 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
353 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
354 main_ui_->menuHelp->insertAction(update_sep, update_action);
355 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
357 master_split_.setObjectName("splitterMaster");
358 extra_split_.setObjectName("splitterExtra");
359 main_ui_->mainStack->addWidget(&master_split_);
361 empty_pane_.setObjectName("emptyPane");
363 packet_list_ = new PacketList(&master_split_);
365 proto_tree_ = new ProtoTree(&master_split_);
366 proto_tree_->installEventFilter(this);
368 byte_view_tab_ = new ByteViewTab(&master_split_);
370 packet_list_->setProtoTree(proto_tree_);
371 packet_list_->setByteViewTab(byte_view_tab_);
372 packet_list_->installEventFilter(this);
374 main_welcome_ = main_ui_->welcomePage;
376 // Packet list and proto tree must exist before these are called.
377 setMenusForSelectedPacket();
378 setMenusForSelectedTreeRow();
380 initShowHideMainWidgets();
381 initTimeDisplayFormatMenu();
382 initTimePrecisionFormatMenu();
384 updatePreferenceActions();
385 updateRecentActions();
386 setForCaptureInProgress(false);
388 setTabOrder(df_combo_box_, packet_list_);
390 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
391 this, SLOT(captureCapturePrepared(capture_session *)));
392 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
393 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
394 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
395 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
396 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
397 this, SLOT(captureCaptureFixedStarted(capture_session *)));
398 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
399 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
400 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
401 this, SLOT(captureCaptureFixedFinished(capture_session *)));
402 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
403 this, SLOT(captureCaptureStopping(capture_session *)));
404 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
405 this, SLOT(captureCaptureFailed(capture_session *)));
406 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
407 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
409 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
410 wsApp, SLOT(captureStarted()));
411 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
412 wsApp, SLOT(captureFinished()));
413 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
414 wsApp, SLOT(captureStarted()));
415 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
416 wsApp, SLOT(captureFinished()));
418 connect(&capture_file_, SIGNAL(captureFileOpened()),
419 this, SLOT(captureFileOpened()));
420 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
421 this, SLOT(captureFileReadStarted()));
422 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
423 this, SLOT(captureFileReadFinished()));
424 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
425 this, SLOT(captureFileReloadStarted()));
426 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
427 this, SLOT(captureFileReadFinished()));
428 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
429 this, SLOT(captureFileRescanStarted()));
430 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
431 this, SLOT(captureFileReadFinished()));
432 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
433 this, SLOT(captureFileRetapStarted()));
434 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
435 this, SLOT(captureFileRetapFinished()));
436 connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
437 this, SLOT(captureFileFlushTapsData()));
438 connect(&capture_file_, SIGNAL(captureFileClosing()),
439 this, SLOT(captureFileClosing()));
440 connect(&capture_file_, SIGNAL(captureFileClosed()),
441 this, SLOT(captureFileClosed()));
443 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
444 this, SLOT(captureFileSaveStarted(QString)));
445 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
446 main_ui_->statusBar, SLOT(popFileStatus()));
447 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
448 main_ui_->statusBar, SLOT(popFileStatus()));
449 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
450 main_ui_->statusBar, SLOT(popFileStatus()));
452 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
453 wsApp, SLOT(captureFileReadStarted()));
454 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
455 wsApp, SLOT(updateTaps()));
457 connect(wsApp, SIGNAL(columnsChanged()),
458 packet_list_, SLOT(columnsChanged()));
459 connect(wsApp, SIGNAL(preferencesChanged()),
460 packet_list_, SLOT(preferencesChanged()));
461 connect(wsApp, SIGNAL(recentFilesRead()),
462 this, SLOT(applyRecentPaneGeometry()));
463 connect(wsApp, SIGNAL(recentFilesRead()),
464 this, SLOT(updateRecentActions()));
465 connect(wsApp, SIGNAL(packetDissectionChanged()),
466 this, SLOT(redissectPackets()));
467 connect(wsApp, SIGNAL(appInitialized()),
468 this, SLOT(filterExpressionsChanged()));
469 connect(wsApp, SIGNAL(filterExpressionsChanged()),
470 this, SLOT(filterExpressionsChanged()));
471 connect(wsApp, SIGNAL(checkDisplayFilter()),
472 this, SLOT(checkDisplayFilter()));
473 connect(wsApp, SIGNAL(fieldsChanged()),
474 this, SLOT(fieldsChanged()));
476 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
477 this, SLOT(mainStackChanged(int)));
479 connect(main_welcome_, SIGNAL(startCapture()),
480 this, SLOT(startCapture()));
481 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
482 this, SLOT(openCaptureFile(QString)));
483 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
484 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
485 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
486 main_ui_->statusBar, SLOT(popFilterStatus()));
488 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
489 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
490 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
491 this, SLOT(redissectPackets()));
492 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
493 this, SLOT(showPreferencesDialog(QString)));
494 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
495 this, SLOT(showPreferencesDialog(QString)));
496 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
497 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
498 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
499 this, SLOT(filterExpressionsChanged()));
501 connect(this, SIGNAL(setCaptureFile(capture_file*)),
502 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
503 connect(this, SIGNAL(setCaptureFile(capture_file*)),
504 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
505 connect(this, SIGNAL(setCaptureFile(capture_file*)),
506 packet_list_, SLOT(setCaptureFile(capture_file*)));
507 connect(this, SIGNAL(setCaptureFile(capture_file*)),
508 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
510 connect(this, SIGNAL(monospaceFontChanged(QFont)),
511 packet_list_, SLOT(setMonospaceFont(QFont)));
512 connect(this, SIGNAL(monospaceFontChanged(QFont)),
513 proto_tree_, SLOT(setMonospaceFont(QFont)));
514 connect(this, SIGNAL(monospaceFontChanged(QFont)),
515 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
517 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
518 packet_list_, SLOT(goNextPacket()));
519 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
520 packet_list_, SLOT(goPreviousPacket()));
521 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
522 packet_list_, SLOT(goFirstPacket()));
523 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
524 packet_list_, SLOT(goLastPacket()));
526 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
527 proto_tree_, SLOT(expandSubtrees()));
528 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
529 proto_tree_, SLOT(expandAll()));
530 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
531 proto_tree_, SLOT(collapseAll()));
533 connect(packet_list_, SIGNAL(packetSelectionChanged()),
534 this, SLOT(setMenusForSelectedPacket()));
535 connect(packet_list_, SIGNAL(packetDissectionChanged()),
536 this, SLOT(redissectPackets()));
537 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
538 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
539 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
540 this, SLOT(showPreferencesDialog(QString)));
541 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
542 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
543 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
544 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
545 packet_list_, SLOT(columnsChanged()));
546 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
547 this, SLOT(openPacketDialog()));
548 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
549 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
550 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
551 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
552 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
553 main_ui_->statusBar, SLOT(popBusyStatus()));
554 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
555 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
556 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
557 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
558 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
559 main_ui_->statusBar, SLOT(popProgressStatus()));
561 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
562 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
563 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
564 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
565 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
566 this, SLOT(openPacketDialog(bool)));
567 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
568 this, SLOT(showPreferencesDialog(QString)));
569 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
570 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
572 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
573 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
575 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
576 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
578 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
579 &capture_file_, SLOT(stopLoading()));
581 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
582 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
584 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
585 this, SLOT(openCaptureFile(QString)));
588 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
590 connect(iface_tree, SIGNAL(itemSelectionChanged()),
591 this, SLOT(interfaceSelectionChanged()));
593 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
594 this, SLOT(captureFilterSyntaxChanged(bool)));
597 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
598 this, SLOT(showExtcapOptionsDialog(QString&)));
601 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
602 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
603 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
604 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
605 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
606 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
609 /* Create plugin_if hooks */
610 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter );
611 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter );
612 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
613 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
615 main_ui_->mainStack->setCurrentWidget(main_welcome_);
618 MainWindow::~MainWindow()
623 QString MainWindow::getFilter()
625 return df_combo_box_->itemText(df_combo_box_->count());
628 QMenu *MainWindow::createPopupMenu()
630 QMenu *menu = new QMenu();
631 menu->addAction(main_ui_->actionViewMainToolbar);
632 menu->addAction(main_ui_->actionViewFilterToolbar);
633 menu->addAction(main_ui_->actionViewWirelessToolbar);
634 menu->addAction(main_ui_->actionViewStatusBar);
635 menu->addSeparator();
636 menu->addAction(main_ui_->actionViewPacketList);
637 menu->addAction(main_ui_->actionViewPacketDetails);
638 menu->addAction(main_ui_->actionViewPacketBytes);
642 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
644 pipe_source_ = source;
645 pipe_child_process_ = child_process;
646 pipe_user_data_ = user_data;
647 pipe_input_cb_ = input_cb;
650 /* Tricky to use pipes in win9x, as no concept of wait. NT can
651 do this but that doesn't cover all win32 platforms. GTK can do
652 this but doesn't seem to work over processes. Attempt to do
653 something similar here, start a timer and check for data on every
655 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
658 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
662 pipe_timer_ = new QTimer(this);
663 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
664 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
665 pipe_timer_->start(200);
667 if (pipe_notifier_) {
668 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
669 delete pipe_notifier_;
672 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
673 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
674 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
675 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
679 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
681 // The user typed some text. Start filling in a filter.
682 // We may need to be more choosy here. We just need to catch events for the packet list,
683 // proto tree, and main welcome widgets.
684 if (event->type() == QEvent::KeyPress) {
685 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
686 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
687 df_combo_box_->lineEdit()->insert(kevt->text());
688 df_combo_box_->lineEdit()->setFocus();
693 return QMainWindow::eventFilter(obj, event);
696 void MainWindow::keyPressEvent(QKeyEvent *event) {
698 // Explicitly focus on the display filter combo.
699 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
700 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
704 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
705 if (event->modifiers() == Qt::NoModifier) {
706 if (event->key() == Qt::Key_Escape) {
707 on_goToCancel_clicked();
708 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
712 return; // goToLineEdit didn't want it and we don't either.
715 // Move up & down the packet list.
716 if (event->key() == Qt::Key_F7) {
717 packet_list_->goPreviousPacket();
718 } else if (event->key() == Qt::Key_F8) {
719 packet_list_->goNextPacket();
722 // Move along, citizen.
723 QMainWindow::keyPressEvent(event);
726 void MainWindow::closeEvent(QCloseEvent *event) {
727 saveWindowGeometry();
729 /* If we're in the middle of stopping a capture, don't do anything;
730 the user can try deleting the window after the capture stops. */
731 if (capture_stopping_) {
736 QString before_what(tr(" before quitting"));
737 if (!testCaptureFileClose(TRUE, before_what)) {
743 capture_interfaces_dialog_.close();
745 // Make sure we kill any open dumpcap processes.
746 delete main_welcome_;
748 // One of the many places we assume one main window.
749 if(!wsApp->isInitialized()) {
750 // If we're still initializing, QCoreApplication::quit() won't
751 // exit properly because we are not in the event loop. This
752 // means that the application won't clean up after itself. We
753 // might want to call wsApp->processEvents() during startup
754 // instead so that we can do a normal exit here.
760 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
763 foreach (QUrl drag_url, event->mimeData()->urls()) {
764 if (!drag_url.toLocalFile().isEmpty()) {
769 if (accept) event->acceptProposedAction();
772 void MainWindow::dropEvent(QDropEvent *event)
774 foreach (QUrl drop_url, event->mimeData()->urls()) {
775 QString local_file = drop_url.toLocalFile();
776 if (!local_file.isEmpty()) {
777 event->acceptProposedAction();
778 openCaptureFile(local_file);
784 // Apply recent settings to the main window geometry.
785 // We haven't loaded the preferences at this point so we assume that the
786 // position and size preference are enabled.
787 void MainWindow::loadWindowGeometry()
789 int min_sensible_dimension = 200;
792 if (recent.gui_geometry_main_maximized) {
793 setWindowState(Qt::WindowMaximized);
797 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
798 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
799 if (!rect_on_screen(recent_geom)) {
800 // We're not visible on any screens. Give up and use the default geometry.
804 // if (prefs.gui_geometry_save_position) {
805 move(recent_geom.topLeft());
808 if (// prefs.gui_geometry_save_size &&
809 recent_geom.width() > min_sensible_dimension &&
810 recent_geom.height() > min_sensible_dimension) {
811 resize(recent_geom.size());
816 void MainWindow::saveWindowGeometry()
818 if (prefs.gui_geometry_save_position) {
819 recent.gui_geometry_main_x = pos().x();
820 recent.gui_geometry_main_y = pos().y();
823 if (prefs.gui_geometry_save_size) {
824 recent.gui_geometry_main_width = size().width();
825 recent.gui_geometry_main_height = size().height();
828 if (prefs.gui_geometry_save_maximized) {
829 // On OS X this is false when it shouldn't be
830 recent.gui_geometry_main_maximized = isMaximized();
833 if (master_split_.sizes().length() > 0) {
834 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
837 if (master_split_.sizes().length() > 2) {
838 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
839 } else if (extra_split_.sizes().length() > 0) {
840 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
844 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
846 case layout_pane_content_none:
848 case layout_pane_content_plist:
850 case layout_pane_content_pdetails:
852 case layout_pane_content_pbytes:
853 return byte_view_tab_;
855 g_assert_not_reached();
860 // Our event loop becomes nested whenever we call update_progress_dlg, which
861 // includes several places in file.c. The GTK+ UI stays out of trouble by
862 // showing a modal progress dialog. We attempt to do the equivalent below by
863 // disabling parts of the main window. At a minumum the ProgressFrame in the
864 // main status bar must remain accessible.
866 // We might want to do this any time the main status bar progress frame is
868 void MainWindow::freeze()
870 freeze_focus_ = wsApp->focusWidget();
872 // XXX Alternatively we could just disable and enable the main menu.
873 for (int i = 0; i < freeze_actions_.size(); i++) {
874 QAction *action = freeze_actions_[i].first;
875 freeze_actions_[i].second = action->isEnabled();
876 action->setEnabled(false);
878 main_ui_->centralWidget->setEnabled(false);
881 void MainWindow::thaw()
883 main_ui_->centralWidget->setEnabled(true);
884 for (int i = 0; i < freeze_actions_.size(); i++) {
885 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
888 if (freeze_focus_) freeze_focus_->setFocus();
889 freeze_focus_ = NULL;
892 void MainWindow::mergeCaptureFile()
894 QString file_name = "";
895 QString read_filter = "";
896 dfilter_t *rfcode = NULL;
899 if (!capture_file_.capFile())
902 if (prefs.gui_ask_unsaved) {
903 if (cf_has_unsaved_data(capture_file_.capFile())) {
904 QMessageBox msg_dialog;
905 gchar *display_basename;
908 msg_dialog.setIcon(QMessageBox::Question);
909 /* This file has unsaved data; ask the user whether to save
911 if (capture_file_.capFile()->is_tempfile) {
912 msg_dialog.setText(tr("Save packets before merging?"));
913 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
916 * Format the message.
918 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
919 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
920 g_free(display_basename);
921 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
924 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
925 msg_dialog.setDefaultButton(QMessageBox::Save);
927 response = msg_dialog.exec();
931 case QMessageBox::Save:
932 /* Save the file but don't close it */
933 saveCaptureFile(capture_file_.capFile(), FALSE);
936 case QMessageBox::Cancel:
938 /* Don't do the merge. */
945 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
947 cf_status_t merge_status;
948 char *in_filenames[2];
951 switch (prefs.gui_fileopen_style) {
953 case FO_STYLE_LAST_OPENED:
954 /* The user has specified that we should start out in the last directory
955 we looked in. If we've already opened a file, use its containing
956 directory, if we could determine it, as the directory, otherwise
957 use the "last opened" directory saved in the preferences file if
959 /* This is now the default behaviour in file_selection_new() */
962 case FO_STYLE_SPECIFIED:
963 /* The user has specified that we should always start out in a
964 specified directory; if they've specified that directory,
965 start out by showing the files in that dir. */
966 if (prefs.gui_fileopen_dir[0] != '\0')
967 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
971 if (merge_dlg.merge(file_name)) {
974 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
975 /* Not valid. Tell the user, and go back and run the file
976 selection box again once they dismiss the alert. */
977 //bad_dfilter_alert_box(top_level, read_filter->str);
978 QMessageBox::warning(this, tr("Invalid Read Filter"),
979 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
988 file_type = capture_file_.capFile()->cd_t;
990 /* Try to merge or append the two files */
992 if (merge_dlg.mergeType() == 0) {
993 /* chronological order */
994 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
995 in_filenames[1] = qstring_strdup(file_name);
996 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
997 } else if (merge_dlg.mergeType() <= 0) {
999 in_filenames[0] = qstring_strdup(file_name);
1000 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1001 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1004 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1005 in_filenames[1] = qstring_strdup(file_name);
1006 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1009 g_free(in_filenames[0]);
1010 g_free(in_filenames[1]);
1012 if (merge_status != CF_OK) {
1014 dfilter_free(rfcode);
1019 cf_close(capture_file_.capFile());
1021 /* Try to open the merged capture file. */
1022 CaptureFile::globalCapFile()->window = this;
1023 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1024 /* We couldn't open it; fail. */
1025 CaptureFile::globalCapFile()->window = NULL;
1027 dfilter_free(rfcode);
1032 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1033 it closed the previous capture file, and thus destroyed any
1034 previous read filter attached to "cf"). */
1035 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1037 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1041 /* Just because we got an error, that doesn't mean we were unable
1042 to read any of the file; we handle what we could get from the
1046 case CF_READ_ABORTED:
1047 /* The user bailed out of re-reading the capture file; the
1048 capture file has been closed - just free the capture file name
1049 string and return (without changing the last containing
1055 /* Save the name of the containing directory specified in the path name,
1056 if any; we can write over cf_merged_name, which is a good thing, given that
1057 "get_dirname()" does write over its argument. */
1058 wsApp->setLastOpenDir(get_dirname(tmpname));
1060 main_ui_->statusBar->showExpert();
1066 void MainWindow::importCaptureFile() {
1067 ImportTextDialog import_dlg;
1069 QString before_what(tr(" before importing a new capture"));
1070 if (!testCaptureFileClose(FALSE, before_what))
1075 if (import_dlg.result() != QDialog::Accepted) {
1076 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1080 openCaptureFile(import_dlg.capfileName());
1083 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1085 gboolean discard_comments;
1087 if (cf->is_tempfile) {
1088 /* This is a temporary capture file, so saving it means saving
1089 it to a permanent file. Prompt the user for a location
1090 to which to save it. Don't require that the file format
1091 support comments - if it's a temporary capture file, it's
1092 probably pcap-ng, which supports comments and, if it's
1093 not pcap-ng, let the user decide what they want to do
1094 if they've added comments. */
1095 saveAsCaptureFile(cf, FALSE, dont_reopen);
1097 if (cf->unsaved_changes) {
1098 cf_write_status_t status;
1100 /* This is not a temporary capture file, but it has unsaved
1101 changes, so saving it means doing a "safe save" on top
1102 of the existing file, in the same format - no UI needed
1103 unless the file has comments and the file's format doesn't
1106 If the file has comments, does the file's format support them?
1107 If not, ask the user whether they want to discard the comments
1108 or choose a different format. */
1109 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1112 /* The file can be saved in the specified format as is;
1113 just drive on and save in the format they selected. */
1114 discard_comments = FALSE;
1117 case SAVE_WITHOUT_COMMENTS:
1118 /* The file can't be saved in the specified format as is,
1119 but it can be saved without the comments, and the user
1120 said "OK, discard the comments", so save it in the
1121 format they specified without the comments. */
1122 discard_comments = TRUE;
1125 case SAVE_IN_ANOTHER_FORMAT:
1126 /* There are file formats in which we can save this that
1127 support comments, and the user said not to delete the
1128 comments. Do a "Save As" so the user can select
1129 one of those formats and choose a file name. */
1130 saveAsCaptureFile(cf, TRUE, dont_reopen);
1134 /* The user said "forget it". Just return. */
1138 /* Squelch warnings that discard_comments is being used
1140 g_assert_not_reached();
1144 /* XXX - cf->filename might get freed out from under us, because
1145 the code path through which cf_save_records() goes currently
1146 closes the current file and then opens and reloads the saved file,
1147 so make a copy and free it later. */
1148 file_name = cf->filename;
1149 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1150 discard_comments, dont_reopen);
1154 /* The save succeeded; we're done.
1155 If we discarded comments, redraw the packet list to reflect
1156 any packets that no longer have comments. */
1157 if (discard_comments)
1158 packet_list_queue_draw();
1160 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1161 updateForUnsavedChanges(); // we update the title bar to remove the *
1164 case CF_WRITE_ERROR:
1165 /* The write failed.
1166 XXX - OK, what do we do now? Let them try a
1167 "Save As", in case they want to try to save to a
1168 different directory r file system? */
1171 case CF_WRITE_ABORTED:
1172 /* The write was aborted; just drive on. */
1176 /* Otherwise just do nothing. */
1180 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1181 QString file_name = "";
1183 gboolean compressed;
1184 cf_write_status_t status;
1186 gboolean discard_comments = FALSE;
1193 CaptureFileDialog save_as_dlg(this, cf);
1195 switch (prefs.gui_fileopen_style) {
1197 case FO_STYLE_LAST_OPENED:
1198 /* The user has specified that we should start out in the last directory
1199 we looked in. If we've already opened a file, use its containing
1200 directory, if we could determine it, as the directory, otherwise
1201 use the "last opened" directory saved in the preferences file if
1203 /* This is now the default behaviour in file_selection_new() */
1206 case FO_STYLE_SPECIFIED:
1207 /* The user has specified that we should always start out in a
1208 specified directory; if they've specified that directory,
1209 start out by showing the files in that dir. */
1210 if (prefs.gui_fileopen_dir[0] != '\0')
1211 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1215 /* If the file has comments, does the format the user selected
1216 support them? If not, ask the user whether they want to
1217 discard the comments or choose a different format. */
1218 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1221 /* The file can be saved in the specified format as is;
1222 just drive on and save in the format they selected. */
1223 discard_comments = FALSE;
1226 case SAVE_WITHOUT_COMMENTS:
1227 /* The file can't be saved in the specified format as is,
1228 but it can be saved without the comments, and the user
1229 said "OK, discard the comments", so save it in the
1230 format they specified without the comments. */
1231 discard_comments = TRUE;
1234 case SAVE_IN_ANOTHER_FORMAT:
1235 /* There are file formats in which we can save this that
1236 support comments, and the user said not to delete the
1237 comments. The combo box of file formats has had the
1238 formats that don't support comments trimmed from it,
1239 so run the dialog again, to let the user decide
1240 whether to save in one of those formats or give up. */
1241 must_support_comments = TRUE;
1245 /* The user said "forget it". Just get rid of the dialog box
1249 file_type = save_as_dlg.selectedFileType();
1250 compressed = save_as_dlg.isCompressed();
1252 fileAddExtension(file_name, file_type, compressed);
1255 // /* If the file exists and it's user-immutable or not writable,
1256 // ask the user whether they want to override that. */
1257 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1258 // /* They don't. Let them try another file name or cancel. */
1263 /* Attempt to save the file */
1264 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1265 discard_comments, dont_reopen);
1269 /* The save succeeded; we're done. */
1270 /* Save the directory name for future file dialogs. */
1271 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1272 set_last_open_dir(dirname);
1274 /* If we discarded comments, redraw the packet list to reflect
1275 any packets that no longer have comments. */
1276 if (discard_comments)
1277 packet_list_queue_draw();
1279 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1280 updateForUnsavedChanges(); // we update the title bar to remove the *
1283 case CF_WRITE_ERROR:
1284 /* The save failed; let the user try again. */
1287 case CF_WRITE_ABORTED:
1288 /* The user aborted the save; just return. */
1295 void MainWindow::exportSelectedPackets() {
1296 QString file_name = "";
1298 gboolean compressed;
1299 packet_range_t range;
1300 cf_write_status_t status;
1302 gboolean discard_comments = FALSE;
1304 if (!capture_file_.capFile())
1307 /* Init the packet range */
1308 packet_range_init(&range, capture_file_.capFile());
1309 range.process_filtered = TRUE;
1310 range.include_dependents = TRUE;
1313 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1315 switch (prefs.gui_fileopen_style) {
1317 case FO_STYLE_LAST_OPENED:
1318 /* The user has specified that we should start out in the last directory
1319 we looked in. If we've already opened a file, use its containing
1320 directory, if we could determine it, as the directory, otherwise
1321 use the "last opened" directory saved in the preferences file if
1323 /* This is now the default behaviour in file_selection_new() */
1326 case FO_STYLE_SPECIFIED:
1327 /* The user has specified that we should always start out in a
1328 specified directory; if they've specified that directory,
1329 start out by showing the files in that dir. */
1330 if (prefs.gui_fileopen_dir[0] != '\0')
1331 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1335 /* If the file has comments, does the format the user selected
1336 support them? If not, ask the user whether they want to
1337 discard the comments or choose a different format. */
1338 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1341 /* The file can be saved in the specified format as is;
1342 just drive on and save in the format they selected. */
1343 discard_comments = FALSE;
1346 case SAVE_WITHOUT_COMMENTS:
1347 /* The file can't be saved in the specified format as is,
1348 but it can be saved without the comments, and the user
1349 said "OK, discard the comments", so save it in the
1350 format they specified without the comments. */
1351 discard_comments = TRUE;
1354 case SAVE_IN_ANOTHER_FORMAT:
1355 /* There are file formats in which we can save this that
1356 support comments, and the user said not to delete the
1357 comments. The combo box of file formats has had the
1358 formats that don't support comments trimmed from it,
1359 so run the dialog again, to let the user decide
1360 whether to save in one of those formats or give up. */
1364 /* The user said "forget it". Just get rid of the dialog box
1370 * Check that we're not going to save on top of the current
1372 * We do it here so we catch all cases ...
1373 * Unfortunately, the file requester gives us an absolute file
1374 * name and the read file name may be relative (if supplied on
1375 * the command line). From Joerg Mayer.
1377 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1378 QMessageBox msg_box;
1379 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1381 msg_box.setIcon(QMessageBox::Critical);
1382 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1383 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1384 msg_box.setStandardButtons(QMessageBox::Ok);
1385 msg_box.setDefaultButton(QMessageBox::Ok);
1387 g_free(display_basename);
1391 file_type = esp_dlg.selectedFileType();
1392 compressed = esp_dlg.isCompressed();
1393 fileAddExtension(file_name, file_type, compressed);
1396 // /* If the file exists and it's user-immutable or not writable,
1397 // ask the user whether they want to override that. */
1398 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1399 // /* They don't. Let them try another file name or cancel. */
1404 /* Attempt to save the file */
1405 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1409 /* The save succeeded; we're done. */
1410 /* Save the directory name for future file dialogs. */
1411 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1412 set_last_open_dir(dirname);
1414 /* If we discarded comments, redraw the packet list to reflect
1415 any packets that no longer have comments. */
1416 if (discard_comments)
1417 packet_list_queue_draw();
1420 case CF_WRITE_ERROR:
1421 /* The save failed; let the user try again. */
1424 case CF_WRITE_ABORTED:
1425 /* The user aborted the save; just return. */
1432 void MainWindow::exportDissections(export_type_e export_type) {
1433 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1434 packet_range_t range;
1436 if (!capture_file_.capFile())
1439 /* Init the packet range */
1440 packet_range_init(&range, capture_file_.capFile());
1441 range.process_filtered = TRUE;
1442 range.include_dependents = TRUE;
1447 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1448 QString file_name_lower;
1449 QString file_suffix;
1450 GSList *extensions_list;
1451 gboolean add_extension;
1454 * Append the default file extension if there's none given by
1455 * the user or if they gave one that's not one of the valid
1456 * extensions for the file type.
1458 file_name_lower = file_name.toLower();
1459 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1460 if (extensions_list != NULL) {
1463 /* We have one or more extensions for this file type.
1464 Start out assuming we need to add the default one. */
1465 add_extension = TRUE;
1467 /* OK, see if the file has one of those extensions. */
1468 for (extension = extensions_list; extension != NULL;
1469 extension = g_slist_next(extension)) {
1470 file_suffix += tr(".") + (char *)extension->data;
1471 if (file_name_lower.endsWith(file_suffix)) {
1473 * The file name has one of the extensions for
1476 add_extension = FALSE;
1479 file_suffix += ".gz";
1480 if (file_name_lower.endsWith(file_suffix)) {
1482 * The file name has one of the extensions for
1485 add_extension = FALSE;
1490 /* We have no extensions for this file type. Don't add one. */
1491 add_extension = FALSE;
1493 if (add_extension) {
1494 if (wtap_default_file_extension(file_type) != NULL) {
1495 file_name += tr(".") + wtap_default_file_extension(file_type);
1503 bool MainWindow::testCaptureFileClose(bool from_quit, QString before_what, bool restart) {
1504 bool capture_in_progress = FALSE;
1506 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1507 return true; /* Already closed, nothing to do */
1510 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS && capture_file_.capFile()->count>0) {
1511 /* This is true if we're reading a capture file *or* if we're doing
1512 a live capture. If we're reading a capture file, the main loop
1513 is busy reading packets, and only accepting input from the
1514 progress dialog, so we can't get here, so this means we're
1516 capture_in_progress = TRUE;
1520 if (prefs.gui_ask_unsaved) {
1521 if (cf_has_unsaved_data(capture_file_.capFile()) || capture_in_progress) {
1522 QMessageBox msg_dialog;
1524 QPushButton *saveButton;
1525 QPushButton *discardButton;
1527 msg_dialog.setIcon(QMessageBox::Question);
1528 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1529 /* This file has unsaved data or there's a capture in
1530 progress; ask the user whether to save the data. */
1531 if (capture_file_.capFile()->is_tempfile) {
1533 msg_dialog.setText(tr("You have unsaved packets"));
1534 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1536 if (capture_in_progress) {
1537 question.append(tr("Do you want to stop the capture and save the captured packets"));
1539 question.append(tr("Do you want to save the captured packets"));
1541 question.append(before_what).append(tr("?"));
1542 msg_dialog.setInformativeText(question);
1547 * Format the message.
1549 if (capture_in_progress) {
1550 question.append(tr("Do you want to stop the capture and save the captured packets"));
1551 question.append(before_what).append(tr("?"));
1552 msg_dialog.setText(question);
1553 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1555 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1556 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1557 .arg(display_basename)
1560 g_free(display_basename);
1561 msg_dialog.setText(question);
1562 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1566 // XXX Text comes from ui/gtk/stock_icons.[ch]
1567 // Note that the button roles differ from the GTK+ version.
1568 // Cancel = RejectRole
1569 // Save = AcceptRole
1570 // Don't Save = DestructiveRole
1571 msg_dialog.addButton(QMessageBox::Cancel);
1573 if (capture_in_progress) {
1575 saveButton = msg_dialog.addButton(tr("Save before Continue"), QMessageBox::AcceptRole);
1577 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1580 saveButton = msg_dialog.addButton(QMessageBox::Save);
1582 msg_dialog.setDefaultButton(saveButton);
1585 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1586 discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1587 QMessageBox::DestructiveRole);
1589 discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1590 QMessageBox::DestructiveRole);
1593 if (capture_in_progress) {
1595 discardButton = msg_dialog.addButton(tr("Continue without Saving"),
1596 QMessageBox::DestructiveRole);
1599 discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1600 QMessageBox::DestructiveRole);
1603 discardButton = msg_dialog.addButton(tr("Continue &without Saving"), QMessageBox::DestructiveRole);
1608 /* According to the Qt doc:
1609 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1611 * Therefore we should use clickedButton() to determine which button was clicked. */
1613 if(msg_dialog.clickedButton() == saveButton)
1616 /* If there's a capture in progress, we have to stop the capture
1617 and then do the save. */
1618 if (capture_in_progress)
1621 /* Save the file and close it */
1622 saveCaptureFile(capture_file_.capFile(), TRUE);
1624 else if(msg_dialog.clickedButton() == discardButton)
1630 * If there's a capture in progress; we have to stop the capture
1631 * and then do the close.
1633 if (capture_in_progress)
1636 /* Just close the file, discarding changes */
1637 cf_close(capture_file_.capFile());
1641 else //cancelButton or some other unspecified button
1647 /* Unchanged file, just close it */
1648 capture_file_.capFile()->state = FILE_READ_ABORTED;
1649 cf_close(capture_file_.capFile());
1652 /* User asked not to be bothered by those prompts, just close it.
1653 XXX - should that apply only to saving temporary files? */
1655 /* If there's a capture in progress, we have to stop the capture
1656 and then do the close. */
1657 if (capture_in_progress)
1660 cf_close(capture_file_.capFile());
1663 return true; /* File closed */
1666 void MainWindow::captureStop() {
1669 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1670 WiresharkApplication::processEvents();
1674 void MainWindow::initMainToolbarIcons()
1676 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1677 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1678 #if !defined(Q_OS_WIN)
1679 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1680 // The OS X HIG specifies 32-pixel icons but they're a little too
1682 icon_size = icon_size * 3 / 2;
1684 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1686 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1687 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1689 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1690 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1691 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1692 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1694 // Menu icons are disabled in main_window.ui for these items.
1695 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1696 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1697 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1698 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1700 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1701 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1702 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1703 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1704 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1705 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1706 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1707 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1708 #if defined(Q_OS_MAC)
1709 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1710 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1712 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1714 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1715 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1717 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1718 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1719 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1720 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1721 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1722 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1723 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1726 void MainWindow::initShowHideMainWidgets()
1728 if (show_hide_actions_) {
1732 show_hide_actions_ = new QActionGroup(this);
1733 QMap<QAction *, QWidget *> shmw_actions;
1735 show_hide_actions_->setExclusive(false);
1736 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1737 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1738 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1739 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1740 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1741 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1742 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1744 foreach (QAction *shmwa, shmw_actions.keys()) {
1745 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1746 show_hide_actions_->addAction(shmwa);
1747 showHideMainWidgets(shmwa);
1750 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1753 Q_DECLARE_METATYPE(ts_type)
1755 void MainWindow::initTimeDisplayFormatMenu()
1757 if (time_display_actions_) {
1761 time_display_actions_ = new QActionGroup(this);
1763 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1764 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1765 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1766 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1767 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1768 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1769 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1770 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1771 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1772 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1774 foreach (QAction* tda, td_actions.keys()) {
1775 tda->setData(qVariantFromValue(td_actions[tda]));
1776 time_display_actions_->addAction(tda);
1779 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1782 Q_DECLARE_METATYPE(ts_precision)
1784 void MainWindow::initTimePrecisionFormatMenu()
1786 if (time_precision_actions_) {
1790 time_precision_actions_ = new QActionGroup(this);
1792 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1793 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1794 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1795 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1796 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1797 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1798 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1800 foreach (QAction* tpa, tp_actions.keys()) {
1801 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1802 time_precision_actions_->addAction(tpa);
1805 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1808 // Menu items which will be disabled when we freeze() and whose state will
1809 // be restored when we thaw(). Add to the list as needed.
1810 void MainWindow::initFreezeActions()
1812 QList<QAction *> freeze_actions = QList<QAction *>()
1813 << main_ui_->actionFileClose
1814 << main_ui_->actionViewReload
1815 << main_ui_->actionEditMarkPacket
1816 << main_ui_->actionEditMarkAllDisplayed
1817 << main_ui_->actionEditUnmarkAllDisplayed
1818 << main_ui_->actionEditIgnorePacket
1819 << main_ui_->actionEditIgnoreAllDisplayed
1820 << main_ui_->actionEditUnignoreAllDisplayed
1821 << main_ui_->actionEditSetTimeReference
1822 << main_ui_->actionEditUnsetAllTimeReferences;
1824 foreach (QAction *action, freeze_actions) {
1825 freeze_actions_ << QPair<QAction *, bool>(action, false);
1830 void MainWindow::setTitlebarForCaptureFile()
1832 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1834 // Qt *REALLY* doesn't like windows that sometimes have a
1835 // title set with setWindowTitle() and other times have a
1836 // file path set; apparently, once you've set the title
1837 // with setWindowTitle(), it sticks, and setWindowFilePath()
1838 // has no effect. It appears to can clear the title with
1839 // setWindowTitle(NULL), but that clears the actual title in
1840 // the title bar, and setWindowFilePath() then, I guess, sees
1841 // that there's already a file path, and does nothing, leaving
1842 // the title bar empty. So you then have to clear the file path
1843 // with setWindowFilePath(NULL), and then set it.
1845 // Maybe there's a #include "you're holding it wrong" here.
1846 // However, I really don't want to hear from people who think
1847 // that a window can never be associated with something other
1848 // than a user file at time T1 and with a user file at time T2,
1849 // given that, in Wireshark, a window can be associated with a
1850 // live capture at time T1 and then, after you've saved the live
1851 // capture to a user file, associated with a user file at time T2.
1853 if (capture_file_.capFile()->is_tempfile) {
1855 // For a temporary file, put the source of the data
1856 // in the window title, not whatever random pile
1857 // of characters is the last component of the path
1860 // XXX - on non-Mac platforms, put in the application
1863 setWindowFilePath(NULL);
1864 setWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1867 // For a user file, set the full path; that way,
1868 // for OS X, it'll set the "proxy icon". Qt
1869 // handles extracting the last component.
1871 // Sadly, some UN*Xes don't necessarily use UTF-8
1872 // for their file names, so we have to map the
1873 // file path to UTF-8. If that fails, we're somewhat
1876 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1881 if (utf8_filename == NULL) {
1882 // So what the heck else can we do here?
1883 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1885 setWindowTitle(NULL);
1886 setWindowFilePath(NULL);
1887 setWindowFilePath(utf8_filename);
1888 g_free(utf8_filename);
1891 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1893 /* We have no capture file. */
1894 setWindowFilePath(NULL);
1895 setDefaultWindowTitle();
1899 void MainWindow::setDefaultWindowTitle()
1901 setWindowTitle(tr("The Wireshark Network Analyzer"));
1904 void MainWindow::setTitlebarForCaptureInProgress()
1906 setWindowFilePath(NULL);
1907 if (capture_file_.capFile()) {
1908 setWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1910 /* We have no capture in progress. */
1911 setDefaultWindowTitle();
1917 /* Enable or disable menu items based on whether you have a capture file
1918 you've finished reading and, if you have one, whether it's been saved
1919 and whether it could be saved except by copying the raw packet data. */
1920 void MainWindow::setMenusForCaptureFile(bool force_disable)
1922 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1923 /* We have no capture file or we're currently reading a file */
1924 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(false);
1925 main_ui_->actionFileMerge->setEnabled(false);
1926 main_ui_->actionFileClose->setEnabled(false);
1927 main_ui_->actionFileSave->setEnabled(false);
1928 main_ui_->actionFileSaveAs->setEnabled(false);
1929 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1930 main_ui_->actionFileExportPackets->setEnabled(false);
1931 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1932 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1933 main_ui_->actionFileExportPDU->setEnabled(false);
1934 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1935 main_ui_->menuFileExportObjects->setEnabled(false);
1936 main_ui_->actionViewReload->setEnabled(false);
1938 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(true);
1939 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1941 main_ui_->actionFileClose->setEnabled(true);
1942 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1943 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1944 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1946 * "Export Specified Packets..." should be available only if
1947 * we can write the file out in at least one format.
1949 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1950 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1951 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1952 main_ui_->actionFileExportPDU->setEnabled(true);
1953 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1954 main_ui_->menuFileExportObjects->setEnabled(true);
1955 main_ui_->actionViewReload->setEnabled(true);
1959 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1960 /* Either a capture was started or stopped; in either case, it's not
1961 in the process of stopping, so allow quitting. */
1963 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1964 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1965 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1966 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1967 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1968 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1969 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1970 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1971 main_ui_->actionFileQuit->setEnabled(true);
1973 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1975 // XXX Fix packet list heading menu sensitivity
1976 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1977 // !capture_in_progress);
1978 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1979 // !capture_in_progress);
1980 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1981 // !capture_in_progress);
1984 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1985 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1986 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1987 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1988 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1989 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1990 #endif /* HAVE_LIBPCAP */
1994 void MainWindow::setMenusForCaptureStopping() {
1995 main_ui_->actionFileQuit->setEnabled(false);
1996 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1998 main_ui_->actionCaptureStart->setChecked(false);
1999 main_ui_->actionCaptureStop->setEnabled(false);
2000 main_ui_->actionCaptureRestart->setEnabled(false);
2001 #endif /* HAVE_LIBPCAP */
2004 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2006 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2008 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2009 // have_captured_packets);
2011 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2012 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2013 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2015 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2016 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2017 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2018 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2019 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2020 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2021 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2023 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2024 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2025 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2026 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2028 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2029 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2030 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2033 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2034 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2035 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2037 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2038 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2039 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2042 void MainWindow::setWindowIcon(const QIcon &icon) {
2043 wsApp->setWindowIcon(icon);
2044 QMainWindow::setWindowIcon(icon);
2047 void MainWindow::updateForUnsavedChanges() {
2048 setTitlebarForCaptureFile();
2049 setMenusForCaptureFile();
2052 void MainWindow::changeEvent(QEvent* event)
2056 switch (event->type())
2058 case QEvent::LanguageChange:
2059 main_ui_->retranslateUi(this);
2060 // make sure that the "Clear Menu" item is retranslated
2061 updateRecentFiles();
2063 case QEvent::LocaleChange:{
2064 QString locale = QLocale::system().name();
2065 locale.truncate(locale.lastIndexOf('_'));
2066 wsApp->loadLanguage(locale);
2073 QMainWindow::changeEvent(event);
2076 /* Update main window items based on whether there's a capture in progress. */
2077 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2079 setMenusForCaptureInProgress(capture_in_progress);
2081 wireless_frame_->setCaptureInProgress(capture_in_progress);
2084 packet_list_->setCaptureInProgress(capture_in_progress);
2085 // set_toolbar_for_capture_in_progress(capture_in_progress);
2087 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2091 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2092 << REGISTER_ANALYZE_GROUP_UNSORTED
2093 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2094 << REGISTER_STAT_GROUP_UNSORTED
2095 << REGISTER_STAT_GROUP_GENERIC
2096 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2097 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2098 << REGISTER_STAT_GROUP_RESPONSE_TIME
2099 << REGISTER_STAT_GROUP_TELEPHONY
2100 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2101 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2102 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2103 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2104 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2105 << REGISTER_TOOLS_GROUP_UNSORTED;
2107 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2109 foreach (QAction *action, actions) {
2110 switch (menu_group) {
2111 case REGISTER_ANALYZE_GROUP_UNSORTED:
2112 case REGISTER_STAT_GROUP_UNSORTED:
2113 main_ui_->menuStatistics->insertAction(
2114 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2117 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2118 main_ui_->menuServiceResponseTime->addAction(action);
2120 case REGISTER_STAT_GROUP_TELEPHONY:
2121 main_ui_->menuTelephony->addAction(action);
2123 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2124 main_ui_->menuANSI->addAction(action);
2126 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2127 main_ui_->menuGSM->addAction(action);
2129 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2130 main_ui_->menuLTE->addAction(action);
2132 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2133 main_ui_->menuMTP3->addAction(action);
2135 case REGISTER_TOOLS_GROUP_UNSORTED:
2137 // Allow the creation of submenus. Mimics the behavor of
2138 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2139 // and GtkUIManager.
2141 // For now we limit the insanity to the "Tools" menu.
2142 QStringList menu_path = action->text().split('/');
2143 QMenu *cur_menu = main_ui_->menuTools;
2144 while (menu_path.length() > 1) {
2145 QString menu_title = menu_path.takeFirst();
2146 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2147 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2149 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2150 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2153 submenu = cur_menu->addMenu(menu_title);
2154 submenu->setObjectName(menu_title.toLower());
2158 action->setText(menu_path.last());
2159 cur_menu->addAction(action);
2163 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2167 // Connect each action type to its corresponding slot. We to
2168 // distinguish various types of actions. Setting their objectName
2169 // seems to work OK.
2170 if (action->objectName() == TapParameterDialog::actionName()) {
2171 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2172 } else if (action->objectName() == FunnelStatistics::actionName()) {
2173 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2177 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2179 foreach (QAction *action, actions) {
2180 switch (menu_group) {
2181 case REGISTER_ANALYZE_GROUP_UNSORTED:
2182 case REGISTER_STAT_GROUP_UNSORTED:
2183 main_ui_->menuStatistics->removeAction(action);
2185 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2186 main_ui_->menuServiceResponseTime->removeAction(action);
2188 case REGISTER_STAT_GROUP_TELEPHONY:
2189 main_ui_->menuTelephony->removeAction(action);
2191 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2192 main_ui_->menuANSI->removeAction(action);
2194 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2195 main_ui_->menuGSM->removeAction(action);
2197 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2198 main_ui_->menuLTE->removeAction(action);
2200 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2201 main_ui_->menuMTP3->removeAction(action);
2203 case REGISTER_TOOLS_GROUP_UNSORTED:
2205 // Allow removal of submenus.
2206 // For now we limit the insanity to the "Tools" menu.
2207 QStringList menu_path = action->text().split('/');
2208 QMenu *cur_menu = main_ui_->menuTools;
2209 while (menu_path.length() > 1) {
2210 QString menu_title = menu_path.takeFirst();
2211 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2212 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2214 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2215 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2219 cur_menu->removeAction(action);
2223 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2229 void MainWindow::addDynamicMenus()
2232 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2233 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2234 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2235 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2236 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2237 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2239 // Fill in each menu
2240 foreach (register_stat_group_t menu_group, menu_groups) {
2241 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2242 addMenuActions(actions, menu_group);
2245 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2246 // We've added a placeholder in order to make sure the "Tools" menu is
2247 // visible. Hide it as needed.
2248 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2249 main_ui_->actionToolsPlaceholder->setVisible(false);
2251 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2252 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2254 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2255 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2257 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2258 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2260 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2261 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2265 void MainWindow::reloadDynamicMenus()
2267 foreach (register_stat_group_t menu_group, menu_groups) {
2268 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2269 removeMenuActions(actions, menu_group);
2271 actions = wsApp->addedMenuGroupItems(menu_group);
2272 addMenuActions(actions, menu_group);
2275 wsApp->clearAddedMenuGroupItems();
2276 wsApp->clearRemovedMenuGroupItems();
2279 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2281 QAction * itemAction = NULL;
2282 ext_menubar_t * item = NULL;
2283 GList * children = NULL;
2285 /* There must exists an xpath parent */
2286 g_assert(subMenu != NULL);
2288 /* If the depth counter exceeds, something must have gone wrong */
2289 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2291 children = menu->children;
2292 /* Iterate the child entries */
2293 while ( children != NULL && children->data != NULL )
2295 item = (ext_menubar_t *) children->data;
2297 if ( item->type == EXT_MENUBAR_MENU )
2299 /* Handle Submenu entry */
2300 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++ );
2302 else if ( item->type == EXT_MENUBAR_SEPARATOR )
2304 subMenu->addSeparator();
2306 else if ( item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL )
2308 itemAction = subMenu->addAction(item->name);
2309 itemAction->setData(QVariant::fromValue((void *)item));
2310 itemAction->setText(item->label);
2311 connect(itemAction, SIGNAL(triggered()),
2312 this, SLOT(externalMenuItem_triggered()));
2316 children = g_list_next(children);
2320 QMenu * MainWindow::searchSubMenu(QString objectName)
2324 if ( objectName.length() > 0 )
2326 QString searchName = QString("menu") + objectName;
2328 lst = main_ui_->menuBar->findChildren<QMenu*>();
2329 foreach (QMenu* m, lst)
2331 if ( QString::compare( m->objectName(), searchName ) == 0 )
2339 void MainWindow::addExternalMenus()
2341 QMenu * subMenu = NULL;
2342 GList * user_menu = NULL;
2343 ext_menu_t * menu = NULL;
2345 user_menu = ext_menubar_get_entries();
2347 while ( ( user_menu != NULL ) && ( user_menu->data != NULL ) )
2349 menu = (ext_menu_t *) user_menu->data;
2351 /* On this level only menu items should exist. Not doing an assert here,
2352 * as it could be an honest mistake */
2353 if ( menu->type != EXT_MENUBAR_MENU )
2355 user_menu = g_list_next(user_menu);
2359 /* Create main submenu and add it to the menubar */
2360 if ( menu->parent_menu != NULL )
2362 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2363 if ( sortUnderneath != NULL)
2364 subMenu = sortUnderneath->addMenu(menu->label);
2367 if ( subMenu == NULL )
2368 subMenu = main_ui_->menuBar->addMenu(menu->label);
2370 /* This will generate the action structure for each menu. It is recursive,
2371 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2372 this->externalMenuHelper(menu, subMenu, 0);
2375 user_menu = g_list_next (user_menu);
2385 * indent-tabs-mode: nil
2388 * ex: set shiftwidth=4 tabstop=8 expandtab:
2389 * :indentSize=4:tabSize=8:noTabs=true: