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_)),
227 capture_stopping_(false),
228 capture_filter_valid_(false),
235 if (!gbl_cur_main_window_) {
236 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
237 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
238 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
239 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
241 gbl_cur_main_window_ = this;
243 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
245 main_ui_->setupUi(this);
246 setWindowIcon(wsApp->normalIcon());
247 setTitlebarForCaptureFile();
248 setMenusForCaptureFile();
249 setForCapturedPackets(false);
250 setMenusForFileSet(false);
251 interfaceSelectionChanged();
252 loadWindowGeometry();
255 main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
258 //To prevent users use features before initialization complete
259 //Otherwise unexpected problems may occur
260 setFeaturesEnabled(false);
261 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
262 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
263 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
264 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
265 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
266 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
268 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
269 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
270 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
271 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
272 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
274 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
278 connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
279 connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
282 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
283 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
284 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
285 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
286 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
287 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
288 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
289 connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
290 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
291 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
293 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
294 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
295 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
296 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
297 this, SLOT(openCaptureFile(QString,QString)));
298 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
300 initMainToolbarIcons();
302 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
303 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
304 // https://bugreports.qt-project.org/browse/QTBUG-22433
305 // This property is obsolete in Qt5 so this issue may be fixed in that version.
306 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
308 wireless_frame_ = new WirelessFrame(this);
309 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
310 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
311 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
312 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
313 this, SLOT(showPreferencesDialog(QString)));
315 main_ui_->goToFrame->hide();
316 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
317 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
319 // XXX For some reason the cursor is drawn funny with an input mask set
320 // https://bugreports.qt-project.org/browse/QTBUG-7174
322 main_ui_->searchFrame->hide();
323 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
324 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
325 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
326 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
328 main_ui_->addressEditorFrame->hide();
329 main_ui_->columnEditorFrame->hide();
330 main_ui_->preferenceEditorFrame->hide();
331 main_ui_->filterExpressionFrame->hide();
334 main_ui_->menuCapture->setEnabled(false);
337 #if defined(Q_OS_MAC)
338 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
339 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
340 ntb->setIconSize(QSize(24, 24));
341 #endif // QT_MACEXTRAS_LIB
343 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
344 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
345 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
347 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
351 #ifdef HAVE_SOFTWARE_UPDATE
352 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
353 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
354 main_ui_->menuHelp->insertAction(update_sep, update_action);
355 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
357 master_split_.setObjectName("splitterMaster");
358 extra_split_.setObjectName("splitterExtra");
359 main_ui_->mainStack->addWidget(&master_split_);
361 empty_pane_.setObjectName("emptyPane");
363 packet_list_ = new PacketList(&master_split_);
365 proto_tree_ = new ProtoTree(&master_split_);
366 proto_tree_->installEventFilter(this);
368 byte_view_tab_ = new ByteViewTab(&master_split_);
370 packet_list_->setProtoTree(proto_tree_);
371 packet_list_->setByteViewTab(byte_view_tab_);
372 packet_list_->installEventFilter(this);
374 main_welcome_ = main_ui_->welcomePage;
376 // Packet list and proto tree must exist before these are called.
377 setMenusForSelectedPacket();
378 setMenusForSelectedTreeRow();
380 initShowHideMainWidgets();
381 initTimeDisplayFormatMenu();
382 initTimePrecisionFormatMenu();
384 updatePreferenceActions();
385 setForCaptureInProgress(false);
387 setTabOrder(df_combo_box_, packet_list_);
389 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
390 this, SLOT(captureCapturePrepared(capture_session *)));
391 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
392 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
393 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
394 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
395 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
396 this, SLOT(captureCaptureFixedStarted(capture_session *)));
397 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
398 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
399 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
400 this, SLOT(captureCaptureFixedFinished(capture_session *)));
401 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
402 this, SLOT(captureCaptureStopping(capture_session *)));
403 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
404 this, SLOT(captureCaptureFailed(capture_session *)));
405 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
406 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
408 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
409 wsApp, SLOT(captureStarted()));
410 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
411 wsApp, SLOT(captureFinished()));
412 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
413 wsApp, SLOT(captureStarted()));
414 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
415 wsApp, SLOT(captureFinished()));
417 connect(&capture_file_, SIGNAL(captureFileOpened()),
418 this, SLOT(captureFileOpened()));
419 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
420 this, SLOT(captureFileReadStarted()));
421 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
422 this, SLOT(captureFileReadFinished()));
423 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
424 this, SLOT(captureFileReloadStarted()));
425 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
426 this, SLOT(captureFileReadFinished()));
427 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
428 this, SLOT(captureFileRescanStarted()));
429 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
430 this, SLOT(captureFileReadFinished()));
431 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
432 this, SLOT(captureFileRetapStarted()));
433 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
434 this, SLOT(captureFileRetapFinished()));
435 connect(&capture_file_, SIGNAL(captureFileClosing()),
436 this, SLOT(captureFileClosing()));
437 connect(&capture_file_, SIGNAL(captureFileClosed()),
438 this, SLOT(captureFileClosed()));
440 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
441 this, SLOT(captureFileSaveStarted(QString)));
442 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
443 main_ui_->statusBar, SLOT(popFileStatus()));
444 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
445 main_ui_->statusBar, SLOT(popFileStatus()));
446 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
447 main_ui_->statusBar, SLOT(popFileStatus()));
449 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
450 wsApp, SLOT(captureFileReadStarted()));
451 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
452 wsApp, SLOT(updateTaps()));
454 connect(wsApp, SIGNAL(recentFilesRead()),
455 packet_list_, SLOT(applyRecentColumnWidths()));
456 connect(wsApp, SIGNAL(columnsChanged()),
457 packet_list_, SLOT(columnsChanged()));
458 connect(wsApp, SIGNAL(preferencesChanged()),
459 packet_list_, SLOT(preferencesChanged()));
460 connect(wsApp, SIGNAL(recentFilesRead()),
461 this, SLOT(applyRecentPaneGeometry()));
462 connect(wsApp, SIGNAL(packetDissectionChanged()),
463 this, SLOT(redissectPackets()));
464 connect(wsApp, SIGNAL(appInitialized()),
465 this, SLOT(filterExpressionsChanged()));
466 connect(wsApp, SIGNAL(filterExpressionsChanged()),
467 this, SLOT(filterExpressionsChanged()));
468 connect(wsApp, SIGNAL(fieldsChanged()),
469 this, SLOT(fieldsChanged()));
471 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
472 this, SLOT(mainStackChanged(int)));
474 connect(main_welcome_, SIGNAL(startCapture()),
475 this, SLOT(startCapture()));
476 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
477 this, SLOT(openCaptureFile(QString)));
478 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
479 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
480 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
481 main_ui_->statusBar, SLOT(popFilterStatus()));
483 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
484 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
485 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
486 this, SLOT(redissectPackets()));
487 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
488 this, SLOT(showPreferencesDialog(QString)));
489 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
490 this, SLOT(showPreferencesDialog(QString)));
491 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
492 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
493 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
494 this, SLOT(filterExpressionsChanged()));
496 connect(this, SIGNAL(setCaptureFile(capture_file*)),
497 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
498 connect(this, SIGNAL(setCaptureFile(capture_file*)),
499 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
500 connect(this, SIGNAL(setCaptureFile(capture_file*)),
501 packet_list_, SLOT(setCaptureFile(capture_file*)));
502 connect(this, SIGNAL(setCaptureFile(capture_file*)),
503 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
505 connect(this, SIGNAL(monospaceFontChanged(QFont)),
506 packet_list_, SLOT(setMonospaceFont(QFont)));
507 connect(this, SIGNAL(monospaceFontChanged(QFont)),
508 proto_tree_, SLOT(setMonospaceFont(QFont)));
509 connect(this, SIGNAL(monospaceFontChanged(QFont)),
510 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
512 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
513 packet_list_, SLOT(goNextPacket()));
514 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
515 packet_list_, SLOT(goPreviousPacket()));
516 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
517 packet_list_, SLOT(goFirstPacket()));
518 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
519 packet_list_, SLOT(goLastPacket()));
521 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
522 proto_tree_, SLOT(expandSubtrees()));
523 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
524 proto_tree_, SLOT(expandAll()));
525 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
526 proto_tree_, SLOT(collapseAll()));
528 connect(packet_list_, SIGNAL(packetSelectionChanged()),
529 this, SLOT(setMenusForSelectedPacket()));
530 connect(packet_list_, SIGNAL(packetDissectionChanged()),
531 this, SLOT(redissectPackets()));
532 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
533 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
534 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
535 this, SLOT(showPreferencesDialog(QString)));
536 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
537 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
538 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
539 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
540 packet_list_, SLOT(columnsChanged()));
541 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
542 this, SLOT(openPacketDialog()));
543 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
544 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
545 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
546 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
547 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
548 main_ui_->statusBar, SLOT(popBusyStatus()));
549 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
550 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
551 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
552 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
553 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
554 main_ui_->statusBar, SLOT(popProgressStatus()));
556 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
557 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
558 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
559 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
560 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
561 this, SLOT(openPacketDialog(bool)));
562 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
563 this, SLOT(showPreferencesDialog(QString)));
564 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
565 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
567 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
568 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
570 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
571 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
573 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
574 &capture_file_, SLOT(stopLoading()));
576 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
577 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
579 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
580 this, SLOT(openCaptureFile(QString)));
583 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
585 connect(iface_tree, SIGNAL(itemSelectionChanged()),
586 this, SLOT(interfaceSelectionChanged()));
588 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
589 this, SLOT(captureFilterSyntaxChanged(bool)));
592 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
593 this, SLOT(showExtcapOptionsDialog(QString&)));
596 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
597 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
598 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
599 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
600 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
601 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
604 /* Create plugin_if hooks */
605 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter );
606 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter );
607 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
608 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
610 main_ui_->mainStack->setCurrentWidget(main_welcome_);
613 MainWindow::~MainWindow()
618 QString MainWindow::getFilter()
620 return df_combo_box_->itemText(df_combo_box_->count());
623 QMenu *MainWindow::createPopupMenu()
625 QMenu *menu = new QMenu();
626 menu->addAction(main_ui_->actionViewMainToolbar);
627 menu->addAction(main_ui_->actionViewFilterToolbar);
628 menu->addAction(main_ui_->actionViewWirelessToolbar);
629 menu->addAction(main_ui_->actionViewStatusBar);
630 menu->addSeparator();
631 menu->addAction(main_ui_->actionViewPacketList);
632 menu->addAction(main_ui_->actionViewPacketDetails);
633 menu->addAction(main_ui_->actionViewPacketBytes);
637 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
639 pipe_source_ = source;
640 pipe_child_process_ = child_process;
641 pipe_user_data_ = user_data;
642 pipe_input_cb_ = input_cb;
645 /* Tricky to use pipes in win9x, as no concept of wait. NT can
646 do this but that doesn't cover all win32 platforms. GTK can do
647 this but doesn't seem to work over processes. Attempt to do
648 something similar here, start a timer and check for data on every
650 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
653 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
657 pipe_timer_ = new QTimer(this);
658 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
659 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
660 pipe_timer_->start(200);
662 if (pipe_notifier_) {
663 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
664 delete pipe_notifier_;
667 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
668 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
669 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
670 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
674 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
676 // The user typed some text. Start filling in a filter.
677 // We may need to be more choosy here. We just need to catch events for the packet list,
678 // proto tree, and main welcome widgets.
679 if (event->type() == QEvent::KeyPress) {
680 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
681 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
682 df_combo_box_->lineEdit()->insert(kevt->text());
683 df_combo_box_->lineEdit()->setFocus();
688 return QMainWindow::eventFilter(obj, event);
691 void MainWindow::keyPressEvent(QKeyEvent *event) {
693 // Explicitly focus on the display filter combo.
694 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
695 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
699 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
700 if (event->modifiers() == Qt::NoModifier) {
701 if (event->key() == Qt::Key_Escape) {
702 on_goToCancel_clicked();
703 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
707 return; // goToLineEdit didn't want it and we don't either.
710 // Move up & down the packet list.
711 if (event->key() == Qt::Key_F7) {
712 packet_list_->goPreviousPacket();
713 } else if (event->key() == Qt::Key_F8) {
714 packet_list_->goNextPacket();
717 // Move along, citizen.
718 QMainWindow::keyPressEvent(event);
721 void MainWindow::closeEvent(QCloseEvent *event) {
722 saveWindowGeometry();
724 /* If we're in the middle of stopping a capture, don't do anything;
725 the user can try deleting the window after the capture stops. */
726 if (capture_stopping_) {
731 QString before_what(tr(" before quitting"));
732 if (!testCaptureFileClose(TRUE, before_what)) {
738 capture_interfaces_dialog_.close();
740 // Make sure we kill any open dumpcap processes.
741 delete main_welcome_;
743 // One of the many places we assume one main window.
744 if(!wsApp->isInitialized()) {
745 // If we're still initializing, QCoreApplication::quit() won't
746 // exit properly because we are not in the event loop. This
747 // means that the application won't clean up after itself. We
748 // might want to call wsApp->processEvents() during startup
749 // instead so that we can do a normal exit here.
755 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
758 foreach (QUrl drag_url, event->mimeData()->urls()) {
759 if (!drag_url.toLocalFile().isEmpty()) {
764 if (accept) event->acceptProposedAction();
767 void MainWindow::dropEvent(QDropEvent *event)
769 foreach (QUrl drop_url, event->mimeData()->urls()) {
770 QString local_file = drop_url.toLocalFile();
771 if (!local_file.isEmpty()) {
772 event->acceptProposedAction();
773 openCaptureFile(local_file);
779 // Apply recent settings to the main window geometry.
780 // We haven't loaded the preferences at this point so we assume that the
781 // position and size preference are enabled.
782 void MainWindow::loadWindowGeometry()
784 int min_sensible_dimension = 200;
787 if (recent.gui_geometry_main_maximized) {
788 setWindowState(Qt::WindowMaximized);
792 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
793 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
794 if (!rect_on_screen(recent_geom)) {
795 // We're not visible on any screens. Give up and use the default geometry.
799 // if (prefs.gui_geometry_save_position) {
800 move(recent_geom.topLeft());
803 if (// prefs.gui_geometry_save_size &&
804 recent_geom.width() > min_sensible_dimension &&
805 recent_geom.height() > min_sensible_dimension) {
806 resize(recent_geom.size());
811 void MainWindow::saveWindowGeometry()
813 if (prefs.gui_geometry_save_position) {
814 recent.gui_geometry_main_x = pos().x();
815 recent.gui_geometry_main_y = pos().y();
818 if (prefs.gui_geometry_save_size) {
819 recent.gui_geometry_main_width = size().width();
820 recent.gui_geometry_main_height = size().height();
823 if (prefs.gui_geometry_save_maximized) {
824 // On OS X this is false when it shouldn't be
825 recent.gui_geometry_main_maximized = isMaximized();
828 if (master_split_.sizes().length() > 0) {
829 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
832 if (master_split_.sizes().length() > 2) {
833 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
834 } else if (extra_split_.sizes().length() > 0) {
835 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
839 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
841 case layout_pane_content_none:
843 case layout_pane_content_plist:
845 case layout_pane_content_pdetails:
847 case layout_pane_content_pbytes:
848 return byte_view_tab_;
850 g_assert_not_reached();
855 // Our event loop becomes nested whenever we call update_progress_dlg, which
856 // includes several places in file.c. The GTK+ UI stays out of trouble by
857 // showing a modal progress dialog. We attempt to do the equivalent below by
858 // disabling parts of the main window. At a minumum the ProgressFrame in the
859 // main status bar must remain accessible.
861 // We might want to do this any time the main status bar progress frame is
863 void MainWindow::freeze()
865 freeze_focus_ = wsApp->focusWidget();
867 // XXX Alternatively we could just disable and enable the main menu.
868 for (int i = 0; i < freeze_actions_.size(); i++) {
869 QAction *action = freeze_actions_[i].first;
870 freeze_actions_[i].second = action->isEnabled();
871 action->setEnabled(false);
873 main_ui_->centralWidget->setEnabled(false);
876 void MainWindow::thaw()
878 main_ui_->centralWidget->setEnabled(true);
879 for (int i = 0; i < freeze_actions_.size(); i++) {
880 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
883 if (freeze_focus_) freeze_focus_->setFocus();
884 freeze_focus_ = NULL;
887 void MainWindow::mergeCaptureFile()
889 QString file_name = "";
890 QString read_filter = "";
891 dfilter_t *rfcode = NULL;
894 if (!capture_file_.capFile())
897 if (prefs.gui_ask_unsaved) {
898 if (cf_has_unsaved_data(capture_file_.capFile())) {
899 QMessageBox msg_dialog;
900 gchar *display_basename;
903 msg_dialog.setIcon(QMessageBox::Question);
904 /* This file has unsaved data; ask the user whether to save
906 if (capture_file_.capFile()->is_tempfile) {
907 msg_dialog.setText(tr("Save packets before merging?"));
908 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
911 * Format the message.
913 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
914 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
915 g_free(display_basename);
916 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
919 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
920 msg_dialog.setDefaultButton(QMessageBox::Save);
922 response = msg_dialog.exec();
926 case QMessageBox::Save:
927 /* Save the file but don't close it */
928 saveCaptureFile(capture_file_.capFile(), FALSE);
931 case QMessageBox::Cancel:
933 /* Don't do the merge. */
940 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
942 cf_status_t merge_status;
943 char *in_filenames[2];
946 switch (prefs.gui_fileopen_style) {
948 case FO_STYLE_LAST_OPENED:
949 /* The user has specified that we should start out in the last directory
950 we looked in. If we've already opened a file, use its containing
951 directory, if we could determine it, as the directory, otherwise
952 use the "last opened" directory saved in the preferences file if
954 /* This is now the default behaviour in file_selection_new() */
957 case FO_STYLE_SPECIFIED:
958 /* The user has specified that we should always start out in a
959 specified directory; if they've specified that directory,
960 start out by showing the files in that dir. */
961 if (prefs.gui_fileopen_dir[0] != '\0')
962 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
966 if (merge_dlg.merge(file_name)) {
969 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
970 /* Not valid. Tell the user, and go back and run the file
971 selection box again once they dismiss the alert. */
972 //bad_dfilter_alert_box(top_level, read_filter->str);
973 QMessageBox::warning(this, tr("Invalid Read Filter"),
974 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
983 file_type = capture_file_.capFile()->cd_t;
985 /* Try to merge or append the two files */
987 if (merge_dlg.mergeType() == 0) {
988 /* chronological order */
989 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
990 in_filenames[1] = qstring_strdup(file_name);
991 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
992 } else if (merge_dlg.mergeType() <= 0) {
994 in_filenames[0] = qstring_strdup(file_name);
995 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
996 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
999 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1000 in_filenames[1] = qstring_strdup(file_name);
1001 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1004 g_free(in_filenames[0]);
1005 g_free(in_filenames[1]);
1007 if (merge_status != CF_OK) {
1009 dfilter_free(rfcode);
1014 cf_close(capture_file_.capFile());
1016 /* Try to open the merged capture file. */
1017 CaptureFile::globalCapFile()->window = this;
1018 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1019 /* We couldn't open it; fail. */
1020 CaptureFile::globalCapFile()->window = NULL;
1022 dfilter_free(rfcode);
1027 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1028 it closed the previous capture file, and thus destroyed any
1029 previous read filter attached to "cf"). */
1030 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1032 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1036 /* Just because we got an error, that doesn't mean we were unable
1037 to read any of the file; we handle what we could get from the
1041 case CF_READ_ABORTED:
1042 /* The user bailed out of re-reading the capture file; the
1043 capture file has been closed - just free the capture file name
1044 string and return (without changing the last containing
1050 /* Save the name of the containing directory specified in the path name,
1051 if any; we can write over cf_merged_name, which is a good thing, given that
1052 "get_dirname()" does write over its argument. */
1053 wsApp->setLastOpenDir(get_dirname(tmpname));
1055 main_ui_->statusBar->showExpert();
1061 void MainWindow::importCaptureFile() {
1062 ImportTextDialog import_dlg;
1064 QString before_what(tr(" before importing a new capture"));
1065 if (!testCaptureFileClose(FALSE, before_what))
1070 if (import_dlg.result() != QDialog::Accepted) {
1071 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1075 openCaptureFile(import_dlg.capfileName());
1078 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1080 gboolean discard_comments;
1082 if (cf->is_tempfile) {
1083 /* This is a temporary capture file, so saving it means saving
1084 it to a permanent file. Prompt the user for a location
1085 to which to save it. Don't require that the file format
1086 support comments - if it's a temporary capture file, it's
1087 probably pcap-ng, which supports comments and, if it's
1088 not pcap-ng, let the user decide what they want to do
1089 if they've added comments. */
1090 saveAsCaptureFile(cf, FALSE, dont_reopen);
1092 if (cf->unsaved_changes) {
1093 cf_write_status_t status;
1095 /* This is not a temporary capture file, but it has unsaved
1096 changes, so saving it means doing a "safe save" on top
1097 of the existing file, in the same format - no UI needed
1098 unless the file has comments and the file's format doesn't
1101 If the file has comments, does the file's format support them?
1102 If not, ask the user whether they want to discard the comments
1103 or choose a different format. */
1104 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1107 /* The file can be saved in the specified format as is;
1108 just drive on and save in the format they selected. */
1109 discard_comments = FALSE;
1112 case SAVE_WITHOUT_COMMENTS:
1113 /* The file can't be saved in the specified format as is,
1114 but it can be saved without the comments, and the user
1115 said "OK, discard the comments", so save it in the
1116 format they specified without the comments. */
1117 discard_comments = TRUE;
1120 case SAVE_IN_ANOTHER_FORMAT:
1121 /* There are file formats in which we can save this that
1122 support comments, and the user said not to delete the
1123 comments. Do a "Save As" so the user can select
1124 one of those formats and choose a file name. */
1125 saveAsCaptureFile(cf, TRUE, dont_reopen);
1129 /* The user said "forget it". Just return. */
1133 /* Squelch warnings that discard_comments is being used
1135 g_assert_not_reached();
1139 /* XXX - cf->filename might get freed out from under us, because
1140 the code path through which cf_save_records() goes currently
1141 closes the current file and then opens and reloads the saved file,
1142 so make a copy and free it later. */
1143 file_name = cf->filename;
1144 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1145 discard_comments, dont_reopen);
1149 /* The save succeeded; we're done.
1150 If we discarded comments, redraw the packet list to reflect
1151 any packets that no longer have comments. */
1152 if (discard_comments)
1153 packet_list_queue_draw();
1155 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1156 updateForUnsavedChanges(); // we update the title bar to remove the *
1159 case CF_WRITE_ERROR:
1160 /* The write failed.
1161 XXX - OK, what do we do now? Let them try a
1162 "Save As", in case they want to try to save to a
1163 different directory r file system? */
1166 case CF_WRITE_ABORTED:
1167 /* The write was aborted; just drive on. */
1171 /* Otherwise just do nothing. */
1175 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1176 QString file_name = "";
1178 gboolean compressed;
1179 cf_write_status_t status;
1181 gboolean discard_comments = FALSE;
1188 CaptureFileDialog save_as_dlg(this, cf);
1190 switch (prefs.gui_fileopen_style) {
1192 case FO_STYLE_LAST_OPENED:
1193 /* The user has specified that we should start out in the last directory
1194 we looked in. If we've already opened a file, use its containing
1195 directory, if we could determine it, as the directory, otherwise
1196 use the "last opened" directory saved in the preferences file if
1198 /* This is now the default behaviour in file_selection_new() */
1201 case FO_STYLE_SPECIFIED:
1202 /* The user has specified that we should always start out in a
1203 specified directory; if they've specified that directory,
1204 start out by showing the files in that dir. */
1205 if (prefs.gui_fileopen_dir[0] != '\0')
1206 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1210 /* If the file has comments, does the format the user selected
1211 support them? If not, ask the user whether they want to
1212 discard the comments or choose a different format. */
1213 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1216 /* The file can be saved in the specified format as is;
1217 just drive on and save in the format they selected. */
1218 discard_comments = FALSE;
1221 case SAVE_WITHOUT_COMMENTS:
1222 /* The file can't be saved in the specified format as is,
1223 but it can be saved without the comments, and the user
1224 said "OK, discard the comments", so save it in the
1225 format they specified without the comments. */
1226 discard_comments = TRUE;
1229 case SAVE_IN_ANOTHER_FORMAT:
1230 /* There are file formats in which we can save this that
1231 support comments, and the user said not to delete the
1232 comments. The combo box of file formats has had the
1233 formats that don't support comments trimmed from it,
1234 so run the dialog again, to let the user decide
1235 whether to save in one of those formats or give up. */
1236 must_support_comments = TRUE;
1240 /* The user said "forget it". Just get rid of the dialog box
1244 file_type = save_as_dlg.selectedFileType();
1245 compressed = save_as_dlg.isCompressed();
1247 fileAddExtension(file_name, file_type, compressed);
1250 // /* If the file exists and it's user-immutable or not writable,
1251 // ask the user whether they want to override that. */
1252 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1253 // /* They don't. Let them try another file name or cancel. */
1258 /* Attempt to save the file */
1259 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1260 discard_comments, dont_reopen);
1264 /* The save succeeded; we're done. */
1265 /* Save the directory name for future file dialogs. */
1266 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1267 set_last_open_dir(dirname);
1269 /* If we discarded comments, redraw the packet list to reflect
1270 any packets that no longer have comments. */
1271 if (discard_comments)
1272 packet_list_queue_draw();
1274 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1275 updateForUnsavedChanges(); // we update the title bar to remove the *
1278 case CF_WRITE_ERROR:
1279 /* The save failed; let the user try again. */
1282 case CF_WRITE_ABORTED:
1283 /* The user aborted the save; just return. */
1290 void MainWindow::exportSelectedPackets() {
1291 QString file_name = "";
1293 gboolean compressed;
1294 packet_range_t range;
1295 cf_write_status_t status;
1297 gboolean discard_comments = FALSE;
1299 if (!capture_file_.capFile())
1302 /* Init the packet range */
1303 packet_range_init(&range, capture_file_.capFile());
1304 range.process_filtered = TRUE;
1305 range.include_dependents = TRUE;
1308 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1310 switch (prefs.gui_fileopen_style) {
1312 case FO_STYLE_LAST_OPENED:
1313 /* The user has specified that we should start out in the last directory
1314 we looked in. If we've already opened a file, use its containing
1315 directory, if we could determine it, as the directory, otherwise
1316 use the "last opened" directory saved in the preferences file if
1318 /* This is now the default behaviour in file_selection_new() */
1321 case FO_STYLE_SPECIFIED:
1322 /* The user has specified that we should always start out in a
1323 specified directory; if they've specified that directory,
1324 start out by showing the files in that dir. */
1325 if (prefs.gui_fileopen_dir[0] != '\0')
1326 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1330 /* If the file has comments, does the format the user selected
1331 support them? If not, ask the user whether they want to
1332 discard the comments or choose a different format. */
1333 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1336 /* The file can be saved in the specified format as is;
1337 just drive on and save in the format they selected. */
1338 discard_comments = FALSE;
1341 case SAVE_WITHOUT_COMMENTS:
1342 /* The file can't be saved in the specified format as is,
1343 but it can be saved without the comments, and the user
1344 said "OK, discard the comments", so save it in the
1345 format they specified without the comments. */
1346 discard_comments = TRUE;
1349 case SAVE_IN_ANOTHER_FORMAT:
1350 /* There are file formats in which we can save this that
1351 support comments, and the user said not to delete the
1352 comments. The combo box of file formats has had the
1353 formats that don't support comments trimmed from it,
1354 so run the dialog again, to let the user decide
1355 whether to save in one of those formats or give up. */
1359 /* The user said "forget it". Just get rid of the dialog box
1365 * Check that we're not going to save on top of the current
1367 * We do it here so we catch all cases ...
1368 * Unfortunately, the file requester gives us an absolute file
1369 * name and the read file name may be relative (if supplied on
1370 * the command line). From Joerg Mayer.
1372 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1373 QMessageBox msg_box;
1374 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1376 msg_box.setIcon(QMessageBox::Critical);
1377 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1378 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1379 msg_box.setStandardButtons(QMessageBox::Ok);
1380 msg_box.setDefaultButton(QMessageBox::Ok);
1382 g_free(display_basename);
1386 file_type = esp_dlg.selectedFileType();
1387 compressed = esp_dlg.isCompressed();
1388 fileAddExtension(file_name, file_type, compressed);
1391 // /* If the file exists and it's user-immutable or not writable,
1392 // ask the user whether they want to override that. */
1393 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1394 // /* They don't. Let them try another file name or cancel. */
1399 /* Attempt to save the file */
1400 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1404 /* The save succeeded; we're done. */
1405 /* Save the directory name for future file dialogs. */
1406 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1407 set_last_open_dir(dirname);
1409 /* If we discarded comments, redraw the packet list to reflect
1410 any packets that no longer have comments. */
1411 if (discard_comments)
1412 packet_list_queue_draw();
1415 case CF_WRITE_ERROR:
1416 /* The save failed; let the user try again. */
1419 case CF_WRITE_ABORTED:
1420 /* The user aborted the save; just return. */
1427 void MainWindow::exportDissections(export_type_e export_type) {
1428 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1429 packet_range_t range;
1431 if (!capture_file_.capFile())
1434 /* Init the packet range */
1435 packet_range_init(&range, capture_file_.capFile());
1436 range.process_filtered = TRUE;
1437 range.include_dependents = TRUE;
1442 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1443 QString file_name_lower;
1444 QString file_suffix;
1445 GSList *extensions_list;
1446 gboolean add_extension;
1449 * Append the default file extension if there's none given by
1450 * the user or if they gave one that's not one of the valid
1451 * extensions for the file type.
1453 file_name_lower = file_name.toLower();
1454 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1455 if (extensions_list != NULL) {
1458 /* We have one or more extensions for this file type.
1459 Start out assuming we need to add the default one. */
1460 add_extension = TRUE;
1462 /* OK, see if the file has one of those extensions. */
1463 for (extension = extensions_list; extension != NULL;
1464 extension = g_slist_next(extension)) {
1465 file_suffix += tr(".") + (char *)extension->data;
1466 if (file_name_lower.endsWith(file_suffix)) {
1468 * The file name has one of the extensions for
1471 add_extension = FALSE;
1474 file_suffix += ".gz";
1475 if (file_name_lower.endsWith(file_suffix)) {
1477 * The file name has one of the extensions for
1480 add_extension = FALSE;
1485 /* We have no extensions for this file type. Don't add one. */
1486 add_extension = FALSE;
1488 if (add_extension) {
1489 if (wtap_default_file_extension(file_type) != NULL) {
1490 file_name += tr(".") + wtap_default_file_extension(file_type);
1498 bool MainWindow::testCaptureFileClose(bool from_quit, QString before_what) {
1499 bool capture_in_progress = FALSE;
1501 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1502 return true; /* Already closed, nothing to do */
1505 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS && capture_file_.capFile()->count>0) {
1506 /* This is true if we're reading a capture file *or* if we're doing
1507 a live capture. If we're reading a capture file, the main loop
1508 is busy reading packets, and only accepting input from the
1509 progress dialog, so we can't get here, so this means we're
1511 capture_in_progress = TRUE;
1515 if (prefs.gui_ask_unsaved) {
1516 if (cf_has_unsaved_data(capture_file_.capFile()) || capture_in_progress) {
1517 QMessageBox msg_dialog;
1519 QPushButton *saveButton;
1520 QPushButton *discardButton;
1522 msg_dialog.setIcon(QMessageBox::Question);
1523 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1524 /* This file has unsaved data or there's a capture in
1525 progress; ask the user whether to save the data. */
1526 if (capture_file_.capFile()->is_tempfile) {
1528 msg_dialog.setText(tr("You have unsaved packets"));
1529 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1531 if (capture_in_progress) {
1532 question.append(tr("Do you want to stop the capture and save the captured packets"));
1534 question.append(tr("Do you want to save the captured packets"));
1536 question.append(before_what).append(tr("?"));
1537 msg_dialog.setInformativeText(question);
1542 * Format the message.
1544 if (capture_in_progress) {
1545 question.append(tr("Do you want to stop the capture and save the captured packets"));
1546 question.append(before_what).append(tr("?"));
1547 msg_dialog.setText(question);
1548 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1550 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1551 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1552 .arg(display_basename)
1555 g_free(display_basename);
1556 msg_dialog.setText(question);
1557 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1561 // XXX Text comes from ui/gtk/stock_icons.[ch]
1562 // Note that the button roles differ from the GTK+ version.
1563 // Cancel = RejectRole
1564 // Save = AcceptRole
1565 // Don't Save = DestructiveRole
1566 msg_dialog.addButton(QMessageBox::Cancel);
1568 if (capture_in_progress) {
1569 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1571 saveButton = msg_dialog.addButton(QMessageBox::Save);
1573 msg_dialog.setDefaultButton(saveButton);
1576 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1577 discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1578 QMessageBox::DestructiveRole);
1580 discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1581 QMessageBox::DestructiveRole);
1584 if (capture_in_progress) {
1585 discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1586 QMessageBox::DestructiveRole);
1588 discardButton = msg_dialog.addButton(tr("Continue &without Saving"), QMessageBox::DestructiveRole);
1593 /* According to the Qt doc:
1594 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1596 * Therefore we should use clickedButton() to determine which button was clicked. */
1598 if(msg_dialog.clickedButton() == saveButton)
1601 /* If there's a capture in progress, we have to stop the capture
1602 and then do the save. */
1603 if (capture_in_progress)
1606 /* Save the file and close it */
1607 saveCaptureFile(capture_file_.capFile(), TRUE);
1609 else if(msg_dialog.clickedButton() == discardButton)
1613 * If there's a capture in progress; we have to stop the capture
1614 * and then do the close.
1616 if (capture_in_progress)
1619 /* Just close the file, discarding changes */
1620 cf_close(capture_file_.capFile());
1623 else //cancelButton or some other unspecified button
1629 /* Unchanged file, just close it */
1630 capture_file_.capFile()->state = FILE_READ_ABORTED;
1631 cf_close(capture_file_.capFile());
1634 /* User asked not to be bothered by those prompts, just close it.
1635 XXX - should that apply only to saving temporary files? */
1637 /* If there's a capture in progress, we have to stop the capture
1638 and then do the close. */
1639 if (capture_in_progress)
1642 cf_close(capture_file_.capFile());
1645 return true; /* File closed */
1648 void MainWindow::captureStop() {
1651 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1652 WiresharkApplication::processEvents();
1656 void MainWindow::initMainToolbarIcons()
1658 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1659 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1660 #if !defined(Q_OS_WIN)
1661 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1662 // The OS X HIG specifies 32-pixel icons but they're a little too
1664 icon_size = icon_size * 3 / 2;
1666 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1668 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1669 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1671 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1672 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1673 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1674 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1676 // Menu icons are disabled in main_window.ui for these items.
1677 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1678 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1679 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1680 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1682 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1683 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1684 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1685 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1686 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1687 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1688 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1689 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1690 #if defined(Q_OS_MAC)
1691 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1692 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1694 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1696 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1697 main_ui_->actionViewColorizePacketList->setChecked(recent.packet_list_colorize);
1698 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1700 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1701 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1702 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1703 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1704 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1705 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1706 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1709 void MainWindow::initShowHideMainWidgets()
1711 if (show_hide_actions_) {
1715 show_hide_actions_ = new QActionGroup(this);
1716 QMap<QAction *, QWidget *> shmw_actions;
1718 show_hide_actions_->setExclusive(false);
1719 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1720 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1721 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1722 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1723 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1724 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1725 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1727 main_ui_->actionViewMainToolbar->setChecked(recent.main_toolbar_show);
1728 main_ui_->actionViewFilterToolbar->setChecked(recent.filter_toolbar_show);
1729 main_ui_->actionViewWirelessToolbar->setChecked(recent.wireless_toolbar_show);
1730 main_ui_->actionViewStatusBar->setChecked(recent.statusbar_show);
1731 main_ui_->actionViewPacketList->setChecked(recent.packet_list_show);
1732 main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show);
1733 main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show);
1735 foreach (QAction *shmwa, shmw_actions.keys()) {
1736 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1737 show_hide_actions_->addAction(shmwa);
1738 showHideMainWidgets(shmwa);
1741 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1744 Q_DECLARE_METATYPE(ts_type)
1746 void MainWindow::initTimeDisplayFormatMenu()
1748 if (time_display_actions_) {
1752 time_display_actions_ = new QActionGroup(this);
1753 QMap<QAction *, ts_type> td_actions;
1755 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1756 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1757 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1758 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1759 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1760 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1761 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1762 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1763 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1764 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1766 foreach (QAction* tda, td_actions.keys()) {
1767 tda->setData(qVariantFromValue(td_actions[tda]));
1768 time_display_actions_->addAction(tda);
1769 if (recent.gui_time_format == td_actions[tda]) {
1770 tda->setChecked(true);
1774 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1776 main_ui_->actionViewTimeDisplaySecondsWithHoursAndMinutes->setChecked(recent.gui_seconds_format == TS_SECONDS_HOUR_MIN_SEC);
1779 Q_DECLARE_METATYPE(ts_precision)
1781 void MainWindow::initTimePrecisionFormatMenu()
1783 if (time_precision_actions_) {
1787 time_precision_actions_ = new QActionGroup(this);
1788 QMap<QAction *, ts_precision> tp_actions;
1789 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1790 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1791 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1792 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1793 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1794 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1795 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1797 foreach (QAction* tpa, tp_actions.keys()) {
1798 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1799 time_precision_actions_->addAction(tpa);
1800 if (recent.gui_time_precision == tp_actions[tpa]) {
1801 tpa->setChecked(true);
1805 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1808 // Menu items which will be disabled when we freeze() and whose state will
1809 // be restored when we thaw(). Add to the list as needed.
1810 void MainWindow::initFreezeActions()
1812 QList<QAction *> freeze_actions = QList<QAction *>()
1813 << main_ui_->actionFileClose
1814 << main_ui_->actionViewReload
1815 << main_ui_->actionEditMarkPacket
1816 << main_ui_->actionEditMarkAllDisplayed
1817 << main_ui_->actionEditUnmarkAllDisplayed
1818 << main_ui_->actionEditIgnorePacket
1819 << main_ui_->actionEditIgnoreAllDisplayed
1820 << main_ui_->actionEditUnignoreAllDisplayed
1821 << main_ui_->actionEditSetTimeReference
1822 << main_ui_->actionEditUnsetAllTimeReferences;
1824 foreach (QAction *action, freeze_actions) {
1825 freeze_actions_ << QPair<QAction *, bool>(action, false);
1830 void MainWindow::setTitlebarForCaptureFile()
1832 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1834 // Qt *REALLY* doesn't like windows that sometimes have a
1835 // title set with setWindowTitle() and other times have a
1836 // file path set; apparently, once you've set the title
1837 // with setWindowTitle(), it sticks, and setWindowFilePath()
1838 // has no effect. It appears to can clear the title with
1839 // setWindowTitle(NULL), but that clears the actual title in
1840 // the title bar, and setWindowFilePath() then, I guess, sees
1841 // that there's already a file path, and does nothing, leaving
1842 // the title bar empty. So you then have to clear the file path
1843 // with setWindowFilePath(NULL), and then set it.
1845 // Maybe there's a #include "you're holding it wrong" here.
1846 // However, I really don't want to hear from people who think
1847 // that a window can never be associated with something other
1848 // than a user file at time T1 and with a user file at time T2,
1849 // given that, in Wireshark, a window can be associated with a
1850 // live capture at time T1 and then, after you've saved the live
1851 // capture to a user file, associated with a user file at time T2.
1853 if (capture_file_.capFile()->is_tempfile) {
1855 // For a temporary file, put the source of the data
1856 // in the window title, not whatever random pile
1857 // of characters is the last component of the path
1860 // XXX - on non-Mac platforms, put in the application
1863 // XXX - Use setWindowModified
1864 setWindowFilePath(NULL);
1865 setWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1868 // For a user file, set the full path; that way,
1869 // for OS X, it'll set the "proxy icon". Qt
1870 // handles extracting the last component.
1872 // Sadly, some UN*Xes don't necessarily use UTF-8
1873 // for their file names, so we have to map the
1874 // file path to UTF-8. If that fails, we're somewhat
1877 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1882 if (utf8_filename == NULL) {
1883 // So what the heck else can we do here?
1884 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1886 setWindowTitle(NULL);
1887 setWindowFilePath(NULL);
1888 setWindowFilePath(utf8_filename);
1889 g_free(utf8_filename);
1892 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1894 /* We have no capture file. */
1895 setWindowFilePath(NULL);
1896 setDefaultWindowTitle();
1900 void MainWindow::setDefaultWindowTitle()
1902 setWindowTitle(tr("The Wireshark Network Analyzer"));
1905 void MainWindow::setTitlebarForCaptureInProgress()
1907 setWindowFilePath(NULL);
1908 if (capture_file_.capFile()) {
1909 setWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1911 /* We have no capture in progress. */
1912 setDefaultWindowTitle();
1918 /* Enable or disable menu items based on whether you have a capture file
1919 you've finished reading and, if you have one, whether it's been saved
1920 and whether it could be saved except by copying the raw packet data. */
1921 void MainWindow::setMenusForCaptureFile(bool force_disable)
1923 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1924 /* We have no capture file or we're currently reading a file */
1925 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(false);
1926 main_ui_->actionFileMerge->setEnabled(false);
1927 main_ui_->actionFileClose->setEnabled(false);
1928 main_ui_->actionFileSave->setEnabled(false);
1929 main_ui_->actionFileSaveAs->setEnabled(false);
1930 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1931 main_ui_->actionFileExportPackets->setEnabled(false);
1932 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1933 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1934 main_ui_->actionFileExportPDU->setEnabled(false);
1935 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1936 main_ui_->menuFileExportObjects->setEnabled(false);
1937 main_ui_->actionViewReload->setEnabled(false);
1939 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(true);
1940 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1942 main_ui_->actionFileClose->setEnabled(true);
1943 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1944 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1945 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1947 * "Export Specified Packets..." should be available only if
1948 * we can write the file out in at least one format.
1950 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1951 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1952 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1953 main_ui_->actionFileExportPDU->setEnabled(true);
1954 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1955 main_ui_->menuFileExportObjects->setEnabled(true);
1956 main_ui_->actionViewReload->setEnabled(true);
1960 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1961 /* Either a capture was started or stopped; in either case, it's not
1962 in the process of stopping, so allow quitting. */
1964 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1965 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1966 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1967 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1968 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1969 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1970 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1971 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1972 main_ui_->actionFileQuit->setEnabled(true);
1974 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1976 // XXX Fix packet list heading menu sensitivity
1977 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1978 // !capture_in_progress);
1979 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1980 // !capture_in_progress);
1981 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1982 // !capture_in_progress);
1985 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1986 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1987 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1988 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1989 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1990 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1991 #endif /* HAVE_LIBPCAP */
1995 void MainWindow::setMenusForCaptureStopping() {
1996 main_ui_->actionFileQuit->setEnabled(false);
1997 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1999 main_ui_->actionCaptureStart->setChecked(false);
2000 main_ui_->actionCaptureStop->setEnabled(false);
2001 main_ui_->actionCaptureRestart->setEnabled(false);
2002 #endif /* HAVE_LIBPCAP */
2005 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2007 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2009 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2010 // have_captured_packets);
2012 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2013 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2014 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2016 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2017 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2018 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2019 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2020 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2021 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2022 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2024 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2025 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2026 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2027 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2029 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2030 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2031 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2034 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2035 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2036 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2038 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2039 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2040 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2043 void MainWindow::setWindowIcon(const QIcon &icon) {
2044 wsApp->setWindowIcon(icon);
2045 QMainWindow::setWindowIcon(icon);
2048 void MainWindow::updateForUnsavedChanges() {
2049 setTitlebarForCaptureFile();
2050 setMenusForCaptureFile();
2053 void MainWindow::changeEvent(QEvent* event)
2057 switch (event->type())
2059 case QEvent::LanguageChange:
2060 main_ui_->retranslateUi(this);
2061 // make sure that the "Clear Menu" item is retranslated
2062 updateRecentFiles();
2064 case QEvent::LocaleChange:{
2065 QString locale = QLocale::system().name();
2066 locale.truncate(locale.lastIndexOf('_'));
2067 wsApp->loadLanguage(locale);
2074 QMainWindow::changeEvent(event);
2077 /* Update main window items based on whether there's a capture in progress. */
2078 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2080 setMenusForCaptureInProgress(capture_in_progress);
2082 wireless_frame_->setCaptureInProgress(capture_in_progress);
2085 packet_list_->setCaptureInProgress(capture_in_progress);
2086 // set_toolbar_for_capture_in_progress(capture_in_progress);
2088 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2092 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2093 << REGISTER_ANALYZE_GROUP_UNSORTED
2094 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2095 << REGISTER_STAT_GROUP_UNSORTED
2096 << REGISTER_STAT_GROUP_GENERIC
2097 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2098 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2099 << REGISTER_STAT_GROUP_RESPONSE_TIME
2100 << REGISTER_STAT_GROUP_TELEPHONY
2101 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2102 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2103 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2104 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2105 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2106 << REGISTER_TOOLS_GROUP_UNSORTED;
2108 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2110 foreach (QAction *action, actions) {
2111 switch (menu_group) {
2112 case REGISTER_ANALYZE_GROUP_UNSORTED:
2113 case REGISTER_STAT_GROUP_UNSORTED:
2114 main_ui_->menuStatistics->insertAction(
2115 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2118 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2119 main_ui_->menuServiceResponseTime->addAction(action);
2121 case REGISTER_STAT_GROUP_TELEPHONY:
2122 main_ui_->menuTelephony->addAction(action);
2124 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2125 main_ui_->menuANSI->addAction(action);
2127 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2128 main_ui_->menuGSM->addAction(action);
2130 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2131 main_ui_->menuLTE->addAction(action);
2133 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2134 main_ui_->menuMTP3->addAction(action);
2136 case REGISTER_TOOLS_GROUP_UNSORTED:
2138 // Allow the creation of submenus. Mimics the behavor of
2139 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2140 // and GtkUIManager.
2142 // For now we limit the insanity to the "Tools" menu.
2143 QStringList menu_path = action->text().split('/');
2144 QMenu *cur_menu = main_ui_->menuTools;
2145 while (menu_path.length() > 1) {
2146 QString menu_title = menu_path.takeFirst();
2147 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2148 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2150 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2151 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2154 submenu = cur_menu->addMenu(menu_title);
2155 submenu->setObjectName(menu_title.toLower());
2159 action->setText(menu_path.last());
2160 cur_menu->addAction(action);
2164 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2168 // Connect each action type to its corresponding slot. We to
2169 // distinguish various types of actions. Setting their objectName
2170 // seems to work OK.
2171 if (action->objectName() == TapParameterDialog::actionName()) {
2172 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2173 } else if (action->objectName() == FunnelStatistics::actionName()) {
2174 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2178 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2180 foreach (QAction *action, actions) {
2181 switch (menu_group) {
2182 case REGISTER_ANALYZE_GROUP_UNSORTED:
2183 case REGISTER_STAT_GROUP_UNSORTED:
2184 main_ui_->menuStatistics->removeAction(action);
2186 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2187 main_ui_->menuServiceResponseTime->removeAction(action);
2189 case REGISTER_STAT_GROUP_TELEPHONY:
2190 main_ui_->menuTelephony->removeAction(action);
2192 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2193 main_ui_->menuANSI->removeAction(action);
2195 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2196 main_ui_->menuGSM->removeAction(action);
2198 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2199 main_ui_->menuLTE->removeAction(action);
2201 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2202 main_ui_->menuMTP3->removeAction(action);
2204 case REGISTER_TOOLS_GROUP_UNSORTED:
2206 // Allow removal of submenus.
2207 // For now we limit the insanity to the "Tools" menu.
2208 QStringList menu_path = action->text().split('/');
2209 QMenu *cur_menu = main_ui_->menuTools;
2210 while (menu_path.length() > 1) {
2211 QString menu_title = menu_path.takeFirst();
2212 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2213 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2215 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2216 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2220 cur_menu->removeAction(action);
2224 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2230 void MainWindow::addDynamicMenus()
2233 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2234 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2235 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2236 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2237 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2238 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2240 // Fill in each menu
2241 foreach (register_stat_group_t menu_group, menu_groups) {
2242 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2243 addMenuActions(actions, menu_group);
2246 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2247 // We've added a placeholder in order to make sure the "Tools" menu is
2248 // visible. Hide it as needed.
2249 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2250 main_ui_->actionToolsPlaceholder->setVisible(false);
2252 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2253 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2255 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2256 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2258 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2259 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2261 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2262 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2266 void MainWindow::reloadDynamicMenus()
2268 foreach (register_stat_group_t menu_group, menu_groups) {
2269 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2270 removeMenuActions(actions, menu_group);
2272 actions = wsApp->addedMenuGroupItems(menu_group);
2273 addMenuActions(actions, menu_group);
2276 wsApp->clearAddedMenuGroupItems();
2277 wsApp->clearRemovedMenuGroupItems();
2280 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2282 QAction * itemAction = NULL;
2283 ext_menubar_t * item = NULL;
2284 GList * children = NULL;
2286 /* There must exists an xpath parent */
2287 g_assert(subMenu != NULL);
2289 /* If the depth counter exceeds, something must have gone wrong */
2290 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2292 children = menu->children;
2293 /* Iterate the child entries */
2294 while ( children != NULL && children->data != NULL )
2296 item = (ext_menubar_t *) children->data;
2298 if ( item->type == EXT_MENUBAR_MENU )
2300 /* Handle Submenu entry */
2301 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++ );
2303 else if ( item->type == EXT_MENUBAR_SEPARATOR )
2305 subMenu->addSeparator();
2307 else if ( item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL )
2309 itemAction = subMenu->addAction(item->name);
2310 itemAction->setData(QVariant::fromValue((void *)item));
2311 itemAction->setText(item->label);
2312 connect(itemAction, SIGNAL(triggered()),
2313 this, SLOT(externalMenuItem_triggered()));
2317 children = g_list_next(children);
2321 QMenu * MainWindow::searchSubMenu(QString objectName)
2325 if ( objectName.length() > 0 )
2327 QString searchName = QString("menu") + objectName;
2329 lst = main_ui_->menuBar->findChildren<QMenu*>();
2330 foreach (QMenu* m, lst)
2332 if ( QString::compare( m->objectName(), searchName ) == 0 )
2340 void MainWindow::addExternalMenus()
2342 QMenu * subMenu = NULL;
2343 GList * user_menu = NULL;
2344 ext_menu_t * menu = NULL;
2346 user_menu = ext_menubar_get_entries();
2348 while ( ( user_menu != NULL ) && ( user_menu->data != NULL ) )
2350 menu = (ext_menu_t *) user_menu->data;
2352 /* On this level only menu items should exist. Not doing an assert here,
2353 * as it could be an honest mistake */
2354 if ( menu->type != EXT_MENUBAR_MENU )
2356 user_menu = g_list_next(user_menu);
2360 /* Create main submenu and add it to the menubar */
2361 if ( menu->parent_menu != NULL )
2363 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2364 if ( sortUnderneath != NULL)
2365 subMenu = sortUnderneath->addMenu(menu->label);
2368 if ( subMenu == NULL )
2369 subMenu = main_ui_->menuBar->addMenu(menu->label);
2371 /* This will generate the action structure for each menu. It is recursive,
2372 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2373 this->externalMenuHelper(menu, subMenu, 0);
2376 user_menu = g_list_next (user_menu);
2386 * indent-tabs-mode: nil
2389 * ex: set shiftwidth=4 tabstop=8 expandtab:
2390 * :indentSize=4:tabSize=8:noTabs=true: