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("splitterMaster");
350 extra_split_.setObjectName("splitterExtra");
351 main_ui_->mainStack->addWidget(&master_split_);
353 empty_pane_.setObjectName("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(captureCaptureUpdateStarted(capture_session *)),
400 wsApp, SLOT(captureStarted()));
401 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
402 wsApp, SLOT(captureFinished()));
403 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
404 wsApp, SLOT(captureStarted()));
405 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
406 wsApp, SLOT(captureFinished()));
408 connect(&capture_file_, SIGNAL(captureFileOpened()),
409 this, SLOT(captureFileOpened()));
410 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
411 this, SLOT(captureFileReadStarted()));
412 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
413 this, SLOT(captureFileReadFinished()));
414 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
415 this, SLOT(captureFileReloadStarted()));
416 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
417 this, SLOT(captureFileReadFinished()));
418 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
419 this, SLOT(captureFileRescanStarted()));
420 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
421 this, SLOT(captureFileReadFinished()));
422 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
423 this, SLOT(captureFileRetapStarted()));
424 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
425 this, SLOT(captureFileRetapFinished()));
426 connect(&capture_file_, SIGNAL(captureFileClosing()),
427 this, SLOT(captureFileClosing()));
428 connect(&capture_file_, SIGNAL(captureFileClosed()),
429 this, SLOT(captureFileClosed()));
431 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
432 this, SLOT(captureFileSaveStarted(QString)));
433 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
434 main_ui_->statusBar, SLOT(popFileStatus()));
435 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
436 main_ui_->statusBar, SLOT(popFileStatus()));
437 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
438 main_ui_->statusBar, SLOT(popFileStatus()));
440 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
441 wsApp, SLOT(captureFileReadStarted()));
442 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
443 wsApp, SLOT(updateTaps()));
445 connect(wsApp, SIGNAL(recentFilesRead()),
446 packet_list_, SLOT(applyRecentColumnWidths()));
447 connect(wsApp, SIGNAL(columnsChanged()),
448 packet_list_, SLOT(columnsChanged()));
449 connect(wsApp, SIGNAL(preferencesChanged()),
450 packet_list_, SLOT(elideModeChanged()));
451 connect(wsApp, SIGNAL(recentFilesRead()),
452 this, SLOT(applyRecentPaneGeometry()));
453 connect(wsApp, SIGNAL(packetDissectionChanged()),
454 this, SLOT(redissectPackets()));
455 connect(wsApp, SIGNAL(appInitialized()),
456 this, SLOT(filterExpressionsChanged()));
457 connect(wsApp, SIGNAL(filterExpressionsChanged()),
458 this, SLOT(filterExpressionsChanged()));
459 connect(wsApp, SIGNAL(fieldsChanged()),
460 this, SLOT(fieldsChanged()));
462 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
463 this, SLOT(mainStackChanged(int)));
465 connect(main_welcome_, SIGNAL(startCapture()),
466 this, SLOT(startCapture()));
467 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
468 this, SLOT(openCaptureFile(QString)));
469 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
470 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
471 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
472 main_ui_->statusBar, SLOT(popFilterStatus()));
474 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
475 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
476 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
477 this, SLOT(redissectPackets()));
478 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
479 this, SLOT(showPreferencesDialog(QString)));
480 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
481 this, SLOT(showPreferencesDialog(QString)));
483 connect(this, SIGNAL(setCaptureFile(capture_file*)),
484 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
485 connect(this, SIGNAL(setCaptureFile(capture_file*)),
486 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
487 connect(this, SIGNAL(setCaptureFile(capture_file*)),
488 packet_list_, SLOT(setCaptureFile(capture_file*)));
489 connect(this, SIGNAL(setCaptureFile(capture_file*)),
490 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
492 connect(this, SIGNAL(monospaceFontChanged(QFont)),
493 packet_list_, SLOT(setMonospaceFont(QFont)));
494 connect(this, SIGNAL(monospaceFontChanged(QFont)),
495 proto_tree_, SLOT(setMonospaceFont(QFont)));
496 connect(this, SIGNAL(monospaceFontChanged(QFont)),
497 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
499 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
500 packet_list_, SLOT(goNextPacket()));
501 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
502 packet_list_, SLOT(goPreviousPacket()));
503 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
504 packet_list_, SLOT(goFirstPacket()));
505 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
506 packet_list_, SLOT(goLastPacket()));
508 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
509 proto_tree_, SLOT(expandSubtrees()));
510 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
511 proto_tree_, SLOT(expandAll()));
512 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
513 proto_tree_, SLOT(collapseAll()));
515 connect(packet_list_, SIGNAL(packetSelectionChanged()),
516 this, SLOT(setMenusForSelectedPacket()));
517 connect(packet_list_, SIGNAL(packetDissectionChanged()),
518 this, SLOT(redissectPackets()));
519 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
520 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
521 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
522 this, SLOT(showPreferencesDialog(QString)));
523 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
524 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
525 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
526 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
527 packet_list_, SLOT(columnsChanged()));
528 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
529 this, SLOT(openPacketDialog()));
530 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
531 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
532 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
533 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
534 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
535 main_ui_->statusBar, SLOT(popBusyStatus()));
536 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
537 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
538 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
539 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
540 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
541 main_ui_->statusBar, SLOT(popProgressStatus()));
543 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
544 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
545 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
546 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
547 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
548 this, SLOT(openPacketDialog(bool)));
549 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
550 this, SLOT(showPreferencesDialog(QString)));
551 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
552 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
554 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
555 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
557 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
558 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
560 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
561 &capture_file_, SLOT(stopLoading()));
563 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
564 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
566 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
567 this, SLOT(openCaptureFile(QString)));
570 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
572 connect(iface_tree, SIGNAL(itemSelectionChanged()),
573 this, SLOT(interfaceSelectionChanged()));
575 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
576 this, SLOT(captureFilterSyntaxChanged(bool)));
579 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
580 this, SLOT(showExtcapOptionsDialog(QString&)));
583 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
584 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
585 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
586 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
587 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
588 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
591 /* Create plugin_if hooks */
592 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter );
593 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter );
594 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
595 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
597 main_ui_->mainStack->setCurrentWidget(main_welcome_);
600 MainWindow::~MainWindow()
605 QString MainWindow::getFilter()
607 return df_combo_box_->itemText(df_combo_box_->count());
610 QMenu *MainWindow::createPopupMenu()
612 QMenu *menu = new QMenu();
613 menu->addAction(main_ui_->actionViewMainToolbar);
614 menu->addAction(main_ui_->actionViewFilterToolbar);
615 menu->addAction(main_ui_->actionViewWirelessToolbar);
616 menu->addAction(main_ui_->actionViewStatusBar);
617 menu->addSeparator();
618 menu->addAction(main_ui_->actionViewPacketList);
619 menu->addAction(main_ui_->actionViewPacketDetails);
620 menu->addAction(main_ui_->actionViewPacketBytes);
624 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
626 pipe_source_ = source;
627 pipe_child_process_ = child_process;
628 pipe_user_data_ = user_data;
629 pipe_input_cb_ = input_cb;
632 /* Tricky to use pipes in win9x, as no concept of wait. NT can
633 do this but that doesn't cover all win32 platforms. GTK can do
634 this but doesn't seem to work over processes. Attempt to do
635 something similar here, start a timer and check for data on every
637 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
640 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
644 pipe_timer_ = new QTimer(this);
645 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
646 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
647 pipe_timer_->start(200);
649 if (pipe_notifier_) {
650 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
651 delete pipe_notifier_;
654 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
655 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
656 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
657 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
661 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
663 // The user typed some text. Start filling in a filter.
664 // We may need to be more choosy here. We just need to catch events for the packet list,
665 // proto tree, and main welcome widgets.
666 if (event->type() == QEvent::KeyPress) {
667 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
668 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
669 df_combo_box_->lineEdit()->insert(kevt->text());
670 df_combo_box_->lineEdit()->setFocus();
675 return QMainWindow::eventFilter(obj, event);
678 void MainWindow::keyPressEvent(QKeyEvent *event) {
680 // Explicitly focus on the display filter combo.
681 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
682 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
686 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
687 if (event->modifiers() == Qt::NoModifier) {
688 if (event->key() == Qt::Key_Escape) {
689 on_goToCancel_clicked();
690 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
694 return; // goToLineEdit didn't want it and we don't either.
697 // Move up & down the packet list.
698 if (event->key() == Qt::Key_F7) {
699 packet_list_->goPreviousPacket();
700 } else if (event->key() == Qt::Key_F8) {
701 packet_list_->goNextPacket();
704 // Move along, citizen.
705 QMainWindow::keyPressEvent(event);
708 void MainWindow::closeEvent(QCloseEvent *event) {
709 saveWindowGeometry();
711 /* If we're in the middle of stopping a capture, don't do anything;
712 the user can try deleting the window after the capture stops. */
713 if (capture_stopping_) {
718 QString before_what(tr(" before quitting"));
719 if (!testCaptureFileClose(TRUE, before_what)) {
725 capture_interfaces_dialog_.close();
727 // Make sure we kill any open dumpcap processes.
728 delete main_welcome_;
730 // One of the many places we assume one main window.
731 if(!wsApp->isInitialized()) {
732 // If we're still initializing, QCoreApplication::quit() won't
733 // exit properly because we are not in the event loop. This
734 // means that the application won't clean up after itself. We
735 // might want to call wsApp->processEvents() during startup
736 // instead so that we can do a normal exit here.
742 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
745 foreach (QUrl drag_url, event->mimeData()->urls()) {
746 if (!drag_url.toLocalFile().isEmpty()) {
751 if (accept) event->acceptProposedAction();
754 void MainWindow::dropEvent(QDropEvent *event)
756 foreach (QUrl drop_url, event->mimeData()->urls()) {
757 QString local_file = drop_url.toLocalFile();
758 if (!local_file.isEmpty()) {
759 event->acceptProposedAction();
760 openCaptureFile(local_file);
766 // Apply recent settings to the main window geometry.
767 // We haven't loaded the preferences at this point so we assume that the
768 // position and size preference are enabled.
769 void MainWindow::loadWindowGeometry()
771 int min_sensible_dimension_ = 200;
774 if (recent.gui_geometry_main_maximized) {
775 setWindowState(Qt::WindowMaximized);
779 // if (prefs.gui_geometry_save_position) {
780 move(recent.gui_geometry_main_x, recent.gui_geometry_main_y);
783 if (// prefs.gui_geometry_save_size &&
784 recent.gui_geometry_main_width > min_sensible_dimension_ &&
785 recent.gui_geometry_main_height > min_sensible_dimension_) {
786 resize(recent.gui_geometry_main_width, recent.gui_geometry_main_height);
791 void MainWindow::saveWindowGeometry()
793 if (prefs.gui_geometry_save_position) {
794 recent.gui_geometry_main_x = pos().x();
795 recent.gui_geometry_main_y = pos().y();
798 if (prefs.gui_geometry_save_size) {
799 recent.gui_geometry_main_width = size().width();
800 recent.gui_geometry_main_height = size().height();
803 if (prefs.gui_geometry_save_maximized) {
804 // On OS X this is false when it shouldn't be
805 recent.gui_geometry_main_maximized = isMaximized();
808 if (master_split_.sizes().length() > 0) {
809 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
812 if (master_split_.sizes().length() > 2) {
813 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
814 } else if (extra_split_.sizes().length() > 0) {
815 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
819 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
821 case layout_pane_content_none:
823 case layout_pane_content_plist:
825 case layout_pane_content_pdetails:
827 case layout_pane_content_pbytes:
828 return byte_view_tab_;
830 g_assert_not_reached();
835 void MainWindow::mergeCaptureFile()
837 QString file_name = "";
838 QString display_filter = "";
839 dfilter_t *rfcode = NULL;
842 if (!capture_file_.capFile())
845 if (prefs.gui_ask_unsaved) {
846 if (cf_has_unsaved_data(capture_file_.capFile())) {
847 QMessageBox msg_dialog;
848 gchar *display_basename;
851 msg_dialog.setIcon(QMessageBox::Question);
852 /* This file has unsaved data; ask the user whether to save
854 if (capture_file_.capFile()->is_tempfile) {
855 msg_dialog.setText(tr("Save packets before merging?"));
856 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
859 * Format the message.
861 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
862 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
863 g_free(display_basename);
864 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
867 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
868 msg_dialog.setDefaultButton(QMessageBox::Save);
870 response = msg_dialog.exec();
874 case QMessageBox::Save:
875 /* Save the file but don't close it */
876 saveCaptureFile(capture_file_.capFile(), FALSE);
879 case QMessageBox::Cancel:
881 /* Don't do the merge. */
888 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), display_filter);
890 cf_status_t merge_status;
891 char *in_filenames[2];
894 switch (prefs.gui_fileopen_style) {
896 case FO_STYLE_LAST_OPENED:
897 /* The user has specified that we should start out in the last directory
898 we looked in. If we've already opened a file, use its containing
899 directory, if we could determine it, as the directory, otherwise
900 use the "last opened" directory saved in the preferences file if
902 /* This is now the default behaviour in file_selection_new() */
905 case FO_STYLE_SPECIFIED:
906 /* The user has specified that we should always start out in a
907 specified directory; if they've specified that directory,
908 start out by showing the files in that dir. */
909 if (prefs.gui_fileopen_dir[0] != '\0')
910 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
914 if (merge_dlg.merge(file_name)) {
917 if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode, &err_msg)) {
918 cf_set_rfcode(capture_file_.capFile(), rfcode);
920 /* Not valid. Tell the user, and go back and run the file
921 selection box again once they dismiss the alert. */
922 //bad_dfilter_alert_box(top_level, display_filter->str);
923 QMessageBox::warning(this, tr("Invalid Display Filter"),
924 QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, err_msg)),
933 file_type = capture_file_.capFile()->cd_t;
935 /* Try to merge or append the two files */
937 if (merge_dlg.mergeType() == 0) {
938 /* chronological order */
939 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
940 in_filenames[1] = qstring_strdup(file_name);
941 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
942 } else if (merge_dlg.mergeType() <= 0) {
944 in_filenames[0] = qstring_strdup(file_name);
945 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
946 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
949 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
950 in_filenames[1] = qstring_strdup(file_name);
951 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
954 g_free(in_filenames[0]);
955 g_free(in_filenames[1]);
957 if (merge_status != CF_OK) {
959 dfilter_free(rfcode);
964 cf_close(capture_file_.capFile());
966 /* Try to open the merged capture file. */
967 CaptureFile::globalCapFile()->window = this;
968 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
969 /* We couldn't open it; fail. */
970 CaptureFile::globalCapFile()->window = NULL;
972 dfilter_free(rfcode);
977 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
978 it closed the previous capture file, and thus destroyed any
979 previous read filter attached to "cf"). */
980 CaptureFile::globalCapFile()->rfcode = rfcode;
982 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
986 /* Just because we got an error, that doesn't mean we were unable
987 to read any of the file; we handle what we could get from the
991 case CF_READ_ABORTED:
992 /* The user bailed out of re-reading the capture file; the
993 capture file has been closed - just free the capture file name
994 string and return (without changing the last containing
1000 /* Save the name of the containing directory specified in the path name,
1001 if any; we can write over cf_merged_name, which is a good thing, given that
1002 "get_dirname()" does write over its argument. */
1003 wsApp->setLastOpenDir(get_dirname(tmpname));
1005 df_combo_box_->setEditText(display_filter);
1006 main_ui_->statusBar->showExpert();
1012 void MainWindow::importCaptureFile() {
1013 ImportTextDialog import_dlg;
1015 QString before_what(tr(" before importing a new capture"));
1016 if (!testCaptureFileClose(FALSE, before_what))
1021 if (import_dlg.result() != QDialog::Accepted) {
1022 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1026 openCaptureFile(import_dlg.capfileName());
1029 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1031 gboolean discard_comments;
1033 if (cf->is_tempfile) {
1034 /* This is a temporary capture file, so saving it means saving
1035 it to a permanent file. Prompt the user for a location
1036 to which to save it. Don't require that the file format
1037 support comments - if it's a temporary capture file, it's
1038 probably pcap-ng, which supports comments and, if it's
1039 not pcap-ng, let the user decide what they want to do
1040 if they've added comments. */
1041 saveAsCaptureFile(cf, FALSE, dont_reopen);
1043 if (cf->unsaved_changes) {
1044 cf_write_status_t status;
1046 /* This is not a temporary capture file, but it has unsaved
1047 changes, so saving it means doing a "safe save" on top
1048 of the existing file, in the same format - no UI needed
1049 unless the file has comments and the file's format doesn't
1052 If the file has comments, does the file's format support them?
1053 If not, ask the user whether they want to discard the comments
1054 or choose a different format. */
1055 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1058 /* The file can be saved in the specified format as is;
1059 just drive on and save in the format they selected. */
1060 discard_comments = FALSE;
1063 case SAVE_WITHOUT_COMMENTS:
1064 /* The file can't be saved in the specified format as is,
1065 but it can be saved without the comments, and the user
1066 said "OK, discard the comments", so save it in the
1067 format they specified without the comments. */
1068 discard_comments = TRUE;
1071 case SAVE_IN_ANOTHER_FORMAT:
1072 /* There are file formats in which we can save this that
1073 support comments, and the user said not to delete the
1074 comments. Do a "Save As" so the user can select
1075 one of those formats and choose a file name. */
1076 saveAsCaptureFile(cf, TRUE, dont_reopen);
1080 /* The user said "forget it". Just return. */
1084 /* Squelch warnings that discard_comments is being used
1086 g_assert_not_reached();
1090 /* XXX - cf->filename might get freed out from under us, because
1091 the code path through which cf_save_records() goes currently
1092 closes the current file and then opens and reloads the saved file,
1093 so make a copy and free it later. */
1094 file_name = cf->filename;
1095 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1096 discard_comments, dont_reopen);
1100 /* The save succeeded; we're done.
1101 If we discarded comments, redraw the packet list to reflect
1102 any packets that no longer have comments. */
1103 if (discard_comments)
1104 packet_list_queue_draw();
1106 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1107 updateForUnsavedChanges(); // we update the title bar to remove the *
1110 case CF_WRITE_ERROR:
1111 /* The write failed.
1112 XXX - OK, what do we do now? Let them try a
1113 "Save As", in case they want to try to save to a
1114 different directory r file system? */
1117 case CF_WRITE_ABORTED:
1118 /* The write was aborted; just drive on. */
1122 /* Otherwise just do nothing. */
1126 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1127 QString file_name = "";
1129 gboolean compressed;
1130 cf_write_status_t status;
1132 gboolean discard_comments = FALSE;
1139 CaptureFileDialog save_as_dlg(this, cf);
1141 switch (prefs.gui_fileopen_style) {
1143 case FO_STYLE_LAST_OPENED:
1144 /* The user has specified that we should start out in the last directory
1145 we looked in. If we've already opened a file, use its containing
1146 directory, if we could determine it, as the directory, otherwise
1147 use the "last opened" directory saved in the preferences file if
1149 /* This is now the default behaviour in file_selection_new() */
1152 case FO_STYLE_SPECIFIED:
1153 /* The user has specified that we should always start out in a
1154 specified directory; if they've specified that directory,
1155 start out by showing the files in that dir. */
1156 if (prefs.gui_fileopen_dir[0] != '\0')
1157 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1161 /* If the file has comments, does the format the user selected
1162 support them? If not, ask the user whether they want to
1163 discard the comments or choose a different format. */
1164 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1167 /* The file can be saved in the specified format as is;
1168 just drive on and save in the format they selected. */
1169 discard_comments = FALSE;
1172 case SAVE_WITHOUT_COMMENTS:
1173 /* The file can't be saved in the specified format as is,
1174 but it can be saved without the comments, and the user
1175 said "OK, discard the comments", so save it in the
1176 format they specified without the comments. */
1177 discard_comments = TRUE;
1180 case SAVE_IN_ANOTHER_FORMAT:
1181 /* There are file formats in which we can save this that
1182 support comments, and the user said not to delete the
1183 comments. The combo box of file formats has had the
1184 formats that don't support comments trimmed from it,
1185 so run the dialog again, to let the user decide
1186 whether to save in one of those formats or give up. */
1187 must_support_comments = TRUE;
1191 /* The user said "forget it". Just get rid of the dialog box
1195 file_type = save_as_dlg.selectedFileType();
1196 compressed = save_as_dlg.isCompressed();
1198 fileAddExtension(file_name, file_type, compressed);
1201 // /* If the file exists and it's user-immutable or not writable,
1202 // ask the user whether they want to override that. */
1203 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1204 // /* They don't. Let them try another file name or cancel. */
1209 /* Attempt to save the file */
1210 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1211 discard_comments, dont_reopen);
1215 /* The save succeeded; we're done. */
1216 /* Save the directory name for future file dialogs. */
1217 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1218 set_last_open_dir(dirname);
1220 /* If we discarded comments, redraw the packet list to reflect
1221 any packets that no longer have comments. */
1222 if (discard_comments)
1223 packet_list_queue_draw();
1225 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1226 updateForUnsavedChanges(); // we update the title bar to remove the *
1229 case CF_WRITE_ERROR:
1230 /* The save failed; let the user try again. */
1233 case CF_WRITE_ABORTED:
1234 /* The user aborted the save; just return. */
1241 void MainWindow::exportSelectedPackets() {
1242 QString file_name = "";
1244 gboolean compressed;
1245 packet_range_t range;
1246 cf_write_status_t status;
1248 gboolean discard_comments = FALSE;
1250 if (!capture_file_.capFile())
1253 /* Init the packet range */
1254 packet_range_init(&range, capture_file_.capFile());
1255 range.process_filtered = TRUE;
1256 range.include_dependents = TRUE;
1259 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1261 switch (prefs.gui_fileopen_style) {
1263 case FO_STYLE_LAST_OPENED:
1264 /* The user has specified that we should start out in the last directory
1265 we looked in. If we've already opened a file, use its containing
1266 directory, if we could determine it, as the directory, otherwise
1267 use the "last opened" directory saved in the preferences file if
1269 /* This is now the default behaviour in file_selection_new() */
1272 case FO_STYLE_SPECIFIED:
1273 /* The user has specified that we should always start out in a
1274 specified directory; if they've specified that directory,
1275 start out by showing the files in that dir. */
1276 if (prefs.gui_fileopen_dir[0] != '\0')
1277 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1281 /* If the file has comments, does the format the user selected
1282 support them? If not, ask the user whether they want to
1283 discard the comments or choose a different format. */
1284 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1287 /* The file can be saved in the specified format as is;
1288 just drive on and save in the format they selected. */
1289 discard_comments = FALSE;
1292 case SAVE_WITHOUT_COMMENTS:
1293 /* The file can't be saved in the specified format as is,
1294 but it can be saved without the comments, and the user
1295 said "OK, discard the comments", so save it in the
1296 format they specified without the comments. */
1297 discard_comments = TRUE;
1300 case SAVE_IN_ANOTHER_FORMAT:
1301 /* There are file formats in which we can save this that
1302 support comments, and the user said not to delete the
1303 comments. The combo box of file formats has had the
1304 formats that don't support comments trimmed from it,
1305 so run the dialog again, to let the user decide
1306 whether to save in one of those formats or give up. */
1310 /* The user said "forget it". Just get rid of the dialog box
1316 * Check that we're not going to save on top of the current
1318 * We do it here so we catch all cases ...
1319 * Unfortunately, the file requester gives us an absolute file
1320 * name and the read file name may be relative (if supplied on
1321 * the command line). From Joerg Mayer.
1323 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1324 QMessageBox msg_box;
1325 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1327 msg_box.setIcon(QMessageBox::Critical);
1328 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1329 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1330 msg_box.setStandardButtons(QMessageBox::Ok);
1331 msg_box.setDefaultButton(QMessageBox::Ok);
1333 g_free(display_basename);
1337 file_type = esp_dlg.selectedFileType();
1338 compressed = esp_dlg.isCompressed();
1339 fileAddExtension(file_name, file_type, compressed);
1342 // /* If the file exists and it's user-immutable or not writable,
1343 // ask the user whether they want to override that. */
1344 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1345 // /* They don't. Let them try another file name or cancel. */
1350 /* Attempt to save the file */
1351 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1355 /* The save succeeded; we're done. */
1356 /* Save the directory name for future file dialogs. */
1357 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1358 set_last_open_dir(dirname);
1360 /* If we discarded comments, redraw the packet list to reflect
1361 any packets that no longer have comments. */
1362 if (discard_comments)
1363 packet_list_queue_draw();
1366 case CF_WRITE_ERROR:
1367 /* The save failed; let the user try again. */
1370 case CF_WRITE_ABORTED:
1371 /* The user aborted the save; just return. */
1378 void MainWindow::exportDissections(export_type_e export_type) {
1379 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1380 packet_range_t range;
1382 if (!capture_file_.capFile())
1385 /* Init the packet range */
1386 packet_range_init(&range, capture_file_.capFile());
1387 range.process_filtered = TRUE;
1388 range.include_dependents = TRUE;
1393 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1394 QString file_name_lower;
1395 QString file_suffix;
1396 GSList *extensions_list;
1397 gboolean add_extension;
1400 * Append the default file extension if there's none given by
1401 * the user or if they gave one that's not one of the valid
1402 * extensions for the file type.
1404 file_name_lower = file_name.toLower();
1405 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1406 if (extensions_list != NULL) {
1409 /* We have one or more extensions for this file type.
1410 Start out assuming we need to add the default one. */
1411 add_extension = TRUE;
1413 /* OK, see if the file has one of those extensions. */
1414 for (extension = extensions_list; extension != NULL;
1415 extension = g_slist_next(extension)) {
1416 file_suffix += tr(".") + (char *)extension->data;
1417 if (file_name_lower.endsWith(file_suffix)) {
1419 * The file name has one of the extensions for
1422 add_extension = FALSE;
1425 file_suffix += ".gz";
1426 if (file_name_lower.endsWith(file_suffix)) {
1428 * The file name has one of the extensions for
1431 add_extension = FALSE;
1436 /* We have no extensions for this file type. Don't add one. */
1437 add_extension = FALSE;
1439 if (add_extension) {
1440 if (wtap_default_file_extension(file_type) != NULL) {
1441 file_name += tr(".") + wtap_default_file_extension(file_type);
1449 bool MainWindow::testCaptureFileClose(bool from_quit, QString before_what) {
1450 bool capture_in_progress = FALSE;
1452 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1453 return true; /* Already closed, nothing to do */
1456 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS && capture_file_.capFile()->count>0) {
1457 /* This is true if we're reading a capture file *or* if we're doing
1458 a live capture. If we're reading a capture file, the main loop
1459 is busy reading packets, and only accepting input from the
1460 progress dialog, so we can't get here, so this means we're
1462 capture_in_progress = TRUE;
1466 if (prefs.gui_ask_unsaved) {
1467 if (cf_has_unsaved_data(capture_file_.capFile()) || capture_in_progress) {
1468 QMessageBox msg_dialog;
1470 QPushButton *saveButton;
1471 QPushButton *discardButton;
1473 msg_dialog.setIcon(QMessageBox::Question);
1474 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1475 /* This file has unsaved data or there's a capture in
1476 progress; ask the user whether to save the data. */
1477 if (capture_file_.capFile()->is_tempfile) {
1479 msg_dialog.setText(tr("You have unsaved packets"));
1480 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1482 if (capture_in_progress) {
1483 question.append(tr("Do you want to stop the capture and save the captured packets"));
1485 question.append(tr("Do you want to save the captured packets"));
1487 question.append(before_what).append(tr("?"));
1488 msg_dialog.setInformativeText(question);
1493 * Format the message.
1495 if (capture_in_progress) {
1496 question.append(tr("Do you want to stop the capture and save the captured packets"));
1497 question.append(before_what).append(tr("?"));
1498 msg_dialog.setText(question);
1499 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1501 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1502 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1503 .arg(display_basename)
1506 g_free(display_basename);
1507 msg_dialog.setText(question);
1508 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1512 // XXX Text comes from ui/gtk/stock_icons.[ch]
1513 // Note that the button roles differ from the GTK+ version.
1514 // Cancel = RejectRole
1515 // Save = AcceptRole
1516 // Don't Save = DestructiveRole
1517 msg_dialog.addButton(QMessageBox::Cancel);
1519 if (capture_in_progress) {
1520 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1522 saveButton = msg_dialog.addButton(QMessageBox::Save);
1524 msg_dialog.setDefaultButton(saveButton);
1527 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1528 discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1529 QMessageBox::DestructiveRole);
1531 discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1532 QMessageBox::DestructiveRole);
1535 if (capture_in_progress) {
1536 discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1537 QMessageBox::DestructiveRole);
1539 discardButton = msg_dialog.addButton(tr("Continue &without Saving"), QMessageBox::DestructiveRole);
1544 /* According to the Qt doc:
1545 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1547 * Therefore we should use clickedButton() to determine which button was clicked. */
1549 if(msg_dialog.clickedButton() == saveButton)
1552 /* If there's a capture in progress, we have to stop the capture
1553 and then do the save. */
1554 if (capture_in_progress)
1557 /* Save the file and close it */
1558 saveCaptureFile(capture_file_.capFile(), TRUE);
1560 else if(msg_dialog.clickedButton() == discardButton)
1564 * If there's a capture in progress; we have to stop the capture
1565 * and then do the close.
1567 if (capture_in_progress)
1570 /* Just close the file, discarding changes */
1571 cf_close(capture_file_.capFile());
1574 else //cancelButton or some other unspecified button
1580 /* Unchanged file, just close it */
1581 capture_file_.capFile()->state = FILE_READ_ABORTED;
1582 cf_close(capture_file_.capFile());
1585 /* User asked not to be bothered by those prompts, just close it.
1586 XXX - should that apply only to saving temporary files? */
1588 /* If there's a capture in progress, we have to stop the capture
1589 and then do the close. */
1590 if (capture_in_progress)
1593 cf_close(capture_file_.capFile());
1596 return true; /* File closed */
1599 void MainWindow::captureStop() {
1602 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1603 WiresharkApplication::processEvents();
1607 void MainWindow::initMainToolbarIcons()
1609 #if defined(Q_OS_WIN)
1610 // Current GTK+ and other Windows app behavior.
1611 main_ui_->mainToolBar->setIconSize(QSize(16, 16));
1613 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1614 main_ui_->mainToolBar->setIconSize(QSize(24, 24));
1617 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1618 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1620 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1621 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1622 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1623 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1625 // Menu icons are disabled in main_window.ui for these items.
1626 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1627 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1628 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1629 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1631 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1632 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1633 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1634 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1635 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1636 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1637 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1639 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1640 main_ui_->actionViewColorizePacketList->setChecked(recent.packet_list_colorize);
1641 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1643 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1644 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1645 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1646 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1647 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1648 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1649 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1652 void MainWindow::initShowHideMainWidgets()
1654 if (show_hide_actions_) {
1658 show_hide_actions_ = new QActionGroup(this);
1659 QMap<QAction *, QWidget *> shmw_actions;
1661 show_hide_actions_->setExclusive(false);
1662 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1663 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1664 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1665 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1666 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1667 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1668 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1670 main_ui_->actionViewMainToolbar->setChecked(recent.main_toolbar_show);
1671 main_ui_->actionViewFilterToolbar->setChecked(recent.filter_toolbar_show);
1672 main_ui_->actionViewWirelessToolbar->setChecked(recent.wireless_toolbar_show);
1673 main_ui_->actionViewStatusBar->setChecked(recent.statusbar_show);
1674 main_ui_->actionViewPacketList->setChecked(recent.packet_list_show);
1675 main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show);
1676 main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show);
1678 foreach (QAction *shmwa, shmw_actions.keys()) {
1679 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1680 show_hide_actions_->addAction(shmwa);
1681 showHideMainWidgets(shmwa);
1684 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1687 Q_DECLARE_METATYPE(ts_type)
1689 void MainWindow::initTimeDisplayFormatMenu()
1691 if (time_display_actions_) {
1695 time_display_actions_ = new QActionGroup(this);
1696 QMap<QAction *, ts_type> td_actions;
1698 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1699 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1700 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1701 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1702 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1703 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1704 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1705 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1706 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1707 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1709 foreach (QAction* tda, td_actions.keys()) {
1710 tda->setData(qVariantFromValue(td_actions[tda]));
1711 time_display_actions_->addAction(tda);
1712 if (recent.gui_time_format == td_actions[tda]) {
1713 tda->setChecked(true);
1717 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1719 main_ui_->actionViewTimeDisplaySecondsWithHoursAndMinutes->setChecked(recent.gui_seconds_format == TS_SECONDS_HOUR_MIN_SEC);
1722 Q_DECLARE_METATYPE(ts_precision)
1724 void MainWindow::initTimePrecisionFormatMenu()
1726 if (time_precision_actions_) {
1730 time_precision_actions_ = new QActionGroup(this);
1731 QMap<QAction *, ts_precision> tp_actions;
1732 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1733 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1734 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1735 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1736 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1737 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1738 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1740 foreach (QAction* tpa, tp_actions.keys()) {
1741 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1742 time_precision_actions_->addAction(tpa);
1743 if (recent.gui_time_precision == tp_actions[tpa]) {
1744 tpa->setChecked(true);
1748 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1752 void MainWindow::setTitlebarForCaptureFile()
1754 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1756 // Qt *REALLY* doesn't like windows that sometimes have a
1757 // title set with setWindowTitle() and other times have a
1758 // file path set; apparently, once you've set the title
1759 // with setWindowTitle(), it sticks, and setWindowFilePath()
1760 // has no effect. It appears to can clear the title with
1761 // setWindowTitle(NULL), but that clears the actual title in
1762 // the title bar, and setWindowFilePath() then, I guess, sees
1763 // that there's already a file path, and does nothing, leaving
1764 // the title bar empty. So you then have to clear the file path
1765 // with setWindowFilePath(NULL), and then set it.
1767 // Maybe there's a #include "you're holding it wrong" here.
1768 // However, I really don't want to hear from people who think
1769 // that a window can never be associated with something other
1770 // than a user file at time T1 and with a user file at time T2,
1771 // given that, in Wireshark, a window can be associated with a
1772 // live capture at time T1 and then, after you've saved the live
1773 // capture to a user file, associated with a user file at time T2.
1775 if (capture_file_.capFile()->is_tempfile) {
1777 // For a temporary file, put the source of the data
1778 // in the window title, not whatever random pile
1779 // of characters is the last component of the path
1782 // XXX - on non-Mac platforms, put in the application
1785 // XXX - Use setWindowModified
1787 setWindowFilePath(NULL);
1788 window_name = g_strdup_printf("[*]%s", cf_get_tempfile_source(capture_file_.capFile())); //TODO : Fix Translate
1789 setWindowTitle(window_name);
1790 g_free(window_name);
1793 // For a user file, set the full path; that way,
1794 // for OS X, it'll set the "proxy icon". Qt
1795 // handles extracting the last component.
1797 // Sadly, some UN*Xes don't necessarily use UTF-8
1798 // for their file names, so we have to map the
1799 // file path to UTF-8. If that fails, we're somewhat
1802 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1807 if (utf8_filename == NULL) {
1808 // So what the heck else can we do here?
1809 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1811 setWindowTitle(NULL);
1812 setWindowFilePath(NULL);
1813 setWindowFilePath(utf8_filename);
1814 g_free(utf8_filename);
1817 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1819 /* We have no capture file. */
1820 setWindowFilePath(NULL);
1821 setWindowTitle(tr("The Wireshark Network Analyzer"));
1825 void MainWindow::setTitlebarForSelectedTreeRow()
1827 setWindowTitle(tr("The Wireshark Network Analyzer"));
1831 void MainWindow::setTitlebarForCaptureInProgress()
1835 setWindowFilePath(NULL);
1836 if (capture_file_.capFile()) {
1837 window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(capture_file_.capFile())); //TODO : Fix Translate
1838 setWindowTitle(window_name);
1839 g_free(window_name);
1841 /* We have no capture in progress. */
1842 setWindowTitle(tr("The Wireshark Network Analyzer"));
1848 /* Enable or disable menu items based on whether you have a capture file
1849 you've finished reading and, if you have one, whether it's been saved
1850 and whether it could be saved except by copying the raw packet data. */
1851 void MainWindow::setMenusForCaptureFile(bool force_disable)
1853 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1854 /* We have no capture file or we're currently reading a file */
1855 main_ui_->actionFileMerge->setEnabled(false);
1856 main_ui_->actionFileClose->setEnabled(false);
1857 main_ui_->actionFileSave->setEnabled(false);
1858 main_ui_->actionFileSaveAs->setEnabled(false);
1859 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1860 main_ui_->actionFileExportPackets->setEnabled(false);
1861 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1862 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1863 main_ui_->actionFileExportPDU->setEnabled(false);
1864 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1865 main_ui_->menuFileExportObjects->setEnabled(false);
1866 main_ui_->actionViewReload->setEnabled(false);
1868 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1870 main_ui_->actionFileClose->setEnabled(true);
1871 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1872 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1873 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1875 * "Export Specified Packets..." should be available only if
1876 * we can write the file out in at least one format.
1878 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1879 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1880 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1881 main_ui_->actionFileExportPDU->setEnabled(true);
1882 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1883 main_ui_->menuFileExportObjects->setEnabled(true);
1884 main_ui_->actionViewReload->setEnabled(true);
1888 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1889 /* Either a capture was started or stopped; in either case, it's not
1890 in the process of stopping, so allow quitting. */
1892 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1893 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1894 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1895 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1896 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1897 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1898 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1899 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1900 main_ui_->actionFileQuit->setEnabled(true);
1902 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1904 // XXX Fix packet list heading menu sensitivity
1905 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1906 // !capture_in_progress);
1907 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1908 // !capture_in_progress);
1909 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1910 // !capture_in_progress);
1913 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1914 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1915 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1916 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1917 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1918 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1919 #endif /* HAVE_LIBPCAP */
1923 void MainWindow::setMenusForCaptureStopping() {
1924 main_ui_->actionFileQuit->setEnabled(false);
1925 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1927 main_ui_->actionCaptureStart->setChecked(false);
1928 main_ui_->actionCaptureStop->setEnabled(false);
1929 main_ui_->actionCaptureRestart->setEnabled(false);
1930 #endif /* HAVE_LIBPCAP */
1933 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1935 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1937 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1938 // have_captured_packets);
1940 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
1941 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
1942 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
1944 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1945 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1946 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1947 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1948 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1950 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
1951 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
1952 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
1953 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
1955 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1956 // have_captured_packets);
1957 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1958 // have_captured_packets);
1959 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
1960 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
1961 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
1964 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1965 bool enable_next = fileset_get_next() != NULL && enable_list_files;
1966 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1968 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1969 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1970 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1973 void MainWindow::updateForUnsavedChanges() {
1974 setTitlebarForCaptureFile();
1975 setMenusForCaptureFile();
1978 void MainWindow::changeEvent(QEvent* event)
1982 switch (event->type())
1984 case QEvent::LanguageChange:
1985 main_ui_->retranslateUi(this);
1987 case QEvent::LocaleChange:{
1988 QString locale = QLocale::system().name();
1989 locale.truncate(locale.lastIndexOf('_'));
1990 wsApp->loadLanguage(locale);
1997 QMainWindow::changeEvent(event);
2000 /* Update main window items based on whether there's a capture in progress. */
2001 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2003 setMenusForCaptureInProgress(capture_in_progress);
2005 wireless_frame_->setCaptureInProgress(capture_in_progress);
2008 packet_list_->setCaptureInProgress(capture_in_progress);
2009 // set_toolbar_for_capture_in_progress(capture_in_progress);
2011 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2015 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2016 << REGISTER_ANALYZE_GROUP_UNSORTED
2017 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2018 << REGISTER_STAT_GROUP_UNSORTED
2019 << REGISTER_STAT_GROUP_GENERIC
2020 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2021 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2022 << REGISTER_STAT_GROUP_RESPONSE_TIME
2023 << REGISTER_STAT_GROUP_TELEPHONY
2024 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2025 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2026 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2027 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2028 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2029 << REGISTER_TOOLS_GROUP_UNSORTED;
2031 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2033 foreach (QAction *action, actions) {
2034 switch (menu_group) {
2035 case REGISTER_ANALYZE_GROUP_UNSORTED:
2036 case REGISTER_STAT_GROUP_UNSORTED:
2037 main_ui_->menuStatistics->insertAction(
2038 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2041 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2042 main_ui_->menuServiceResponseTime->addAction(action);
2044 case REGISTER_STAT_GROUP_TELEPHONY:
2045 main_ui_->menuTelephony->addAction(action);
2047 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2048 main_ui_->menuANSI->addAction(action);
2050 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2051 main_ui_->menuGSM->addAction(action);
2053 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2054 main_ui_->menuLTE->addAction(action);
2056 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2057 main_ui_->menuMTP3->addAction(action);
2059 case REGISTER_TOOLS_GROUP_UNSORTED:
2061 // Allow the creation of submenus. Mimics the behavor of
2062 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2063 // and GtkUIManager.
2065 // For now we limit the insanity to the "Tools" menu.
2066 QStringList menu_path = action->text().split('/');
2067 QMenu *cur_menu = main_ui_->menuTools;
2068 while (menu_path.length() > 1) {
2069 QString menu_title = menu_path.takeFirst();
2070 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2071 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2073 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2074 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2077 submenu = cur_menu->addMenu(menu_title);
2078 submenu->setObjectName(menu_title.toLower());
2082 action->setText(menu_path.last());
2083 cur_menu->addAction(action);
2087 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2091 // Connect each action type to its corresponding slot. We to
2092 // distinguish various types of actions. Setting their objectName
2093 // seems to work OK.
2094 if (action->objectName() == TapParameterDialog::actionName()) {
2095 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2096 } else if (action->objectName() == FunnelStatistics::actionName()) {
2097 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2101 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2103 foreach (QAction *action, actions) {
2104 switch (menu_group) {
2105 case REGISTER_ANALYZE_GROUP_UNSORTED:
2106 case REGISTER_STAT_GROUP_UNSORTED:
2107 main_ui_->menuStatistics->removeAction(action);
2109 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2110 main_ui_->menuServiceResponseTime->removeAction(action);
2112 case REGISTER_STAT_GROUP_TELEPHONY:
2113 main_ui_->menuTelephony->removeAction(action);
2115 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2116 main_ui_->menuANSI->removeAction(action);
2118 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2119 main_ui_->menuGSM->removeAction(action);
2121 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2122 main_ui_->menuLTE->removeAction(action);
2124 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2125 main_ui_->menuMTP3->removeAction(action);
2127 case REGISTER_TOOLS_GROUP_UNSORTED:
2129 // Allow removal of submenus.
2130 // For now we limit the insanity to the "Tools" menu.
2131 QStringList menu_path = action->text().split('/');
2132 QMenu *cur_menu = main_ui_->menuTools;
2133 while (menu_path.length() > 1) {
2134 QString menu_title = menu_path.takeFirst();
2135 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2136 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2138 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2139 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2143 cur_menu->removeAction(action);
2147 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2153 void MainWindow::addDynamicMenus()
2156 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2157 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2158 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2159 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2160 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2162 // Fill in each menu
2163 foreach (register_stat_group_t menu_group, menu_groups) {
2164 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2165 addMenuActions(actions, menu_group);
2168 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2169 // We've added a placeholder in order to make sure the "Tools" menu is
2170 // visible. Hide it as needed.
2171 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2172 main_ui_->actionToolsPlaceholder->setVisible(false);
2174 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2175 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2177 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2178 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2180 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2181 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2183 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2184 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2188 void MainWindow::reloadDynamicMenus()
2190 foreach (register_stat_group_t menu_group, menu_groups) {
2191 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2192 removeMenuActions(actions, menu_group);
2194 actions = wsApp->addedMenuGroupItems(menu_group);
2195 addMenuActions(actions, menu_group);
2198 wsApp->clearAddedMenuGroupItems();
2199 wsApp->clearRemovedMenuGroupItems();
2202 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2204 QAction * itemAction = NULL;
2205 ext_menubar_t * item = NULL;
2206 GList * children = NULL;
2208 /* There must exists an xpath parent */
2209 g_assert(subMenu != NULL);
2211 /* If the depth counter exceeds, something must have gone wrong */
2212 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2214 children = menu->children;
2215 /* Iterate the child entries */
2216 while ( children != NULL && children->data != NULL )
2218 item = (ext_menubar_t *) children->data;
2220 if ( item->type == EXT_MENUBAR_MENU )
2222 /* Handle Submenu entry */
2223 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++ );
2225 else if ( item->type == EXT_MENUBAR_SEPARATOR )
2227 subMenu->addSeparator();
2229 else if ( item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL )
2231 itemAction = subMenu->addAction(item->name);
2232 itemAction->setData(QVariant::fromValue((void *)item));
2233 itemAction->setText(item->label);
2234 connect(itemAction, SIGNAL(triggered()),
2235 this, SLOT(externalMenuItem_triggered()));
2239 children = g_list_next(children);
2243 QMenu * MainWindow::searchSubMenu(QString objectName)
2247 if ( objectName.length() > 0 )
2249 QString searchName = QString("menu") + objectName;
2251 lst = main_ui_->menuBar->findChildren<QMenu*>();
2252 foreach (QMenu* m, lst)
2254 if ( QString::compare( m->objectName(), searchName ) == 0 )
2262 void MainWindow::addExternalMenus()
2264 QMenu * subMenu = NULL;
2265 GList * user_menu = NULL;
2266 ext_menu_t * menu = NULL;
2268 user_menu = ext_menubar_get_entries();
2270 while ( ( user_menu != NULL ) && ( user_menu->data != NULL ) )
2272 menu = (ext_menu_t *) user_menu->data;
2274 /* On this level only menu items should exist. Not doing an assert here,
2275 * as it could be an honest mistake */
2276 if ( menu->type != EXT_MENUBAR_MENU )
2278 user_menu = g_list_next(user_menu);
2282 /* Create main submenu and add it to the menubar */
2283 if ( menu->parent_menu != NULL )
2285 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2286 if ( sortUnderneath != NULL)
2287 subMenu = sortUnderneath->addMenu(menu->label);
2290 if ( subMenu == NULL )
2291 subMenu = main_ui_->menuBar->addMenu(menu->label);
2293 /* This will generate the action structure for each menu. It is recursive,
2294 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2295 this->externalMenuHelper(menu, subMenu, 0);
2298 user_menu = g_list_next (user_menu);
2308 * indent-tabs-mode: nil
2311 * ex: set shiftwidth=4 tabstop=8 expandtab:
2312 * :indentSize=4:tabSize=8:noTabs=true: