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 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_)),
226 capture_stopping_(false),
227 capture_filter_valid_(false),
234 if (!gbl_cur_main_window_) {
235 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
236 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
237 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
238 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
240 gbl_cur_main_window_ = this;
242 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
244 main_ui_->setupUi(this);
245 setWindowIcon(wsApp->normalIcon());
246 setTitlebarForCaptureFile();
247 setMenusForCaptureFile();
248 setForCapturedPackets(false);
249 setMenusForFileSet(false);
250 interfaceSelectionChanged();
251 loadWindowGeometry();
253 //To prevent users use features before initialization complete
254 //Otherwise unexpected problems may occur
255 setFeaturesEnabled(false);
256 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
257 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
258 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
259 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
260 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
261 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
263 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
264 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
265 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
266 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
267 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
269 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
273 connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
274 connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
277 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
278 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
279 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
280 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
281 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
282 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
283 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
284 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
286 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
287 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
288 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
289 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
290 this, SLOT(openCaptureFile(QString,QString)));
291 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
293 initMainToolbarIcons();
295 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
296 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
297 // https://bugreports.qt-project.org/browse/QTBUG-22433
298 // This property is obsolete in Qt5 so this issue may be fixed in that version.
299 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
301 wireless_frame_ = new WirelessFrame(this);
302 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
303 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
304 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
305 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
306 this, SLOT(showPreferencesDialog(QString)));
308 main_ui_->goToFrame->hide();
309 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
310 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
312 // XXX For some reason the cursor is drawn funny with an input mask set
313 // https://bugreports.qt-project.org/browse/QTBUG-7174
315 main_ui_->searchFrame->hide();
316 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
317 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
318 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
319 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
321 main_ui_->addressEditorFrame->hide();
322 main_ui_->columnEditorFrame->hide();
323 main_ui_->preferenceEditorFrame->hide();
326 main_ui_->menuCapture->setEnabled(false);
329 #if defined(Q_OS_MAC)
330 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
331 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
332 ntb->setIconSize(QSize(24, 24));
333 #endif // QT_MACEXTRAS_LIB
335 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
336 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
337 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
339 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
343 #ifdef HAVE_SOFTWARE_UPDATE
344 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
345 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
346 main_ui_->menuHelp->insertAction(update_sep, update_action);
347 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
349 master_split_.setObjectName(tr("splitterMaster"));
350 extra_split_.setObjectName(tr("splitterExtra"));
351 main_ui_->mainStack->addWidget(&master_split_);
353 empty_pane_.setObjectName(tr("emptyPane"));
355 packet_list_ = new PacketList(&master_split_);
357 proto_tree_ = new ProtoTree(&master_split_);
358 proto_tree_->installEventFilter(this);
360 byte_view_tab_ = new ByteViewTab(&master_split_);
362 packet_list_->setProtoTree(proto_tree_);
363 packet_list_->setByteViewTab(byte_view_tab_);
364 packet_list_->installEventFilter(this);
366 main_welcome_ = main_ui_->welcomePage;
368 // Packet list and proto tree must exist before these are called.
369 setMenusForSelectedPacket();
370 setMenusForSelectedTreeRow();
372 initShowHideMainWidgets();
373 initTimeDisplayFormatMenu();
374 initTimePrecisionFormatMenu();
375 updatePreferenceActions();
376 setForCaptureInProgress(false);
378 setTabOrder(df_combo_box_, packet_list_);
380 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
381 this, SLOT(captureCapturePrepared(capture_session *)));
382 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
383 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
384 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
385 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
386 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
387 this, SLOT(captureCaptureFixedStarted(capture_session *)));
388 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
389 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
390 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
391 this, SLOT(captureCaptureFixedFinished(capture_session *)));
392 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
393 this, SLOT(captureCaptureStopping(capture_session *)));
394 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
395 this, SLOT(captureCaptureFailed(capture_session *)));
396 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
397 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
399 connect(&capture_file_, SIGNAL(captureFileOpened()),
400 this, SLOT(captureFileOpened()));
401 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
402 this, SLOT(captureFileReadStarted()));
403 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
404 this, SLOT(captureFileReadFinished()));
405 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
406 this, SLOT(captureFileReloadStarted()));
407 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
408 this, SLOT(captureFileReadFinished()));
409 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
410 this, SLOT(captureFileRescanStarted()));
411 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
412 this, SLOT(captureFileReadFinished()));
413 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
414 this, SLOT(captureFileRetapStarted()));
415 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
416 this, SLOT(captureFileRetapFinished()));
417 connect(&capture_file_, SIGNAL(captureFileClosing()),
418 this, SLOT(captureFileClosing()));
419 connect(&capture_file_, SIGNAL(captureFileClosed()),
420 this, SLOT(captureFileClosed()));
422 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
423 this, SLOT(captureFileSaveStarted(QString)));
424 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
425 main_ui_->statusBar, SLOT(popFileStatus()));
426 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
427 main_ui_->statusBar, SLOT(popFileStatus()));
428 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
429 main_ui_->statusBar, SLOT(popFileStatus()));
431 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
432 wsApp, SLOT(captureFileReadStarted()));
433 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
434 wsApp, SLOT(updateTaps()));
436 connect(wsApp, SIGNAL(recentFilesRead()),
437 packet_list_, SLOT(applyRecentColumnWidths()));
438 connect(wsApp, SIGNAL(columnsChanged()),
439 packet_list_, SLOT(columnsChanged()));
440 connect(wsApp, SIGNAL(preferencesChanged()),
441 packet_list_, SLOT(elideModeChanged()));
442 connect(wsApp, SIGNAL(recentFilesRead()),
443 this, SLOT(applyRecentPaneGeometry()));
444 connect(wsApp, SIGNAL(packetDissectionChanged()),
445 this, SLOT(redissectPackets()));
446 connect(wsApp, SIGNAL(appInitialized()),
447 this, SLOT(filterExpressionsChanged()));
448 connect(wsApp, SIGNAL(filterExpressionsChanged()),
449 this, SLOT(filterExpressionsChanged()));
450 connect(wsApp, SIGNAL(fieldsChanged()),
451 this, SLOT(fieldsChanged()));
453 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
454 this, SLOT(mainStackChanged(int)));
456 connect(main_welcome_, SIGNAL(startCapture()),
457 this, SLOT(startCapture()));
458 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
459 this, SLOT(openCaptureFile(QString)));
460 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
461 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
462 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
463 main_ui_->statusBar, SLOT(popFilterStatus()));
465 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
466 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
467 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
468 this, SLOT(redissectPackets()));
469 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
470 this, SLOT(showPreferencesDialog(QString)));
471 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
472 this, SLOT(showPreferencesDialog(QString)));
474 connect(this, SIGNAL(setCaptureFile(capture_file*)),
475 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
476 connect(this, SIGNAL(setCaptureFile(capture_file*)),
477 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
478 connect(this, SIGNAL(setCaptureFile(capture_file*)),
479 packet_list_, SLOT(setCaptureFile(capture_file*)));
480 connect(this, SIGNAL(setCaptureFile(capture_file*)),
481 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
483 connect(this, SIGNAL(monospaceFontChanged(QFont)),
484 packet_list_, SLOT(setMonospaceFont(QFont)));
485 connect(this, SIGNAL(monospaceFontChanged(QFont)),
486 proto_tree_, SLOT(setMonospaceFont(QFont)));
487 connect(this, SIGNAL(monospaceFontChanged(QFont)),
488 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
490 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
491 packet_list_, SLOT(goNextPacket()));
492 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
493 packet_list_, SLOT(goPreviousPacket()));
494 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
495 packet_list_, SLOT(goFirstPacket()));
496 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
497 packet_list_, SLOT(goLastPacket()));
499 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
500 proto_tree_, SLOT(expandSubtrees()));
501 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
502 proto_tree_, SLOT(expandAll()));
503 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
504 proto_tree_, SLOT(collapseAll()));
506 connect(packet_list_, SIGNAL(packetSelectionChanged()),
507 this, SLOT(setMenusForSelectedPacket()));
508 connect(packet_list_, SIGNAL(packetDissectionChanged()),
509 this, SLOT(redissectPackets()));
510 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
511 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
512 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
513 this, SLOT(showPreferencesDialog(QString)));
514 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
515 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
516 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
517 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
518 packet_list_, SLOT(columnsChanged()));
519 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
520 this, SLOT(openPacketDialog()));
521 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
522 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
523 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
524 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
525 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
526 main_ui_->statusBar, SLOT(popBusyStatus()));
527 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
528 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
529 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
530 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
531 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
532 main_ui_->statusBar, SLOT(popProgressStatus()));
534 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
535 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
536 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
537 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
538 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
539 this, SLOT(openPacketDialog(bool)));
540 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
541 this, SLOT(showPreferencesDialog(QString)));
542 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
543 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
545 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
546 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
548 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
549 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
551 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
552 &capture_file_, SLOT(stopLoading()));
554 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
555 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
557 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
558 this, SLOT(openCaptureFile(QString)));
561 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
563 connect(iface_tree, SIGNAL(itemSelectionChanged()),
564 this, SLOT(interfaceSelectionChanged()));
566 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
567 this, SLOT(captureFilterSyntaxChanged(bool)));
570 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
571 this, SLOT(showExtcapOptionsDialog(QString&)));
574 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
575 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
576 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
577 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
578 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
579 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
582 /* Create plugin_if hooks */
583 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter );
584 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter );
585 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
586 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
588 main_ui_->mainStack->setCurrentWidget(main_welcome_);
591 MainWindow::~MainWindow()
596 QString MainWindow::getFilter()
598 return df_combo_box_->itemText(df_combo_box_->count());
601 QMenu *MainWindow::createPopupMenu()
603 QMenu *menu = new QMenu();
604 menu->addAction(main_ui_->actionViewMainToolbar);
605 menu->addAction(main_ui_->actionViewFilterToolbar);
606 menu->addAction(main_ui_->actionViewWirelessToolbar);
607 menu->addAction(main_ui_->actionViewStatusBar);
608 menu->addSeparator();
609 menu->addAction(main_ui_->actionViewPacketList);
610 menu->addAction(main_ui_->actionViewPacketDetails);
611 menu->addAction(main_ui_->actionViewPacketBytes);
615 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
617 pipe_source_ = source;
618 pipe_child_process_ = child_process;
619 pipe_user_data_ = user_data;
620 pipe_input_cb_ = input_cb;
623 /* Tricky to use pipes in win9x, as no concept of wait. NT can
624 do this but that doesn't cover all win32 platforms. GTK can do
625 this but doesn't seem to work over processes. Attempt to do
626 something similar here, start a timer and check for data on every
628 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
631 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
635 pipe_timer_ = new QTimer(this);
636 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
637 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
638 pipe_timer_->start(200);
640 if (pipe_notifier_) {
641 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
642 delete pipe_notifier_;
645 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
646 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
647 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
648 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
652 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
654 // The user typed some text. Start filling in a filter.
655 // We may need to be more choosy here. We just need to catch events for the packet list,
656 // proto tree, and main welcome widgets.
657 if (event->type() == QEvent::KeyPress) {
658 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
659 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
660 df_combo_box_->lineEdit()->insert(kevt->text());
661 df_combo_box_->lineEdit()->setFocus();
666 return QMainWindow::eventFilter(obj, event);
669 void MainWindow::keyPressEvent(QKeyEvent *event) {
671 // Explicitly focus on the display filter combo.
672 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
673 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
677 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
678 if (event->modifiers() == Qt::NoModifier) {
679 if (event->key() == Qt::Key_Escape) {
680 on_goToCancel_clicked();
681 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
685 return; // goToLineEdit didn't want it and we don't either.
688 // Move up & down the packet list.
689 if (event->key() == Qt::Key_F7) {
690 packet_list_->goPreviousPacket();
691 } else if (event->key() == Qt::Key_F8) {
692 packet_list_->goNextPacket();
695 // Move along, citizen.
696 QMainWindow::keyPressEvent(event);
699 void MainWindow::closeEvent(QCloseEvent *event) {
700 saveWindowGeometry();
702 /* If we're in the middle of stopping a capture, don't do anything;
703 the user can try deleting the window after the capture stops. */
704 if (capture_stopping_) {
709 QString before_what(tr(" before quitting"));
710 if (!testCaptureFileClose(TRUE, before_what)) {
716 capture_interfaces_dialog_.close();
718 // Make sure we kill any open dumpcap processes.
719 delete main_welcome_;
721 // One of the many places we assume one main window.
722 if(!wsApp->isInitialized()) {
723 // If we're still initializing, QCoreApplication::quit() won't
724 // exit properly because we are not in the event loop. This
725 // means that the application won't clean up after itself. We
726 // might want to call wsApp->processEvents() during startup
727 // instead so that we can do a normal exit here.
733 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
736 foreach (QUrl drag_url, event->mimeData()->urls()) {
737 if (!drag_url.toLocalFile().isEmpty()) {
742 if (accept) event->acceptProposedAction();
745 void MainWindow::dropEvent(QDropEvent *event)
747 foreach (QUrl drop_url, event->mimeData()->urls()) {
748 QString local_file = drop_url.toLocalFile();
749 if (!local_file.isEmpty()) {
750 event->acceptProposedAction();
751 openCaptureFile(local_file);
757 // Apply recent settings to the main window geometry.
758 // We haven't loaded the preferences at this point so we assume that the
759 // position and size preference are enabled.
760 void MainWindow::loadWindowGeometry()
762 int min_sensible_dimension_ = 200;
765 if (recent.gui_geometry_main_maximized) {
766 setWindowState(Qt::WindowMaximized);
770 // if (prefs.gui_geometry_save_position) {
771 move(recent.gui_geometry_main_x, recent.gui_geometry_main_y);
774 if (// prefs.gui_geometry_save_size &&
775 recent.gui_geometry_main_width > min_sensible_dimension_ &&
776 recent.gui_geometry_main_height > min_sensible_dimension_) {
777 resize(recent.gui_geometry_main_width, recent.gui_geometry_main_height);
782 void MainWindow::saveWindowGeometry()
784 if (prefs.gui_geometry_save_position) {
785 recent.gui_geometry_main_x = pos().x();
786 recent.gui_geometry_main_y = pos().y();
789 if (prefs.gui_geometry_save_size) {
790 recent.gui_geometry_main_width = size().width();
791 recent.gui_geometry_main_height = size().height();
794 if (prefs.gui_geometry_save_maximized) {
795 // On OS X this is false when it shouldn't be
796 recent.gui_geometry_main_maximized = isMaximized();
799 if (master_split_.sizes().length() > 0) {
800 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
803 if (master_split_.sizes().length() > 2) {
804 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
805 } else if (extra_split_.sizes().length() > 0) {
806 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
810 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
812 case layout_pane_content_none:
814 case layout_pane_content_plist:
816 case layout_pane_content_pdetails:
818 case layout_pane_content_pbytes:
819 return byte_view_tab_;
821 g_assert_not_reached();
826 void MainWindow::mergeCaptureFile()
828 QString file_name = "";
829 QString display_filter = "";
830 dfilter_t *rfcode = NULL;
833 if (!capture_file_.capFile())
836 if (prefs.gui_ask_unsaved) {
837 if (cf_has_unsaved_data(capture_file_.capFile())) {
838 QMessageBox msg_dialog;
839 gchar *display_basename;
842 msg_dialog.setIcon(QMessageBox::Question);
843 /* This file has unsaved data; ask the user whether to save
845 if (capture_file_.capFile()->is_tempfile) {
846 msg_dialog.setText(tr("Save packets before merging?"));
847 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
850 * Format the message.
852 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
853 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
854 g_free(display_basename);
855 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
858 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
859 msg_dialog.setDefaultButton(QMessageBox::Save);
861 response = msg_dialog.exec();
865 case QMessageBox::Save:
866 /* Save the file but don't close it */
867 saveCaptureFile(capture_file_.capFile(), FALSE);
870 case QMessageBox::Cancel:
872 /* Don't do the merge. */
879 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), display_filter);
881 cf_status_t merge_status;
882 char *in_filenames[2];
885 switch (prefs.gui_fileopen_style) {
887 case FO_STYLE_LAST_OPENED:
888 /* The user has specified that we should start out in the last directory
889 we looked in. If we've already opened a file, use its containing
890 directory, if we could determine it, as the directory, otherwise
891 use the "last opened" directory saved in the preferences file if
893 /* This is now the default behaviour in file_selection_new() */
896 case FO_STYLE_SPECIFIED:
897 /* The user has specified that we should always start out in a
898 specified directory; if they've specified that directory,
899 start out by showing the files in that dir. */
900 if (prefs.gui_fileopen_dir[0] != '\0')
901 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
905 if (merge_dlg.merge(file_name)) {
908 if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode, &err_msg)) {
909 cf_set_rfcode(capture_file_.capFile(), rfcode);
911 /* Not valid. Tell the user, and go back and run the file
912 selection box again once they dismiss the alert. */
913 //bad_dfilter_alert_box(top_level, display_filter->str);
914 QMessageBox::warning(this, tr("Invalid Display Filter"),
915 QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, err_msg)),
924 file_type = capture_file_.capFile()->cd_t;
926 /* Try to merge or append the two files */
928 if (merge_dlg.mergeType() == 0) {
929 /* chronological order */
930 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
931 in_filenames[1] = qstring_strdup(file_name);
932 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
933 } else if (merge_dlg.mergeType() <= 0) {
935 in_filenames[0] = qstring_strdup(file_name);
936 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
937 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
940 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
941 in_filenames[1] = qstring_strdup(file_name);
942 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
945 g_free(in_filenames[0]);
946 g_free(in_filenames[1]);
948 if (merge_status != CF_OK) {
950 dfilter_free(rfcode);
955 cf_close(capture_file_.capFile());
957 /* Try to open the merged capture file. */
958 CaptureFile::globalCapFile()->window = this;
959 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
960 /* We couldn't open it; fail. */
961 CaptureFile::globalCapFile()->window = NULL;
963 dfilter_free(rfcode);
968 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
969 it closed the previous capture file, and thus destroyed any
970 previous read filter attached to "cf"). */
971 CaptureFile::globalCapFile()->rfcode = rfcode;
973 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
977 /* Just because we got an error, that doesn't mean we were unable
978 to read any of the file; we handle what we could get from the
982 case CF_READ_ABORTED:
983 /* The user bailed out of re-reading the capture file; the
984 capture file has been closed - just free the capture file name
985 string and return (without changing the last containing
991 /* Save the name of the containing directory specified in the path name,
992 if any; we can write over cf_merged_name, which is a good thing, given that
993 "get_dirname()" does write over its argument. */
994 wsApp->setLastOpenDir(get_dirname(tmpname));
996 df_combo_box_->setEditText(display_filter);
997 main_ui_->statusBar->showExpert();
1003 void MainWindow::importCaptureFile() {
1004 ImportTextDialog import_dlg;
1006 QString before_what(tr(" before importing a new capture"));
1007 if (!testCaptureFileClose(FALSE, before_what))
1012 if (import_dlg.result() != QDialog::Accepted) {
1013 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1017 openCaptureFile(import_dlg.capfileName());
1020 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1022 gboolean discard_comments;
1024 if (cf->is_tempfile) {
1025 /* This is a temporary capture file, so saving it means saving
1026 it to a permanent file. Prompt the user for a location
1027 to which to save it. Don't require that the file format
1028 support comments - if it's a temporary capture file, it's
1029 probably pcap-ng, which supports comments and, if it's
1030 not pcap-ng, let the user decide what they want to do
1031 if they've added comments. */
1032 saveAsCaptureFile(cf, FALSE, dont_reopen);
1034 if (cf->unsaved_changes) {
1035 cf_write_status_t status;
1037 /* This is not a temporary capture file, but it has unsaved
1038 changes, so saving it means doing a "safe save" on top
1039 of the existing file, in the same format - no UI needed
1040 unless the file has comments and the file's format doesn't
1043 If the file has comments, does the file's format support them?
1044 If not, ask the user whether they want to discard the comments
1045 or choose a different format. */
1046 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1049 /* The file can be saved in the specified format as is;
1050 just drive on and save in the format they selected. */
1051 discard_comments = FALSE;
1054 case SAVE_WITHOUT_COMMENTS:
1055 /* The file can't be saved in the specified format as is,
1056 but it can be saved without the comments, and the user
1057 said "OK, discard the comments", so save it in the
1058 format they specified without the comments. */
1059 discard_comments = TRUE;
1062 case SAVE_IN_ANOTHER_FORMAT:
1063 /* There are file formats in which we can save this that
1064 support comments, and the user said not to delete the
1065 comments. Do a "Save As" so the user can select
1066 one of those formats and choose a file name. */
1067 saveAsCaptureFile(cf, TRUE, dont_reopen);
1071 /* The user said "forget it". Just return. */
1075 /* Squelch warnings that discard_comments is being used
1077 g_assert_not_reached();
1081 /* XXX - cf->filename might get freed out from under us, because
1082 the code path through which cf_save_records() goes currently
1083 closes the current file and then opens and reloads the saved file,
1084 so make a copy and free it later. */
1085 file_name = cf->filename;
1086 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1087 discard_comments, dont_reopen);
1091 /* The save succeeded; we're done.
1092 If we discarded comments, redraw the packet list to reflect
1093 any packets that no longer have comments. */
1094 if (discard_comments)
1095 packet_list_queue_draw();
1097 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1098 updateForUnsavedChanges(); // we update the title bar to remove the *
1101 case CF_WRITE_ERROR:
1102 /* The write failed.
1103 XXX - OK, what do we do now? Let them try a
1104 "Save As", in case they want to try to save to a
1105 different directory r file system? */
1108 case CF_WRITE_ABORTED:
1109 /* The write was aborted; just drive on. */
1113 /* Otherwise just do nothing. */
1117 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1118 QString file_name = "";
1120 gboolean compressed;
1121 cf_write_status_t status;
1123 gboolean discard_comments = FALSE;
1130 CaptureFileDialog save_as_dlg(this, cf);
1132 switch (prefs.gui_fileopen_style) {
1134 case FO_STYLE_LAST_OPENED:
1135 /* The user has specified that we should start out in the last directory
1136 we looked in. If we've already opened a file, use its containing
1137 directory, if we could determine it, as the directory, otherwise
1138 use the "last opened" directory saved in the preferences file if
1140 /* This is now the default behaviour in file_selection_new() */
1143 case FO_STYLE_SPECIFIED:
1144 /* The user has specified that we should always start out in a
1145 specified directory; if they've specified that directory,
1146 start out by showing the files in that dir. */
1147 if (prefs.gui_fileopen_dir[0] != '\0')
1148 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1152 /* If the file has comments, does the format the user selected
1153 support them? If not, ask the user whether they want to
1154 discard the comments or choose a different format. */
1155 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1158 /* The file can be saved in the specified format as is;
1159 just drive on and save in the format they selected. */
1160 discard_comments = FALSE;
1163 case SAVE_WITHOUT_COMMENTS:
1164 /* The file can't be saved in the specified format as is,
1165 but it can be saved without the comments, and the user
1166 said "OK, discard the comments", so save it in the
1167 format they specified without the comments. */
1168 discard_comments = TRUE;
1171 case SAVE_IN_ANOTHER_FORMAT:
1172 /* There are file formats in which we can save this that
1173 support comments, and the user said not to delete the
1174 comments. The combo box of file formats has had the
1175 formats that don't support comments trimmed from it,
1176 so run the dialog again, to let the user decide
1177 whether to save in one of those formats or give up. */
1178 must_support_comments = TRUE;
1182 /* The user said "forget it". Just get rid of the dialog box
1186 file_type = save_as_dlg.selectedFileType();
1187 compressed = save_as_dlg.isCompressed();
1189 fileAddExtension(file_name, file_type, compressed);
1192 // /* If the file exists and it's user-immutable or not writable,
1193 // ask the user whether they want to override that. */
1194 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1195 // /* They don't. Let them try another file name or cancel. */
1200 /* Attempt to save the file */
1201 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1202 discard_comments, dont_reopen);
1206 /* The save succeeded; we're done. */
1207 /* Save the directory name for future file dialogs. */
1208 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1209 set_last_open_dir(dirname);
1211 /* If we discarded comments, redraw the packet list to reflect
1212 any packets that no longer have comments. */
1213 if (discard_comments)
1214 packet_list_queue_draw();
1216 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1217 updateForUnsavedChanges(); // we update the title bar to remove the *
1220 case CF_WRITE_ERROR:
1221 /* The save failed; let the user try again. */
1224 case CF_WRITE_ABORTED:
1225 /* The user aborted the save; just return. */
1232 void MainWindow::exportSelectedPackets() {
1233 QString file_name = "";
1235 gboolean compressed;
1236 packet_range_t range;
1237 cf_write_status_t status;
1239 gboolean discard_comments = FALSE;
1241 if (!capture_file_.capFile())
1244 /* Init the packet range */
1245 packet_range_init(&range, capture_file_.capFile());
1246 range.process_filtered = TRUE;
1247 range.include_dependents = TRUE;
1250 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1252 switch (prefs.gui_fileopen_style) {
1254 case FO_STYLE_LAST_OPENED:
1255 /* The user has specified that we should start out in the last directory
1256 we looked in. If we've already opened a file, use its containing
1257 directory, if we could determine it, as the directory, otherwise
1258 use the "last opened" directory saved in the preferences file if
1260 /* This is now the default behaviour in file_selection_new() */
1263 case FO_STYLE_SPECIFIED:
1264 /* The user has specified that we should always start out in a
1265 specified directory; if they've specified that directory,
1266 start out by showing the files in that dir. */
1267 if (prefs.gui_fileopen_dir[0] != '\0')
1268 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1272 /* If the file has comments, does the format the user selected
1273 support them? If not, ask the user whether they want to
1274 discard the comments or choose a different format. */
1275 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1278 /* The file can be saved in the specified format as is;
1279 just drive on and save in the format they selected. */
1280 discard_comments = FALSE;
1283 case SAVE_WITHOUT_COMMENTS:
1284 /* The file can't be saved in the specified format as is,
1285 but it can be saved without the comments, and the user
1286 said "OK, discard the comments", so save it in the
1287 format they specified without the comments. */
1288 discard_comments = TRUE;
1291 case SAVE_IN_ANOTHER_FORMAT:
1292 /* There are file formats in which we can save this that
1293 support comments, and the user said not to delete the
1294 comments. The combo box of file formats has had the
1295 formats that don't support comments trimmed from it,
1296 so run the dialog again, to let the user decide
1297 whether to save in one of those formats or give up. */
1301 /* The user said "forget it". Just get rid of the dialog box
1307 * Check that we're not going to save on top of the current
1309 * We do it here so we catch all cases ...
1310 * Unfortunately, the file requester gives us an absolute file
1311 * name and the read file name may be relative (if supplied on
1312 * the command line). From Joerg Mayer.
1314 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1315 QMessageBox msg_box;
1316 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1318 msg_box.setIcon(QMessageBox::Critical);
1319 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1320 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1321 msg_box.setStandardButtons(QMessageBox::Ok);
1322 msg_box.setDefaultButton(QMessageBox::Ok);
1324 g_free(display_basename);
1328 file_type = esp_dlg.selectedFileType();
1329 compressed = esp_dlg.isCompressed();
1330 fileAddExtension(file_name, file_type, compressed);
1333 // /* If the file exists and it's user-immutable or not writable,
1334 // ask the user whether they want to override that. */
1335 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1336 // /* They don't. Let them try another file name or cancel. */
1341 /* Attempt to save the file */
1342 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1346 /* The save succeeded; we're done. */
1347 /* Save the directory name for future file dialogs. */
1348 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1349 set_last_open_dir(dirname);
1351 /* If we discarded comments, redraw the packet list to reflect
1352 any packets that no longer have comments. */
1353 if (discard_comments)
1354 packet_list_queue_draw();
1357 case CF_WRITE_ERROR:
1358 /* The save failed; let the user try again. */
1361 case CF_WRITE_ABORTED:
1362 /* The user aborted the save; just return. */
1369 void MainWindow::exportDissections(export_type_e export_type) {
1370 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1371 packet_range_t range;
1373 if (!capture_file_.capFile())
1376 /* Init the packet range */
1377 packet_range_init(&range, capture_file_.capFile());
1378 range.process_filtered = TRUE;
1379 range.include_dependents = TRUE;
1384 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1385 QString file_name_lower;
1386 QString file_suffix;
1387 GSList *extensions_list;
1388 gboolean add_extension;
1391 * Append the default file extension if there's none given by
1392 * the user or if they gave one that's not one of the valid
1393 * extensions for the file type.
1395 file_name_lower = file_name.toLower();
1396 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1397 if (extensions_list != NULL) {
1400 /* We have one or more extensions for this file type.
1401 Start out assuming we need to add the default one. */
1402 add_extension = TRUE;
1404 /* OK, see if the file has one of those extensions. */
1405 for (extension = extensions_list; extension != NULL;
1406 extension = g_slist_next(extension)) {
1407 file_suffix += tr(".") + (char *)extension->data;
1408 if (file_name_lower.endsWith(file_suffix)) {
1410 * The file name has one of the extensions for
1413 add_extension = FALSE;
1416 file_suffix += ".gz";
1417 if (file_name_lower.endsWith(file_suffix)) {
1419 * The file name has one of the extensions for
1422 add_extension = FALSE;
1427 /* We have no extensions for this file type. Don't add one. */
1428 add_extension = FALSE;
1430 if (add_extension) {
1431 if (wtap_default_file_extension(file_type) != NULL) {
1432 file_name += tr(".") + wtap_default_file_extension(file_type);
1440 bool MainWindow::testCaptureFileClose(bool from_quit, QString before_what) {
1441 bool capture_in_progress = FALSE;
1443 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1444 return true; /* Already closed, nothing to do */
1447 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS && capture_file_.capFile()->count>0) {
1448 /* This is true if we're reading a capture file *or* if we're doing
1449 a live capture. If we're reading a capture file, the main loop
1450 is busy reading packets, and only accepting input from the
1451 progress dialog, so we can't get here, so this means we're
1453 capture_in_progress = TRUE;
1457 if (prefs.gui_ask_unsaved) {
1458 if (cf_has_unsaved_data(capture_file_.capFile()) || capture_in_progress) {
1459 QMessageBox msg_dialog;
1461 QPushButton *saveButton;
1462 QPushButton *discardButton;
1464 msg_dialog.setIcon(QMessageBox::Question);
1465 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1466 /* This file has unsaved data or there's a capture in
1467 progress; ask the user whether to save the data. */
1468 if (capture_file_.capFile()->is_tempfile) {
1470 msg_dialog.setText(tr("You have unsaved packets"));
1471 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1473 if (capture_in_progress) {
1474 question.append(tr("Do you want to stop the capture and save the captured packets"));
1476 question.append(tr("Do you want to save the captured packets"));
1478 question.append(before_what).append(tr("?"));
1479 msg_dialog.setInformativeText(question);
1484 * Format the message.
1486 if (capture_in_progress) {
1487 question.append(tr("Do you want to stop the capture and save the captured packets"));
1488 question.append(before_what).append(tr("?"));
1489 msg_dialog.setText(question);
1490 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1492 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1493 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1494 .arg(display_basename)
1497 g_free(display_basename);
1498 msg_dialog.setText(question);
1499 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1503 // XXX Text comes from ui/gtk/stock_icons.[ch]
1504 // Note that the button roles differ from the GTK+ version.
1505 // Cancel = RejectRole
1506 // Save = AcceptRole
1507 // Don't Save = DestructiveRole
1508 msg_dialog.addButton(QMessageBox::Cancel);
1510 if (capture_in_progress) {
1511 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1513 saveButton = msg_dialog.addButton(QMessageBox::Save);
1515 msg_dialog.setDefaultButton(saveButton);
1518 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1519 discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1520 QMessageBox::DestructiveRole);
1522 discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1523 QMessageBox::DestructiveRole);
1526 if (capture_in_progress) {
1527 discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1528 QMessageBox::DestructiveRole);
1530 discardButton = msg_dialog.addButton(tr("Continue &without Saving"), QMessageBox::DestructiveRole);
1535 /* According to the Qt doc:
1536 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1538 * Therefore we should use clickedButton() to determine which button was clicked. */
1540 if(msg_dialog.clickedButton() == saveButton)
1543 /* If there's a capture in progress, we have to stop the capture
1544 and then do the save. */
1545 if (capture_in_progress)
1548 /* Save the file and close it */
1549 saveCaptureFile(capture_file_.capFile(), TRUE);
1551 else if(msg_dialog.clickedButton() == discardButton)
1555 * If there's a capture in progress; we have to stop the capture
1556 * and then do the close.
1558 if (capture_in_progress)
1561 /* Just close the file, discarding changes */
1562 cf_close(capture_file_.capFile());
1565 else //cancelButton or some other unspecified button
1571 /* Unchanged file, just close it */
1572 capture_file_.capFile()->state = FILE_READ_ABORTED;
1573 cf_close(capture_file_.capFile());
1576 /* User asked not to be bothered by those prompts, just close it.
1577 XXX - should that apply only to saving temporary files? */
1579 /* If there's a capture in progress, we have to stop the capture
1580 and then do the close. */
1581 if (capture_in_progress)
1584 cf_close(capture_file_.capFile());
1587 return true; /* File closed */
1590 void MainWindow::captureStop() {
1593 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1594 WiresharkApplication::processEvents();
1598 void MainWindow::initMainToolbarIcons()
1600 #if defined(Q_OS_WIN)
1601 // Current GTK+ and other Windows app behavior.
1602 main_ui_->mainToolBar->setIconSize(QSize(16, 16));
1604 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1605 main_ui_->mainToolBar->setIconSize(QSize(24, 24));
1608 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1609 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1611 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1612 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1613 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1614 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1616 // Menu icons are disabled in main_window.ui for these items.
1617 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1618 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1619 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1620 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1622 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1623 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1624 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1625 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1626 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1627 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1628 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1630 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1631 main_ui_->actionViewColorizePacketList->setChecked(recent.packet_list_colorize);
1632 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1634 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1635 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1636 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1637 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1638 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1639 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1640 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1643 void MainWindow::initShowHideMainWidgets()
1645 if (show_hide_actions_) {
1649 show_hide_actions_ = new QActionGroup(this);
1650 QMap<QAction *, QWidget *> shmw_actions;
1652 show_hide_actions_->setExclusive(false);
1653 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1654 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1655 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1656 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1657 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1658 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1659 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1661 main_ui_->actionViewMainToolbar->setChecked(recent.main_toolbar_show);
1662 main_ui_->actionViewFilterToolbar->setChecked(recent.filter_toolbar_show);
1663 main_ui_->actionViewWirelessToolbar->setChecked(recent.wireless_toolbar_show);
1664 main_ui_->actionViewStatusBar->setChecked(recent.statusbar_show);
1665 main_ui_->actionViewPacketList->setChecked(recent.packet_list_show);
1666 main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show);
1667 main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show);
1669 foreach (QAction *shmwa, shmw_actions.keys()) {
1670 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1671 show_hide_actions_->addAction(shmwa);
1672 showHideMainWidgets(shmwa);
1675 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1678 Q_DECLARE_METATYPE(ts_type)
1680 void MainWindow::initTimeDisplayFormatMenu()
1682 if (time_display_actions_) {
1686 time_display_actions_ = new QActionGroup(this);
1687 QMap<QAction *, ts_type> td_actions;
1689 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1690 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1691 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1692 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1693 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1694 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1695 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1696 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1697 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1698 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1700 foreach (QAction* tda, td_actions.keys()) {
1701 tda->setData(qVariantFromValue(td_actions[tda]));
1702 time_display_actions_->addAction(tda);
1703 if (recent.gui_time_format == td_actions[tda]) {
1704 tda->setChecked(true);
1708 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1710 main_ui_->actionViewTimeDisplaySecondsWithHoursAndMinutes->setChecked(recent.gui_seconds_format == TS_SECONDS_HOUR_MIN_SEC);
1713 Q_DECLARE_METATYPE(ts_precision)
1715 void MainWindow::initTimePrecisionFormatMenu()
1717 if (time_precision_actions_) {
1721 time_precision_actions_ = new QActionGroup(this);
1722 QMap<QAction *, ts_precision> tp_actions;
1723 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1724 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1725 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1726 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1727 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1728 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1729 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1731 foreach (QAction* tpa, tp_actions.keys()) {
1732 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1733 time_precision_actions_->addAction(tpa);
1734 if (recent.gui_time_precision == tp_actions[tpa]) {
1735 tpa->setChecked(true);
1739 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1743 void MainWindow::setTitlebarForCaptureFile()
1745 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1747 // Qt *REALLY* doesn't like windows that sometimes have a
1748 // title set with setWindowTitle() and other times have a
1749 // file path set; apparently, once you've set the title
1750 // with setWindowTitle(), it sticks, and setWindowFilePath()
1751 // has no effect. It appears to can clear the title with
1752 // setWindowTitle(NULL), but that clears the actual title in
1753 // the title bar, and setWindowFilePath() then, I guess, sees
1754 // that there's already a file path, and does nothing, leaving
1755 // the title bar empty. So you then have to clear the file path
1756 // with setWindowFilePath(NULL), and then set it.
1758 // Maybe there's a #include "you're holding it wrong" here.
1759 // However, I really don't want to hear from people who think
1760 // that a window can never be associated with something other
1761 // than a user file at time T1 and with a user file at time T2,
1762 // given that, in Wireshark, a window can be associated with a
1763 // live capture at time T1 and then, after you've saved the live
1764 // capture to a user file, associated with a user file at time T2.
1766 if (capture_file_.capFile()->is_tempfile) {
1768 // For a temporary file, put the source of the data
1769 // in the window title, not whatever random pile
1770 // of characters is the last component of the path
1773 // XXX - on non-Mac platforms, put in the application
1776 // XXX - Use setWindowModified
1778 setWindowFilePath(NULL);
1779 window_name = g_strdup_printf("[*]%s", cf_get_tempfile_source(capture_file_.capFile())); //TODO : Fix Translate
1780 setWindowTitle(window_name);
1781 g_free(window_name);
1784 // For a user file, set the full path; that way,
1785 // for OS X, it'll set the "proxy icon". Qt
1786 // handles extracting the last component.
1788 // Sadly, some UN*Xes don't necessarily use UTF-8
1789 // for their file names, so we have to map the
1790 // file path to UTF-8. If that fails, we're somewhat
1793 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1798 if (utf8_filename == NULL) {
1799 // So what the heck else can we do here?
1800 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1802 setWindowTitle(NULL);
1803 setWindowFilePath(NULL);
1804 setWindowFilePath(utf8_filename);
1805 g_free(utf8_filename);
1808 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1810 /* We have no capture file. */
1811 setWindowFilePath(NULL);
1812 setWindowTitle(tr("The Wireshark Network Analyzer"));
1816 void MainWindow::setTitlebarForSelectedTreeRow()
1818 setWindowTitle(tr("The Wireshark Network Analyzer"));
1822 void MainWindow::setTitlebarForCaptureInProgress()
1826 setWindowFilePath(NULL);
1827 if (capture_file_.capFile()) {
1828 window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(capture_file_.capFile())); //TODO : Fix Translate
1829 setWindowTitle(window_name);
1830 g_free(window_name);
1832 /* We have no capture in progress. */
1833 setWindowTitle(tr("The Wireshark Network Analyzer"));
1839 /* Enable or disable menu items based on whether you have a capture file
1840 you've finished reading and, if you have one, whether it's been saved
1841 and whether it could be saved except by copying the raw packet data. */
1842 void MainWindow::setMenusForCaptureFile(bool force_disable)
1844 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1845 /* We have no capture file or we're currently reading a file */
1846 main_ui_->actionFileMerge->setEnabled(false);
1847 main_ui_->actionFileClose->setEnabled(false);
1848 main_ui_->actionFileSave->setEnabled(false);
1849 main_ui_->actionFileSaveAs->setEnabled(false);
1850 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1851 main_ui_->actionFileExportPackets->setEnabled(false);
1852 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1853 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1854 main_ui_->actionFileExportPDU->setEnabled(false);
1855 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1856 main_ui_->menuFileExportObjects->setEnabled(false);
1857 main_ui_->actionViewReload->setEnabled(false);
1859 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1861 main_ui_->actionFileClose->setEnabled(true);
1862 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1863 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1864 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1866 * "Export Specified Packets..." should be available only if
1867 * we can write the file out in at least one format.
1869 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1870 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1871 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1872 main_ui_->actionFileExportPDU->setEnabled(true);
1873 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1874 main_ui_->menuFileExportObjects->setEnabled(true);
1875 main_ui_->actionViewReload->setEnabled(true);
1879 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1880 /* Either a capture was started or stopped; in either case, it's not
1881 in the process of stopping, so allow quitting. */
1883 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1884 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1885 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1886 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1887 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1888 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1889 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1890 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1891 main_ui_->actionFileQuit->setEnabled(true);
1893 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1895 // XXX Fix packet list heading menu sensitivity
1896 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1897 // !capture_in_progress);
1898 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1899 // !capture_in_progress);
1900 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1901 // !capture_in_progress);
1904 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1905 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1906 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1907 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1908 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1909 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1910 #endif /* HAVE_LIBPCAP */
1914 void MainWindow::setMenusForCaptureStopping() {
1915 main_ui_->actionFileQuit->setEnabled(false);
1916 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1918 main_ui_->actionCaptureStart->setChecked(false);
1919 main_ui_->actionCaptureStop->setEnabled(false);
1920 main_ui_->actionCaptureRestart->setEnabled(false);
1921 #endif /* HAVE_LIBPCAP */
1924 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1926 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1928 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1929 // have_captured_packets);
1931 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
1932 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
1933 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
1935 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1936 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1937 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1938 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1939 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1941 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
1942 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
1943 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
1944 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
1946 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1947 // have_captured_packets);
1948 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1949 // have_captured_packets);
1950 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
1951 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
1952 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
1955 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1956 bool enable_next = fileset_get_next() != NULL && enable_list_files;
1957 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1959 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1960 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1961 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1964 void MainWindow::updateForUnsavedChanges() {
1965 setTitlebarForCaptureFile();
1966 setMenusForCaptureFile();
1969 void MainWindow::changeEvent(QEvent* event)
1973 switch (event->type())
1975 case QEvent::LanguageChange:
1976 main_ui_->retranslateUi(this);
1978 case QEvent::LocaleChange:{
1979 QString locale = QLocale::system().name();
1980 locale.truncate(locale.lastIndexOf('_'));
1981 wsApp->loadLanguage(locale);
1988 QMainWindow::changeEvent(event);
1991 /* Update main window items based on whether there's a capture in progress. */
1992 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1994 setMenusForCaptureInProgress(capture_in_progress);
1996 wireless_frame_->setCaptureInProgress(capture_in_progress);
1999 packet_list_->setCaptureInProgress(capture_in_progress);
2000 // set_toolbar_for_capture_in_progress(capture_in_progress);
2002 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2006 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2007 << REGISTER_ANALYZE_GROUP_UNSORTED
2008 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2009 << REGISTER_STAT_GROUP_UNSORTED
2010 << REGISTER_STAT_GROUP_GENERIC
2011 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2012 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2013 << REGISTER_STAT_GROUP_RESPONSE_TIME
2014 << REGISTER_STAT_GROUP_TELEPHONY
2015 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2016 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2017 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2018 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2019 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2020 << REGISTER_TOOLS_GROUP_UNSORTED;
2022 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2024 foreach (QAction *action, actions) {
2025 switch (menu_group) {
2026 case REGISTER_ANALYZE_GROUP_UNSORTED:
2027 case REGISTER_STAT_GROUP_UNSORTED:
2028 main_ui_->menuStatistics->insertAction(
2029 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2032 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2033 main_ui_->menuServiceResponseTime->addAction(action);
2035 case REGISTER_STAT_GROUP_TELEPHONY:
2036 main_ui_->menuTelephony->addAction(action);
2038 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2039 main_ui_->menuANSI->addAction(action);
2041 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2042 main_ui_->menuGSM->addAction(action);
2044 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2045 main_ui_->menuLTE->addAction(action);
2047 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2048 main_ui_->menuMTP3->addAction(action);
2050 case REGISTER_TOOLS_GROUP_UNSORTED:
2052 // Allow the creation of submenus. Mimics the behavor of
2053 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2054 // and GtkUIManager.
2056 // For now we limit the insanity to the "Tools" menu.
2057 QStringList menu_path = action->text().split('/');
2058 QMenu *cur_menu = main_ui_->menuTools;
2059 while (menu_path.length() > 1) {
2060 QString menu_title = menu_path.takeFirst();
2061 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2062 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2064 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2065 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2068 submenu = cur_menu->addMenu(menu_title);
2069 submenu->setObjectName(menu_title.toLower());
2073 action->setText(menu_path.last());
2074 cur_menu->addAction(action);
2078 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2082 // Connect each action type to its corresponding slot. We to
2083 // distinguish various types of actions. Setting their objectName
2084 // seems to work OK.
2085 if (action->objectName() == TapParameterDialog::actionName()) {
2086 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2087 } else if (action->objectName() == FunnelStatistics::actionName()) {
2088 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2092 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2094 foreach (QAction *action, actions) {
2095 switch (menu_group) {
2096 case REGISTER_ANALYZE_GROUP_UNSORTED:
2097 case REGISTER_STAT_GROUP_UNSORTED:
2098 main_ui_->menuStatistics->removeAction(action);
2100 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2101 main_ui_->menuServiceResponseTime->removeAction(action);
2103 case REGISTER_STAT_GROUP_TELEPHONY:
2104 main_ui_->menuTelephony->removeAction(action);
2106 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2107 main_ui_->menuANSI->removeAction(action);
2109 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2110 main_ui_->menuGSM->removeAction(action);
2112 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2113 main_ui_->menuLTE->removeAction(action);
2115 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2116 main_ui_->menuMTP3->removeAction(action);
2118 case REGISTER_TOOLS_GROUP_UNSORTED:
2120 // Allow removal of submenus.
2121 // For now we limit the insanity to the "Tools" menu.
2122 QStringList menu_path = action->text().split('/');
2123 QMenu *cur_menu = main_ui_->menuTools;
2124 while (menu_path.length() > 1) {
2125 QString menu_title = menu_path.takeFirst();
2126 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2127 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2129 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2130 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2134 cur_menu->removeAction(action);
2138 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2144 void MainWindow::addDynamicMenus()
2147 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2148 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2149 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2151 // Fill in each menu
2152 foreach (register_stat_group_t menu_group, menu_groups) {
2153 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2154 addMenuActions(actions, menu_group);
2157 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2158 // We've added a placeholder in order to make sure the "Tools" menu is
2159 // visible. Hide it as needed.
2160 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2161 main_ui_->actionToolsPlaceholder->setVisible(false);
2163 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2164 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2166 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2167 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2169 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2170 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2172 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2173 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2177 void MainWindow::reloadDynamicMenus()
2179 foreach (register_stat_group_t menu_group, menu_groups) {
2180 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2181 removeMenuActions(actions, menu_group);
2183 actions = wsApp->addedMenuGroupItems(menu_group);
2184 addMenuActions(actions, menu_group);
2187 wsApp->clearAddedMenuGroupItems();
2188 wsApp->clearRemovedMenuGroupItems();
2191 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2193 QAction * itemAction = NULL;
2194 ext_menubar_t * item = NULL;
2195 GList * children = NULL;
2197 /* There must exists an xpath parent */
2198 g_assert(subMenu != NULL);
2200 /* If the depth counter exceeds, something must have gone wrong */
2201 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2203 children = menu->children;
2204 /* Iterate the child entries */
2205 while ( children != NULL && children->data != NULL )
2207 item = (ext_menubar_t *) children->data;
2209 if ( item->type == EXT_MENUBAR_MENU )
2211 /* Handle Submenu entry */
2212 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++ );
2214 else if ( item->type == EXT_MENUBAR_SEPARATOR )
2216 subMenu->addSeparator();
2218 else if ( item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL )
2220 itemAction = subMenu->addAction(item->name);
2221 itemAction->setData(QVariant::fromValue((void *)item));
2222 itemAction->setText(item->label);
2223 connect(itemAction, SIGNAL(triggered()),
2224 this, SLOT(externalMenuItem_triggered()));
2228 children = g_list_next(children);
2232 QMenu * MainWindow::searchSubMenu(QString objectName)
2236 if ( objectName.length() > 0 )
2238 QString searchName = QString("menu") + objectName;
2240 lst = main_ui_->menuBar->findChildren<QMenu*>();
2241 foreach (QMenu* m, lst)
2243 if ( QString::compare( m->objectName(), searchName ) == 0 )
2251 void MainWindow::addExternalMenus()
2253 QMenu * subMenu = NULL;
2254 GList * user_menu = NULL;
2255 ext_menu_t * menu = NULL;
2257 user_menu = ext_menubar_get_entries();
2259 while ( ( user_menu != NULL ) && ( user_menu->data != NULL ) )
2261 menu = (ext_menu_t *) user_menu->data;
2263 /* On this level only menu items should exist. Not doing an assert here,
2264 * as it could be an honest mistake */
2265 if ( menu->type != EXT_MENUBAR_MENU )
2267 user_menu = g_list_next(user_menu);
2271 /* Create main submenu and add it to the menubar */
2272 if ( menu->parent_menu != NULL )
2274 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2275 if ( sortUnderneath != NULL)
2276 subMenu = sortUnderneath->addMenu(menu->label);
2279 if ( subMenu == NULL )
2280 subMenu = main_ui_->menuBar->addMenu(menu->label);
2282 /* This will generate the action structure for each menu. It is recursive,
2283 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2284 this->externalMenuHelper(menu, subMenu, 0);
2287 user_menu = g_list_next (user_menu);
2297 * indent-tabs-mode: nil
2300 * ex: set shiftwidth=4 tabstop=8 expandtab:
2301 * :indentSize=4:tabSize=8:noTabs=true: