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);
125 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
129 va_start(ap, msg_format);
130 SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
138 * Alert box, with optional "don't show this message again" variable
139 * and checkbox, and optional secondary text.
142 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
143 const char *secondary_msg, const char *msg_format, ...)
145 if (notagain && *notagain) {
151 va_start(ap, msg_format);
152 SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
155 sd.setDetailedText(secondary_msg);
157 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
158 QCheckBox *cb = NULL;
160 cb = new QCheckBox();
161 cb->setChecked(true);
162 cb->setText(QObject::tr("Don't show this message again."));
169 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
170 if (notagain && cb) {
171 *notagain = cb->isChecked();
177 * Error alert box, taking a format and a va_list argument.
180 vsimple_error_message_box(const char *msg_format, va_list ap)
182 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
187 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
188 QList<QAction *> actions = parent_menu->actions();
189 QList<QAction *>::const_iterator i;
190 for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
191 if ((*i)->text()==menu_text) {
195 // If we get here there menu entry was not found, add a sub menu
196 return parent_menu->addMenu(menu_text);
199 MainWindow::MainWindow(QWidget *parent) :
201 main_ui_(new Ui::MainWindow),
202 cur_layout_(QVector<unsigned>()),
203 df_combo_box_(new DisplayFilterCombo()),
206 previous_focus_(NULL),
207 show_hide_actions_(NULL),
208 time_display_actions_(NULL),
209 time_precision_actions_(NULL),
210 funnel_statistics_(new FunnelStatistics(this, capture_file_)),
211 capture_stopping_(false),
212 capture_filter_valid_(false),
219 if (!gbl_cur_main_window_) {
220 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
221 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
222 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
223 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
225 gbl_cur_main_window_ = this;
227 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
229 main_ui_->setupUi(this);
230 setWindowIcon(wsApp->normalIcon());
231 setTitlebarForCaptureFile();
232 setMenusForCaptureFile();
233 setForCapturedPackets(false);
234 setMenusForFileSet(false);
235 interfaceSelectionChanged();
236 loadWindowGeometry();
238 //To prevent users use features before initialization complete
239 //Otherwise unexpected problems may occur
240 setFeaturesEnabled(false);
241 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
242 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
243 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
244 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
245 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
246 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
248 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
249 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
250 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
251 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
252 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
254 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
258 connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
259 connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
262 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
263 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
264 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
265 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
266 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
267 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
268 connect(df_edit, SIGNAL(filterPackets(QString&,bool)), this, SLOT(filterPackets(QString&,bool)));
269 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
271 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
272 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
273 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
274 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString&,QString&)),
275 this, SLOT(openCaptureFile(QString&,QString&)));
276 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
278 initMainToolbarIcons();
280 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
281 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
282 // https://bugreports.qt-project.org/browse/QTBUG-22433
283 // This property is obsolete in Qt5 so this issue may be fixed in that version.
284 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
286 wireless_frame_ = new WirelessFrame(this);
287 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
288 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
289 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
290 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
291 this, SLOT(showPreferencesDialog(QString)));
293 main_ui_->goToFrame->hide();
294 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
295 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
297 // XXX For some reason the cursor is drawn funny with an input mask set
298 // https://bugreports.qt-project.org/browse/QTBUG-7174
300 main_ui_->searchFrame->hide();
301 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
302 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
303 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
304 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
306 main_ui_->addressEditorFrame->hide();
307 main_ui_->columnEditorFrame->hide();
308 main_ui_->preferenceEditorFrame->hide();
311 main_ui_->menuCapture->setEnabled(false);
314 #if defined(Q_OS_MAC)
315 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
316 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
317 ntb->setIconSize(QSize(24, 24));
318 #endif // QT_MACEXTRAS_LIB
320 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
321 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
322 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
324 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
328 #ifdef HAVE_SOFTWARE_UPDATE
329 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
330 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
331 main_ui_->menuHelp->insertAction(update_sep, update_action);
332 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
334 master_split_.setObjectName(tr("splitterMaster"));
335 extra_split_.setObjectName(tr("splitterExtra"));
336 main_ui_->mainStack->addWidget(&master_split_);
338 empty_pane_.setObjectName(tr("emptyPane"));
340 packet_list_ = new PacketList(&master_split_);
342 proto_tree_ = new ProtoTree(&master_split_);
343 proto_tree_->installEventFilter(this);
345 byte_view_tab_ = new ByteViewTab(&master_split_);
347 packet_list_->setProtoTree(proto_tree_);
348 packet_list_->setByteViewTab(byte_view_tab_);
349 packet_list_->installEventFilter(this);
351 main_welcome_ = main_ui_->welcomePage;
353 // Packet list and proto tree must exist before these are called.
354 setMenusForSelectedPacket();
355 setMenusForSelectedTreeRow();
357 initShowHideMainWidgets();
358 initTimeDisplayFormatMenu();
359 initTimePrecisionFormatMenu();
360 updatePreferenceActions();
361 setForCaptureInProgress(false);
363 setTabOrder(df_combo_box_, packet_list_);
365 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
366 this, SLOT(captureCapturePrepared(capture_session *)));
367 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
368 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
369 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
370 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
371 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
372 this, SLOT(captureCaptureFixedStarted(capture_session *)));
373 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
374 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
375 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
376 this, SLOT(captureCaptureFixedFinished(capture_session *)));
377 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
378 this, SLOT(captureCaptureStopping(capture_session *)));
379 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
380 this, SLOT(captureCaptureFailed(capture_session *)));
381 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
382 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
384 connect(&capture_file_, SIGNAL(captureFileOpened()),
385 this, SLOT(captureFileOpened()));
386 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
387 this, SLOT(captureFileReadStarted()));
388 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
389 this, SLOT(captureFileReadFinished()));
390 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
391 this, SLOT(captureFileReloadStarted()));
392 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
393 this, SLOT(captureFileReadFinished()));
394 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
395 this, SLOT(captureFileRescanStarted()));
396 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
397 this, SLOT(captureFileReadFinished()));
398 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
399 this, SLOT(captureFileRetapStarted()));
400 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
401 this, SLOT(captureFileRetapFinished()));
402 connect(&capture_file_, SIGNAL(captureFileClosing()),
403 this, SLOT(captureFileClosing()));
404 connect(&capture_file_, SIGNAL(captureFileClosed()),
405 this, SLOT(captureFileClosed()));
407 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
408 this, SLOT(captureFileSaveStarted(QString)));
409 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
410 main_ui_->statusBar, SLOT(popFileStatus()));
411 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
412 main_ui_->statusBar, SLOT(popFileStatus()));
413 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
414 main_ui_->statusBar, SLOT(popFileStatus()));
416 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
417 wsApp, SLOT(captureFileReadStarted()));
418 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
419 wsApp, SLOT(updateTaps()));
421 connect(wsApp, SIGNAL(recentFilesRead()),
422 packet_list_, SLOT(applyRecentColumnWidths()));
423 connect(wsApp, SIGNAL(columnsChanged()),
424 packet_list_, SLOT(columnsChanged()));
425 connect(wsApp, SIGNAL(preferencesChanged()),
426 packet_list_, SLOT(elideModeChanged()));
427 connect(wsApp, SIGNAL(recentFilesRead()),
428 this, SLOT(applyRecentPaneGeometry()));
429 connect(wsApp, SIGNAL(packetDissectionChanged()),
430 this, SLOT(redissectPackets()));
431 connect(wsApp, SIGNAL(appInitialized()),
432 this, SLOT(filterExpressionsChanged()));
433 connect(wsApp, SIGNAL(filterExpressionsChanged()),
434 this, SLOT(filterExpressionsChanged()));
435 connect(wsApp, SIGNAL(fieldsChanged()),
436 this, SLOT(fieldsChanged()));
438 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
439 this, SLOT(mainStackChanged(int)));
441 connect(main_welcome_, SIGNAL(startCapture()),
442 this, SLOT(startCapture()));
443 connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
444 this, SLOT(openCaptureFile(QString&)));
445 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
446 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
447 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
448 main_ui_->statusBar, SLOT(popFilterStatus()));
450 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
451 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
452 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
453 this, SLOT(redissectPackets()));
454 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
455 this, SLOT(showPreferencesDialog(QString)));
456 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
457 this, SLOT(showPreferencesDialog(QString)));
459 connect(this, SIGNAL(setCaptureFile(capture_file*)),
460 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
461 connect(this, SIGNAL(setCaptureFile(capture_file*)),
462 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
463 connect(this, SIGNAL(setCaptureFile(capture_file*)),
464 packet_list_, SLOT(setCaptureFile(capture_file*)));
465 connect(this, SIGNAL(setCaptureFile(capture_file*)),
466 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
468 connect(this, SIGNAL(monospaceFontChanged(QFont)),
469 packet_list_, SLOT(setMonospaceFont(QFont)));
470 connect(this, SIGNAL(monospaceFontChanged(QFont)),
471 proto_tree_, SLOT(setMonospaceFont(QFont)));
472 connect(this, SIGNAL(monospaceFontChanged(QFont)),
473 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
475 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
476 packet_list_, SLOT(goNextPacket()));
477 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
478 packet_list_, SLOT(goPreviousPacket()));
479 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
480 packet_list_, SLOT(goFirstPacket()));
481 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
482 packet_list_, SLOT(goLastPacket()));
484 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
485 proto_tree_, SLOT(expandSubtrees()));
486 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
487 proto_tree_, SLOT(expandAll()));
488 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
489 proto_tree_, SLOT(collapseAll()));
491 connect(packet_list_, SIGNAL(packetSelectionChanged()),
492 this, SLOT(setMenusForSelectedPacket()));
493 connect(packet_list_, SIGNAL(packetDissectionChanged()),
494 this, SLOT(redissectPackets()));
495 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
496 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
497 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
498 this, SLOT(showPreferencesDialog(QString)));
499 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
500 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
501 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
502 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
503 packet_list_, SLOT(redrawVisiblePackets()));
504 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
505 this, SLOT(openPacketDialog()));
506 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
507 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
508 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
509 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
510 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
511 main_ui_->statusBar, SLOT(popBusyStatus()));
512 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
513 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
514 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
515 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
516 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
517 main_ui_->statusBar, SLOT(popProgressStatus()));
519 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
520 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
521 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
522 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
523 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
524 this, SLOT(openPacketDialog(bool)));
525 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
526 this, SLOT(showPreferencesDialog(QString)));
527 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
528 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
530 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
531 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
533 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
534 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
536 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
537 &capture_file_, SLOT(stopLoading()));
539 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
540 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
542 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
543 this, SLOT(openCaptureFile(QString&)));
546 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
548 connect(iface_tree, SIGNAL(itemSelectionChanged()),
549 this, SLOT(interfaceSelectionChanged()));
551 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
552 this, SLOT(captureFilterSyntaxChanged(bool)));
555 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
556 this, SLOT(showExtcapOptionsDialog(QString&)));
559 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
560 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
561 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
562 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
563 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
564 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
567 /* Create plugin_if hooks */
568 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter );
569 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter );
570 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
572 main_ui_->mainStack->setCurrentWidget(main_welcome_);
575 MainWindow::~MainWindow()
580 QString MainWindow::getFilter()
582 return df_combo_box_->itemText(df_combo_box_->count());
585 QMenu *MainWindow::createPopupMenu()
587 QMenu *menu = new QMenu();
588 menu->addAction(main_ui_->actionViewMainToolbar);
589 menu->addAction(main_ui_->actionViewFilterToolbar);
590 menu->addAction(main_ui_->actionViewWirelessToolbar);
591 menu->addAction(main_ui_->actionViewStatusBar);
592 menu->addSeparator();
593 menu->addAction(main_ui_->actionViewPacketList);
594 menu->addAction(main_ui_->actionViewPacketDetails);
595 menu->addAction(main_ui_->actionViewPacketBytes);
599 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
601 pipe_source_ = source;
602 pipe_child_process_ = child_process;
603 pipe_user_data_ = user_data;
604 pipe_input_cb_ = input_cb;
607 /* Tricky to use pipes in win9x, as no concept of wait. NT can
608 do this but that doesn't cover all win32 platforms. GTK can do
609 this but doesn't seem to work over processes. Attempt to do
610 something similar here, start a timer and check for data on every
612 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
615 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
619 pipe_timer_ = new QTimer(this);
620 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
621 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
622 pipe_timer_->start(200);
624 if (pipe_notifier_) {
625 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
626 delete pipe_notifier_;
629 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
630 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
631 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
632 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
636 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
638 // The user typed some text. Start filling in a filter.
639 // We may need to be more choosy here. We just need to catch events for the packet list,
640 // proto tree, and main welcome widgets.
641 if (event->type() == QEvent::KeyPress) {
642 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
643 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
644 df_combo_box_->lineEdit()->insert(kevt->text());
645 df_combo_box_->lineEdit()->setFocus();
650 return QMainWindow::eventFilter(obj, event);
653 void MainWindow::keyPressEvent(QKeyEvent *event) {
655 // Explicitly focus on the display filter combo.
656 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
657 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
661 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
662 if (event->modifiers() == Qt::NoModifier) {
663 if (event->key() == Qt::Key_Escape) {
664 on_goToCancel_clicked();
665 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
669 return; // goToLineEdit didn't want it and we don't either.
672 // Move up & down the packet list.
673 if (event->key() == Qt::Key_F7) {
674 packet_list_->goPreviousPacket();
675 } else if (event->key() == Qt::Key_F8) {
676 packet_list_->goNextPacket();
679 // Move along, citizen.
680 QMainWindow::keyPressEvent(event);
683 void MainWindow::closeEvent(QCloseEvent *event) {
684 saveWindowGeometry();
686 /* If we're in the middle of stopping a capture, don't do anything;
687 the user can try deleting the window after the capture stops. */
688 if (capture_stopping_) {
693 QString before_what(tr(" before quitting"));
694 if (!testCaptureFileClose(TRUE, before_what)) {
700 capture_interfaces_dialog_.close();
702 // Make sure we kill any open dumpcap processes.
703 delete main_welcome_;
705 // One of the many places we assume one main window.
706 if(!wsApp->isInitialized()) {
707 // If we're still initializing, QCoreApplication::quit() won't
708 // exit properly because we are not in the event loop. This
709 // means that the application won't clean up after itself. We
710 // might want to call wsApp->processEvents() during startup
711 // instead so that we can do a normal exit here.
717 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
720 foreach (QUrl drag_url, event->mimeData()->urls()) {
721 if (!drag_url.toLocalFile().isEmpty()) {
726 if (accept) event->acceptProposedAction();
729 void MainWindow::dropEvent(QDropEvent *event)
731 foreach (QUrl drop_url, event->mimeData()->urls()) {
732 QString local_file = drop_url.toLocalFile();
733 if (!local_file.isEmpty()) {
734 event->acceptProposedAction();
735 openCaptureFile(local_file);
741 // Apply recent settings to the main window geometry.
742 // We haven't loaded the preferences at this point so we assume that the
743 // position and size preference are enabled.
744 void MainWindow::loadWindowGeometry()
746 int min_sensible_dimension_ = 200;
749 if (recent.gui_geometry_main_maximized) {
750 setWindowState(Qt::WindowMaximized);
754 // if (prefs.gui_geometry_save_position) {
755 move(recent.gui_geometry_main_x, recent.gui_geometry_main_y);
758 if (// prefs.gui_geometry_save_size &&
759 recent.gui_geometry_main_width > min_sensible_dimension_ &&
760 recent.gui_geometry_main_height > min_sensible_dimension_) {
761 resize(recent.gui_geometry_main_width, recent.gui_geometry_main_height);
766 void MainWindow::saveWindowGeometry()
768 if (prefs.gui_geometry_save_position) {
769 recent.gui_geometry_main_x = pos().x();
770 recent.gui_geometry_main_y = pos().y();
773 if (prefs.gui_geometry_save_size) {
774 recent.gui_geometry_main_width = size().width();
775 recent.gui_geometry_main_height = size().height();
778 if (prefs.gui_geometry_save_maximized) {
779 // On OS X this is false when it shouldn't be
780 recent.gui_geometry_main_maximized = isMaximized();
783 if (master_split_.sizes().length() > 0) {
784 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
787 if (master_split_.sizes().length() > 2) {
788 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
789 } else if (extra_split_.sizes().length() > 0) {
790 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
794 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
796 case layout_pane_content_none:
798 case layout_pane_content_plist:
800 case layout_pane_content_pdetails:
802 case layout_pane_content_pbytes:
803 return byte_view_tab_;
805 g_assert_not_reached();
810 void MainWindow::mergeCaptureFile()
812 QString file_name = "";
813 QString display_filter = "";
814 dfilter_t *rfcode = NULL;
817 if (!capture_file_.capFile())
820 if (prefs.gui_ask_unsaved) {
821 if (cf_has_unsaved_data(capture_file_.capFile())) {
822 QMessageBox msg_dialog;
823 gchar *display_basename;
826 msg_dialog.setIcon(QMessageBox::Question);
827 /* This file has unsaved data; ask the user whether to save
829 if (capture_file_.capFile()->is_tempfile) {
830 msg_dialog.setText(tr("Save packets before merging?"));
831 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
834 * Format the message.
836 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
837 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
838 g_free(display_basename);
839 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
842 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
843 msg_dialog.setDefaultButton(QMessageBox::Save);
845 response = msg_dialog.exec();
849 case QMessageBox::Save:
850 /* Save the file but don't close it */
851 saveCaptureFile(capture_file_.capFile(), FALSE);
854 case QMessageBox::Cancel:
856 /* Don't do the merge. */
863 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), display_filter);
865 cf_status_t merge_status;
866 char *in_filenames[2];
869 switch (prefs.gui_fileopen_style) {
871 case FO_STYLE_LAST_OPENED:
872 /* The user has specified that we should start out in the last directory
873 we looked in. If we've already opened a file, use its containing
874 directory, if we could determine it, as the directory, otherwise
875 use the "last opened" directory saved in the preferences file if
877 /* This is now the default behaviour in file_selection_new() */
880 case FO_STYLE_SPECIFIED:
881 /* The user has specified that we should always start out in a
882 specified directory; if they've specified that directory,
883 start out by showing the files in that dir. */
884 if (prefs.gui_fileopen_dir[0] != '\0')
885 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
889 if (merge_dlg.merge(file_name)) {
892 if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode, &err_msg)) {
893 cf_set_rfcode(capture_file_.capFile(), rfcode);
895 /* Not valid. Tell the user, and go back and run the file
896 selection box again once they dismiss the alert. */
897 //bad_dfilter_alert_box(top_level, display_filter->str);
898 QMessageBox::warning(this, tr("Invalid Display Filter"),
899 QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, err_msg)),
908 file_type = capture_file_.capFile()->cd_t;
910 /* Try to merge or append the two files */
912 if (merge_dlg.mergeType() == 0) {
913 /* chronological order */
914 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
915 in_filenames[1] = qstring_strdup(file_name);
916 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
917 } else if (merge_dlg.mergeType() <= 0) {
919 in_filenames[0] = qstring_strdup(file_name);
920 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
921 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
924 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
925 in_filenames[1] = qstring_strdup(file_name);
926 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
929 g_free(in_filenames[0]);
930 g_free(in_filenames[1]);
932 if (merge_status != CF_OK) {
934 dfilter_free(rfcode);
939 cf_close(capture_file_.capFile());
941 /* Try to open the merged capture file. */
942 CaptureFile::globalCapFile()->window = this;
943 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
944 /* We couldn't open it; fail. */
945 CaptureFile::globalCapFile()->window = NULL;
947 dfilter_free(rfcode);
952 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
953 it closed the previous capture file, and thus destroyed any
954 previous read filter attached to "cf"). */
955 CaptureFile::globalCapFile()->rfcode = rfcode;
957 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
961 /* Just because we got an error, that doesn't mean we were unable
962 to read any of the file; we handle what we could get from the
966 case CF_READ_ABORTED:
967 /* The user bailed out of re-reading the capture file; the
968 capture file has been closed - just free the capture file name
969 string and return (without changing the last containing
975 /* Save the name of the containing directory specified in the path name,
976 if any; we can write over cf_merged_name, which is a good thing, given that
977 "get_dirname()" does write over its argument. */
978 wsApp->setLastOpenDir(get_dirname(tmpname));
980 df_combo_box_->setEditText(display_filter);
981 main_ui_->statusBar->showExpert();
987 void MainWindow::importCaptureFile() {
988 ImportTextDialog import_dlg;
990 QString before_what(tr(" before importing a new capture"));
991 if (!testCaptureFileClose(FALSE, before_what))
996 if (import_dlg.result() != QDialog::Accepted) {
997 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1001 openCaptureFile(import_dlg.capfileName());
1004 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1006 gboolean discard_comments;
1008 if (cf->is_tempfile) {
1009 /* This is a temporary capture file, so saving it means saving
1010 it to a permanent file. Prompt the user for a location
1011 to which to save it. Don't require that the file format
1012 support comments - if it's a temporary capture file, it's
1013 probably pcap-ng, which supports comments and, if it's
1014 not pcap-ng, let the user decide what they want to do
1015 if they've added comments. */
1016 saveAsCaptureFile(cf, FALSE, dont_reopen);
1018 if (cf->unsaved_changes) {
1019 cf_write_status_t status;
1021 /* This is not a temporary capture file, but it has unsaved
1022 changes, so saving it means doing a "safe save" on top
1023 of the existing file, in the same format - no UI needed
1024 unless the file has comments and the file's format doesn't
1027 If the file has comments, does the file's format support them?
1028 If not, ask the user whether they want to discard the comments
1029 or choose a different format. */
1030 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1033 /* The file can be saved in the specified format as is;
1034 just drive on and save in the format they selected. */
1035 discard_comments = FALSE;
1038 case SAVE_WITHOUT_COMMENTS:
1039 /* The file can't be saved in the specified format as is,
1040 but it can be saved without the comments, and the user
1041 said "OK, discard the comments", so save it in the
1042 format they specified without the comments. */
1043 discard_comments = TRUE;
1046 case SAVE_IN_ANOTHER_FORMAT:
1047 /* There are file formats in which we can save this that
1048 support comments, and the user said not to delete the
1049 comments. Do a "Save As" so the user can select
1050 one of those formats and choose a file name. */
1051 saveAsCaptureFile(cf, TRUE, dont_reopen);
1055 /* The user said "forget it". Just return. */
1059 /* Squelch warnings that discard_comments is being used
1061 g_assert_not_reached();
1065 /* XXX - cf->filename might get freed out from under us, because
1066 the code path through which cf_save_records() goes currently
1067 closes the current file and then opens and reloads the saved file,
1068 so make a copy and free it later. */
1069 file_name = cf->filename;
1070 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1071 discard_comments, dont_reopen);
1075 /* The save succeeded; we're done.
1076 If we discarded comments, redraw the packet list to reflect
1077 any packets that no longer have comments. */
1078 if (discard_comments)
1079 packet_list_queue_draw();
1081 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1082 updateForUnsavedChanges(); // we update the title bar to remove the *
1085 case CF_WRITE_ERROR:
1086 /* The write failed.
1087 XXX - OK, what do we do now? Let them try a
1088 "Save As", in case they want to try to save to a
1089 different directory r file system? */
1092 case CF_WRITE_ABORTED:
1093 /* The write was aborted; just drive on. */
1097 /* Otherwise just do nothing. */
1101 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1102 QString file_name = "";
1104 gboolean compressed;
1105 cf_write_status_t status;
1107 gboolean discard_comments = FALSE;
1114 CaptureFileDialog save_as_dlg(this, cf);
1116 switch (prefs.gui_fileopen_style) {
1118 case FO_STYLE_LAST_OPENED:
1119 /* The user has specified that we should start out in the last directory
1120 we looked in. If we've already opened a file, use its containing
1121 directory, if we could determine it, as the directory, otherwise
1122 use the "last opened" directory saved in the preferences file if
1124 /* This is now the default behaviour in file_selection_new() */
1127 case FO_STYLE_SPECIFIED:
1128 /* The user has specified that we should always start out in a
1129 specified directory; if they've specified that directory,
1130 start out by showing the files in that dir. */
1131 if (prefs.gui_fileopen_dir[0] != '\0')
1132 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1136 /* If the file has comments, does the format the user selected
1137 support them? If not, ask the user whether they want to
1138 discard the comments or choose a different format. */
1139 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1142 /* The file can be saved in the specified format as is;
1143 just drive on and save in the format they selected. */
1144 discard_comments = FALSE;
1147 case SAVE_WITHOUT_COMMENTS:
1148 /* The file can't be saved in the specified format as is,
1149 but it can be saved without the comments, and the user
1150 said "OK, discard the comments", so save it in the
1151 format they specified without the comments. */
1152 discard_comments = TRUE;
1155 case SAVE_IN_ANOTHER_FORMAT:
1156 /* There are file formats in which we can save this that
1157 support comments, and the user said not to delete the
1158 comments. The combo box of file formats has had the
1159 formats that don't support comments trimmed from it,
1160 so run the dialog again, to let the user decide
1161 whether to save in one of those formats or give up. */
1162 must_support_comments = TRUE;
1166 /* The user said "forget it". Just get rid of the dialog box
1170 file_type = save_as_dlg.selectedFileType();
1171 compressed = save_as_dlg.isCompressed();
1173 fileAddExtension(file_name, file_type, compressed);
1176 // /* If the file exists and it's user-immutable or not writable,
1177 // ask the user whether they want to override that. */
1178 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1179 // /* They don't. Let them try another file name or cancel. */
1184 /* Attempt to save the file */
1185 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1186 discard_comments, dont_reopen);
1190 /* The save succeeded; we're done. */
1191 /* Save the directory name for future file dialogs. */
1192 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1193 set_last_open_dir(dirname);
1195 /* If we discarded comments, redraw the packet list to reflect
1196 any packets that no longer have comments. */
1197 if (discard_comments)
1198 packet_list_queue_draw();
1200 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1201 updateForUnsavedChanges(); // we update the title bar to remove the *
1204 case CF_WRITE_ERROR:
1205 /* The save failed; let the user try again. */
1208 case CF_WRITE_ABORTED:
1209 /* The user aborted the save; just return. */
1216 void MainWindow::exportSelectedPackets() {
1217 QString file_name = "";
1219 gboolean compressed;
1220 packet_range_t range;
1221 cf_write_status_t status;
1223 gboolean discard_comments = FALSE;
1225 if (!capture_file_.capFile())
1228 /* Init the packet range */
1229 packet_range_init(&range, capture_file_.capFile());
1230 range.process_filtered = TRUE;
1231 range.include_dependents = TRUE;
1234 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1236 switch (prefs.gui_fileopen_style) {
1238 case FO_STYLE_LAST_OPENED:
1239 /* The user has specified that we should start out in the last directory
1240 we looked in. If we've already opened a file, use its containing
1241 directory, if we could determine it, as the directory, otherwise
1242 use the "last opened" directory saved in the preferences file if
1244 /* This is now the default behaviour in file_selection_new() */
1247 case FO_STYLE_SPECIFIED:
1248 /* The user has specified that we should always start out in a
1249 specified directory; if they've specified that directory,
1250 start out by showing the files in that dir. */
1251 if (prefs.gui_fileopen_dir[0] != '\0')
1252 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1256 /* If the file has comments, does the format the user selected
1257 support them? If not, ask the user whether they want to
1258 discard the comments or choose a different format. */
1259 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1262 /* The file can be saved in the specified format as is;
1263 just drive on and save in the format they selected. */
1264 discard_comments = FALSE;
1267 case SAVE_WITHOUT_COMMENTS:
1268 /* The file can't be saved in the specified format as is,
1269 but it can be saved without the comments, and the user
1270 said "OK, discard the comments", so save it in the
1271 format they specified without the comments. */
1272 discard_comments = TRUE;
1275 case SAVE_IN_ANOTHER_FORMAT:
1276 /* There are file formats in which we can save this that
1277 support comments, and the user said not to delete the
1278 comments. The combo box of file formats has had the
1279 formats that don't support comments trimmed from it,
1280 so run the dialog again, to let the user decide
1281 whether to save in one of those formats or give up. */
1285 /* The user said "forget it". Just get rid of the dialog box
1291 * Check that we're not going to save on top of the current
1293 * We do it here so we catch all cases ...
1294 * Unfortunately, the file requester gives us an absolute file
1295 * name and the read file name may be relative (if supplied on
1296 * the command line). From Joerg Mayer.
1298 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1299 QMessageBox msg_box;
1300 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1302 msg_box.setIcon(QMessageBox::Critical);
1303 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1304 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1305 msg_box.setStandardButtons(QMessageBox::Ok);
1306 msg_box.setDefaultButton(QMessageBox::Ok);
1308 g_free(display_basename);
1312 file_type = esp_dlg.selectedFileType();
1313 compressed = esp_dlg.isCompressed();
1314 fileAddExtension(file_name, file_type, compressed);
1317 // /* If the file exists and it's user-immutable or not writable,
1318 // ask the user whether they want to override that. */
1319 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1320 // /* They don't. Let them try another file name or cancel. */
1325 /* Attempt to save the file */
1326 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1330 /* The save succeeded; we're done. */
1331 /* Save the directory name for future file dialogs. */
1332 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1333 set_last_open_dir(dirname);
1335 /* If we discarded comments, redraw the packet list to reflect
1336 any packets that no longer have comments. */
1337 if (discard_comments)
1338 packet_list_queue_draw();
1341 case CF_WRITE_ERROR:
1342 /* The save failed; let the user try again. */
1345 case CF_WRITE_ABORTED:
1346 /* The user aborted the save; just return. */
1353 void MainWindow::exportDissections(export_type_e export_type) {
1354 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1355 packet_range_t range;
1357 if (!capture_file_.capFile())
1360 /* Init the packet range */
1361 packet_range_init(&range, capture_file_.capFile());
1362 range.process_filtered = TRUE;
1363 range.include_dependents = TRUE;
1368 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1369 QString file_name_lower;
1370 QString file_suffix;
1371 GSList *extensions_list;
1372 gboolean add_extension;
1375 * Append the default file extension if there's none given by
1376 * the user or if they gave one that's not one of the valid
1377 * extensions for the file type.
1379 file_name_lower = file_name.toLower();
1380 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1381 if (extensions_list != NULL) {
1384 /* We have one or more extensions for this file type.
1385 Start out assuming we need to add the default one. */
1386 add_extension = TRUE;
1388 /* OK, see if the file has one of those extensions. */
1389 for (extension = extensions_list; extension != NULL;
1390 extension = g_slist_next(extension)) {
1391 file_suffix += tr(".") + (char *)extension->data;
1392 if (file_name_lower.endsWith(file_suffix)) {
1394 * The file name has one of the extensions for
1397 add_extension = FALSE;
1400 file_suffix += ".gz";
1401 if (file_name_lower.endsWith(file_suffix)) {
1403 * The file name has one of the extensions for
1406 add_extension = FALSE;
1411 /* We have no extensions for this file type. Don't add one. */
1412 add_extension = FALSE;
1414 if (add_extension) {
1415 if (wtap_default_file_extension(file_type) != NULL) {
1416 file_name += tr(".") + wtap_default_file_extension(file_type);
1424 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
1425 bool capture_in_progress = FALSE;
1427 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1428 return true; /* Already closed, nothing to do */
1431 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS && capture_file_.capFile()->count>0) {
1432 /* This is true if we're reading a capture file *or* if we're doing
1433 a live capture. If we're reading a capture file, the main loop
1434 is busy reading packets, and only accepting input from the
1435 progress dialog, so we can't get here, so this means we're
1437 capture_in_progress = TRUE;
1441 if (prefs.gui_ask_unsaved) {
1442 if (cf_has_unsaved_data(capture_file_.capFile()) || capture_in_progress) {
1443 QMessageBox msg_dialog;
1445 QPushButton *saveButton;
1446 QPushButton *discardButton;
1448 msg_dialog.setIcon(QMessageBox::Question);
1449 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1450 /* This file has unsaved data or there's a capture in
1451 progress; ask the user whether to save the data. */
1452 if (capture_file_.capFile()->is_tempfile) {
1454 msg_dialog.setText(tr("You have unsaved packets"));
1455 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1457 if (capture_in_progress) {
1458 question.append(tr("Do you want to stop the capture and save the captured packets"));
1460 question.append(tr("Do you want to save the captured packets"));
1462 question.append(before_what).append(tr("?"));
1463 msg_dialog.setInformativeText(question);
1468 * Format the message.
1470 if (capture_in_progress) {
1471 question.append(tr("Do you want to stop the capture and save the captured packets"));
1472 question.append(before_what).append(tr("?"));
1473 msg_dialog.setText(question);
1474 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1476 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1477 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1478 .arg(display_basename)
1481 g_free(display_basename);
1482 msg_dialog.setText(question);
1483 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1487 // XXX Text comes from ui/gtk/stock_icons.[ch]
1488 // Note that the button roles differ from the GTK+ version.
1489 // Cancel = RejectRole
1490 // Save = AcceptRole
1491 // Don't Save = DestructiveRole
1492 msg_dialog.addButton(QMessageBox::Cancel);
1494 if (capture_in_progress) {
1495 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1497 saveButton = msg_dialog.addButton(QMessageBox::Save);
1499 msg_dialog.setDefaultButton(saveButton);
1502 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1503 discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1504 QMessageBox::DestructiveRole);
1506 discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1507 QMessageBox::DestructiveRole);
1510 if (capture_in_progress) {
1511 discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1512 QMessageBox::DestructiveRole);
1514 discardButton = msg_dialog.addButton(tr("Continue &without Saving"), QMessageBox::DestructiveRole);
1519 /* According to the Qt doc:
1520 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1522 * Therefore we should use clickedButton() to determine which button was clicked. */
1524 if(msg_dialog.clickedButton() == saveButton)
1527 /* If there's a capture in progress, we have to stop the capture
1528 and then do the save. */
1529 if (capture_in_progress)
1532 /* Save the file and close it */
1533 saveCaptureFile(capture_file_.capFile(), TRUE);
1535 else if(msg_dialog.clickedButton() == discardButton)
1539 * If there's a capture in progress; we have to stop the capture
1540 * and then do the close.
1542 if (capture_in_progress)
1545 /* Just close the file, discarding changes */
1546 cf_close(capture_file_.capFile());
1549 else //cancelButton or some other unspecified button
1555 /* Unchanged file, just close it */
1556 capture_file_.capFile()->state = FILE_READ_ABORTED;
1557 cf_close(capture_file_.capFile());
1560 /* User asked not to be bothered by those prompts, just close it.
1561 XXX - should that apply only to saving temporary files? */
1563 /* If there's a capture in progress, we have to stop the capture
1564 and then do the close. */
1565 if (capture_in_progress)
1568 cf_close(capture_file_.capFile());
1571 return true; /* File closed */
1574 void MainWindow::captureStop() {
1577 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1578 WiresharkApplication::processEvents();
1582 void MainWindow::initMainToolbarIcons()
1584 #if defined(Q_OS_WIN)
1585 // Current GTK+ and other Windows app behavior.
1586 main_ui_->mainToolBar->setIconSize(QSize(16, 16));
1588 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1589 main_ui_->mainToolBar->setIconSize(QSize(24, 24));
1592 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1593 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1595 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1596 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1597 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1598 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1600 // Menu icons are disabled in main_window.ui for these items.
1601 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1602 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1603 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1604 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1606 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1607 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1608 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1609 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1610 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1611 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1612 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1614 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1615 main_ui_->actionViewColorizePacketList->setChecked(recent.packet_list_colorize);
1616 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1618 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1619 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1620 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1621 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1622 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1623 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1624 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1627 void MainWindow::initShowHideMainWidgets()
1629 if (show_hide_actions_) {
1633 show_hide_actions_ = new QActionGroup(this);
1634 QMap<QAction *, QWidget *> shmw_actions;
1636 show_hide_actions_->setExclusive(false);
1637 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1638 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1639 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1640 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1641 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1642 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1643 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1645 main_ui_->actionViewMainToolbar->setChecked(recent.main_toolbar_show);
1646 main_ui_->actionViewFilterToolbar->setChecked(recent.filter_toolbar_show);
1647 main_ui_->actionViewWirelessToolbar->setChecked(recent.wireless_toolbar_show);
1648 main_ui_->actionViewStatusBar->setChecked(recent.statusbar_show);
1649 main_ui_->actionViewPacketList->setChecked(recent.packet_list_show);
1650 main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show);
1651 main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show);
1653 foreach (QAction *shmwa, shmw_actions.keys()) {
1654 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1655 show_hide_actions_->addAction(shmwa);
1656 showHideMainWidgets(shmwa);
1659 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1662 Q_DECLARE_METATYPE(ts_type)
1664 void MainWindow::initTimeDisplayFormatMenu()
1666 if (time_display_actions_) {
1670 time_display_actions_ = new QActionGroup(this);
1671 QMap<QAction *, ts_type> td_actions;
1673 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1674 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1675 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1676 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1677 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1678 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1679 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1680 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1681 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1682 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1684 foreach (QAction* tda, td_actions.keys()) {
1685 tda->setData(qVariantFromValue(td_actions[tda]));
1686 time_display_actions_->addAction(tda);
1687 if (recent.gui_time_format == td_actions[tda]) {
1688 tda->setChecked(true);
1692 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1694 main_ui_->actionViewTimeDisplaySecondsWithHoursAndMinutes->setChecked(recent.gui_seconds_format == TS_SECONDS_HOUR_MIN_SEC);
1697 Q_DECLARE_METATYPE(ts_precision)
1699 void MainWindow::initTimePrecisionFormatMenu()
1701 if (time_precision_actions_) {
1705 time_precision_actions_ = new QActionGroup(this);
1706 QMap<QAction *, ts_precision> tp_actions;
1707 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1708 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1709 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1710 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1711 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1712 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1713 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1715 foreach (QAction* tpa, tp_actions.keys()) {
1716 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1717 time_precision_actions_->addAction(tpa);
1718 if (recent.gui_time_precision == tp_actions[tpa]) {
1719 tpa->setChecked(true);
1723 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1727 void MainWindow::setTitlebarForCaptureFile()
1729 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1731 // Qt *REALLY* doesn't like windows that sometimes have a
1732 // title set with setWindowTitle() and other times have a
1733 // file path set; apparently, once you've set the title
1734 // with setWindowTitle(), it sticks, and setWindowFilePath()
1735 // has no effect. It appears to can clear the title with
1736 // setWindowTitle(NULL), but that clears the actual title in
1737 // the title bar, and setWindowFilePath() then, I guess, sees
1738 // that there's already a file path, and does nothing, leaving
1739 // the title bar empty. So you then have to clear the file path
1740 // with setWindowFilePath(NULL), and then set it.
1742 // Maybe there's a #include "you're holding it wrong" here.
1743 // However, I really don't want to hear from people who think
1744 // that a window can never be associated with something other
1745 // than a user file at time T1 and with a user file at time T2,
1746 // given that, in Wireshark, a window can be associated with a
1747 // live capture at time T1 and then, after you've saved the live
1748 // capture to a user file, associated with a user file at time T2.
1750 if (capture_file_.capFile()->is_tempfile) {
1752 // For a temporary file, put the source of the data
1753 // in the window title, not whatever random pile
1754 // of characters is the last component of the path
1757 // XXX - on non-Mac platforms, put in the application
1760 // XXX - Use setWindowModified
1762 setWindowFilePath(NULL);
1763 window_name = g_strdup_printf("[*]%s", cf_get_tempfile_source(capture_file_.capFile())); //TODO : Fix Translate
1764 setWindowTitle(window_name);
1765 g_free(window_name);
1768 // For a user file, set the full path; that way,
1769 // for OS X, it'll set the "proxy icon". Qt
1770 // handles extracting the last component.
1772 // Sadly, some UN*Xes don't necessarily use UTF-8
1773 // for their file names, so we have to map the
1774 // file path to UTF-8. If that fails, we're somewhat
1777 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1782 if (utf8_filename == NULL) {
1783 // So what the heck else can we do here?
1784 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1786 setWindowTitle(NULL);
1787 setWindowFilePath(NULL);
1788 setWindowFilePath(utf8_filename);
1789 g_free(utf8_filename);
1792 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1794 /* We have no capture file. */
1795 setWindowFilePath(NULL);
1796 setWindowTitle(tr("The Wireshark Network Analyzer"));
1800 void MainWindow::setTitlebarForSelectedTreeRow()
1802 setWindowTitle(tr("The Wireshark Network Analyzer"));
1806 void MainWindow::setTitlebarForCaptureInProgress()
1810 setWindowFilePath(NULL);
1811 if (capture_file_.capFile()) {
1812 window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(capture_file_.capFile())); //TODO : Fix Translate
1813 setWindowTitle(window_name);
1814 g_free(window_name);
1816 /* We have no capture in progress. */
1817 setWindowTitle(tr("The Wireshark Network Analyzer"));
1823 /* Enable or disable menu items based on whether you have a capture file
1824 you've finished reading and, if you have one, whether it's been saved
1825 and whether it could be saved except by copying the raw packet data. */
1826 void MainWindow::setMenusForCaptureFile(bool force_disable)
1828 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1829 /* We have no capture file or we're currently reading a file */
1830 main_ui_->actionFileMerge->setEnabled(false);
1831 main_ui_->actionFileClose->setEnabled(false);
1832 main_ui_->actionFileSave->setEnabled(false);
1833 main_ui_->actionFileSaveAs->setEnabled(false);
1834 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1835 main_ui_->actionFileExportPackets->setEnabled(false);
1836 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1837 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1838 main_ui_->actionFileExportPDU->setEnabled(false);
1839 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1840 main_ui_->menuFileExportObjects->setEnabled(false);
1841 main_ui_->actionViewReload->setEnabled(false);
1843 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1845 main_ui_->actionFileClose->setEnabled(true);
1846 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1847 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1848 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1850 * "Export Specified Packets..." should be available only if
1851 * we can write the file out in at least one format.
1853 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1854 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1855 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1856 main_ui_->actionFileExportPDU->setEnabled(true);
1857 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1858 main_ui_->menuFileExportObjects->setEnabled(true);
1859 main_ui_->actionViewReload->setEnabled(true);
1863 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1864 /* Either a capture was started or stopped; in either case, it's not
1865 in the process of stopping, so allow quitting. */
1867 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1868 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1869 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1870 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1871 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1872 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1873 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1874 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1875 main_ui_->actionFileQuit->setEnabled(true);
1877 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1879 // XXX Fix packet list heading menu sensitivity
1880 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1881 // !capture_in_progress);
1882 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1883 // !capture_in_progress);
1884 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1885 // !capture_in_progress);
1888 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1889 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1890 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1891 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1892 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1893 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1894 #endif /* HAVE_LIBPCAP */
1898 void MainWindow::setMenusForCaptureStopping() {
1899 main_ui_->actionFileQuit->setEnabled(false);
1900 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1902 main_ui_->actionCaptureStart->setChecked(false);
1903 main_ui_->actionCaptureStop->setEnabled(false);
1904 main_ui_->actionCaptureRestart->setEnabled(false);
1905 #endif /* HAVE_LIBPCAP */
1908 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1910 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1912 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1913 // have_captured_packets);
1915 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
1916 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
1917 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
1919 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1920 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1921 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1922 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1923 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1925 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
1926 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
1927 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
1928 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
1930 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1931 // have_captured_packets);
1932 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1933 // have_captured_packets);
1934 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
1935 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
1936 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
1939 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1940 bool enable_next = fileset_get_next() != NULL && enable_list_files;
1941 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1943 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1944 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1945 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1948 void MainWindow::updateForUnsavedChanges() {
1949 setTitlebarForCaptureFile();
1950 setMenusForCaptureFile();
1953 void MainWindow::changeEvent(QEvent* event)
1957 switch (event->type())
1959 case QEvent::LanguageChange:
1960 main_ui_->retranslateUi(this);
1962 case QEvent::LocaleChange:{
1963 QString locale = QLocale::system().name();
1964 locale.truncate(locale.lastIndexOf('_'));
1965 wsApp->loadLanguage(locale);
1972 QMainWindow::changeEvent(event);
1975 /* Update main window items based on whether there's a capture in progress. */
1976 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1978 setMenusForCaptureInProgress(capture_in_progress);
1980 wireless_frame_->setCaptureInProgress(capture_in_progress);
1983 packet_list_->setCaptureInProgress(capture_in_progress);
1984 // set_toolbar_for_capture_in_progress(capture_in_progress);
1986 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1990 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
1991 << REGISTER_ANALYZE_GROUP_UNSORTED
1992 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
1993 << REGISTER_STAT_GROUP_UNSORTED
1994 << REGISTER_STAT_GROUP_GENERIC
1995 << REGISTER_STAT_GROUP_CONVERSATION_LIST
1996 << REGISTER_STAT_GROUP_ENDPOINT_LIST
1997 << REGISTER_STAT_GROUP_RESPONSE_TIME
1998 << REGISTER_STAT_GROUP_TELEPHONY
1999 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2000 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2001 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2002 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2003 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2004 << REGISTER_TOOLS_GROUP_UNSORTED;
2006 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2008 foreach (QAction *action, actions) {
2009 switch (menu_group) {
2010 case REGISTER_ANALYZE_GROUP_UNSORTED:
2011 case REGISTER_STAT_GROUP_UNSORTED:
2012 main_ui_->menuStatistics->insertAction(
2013 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2016 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2017 main_ui_->menuServiceResponseTime->addAction(action);
2019 case REGISTER_STAT_GROUP_TELEPHONY:
2020 main_ui_->menuTelephony->addAction(action);
2022 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2023 main_ui_->menuANSI->addAction(action);
2025 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2026 main_ui_->menuGSM->addAction(action);
2028 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2029 main_ui_->menuMTP3->addAction(action);
2031 case REGISTER_TOOLS_GROUP_UNSORTED:
2033 // Allow the creation of submenus. Mimics the behavor of
2034 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2035 // and GtkUIManager.
2037 // For now we limit the insanity to the "Tools" menu.
2038 QStringList menu_path = action->text().split('/');
2039 QMenu *cur_menu = main_ui_->menuTools;
2040 while (menu_path.length() > 1) {
2041 QString menu_title = menu_path.takeFirst();
2042 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2043 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2045 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2046 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2049 submenu = cur_menu->addMenu(menu_title);
2050 submenu->setObjectName(menu_title.toLower());
2054 action->setText(menu_path.last());
2055 cur_menu->addAction(action);
2059 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2063 // Connect each action type to its corresponding slot. We to
2064 // distinguish various types of actions. Setting their objectName
2065 // seems to work OK.
2066 if (action->objectName() == TapParameterDialog::actionName()) {
2067 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2068 } else if (action->objectName() == FunnelStatistics::actionName()) {
2069 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2073 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2075 foreach (QAction *action, actions) {
2076 switch (menu_group) {
2077 case REGISTER_ANALYZE_GROUP_UNSORTED:
2078 case REGISTER_STAT_GROUP_UNSORTED:
2079 main_ui_->menuStatistics->removeAction(action);
2081 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2082 main_ui_->menuServiceResponseTime->removeAction(action);
2084 case REGISTER_STAT_GROUP_TELEPHONY:
2085 main_ui_->menuTelephony->removeAction(action);
2087 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2088 main_ui_->menuANSI->removeAction(action);
2090 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2091 main_ui_->menuGSM->removeAction(action);
2093 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2094 main_ui_->menuMTP3->removeAction(action);
2096 case REGISTER_TOOLS_GROUP_UNSORTED:
2098 // Allow removal of submenus.
2099 // For now we limit the insanity to the "Tools" menu.
2100 QStringList menu_path = action->text().split('/');
2101 QMenu *cur_menu = main_ui_->menuTools;
2102 while (menu_path.length() > 1) {
2103 QString menu_title = menu_path.takeFirst();
2104 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2105 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2107 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2108 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2112 cur_menu->removeAction(action);
2116 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2122 void MainWindow::addDynamicMenus()
2125 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2126 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2128 // Fill in each menu
2129 foreach (register_stat_group_t menu_group, menu_groups) {
2130 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2131 addMenuActions(actions, menu_group);
2134 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2135 // We've added a placeholder in order to make sure the "Tools" menu is
2136 // visible. Hide it as needed.
2137 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2138 main_ui_->actionToolsPlaceholder->setVisible(false);
2140 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2141 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2143 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2144 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2146 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2147 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2151 void MainWindow::reloadDynamicMenus()
2153 foreach (register_stat_group_t menu_group, menu_groups) {
2154 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2155 removeMenuActions(actions, menu_group);
2157 actions = wsApp->addedMenuGroupItems(menu_group);
2158 addMenuActions(actions, menu_group);
2161 wsApp->clearAddedMenuGroupItems();
2162 wsApp->clearRemovedMenuGroupItems();
2165 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2167 QAction * itemAction = NULL;
2168 ext_menubar_t * item = NULL;
2169 GList * children = NULL;
2171 /* There must exists an xpath parent */
2172 g_assert(subMenu != NULL);
2174 /* If the depth counter exceeds, something must have gone wrong */
2175 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2177 children = menu->children;
2178 /* Iterate the child entries */
2179 while ( children != NULL && children->data != NULL )
2181 item = (ext_menubar_t *) children->data;
2183 if ( item->type == EXT_MENUBAR_MENU )
2185 /* Handle Submenu entry */
2186 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++ );
2188 else if ( item->type == EXT_MENUBAR_SEPARATOR )
2190 subMenu->addSeparator();
2192 else if ( item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL )
2194 itemAction = subMenu->addAction(item->name);
2195 itemAction->setData(QVariant::fromValue((void *)item));
2196 itemAction->setText(item->label);
2197 connect(itemAction, SIGNAL(triggered()),
2198 this, SLOT(externalMenuItem_triggered()));
2202 children = g_list_next(children);
2206 void MainWindow::addExternalMenus()
2208 QMenu * subMenu = NULL;
2209 GList * user_menu = NULL;
2210 ext_menu_t * menu = NULL;
2212 user_menu = ext_menubar_get_entries();
2214 while ( ( user_menu != NULL ) && ( user_menu->data != NULL ) )
2216 menu = (ext_menu_t *) user_menu->data;
2218 /* On this level only menu items should exist. Not doing an assert here,
2219 * as it could be an honest mistake */
2220 if ( menu->type != EXT_MENUBAR_MENU )
2222 user_menu = g_list_next(user_menu);
2226 /* Create main submenu and add it to the menubar */
2227 subMenu = main_ui_->menuBar->addMenu(menu->label);
2229 /* This will generate the action structure for each menu. It is recursive,
2230 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2231 this->externalMenuHelper(menu, subMenu, 0);
2234 user_menu = g_list_next (user_menu);
2244 * indent-tabs-mode: nil
2247 * ex: set shiftwidth=4 tabstop=8 expandtab:
2248 * :indentSize=4:tabSize=8:noTabs=true: