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_welcome_, SIGNAL(startCapture()),
439 this, SLOT(startCapture()));
440 connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
441 this, SLOT(openCaptureFile(QString&)));
442 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
443 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
444 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
445 main_ui_->statusBar, SLOT(popFilterStatus()));
447 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
448 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
449 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
450 this, SLOT(redissectPackets()));
451 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
452 this, SLOT(showPreferencesDialog(QString)));
453 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
454 this, SLOT(showPreferencesDialog(QString)));
456 connect(this, SIGNAL(setCaptureFile(capture_file*)),
457 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
458 connect(this, SIGNAL(setCaptureFile(capture_file*)),
459 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
460 connect(this, SIGNAL(setCaptureFile(capture_file*)),
461 packet_list_, SLOT(setCaptureFile(capture_file*)));
462 connect(this, SIGNAL(setCaptureFile(capture_file*)),
463 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
465 connect(this, SIGNAL(monospaceFontChanged(QFont)),
466 packet_list_, SLOT(setMonospaceFont(QFont)));
467 connect(this, SIGNAL(monospaceFontChanged(QFont)),
468 proto_tree_, SLOT(setMonospaceFont(QFont)));
469 connect(this, SIGNAL(monospaceFontChanged(QFont)),
470 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
472 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
473 packet_list_, SLOT(goNextPacket()));
474 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
475 packet_list_, SLOT(goPreviousPacket()));
476 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
477 packet_list_, SLOT(goFirstPacket()));
478 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
479 packet_list_, SLOT(goLastPacket()));
481 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
482 proto_tree_, SLOT(expandSubtrees()));
483 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
484 proto_tree_, SLOT(expandAll()));
485 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
486 proto_tree_, SLOT(collapseAll()));
488 connect(packet_list_, SIGNAL(packetSelectionChanged()),
489 this, SLOT(setMenusForSelectedPacket()));
490 connect(packet_list_, SIGNAL(packetDissectionChanged()),
491 this, SLOT(redissectPackets()));
492 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
493 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
494 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
495 this, SLOT(showPreferencesDialog(QString)));
496 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
497 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
498 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
499 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
500 packet_list_, SLOT(redrawVisiblePackets()));
501 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
502 this, SLOT(openPacketDialog()));
503 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
504 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
505 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
506 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
507 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
508 main_ui_->statusBar, SLOT(popBusyStatus()));
509 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
510 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
511 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
512 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
513 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
514 main_ui_->statusBar, SLOT(popProgressStatus()));
516 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
517 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
518 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
519 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
520 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
521 this, SLOT(openPacketDialog(bool)));
522 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
523 this, SLOT(showPreferencesDialog(QString)));
524 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
525 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
527 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
528 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
530 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
531 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
533 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
534 &capture_file_, SLOT(stopLoading()));
536 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
537 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
539 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
540 this, SLOT(openCaptureFile(QString&)));
543 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
545 connect(iface_tree, SIGNAL(itemSelectionChanged()),
546 this, SLOT(interfaceSelectionChanged()));
548 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
549 this, SLOT(captureFilterSyntaxChanged(bool)));
552 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
553 this, SLOT(showExtcapOptionsDialog(QString&)));
556 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
557 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
558 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
559 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
560 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
561 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
564 /* Create plugin_if hooks */
565 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter );
566 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter );
567 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
569 main_ui_->mainStack->setCurrentWidget(main_welcome_);
572 MainWindow::~MainWindow()
577 QString MainWindow::getFilter()
579 return df_combo_box_->itemText(df_combo_box_->count());
582 QMenu *MainWindow::createPopupMenu()
584 QMenu *menu = new QMenu();
585 menu->addAction(main_ui_->actionViewMainToolbar);
586 menu->addAction(main_ui_->actionViewFilterToolbar);
587 menu->addAction(main_ui_->actionViewWirelessToolbar);
588 menu->addAction(main_ui_->actionViewStatusBar);
589 menu->addSeparator();
590 menu->addAction(main_ui_->actionViewPacketList);
591 menu->addAction(main_ui_->actionViewPacketDetails);
592 menu->addAction(main_ui_->actionViewPacketBytes);
596 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
598 pipe_source_ = source;
599 pipe_child_process_ = child_process;
600 pipe_user_data_ = user_data;
601 pipe_input_cb_ = input_cb;
604 /* Tricky to use pipes in win9x, as no concept of wait. NT can
605 do this but that doesn't cover all win32 platforms. GTK can do
606 this but doesn't seem to work over processes. Attempt to do
607 something similar here, start a timer and check for data on every
609 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
612 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
616 pipe_timer_ = new QTimer(this);
617 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
618 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
619 pipe_timer_->start(200);
621 if (pipe_notifier_) {
622 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
623 delete pipe_notifier_;
626 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
627 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
628 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
629 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
633 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
635 // The user typed some text. Start filling in a filter.
636 // We may need to be more choosy here. We just need to catch events for the packet list,
637 // proto tree, and main welcome widgets.
638 if (event->type() == QEvent::KeyPress) {
639 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
640 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
641 df_combo_box_->lineEdit()->insert(kevt->text());
642 df_combo_box_->lineEdit()->setFocus();
647 return QMainWindow::eventFilter(obj, event);
650 void MainWindow::keyPressEvent(QKeyEvent *event) {
652 // Explicitly focus on the display filter combo.
653 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
654 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
658 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
659 if (event->modifiers() == Qt::NoModifier) {
660 if (event->key() == Qt::Key_Escape) {
661 on_goToCancel_clicked();
662 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
666 return; // goToLineEdit didn't want it and we don't either.
669 // Move up & down the packet list.
670 if (event->key() == Qt::Key_F7) {
671 packet_list_->goPreviousPacket();
672 } else if (event->key() == Qt::Key_F8) {
673 packet_list_->goNextPacket();
676 // Move along, citizen.
677 QMainWindow::keyPressEvent(event);
680 void MainWindow::closeEvent(QCloseEvent *event) {
681 saveWindowGeometry();
683 /* If we're in the middle of stopping a capture, don't do anything;
684 the user can try deleting the window after the capture stops. */
685 if (capture_stopping_) {
690 QString before_what(tr(" before quitting"));
691 if (!testCaptureFileClose(TRUE, before_what)) {
697 capture_interfaces_dialog_.close();
699 // Make sure we kill any open dumpcap processes.
700 delete main_welcome_;
702 // One of the many places we assume one main window.
703 if(!wsApp->isInitialized()) {
704 // If we're still initializing, QCoreApplication::quit() won't
705 // exit properly because we are not in the event loop. This
706 // means that the application won't clean up after itself. We
707 // might want to call wsApp->processEvents() during startup
708 // instead so that we can do a normal exit here.
714 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
717 foreach (QUrl drag_url, event->mimeData()->urls()) {
718 if (!drag_url.toLocalFile().isEmpty()) {
723 if (accept) event->acceptProposedAction();
726 void MainWindow::dropEvent(QDropEvent *event)
728 foreach (QUrl drop_url, event->mimeData()->urls()) {
729 QString local_file = drop_url.toLocalFile();
730 if (!local_file.isEmpty()) {
731 event->acceptProposedAction();
732 openCaptureFile(local_file);
738 // Apply recent settings to the main window geometry.
739 // We haven't loaded the preferences at this point so we assume that the
740 // position and size preference are enabled.
741 void MainWindow::loadWindowGeometry()
743 int min_sensible_dimension_ = 200;
746 if (recent.gui_geometry_main_maximized) {
747 setWindowState(Qt::WindowMaximized);
751 // if (prefs.gui_geometry_save_position) {
752 move(recent.gui_geometry_main_x, recent.gui_geometry_main_y);
755 if (// prefs.gui_geometry_save_size &&
756 recent.gui_geometry_main_width > min_sensible_dimension_ &&
757 recent.gui_geometry_main_height > min_sensible_dimension_) {
758 resize(recent.gui_geometry_main_width, recent.gui_geometry_main_height);
763 void MainWindow::saveWindowGeometry()
765 if (prefs.gui_geometry_save_position) {
766 recent.gui_geometry_main_x = pos().x();
767 recent.gui_geometry_main_y = pos().y();
770 if (prefs.gui_geometry_save_size) {
771 recent.gui_geometry_main_width = size().width();
772 recent.gui_geometry_main_height = size().height();
775 if (prefs.gui_geometry_save_maximized) {
776 // On OS X this is false when it shouldn't be
777 recent.gui_geometry_main_maximized = isMaximized();
780 if (master_split_.sizes().length() > 0) {
781 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
784 if (master_split_.sizes().length() > 2) {
785 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
786 } else if (extra_split_.sizes().length() > 0) {
787 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
791 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
793 case layout_pane_content_none:
795 case layout_pane_content_plist:
797 case layout_pane_content_pdetails:
799 case layout_pane_content_pbytes:
800 return byte_view_tab_;
802 g_assert_not_reached();
807 void MainWindow::mergeCaptureFile()
809 QString file_name = "";
810 QString display_filter = "";
811 dfilter_t *rfcode = NULL;
814 if (!capture_file_.capFile())
817 if (prefs.gui_ask_unsaved) {
818 if (cf_has_unsaved_data(capture_file_.capFile())) {
819 QMessageBox msg_dialog;
820 gchar *display_basename;
823 msg_dialog.setIcon(QMessageBox::Question);
824 /* This file has unsaved data; ask the user whether to save
826 if (capture_file_.capFile()->is_tempfile) {
827 msg_dialog.setText(tr("Save packets before merging?"));
828 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
831 * Format the message.
833 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
834 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
835 g_free(display_basename);
836 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
839 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
840 msg_dialog.setDefaultButton(QMessageBox::Save);
842 response = msg_dialog.exec();
846 case QMessageBox::Save:
847 /* Save the file but don't close it */
848 saveCaptureFile(capture_file_.capFile(), FALSE);
851 case QMessageBox::Cancel:
853 /* Don't do the merge. */
860 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), display_filter);
862 cf_status_t merge_status;
863 char *in_filenames[2];
866 switch (prefs.gui_fileopen_style) {
868 case FO_STYLE_LAST_OPENED:
869 /* The user has specified that we should start out in the last directory
870 we looked in. If we've already opened a file, use its containing
871 directory, if we could determine it, as the directory, otherwise
872 use the "last opened" directory saved in the preferences file if
874 /* This is now the default behaviour in file_selection_new() */
877 case FO_STYLE_SPECIFIED:
878 /* The user has specified that we should always start out in a
879 specified directory; if they've specified that directory,
880 start out by showing the files in that dir. */
881 if (prefs.gui_fileopen_dir[0] != '\0')
882 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
886 if (merge_dlg.merge(file_name)) {
889 if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode, &err_msg)) {
890 cf_set_rfcode(capture_file_.capFile(), rfcode);
892 /* Not valid. Tell the user, and go back and run the file
893 selection box again once they dismiss the alert. */
894 //bad_dfilter_alert_box(top_level, display_filter->str);
895 QMessageBox::warning(this, tr("Invalid Display Filter"),
896 QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, err_msg)),
905 file_type = capture_file_.capFile()->cd_t;
907 /* Try to merge or append the two files */
909 if (merge_dlg.mergeType() == 0) {
910 /* chronological order */
911 in_filenames[0] = capture_file_.capFile()->filename;
912 in_filenames[1] = file_name.toUtf8().data();
913 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
914 } else if (merge_dlg.mergeType() <= 0) {
916 in_filenames[0] = file_name.toUtf8().data();
917 in_filenames[1] = capture_file_.capFile()->filename;
918 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
921 in_filenames[0] = capture_file_.capFile()->filename;
922 in_filenames[1] = file_name.toUtf8().data();
923 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
926 if (merge_status != CF_OK) {
928 dfilter_free(rfcode);
933 cf_close(capture_file_.capFile());
935 /* Try to open the merged capture file. */
936 CaptureFile::globalCapFile()->window = this;
937 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
938 /* We couldn't open it; fail. */
939 CaptureFile::globalCapFile()->window = NULL;
941 dfilter_free(rfcode);
946 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
947 it closed the previous capture file, and thus destroyed any
948 previous read filter attached to "cf"). */
949 CaptureFile::globalCapFile()->rfcode = rfcode;
951 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
955 /* Just because we got an error, that doesn't mean we were unable
956 to read any of the file; we handle what we could get from the
960 case CF_READ_ABORTED:
961 /* The user bailed out of re-reading the capture file; the
962 capture file has been closed - just free the capture file name
963 string and return (without changing the last containing
969 /* Save the name of the containing directory specified in the path name,
970 if any; we can write over cf_merged_name, which is a good thing, given that
971 "get_dirname()" does write over its argument. */
972 wsApp->setLastOpenDir(get_dirname(tmpname));
974 df_combo_box_->setEditText(display_filter);
975 main_ui_->statusBar->showExpert();
981 void MainWindow::importCaptureFile() {
982 ImportTextDialog import_dlg;
984 QString before_what(tr(" before importing a new capture"));
985 if (!testCaptureFileClose(FALSE, before_what))
990 if (import_dlg.result() != QDialog::Accepted) {
991 main_ui_->mainStack->setCurrentWidget(main_welcome_);
995 openCaptureFile(import_dlg.capfileName());
998 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1000 gboolean discard_comments;
1002 if (cf->is_tempfile) {
1003 /* This is a temporary capture file, so saving it means saving
1004 it to a permanent file. Prompt the user for a location
1005 to which to save it. Don't require that the file format
1006 support comments - if it's a temporary capture file, it's
1007 probably pcap-ng, which supports comments and, if it's
1008 not pcap-ng, let the user decide what they want to do
1009 if they've added comments. */
1010 saveAsCaptureFile(cf, FALSE, dont_reopen);
1012 if (cf->unsaved_changes) {
1013 cf_write_status_t status;
1015 /* This is not a temporary capture file, but it has unsaved
1016 changes, so saving it means doing a "safe save" on top
1017 of the existing file, in the same format - no UI needed
1018 unless the file has comments and the file's format doesn't
1021 If the file has comments, does the file's format support them?
1022 If not, ask the user whether they want to discard the comments
1023 or choose a different format. */
1024 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1027 /* The file can be saved in the specified format as is;
1028 just drive on and save in the format they selected. */
1029 discard_comments = FALSE;
1032 case SAVE_WITHOUT_COMMENTS:
1033 /* The file can't be saved in the specified format as is,
1034 but it can be saved without the comments, and the user
1035 said "OK, discard the comments", so save it in the
1036 format they specified without the comments. */
1037 discard_comments = TRUE;
1040 case SAVE_IN_ANOTHER_FORMAT:
1041 /* There are file formats in which we can save this that
1042 support comments, and the user said not to delete the
1043 comments. Do a "Save As" so the user can select
1044 one of those formats and choose a file name. */
1045 saveAsCaptureFile(cf, TRUE, dont_reopen);
1049 /* The user said "forget it". Just return. */
1053 /* Squelch warnings that discard_comments is being used
1055 g_assert_not_reached();
1059 /* XXX - cf->filename might get freed out from under us, because
1060 the code path through which cf_save_records() goes currently
1061 closes the current file and then opens and reloads the saved file,
1062 so make a copy and free it later. */
1063 file_name = cf->filename;
1064 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1065 discard_comments, dont_reopen);
1069 /* The save succeeded; we're done.
1070 If we discarded comments, redraw the packet list to reflect
1071 any packets that no longer have comments. */
1072 if (discard_comments)
1073 packet_list_queue_draw();
1075 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1076 updateForUnsavedChanges(); // we update the title bar to remove the *
1079 case CF_WRITE_ERROR:
1080 /* The write failed.
1081 XXX - OK, what do we do now? Let them try a
1082 "Save As", in case they want to try to save to a
1083 different directory r file system? */
1086 case CF_WRITE_ABORTED:
1087 /* The write was aborted; just drive on. */
1091 /* Otherwise just do nothing. */
1095 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1096 QString file_name = "";
1098 gboolean compressed;
1099 cf_write_status_t status;
1101 gboolean discard_comments = FALSE;
1108 CaptureFileDialog save_as_dlg(this, cf);
1110 switch (prefs.gui_fileopen_style) {
1112 case FO_STYLE_LAST_OPENED:
1113 /* The user has specified that we should start out in the last directory
1114 we looked in. If we've already opened a file, use its containing
1115 directory, if we could determine it, as the directory, otherwise
1116 use the "last opened" directory saved in the preferences file if
1118 /* This is now the default behaviour in file_selection_new() */
1121 case FO_STYLE_SPECIFIED:
1122 /* The user has specified that we should always start out in a
1123 specified directory; if they've specified that directory,
1124 start out by showing the files in that dir. */
1125 if (prefs.gui_fileopen_dir[0] != '\0')
1126 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1130 /* If the file has comments, does the format the user selected
1131 support them? If not, ask the user whether they want to
1132 discard the comments or choose a different format. */
1133 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1136 /* The file can be saved in the specified format as is;
1137 just drive on and save in the format they selected. */
1138 discard_comments = FALSE;
1141 case SAVE_WITHOUT_COMMENTS:
1142 /* The file can't be saved in the specified format as is,
1143 but it can be saved without the comments, and the user
1144 said "OK, discard the comments", so save it in the
1145 format they specified without the comments. */
1146 discard_comments = TRUE;
1149 case SAVE_IN_ANOTHER_FORMAT:
1150 /* There are file formats in which we can save this that
1151 support comments, and the user said not to delete the
1152 comments. The combo box of file formats has had the
1153 formats that don't support comments trimmed from it,
1154 so run the dialog again, to let the user decide
1155 whether to save in one of those formats or give up. */
1156 must_support_comments = TRUE;
1160 /* The user said "forget it". Just get rid of the dialog box
1164 file_type = save_as_dlg.selectedFileType();
1165 compressed = save_as_dlg.isCompressed();
1167 fileAddExtension(file_name, file_type, compressed);
1170 // /* If the file exists and it's user-immutable or not writable,
1171 // ask the user whether they want to override that. */
1172 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1173 // /* They don't. Let them try another file name or cancel. */
1178 /* Attempt to save the file */
1179 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1180 discard_comments, dont_reopen);
1184 /* The save succeeded; we're done. */
1185 /* Save the directory name for future file dialogs. */
1186 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1187 set_last_open_dir(dirname);
1189 /* If we discarded comments, redraw the packet list to reflect
1190 any packets that no longer have comments. */
1191 if (discard_comments)
1192 packet_list_queue_draw();
1194 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1195 updateForUnsavedChanges(); // we update the title bar to remove the *
1198 case CF_WRITE_ERROR:
1199 /* The save failed; let the user try again. */
1202 case CF_WRITE_ABORTED:
1203 /* The user aborted the save; just return. */
1210 void MainWindow::exportSelectedPackets() {
1211 QString file_name = "";
1213 gboolean compressed;
1214 packet_range_t range;
1215 cf_write_status_t status;
1217 gboolean discard_comments = FALSE;
1219 if (!capture_file_.capFile())
1222 /* Init the packet range */
1223 packet_range_init(&range, capture_file_.capFile());
1224 range.process_filtered = TRUE;
1225 range.include_dependents = TRUE;
1228 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1230 switch (prefs.gui_fileopen_style) {
1232 case FO_STYLE_LAST_OPENED:
1233 /* The user has specified that we should start out in the last directory
1234 we looked in. If we've already opened a file, use its containing
1235 directory, if we could determine it, as the directory, otherwise
1236 use the "last opened" directory saved in the preferences file if
1238 /* This is now the default behaviour in file_selection_new() */
1241 case FO_STYLE_SPECIFIED:
1242 /* The user has specified that we should always start out in a
1243 specified directory; if they've specified that directory,
1244 start out by showing the files in that dir. */
1245 if (prefs.gui_fileopen_dir[0] != '\0')
1246 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1250 /* If the file has comments, does the format the user selected
1251 support them? If not, ask the user whether they want to
1252 discard the comments or choose a different format. */
1253 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1256 /* The file can be saved in the specified format as is;
1257 just drive on and save in the format they selected. */
1258 discard_comments = FALSE;
1261 case SAVE_WITHOUT_COMMENTS:
1262 /* The file can't be saved in the specified format as is,
1263 but it can be saved without the comments, and the user
1264 said "OK, discard the comments", so save it in the
1265 format they specified without the comments. */
1266 discard_comments = TRUE;
1269 case SAVE_IN_ANOTHER_FORMAT:
1270 /* There are file formats in which we can save this that
1271 support comments, and the user said not to delete the
1272 comments. The combo box of file formats has had the
1273 formats that don't support comments trimmed from it,
1274 so run the dialog again, to let the user decide
1275 whether to save in one of those formats or give up. */
1279 /* The user said "forget it". Just get rid of the dialog box
1285 * Check that we're not going to save on top of the current
1287 * We do it here so we catch all cases ...
1288 * Unfortunately, the file requester gives us an absolute file
1289 * name and the read file name may be relative (if supplied on
1290 * the command line). From Joerg Mayer.
1292 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1293 QMessageBox msg_box;
1294 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1296 msg_box.setIcon(QMessageBox::Critical);
1297 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1298 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1299 msg_box.setStandardButtons(QMessageBox::Ok);
1300 msg_box.setDefaultButton(QMessageBox::Ok);
1302 g_free(display_basename);
1306 file_type = esp_dlg.selectedFileType();
1307 compressed = esp_dlg.isCompressed();
1308 fileAddExtension(file_name, file_type, compressed);
1311 // /* If the file exists and it's user-immutable or not writable,
1312 // ask the user whether they want to override that. */
1313 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1314 // /* They don't. Let them try another file name or cancel. */
1319 /* Attempt to save the file */
1320 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1324 /* The save succeeded; we're done. */
1325 /* Save the directory name for future file dialogs. */
1326 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1327 set_last_open_dir(dirname);
1329 /* If we discarded comments, redraw the packet list to reflect
1330 any packets that no longer have comments. */
1331 if (discard_comments)
1332 packet_list_queue_draw();
1335 case CF_WRITE_ERROR:
1336 /* The save failed; let the user try again. */
1339 case CF_WRITE_ABORTED:
1340 /* The user aborted the save; just return. */
1347 void MainWindow::exportDissections(export_type_e export_type) {
1348 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1349 packet_range_t range;
1351 if (!capture_file_.capFile())
1354 /* Init the packet range */
1355 packet_range_init(&range, capture_file_.capFile());
1356 range.process_filtered = TRUE;
1357 range.include_dependents = TRUE;
1362 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1363 QString file_name_lower;
1364 QString file_suffix;
1365 GSList *extensions_list;
1366 gboolean add_extension;
1369 * Append the default file extension if there's none given by
1370 * the user or if they gave one that's not one of the valid
1371 * extensions for the file type.
1373 file_name_lower = file_name.toLower();
1374 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1375 if (extensions_list != NULL) {
1378 /* We have one or more extensions for this file type.
1379 Start out assuming we need to add the default one. */
1380 add_extension = TRUE;
1382 /* OK, see if the file has one of those extensions. */
1383 for (extension = extensions_list; extension != NULL;
1384 extension = g_slist_next(extension)) {
1385 file_suffix += tr(".") + (char *)extension->data;
1386 if (file_name_lower.endsWith(file_suffix)) {
1388 * The file name has one of the extensions for
1391 add_extension = FALSE;
1394 file_suffix += ".gz";
1395 if (file_name_lower.endsWith(file_suffix)) {
1397 * The file name has one of the extensions for
1400 add_extension = FALSE;
1405 /* We have no extensions for this file type. Don't add one. */
1406 add_extension = FALSE;
1408 if (add_extension) {
1409 if (wtap_default_file_extension(file_type) != NULL) {
1410 file_name += tr(".") + wtap_default_file_extension(file_type);
1418 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
1419 bool capture_in_progress = FALSE;
1421 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1422 return true; /* Already closed, nothing to do */
1425 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS && capture_file_.capFile()->count>0) {
1426 /* This is true if we're reading a capture file *or* if we're doing
1427 a live capture. If we're reading a capture file, the main loop
1428 is busy reading packets, and only accepting input from the
1429 progress dialog, so we can't get here, so this means we're
1431 capture_in_progress = TRUE;
1435 if (prefs.gui_ask_unsaved) {
1436 if (cf_has_unsaved_data(capture_file_.capFile()) || capture_in_progress) {
1437 QMessageBox msg_dialog;
1439 QPushButton *saveButton;
1440 QPushButton *discardButton;
1442 msg_dialog.setIcon(QMessageBox::Question);
1443 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1444 /* This file has unsaved data or there's a capture in
1445 progress; ask the user whether to save the data. */
1446 if (capture_file_.capFile()->is_tempfile) {
1448 msg_dialog.setText(tr("You have unsaved packets"));
1449 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1451 if (capture_in_progress) {
1452 question.append(tr("Do you want to stop the capture and save the captured packets"));
1454 question.append(tr("Do you want to save the captured packets"));
1456 question.append(before_what).append(tr("?"));
1457 msg_dialog.setInformativeText(question);
1462 * Format the message.
1464 if (capture_in_progress) {
1465 question.append(tr("Do you want to stop the capture and save the captured packets"));
1466 question.append(before_what).append(tr("?"));
1467 msg_dialog.setText(question);
1468 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1470 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1471 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1472 .arg(display_basename)
1475 g_free(display_basename);
1476 msg_dialog.setText(question);
1477 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1481 // XXX Text comes from ui/gtk/stock_icons.[ch]
1482 // Note that the button roles differ from the GTK+ version.
1483 // Cancel = RejectRole
1484 // Save = AcceptRole
1485 // Don't Save = DestructiveRole
1486 msg_dialog.addButton(QMessageBox::Cancel);
1488 if (capture_in_progress) {
1489 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1491 saveButton = msg_dialog.addButton(QMessageBox::Save);
1493 msg_dialog.setDefaultButton(saveButton);
1496 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1497 discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1498 QMessageBox::DestructiveRole);
1500 discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1501 QMessageBox::DestructiveRole);
1504 if (capture_in_progress) {
1505 discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1506 QMessageBox::DestructiveRole);
1508 discardButton = msg_dialog.addButton(tr("Continue &without Saving"), QMessageBox::DestructiveRole);
1513 /* According to the Qt doc:
1514 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1516 * Therefore we should use clickedButton() to determine which button was clicked. */
1518 if(msg_dialog.clickedButton() == saveButton)
1521 /* If there's a capture in progress, we have to stop the capture
1522 and then do the save. */
1523 if (capture_in_progress)
1526 /* Save the file and close it */
1527 saveCaptureFile(capture_file_.capFile(), TRUE);
1529 else if(msg_dialog.clickedButton() == discardButton)
1533 * If there's a capture in progress; we have to stop the capture
1534 * and then do the close.
1536 if (capture_in_progress)
1539 /* Just close the file, discarding changes */
1540 cf_close(capture_file_.capFile());
1543 else //cancelButton or some other unspecified button
1549 /* Unchanged file, just close it */
1550 capture_file_.capFile()->state = FILE_READ_ABORTED;
1551 cf_close(capture_file_.capFile());
1554 /* User asked not to be bothered by those prompts, just close it.
1555 XXX - should that apply only to saving temporary files? */
1557 /* If there's a capture in progress, we have to stop the capture
1558 and then do the close. */
1559 if (capture_in_progress)
1562 cf_close(capture_file_.capFile());
1565 return true; /* File closed */
1568 void MainWindow::captureStop() {
1571 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1572 WiresharkApplication::processEvents();
1576 void MainWindow::initMainToolbarIcons()
1578 #if defined(Q_OS_WIN)
1579 // Current GTK+ and other Windows app behavior.
1580 main_ui_->mainToolBar->setIconSize(QSize(16, 16));
1582 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1583 main_ui_->mainToolBar->setIconSize(QSize(24, 24));
1586 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1587 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1589 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1590 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1591 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1592 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1594 // Menu icons are disabled in main_window.ui for these items.
1595 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1596 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1597 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1598 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1600 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1601 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1602 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1603 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1604 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1605 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1606 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1608 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1609 main_ui_->actionViewColorizePacketList->setChecked(recent.packet_list_colorize);
1610 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1612 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1613 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1614 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1615 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1616 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1617 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1618 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1621 void MainWindow::initShowHideMainWidgets()
1623 if (show_hide_actions_) {
1627 show_hide_actions_ = new QActionGroup(this);
1628 QMap<QAction *, QWidget *> shmw_actions;
1630 show_hide_actions_->setExclusive(false);
1631 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1632 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1633 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1634 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1635 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1636 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1637 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1639 main_ui_->actionViewMainToolbar->setChecked(recent.main_toolbar_show);
1640 main_ui_->actionViewFilterToolbar->setChecked(recent.filter_toolbar_show);
1641 main_ui_->actionViewWirelessToolbar->setChecked(recent.wireless_toolbar_show);
1642 main_ui_->actionViewStatusBar->setChecked(recent.statusbar_show);
1643 main_ui_->actionViewPacketList->setChecked(recent.packet_list_show);
1644 main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show);
1645 main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show);
1647 foreach (QAction *shmwa, shmw_actions.keys()) {
1648 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1649 show_hide_actions_->addAction(shmwa);
1650 showHideMainWidgets(shmwa);
1653 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1656 Q_DECLARE_METATYPE(ts_type)
1658 void MainWindow::initTimeDisplayFormatMenu()
1660 if (time_display_actions_) {
1664 time_display_actions_ = new QActionGroup(this);
1665 QMap<QAction *, ts_type> td_actions;
1667 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1668 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1669 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1670 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1671 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1672 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1673 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1674 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1675 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1676 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1678 foreach (QAction* tda, td_actions.keys()) {
1679 tda->setData(qVariantFromValue(td_actions[tda]));
1680 time_display_actions_->addAction(tda);
1681 if (recent.gui_time_format == td_actions[tda]) {
1682 tda->setChecked(true);
1686 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1688 main_ui_->actionViewTimeDisplaySecondsWithHoursAndMinutes->setChecked(recent.gui_seconds_format == TS_SECONDS_HOUR_MIN_SEC);
1691 Q_DECLARE_METATYPE(ts_precision)
1693 void MainWindow::initTimePrecisionFormatMenu()
1695 if (time_precision_actions_) {
1699 time_precision_actions_ = new QActionGroup(this);
1700 QMap<QAction *, ts_precision> tp_actions;
1701 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1702 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1703 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1704 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1705 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1706 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1707 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1709 foreach (QAction* tpa, tp_actions.keys()) {
1710 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1711 time_precision_actions_->addAction(tpa);
1712 if (recent.gui_time_precision == tp_actions[tpa]) {
1713 tpa->setChecked(true);
1717 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1721 void MainWindow::setTitlebarForCaptureFile()
1723 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1725 // Qt *REALLY* doesn't like windows that sometimes have a
1726 // title set with setWindowTitle() and other times have a
1727 // file path set; apparently, once you've set the title
1728 // with setWindowTitle(), it sticks, and setWindowFilePath()
1729 // has no effect. It appears to can clear the title with
1730 // setWindowTitle(NULL), but that clears the actual title in
1731 // the title bar, and setWindowFilePath() then, I guess, sees
1732 // that there's already a file path, and does nothing, leaving
1733 // the title bar empty. So you then have to clear the file path
1734 // with setWindowFilePath(NULL), and then set it.
1736 // Maybe there's a #include "you're holding it wrong" here.
1737 // However, I really don't want to hear from people who think
1738 // that a window can never be associated with something other
1739 // than a user file at time T1 and with a user file at time T2,
1740 // given that, in Wireshark, a window can be associated with a
1741 // live capture at time T1 and then, after you've saved the live
1742 // capture to a user file, associated with a user file at time T2.
1744 if (capture_file_.capFile()->is_tempfile) {
1746 // For a temporary file, put the source of the data
1747 // in the window title, not whatever random pile
1748 // of characters is the last component of the path
1751 // XXX - on non-Mac platforms, put in the application
1754 // XXX - Use setWindowModified
1756 setWindowFilePath(NULL);
1757 window_name = g_strdup_printf("[*]%s", cf_get_tempfile_source(capture_file_.capFile())); //TODO : Fix Translate
1758 setWindowTitle(window_name);
1759 g_free(window_name);
1762 // For a user file, set the full path; that way,
1763 // for OS X, it'll set the "proxy icon". Qt
1764 // handles extracting the last component.
1766 // Sadly, some UN*Xes don't necessarily use UTF-8
1767 // for their file names, so we have to map the
1768 // file path to UTF-8. If that fails, we're somewhat
1771 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1776 if (utf8_filename == NULL) {
1777 // So what the heck else can we do here?
1778 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1780 setWindowTitle(NULL);
1781 setWindowFilePath(NULL);
1782 setWindowFilePath(utf8_filename);
1783 g_free(utf8_filename);
1786 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1788 /* We have no capture file. */
1789 setWindowFilePath(NULL);
1790 setWindowTitle(tr("The Wireshark Network Analyzer"));
1794 void MainWindow::setTitlebarForSelectedTreeRow()
1796 setWindowTitle(tr("The Wireshark Network Analyzer"));
1800 void MainWindow::setTitlebarForCaptureInProgress()
1804 setWindowFilePath(NULL);
1805 if (capture_file_.capFile()) {
1806 window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(capture_file_.capFile())); //TODO : Fix Translate
1807 setWindowTitle(window_name);
1808 g_free(window_name);
1810 /* We have no capture in progress. */
1811 setWindowTitle(tr("The Wireshark Network Analyzer"));
1817 /* Enable or disable menu items based on whether you have a capture file
1818 you've finished reading and, if you have one, whether it's been saved
1819 and whether it could be saved except by copying the raw packet data. */
1820 void MainWindow::setMenusForCaptureFile(bool force_disable)
1822 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1823 /* We have no capture file or we're currently reading a file */
1824 main_ui_->actionFileMerge->setEnabled(false);
1825 main_ui_->actionFileClose->setEnabled(false);
1826 main_ui_->actionFileSave->setEnabled(false);
1827 main_ui_->actionFileSaveAs->setEnabled(false);
1828 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1829 main_ui_->actionFileExportPackets->setEnabled(false);
1830 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1831 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1832 main_ui_->actionFileExportPDU->setEnabled(false);
1833 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1834 main_ui_->menuFileExportObjects->setEnabled(false);
1835 main_ui_->actionViewReload->setEnabled(false);
1837 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1839 main_ui_->actionFileClose->setEnabled(true);
1840 main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1841 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1842 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1844 * "Export Specified Packets..." should be available only if
1845 * we can write the file out in at least one format.
1847 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1848 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1849 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1850 main_ui_->actionFileExportPDU->setEnabled(true);
1851 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1852 main_ui_->menuFileExportObjects->setEnabled(true);
1853 main_ui_->actionViewReload->setEnabled(true);
1857 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1858 /* Either a capture was started or stopped; in either case, it's not
1859 in the process of stopping, so allow quitting. */
1861 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1862 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1863 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1864 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1865 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1866 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1867 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1868 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1869 main_ui_->actionFileQuit->setEnabled(true);
1871 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1873 // XXX Fix packet list heading menu sensitivity
1874 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1875 // !capture_in_progress);
1876 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1877 // !capture_in_progress);
1878 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1879 // !capture_in_progress);
1882 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1883 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1884 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1885 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1886 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1887 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1888 #endif /* HAVE_LIBPCAP */
1892 void MainWindow::setMenusForCaptureStopping() {
1893 main_ui_->actionFileQuit->setEnabled(false);
1894 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1896 main_ui_->actionCaptureStart->setChecked(false);
1897 main_ui_->actionCaptureStop->setEnabled(false);
1898 main_ui_->actionCaptureRestart->setEnabled(false);
1899 #endif /* HAVE_LIBPCAP */
1902 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1904 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1906 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1907 // have_captured_packets);
1909 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
1910 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
1911 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
1913 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1914 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1915 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1916 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1917 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1919 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
1920 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
1921 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
1922 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
1924 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1925 // have_captured_packets);
1926 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1927 // have_captured_packets);
1928 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
1929 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
1930 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
1933 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1934 bool enable_next = fileset_get_next() != NULL && enable_list_files;
1935 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1937 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1938 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1939 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1942 void MainWindow::updateForUnsavedChanges() {
1943 setTitlebarForCaptureFile();
1944 setMenusForCaptureFile();
1947 void MainWindow::changeEvent(QEvent* event)
1951 switch (event->type())
1953 case QEvent::LanguageChange:
1954 main_ui_->retranslateUi(this);
1956 case QEvent::LocaleChange:{
1957 QString locale = QLocale::system().name();
1958 locale.truncate(locale.lastIndexOf('_'));
1959 wsApp->loadLanguage(locale);
1966 QMainWindow::changeEvent(event);
1969 /* Update main window items based on whether there's a capture in progress. */
1970 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1972 setMenusForCaptureInProgress(capture_in_progress);
1974 wireless_frame_->setCaptureInProgress(capture_in_progress);
1977 packet_list_->setCaptureInProgress(capture_in_progress);
1978 // set_toolbar_for_capture_in_progress(capture_in_progress);
1980 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1984 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
1985 << REGISTER_ANALYZE_GROUP_UNSORTED
1986 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
1987 << REGISTER_STAT_GROUP_UNSORTED
1988 << REGISTER_STAT_GROUP_GENERIC
1989 << REGISTER_STAT_GROUP_CONVERSATION_LIST
1990 << REGISTER_STAT_GROUP_ENDPOINT_LIST
1991 << REGISTER_STAT_GROUP_RESPONSE_TIME
1992 << REGISTER_STAT_GROUP_TELEPHONY
1993 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
1994 << REGISTER_STAT_GROUP_TELEPHONY_GSM
1995 << REGISTER_STAT_GROUP_TELEPHONY_LTE
1996 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
1997 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
1998 << REGISTER_TOOLS_GROUP_UNSORTED;
2000 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2002 foreach (QAction *action, actions) {
2003 switch (menu_group) {
2004 case REGISTER_ANALYZE_GROUP_UNSORTED:
2005 case REGISTER_STAT_GROUP_UNSORTED:
2006 main_ui_->menuStatistics->insertAction(
2007 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2010 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2011 main_ui_->menuServiceResponseTime->addAction(action);
2013 case REGISTER_STAT_GROUP_TELEPHONY:
2014 main_ui_->menuTelephony->addAction(action);
2016 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2017 main_ui_->menuANSI->addAction(action);
2019 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2020 main_ui_->menuGSM->addAction(action);
2022 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2023 main_ui_->menuMTP3->addAction(action);
2025 case REGISTER_TOOLS_GROUP_UNSORTED:
2027 // Allow the creation of submenus. Mimics the behavor of
2028 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2029 // and GtkUIManager.
2031 // For now we limit the insanity to the "Tools" menu.
2032 QStringList menu_path = action->text().split('/');
2033 QMenu *cur_menu = main_ui_->menuTools;
2034 while (menu_path.length() > 1) {
2035 QString menu_title = menu_path.takeFirst();
2036 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2037 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2039 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2040 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2043 submenu = cur_menu->addMenu(menu_title);
2044 submenu->setObjectName(menu_title.toLower());
2048 action->setText(menu_path.last());
2049 cur_menu->addAction(action);
2053 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2057 // Connect each action type to its corresponding slot. We to
2058 // distinguish various types of actions. Setting their objectName
2059 // seems to work OK.
2060 if (action->objectName() == TapParameterDialog::actionName()) {
2061 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2062 } else if (action->objectName() == FunnelStatistics::actionName()) {
2063 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2067 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2069 foreach (QAction *action, actions) {
2070 switch (menu_group) {
2071 case REGISTER_ANALYZE_GROUP_UNSORTED:
2072 case REGISTER_STAT_GROUP_UNSORTED:
2073 main_ui_->menuStatistics->removeAction(action);
2075 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2076 main_ui_->menuServiceResponseTime->removeAction(action);
2078 case REGISTER_STAT_GROUP_TELEPHONY:
2079 main_ui_->menuTelephony->removeAction(action);
2081 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2082 main_ui_->menuANSI->removeAction(action);
2084 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2085 main_ui_->menuGSM->removeAction(action);
2087 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2088 main_ui_->menuMTP3->removeAction(action);
2090 case REGISTER_TOOLS_GROUP_UNSORTED:
2092 // Allow removal of submenus.
2093 // For now we limit the insanity to the "Tools" menu.
2094 QStringList menu_path = action->text().split('/');
2095 QMenu *cur_menu = main_ui_->menuTools;
2096 while (menu_path.length() > 1) {
2097 QString menu_title = menu_path.takeFirst();
2098 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2099 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2101 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2102 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2106 cur_menu->removeAction(action);
2110 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2116 void MainWindow::addDynamicMenus()
2119 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2120 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2122 // Fill in each menu
2123 foreach (register_stat_group_t menu_group, menu_groups) {
2124 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2125 addMenuActions(actions, menu_group);
2128 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2129 // We've added a placeholder in order to make sure the "Tools" menu is
2130 // visible. Hide it as needed.
2131 if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2132 main_ui_->actionToolsPlaceholder->setVisible(false);
2134 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2135 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2137 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2138 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2140 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2141 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2145 void MainWindow::reloadDynamicMenus()
2147 foreach (register_stat_group_t menu_group, menu_groups) {
2148 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2149 removeMenuActions(actions, menu_group);
2151 actions = wsApp->addedMenuGroupItems(menu_group);
2152 addMenuActions(actions, menu_group);
2155 wsApp->clearAddedMenuGroupItems();
2156 wsApp->clearRemovedMenuGroupItems();
2159 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2161 QAction * itemAction = NULL;
2162 ext_menubar_t * item = NULL;
2163 GList * children = NULL;
2165 /* There must exists an xpath parent */
2166 g_assert(subMenu != NULL);
2168 /* If the depth counter exceeds, something must have gone wrong */
2169 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2171 children = menu->children;
2172 /* Iterate the child entries */
2173 while ( children != NULL && children->data != NULL )
2175 item = (ext_menubar_t *) children->data;
2177 if ( item->type == EXT_MENUBAR_MENU )
2179 /* Handle Submenu entry */
2180 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++ );
2182 else if ( item->type == EXT_MENUBAR_SEPARATOR )
2184 subMenu->addSeparator();
2186 else if ( item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL )
2188 itemAction = subMenu->addAction(item->name);
2189 itemAction->setData(QVariant::fromValue((void *)item));
2190 itemAction->setText(item->label);
2191 connect(itemAction, SIGNAL(triggered()),
2192 this, SLOT(externalMenuItem_triggered()));
2196 children = g_list_next(children);
2200 void MainWindow::addExternalMenus()
2202 QMenu * subMenu = NULL;
2203 GList * user_menu = NULL;
2204 ext_menu_t * menu = NULL;
2206 user_menu = ext_menubar_get_entries();
2208 while ( ( user_menu != NULL ) && ( user_menu->data != NULL ) )
2210 menu = (ext_menu_t *) user_menu->data;
2212 /* On this level only menu items should exist. Not doing an assert here,
2213 * as it could be an honest mistake */
2214 if ( menu->type != EXT_MENUBAR_MENU )
2216 user_menu = g_list_next(user_menu);
2220 /* Create main submenu and add it to the menubar */
2221 subMenu = main_ui_->menuBar->addMenu(menu->label);
2223 /* This will generate the action structure for each menu. It is recursive,
2224 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2225 this->externalMenuHelper(menu, subMenu, 0);
2228 user_menu = g_list_next (user_menu);
2238 * indent-tabs-mode: nil
2241 * ex: set shiftwidth=4 tabstop=8 expandtab:
2242 * :indentSize=4:tabSize=8:noTabs=true: