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 was_maximized_(false),
291 capture_stopping_(false),
292 capture_filter_valid_(false)
294 , capture_interfaces_dialog_(NULL)
300 , pipe_notifier_(NULL)
302 #if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
306 if (!gbl_cur_main_window_) {
307 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
308 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
309 connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
310 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
312 gbl_cur_main_window_ = this;
314 capture_session_init(&cap_session_, CaptureFile::globalCapFile());
317 // setpUi calls QMetaObject::connectSlotsByName(this). connectSlotsByName
318 // iterates over *all* of our children, looking for matching "on_" slots.
319 // The fewer children we have at this point the better.
320 main_ui_->setupUi(this);
321 setWindowIcon(wsApp->normalIcon());
322 setTitlebarForCaptureFile();
323 setMenusForCaptureFile();
324 setForCapturedPackets(false);
325 setMenusForFileSet(false);
326 interfaceSelectionChanged();
327 loadWindowGeometry();
330 main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
333 qRegisterMetaType<FilterAction::Action>("FilterAction::Action");
334 qRegisterMetaType<FilterAction::ActionType>("FilterAction::ActionType");
335 connect(this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)),
336 this, SLOT(queuedFilterAction(QString,FilterAction::Action,FilterAction::ActionType)),
337 Qt::QueuedConnection);
339 //To prevent users use features before initialization complete
340 //Otherwise unexpected problems may occur
341 setFeaturesEnabled(false);
342 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
343 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(applyGlobalCommandLineOptions()));
344 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
345 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
346 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
347 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
348 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
349 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initConversationMenus()));
351 connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
352 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
353 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
354 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
355 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
356 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
358 connect(wsApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures()));
359 updateRecentCaptures();
361 df_combo_box_ = new DisplayFilterCombo();
362 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
363 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
364 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
365 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
366 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
367 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
368 connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
369 connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
370 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
371 connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
373 funnel_statistics_ = new FunnelStatistics(this, capture_file_);
374 connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
375 connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
376 connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
377 connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
378 this, SLOT(openCaptureFile(QString,QString)));
379 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
381 file_set_dialog_ = new FileSetDialog(this);
382 connect(file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
383 this, SLOT(openCaptureFile(QString)));
385 initMainToolbarIcons();
387 main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
389 // Make sure filter expressions overflow into a menu instead of a
390 // larger toolbar. We do this by adding them to a child toolbar.
391 // https://bugreports.qt.io/browse/QTBUG-2472
392 filter_expression_toolbar_ = new QToolBar();
393 filter_expression_toolbar_->setStyleSheet("QToolBar { background: none; border: none; }");
394 main_ui_->displayFilterToolBar->addWidget(filter_expression_toolbar_);
396 wireless_frame_ = new WirelessFrame(this);
397 main_ui_->wirelessToolBar->addWidget(wireless_frame_);
398 connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
399 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
400 connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
401 this, SLOT(showPreferencesDialog(QString)));
403 main_ui_->goToFrame->hide();
404 connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
405 main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
407 // XXX For some reason the cursor is drawn funny with an input mask set
408 // https://bugreports.qt-project.org/browse/QTBUG-7174
410 main_ui_->searchFrame->hide();
411 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
412 main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
413 connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
414 main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
416 main_ui_->addressEditorFrame->hide();
417 main_ui_->columnEditorFrame->hide();
418 main_ui_->preferenceEditorFrame->hide();
419 main_ui_->filterExpressionFrame->hide();
422 main_ui_->menuCapture->setEnabled(false);
425 // Set OS specific shortcuts for fullscreen mode
426 #if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
427 main_ui_->actionViewFullScreen->setShortcut(QKeySequence::FullScreen);
429 main_ui_->actionViewFullScreen->setShortcut(QKeySequence(Qt::Key_F11));
432 #if defined(Q_OS_MAC)
433 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
434 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
435 ntb->setIconSize(QSize(24, 24));
436 #endif // QT_MACEXTRAS_LIB
438 main_ui_->goToPacketLabel->setAttribute(Qt::WA_MacSmallSize, true);
439 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
440 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
441 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
443 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
447 #ifdef HAVE_SOFTWARE_UPDATE
448 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
449 QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
450 main_ui_->menuHelp->insertAction(update_sep, update_action);
451 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
453 master_split_.setObjectName("splitterMaster");
454 extra_split_.setObjectName("splitterExtra");
455 main_ui_->mainStack->addWidget(&master_split_);
457 empty_pane_.setObjectName("emptyPane");
459 packet_list_ = new PacketList(&master_split_);
461 proto_tree_ = new ProtoTree(&master_split_);
462 proto_tree_->installEventFilter(this);
464 byte_view_tab_ = new ByteViewTab(&master_split_);
466 packet_list_->setProtoTree(proto_tree_);
467 packet_list_->setByteViewTab(byte_view_tab_);
468 packet_list_->installEventFilter(this);
470 main_welcome_ = main_ui_->welcomePage;
472 // Packet list and proto tree must exist before these are called.
473 setMenusForSelectedPacket();
474 setMenusForSelectedTreeRow();
476 initShowHideMainWidgets();
477 initTimeDisplayFormatMenu();
478 initTimePrecisionFormatMenu();
480 updatePreferenceActions();
481 updateRecentActions();
482 setForCaptureInProgress(false);
484 setTabOrder(df_combo_box_->lineEdit(), packet_list_);
485 setTabOrder(packet_list_, proto_tree_);
487 connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
488 this, SLOT(captureCapturePrepared(capture_session *)));
489 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
490 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
491 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
492 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
493 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
494 this, SLOT(captureCaptureFixedStarted(capture_session *)));
495 connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
496 main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
497 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
498 this, SLOT(captureCaptureFixedFinished(capture_session *)));
499 connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
500 this, SLOT(captureCaptureStopping(capture_session *)));
501 connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
502 this, SLOT(captureCaptureFailed(capture_session *)));
503 connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
504 main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
506 connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
507 wsApp, SLOT(captureStarted()));
508 connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
509 wsApp, SLOT(captureFinished()));
510 connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
511 wsApp, SLOT(captureStarted()));
512 connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
513 wsApp, SLOT(captureFinished()));
515 connect(&capture_file_, SIGNAL(captureFileOpened()),
516 this, SLOT(captureFileOpened()));
517 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
518 this, SLOT(captureFileReadStarted()));
519 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
520 this, SLOT(captureFileReadFinished()));
521 connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
522 this, SLOT(captureFileReloadStarted()));
523 connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
524 this, SLOT(captureFileReadFinished()));
525 connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
526 this, SLOT(captureFileRescanStarted()));
527 connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
528 this, SLOT(captureFileReadFinished()));
529 connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
530 this, SLOT(captureFileRetapStarted()));
531 connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
532 this, SLOT(captureFileRetapFinished()));
533 connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
534 this, SLOT(captureFileFlushTapsData()));
535 connect(&capture_file_, SIGNAL(captureFileClosing()),
536 this, SLOT(captureFileClosing()));
537 connect(&capture_file_, SIGNAL(captureFileClosed()),
538 this, SLOT(captureFileClosed()));
540 connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
541 this, SLOT(captureFileSaveStarted(QString)));
542 connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
543 main_ui_->statusBar, SLOT(popFileStatus()));
544 connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
545 main_ui_->statusBar, SLOT(popFileStatus()));
546 connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
547 main_ui_->statusBar, SLOT(popFileStatus()));
549 connect(&capture_file_, SIGNAL(captureFileReadStarted()),
550 wsApp, SLOT(captureFileReadStarted()));
551 connect(&capture_file_, SIGNAL(captureFileReadFinished()),
552 wsApp, SLOT(updateTaps()));
554 connect(wsApp, SIGNAL(columnsChanged()),
555 packet_list_, SLOT(columnsChanged()));
556 connect(wsApp, SIGNAL(preferencesChanged()),
557 packet_list_, SLOT(preferencesChanged()));
558 connect(wsApp, SIGNAL(recentPreferencesRead()),
559 this, SLOT(applyRecentPaneGeometry()));
560 connect(wsApp, SIGNAL(recentPreferencesRead()),
561 this, SLOT(updateRecentActions()));
562 connect(wsApp, SIGNAL(packetDissectionChanged()),
563 this, SLOT(redissectPackets()), Qt::QueuedConnection);
564 connect(wsApp, SIGNAL(appInitialized()),
565 this, SLOT(filterExpressionsChanged()));
566 connect(wsApp, SIGNAL(filterExpressionsChanged()),
567 this, SLOT(filterExpressionsChanged()));
568 connect(wsApp, SIGNAL(checkDisplayFilter()),
569 this, SLOT(checkDisplayFilter()));
570 connect(wsApp, SIGNAL(fieldsChanged()),
571 this, SLOT(fieldsChanged()));
572 connect(wsApp, SIGNAL(reloadLuaPlugins()),
573 this, SLOT(reloadLuaPlugins()));
575 connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
576 this, SLOT(mainStackChanged(int)));
578 connect(main_welcome_, SIGNAL(startCapture()),
579 this, SLOT(startCapture()));
580 connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
581 this, SLOT(openCaptureFile(QString)));
582 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
583 main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
584 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
585 main_ui_->statusBar, SLOT(popFilterStatus()));
587 connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
588 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
589 connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
590 this, SLOT(redissectPackets()));
591 connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
592 this, SLOT(showPreferencesDialog(QString)));
593 connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
594 this, SLOT(showPreferencesDialog(QString)));
595 connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
596 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
597 connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
598 this, SLOT(filterExpressionsChanged()));
600 connect(this, SIGNAL(setCaptureFile(capture_file*)),
601 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
602 connect(this, SIGNAL(setCaptureFile(capture_file*)),
603 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
604 connect(this, SIGNAL(setCaptureFile(capture_file*)),
605 packet_list_, SLOT(setCaptureFile(capture_file*)));
606 connect(this, SIGNAL(setCaptureFile(capture_file*)),
607 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
609 connect(this, SIGNAL(monospaceFontChanged(QFont)),
610 packet_list_, SLOT(setMonospaceFont(QFont)));
611 connect(this, SIGNAL(monospaceFontChanged(QFont)),
612 proto_tree_, SLOT(setMonospaceFont(QFont)));
613 connect(this, SIGNAL(monospaceFontChanged(QFont)),
614 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
616 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
617 packet_list_, SLOT(goNextPacket()));
618 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
619 packet_list_, SLOT(goPreviousPacket()));
620 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
621 packet_list_, SLOT(goFirstPacket()));
622 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
623 packet_list_, SLOT(goLastPacket()));
625 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
626 proto_tree_, SLOT(expandSubtrees()));
627 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
628 proto_tree_, SLOT(expandAll()));
629 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
630 proto_tree_, SLOT(collapseAll()));
632 connect(packet_list_, SIGNAL(packetSelectionChanged()),
633 this, SLOT(setMenusForSelectedPacket()));
634 connect(packet_list_, SIGNAL(packetDissectionChanged()),
635 this, SLOT(redissectPackets()));
636 connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
637 this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
638 connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
639 this, SLOT(showPreferencesDialog(QString)));
640 connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
641 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
642 connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
643 connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
644 packet_list_, SLOT(columnsChanged()));
645 connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
646 this, SLOT(openPacketDialog()));
647 connect(packet_list_, SIGNAL(packetListScrolled(bool)),
648 main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
649 connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
650 main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
651 connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
652 main_ui_->statusBar, SLOT(popBusyStatus()));
653 connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
654 main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
655 connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
656 main_ui_->statusBar, SLOT(updateProgressStatus(int)));
657 connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
658 main_ui_->statusBar, SLOT(popProgressStatus()));
660 connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
661 main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
662 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
663 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
664 connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
665 this, SLOT(openPacketDialog(bool)));
666 connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
667 this, SLOT(showPreferencesDialog(QString)));
668 connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
669 main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
671 connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
672 main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
673 connect(byte_view_tab_, SIGNAL(currentChanged(int)),
674 this, SLOT(byteViewTabChanged(int)));
676 connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
677 this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
679 connect(main_ui_->statusBar, SIGNAL(stopLoading()),
680 &capture_file_, SLOT(stopLoading()));
682 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
683 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
686 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
688 connect(iface_tree, SIGNAL(itemSelectionChanged()),
689 this, SLOT(interfaceSelectionChanged()));
691 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
692 this, SLOT(captureFilterSyntaxChanged(bool)));
695 connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
696 this, SLOT(showExtcapOptionsDialog(QString&)));
699 #endif // HAVE_LIBPCAP
701 /* Create plugin_if hooks */
702 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
703 plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
704 plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
705 plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
707 plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
710 main_ui_->mainStack->setCurrentWidget(main_welcome_);
713 MainWindow::~MainWindow()
718 QString MainWindow::getFilter()
720 return df_combo_box_->currentText();
723 QMenu *MainWindow::createPopupMenu()
725 QMenu *menu = new QMenu();
726 menu->addAction(main_ui_->actionViewMainToolbar);
727 menu->addAction(main_ui_->actionViewFilterToolbar);
728 menu->addAction(main_ui_->actionViewWirelessToolbar);
729 menu->addAction(main_ui_->actionViewStatusBar);
730 menu->addSeparator();
731 menu->addAction(main_ui_->actionViewPacketList);
732 menu->addAction(main_ui_->actionViewPacketDetails);
733 menu->addAction(main_ui_->actionViewPacketBytes);
737 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
739 pipe_source_ = source;
740 pipe_child_process_ = child_process;
741 pipe_user_data_ = user_data;
742 pipe_input_cb_ = input_cb;
745 /* Tricky to use pipes in win9x, as no concept of wait. NT can
746 do this but that doesn't cover all win32 platforms. GTK can do
747 this but doesn't seem to work over processes. Attempt to do
748 something similar here, start a timer and check for data on every
750 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
753 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
757 pipe_timer_ = new QTimer(this);
758 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
759 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
760 pipe_timer_->start(200);
762 if (pipe_notifier_) {
763 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
764 delete pipe_notifier_;
767 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
768 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
769 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
770 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
774 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
776 // The user typed some text. Start filling in a filter.
777 // We may need to be more choosy here. We just need to catch events for the packet list,
778 // proto tree, and main welcome widgets.
779 if (event->type() == QEvent::KeyPress) {
780 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
781 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
782 df_combo_box_->lineEdit()->insert(kevt->text());
783 df_combo_box_->lineEdit()->setFocus();
788 return QMainWindow::eventFilter(obj, event);
791 void MainWindow::keyPressEvent(QKeyEvent *event) {
793 // Explicitly focus on the display filter combo.
794 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
795 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
799 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
800 if (event->modifiers() == Qt::NoModifier) {
801 if (event->key() == Qt::Key_Escape) {
802 on_goToCancel_clicked();
803 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
807 return; // goToLineEdit didn't want it and we don't either.
810 // Move up & down the packet list.
811 if (event->key() == Qt::Key_F7) {
812 packet_list_->goPreviousPacket();
813 } else if (event->key() == Qt::Key_F8) {
814 packet_list_->goNextPacket();
817 // Move along, citizen.
818 QMainWindow::keyPressEvent(event);
821 void MainWindow::closeEvent(QCloseEvent *event) {
822 saveWindowGeometry();
824 /* If we're in the middle of stopping a capture, don't do anything;
825 the user can try deleting the window after the capture stops. */
826 if (capture_stopping_) {
831 QString before_what(tr(" before quitting"));
832 if (!testCaptureFileClose(before_what, Quit)) {
838 if (capture_interfaces_dialog_) capture_interfaces_dialog_->close();
840 // Make sure we kill any open dumpcap processes.
841 delete main_welcome_;
843 // One of the many places we assume one main window.
844 if(!wsApp->isInitialized()) {
845 // If we're still initializing, QCoreApplication::quit() won't
846 // exit properly because we are not in the event loop. This
847 // means that the application won't clean up after itself. We
848 // might want to call wsApp->processEvents() during startup
849 // instead so that we can do a normal exit here.
855 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
858 foreach (QUrl drag_url, event->mimeData()->urls()) {
859 if (!drag_url.toLocalFile().isEmpty()) {
864 if (accept) event->acceptProposedAction();
867 void MainWindow::dropEvent(QDropEvent *event)
869 foreach (QUrl drop_url, event->mimeData()->urls()) {
870 QString local_file = drop_url.toLocalFile();
871 if (!local_file.isEmpty()) {
872 event->acceptProposedAction();
873 openCaptureFile(local_file);
879 // Apply recent settings to the main window geometry.
880 // We haven't loaded the preferences at this point so we assume that the
881 // position and size preference are enabled.
882 // Note we might end up with unexpected screen geometries if the user
883 // unplugs or plugs in a monitor:
884 // https://bugreports.qt.io/browse/QTBUG-44213
885 void MainWindow::loadWindowGeometry()
887 int min_sensible_dimension = 200;
890 if (recent.gui_geometry_main_maximized) {
891 setWindowState(Qt::WindowMaximized);
895 QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
896 recent.gui_geometry_main_width, recent.gui_geometry_main_height);
897 if (!rect_on_screen(recent_geom)) {
898 // We're not visible on any screens. See if we can move onscreen
900 recent_geom.moveTo(50, 50); // recent.c defaults to 20.
903 if (!rect_on_screen(recent_geom)) {
904 // Give up and use the default geometry.
908 // if (prefs.gui_geometry_save_position) {
909 move(recent_geom.topLeft());
912 if (// prefs.gui_geometry_save_size &&
913 recent_geom.width() > min_sensible_dimension &&
914 recent_geom.height() > min_sensible_dimension) {
915 resize(recent_geom.size());
920 void MainWindow::saveWindowGeometry()
922 if (prefs.gui_geometry_save_position) {
923 recent.gui_geometry_main_x = pos().x();
924 recent.gui_geometry_main_y = pos().y();
927 if (prefs.gui_geometry_save_size) {
928 recent.gui_geometry_main_width = size().width();
929 recent.gui_geometry_main_height = size().height();
932 if (prefs.gui_geometry_save_maximized) {
933 // On OS X this is false when it shouldn't be
934 recent.gui_geometry_main_maximized = isMaximized();
937 if (master_split_.sizes().length() > 0) {
938 recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
941 if (master_split_.sizes().length() > 2) {
942 recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
943 } else if (extra_split_.sizes().length() > 0) {
944 recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
948 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
950 case layout_pane_content_none:
952 case layout_pane_content_plist:
954 case layout_pane_content_pdetails:
956 case layout_pane_content_pbytes:
957 return byte_view_tab_;
959 g_assert_not_reached();
964 // Our event loop becomes nested whenever we call update_progress_dlg, which
965 // includes several places in file.c. The GTK+ UI stays out of trouble by
966 // showing a modal progress dialog. We attempt to do the equivalent below by
967 // disabling parts of the main window. At a minumum the ProgressFrame in the
968 // main status bar must remain accessible.
970 // We might want to do this any time the main status bar progress frame is
972 void MainWindow::freeze()
974 freeze_focus_ = wsApp->focusWidget();
976 // XXX Alternatively we could just disable and enable the main menu.
977 for (int i = 0; i < freeze_actions_.size(); i++) {
978 QAction *action = freeze_actions_[i].first;
979 freeze_actions_[i].second = action->isEnabled();
980 action->setEnabled(false);
982 main_ui_->centralWidget->setEnabled(false);
985 void MainWindow::thaw()
987 main_ui_->centralWidget->setEnabled(true);
988 for (int i = 0; i < freeze_actions_.size(); i++) {
989 freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
992 if (freeze_focus_) freeze_focus_->setFocus();
993 freeze_focus_ = NULL;
996 void MainWindow::mergeCaptureFile()
998 QString file_name = "";
999 QString read_filter = "";
1000 dfilter_t *rfcode = NULL;
1003 if (!capture_file_.capFile())
1006 if (prefs.gui_ask_unsaved) {
1007 if (cf_has_unsaved_data(capture_file_.capFile())) {
1008 QMessageBox msg_dialog;
1009 gchar *display_basename;
1012 msg_dialog.setIcon(QMessageBox::Question);
1013 /* This file has unsaved data; ask the user whether to save
1015 if (capture_file_.capFile()->is_tempfile) {
1016 msg_dialog.setText(tr("Save packets before merging?"));
1017 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
1020 * Format the message.
1022 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1023 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
1024 g_free(display_basename);
1025 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
1028 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
1029 msg_dialog.setDefaultButton(QMessageBox::Save);
1031 response = msg_dialog.exec();
1035 case QMessageBox::Save:
1036 /* Save the file but don't close it */
1037 saveCaptureFile(capture_file_.capFile(), false);
1040 case QMessageBox::Cancel:
1042 /* Don't do the merge. */
1049 CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1051 cf_status_t merge_status;
1052 char *in_filenames[2];
1055 if (merge_dlg.merge(file_name)) {
1058 if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1059 /* Not valid. Tell the user, and go back and run the file
1060 selection box again once they dismiss the alert. */
1061 // Similar to commandline_info.jfilter section in main().
1062 QMessageBox::warning(this, tr("Invalid Read Filter"),
1063 QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1072 file_type = capture_file_.capFile()->cd_t;
1074 /* Try to merge or append the two files */
1076 if (merge_dlg.mergeType() == 0) {
1077 /* chronological order */
1078 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1079 in_filenames[1] = qstring_strdup(file_name);
1080 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
1081 } else if (merge_dlg.mergeType() <= 0) {
1083 in_filenames[0] = qstring_strdup(file_name);
1084 in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1085 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1088 in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1089 in_filenames[1] = qstring_strdup(file_name);
1090 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1093 g_free(in_filenames[0]);
1094 g_free(in_filenames[1]);
1096 if (merge_status != CF_OK) {
1098 dfilter_free(rfcode);
1103 cf_close(capture_file_.capFile());
1105 /* Try to open the merged capture file. */
1106 CaptureFile::globalCapFile()->window = this;
1107 if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1108 /* We couldn't open it; fail. */
1109 CaptureFile::globalCapFile()->window = NULL;
1111 dfilter_free(rfcode);
1116 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1117 it closed the previous capture file, and thus destroyed any
1118 previous read filter attached to "cf"). */
1119 cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1121 switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1125 /* Just because we got an error, that doesn't mean we were unable
1126 to read any of the file; we handle what we could get from the
1130 case CF_READ_ABORTED:
1131 /* The user bailed out of re-reading the capture file; the
1132 capture file has been closed - just free the capture file name
1133 string and return (without changing the last containing
1139 /* Save the name of the containing directory specified in the path name,
1140 if any; we can write over cf_merged_name, which is a good thing, given that
1141 "get_dirname()" does write over its argument. */
1142 wsApp->setLastOpenDir(get_dirname(tmpname));
1144 main_ui_->statusBar->showExpert();
1150 void MainWindow::importCaptureFile() {
1151 ImportTextDialog import_dlg;
1153 QString before_what(tr(" before importing a capture"));
1154 if (!testCaptureFileClose(before_what))
1159 if (import_dlg.result() != QDialog::Accepted) {
1160 main_ui_->mainStack->setCurrentWidget(main_welcome_);
1164 openCaptureFile(import_dlg.capfileName());
1167 bool MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1169 gboolean discard_comments;
1171 if (cf->is_tempfile) {
1172 /* This is a temporary capture file, so saving it means saving
1173 it to a permanent file. Prompt the user for a location
1174 to which to save it. Don't require that the file format
1175 support comments - if it's a temporary capture file, it's
1176 probably pcap-ng, which supports comments and, if it's
1177 not pcap-ng, let the user decide what they want to do
1178 if they've added comments. */
1179 return saveAsCaptureFile(cf, FALSE, dont_reopen);
1181 if (cf->unsaved_changes) {
1182 cf_write_status_t status;
1184 /* This is not a temporary capture file, but it has unsaved
1185 changes, so saving it means doing a "safe save" on top
1186 of the existing file, in the same format - no UI needed
1187 unless the file has comments and the file's format doesn't
1190 If the file has comments, does the file's format support them?
1191 If not, ask the user whether they want to discard the comments
1192 or choose a different format. */
1193 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1196 /* The file can be saved in the specified format as is;
1197 just drive on and save in the format they selected. */
1198 discard_comments = FALSE;
1201 case SAVE_WITHOUT_COMMENTS:
1202 /* The file can't be saved in the specified format as is,
1203 but it can be saved without the comments, and the user
1204 said "OK, discard the comments", so save it in the
1205 format they specified without the comments. */
1206 discard_comments = TRUE;
1209 case SAVE_IN_ANOTHER_FORMAT:
1210 /* There are file formats in which we can save this that
1211 support comments, and the user said not to delete the
1212 comments. Do a "Save As" so the user can select
1213 one of those formats and choose a file name. */
1214 return saveAsCaptureFile(cf, TRUE, dont_reopen);
1217 /* The user said "forget it". Just return. */
1221 /* Squelch warnings that discard_comments is being used
1223 g_assert_not_reached();
1227 /* XXX - cf->filename might get freed out from under us, because
1228 the code path through which cf_save_records() goes currently
1229 closes the current file and then opens and reloads the saved file,
1230 so make a copy and free it later. */
1231 file_name = cf->filename;
1232 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1233 discard_comments, dont_reopen);
1237 /* The save succeeded; we're done.
1238 If we discarded comments, redraw the packet list to reflect
1239 any packets that no longer have comments. */
1240 if (discard_comments)
1241 packet_list_queue_draw();
1243 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1244 updateForUnsavedChanges(); // we update the title bar to remove the *
1247 case CF_WRITE_ERROR:
1248 /* The write failed.
1249 XXX - OK, what do we do now? Let them try a
1250 "Save As", in case they want to try to save to a
1251 different directory or file system? */
1254 case CF_WRITE_ABORTED:
1255 /* The write was aborted; just drive on. */
1259 /* Otherwise just do nothing. */
1265 bool MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1266 QString file_name = "";
1268 gboolean compressed;
1269 cf_write_status_t status;
1271 gboolean discard_comments = FALSE;
1278 CaptureFileDialog save_as_dlg(this, cf);
1280 /* If the file has comments, does the format the user selected
1281 support them? If not, ask the user whether they want to
1282 discard the comments or choose a different format. */
1283 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1286 /* The file can be saved in the specified format as is;
1287 just drive on and save in the format they selected. */
1288 discard_comments = FALSE;
1291 case SAVE_WITHOUT_COMMENTS:
1292 /* The file can't be saved in the specified format as is,
1293 but it can be saved without the comments, and the user
1294 said "OK, discard the comments", so save it in the
1295 format they specified without the comments. */
1296 discard_comments = TRUE;
1299 case SAVE_IN_ANOTHER_FORMAT:
1300 /* There are file formats in which we can save this that
1301 support comments, and the user said not to delete the
1302 comments. The combo box of file formats has had the
1303 formats that don't support comments trimmed from it,
1304 so run the dialog again, to let the user decide
1305 whether to save in one of those formats or give up. */
1306 must_support_comments = TRUE;
1310 /* The user said "forget it". Just get rid of the dialog box
1314 file_type = save_as_dlg.selectedFileType();
1315 compressed = save_as_dlg.isCompressed();
1317 fileAddExtension(file_name, file_type, compressed);
1320 // /* If the file exists and it's user-immutable or not writable,
1321 // ask the user whether they want to override that. */
1322 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1323 // /* They don't. Let them try another file name or cancel. */
1328 /* Attempt to save the file */
1329 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1330 discard_comments, dont_reopen);
1334 /* The save succeeded; we're done. */
1335 /* Save the directory name for future file dialogs. */
1336 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1337 set_last_open_dir(get_dirname(dirname));
1339 /* If we discarded comments, redraw the packet list to reflect
1340 any packets that no longer have comments. */
1341 if (discard_comments)
1342 packet_list_queue_draw();
1344 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1345 updateForUnsavedChanges(); // we update the title bar to remove the *
1346 /* Add this filename to the list of recent files in the "Recent Files" submenu */
1347 add_menu_recent_capture_file(file_name.toUtf8().constData());
1350 case CF_WRITE_ERROR:
1351 /* The save failed; let the user try again. */
1354 case CF_WRITE_ABORTED:
1355 /* The user aborted the save; just return. */
1362 void MainWindow::exportSelectedPackets() {
1363 QString file_name = "";
1365 gboolean compressed;
1366 packet_range_t range;
1367 cf_write_status_t status;
1369 gboolean discard_comments = FALSE;
1371 if (!capture_file_.capFile())
1374 /* Init the packet range */
1375 packet_range_init(&range, capture_file_.capFile());
1376 range.process_filtered = TRUE;
1377 range.include_dependents = TRUE;
1380 CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1382 /* If the file has comments, does the format the user selected
1383 support them? If not, ask the user whether they want to
1384 discard the comments or choose a different format. */
1385 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1388 /* The file can be saved in the specified format as is;
1389 just drive on and save in the format they selected. */
1390 discard_comments = FALSE;
1393 case SAVE_WITHOUT_COMMENTS:
1394 /* The file can't be saved in the specified format as is,
1395 but it can be saved without the comments, and the user
1396 said "OK, discard the comments", so save it in the
1397 format they specified without the comments. */
1398 discard_comments = TRUE;
1401 case SAVE_IN_ANOTHER_FORMAT:
1402 /* There are file formats in which we can save this that
1403 support comments, and the user said not to delete the
1404 comments. The combo box of file formats has had the
1405 formats that don't support comments trimmed from it,
1406 so run the dialog again, to let the user decide
1407 whether to save in one of those formats or give up. */
1411 /* The user said "forget it". Just get rid of the dialog box
1417 * Check that we're not going to save on top of the current
1419 * We do it here so we catch all cases ...
1420 * Unfortunately, the file requester gives us an absolute file
1421 * name and the read file name may be relative (if supplied on
1422 * the command line). From Joerg Mayer.
1424 if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1425 QMessageBox msg_box;
1426 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1428 msg_box.setIcon(QMessageBox::Critical);
1429 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1430 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1431 msg_box.setStandardButtons(QMessageBox::Ok);
1432 msg_box.setDefaultButton(QMessageBox::Ok);
1434 g_free(display_basename);
1438 file_type = esp_dlg.selectedFileType();
1439 compressed = esp_dlg.isCompressed();
1440 fileAddExtension(file_name, file_type, compressed);
1443 // /* If the file exists and it's user-immutable or not writable,
1444 // ask the user whether they want to override that. */
1445 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1446 // /* They don't. Let them try another file name or cancel. */
1451 /* Attempt to save the file */
1452 status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1456 /* The save succeeded; we're done. */
1457 /* Save the directory name for future file dialogs. */
1458 dirname = qstring_strdup(file_name); /* Overwrites cf_name */
1459 set_last_open_dir(get_dirname(dirname));
1461 /* If we discarded comments, redraw the packet list to reflect
1462 any packets that no longer have comments. */
1463 if (discard_comments)
1464 packet_list_queue_draw();
1467 case CF_WRITE_ERROR:
1468 /* The save failed; let the user try again. */
1471 case CF_WRITE_ABORTED:
1472 /* The user aborted the save; just return. */
1479 void MainWindow::exportDissections(export_type_e export_type) {
1480 ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1481 packet_range_t range;
1483 if (!capture_file_.capFile())
1486 /* Init the packet range */
1487 packet_range_init(&range, capture_file_.capFile());
1488 range.process_filtered = TRUE;
1489 range.include_dependents = TRUE;
1494 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1495 QString file_name_lower;
1496 QString file_suffix;
1497 GSList *extensions_list;
1498 gboolean add_extension;
1501 * Append the default file extension if there's none given by
1502 * the user or if they gave one that's not one of the valid
1503 * extensions for the file type.
1505 file_name_lower = file_name.toLower();
1506 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1507 if (extensions_list != NULL) {
1510 /* We have one or more extensions for this file type.
1511 Start out assuming we need to add the default one. */
1512 add_extension = TRUE;
1514 /* OK, see if the file has one of those extensions. */
1515 for (extension = extensions_list; extension != NULL;
1516 extension = g_slist_next(extension)) {
1517 file_suffix += tr(".") + (char *)extension->data;
1518 if (file_name_lower.endsWith(file_suffix)) {
1520 * The file name has one of the extensions for
1523 add_extension = FALSE;
1526 file_suffix += ".gz";
1527 if (file_name_lower.endsWith(file_suffix)) {
1529 * The file name has one of the extensions for
1532 add_extension = FALSE;
1537 /* We have no extensions for this file type. Don't add one. */
1538 add_extension = FALSE;
1540 if (add_extension) {
1541 if (wtap_default_file_extension(file_type) != NULL) {
1542 file_name += tr(".") + wtap_default_file_extension(file_type);
1550 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1551 bool capture_in_progress = false;
1552 bool do_close_file = false;
1554 if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1555 return true; /* Already closed, nothing to do */
1558 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1559 /* This is true if we're reading a capture file *or* if we're doing
1560 a live capture. If we're reading a capture file, the main loop
1561 is busy reading packets, and only accepting input from the
1562 progress dialog, so we can't get here, so this means we're
1564 capture_in_progress = true;
1568 if (prefs.gui_ask_unsaved) {
1569 if (cf_has_unsaved_data(capture_file_.capFile()) ||
1570 (capture_in_progress && capture_file_.capFile()->count > 0))
1572 QMessageBox msg_dialog;
1575 QPushButton *save_button;
1576 QPushButton *discard_button;
1578 msg_dialog.setIcon(QMessageBox::Question);
1579 msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1581 /* This file has unsaved data or there's a capture in
1582 progress; ask the user whether to save the data. */
1583 if (capture_in_progress && context != Restart) {
1584 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1585 infotext = tr("Your captured packets will be lost if you don't save them.");
1586 } else if (capture_file_.capFile()->is_tempfile) {
1587 if (context == Reload) {
1588 // Reloading a tempfile will keep the packets, so this is not unsaved packets
1589 question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1590 infotext = tr("Your changes will be lost if you don't save them.");
1592 question = tr("Do you want to save the captured packets%1?").arg(before_what);
1593 infotext = tr("Your captured packets will be lost if you don't save them.");
1596 // No capture in progress and not a tempfile, so this is not unsaved packets
1597 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1598 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1599 infotext = tr("Your changes will be lost if you don't save them.");
1600 g_free(display_basename);
1603 msg_dialog.setText(question);
1604 msg_dialog.setInformativeText(infotext);
1606 // XXX Text comes from ui/gtk/stock_icons.[ch]
1607 // Note that the button roles differ from the GTK+ version.
1608 // Cancel = RejectRole
1609 // Save = AcceptRole
1610 // Don't Save = DestructiveRole
1611 msg_dialog.addButton(QMessageBox::Cancel);
1613 if (capture_in_progress) {
1614 QString save_button_text;
1615 if (context == Restart) {
1616 save_button_text = tr("Save before Continue");
1618 save_button_text = tr("Stop and Save");
1620 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1622 save_button = msg_dialog.addButton(QMessageBox::Save);
1624 msg_dialog.setDefaultButton(save_button);
1626 QString discard_button_text;
1627 if (capture_in_progress) {
1630 discard_button_text = tr("Stop and Quit &without Saving");
1633 discard_button_text = tr("Continue &without Saving");
1636 discard_button_text = tr("Stop and Continue &without Saving");
1642 discard_button_text = tr("Quit &without Saving");
1646 discard_button_text = tr("Continue &without Saving");
1650 discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1653 /* According to the Qt doc:
1654 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1656 * Therefore we should use clickedButton() to determine which button was clicked. */
1658 if (msg_dialog.clickedButton() == save_button) {
1660 /* If there's a capture in progress, we have to stop the capture
1661 and then do the save. */
1662 if (capture_in_progress)
1665 /* Save the file and close it */
1666 if (saveCaptureFile(capture_file_.capFile(), true) == false)
1668 do_close_file = true;
1669 } else if(msg_dialog.clickedButton() == discard_button) {
1670 /* Just close the file, discarding changes */
1671 do_close_file = true;
1673 // cancelButton or some other unspecified button
1677 /* Unchanged file or capturing with no packets */
1678 do_close_file = true;
1681 /* User asked not to be bothered by those prompts, just close it.
1682 XXX - should that apply only to saving temporary files? */
1683 do_close_file = true;
1686 if (do_close_file) {
1688 /* If there's a capture in progress, we have to stop the capture
1689 and then do the close. */
1690 if (capture_in_progress)
1693 /* captureStop() will close the file if not having any packets */
1694 if (capture_file_.capFile() && context != Restart && context != Reload)
1695 // Don't really close if Restart or Reload
1696 cf_close(capture_file_.capFile());
1699 return true; /* File closed */
1702 void MainWindow::captureStop() {
1705 while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1706 WiresharkApplication::processEvents();
1710 void MainWindow::initMainToolbarIcons()
1712 // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1713 int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1714 #if !defined(Q_OS_WIN)
1715 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1716 // The OS X HIG specifies 32-pixel icons but they're a little too
1718 icon_size = icon_size * 3 / 2;
1720 main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1722 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1723 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1725 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1726 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1727 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1728 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1730 // Menu icons are disabled in main_window.ui for these items.
1731 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1732 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1733 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1734 main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1736 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1737 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1738 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1739 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1740 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1741 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1742 main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1743 main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1744 #if defined(Q_OS_MAC)
1745 main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1746 main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1748 main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1750 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1752 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1753 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1754 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1755 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1756 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1757 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1758 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1761 void MainWindow::initShowHideMainWidgets()
1763 if (show_hide_actions_) {
1767 show_hide_actions_ = new QActionGroup(this);
1768 QMap<QAction *, QWidget *> shmw_actions;
1770 show_hide_actions_->setExclusive(false);
1771 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1772 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1773 shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1774 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1775 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1776 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1777 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1779 foreach (QAction *shmwa, shmw_actions.keys()) {
1780 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1781 show_hide_actions_->addAction(shmwa);
1782 showHideMainWidgets(shmwa);
1785 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1788 Q_DECLARE_METATYPE(ts_type)
1790 void MainWindow::initTimeDisplayFormatMenu()
1792 if (time_display_actions_) {
1796 time_display_actions_ = new QActionGroup(this);
1798 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1799 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1800 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1801 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1802 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1803 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1804 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1805 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1806 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1807 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1809 foreach (QAction* tda, td_actions.keys()) {
1810 tda->setData(qVariantFromValue(td_actions[tda]));
1811 time_display_actions_->addAction(tda);
1814 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1817 Q_DECLARE_METATYPE(ts_precision)
1819 void MainWindow::initTimePrecisionFormatMenu()
1821 if (time_precision_actions_) {
1825 time_precision_actions_ = new QActionGroup(this);
1827 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1828 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1829 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1830 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1831 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1832 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1833 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1835 foreach (QAction* tpa, tp_actions.keys()) {
1836 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1837 time_precision_actions_->addAction(tpa);
1840 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1843 // Menu items which will be disabled when we freeze() and whose state will
1844 // be restored when we thaw(). Add to the list as needed.
1845 void MainWindow::initFreezeActions()
1847 QList<QAction *> freeze_actions = QList<QAction *>()
1848 << main_ui_->actionFileClose
1849 << main_ui_->actionViewReload
1850 << main_ui_->actionEditMarkPacket
1851 << main_ui_->actionEditMarkAllDisplayed
1852 << main_ui_->actionEditUnmarkAllDisplayed
1853 << main_ui_->actionEditIgnorePacket
1854 << main_ui_->actionEditIgnoreAllDisplayed
1855 << main_ui_->actionEditUnignoreAllDisplayed
1856 << main_ui_->actionEditSetTimeReference
1857 << main_ui_->actionEditUnsetAllTimeReferences;
1859 foreach (QAction *action, freeze_actions) {
1860 freeze_actions_ << QPair<QAction *, bool>(action, false);
1864 void MainWindow::initConversationMenus()
1868 QList<QAction *> cc_actions = QList<QAction *>()
1869 << main_ui_->actionViewColorizeConversation1 << main_ui_->actionViewColorizeConversation2
1870 << main_ui_->actionViewColorizeConversation3 << main_ui_->actionViewColorizeConversation4
1871 << main_ui_->actionViewColorizeConversation5 << main_ui_->actionViewColorizeConversation6
1872 << main_ui_->actionViewColorizeConversation7 << main_ui_->actionViewColorizeConversation8
1873 << main_ui_->actionViewColorizeConversation9 << main_ui_->actionViewColorizeConversation10;
1875 for (GList *conv_filter_list_entry = conv_filter_list; conv_filter_list_entry; conv_filter_list_entry = g_list_next(conv_filter_list_entry)) {
1877 conversation_filter_t* conv_filter = (conversation_filter_t *)conv_filter_list_entry->data;
1878 ConversationAction *conv_action = new ConversationAction(main_ui_->menuConversationFilter, conv_filter);
1879 main_ui_->menuConversationFilter->addAction(conv_action);
1881 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
1882 connect(conv_action, SIGNAL(triggered()), this, SLOT(applyConversationFilter()));
1884 // Packet list context menu items
1885 packet_list_->conversationMenu()->addAction(conv_action);
1887 QMenu *submenu = packet_list_->colorizeMenu()->addMenu(conv_action->text());
1890 foreach (QAction *cc_action, cc_actions) {
1891 conv_action = new ConversationAction(submenu, conv_filter);
1892 conv_action->setText(cc_action->text());
1893 conv_action->setIcon(cc_action->icon());
1894 conv_action->setColorNumber(i++);
1895 submenu->addAction(conv_action);
1896 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
1897 connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
1900 conv_action = new ConversationAction(submenu, conv_filter);
1901 conv_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
1902 submenu->addAction(conv_action);
1903 connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
1904 connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
1906 // Proto tree conversation menu is filled in in ProtoTree::contextMenuEvent.
1907 // We should probably do that here.
1910 // Proto tree colorization items
1912 ColorizeAction *colorize_action;
1913 foreach (QAction *cc_action, cc_actions) {
1914 colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
1915 colorize_action->setText(cc_action->text());
1916 colorize_action->setIcon(cc_action->icon());
1917 colorize_action->setColorNumber(i++);
1918 proto_tree_->colorizeMenu()->addAction(colorize_action);
1919 connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
1920 connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
1923 colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
1924 colorize_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
1925 proto_tree_->colorizeMenu()->addAction(colorize_action);
1926 connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
1927 connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
1931 void MainWindow::setTitlebarForCaptureFile()
1933 if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1934 if (capture_file_.capFile()->is_tempfile) {
1936 // For a temporary file, put the source of the data
1937 // in the window title, not whatever random pile
1938 // of characters is the last component of the path
1941 // XXX - on non-Mac platforms, put in the application
1944 setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1947 // For a user file, set the full path; that way,
1948 // for OS X, it'll set the "proxy icon". Qt
1949 // handles extracting the last component.
1951 // Sadly, some UN*Xes don't necessarily use UTF-8
1952 // for their file names, so we have to map the
1953 // file path to UTF-8. If that fails, we're somewhat
1956 char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1961 if (utf8_filename) {
1962 QFileInfo fi(utf8_filename);
1963 setWSWindowTitle(QString("[*]%1").arg(fi.fileName()));
1964 setWindowFilePath(utf8_filename);
1965 g_free(utf8_filename);
1967 // So what the heck else can we do here?
1968 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1971 setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1973 /* We have no capture file. */
1978 QString MainWindow::replaceWindowTitleVariables(QString title)
1980 title.replace ("%P", get_profile_name());
1981 title.replace ("%V", get_ws_vcs_version_info());
1986 void MainWindow::setWSWindowTitle(QString title)
1988 if (title.isEmpty()) {
1989 title = tr("The Wireshark Network Analyzer");
1992 if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
1993 QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
1994 title.prepend(QString("[%1] ").arg(custom_title));
1997 if (prefs.gui_window_title && prefs.gui_window_title[0]) {
1998 QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
2000 // On OS X we separate the titles with a unicode em dash
2001 title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
2003 title.append(QString(" [%1]").arg(custom_title));
2007 setWindowTitle(title);
2008 setWindowFilePath(NULL);
2011 void MainWindow::setTitlebarForCaptureInProgress()
2013 if (capture_file_.capFile()) {
2014 setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
2016 /* We have no capture in progress. */
2023 /* Enable or disable menu items based on whether you have a capture file
2024 you've finished reading and, if you have one, whether it's been saved
2025 and whether it could be saved except by copying the raw packet data. */
2026 void MainWindow::setMenusForCaptureFile(bool force_disable)
2029 bool can_write = false;
2030 bool can_save = false;
2031 bool can_save_as = false;
2033 if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
2034 /* We have no capture file or we're currently reading a file */
2037 /* We have a capture file. Can we write or save? */
2038 can_write = cf_can_write_with_wiretap(capture_file_.capFile());
2039 can_save = cf_can_save(capture_file_.capFile());
2040 can_save_as = cf_can_save_as(capture_file_.capFile());
2043 main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(enable);
2044 main_ui_->actionFileMerge->setEnabled(can_write);
2045 main_ui_->actionFileClose->setEnabled(enable);
2046 main_ui_->actionFileSave->setEnabled(can_save);
2047 main_ui_->actionFileSaveAs->setEnabled(can_save_as);
2048 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(enable);
2050 * "Export Specified Packets..." should be available only if
2051 * we can write the file out in at least one format.
2053 main_ui_->actionFileExportPackets->setEnabled(can_write);
2055 main_ui_->actionFileExportAsCArrays->setEnabled(enable);
2056 main_ui_->actionFileExportAsCSV->setEnabled(enable);
2057 main_ui_->actionFileExportAsPDML->setEnabled(enable);
2058 main_ui_->actionFileExportAsPlainText->setEnabled(enable);
2059 main_ui_->actionFileExportAsPSML->setEnabled(enable);
2060 main_ui_->actionFileExportAsJSON->setEnabled(enable);
2062 main_ui_->actionFileExportPacketBytes->setEnabled(enable);
2063 main_ui_->actionFileExportPDU->setEnabled(enable);
2064 main_ui_->actionFileExportSSLSessionKeys->setEnabled(enable);
2066 foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2067 eo_action->setEnabled(enable);
2070 main_ui_->actionViewReload->setEnabled(enable);
2073 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
2074 /* Either a capture was started or stopped; in either case, it's not
2075 in the process of stopping, so allow quitting. */
2077 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
2078 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
2080 main_ui_->actionFileExportAsCArrays->setEnabled(capture_in_progress);
2081 main_ui_->actionFileExportAsCSV->setEnabled(capture_in_progress);
2082 main_ui_->actionFileExportAsPDML->setEnabled(capture_in_progress);
2083 main_ui_->actionFileExportAsPlainText->setEnabled(capture_in_progress);
2084 main_ui_->actionFileExportAsPSML->setEnabled(capture_in_progress);
2085 main_ui_->actionFileExportAsJSON->setEnabled(capture_in_progress);
2087 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
2088 main_ui_->actionFileExportPDU->setEnabled(!capture_in_progress);
2089 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
2091 foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
2092 eo_action->setEnabled(capture_in_progress);
2095 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
2096 main_ui_->actionFileQuit->setEnabled(true);
2098 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
2100 // XXX Fix packet list heading menu sensitivity
2101 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
2102 // !capture_in_progress);
2103 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
2104 // !capture_in_progress);
2105 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
2106 // !capture_in_progress);
2109 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
2110 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
2111 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
2112 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
2113 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
2114 main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
2115 #endif /* HAVE_LIBPCAP */
2119 void MainWindow::setMenusForCaptureStopping() {
2120 main_ui_->actionFileQuit->setEnabled(false);
2121 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
2123 main_ui_->actionCaptureStart->setChecked(false);
2124 main_ui_->actionCaptureStop->setEnabled(false);
2125 main_ui_->actionCaptureRestart->setEnabled(false);
2126 #endif /* HAVE_LIBPCAP */
2129 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2131 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2133 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2134 // have_captured_packets);
2136 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2137 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2138 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2140 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2141 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2142 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2143 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2144 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2145 main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2146 main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2148 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2149 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2150 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2151 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2153 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2154 main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2155 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2158 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2159 bool enable_next = fileset_get_next() != NULL && enable_list_files;
2160 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2162 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2163 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2164 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2167 void MainWindow::setWindowIcon(const QIcon &icon) {
2168 wsApp->setWindowIcon(icon);
2169 QMainWindow::setWindowIcon(icon);
2172 void MainWindow::updateForUnsavedChanges() {
2173 setTitlebarForCaptureFile();
2174 setMenusForCaptureFile();
2177 void MainWindow::changeEvent(QEvent* event)
2181 switch (event->type())
2183 case QEvent::LanguageChange:
2184 main_ui_->retranslateUi(this);
2185 // make sure that the "Clear Menu" item is retranslated
2186 wsApp->emitAppSignal(WiresharkApplication::RecentCapturesChanged);
2188 case QEvent::LocaleChange:{
2189 QString locale = QLocale::system().name();
2190 locale.truncate(locale.lastIndexOf('_'));
2191 wsApp->loadLanguage(locale);
2194 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
2195 case QEvent::WindowStateChange:
2196 main_ui_->actionViewFullScreen->setChecked(this->isFullScreen());
2203 QMainWindow::changeEvent(event);
2206 void MainWindow::resizeEvent(QResizeEvent *event)
2208 df_combo_box_->setMinimumWidth(width() * 2 / 3); // Arbitrary
2209 QMainWindow::resizeEvent(event);
2212 /* Update main window items based on whether there's a capture in progress. */
2213 void MainWindow::setForCaptureInProgress(bool capture_in_progress)
2215 setMenusForCaptureInProgress(capture_in_progress);
2217 wireless_frame_->setCaptureInProgress(capture_in_progress);
2220 packet_list_->setCaptureInProgress(capture_in_progress);
2221 packet_list_->setVerticalAutoScroll(capture_in_progress && main_ui_->actionGoAutoScroll->isChecked());
2223 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2227 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2228 << REGISTER_ANALYZE_GROUP_UNSORTED
2229 << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2230 << REGISTER_STAT_GROUP_UNSORTED
2231 << REGISTER_STAT_GROUP_GENERIC
2232 << REGISTER_STAT_GROUP_CONVERSATION_LIST
2233 << REGISTER_STAT_GROUP_ENDPOINT_LIST
2234 << REGISTER_STAT_GROUP_RESPONSE_TIME
2235 << REGISTER_STAT_GROUP_TELEPHONY
2236 << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2237 << REGISTER_STAT_GROUP_TELEPHONY_GSM
2238 << REGISTER_STAT_GROUP_TELEPHONY_LTE
2239 << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2240 << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2241 << REGISTER_TOOLS_GROUP_UNSORTED;
2243 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2245 foreach (QAction *action, actions) {
2246 switch (menu_group) {
2247 case REGISTER_ANALYZE_GROUP_UNSORTED:
2248 case REGISTER_STAT_GROUP_UNSORTED:
2249 main_ui_->menuStatistics->insertAction(
2250 main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2253 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2254 main_ui_->menuServiceResponseTime->addAction(action);
2256 case REGISTER_STAT_GROUP_TELEPHONY:
2257 main_ui_->menuTelephony->addAction(action);
2259 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2260 main_ui_->menuANSI->addAction(action);
2262 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2263 main_ui_->menuGSM->addAction(action);
2265 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2266 main_ui_->menuLTE->addAction(action);
2268 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2269 main_ui_->menuMTP3->addAction(action);
2271 case REGISTER_TOOLS_GROUP_UNSORTED:
2273 // Allow the creation of submenus. Mimics the behavor of
2274 // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2275 // and GtkUIManager.
2277 // For now we limit the insanity to the "Tools" menu.
2278 QStringList menu_path = action->text().split('/');
2279 QMenu *cur_menu = main_ui_->menuTools;
2280 while (menu_path.length() > 1) {
2281 QString menu_title = menu_path.takeFirst();
2282 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2283 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2285 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2286 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2289 submenu = cur_menu->addMenu(menu_title);
2290 submenu->setObjectName(menu_title.toLower());
2294 action->setText(menu_path.last());
2295 cur_menu->addAction(action);
2299 // qDebug() << "FIX: Add" << action->text() << "to the menu";
2303 // Connect each action type to its corresponding slot. We to
2304 // distinguish various types of actions. Setting their objectName
2305 // seems to work OK.
2306 if (action->objectName() == TapParameterDialog::actionName()) {
2307 connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2308 } else if (action->objectName() == FunnelStatistics::actionName()) {
2309 connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2313 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2315 foreach (QAction *action, actions) {
2316 switch (menu_group) {
2317 case REGISTER_ANALYZE_GROUP_UNSORTED:
2318 case REGISTER_STAT_GROUP_UNSORTED:
2319 main_ui_->menuStatistics->removeAction(action);
2321 case REGISTER_STAT_GROUP_RESPONSE_TIME:
2322 main_ui_->menuServiceResponseTime->removeAction(action);
2324 case REGISTER_STAT_GROUP_TELEPHONY:
2325 main_ui_->menuTelephony->removeAction(action);
2327 case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2328 main_ui_->menuANSI->removeAction(action);
2330 case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2331 main_ui_->menuGSM->removeAction(action);
2333 case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2334 main_ui_->menuLTE->removeAction(action);
2336 case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2337 main_ui_->menuMTP3->removeAction(action);
2339 case REGISTER_TOOLS_GROUP_UNSORTED:
2341 // Allow removal of submenus.
2342 // For now we limit the insanity to the "Tools" menu.
2343 QStringList menu_path = action->text().split('/');
2344 QMenu *cur_menu = main_ui_->menuTools;
2345 while (menu_path.length() > 1) {
2346 QString menu_title = menu_path.takeFirst();
2347 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2348 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2350 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2351 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2355 cur_menu->removeAction(action);
2359 // qDebug() << "FIX: Remove" << action->text() << "from the menu";
2365 void MainWindow::addDynamicMenus()
2368 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2369 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2370 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2371 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2372 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2373 wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2375 // Fill in each menu
2376 foreach (register_stat_group_t menu_group, menu_groups) {
2377 QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2378 addMenuActions(actions, menu_group);
2381 // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2382 // We've added a placeholder in order to make sure some menus are visible.
2383 // Hide them as needed.
2384 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2385 main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2387 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2388 main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2390 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2391 main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2393 if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2394 main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2398 void MainWindow::reloadDynamicMenus()
2400 foreach (register_stat_group_t menu_group, menu_groups) {
2401 QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2402 removeMenuActions(actions, menu_group);
2404 actions = wsApp->addedMenuGroupItems(menu_group);
2405 addMenuActions(actions, menu_group);
2408 wsApp->clearAddedMenuGroupItems();
2409 wsApp->clearRemovedMenuGroupItems();
2412 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
2414 QAction * itemAction = NULL;
2415 ext_menubar_t * item = NULL;
2416 GList * children = NULL;
2418 /* There must exists an xpath parent */
2419 g_assert(subMenu != NULL);
2421 /* If the depth counter exceeds, something must have gone wrong */
2422 g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2424 children = menu->children;
2425 /* Iterate the child entries */
2426 while (children && children->data) {
2427 item = (ext_menubar_t *) children->data;
2429 if (item->type == EXT_MENUBAR_MENU) {
2430 /* Handle Submenu entry */
2431 this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2432 } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2433 subMenu->addSeparator();
2434 } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2435 itemAction = subMenu->addAction(item->name);
2436 itemAction->setData(QVariant::fromValue((void *)item));
2437 itemAction->setText(item->label);
2438 connect(itemAction, SIGNAL(triggered()),
2439 this, SLOT(externalMenuItem_triggered()));
2443 children = g_list_next(children);
2447 QMenu * MainWindow::searchSubMenu(QString objectName)
2451 if (objectName.length() > 0) {
2452 QString searchName = QString("menu") + objectName;
2454 lst = main_ui_->menuBar->findChildren<QMenu*>();
2455 foreach (QMenu* m, lst) {
2456 if (QString::compare(m->objectName(), searchName) == 0)
2464 void MainWindow::addExternalMenus()
2466 QMenu * subMenu = NULL;
2467 GList * user_menu = NULL;
2468 ext_menu_t * menu = NULL;
2470 user_menu = ext_menubar_get_entries();
2472 while (user_menu && user_menu->data) {
2473 menu = (ext_menu_t *) user_menu->data;
2475 /* On this level only menu items should exist. Not doing an assert here,
2476 * as it could be an honest mistake */
2477 if (menu->type != EXT_MENUBAR_MENU) {
2478 user_menu = g_list_next(user_menu);
2482 /* Create main submenu and add it to the menubar */
2483 if (menu->parent_menu) {
2484 QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2486 subMenu = sortUnderneath->addMenu(menu->label);
2490 subMenu = main_ui_->menuBar->addMenu(menu->label);
2492 /* This will generate the action structure for each menu. It is recursive,
2493 * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2494 this->externalMenuHelper(menu, subMenu, 0);
2497 user_menu = g_list_next (user_menu);
2507 * indent-tabs-mode: nil
2510 * ex: set shiftwidth=4 tabstop=8 expandtab:
2511 * :indentSize=4:tabSize=8:noTabs=true: