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/dissector_filters.h"
27 #include <epan/epan_dissect.h>
28 #include <wsutil/filesystem.h>
29 #include <ws_version_info.h>
30 #include <epan/prefs.h>
31 #include <epan/stats_tree_priv.h>
32 #include <epan/plugin_if.h>
34 #include "ui/commandline.h"
37 #include "ui/capture.h"
38 #include <capchild/capture_session.h>
41 #include "ui/alert_box.h"
43 #include "ui/capture_ui_utils.h"
45 #include "ui/capture_globals.h"
46 #include "ui/main_statusbar.h"
47 #include "ui/recent.h"
48 #include "ui/recent_utils.h"
50 #include "ui/preference_utils.h"
52 #include "byte_view_tab.h"
54 #include "capture_interfaces_dialog.h"
56 #include "conversation_colorize_action.h"
57 #include "display_filter_edit.h"
58 #include "export_dissection_dialog.h"
59 #include "file_set_dialog.h"
60 #include "funnel_statistics.h"
61 #include "import_text_dialog.h"
62 #include "packet_list.h"
63 #include "proto_tree.h"
64 #include "simple_dialog.h"
65 #include "stock_icon.h"
66 #include "tap_parameter_dialog.h"
67 #include "wireless_frame.h"
68 #include "wireshark_application.h"
70 #include "qt_ui_utils.h"
73 #include <QActionGroup>
74 #include <QDesktopWidget>
76 #include <QMessageBox>
77 #include <QMetaObject>
80 #include <QToolButton>
81 #include <QTreeWidget>
84 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
85 #include <QtMacExtras/QMacNativeToolBar>
89 //menu_recent_file_write_all
91 // If we ever add support for multiple windows this will need to be replaced.
92 static MainWindow *gbl_cur_main_window_ = NULL;
94 void pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
96 gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
99 static void plugin_if_mainwindow_apply_filter(gconstpointer user_data)
101 if (!gbl_cur_main_window_ || !user_data)
104 GHashTable * data_set = (GHashTable *) user_data;
106 if (g_hash_table_lookup_extended(data_set, "filter_string", NULL, NULL)) {
107 QString filter((const char *)g_hash_table_lookup(data_set, "filter_string"));
108 gbl_cur_main_window_->filterPackets(filter);
112 static void plugin_if_mainwindow_preference(gconstpointer user_data)
114 if (!gbl_cur_main_window_ || !user_data)
117 GHashTable * data_set = (GHashTable *) user_data;
118 const char * module_name;
119 const char * pref_name;
120 const char * pref_value;
122 if (g_hash_table_lookup_extended(data_set, "pref_module", NULL, (void**)&module_name) &&
123 g_hash_table_lookup_extended(data_set, "pref_key", NULL, (void**)&pref_name) &&
124 g_hash_table_lookup_extended(data_set, "pref_value", NULL, (void**)&pref_value))
126 if (prefs_store_ext(module_name, pref_name, pref_value)) {
127 wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
128 wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
133 static void plugin_if_mainwindow_gotoframe(gconstpointer user_data)
135 if (!gbl_cur_main_window_ || !user_data)
138 GHashTable * data_set = (GHashTable *) user_data;
141 if (g_hash_table_lookup_extended(data_set, "frame_nr", NULL, &framenr)) {
142 if (GPOINTER_TO_UINT(framenr) != 0)
143 gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr));
149 static void plugin_if_mainwindow_get_ws_info(gconstpointer user_data)
151 if (!gbl_cur_main_window_ || !user_data)
154 GHashTable * data_set = (GHashTable *)user_data;
155 ws_info_t *ws_info = NULL;
157 if (!g_hash_table_lookup_extended(data_set, "ws_info", NULL, (void**)&ws_info))
160 CaptureFile *cfWrap = gbl_cur_main_window_->captureFile();
161 capture_file *cf = cfWrap->capFile();
163 ws_info->ws_info_supported = true;
166 ws_info->cf_state = cf->state;
167 ws_info->cf_count = cf->count;
169 g_free(ws_info->cf_filename);
170 ws_info->cf_filename = g_strdup(cf->filename);
172 if (cf->state == FILE_READ_DONE) {
173 ws_info->cf_framenr = cf->current_frame->num;
174 ws_info->frame_passed_dfilter = (cf->current_frame->flags.passed_dfilter == 1);
176 ws_info->cf_framenr = 0;
177 ws_info->frame_passed_dfilter = FALSE;
179 } else if (ws_info->cf_state != FILE_CLOSED) {
180 /* Initialise the ws_info structure */
181 ws_info->cf_count = 0;
183 g_free(ws_info->cf_filename);
184 ws_info->cf_filename = NULL;
186 ws_info->cf_framenr = 0;
187 ws_info->frame_passed_dfilter = FALSE;
188 ws_info->cf_state = FILE_CLOSED;
192 #endif /* HAVE_LIBPCAP */
195 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
199 va_start(ap, msg_format);
200 SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
208 * Alert box, with optional "don't show this message again" variable
209 * and checkbox, and optional secondary text.
212 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
213 const char *secondary_msg, const char *msg_format, ...)
215 if (notagain && *notagain) {
221 va_start(ap, msg_format);
222 SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
225 sd.setDetailedText(secondary_msg);
227 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
228 QCheckBox *cb = NULL;
230 cb = new QCheckBox();
231 cb->setChecked(true);
232 cb->setText(QObject::tr("Don't show this message again."));
239 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
240 if (notagain && cb) {
241 *notagain = cb->isChecked();
247 * Error alert box, taking a format and a va_list argument.
250 vsimple_error_message_box(const char *msg_format, va_list ap)
253 // We want to quit after reading the capture file, hence
254 // we don't actually open the error dialog.
255 if (global_commandline_info.quit_after_cap)
259 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
264 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
265 QList<QAction *> actions = parent_menu->actions();
266 QList<QAction *>::const_iterator i;
267 for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
268 if ((*i)->text()==menu_text) {
272 // If we get here there menu entry was not found, add a sub menu
273 return parent_menu->addMenu(menu_text);
276 MainWindow::MainWindow(QWidget *parent) :
278 main_ui_(new Ui::MainWindow),
279 cur_layout_(QVector<unsigned>()),
283 previous_focus_(NULL),
284 file_set_dialog_(NULL),
285 show_hide_actions_(NULL),
286 time_display_actions_(NULL),
287 time_precision_actions_(NULL),
288 funnel_statistics_(NULL),
290 capture_stopping_(false),
291 capture_filter_valid_(false)
293 , capture_interfaces_dialog_(NULL)
299 , pipe_notifier_(NULL)
301 #if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
305 if (!gbl_cur_main_window_) {
306 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
307 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
308 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
309 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
311 gbl_cur_main_window_ = this;
313 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
316 // setpUi calls QMetaObject::connectSlotsByName(this). connectSlotsByName
317 // iterates over *all* of our children, looking for matching "on_" slots.
318 // The fewer children we have at this point the better.
319 main_ui_->setupUi(this);
320 setWindowIcon(wsApp->normalIcon());
321 setTitlebarForCaptureFile();
322 setMenusForCaptureFile();
323 setForCapturedPackets(false);
324 setMenusForFileSet(false);
325 interfaceSelectionChanged();
326 loadWindowGeometry();
329 main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
332 qRegisterMetaType<FilterAction::Action>("FilterAction::Action");
333 qRegisterMetaType<FilterAction::ActionType>("FilterAction::ActionType");
334 connect(this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)),
335 this, SLOT(queuedFilterAction(QString,FilterAction::Action,FilterAction::ActionType)),
336 Qt::QueuedConnection);
338 //To prevent users use features before initialization complete
339 //Otherwise unexpected problems may occur
340 setFeaturesEnabled(false);
341 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
342 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(applyGlobalCommandLineOptions()));
343 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
344 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
345 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
346 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
347 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
348 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initConversationMenus()));
350 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
351 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
352 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
353 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
354 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
355 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
357 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
360 df_combo_box_ = new DisplayFilterCombo();
361 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
362 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
363 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
364 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
365 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
366 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
367 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
368 connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
369 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
370 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
372 funnel_statistics_ = new FunnelStatistics(this, capture_file_);
373 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
374 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
375 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
376 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
377 this, SLOT(openCaptureFile(QString,QString)));
378 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
380 initMainToolbarIcons();
382 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
384 // Make sure filter expressions overflow into a menu instead of a
385 // larger toolbar. We do this by adding them to a child toolbar.
386 // https://bugreports.qt.io/browse/QTBUG-2472
387 filter_expression_toolbar_ = new QToolBar();
388 filter_expression_toolbar_->setStyleSheet("QToolBar { background: none; border: none; }");
389 main_ui_->displayFilterToolBar->addWidget(filter_expression_toolbar_);
391 wireless_frame_ = new WirelessFrame(this);
392 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
393 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
394 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
395 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
396 this, SLOT(showPreferencesDialog(QString)));
398 main_ui_->goToFrame->hide();
399 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
400 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
402 // XXX For some reason the cursor is drawn funny with an input mask set
403 // https://bugreports.qt-project.org/browse/QTBUG-7174
405 main_ui_->searchFrame->hide();
406 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
407 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
408 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
409 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
411 main_ui_->addressEditorFrame->hide();
412 main_ui_->columnEditorFrame->hide();
413 main_ui_->preferenceEditorFrame->hide();
414 main_ui_->filterExpressionFrame->hide();
417 main_ui_->menuCapture->setEnabled(false);
420 #if defined(Q_OS_MAC)
421 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
422 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
423 ntb->setIconSize(QSize(24, 24));
424 #endif // QT_MACEXTRAS_LIB
426 main_ui_->goToPacketLabel->setAttribute(Qt::WA_MacSmallSize, true);
427 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
428 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
429 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
431 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
435 #ifdef HAVE_SOFTWARE_UPDATE
436 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
437 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
438 main_ui_->menuHelp->insertAction(update_sep, update_action);
439 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
441 master_split_.setObjectName("splitterMaster");
442 extra_split_.setObjectName("splitterExtra");
443 main_ui_->mainStack->addWidget(&master_split_);
445 empty_pane_.setObjectName("emptyPane");
447 packet_list_ = new PacketList(&master_split_);
449 proto_tree_ = new ProtoTree(&master_split_);
450 proto_tree_->installEventFilter(this);
452 byte_view_tab_ = new ByteViewTab(&master_split_);
454 packet_list_->setProtoTree(proto_tree_);
455 packet_list_->setByteViewTab(byte_view_tab_);
456 packet_list_->installEventFilter(this);
458 main_welcome_ = main_ui_->welcomePage;
460 // Packet list and proto tree must exist before these are called.
461 setMenusForSelectedPacket();
462 setMenusForSelectedTreeRow();
464 initShowHideMainWidgets();
465 initTimeDisplayFormatMenu();
466 initTimePrecisionFormatMenu();
468 updatePreferenceActions();
469 updateRecentActions();
470 setForCaptureInProgress(false);
472 setTabOrder(df_combo_box_->lineEdit(), packet_list_);
473 setTabOrder(packet_list_, proto_tree_);
475 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
476 this, SLOT(captureCapturePrepared(capture_session *)));
477 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
478 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
479 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
480 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
481 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
482 this, SLOT(captureCaptureFixedStarted(capture_session *)));
483 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
484 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
485 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
486 this, SLOT(captureCaptureFixedFinished(capture_session *)));
487 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
488 this, SLOT(captureCaptureStopping(capture_session *)));
489 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
490 this, SLOT(captureCaptureFailed(capture_session *)));
491 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
492 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
494 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
495 wsApp, SLOT(captureStarted()));
496 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
497 wsApp, SLOT(captureFinished()));
498 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
499 wsApp, SLOT(captureStarted()));
500 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
501 wsApp, SLOT(captureFinished()));
503 connect(&capture_file_, SIGNAL(captureFileOpened()),
504 this, SLOT(captureFileOpened()));
505 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
506 this, SLOT(captureFileReadStarted()));
507 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
508 this, SLOT(captureFileReadFinished()));
509 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
510 this, SLOT(captureFileReloadStarted()));
511 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
512 this, SLOT(captureFileReadFinished()));
513 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
514 this, SLOT(captureFileRescanStarted()));
515 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
516 this, SLOT(captureFileReadFinished()));
517 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
518 this, SLOT(captureFileRetapStarted()));
519 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
520 this, SLOT(captureFileRetapFinished()));
521 connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
522 this, SLOT(captureFileFlushTapsData()));
523 connect(&capture_file_, SIGNAL(captureFileClosing()),
524 this, SLOT(captureFileClosing()));
525 connect(&capture_file_, SIGNAL(captureFileClosed()),
526 this, SLOT(captureFileClosed()));
528 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
529 this, SLOT(captureFileSaveStarted(QString)));
530 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
531 main_ui_->statusBar, SLOT(popFileStatus()));
532 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
533 main_ui_->statusBar, SLOT(popFileStatus()));
534 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
535 main_ui_->statusBar, SLOT(popFileStatus()));
537 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
538 wsApp, SLOT(captureFileReadStarted()));
539 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
540 wsApp, SLOT(updateTaps()));
542 connect(wsApp, SIGNAL(columnsChanged()),
543 packet_list_, SLOT(columnsChanged()));
544 connect(wsApp, SIGNAL(preferencesChanged()),
545 packet_list_, SLOT(preferencesChanged()));
546 connect(wsApp, SIGNAL(recentFilesRead()),
547 this, SLOT(applyRecentPaneGeometry()));
548 connect(wsApp, SIGNAL(recentFilesRead()),
549 this, SLOT(updateRecentActions()));
550 connect(wsApp, SIGNAL(packetDissectionChanged()),
551 this, SLOT(redissectPackets()), Qt::QueuedConnection);
552 connect(wsApp, SIGNAL(appInitialized()),
553 this, SLOT(filterExpressionsChanged()));
554 connect(wsApp, SIGNAL(filterExpressionsChanged()),
555 this, SLOT(filterExpressionsChanged()));
556 connect(wsApp, SIGNAL(checkDisplayFilter()),
557 this, SLOT(checkDisplayFilter()));
558 connect(wsApp, SIGNAL(fieldsChanged()),
559 this, SLOT(fieldsChanged()));
560 connect(wsApp, SIGNAL(reloadLuaPlugins()),
561 this, SLOT(reloadLuaPlugins()));
563 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
564 this, SLOT(mainStackChanged(int)));
566 connect(main_welcome_, SIGNAL(startCapture()),
567 this, SLOT(startCapture()));
568 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
569 this, SLOT(openCaptureFile(QString)));
570 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
571 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
572 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
573 main_ui_->statusBar, SLOT(popFilterStatus()));
575 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
576 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
577 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
578 this, SLOT(redissectPackets()));
579 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
580 this, SLOT(showPreferencesDialog(QString)));
581 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
582 this, SLOT(showPreferencesDialog(QString)));
583 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
584 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
585 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
586 this, SLOT(filterExpressionsChanged()));
588 connect(this, SIGNAL(setCaptureFile(capture_file*)),
589 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
590 connect(this, SIGNAL(setCaptureFile(capture_file*)),
591 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
592 connect(this, SIGNAL(setCaptureFile(capture_file*)),
593 packet_list_, SLOT(setCaptureFile(capture_file*)));
594 connect(this, SIGNAL(setCaptureFile(capture_file*)),
595 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
597 connect(this, SIGNAL(monospaceFontChanged(QFont)),
598 packet_list_, SLOT(setMonospaceFont(QFont)));
599 connect(this, SIGNAL(monospaceFontChanged(QFont)),
600 proto_tree_, SLOT(setMonospaceFont(QFont)));
601 connect(this, SIGNAL(monospaceFontChanged(QFont)),
602 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
604 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
605 packet_list_, SLOT(goNextPacket()));
606 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
607 packet_list_, SLOT(goPreviousPacket()));
608 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
609 packet_list_, SLOT(goFirstPacket()));
610 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
611 packet_list_, SLOT(goLastPacket()));
613 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
614 proto_tree_, SLOT(expandSubtrees()));
615 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
616 proto_tree_, SLOT(expandAll()));
617 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
618 proto_tree_, SLOT(collapseAll()));
620 connect(packet_list_, SIGNAL(packetSelectionChanged()),
621 this, SLOT(setMenusForSelectedPacket()));
622 connect(packet_list_, SIGNAL(packetDissectionChanged()),
623 this, SLOT(redissectPackets()));
624 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
625 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
626 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
627 this, SLOT(showPreferencesDialog(QString)));
628 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
629 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
630 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
631 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
632 packet_list_, SLOT(columnsChanged()));
633 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
634 this, SLOT(openPacketDialog()));
635 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
636 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
637 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
638 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
639 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
640 main_ui_->statusBar, SLOT(popBusyStatus()));
641 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
642 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
643 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
644 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
645 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
646 main_ui_->statusBar, SLOT(popProgressStatus()));
648 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
649 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
650 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
651 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
652 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
653 this, SLOT(openPacketDialog(bool)));
654 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
655 this, SLOT(showPreferencesDialog(QString)));
656 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
657 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
659 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
660 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
661 connect(byte_view_tab_, SIGNAL(currentChanged(int)),
662 this, SLOT(byteViewTabChanged(int)));
664 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
665 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
667 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
668 &capture_file_, SLOT(stopLoading()));
670 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
671 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
674 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
676 connect(iface_tree, SIGNAL(itemSelectionChanged()),
677 this, SLOT(interfaceSelectionChanged()));
679 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
680 this, SLOT(captureFilterSyntaxChanged(bool)));
683 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
684 this, SLOT(showExtcapOptionsDialog(QString&)));
687 #endif // HAVE_LIBPCAP
689 /* Create plugin_if hooks */
690 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
691 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
692 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
693 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
695 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
698 main_ui_->mainStack->setCurrentWidget(main_welcome_);
701 MainWindow::~MainWindow()
706 QString MainWindow::getFilter()
708 return df_combo_box_->currentText();
711 QMenu *MainWindow::createPopupMenu()
713 QMenu *menu = new QMenu();
714 menu->addAction(main_ui_->actionViewMainToolbar);
715 menu->addAction(main_ui_->actionViewFilterToolbar);
716 menu->addAction(main_ui_->actionViewWirelessToolbar);
717 menu->addAction(main_ui_->actionViewStatusBar);
718 menu->addSeparator();
719 menu->addAction(main_ui_->actionViewPacketList);
720 menu->addAction(main_ui_->actionViewPacketDetails);
721 menu->addAction(main_ui_->actionViewPacketBytes);
725 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
727 pipe_source_ = source;
728 pipe_child_process_ = child_process;
729 pipe_user_data_ = user_data;
730 pipe_input_cb_ = input_cb;
733 /* Tricky to use pipes in win9x, as no concept of wait. NT can
734 do this but that doesn't cover all win32 platforms. GTK can do
735 this but doesn't seem to work over processes. Attempt to do
736 something similar here, start a timer and check for data on every
738 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
741 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
745 pipe_timer_ = new QTimer(this);
746 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
747 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
748 pipe_timer_->start(200);
750 if (pipe_notifier_) {
751 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
752 delete pipe_notifier_;
755 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
756 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
757 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
758 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
762 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
764 // The user typed some text. Start filling in a filter.
765 // We may need to be more choosy here. We just need to catch events for the packet list,
766 // proto tree, and main welcome widgets.
767 if (event->type() == QEvent::KeyPress) {
768 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
769 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
770 df_combo_box_->lineEdit()->insert(kevt->text());
771 df_combo_box_->lineEdit()->setFocus();
776 return QMainWindow::eventFilter(obj, event);
779 void MainWindow::keyPressEvent(QKeyEvent *event) {
781 // Explicitly focus on the display filter combo.
782 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
783 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
787 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
788 if (event->modifiers() == Qt::NoModifier) {
789 if (event->key() == Qt::Key_Escape) {
790 on_goToCancel_clicked();
791 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
795 return; // goToLineEdit didn't want it and we don't either.
798 // Move up & down the packet list.
799 if (event->key() == Qt::Key_F7) {
800 packet_list_->goPreviousPacket();
801 } else if (event->key() == Qt::Key_F8) {
802 packet_list_->goNextPacket();
805 // Move along, citizen.
806 QMainWindow::keyPressEvent(event);
809 void MainWindow::closeEvent(QCloseEvent *event) {
810 saveWindowGeometry();
812 /* If we're in the middle of stopping a capture, don't do anything;
813 the user can try deleting the window after the capture stops. */
814 if (capture_stopping_) {
819 QString before_what(tr(" before quitting"));
820 if (!testCaptureFileClose(before_what, Quit)) {
826 if (capture_interfaces_dialog_) capture_interfaces_dialog_->close();
828 // Make sure we kill any open dumpcap processes.
829 delete main_welcome_;
831 // One of the many places we assume one main window.
832 if(!wsApp->isInitialized()) {
833 // If we're still initializing, QCoreApplication::quit() won't
834 // exit properly because we are not in the event loop. This
835 // means that the application won't clean up after itself. We
836 // might want to call wsApp->processEvents() during startup
837 // instead so that we can do a normal exit here.
843 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
846 foreach (QUrl drag_url, event->mimeData()->urls()) {
847 if (!drag_url.toLocalFile().isEmpty()) {
852 if (accept) event->acceptProposedAction();
855 void MainWindow::dropEvent(QDropEvent *event)
857 foreach (QUrl drop_url, event->mimeData()->urls()) {
858 QString local_file = drop_url.toLocalFile();
859 if (!local_file.isEmpty()) {
860 event->acceptProposedAction();
861 openCaptureFile(local_file);
867 // Apply recent settings to the main window geometry.
868 // We haven't loaded the preferences at this point so we assume that the
869 // position and size preference are enabled.
870 // Note we might end up with unexpected screen geometries if the user
871 // unplugs or plugs in a monitor:
872 // https://bugreports.qt.io/browse/QTBUG-44213
873 void MainWindow::loadWindowGeometry()
875 int min_sensible_dimension = 200;
878 if (recent.gui_geometry_main_maximized) {
879 setWindowState(Qt::WindowMaximized);
883 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
884 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
885 if (!rect_on_screen(recent_geom)) {
886 // We're not visible on any screens. See if we can move onscreen
888 recent_geom.moveTo(50, 50); // recent.c defaults to 20.
891 if (!rect_on_screen(recent_geom)) {
892 // Give up and use the default geometry.
896 // if (prefs.gui_geometry_save_position) {
897 move(recent_geom.topLeft());
900 if (// prefs.gui_geometry_save_size &&
901 recent_geom.width() > min_sensible_dimension &&
902 recent_geom.height() > min_sensible_dimension) {
903 resize(recent_geom.size());
908 void MainWindow::saveWindowGeometry()
910 if (prefs.gui_geometry_save_position) {
911 recent.gui_geometry_main_x = pos().x();
912 recent.gui_geometry_main_y = pos().y();
915 if (prefs.gui_geometry_save_size) {
916 recent.gui_geometry_main_width = size().width();
917 recent.gui_geometry_main_height = size().height();
920 if (prefs.gui_geometry_save_maximized) {
921 // On OS X this is false when it shouldn't be
922 recent.gui_geometry_main_maximized = isMaximized();
925 if (master_split_.sizes().length() > 0) {
926 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
929 if (master_split_.sizes().length() > 2) {
930 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
931 } else if (extra_split_.sizes().length() > 0) {
932 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
936 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
938 case layout_pane_content_none:
940 case layout_pane_content_plist:
942 case layout_pane_content_pdetails:
944 case layout_pane_content_pbytes:
945 return byte_view_tab_;
947 g_assert_not_reached();
952 // Our event loop becomes nested whenever we call update_progress_dlg, which
953 // includes several places in file.c. The GTK+ UI stays out of trouble by
954 // showing a modal progress dialog. We attempt to do the equivalent below by
955 // disabling parts of the main window. At a minumum the ProgressFrame in the
956 // main status bar must remain accessible.
958 // We might want to do this any time the main status bar progress frame is
960 void MainWindow::freeze()
962 freeze_focus_ = wsApp->focusWidget();
964 // XXX Alternatively we could just disable and enable the main menu.
965 for (int i = 0; i < freeze_actions_.size(); i++) {
966 QAction *action = freeze_actions_[i].first;
967 freeze_actions_[i].second = action->isEnabled();
968 action->setEnabled(false);
970 main_ui_->centralWidget->setEnabled(false);
973 void MainWindow::thaw()
975 main_ui_->centralWidget->setEnabled(true);
976 for (int i = 0; i < freeze_actions_.size(); i++) {
977 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
980 if (freeze_focus_) freeze_focus_->setFocus();
981 freeze_focus_ = NULL;
984 void MainWindow::mergeCaptureFile()
986 QString file_name = "";
987 QString read_filter = "";
988 dfilter_t *rfcode = NULL;
991 if (!capture_file_.capFile())
994 if (prefs.gui_ask_unsaved) {
995 if (cf_has_unsaved_data(capture_file_.capFile())) {
996 QMessageBox msg_dialog;
997 gchar *display_basename;
1000 msg_dialog.setIcon(QMessageBox::Question);
1001 /* This file has unsaved data; ask the user whether to save
1003 if (capture_file_.capFile()->is_tempfile) {
1004 msg_dialog.setText(tr("Save packets before merging?"));
1005 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
1008 * Format the message.
1010 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1011 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
1012 g_free(display_basename);
1013 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
1016 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
1017 msg_dialog.setDefaultButton(QMessageBox::Save);
1019 response = msg_dialog.exec();
1023 case QMessageBox::Save:
1024 /* Save the file but don't close it */
1025 saveCaptureFile(capture_file_.capFile(), false);
1028 case QMessageBox::Cancel:
1030 /* Don't do the merge. */
1037 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1039 cf_status_t merge_status;
1040 char *in_filenames[2];
1043 if (merge_dlg.merge(file_name)) {
1046 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1047 /* Not valid. Tell the user, and go back and run the file
1048 selection box again once they dismiss the alert. */
1049 // Similar to commandline_info.jfilter section in main().
1050 QMessageBox::warning(this, tr("Invalid Read Filter"),
1051 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1060 file_type = capture_file_.capFile()->cd_t;
1062 /* Try to merge or append the two files */
1064 if (merge_dlg.mergeType() == 0) {
1065 /* chronological order */
1066 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1067 in_filenames[1] = qstring_strdup(file_name);
1068 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
1069 } else if (merge_dlg.mergeType() <= 0) {
1071 in_filenames[0] = qstring_strdup(file_name);
1072 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1073 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1076 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1077 in_filenames[1] = qstring_strdup(file_name);
1078 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1081 g_free(in_filenames[0]);
1082 g_free(in_filenames[1]);
1084 if (merge_status != CF_OK) {
1086 dfilter_free(rfcode);
1091 cf_close(capture_file_.capFile());
1093 /* Try to open the merged capture file. */
1094 CaptureFile::globalCapFile()->window = this;
1095 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1096 /* We couldn't open it; fail. */
1097 CaptureFile::globalCapFile()->window = NULL;
1099 dfilter_free(rfcode);
1104 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1105 it closed the previous capture file, and thus destroyed any
1106 previous read filter attached to "cf"). */
1107 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1109 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1113 /* Just because we got an error, that doesn't mean we were unable
1114 to read any of the file; we handle what we could get from the
1118 case CF_READ_ABORTED:
1119 /* The user bailed out of re-reading the capture file; the
1120 capture file has been closed - just free the capture file name
1121 string and return (without changing the last containing
1127 /* Save the name of the containing directory specified in the path name,
1128 if any; we can write over cf_merged_name, which is a good thing, given that
1129 "get_dirname()" does write over its argument. */
1130 wsApp->setLastOpenDir(get_dirname(tmpname));
1132 main_ui_->statusBar->showExpert();
1138 void MainWindow::importCaptureFile() {
1139 ImportTextDialog import_dlg;
1141 QString before_what(tr(" before importing a capture"));
1142 if (!testCaptureFileClose(before_what))
1147 if (import_dlg.result() != QDialog::Accepted) {
1148 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1152 openCaptureFile(import_dlg.capfileName());
1155 bool MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1157 gboolean discard_comments;
1159 if (cf->is_tempfile) {
1160 /* This is a temporary capture file, so saving it means saving
1161 it to a permanent file. Prompt the user for a location
1162 to which to save it. Don't require that the file format
1163 support comments - if it's a temporary capture file, it's
1164 probably pcap-ng, which supports comments and, if it's
1165 not pcap-ng, let the user decide what they want to do
1166 if they've added comments. */
1167 return saveAsCaptureFile(cf, FALSE, dont_reopen);
1169 if (cf->unsaved_changes) {
1170 cf_write_status_t status;
1172 /* This is not a temporary capture file, but it has unsaved
1173 changes, so saving it means doing a "safe save" on top
1174 of the existing file, in the same format - no UI needed
1175 unless the file has comments and the file's format doesn't
1178 If the file has comments, does the file's format support them?
1179 If not, ask the user whether they want to discard the comments
1180 or choose a different format. */
1181 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1184 /* The file can be saved in the specified format as is;
1185 just drive on and save in the format they selected. */
1186 discard_comments = FALSE;
1189 case SAVE_WITHOUT_COMMENTS:
1190 /* The file can't be saved in the specified format as is,
1191 but it can be saved without the comments, and the user
1192 said "OK, discard the comments", so save it in the
1193 format they specified without the comments. */
1194 discard_comments = TRUE;
1197 case SAVE_IN_ANOTHER_FORMAT:
1198 /* There are file formats in which we can save this that
1199 support comments, and the user said not to delete the
1200 comments. Do a "Save As" so the user can select
1201 one of those formats and choose a file name. */
1202 return saveAsCaptureFile(cf, TRUE, dont_reopen);
1205 /* The user said "forget it". Just return. */
1209 /* Squelch warnings that discard_comments is being used
1211 g_assert_not_reached();
1215 /* XXX - cf->filename might get freed out from under us, because
1216 the code path through which cf_save_records() goes currently
1217 closes the current file and then opens and reloads the saved file,
1218 so make a copy and free it later. */
1219 file_name = cf->filename;
1220 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1221 discard_comments, dont_reopen);
1225 /* The save succeeded; we're done.
1226 If we discarded comments, redraw the packet list to reflect
1227 any packets that no longer have comments. */
1228 if (discard_comments)
1229 packet_list_queue_draw();
1231 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1232 updateForUnsavedChanges(); // we update the title bar to remove the *
1235 case CF_WRITE_ERROR:
1236 /* The write failed.
1237 XXX - OK, what do we do now? Let them try a
1238 "Save As", in case they want to try to save to a
1239 different directory or file system? */
1242 case CF_WRITE_ABORTED:
1243 /* The write was aborted; just drive on. */
1247 /* Otherwise just do nothing. */
1253 bool MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1254 QString file_name = "";
1256 gboolean compressed;
1257 cf_write_status_t status;
1259 gboolean discard_comments = FALSE;
1266 CaptureFileDialog save_as_dlg(this, cf);
1268 /* If the file has comments, does the format the user selected
1269 support them? If not, ask the user whether they want to
1270 discard the comments or choose a different format. */
1271 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1274 /* The file can be saved in the specified format as is;
1275 just drive on and save in the format they selected. */
1276 discard_comments = FALSE;
1279 case SAVE_WITHOUT_COMMENTS:
1280 /* The file can't be saved in the specified format as is,
1281 but it can be saved without the comments, and the user
1282 said "OK, discard the comments", so save it in the
1283 format they specified without the comments. */
1284 discard_comments = TRUE;
1287 case SAVE_IN_ANOTHER_FORMAT:
1288 /* There are file formats in which we can save this that
1289 support comments, and the user said not to delete the
1290 comments. The combo box of file formats has had the
1291 formats that don't support comments trimmed from it,
1292 so run the dialog again, to let the user decide
1293 whether to save in one of those formats or give up. */
1294 must_support_comments = TRUE;
1298 /* The user said "forget it". Just get rid of the dialog box
1302 file_type = save_as_dlg.selectedFileType();
1303 compressed = save_as_dlg.isCompressed();
1305 fileAddExtension(file_name, file_type, compressed);
1308 // /* If the file exists and it's user-immutable or not writable,
1309 // ask the user whether they want to override that. */
1310 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1311 // /* They don't. Let them try another file name or cancel. */
1316 /* Attempt to save the file */
1317 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1318 discard_comments, dont_reopen);
1322 /* The save succeeded; we're done. */
1323 /* Save the directory name for future file dialogs. */
1324 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1325 set_last_open_dir(get_dirname(dirname));
1327 /* If we discarded comments, redraw the packet list to reflect
1328 any packets that no longer have comments. */
1329 if (discard_comments)
1330 packet_list_queue_draw();
1332 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1333 updateForUnsavedChanges(); // we update the title bar to remove the *
1334 /* Add this filename to the list of recent files in the "Recent Files" submenu */
1335 add_menu_recent_capture_file(file_name.toUtf8().constData());
1338 case CF_WRITE_ERROR:
1339 /* The save failed; let the user try again. */
1342 case CF_WRITE_ABORTED:
1343 /* The user aborted the save; just return. */
1350 void MainWindow::exportSelectedPackets() {
1351 QString file_name = "";
1353 gboolean compressed;
1354 packet_range_t range;
1355 cf_write_status_t status;
1357 gboolean discard_comments = FALSE;
1359 if (!capture_file_.capFile())
1362 /* Init the packet range */
1363 packet_range_init(&range, capture_file_.capFile());
1364 range.process_filtered = TRUE;
1365 range.include_dependents = TRUE;
1368 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1370 /* If the file has comments, does the format the user selected
1371 support them? If not, ask the user whether they want to
1372 discard the comments or choose a different format. */
1373 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1376 /* The file can be saved in the specified format as is;
1377 just drive on and save in the format they selected. */
1378 discard_comments = FALSE;
1381 case SAVE_WITHOUT_COMMENTS:
1382 /* The file can't be saved in the specified format as is,
1383 but it can be saved without the comments, and the user
1384 said "OK, discard the comments", so save it in the
1385 format they specified without the comments. */
1386 discard_comments = TRUE;
1389 case SAVE_IN_ANOTHER_FORMAT:
1390 /* There are file formats in which we can save this that
1391 support comments, and the user said not to delete the
1392 comments. The combo box of file formats has had the
1393 formats that don't support comments trimmed from it,
1394 so run the dialog again, to let the user decide
1395 whether to save in one of those formats or give up. */
1399 /* The user said "forget it". Just get rid of the dialog box
1405 * Check that we're not going to save on top of the current
1407 * We do it here so we catch all cases ...
1408 * Unfortunately, the file requester gives us an absolute file
1409 * name and the read file name may be relative (if supplied on
1410 * the command line). From Joerg Mayer.
1412 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1413 QMessageBox msg_box;
1414 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1416 msg_box.setIcon(QMessageBox::Critical);
1417 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1418 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1419 msg_box.setStandardButtons(QMessageBox::Ok);
1420 msg_box.setDefaultButton(QMessageBox::Ok);
1422 g_free(display_basename);
1426 file_type = esp_dlg.selectedFileType();
1427 compressed = esp_dlg.isCompressed();
1428 fileAddExtension(file_name, file_type, compressed);
1431 // /* If the file exists and it's user-immutable or not writable,
1432 // ask the user whether they want to override that. */
1433 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1434 // /* They don't. Let them try another file name or cancel. */
1439 /* Attempt to save the file */
1440 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1444 /* The save succeeded; we're done. */
1445 /* Save the directory name for future file dialogs. */
1446 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1447 set_last_open_dir(get_dirname(dirname));
1449 /* If we discarded comments, redraw the packet list to reflect
1450 any packets that no longer have comments. */
1451 if (discard_comments)
1452 packet_list_queue_draw();
1455 case CF_WRITE_ERROR:
1456 /* The save failed; let the user try again. */
1459 case CF_WRITE_ABORTED:
1460 /* The user aborted the save; just return. */
1467 void MainWindow::exportDissections(export_type_e export_type) {
1468 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1469 packet_range_t range;
1471 if (!capture_file_.capFile())
1474 /* Init the packet range */
1475 packet_range_init(&range, capture_file_.capFile());
1476 range.process_filtered = TRUE;
1477 range.include_dependents = TRUE;
1482 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1483 QString file_name_lower;
1484 QString file_suffix;
1485 GSList *extensions_list;
1486 gboolean add_extension;
1489 * Append the default file extension if there's none given by
1490 * the user or if they gave one that's not one of the valid
1491 * extensions for the file type.
1493 file_name_lower = file_name.toLower();
1494 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1495 if (extensions_list != NULL) {
1498 /* We have one or more extensions for this file type.
1499 Start out assuming we need to add the default one. */
1500 add_extension = TRUE;
1502 /* OK, see if the file has one of those extensions. */
1503 for (extension = extensions_list; extension != NULL;
1504 extension = g_slist_next(extension)) {
1505 file_suffix += tr(".") + (char *)extension->data;
1506 if (file_name_lower.endsWith(file_suffix)) {
1508 * The file name has one of the extensions for
1511 add_extension = FALSE;
1514 file_suffix += ".gz";
1515 if (file_name_lower.endsWith(file_suffix)) {
1517 * The file name has one of the extensions for
1520 add_extension = FALSE;
1525 /* We have no extensions for this file type. Don't add one. */
1526 add_extension = FALSE;
1528 if (add_extension) {
1529 if (wtap_default_file_extension(file_type) != NULL) {
1530 file_name += tr(".") + wtap_default_file_extension(file_type);
1538 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1539 bool capture_in_progress = false;
1540 bool do_close_file = false;
1542 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1543 return true; /* Already closed, nothing to do */
1546 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1547 /* This is true if we're reading a capture file *or* if we're doing
1548 a live capture. If we're reading a capture file, the main loop
1549 is busy reading packets, and only accepting input from the
1550 progress dialog, so we can't get here, so this means we're
1552 capture_in_progress = true;
1556 if (prefs.gui_ask_unsaved) {
1557 if (cf_has_unsaved_data(capture_file_.capFile()) ||
1558 (capture_in_progress && capture_file_.capFile()->count > 0))
1560 QMessageBox msg_dialog;
1563 QPushButton *save_button;
1564 QPushButton *discard_button;
1566 msg_dialog.setIcon(QMessageBox::Question);
1567 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1569 /* This file has unsaved data or there's a capture in
1570 progress; ask the user whether to save the data. */
1571 if (capture_in_progress && context != Restart) {
1572 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1573 infotext = tr("Your captured packets will be lost if you don't save them.");
1574 } else if (capture_file_.capFile()->is_tempfile) {
1575 if (context == Reload) {
1576 // Reloading a tempfile will keep the packets, so this is not unsaved packets
1577 question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1578 infotext = tr("Your changes will be lost if you don't save them.");
1580 question = tr("Do you want to save the captured packets%1?").arg(before_what);
1581 infotext = tr("Your captured packets will be lost if you don't save them.");
1584 // No capture in progress and not a tempfile, so this is not unsaved packets
1585 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1586 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1587 infotext = tr("Your changes will be lost if you don't save them.");
1588 g_free(display_basename);
1591 msg_dialog.setText(question);
1592 msg_dialog.setInformativeText(infotext);
1594 // XXX Text comes from ui/gtk/stock_icons.[ch]
1595 // Note that the button roles differ from the GTK+ version.
1596 // Cancel = RejectRole
1597 // Save = AcceptRole
1598 // Don't Save = DestructiveRole
1599 msg_dialog.addButton(QMessageBox::Cancel);
1601 if (capture_in_progress) {
1602 QString save_button_text;
1603 if (context == Restart) {
1604 save_button_text = tr("Save before Continue");
1606 save_button_text = tr("Stop and Save");
1608 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1610 save_button = msg_dialog.addButton(QMessageBox::Save);
1612 msg_dialog.setDefaultButton(save_button);
1614 QString discard_button_text;
1615 if (capture_in_progress) {
1618 discard_button_text = tr("Stop and Quit &without Saving");
1621 discard_button_text = tr("Continue &without Saving");
1624 discard_button_text = tr("Stop and Continue &without Saving");
1630 discard_button_text = tr("Quit &without Saving");
1634 discard_button_text = tr("Continue &without Saving");
1638 discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1641 /* According to the Qt doc:
1642 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1644 * Therefore we should use clickedButton() to determine which button was clicked. */
1646 if (msg_dialog.clickedButton() == save_button) {
1648 /* If there's a capture in progress, we have to stop the capture
1649 and then do the save. */
1650 if (capture_in_progress)
1653 /* Save the file and close it */
1654 if (saveCaptureFile(capture_file_.capFile(), true) == false)
1656 do_close_file = true;
1657 } else if(msg_dialog.clickedButton() == discard_button) {
1658 /* Just close the file, discarding changes */
1659 do_close_file = true;
1661 // cancelButton or some other unspecified button
1665 /* Unchanged file or capturing with no packets */
1666 do_close_file = true;
1669 /* User asked not to be bothered by those prompts, just close it.
1670 XXX - should that apply only to saving temporary files? */
1671 do_close_file = true;
1674 if (do_close_file) {
1676 /* If there's a capture in progress, we have to stop the capture
1677 and then do the close. */
1678 if (capture_in_progress)
1681 /* captureStop() will close the file if not having any packets */
1682 if (capture_file_.capFile() && context != Restart && context != Reload)
1683 // Don't really close if Restart or Reload
1684 cf_close(capture_file_.capFile());
1687 return true; /* File closed */
1690 void MainWindow::captureStop() {
1693 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1694 WiresharkApplication::processEvents();
1698 void MainWindow::initMainToolbarIcons()
1700 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1701 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1702 #if !defined(Q_OS_WIN)
1703 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1704 // The OS X HIG specifies 32-pixel icons but they're a little too
1706 icon_size = icon_size * 3 / 2;
1708 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1710 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1711 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1713 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1714 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1715 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1716 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1718 // Menu icons are disabled in main_window.ui for these items.
1719 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1720 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1721 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1722 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1724 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1725 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1726 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1727 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1728 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1729 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1730 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1731 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1732 #if defined(Q_OS_MAC)
1733 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1734 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1736 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1738 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1740 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1741 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1742 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1743 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1744 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1745 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1746 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1749 void MainWindow::initShowHideMainWidgets()
1751 if (show_hide_actions_) {
1755 show_hide_actions_ = new QActionGroup(this);
1756 QMap<QAction *, QWidget *> shmw_actions;
1758 show_hide_actions_->setExclusive(false);
1759 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1760 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1761 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1762 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1763 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1764 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1765 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1767 foreach (QAction *shmwa, shmw_actions.keys()) {
1768 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1769 show_hide_actions_->addAction(shmwa);
1770 showHideMainWidgets(shmwa);
1773 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1776 Q_DECLARE_METATYPE(ts_type)
1778 void MainWindow::initTimeDisplayFormatMenu()
1780 if (time_display_actions_) {
1784 time_display_actions_ = new QActionGroup(this);
1786 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1787 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1788 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1789 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1790 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1791 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1792 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1793 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1794 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1795 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1797 foreach (QAction* tda, td_actions.keys()) {
1798 tda->setData(qVariantFromValue(td_actions[tda]));
1799 time_display_actions_->addAction(tda);
1802 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1805 Q_DECLARE_METATYPE(ts_precision)
1807 void MainWindow::initTimePrecisionFormatMenu()
1809 if (time_precision_actions_) {
1813 time_precision_actions_ = new QActionGroup(this);
1815 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1816 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1817 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1818 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1819 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1820 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1821 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1823 foreach (QAction* tpa, tp_actions.keys()) {
1824 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1825 time_precision_actions_->addAction(tpa);
1828 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1831 // Menu items which will be disabled when we freeze() and whose state will
1832 // be restored when we thaw(). Add to the list as needed.
1833 void MainWindow::initFreezeActions()
1835 QList<QAction *> freeze_actions = QList<QAction *>()
1836 << main_ui_->actionFileClose
1837 << main_ui_->actionViewReload
1838 << main_ui_->actionEditMarkPacket
1839 << main_ui_->actionEditMarkAllDisplayed
1840 << main_ui_->actionEditUnmarkAllDisplayed
1841 << main_ui_->actionEditIgnorePacket
1842 << main_ui_->actionEditIgnoreAllDisplayed
1843 << main_ui_->actionEditUnignoreAllDisplayed
1844 << main_ui_->actionEditSetTimeReference
1845 << main_ui_->actionEditUnsetAllTimeReferences;
1847 foreach (QAction *action, freeze_actions) {
1848 freeze_actions_ << QPair<QAction *, bool>(action, false);
1852 void MainWindow::initConversationMenus()
1856 QList<QAction *> cc_actions = QList<QAction *>()
1857 << main_ui_->actionViewColorizeConversation1 << main_ui_->actionViewColorizeConversation2
1858 << main_ui_->actionViewColorizeConversation3 << main_ui_->actionViewColorizeConversation4
1859 << main_ui_->actionViewColorizeConversation5 << main_ui_->actionViewColorizeConversation6
1860 << main_ui_->actionViewColorizeConversation7 << main_ui_->actionViewColorizeConversation8
1861 << main_ui_->actionViewColorizeConversation9 << main_ui_->actionViewColorizeConversation10;
1863 for (GList *conv_filter_list_entry = conv_filter_list; conv_filter_list_entry; conv_filter_list_entry = g_list_next(conv_filter_list_entry)) {
1865 conversation_filter_t* conv_filter = (conversation_filter_t *)conv_filter_list_entry->data;
1866 ConversationAction *conv_action = new ConversationAction(main_ui_->menuConversationFilter, conv_filter);
1867 main_ui_->menuConversationFilter->addAction(conv_action);
1869 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
1870 connect(conv_action, SIGNAL(triggered()), this, SLOT(applyConversationFilter()));
1872 // Packet list context menu items
1873 packet_list_->conversationMenu()->addAction(conv_action);
1875 QMenu *submenu = packet_list_->colorizeMenu()->addMenu(conv_action->text());
1878 foreach (QAction *cc_action, cc_actions) {
1879 conv_action = new ConversationAction(submenu, conv_filter);
1880 conv_action->setText(cc_action->text());
1881 conv_action->setIcon(cc_action->icon());
1882 conv_action->setColorNumber(i++);
1883 submenu->addAction(conv_action);
1884 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
1885 connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
1888 conv_action = new ConversationAction(submenu, conv_filter);
1889 conv_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
1890 submenu->addAction(conv_action);
1891 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
1892 connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
1894 // Proto tree conversation menu is filled in in ProtoTree::contextMenuEvent.
1895 // We should probably do that here.
1898 // Proto tree colorization items
1900 ColorizeAction *colorize_action;
1901 foreach (QAction *cc_action, cc_actions) {
1902 colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
1903 colorize_action->setText(cc_action->text());
1904 colorize_action->setIcon(cc_action->icon());
1905 colorize_action->setColorNumber(i++);
1906 proto_tree_->colorizeMenu()->addAction(colorize_action);
1907 connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
1908 connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
1911 colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
1912 colorize_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
1913 proto_tree_->colorizeMenu()->addAction(colorize_action);
1914 connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
1915 connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
1919 void MainWindow::setTitlebarForCaptureFile()
1921 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1922 if (capture_file_.capFile()->is_tempfile) {
1924 // For a temporary file, put the source of the data
1925 // in the window title, not whatever random pile
1926 // of characters is the last component of the path
1929 // XXX - on non-Mac platforms, put in the application
1932 setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1935 // For a user file, set the full path; that way,
1936 // for OS X, it'll set the "proxy icon". Qt
1937 // handles extracting the last component.
1939 // Sadly, some UN*Xes don't necessarily use UTF-8
1940 // for their file names, so we have to map the
1941 // file path to UTF-8. If that fails, we're somewhat
1944 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1949 if (utf8_filename) {
1950 QFileInfo fi(utf8_filename);
1951 setWSWindowTitle(QString("[*]%1").arg(fi.fileName()));
1952 setWindowFilePath(utf8_filename);
1953 g_free(utf8_filename);
1955 // So what the heck else can we do here?
1956 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1959 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1961 /* We have no capture file. */
1966 QString MainWindow::replaceWindowTitleVariables(QString title)
1968 title.replace ("%P", get_profile_name());
1969 title.replace ("%V", get_ws_vcs_version_info());
1974 void MainWindow::setWSWindowTitle(QString title)
1976 if (title.isEmpty()) {
1977 title = tr("The Wireshark Network Analyzer");
1980 if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
1981 QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
1982 title.prepend(QString("[%1] ").arg(custom_title));
1985 if (prefs.gui_window_title && prefs.gui_window_title[0]) {
1986 QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
1988 // On OS X we separate the titles with a unicode em dash
1989 title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
1991 title.append(QString(" [%1]").arg(custom_title));
1995 setWindowTitle(title);
1996 setWindowFilePath(NULL);
1999 void MainWindow::setTitlebarForCaptureInProgress()
2001 if (capture_file_.capFile()) {
2002 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
2004 /* We have no capture in progress. */
2011 /* Enable or disable menu items based on whether you have a capture file
2012 you've finished reading and, if you have one, whether it's been saved
2013 and whether it could be saved except by copying the raw packet data. */
2014 void MainWindow::setMenusForCaptureFile(bool force_disable)
2017 bool can_write = false;
2018 bool can_save = false;
2019 bool can_save_as = false;
2021 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
2022 /* We have no capture file or we're currently reading a file */
2025 /* We have a capture file. Can we write or save? */
2026 can_write = cf_can_write_with_wiretap(capture_file_.capFile());
2027 can_save = cf_can_save(capture_file_.capFile());
2028 can_save_as = cf_can_save_as(capture_file_.capFile());
2031 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(enable);
2032 main_ui_->actionFileMerge->setEnabled(can_write);
2033 main_ui_->actionFileClose->setEnabled(enable);
2034 main_ui_->actionFileSave->setEnabled(can_save);
2035 main_ui_->actionFileSaveAs->setEnabled(can_save_as);
2036 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(enable);
2038 * "Export Specified Packets..." should be available only if
2039 * we can write the file out in at least one format.
2041 main_ui_->actionFileExportPackets->setEnabled(can_write);
2043 main_ui_->actionFileExportAsCArrays->setEnabled(enable);
2044 main_ui_->actionFileExportAsCSV->setEnabled(enable);
2045 main_ui_->actionFileExportAsPDML->setEnabled(enable);
2046 main_ui_->actionFileExportAsPlainText->setEnabled(enable);
2047 main_ui_->actionFileExportAsPSML->setEnabled(enable);
2048 main_ui_->actionFileExportAsJSON->setEnabled(enable);
2050 main_ui_->actionFileExportPacketBytes->setEnabled(enable);
2051 main_ui_->actionFileExportPDU->setEnabled(enable);
2052 main_ui_->actionFileExportSSLSessionKeys->setEnabled(enable);
2054 foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2055 eo_action->setEnabled(enable);
2058 main_ui_->actionViewReload->setEnabled(enable);
2061 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
2062 /* Either a capture was started or stopped; in either case, it's not
2063 in the process of stopping, so allow quitting. */
2065 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
2066 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
2068 main_ui_->actionFileExportAsCArrays->setEnabled(capture_in_progress);
2069 main_ui_->actionFileExportAsCSV->setEnabled(capture_in_progress);
2070 main_ui_->actionFileExportAsPDML->setEnabled(capture_in_progress);
2071 main_ui_->actionFileExportAsPlainText->setEnabled(capture_in_progress);
2072 main_ui_->actionFileExportAsPSML->setEnabled(capture_in_progress);
2073 main_ui_->actionFileExportAsJSON->setEnabled(capture_in_progress);
2075 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
2076 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
2077 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
2079 foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2080 eo_action->setEnabled(capture_in_progress);
2083 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
2084 main_ui_->actionFileQuit->setEnabled(true);
2086 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
2088 // XXX Fix packet list heading menu sensitivity
2089 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2090 // !capture_in_progress);
2091 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2092 // !capture_in_progress);
2093 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2094 // !capture_in_progress);
2097 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
2098 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
2099 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
2100 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
2101 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
2102 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
2103 #endif /* HAVE_LIBPCAP */
2107 void MainWindow::setMenusForCaptureStopping() {
2108 main_ui_->actionFileQuit->setEnabled(false);
2109 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
2111 main_ui_->actionCaptureStart->setChecked(false);
2112 main_ui_->actionCaptureStop->setEnabled(false);
2113 main_ui_->actionCaptureRestart->setEnabled(false);
2114 #endif /* HAVE_LIBPCAP */
2117 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2119 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2121 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2122 // have_captured_packets);
2124 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2125 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2126 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2128 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2129 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2130 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2131 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2132 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2133 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2134 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2136 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2137 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2138 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2139 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2141 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2142 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2143 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2146 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2147 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2148 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2150 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2151 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2152 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2155 void MainWindow::setWindowIcon(const QIcon &icon) {
2156 wsApp->setWindowIcon(icon);
2157 QMainWindow::setWindowIcon(icon);
2160 void MainWindow::updateForUnsavedChanges() {
2161 setTitlebarForCaptureFile();
2162 setMenusForCaptureFile();
2165 void MainWindow::changeEvent(QEvent* event)
2169 switch (event->type())
2171 case QEvent::LanguageChange:
2172 main_ui_->retranslateUi(this);
2173 // make sure that the "Clear Menu" item is retranslated
2174 updateRecentFiles();
2176 case QEvent::LocaleChange:{
2177 QString locale = QLocale::system().name();
2178 locale.truncate(locale.lastIndexOf('_'));
2179 wsApp->loadLanguage(locale);
2186 QMainWindow::changeEvent(event);
2189 void MainWindow::resizeEvent(QResizeEvent *event)
2191 df_combo_box_->setMinimumWidth(width() * 2 / 3); // Arbitrary
2192 QMainWindow::resizeEvent(event);
2195 /* Update main window items based on whether there's a capture in progress. */
2196 void MainWindow::setForCaptureInProgress(bool capture_in_progress)
2198 setMenusForCaptureInProgress(capture_in_progress);
2200 wireless_frame_->setCaptureInProgress(capture_in_progress);
2203 packet_list_->setCaptureInProgress(capture_in_progress);
2204 packet_list_->setVerticalAutoScroll(capture_in_progress && main_ui_->actionGoAutoScroll->isChecked());
2206 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2210 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2211 << REGISTER_ANALYZE_GROUP_UNSORTED
2212 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2213 << REGISTER_STAT_GROUP_UNSORTED
2214 << REGISTER_STAT_GROUP_GENERIC
2215 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2216 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2217 << REGISTER_STAT_GROUP_RESPONSE_TIME
2218 << REGISTER_STAT_GROUP_TELEPHONY
2219 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2220 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2221 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2222 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2223 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2224 << REGISTER_TOOLS_GROUP_UNSORTED;
2226 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2228 foreach (QAction *action, actions) {
2229 switch (menu_group) {
2230 case REGISTER_ANALYZE_GROUP_UNSORTED:
2231 case REGISTER_STAT_GROUP_UNSORTED:
2232 main_ui_->menuStatistics->insertAction(
2233 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2236 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2237 main_ui_->menuServiceResponseTime->addAction(action);
2239 case REGISTER_STAT_GROUP_TELEPHONY:
2240 main_ui_->menuTelephony->addAction(action);
2242 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2243 main_ui_->menuANSI->addAction(action);
2245 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2246 main_ui_->menuGSM->addAction(action);
2248 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2249 main_ui_->menuLTE->addAction(action);
2251 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2252 main_ui_->menuMTP3->addAction(action);
2254 case REGISTER_TOOLS_GROUP_UNSORTED:
2256 // Allow the creation of submenus. Mimics the behavor of
2257 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2258 // and GtkUIManager.
2260 // For now we limit the insanity to the "Tools" menu.
2261 QStringList menu_path = action->text().split('/');
2262 QMenu *cur_menu = main_ui_->menuTools;
2263 while (menu_path.length() > 1) {
2264 QString menu_title = menu_path.takeFirst();
2265 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2266 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2268 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2269 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2272 submenu = cur_menu->addMenu(menu_title);
2273 submenu->setObjectName(menu_title.toLower());
2277 action->setText(menu_path.last());
2278 cur_menu->addAction(action);
2282 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2286 // Connect each action type to its corresponding slot. We to
2287 // distinguish various types of actions. Setting their objectName
2288 // seems to work OK.
2289 if (action->objectName() == TapParameterDialog::actionName()) {
2290 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2291 } else if (action->objectName() == FunnelStatistics::actionName()) {
2292 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2296 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2298 foreach (QAction *action, actions) {
2299 switch (menu_group) {
2300 case REGISTER_ANALYZE_GROUP_UNSORTED:
2301 case REGISTER_STAT_GROUP_UNSORTED:
2302 main_ui_->menuStatistics->removeAction(action);
2304 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2305 main_ui_->menuServiceResponseTime->removeAction(action);
2307 case REGISTER_STAT_GROUP_TELEPHONY:
2308 main_ui_->menuTelephony->removeAction(action);
2310 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2311 main_ui_->menuANSI->removeAction(action);
2313 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2314 main_ui_->menuGSM->removeAction(action);
2316 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2317 main_ui_->menuLTE->removeAction(action);
2319 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2320 main_ui_->menuMTP3->removeAction(action);
2322 case REGISTER_TOOLS_GROUP_UNSORTED:
2324 // Allow removal of submenus.
2325 // For now we limit the insanity to the "Tools" menu.
2326 QStringList menu_path = action->text().split('/');
2327 QMenu *cur_menu = main_ui_->menuTools;
2328 while (menu_path.length() > 1) {
2329 QString menu_title = menu_path.takeFirst();
2330 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2331 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2333 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2334 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2338 cur_menu->removeAction(action);
2342 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2348 void MainWindow::addDynamicMenus()
2351 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2352 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2353 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2354 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2355 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2356 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2358 // Fill in each menu
2359 foreach (register_stat_group_t menu_group, menu_groups) {
2360 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2361 addMenuActions(actions, menu_group);
2364 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2365 // We've added a placeholder in order to make sure some menus are visible.
2366 // Hide them as needed.
2367 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2368 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2370 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2371 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2373 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2374 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2376 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2377 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2381 void MainWindow::reloadDynamicMenus()
2383 foreach (register_stat_group_t menu_group, menu_groups) {
2384 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2385 removeMenuActions(actions, menu_group);
2387 actions = wsApp->addedMenuGroupItems(menu_group);
2388 addMenuActions(actions, menu_group);
2391 wsApp->clearAddedMenuGroupItems();
2392 wsApp->clearRemovedMenuGroupItems();
2395 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2397 QAction * itemAction = NULL;
2398 ext_menubar_t * item = NULL;
2399 GList * children = NULL;
2401 /* There must exists an xpath parent */
2402 g_assert(subMenu != NULL);
2404 /* If the depth counter exceeds, something must have gone wrong */
2405 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2407 children = menu->children;
2408 /* Iterate the child entries */
2409 while (children && children->data) {
2410 item = (ext_menubar_t *) children->data;
2412 if (item->type == EXT_MENUBAR_MENU) {
2413 /* Handle Submenu entry */
2414 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2415 } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2416 subMenu->addSeparator();
2417 } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2418 itemAction = subMenu->addAction(item->name);
2419 itemAction->setData(QVariant::fromValue((void *)item));
2420 itemAction->setText(item->label);
2421 connect(itemAction, SIGNAL(triggered()),
2422 this, SLOT(externalMenuItem_triggered()));
2426 children = g_list_next(children);
2430 QMenu * MainWindow::searchSubMenu(QString objectName)
2434 if (objectName.length() > 0) {
2435 QString searchName = QString("menu") + objectName;
2437 lst = main_ui_->menuBar->findChildren<QMenu*>();
2438 foreach (QMenu* m, lst) {
2439 if (QString::compare(m->objectName(), searchName) == 0)
2447 void MainWindow::addExternalMenus()
2449 QMenu * subMenu = NULL;
2450 GList * user_menu = NULL;
2451 ext_menu_t * menu = NULL;
2453 user_menu = ext_menubar_get_entries();
2455 while (user_menu && user_menu->data) {
2456 menu = (ext_menu_t *) user_menu->data;
2458 /* On this level only menu items should exist. Not doing an assert here,
2459 * as it could be an honest mistake */
2460 if (menu->type != EXT_MENUBAR_MENU) {
2461 user_menu = g_list_next(user_menu);
2465 /* Create main submenu and add it to the menubar */
2466 if (menu->parent_menu) {
2467 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2469 subMenu = sortUnderneath->addMenu(menu->label);
2473 subMenu = main_ui_->menuBar->addMenu(menu->label);
2475 /* This will generate the action structure for each menu. It is recursive,
2476 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2477 this->externalMenuHelper(menu, subMenu, 0);
2480 user_menu = g_list_next (user_menu);
2490 * indent-tabs-mode: nil
2493 * ex: set shiftwidth=4 tabstop=8 expandtab:
2494 * :indentSize=4:tabSize=8:noTabs=true: