Confirm discarding data before restarting capture.
[metze/wireshark/wip.git] / ui / qt / main_window.cpp
1 /* main_window.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
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.
11  *
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.
16  *
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.
20  */
21
22 #include "main_window.h"
23 #include <ui_main_window.h>
24
25 #include <epan/addr_resolv.h>
26 #include <epan/epan_dissect.h>
27 #include <wsutil/filesystem.h>
28 #include <epan/prefs.h>
29 #include <epan/stats_tree_priv.h>
30 #include <epan/plugin_if.h>
31
32 #ifdef HAVE_LIBPCAP
33 #include "ui/capture.h"
34 #include <capchild/capture_session.h>
35 #endif
36
37 #include "ui/alert_box.h"
38 #ifdef HAVE_LIBPCAP
39 #include "ui/capture_ui_utils.h"
40 #endif
41 #include "ui/capture_globals.h"
42 #include "ui/main_statusbar.h"
43 #include "ui/recent.h"
44 #include "ui/util.h"
45 #include "ui/preference_utils.h"
46
47 #include "byte_view_tab.h"
48 #include "display_filter_edit.h"
49 #include "export_dissection_dialog.h"
50 #include "funnel_statistics.h"
51 #include "import_text_dialog.h"
52 #include "packet_list.h"
53 #include "proto_tree.h"
54 #include "simple_dialog.h"
55 #include "stock_icon.h"
56 #include "tap_parameter_dialog.h"
57 #include "wireless_frame.h"
58 #include "wireshark_application.h"
59
60 #include "qt_ui_utils.h"
61
62 #include <QAction>
63 #include <QActionGroup>
64 #include <QDesktopWidget>
65 #include <QKeyEvent>
66 #include <QMessageBox>
67 #include <QMetaObject>
68 #include <QMimeData>
69 #include <QTabWidget>
70 #include <QToolButton>
71 #include <QTreeWidget>
72 #include <QUrl>
73
74 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
75 #include <QtMacExtras/QMacNativeToolBar>
76 #endif
77
78
79 //menu_recent_file_write_all
80
81 // If we ever add support for multiple windows this will need to be replaced.
82 static MainWindow *gbl_cur_main_window_ = NULL;
83
84 void pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
85 {
86     gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
87 }
88
89 static void plugin_if_mainwindow_apply_filter(gconstpointer user_data)
90 {
91     if ( gbl_cur_main_window_ != NULL && user_data != NULL )
92     {
93         GHashTable * dataSet = (GHashTable *) user_data;
94
95         if ( g_hash_table_lookup_extended(dataSet, "filter_string", NULL, NULL ) )
96         {
97             QString filter((const char *)g_hash_table_lookup(dataSet, "filter_string"));
98             gbl_cur_main_window_->filterPackets(filter);
99         }
100     }
101 }
102
103 static void plugin_if_mainwindow_preference(gconstpointer user_data)
104 {
105     if ( gbl_cur_main_window_ != NULL && user_data != NULL )
106     {
107         GHashTable * dataSet = (GHashTable *) user_data;
108         const char * module_name;
109         const char * pref_name;
110         const char * pref_value;
111         if ( g_hash_table_lookup_extended(dataSet, "pref_module", NULL, (void**)&module_name ) &&
112                 g_hash_table_lookup_extended(dataSet, "pref_key", NULL, (void**)&pref_name ) &&
113                 g_hash_table_lookup_extended(dataSet, "pref_value", NULL, (void**)&pref_value ) )
114         {
115             if ( prefs_store_ext(module_name, pref_name, pref_value) )
116             {
117                 wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
118                 wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
119             }
120         }
121     }
122 }
123
124 static void plugin_if_mainwindow_gotoframe(gconstpointer user_data)
125 {
126     if ( gbl_cur_main_window_ != NULL && user_data != NULL )
127     {
128         GHashTable * dataSet = (GHashTable *) user_data;
129         gpointer framenr;
130
131         if ( g_hash_table_lookup_extended(dataSet, "frame_nr", NULL, &framenr ) )
132         {
133             if ( GPOINTER_TO_UINT(framenr) != 0 )
134                 gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr));
135         }
136     }
137 }
138
139 gpointer
140 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
141 {
142     va_list ap;
143
144     va_start(ap, msg_format);
145     SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
146     va_end(ap);
147
148     sd.exec();
149     return NULL;
150 }
151
152 /*
153  * Alert box, with optional "don't show this message again" variable
154  * and checkbox, and optional secondary text.
155  */
156 void
157 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
158                    const char *secondary_msg, const char *msg_format, ...)
159 {
160     if (notagain && *notagain) {
161         return;
162     }
163
164     va_list ap;
165
166     va_start(ap, msg_format);
167     SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
168     va_end(ap);
169
170     sd.setDetailedText(secondary_msg);
171
172 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
173     QCheckBox *cb = NULL;
174     if (notagain) {
175         cb = new QCheckBox();
176         cb->setChecked(true);
177         cb->setText(QObject::tr("Don't show this message again."));
178         sd.setCheckBox(cb);
179     }
180 #endif
181
182     sd.exec();
183
184 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
185     if (notagain && cb) {
186         *notagain = cb->isChecked();
187     }
188 #endif
189 }
190
191 /*
192  * Error alert box, taking a format and a va_list argument.
193  */
194 void
195 vsimple_error_message_box(const char *msg_format, va_list ap)
196 {
197     SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
198     sd.exec();
199 }
200
201
202 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
203     QList<QAction *> actions = parent_menu->actions();
204     QList<QAction *>::const_iterator i;
205     for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
206         if ((*i)->text()==menu_text) {
207             return (*i)->menu();
208         }
209     }
210     // If we get here there menu entry was not found, add a sub menu
211     return parent_menu->addMenu(menu_text);
212 }
213
214 MainWindow::MainWindow(QWidget *parent) :
215     QMainWindow(parent),
216     main_ui_(new Ui::MainWindow),
217     cur_layout_(QVector<unsigned>()),
218     df_combo_box_(new DisplayFilterCombo()),
219     packet_list_(NULL),
220     proto_tree_(NULL),
221     previous_focus_(NULL),
222     show_hide_actions_(NULL),
223     time_display_actions_(NULL),
224     time_precision_actions_(NULL),
225     funnel_statistics_(new FunnelStatistics(this, capture_file_)),
226     freeze_focus_(NULL),
227     capture_stopping_(false),
228     capture_filter_valid_(false),
229 #ifdef _WIN32
230     pipe_timer_(NULL)
231 #else
232     pipe_notifier_(NULL)
233 #endif
234 {
235     if (!gbl_cur_main_window_) {
236         connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
237                 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
238         connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
239                 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
240     }
241     gbl_cur_main_window_ = this;
242 #ifdef HAVE_LIBPCAP
243     capture_session_init(&cap_session_, CaptureFile::globalCapFile());
244 #endif
245     main_ui_->setupUi(this);
246     setWindowIcon(wsApp->normalIcon());
247     setTitlebarForCaptureFile();
248     setMenusForCaptureFile();
249     setForCapturedPackets(false);
250     setMenusForFileSet(false);
251     interfaceSelectionChanged();
252     loadWindowGeometry();
253
254 #ifndef HAVE_LUA
255     main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
256 #endif
257
258     //To prevent users use features before initialization complete
259     //Otherwise unexpected problems may occur
260     setFeaturesEnabled(false);
261     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
262     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
263     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
264     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
265     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
266     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
267
268     connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
269     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
270     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
271     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
272     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
273
274     connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
275     updateRecentFiles();
276
277 #ifdef HAVE_LIBPCAP
278     connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
279     connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
280 #endif
281
282     const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
283     connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
284             main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
285     connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
286     connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
287             main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
288     connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
289     connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
290             this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
291     connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
292
293     connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
294     connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
295     connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
296     connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
297             this, SLOT(openCaptureFile(QString,QString)));
298     connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
299
300     initMainToolbarIcons();
301
302     // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
303     // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
304     // https://bugreports.qt-project.org/browse/QTBUG-22433
305     // This property is obsolete in Qt5 so this issue may be fixed in that version.
306     main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
307
308     wireless_frame_ = new WirelessFrame(this);
309     main_ui_->wirelessToolBar->addWidget(wireless_frame_);
310     connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
311             main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
312     connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
313              this, SLOT(showPreferencesDialog(QString)));
314
315     main_ui_->goToFrame->hide();
316     connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
317             main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
318
319     // XXX For some reason the cursor is drawn funny with an input mask set
320     // https://bugreports.qt-project.org/browse/QTBUG-7174
321
322     main_ui_->searchFrame->hide();
323     connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
324             main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
325     connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
326             main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
327
328     main_ui_->addressEditorFrame->hide();
329     main_ui_->columnEditorFrame->hide();
330     main_ui_->preferenceEditorFrame->hide();
331     main_ui_->filterExpressionFrame->hide();
332
333 #ifndef HAVE_LIBPCAP
334     main_ui_->menuCapture->setEnabled(false);
335 #endif
336
337 #if defined(Q_OS_MAC)
338 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
339     QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
340     ntb->setIconSize(QSize(24, 24));
341 #endif // QT_MACEXTRAS_LIB
342
343     main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
344     main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
345     main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
346
347     main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
348
349 #endif // Q_OS_MAC
350
351 #ifdef HAVE_SOFTWARE_UPDATE
352     QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
353     QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
354     main_ui_->menuHelp->insertAction(update_sep, update_action);
355     connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
356 #endif
357     master_split_.setObjectName("splitterMaster");
358     extra_split_.setObjectName("splitterExtra");
359     main_ui_->mainStack->addWidget(&master_split_);
360
361     empty_pane_.setObjectName("emptyPane");
362
363     packet_list_ = new PacketList(&master_split_);
364
365     proto_tree_ = new ProtoTree(&master_split_);
366     proto_tree_->installEventFilter(this);
367
368     byte_view_tab_ = new ByteViewTab(&master_split_);
369
370     packet_list_->setProtoTree(proto_tree_);
371     packet_list_->setByteViewTab(byte_view_tab_);
372     packet_list_->installEventFilter(this);
373
374     main_welcome_ = main_ui_->welcomePage;
375
376     // Packet list and proto tree must exist before these are called.
377     setMenusForSelectedPacket();
378     setMenusForSelectedTreeRow();
379
380     initShowHideMainWidgets();
381     initTimeDisplayFormatMenu();
382     initTimePrecisionFormatMenu();
383     initFreezeActions();
384     updatePreferenceActions();
385     updateRecentActions();
386     setForCaptureInProgress(false);
387
388     setTabOrder(df_combo_box_, packet_list_);
389
390     connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
391             this, SLOT(captureCapturePrepared(capture_session *)));
392     connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
393             this, SLOT(captureCaptureUpdateStarted(capture_session *)));
394     connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
395             this, SLOT(captureCaptureUpdateFinished(capture_session *)));
396     connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
397             this, SLOT(captureCaptureFixedStarted(capture_session *)));
398     connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
399             main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
400     connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
401             this, SLOT(captureCaptureFixedFinished(capture_session *)));
402     connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
403             this, SLOT(captureCaptureStopping(capture_session *)));
404     connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
405             this, SLOT(captureCaptureFailed(capture_session *)));
406     connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
407             main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
408
409     connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
410             wsApp, SLOT(captureStarted()));
411     connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
412             wsApp, SLOT(captureFinished()));
413     connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
414             wsApp, SLOT(captureStarted()));
415     connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
416             wsApp, SLOT(captureFinished()));
417
418     connect(&capture_file_, SIGNAL(captureFileOpened()),
419             this, SLOT(captureFileOpened()));
420     connect(&capture_file_, SIGNAL(captureFileReadStarted()),
421             this, SLOT(captureFileReadStarted()));
422     connect(&capture_file_, SIGNAL(captureFileReadFinished()),
423             this, SLOT(captureFileReadFinished()));
424     connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
425             this, SLOT(captureFileReloadStarted()));
426     connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
427             this, SLOT(captureFileReadFinished()));
428     connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
429             this, SLOT(captureFileRescanStarted()));
430     connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
431             this, SLOT(captureFileReadFinished()));
432     connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
433             this, SLOT(captureFileRetapStarted()));
434     connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
435             this, SLOT(captureFileRetapFinished()));
436     connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
437             this, SLOT(captureFileFlushTapsData()));
438     connect(&capture_file_, SIGNAL(captureFileClosing()),
439             this, SLOT(captureFileClosing()));
440     connect(&capture_file_, SIGNAL(captureFileClosed()),
441             this, SLOT(captureFileClosed()));
442
443     connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
444             this, SLOT(captureFileSaveStarted(QString)));
445     connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
446             main_ui_->statusBar, SLOT(popFileStatus()));
447     connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
448             main_ui_->statusBar, SLOT(popFileStatus()));
449     connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
450             main_ui_->statusBar, SLOT(popFileStatus()));
451
452     connect(&capture_file_, SIGNAL(captureFileReadStarted()),
453             wsApp, SLOT(captureFileReadStarted()));
454     connect(&capture_file_, SIGNAL(captureFileReadFinished()),
455             wsApp, SLOT(updateTaps()));
456
457     connect(wsApp, SIGNAL(columnsChanged()),
458             packet_list_, SLOT(columnsChanged()));
459     connect(wsApp, SIGNAL(preferencesChanged()),
460             packet_list_, SLOT(preferencesChanged()));
461     connect(wsApp, SIGNAL(recentFilesRead()),
462             this, SLOT(applyRecentPaneGeometry()));
463     connect(wsApp, SIGNAL(recentFilesRead()),
464             this, SLOT(updateRecentActions()));
465     connect(wsApp, SIGNAL(packetDissectionChanged()),
466             this, SLOT(redissectPackets()));
467     connect(wsApp, SIGNAL(appInitialized()),
468             this, SLOT(filterExpressionsChanged()));
469     connect(wsApp, SIGNAL(filterExpressionsChanged()),
470             this, SLOT(filterExpressionsChanged()));
471     connect(wsApp, SIGNAL(checkDisplayFilter()),
472             this, SLOT(checkDisplayFilter()));
473     connect(wsApp, SIGNAL(fieldsChanged()),
474             this, SLOT(fieldsChanged()));
475
476     connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
477             this, SLOT(mainStackChanged(int)));
478
479     connect(main_welcome_, SIGNAL(startCapture()),
480             this, SLOT(startCapture()));
481     connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
482             this, SLOT(openCaptureFile(QString)));
483     connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
484             main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
485     connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
486             main_ui_->statusBar, SLOT(popFilterStatus()));
487
488     connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
489             main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
490     connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
491             this, SLOT(redissectPackets()));
492     connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
493             this, SLOT(showPreferencesDialog(QString)));
494     connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
495             this, SLOT(showPreferencesDialog(QString)));
496     connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
497             this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
498     connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
499             this, SLOT(filterExpressionsChanged()));
500
501     connect(this, SIGNAL(setCaptureFile(capture_file*)),
502             main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
503     connect(this, SIGNAL(setCaptureFile(capture_file*)),
504             main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
505     connect(this, SIGNAL(setCaptureFile(capture_file*)),
506             packet_list_, SLOT(setCaptureFile(capture_file*)));
507     connect(this, SIGNAL(setCaptureFile(capture_file*)),
508             byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
509
510     connect(this, SIGNAL(monospaceFontChanged(QFont)),
511             packet_list_, SLOT(setMonospaceFont(QFont)));
512     connect(this, SIGNAL(monospaceFontChanged(QFont)),
513             proto_tree_, SLOT(setMonospaceFont(QFont)));
514     connect(this, SIGNAL(monospaceFontChanged(QFont)),
515             byte_view_tab_, SLOT(setMonospaceFont(QFont)));
516
517     connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
518             packet_list_, SLOT(goNextPacket()));
519     connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
520             packet_list_, SLOT(goPreviousPacket()));
521     connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
522             packet_list_, SLOT(goFirstPacket()));
523     connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
524             packet_list_, SLOT(goLastPacket()));
525
526     connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
527             proto_tree_, SLOT(expandSubtrees()));
528     connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
529             proto_tree_, SLOT(expandAll()));
530     connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
531             proto_tree_, SLOT(collapseAll()));
532
533     connect(packet_list_, SIGNAL(packetSelectionChanged()),
534             this, SLOT(setMenusForSelectedPacket()));
535     connect(packet_list_, SIGNAL(packetDissectionChanged()),
536             this, SLOT(redissectPackets()));
537     connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
538             this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
539     connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
540             this, SLOT(showPreferencesDialog(QString)));
541     connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
542             main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
543     connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
544     connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
545             packet_list_, SLOT(columnsChanged()));
546     connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
547             this, SLOT(openPacketDialog()));
548     connect(packet_list_, SIGNAL(packetListScrolled(bool)),
549             main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
550     connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
551             main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
552     connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
553             main_ui_->statusBar, SLOT(popBusyStatus()));
554     connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
555             main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
556     connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
557             main_ui_->statusBar, SLOT(updateProgressStatus(int)));
558     connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
559             main_ui_->statusBar, SLOT(popProgressStatus()));
560
561     connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
562             main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
563     connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
564             this, SLOT(setMenusForSelectedTreeRow(field_info *)));
565     connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
566             this, SLOT(openPacketDialog(bool)));
567     connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
568             this, SLOT(showPreferencesDialog(QString)));
569     connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
570             main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
571
572     connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
573             main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
574
575     connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
576             this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
577
578     connect(main_ui_->statusBar, SIGNAL(stopLoading()),
579             &capture_file_, SLOT(stopLoading()));
580
581     connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
582             this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
583
584     connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
585             this, SLOT(openCaptureFile(QString)));
586
587 #ifdef HAVE_LIBPCAP
588     QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
589     if (iface_tree) {
590         connect(iface_tree, SIGNAL(itemSelectionChanged()),
591                 this, SLOT(interfaceSelectionChanged()));
592     }
593     connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
594             this, SLOT(captureFilterSyntaxChanged(bool)));
595
596 #if HAVE_EXTCAP
597         connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
598                 this, SLOT(showExtcapOptionsDialog(QString&)));
599 #endif
600
601     connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
602             this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
603     connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
604             this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
605     connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
606             this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
607 #endif
608
609     /* Create plugin_if hooks */
610     plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter );
611     plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter );
612     plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
613     plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
614
615     main_ui_->mainStack->setCurrentWidget(main_welcome_);
616 }
617
618 MainWindow::~MainWindow()
619 {
620     delete main_ui_;
621 }
622
623 QString MainWindow::getFilter()
624 {
625     return df_combo_box_->itemText(df_combo_box_->count());
626 }
627
628 QMenu *MainWindow::createPopupMenu()
629 {
630     QMenu *menu = new QMenu();
631     menu->addAction(main_ui_->actionViewMainToolbar);
632     menu->addAction(main_ui_->actionViewFilterToolbar);
633     menu->addAction(main_ui_->actionViewWirelessToolbar);
634     menu->addAction(main_ui_->actionViewStatusBar);
635     menu->addSeparator();
636     menu->addAction(main_ui_->actionViewPacketList);
637     menu->addAction(main_ui_->actionViewPacketDetails);
638     menu->addAction(main_ui_->actionViewPacketBytes);
639     return menu;
640 }
641
642 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
643 {
644     pipe_source_        = source;
645     pipe_child_process_ = child_process;
646     pipe_user_data_     = user_data;
647     pipe_input_cb_      = input_cb;
648
649 #ifdef _WIN32
650     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
651        do this but that doesn't cover all win32 platforms.  GTK can do
652        this but doesn't seem to work over processes.  Attempt to do
653        something similar here, start a timer and check for data on every
654        timeout. */
655        /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
656
657     if (pipe_timer_) {
658         disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
659         delete pipe_timer_;
660     }
661
662     pipe_timer_ = new QTimer(this);
663     connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
664     connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
665     pipe_timer_->start(200);
666 #else
667     if (pipe_notifier_) {
668         disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
669         delete pipe_notifier_;
670     }
671
672     pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
673     // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
674     connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
675     connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
676 #endif
677 }
678
679 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
680
681     // The user typed some text. Start filling in a filter.
682     // We may need to be more choosy here. We just need to catch events for the packet list,
683     // proto tree, and main welcome widgets.
684     if (event->type() == QEvent::KeyPress) {
685         QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
686         if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
687             df_combo_box_->lineEdit()->insert(kevt->text());
688             df_combo_box_->lineEdit()->setFocus();
689             return true;
690         }
691     }
692
693     return QMainWindow::eventFilter(obj, event);
694 }
695
696 void MainWindow::keyPressEvent(QKeyEvent *event) {
697
698     // Explicitly focus on the display filter combo.
699     if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
700         df_combo_box_->setFocus(Qt::ShortcutFocusReason);
701         return;
702     }
703
704     if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
705         if (event->modifiers() == Qt::NoModifier) {
706             if (event->key() == Qt::Key_Escape) {
707                 on_goToCancel_clicked();
708             } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
709                 on_goToGo_clicked();
710             }
711         }
712         return; // goToLineEdit didn't want it and we don't either.
713     }
714
715     // Move up & down the packet list.
716     if (event->key() == Qt::Key_F7) {
717         packet_list_->goPreviousPacket();
718     } else if (event->key() == Qt::Key_F8) {
719         packet_list_->goNextPacket();
720     }
721
722     // Move along, citizen.
723     QMainWindow::keyPressEvent(event);
724 }
725
726 void MainWindow::closeEvent(QCloseEvent *event) {
727     saveWindowGeometry();
728
729     /* If we're in the middle of stopping a capture, don't do anything;
730        the user can try deleting the window after the capture stops. */
731     if (capture_stopping_) {
732         event->ignore();
733         return;
734     }
735
736     QString before_what(tr(" before quitting"));
737     if (!testCaptureFileClose(TRUE, before_what)) {
738         event->ignore();
739         return;
740     }
741
742 #ifdef HAVE_LIBPCAP
743     capture_interfaces_dialog_.close();
744 #endif
745     // Make sure we kill any open dumpcap processes.
746     delete main_welcome_;
747
748     // One of the many places we assume one main window.
749     if(!wsApp->isInitialized()) {
750         // If we're still initializing, QCoreApplication::quit() won't
751         // exit properly because we are not in the event loop. This
752         // means that the application won't clean up after itself. We
753         // might want to call wsApp->processEvents() during startup
754         // instead so that we can do a normal exit here.
755         exit(0);
756     }
757     wsApp->quit();
758 }
759
760 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
761 {
762     bool accept = false;
763     foreach (QUrl drag_url, event->mimeData()->urls()) {
764         if (!drag_url.toLocalFile().isEmpty()) {
765             accept = true;
766             break;
767         }
768     }
769     if (accept) event->acceptProposedAction();
770 }
771
772 void MainWindow::dropEvent(QDropEvent *event)
773 {
774     foreach (QUrl drop_url, event->mimeData()->urls()) {
775         QString local_file = drop_url.toLocalFile();
776         if (!local_file.isEmpty()) {
777             event->acceptProposedAction();
778             openCaptureFile(local_file);
779             break;
780         }
781     }
782 }
783
784 // Apply recent settings to the main window geometry.
785 // We haven't loaded the preferences at this point so we assume that the
786 // position and size preference are enabled.
787 void MainWindow::loadWindowGeometry()
788 {
789     int min_sensible_dimension = 200;
790
791 #ifndef Q_OS_MAC
792     if (recent.gui_geometry_main_maximized) {
793         setWindowState(Qt::WindowMaximized);
794     } else
795 #endif
796     {
797         QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
798                           recent.gui_geometry_main_width, recent.gui_geometry_main_height);
799         if (!rect_on_screen(recent_geom)) {
800             // We're not visible on any screens. Give up and use the default geometry.
801             return;
802         }
803
804 //        if (prefs.gui_geometry_save_position) {
805             move(recent_geom.topLeft());
806 //        }
807
808         if (// prefs.gui_geometry_save_size &&
809                 recent_geom.width() > min_sensible_dimension &&
810                 recent_geom.height() > min_sensible_dimension) {
811             resize(recent_geom.size());
812         }
813     }
814 }
815
816 void MainWindow::saveWindowGeometry()
817 {
818     if (prefs.gui_geometry_save_position) {
819         recent.gui_geometry_main_x = pos().x();
820         recent.gui_geometry_main_y = pos().y();
821     }
822
823     if (prefs.gui_geometry_save_size) {
824         recent.gui_geometry_main_width = size().width();
825         recent.gui_geometry_main_height = size().height();
826     }
827
828     if (prefs.gui_geometry_save_maximized) {
829         // On OS X this is false when it shouldn't be
830         recent.gui_geometry_main_maximized = isMaximized();
831     }
832
833     if (master_split_.sizes().length() > 0) {
834         recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
835     }
836
837     if (master_split_.sizes().length() > 2) {
838         recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
839     } else if (extra_split_.sizes().length() > 0) {
840         recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
841     }
842 }
843
844 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
845     switch (type) {
846         case layout_pane_content_none:
847             return &empty_pane_;
848         case layout_pane_content_plist:
849             return packet_list_;
850         case layout_pane_content_pdetails:
851             return proto_tree_;
852         case layout_pane_content_pbytes:
853             return byte_view_tab_;
854         default:
855             g_assert_not_reached();
856             return NULL;
857     }
858 }
859
860 // Our event loop becomes nested whenever we call update_progress_dlg, which
861 // includes several places in file.c. The GTK+ UI stays out of trouble by
862 // showing a modal progress dialog. We attempt to do the equivalent below by
863 // disabling parts of the main window. At a minumum the ProgressFrame in the
864 // main status bar must remain accessible.
865 //
866 // We might want to do this any time the main status bar progress frame is
867 // shown and hidden.
868 void MainWindow::freeze()
869 {
870     freeze_focus_ = wsApp->focusWidget();
871
872     // XXX Alternatively we could just disable and enable the main menu.
873     for (int i = 0; i < freeze_actions_.size(); i++) {
874         QAction *action = freeze_actions_[i].first;
875         freeze_actions_[i].second = action->isEnabled();
876         action->setEnabled(false);
877     }
878     main_ui_->centralWidget->setEnabled(false);
879 }
880
881 void MainWindow::thaw()
882 {
883     main_ui_->centralWidget->setEnabled(true);
884     for (int i = 0; i < freeze_actions_.size(); i++) {
885         freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
886     }
887
888     if (freeze_focus_) freeze_focus_->setFocus();
889     freeze_focus_ = NULL;
890 }
891
892 void MainWindow::mergeCaptureFile()
893 {
894     QString file_name = "";
895     QString read_filter = "";
896     dfilter_t *rfcode = NULL;
897     int err;
898
899     if (!capture_file_.capFile())
900         return;
901
902     if (prefs.gui_ask_unsaved) {
903         if (cf_has_unsaved_data(capture_file_.capFile())) {
904             QMessageBox msg_dialog;
905             gchar *display_basename;
906             int response;
907
908             msg_dialog.setIcon(QMessageBox::Question);
909             /* This file has unsaved data; ask the user whether to save
910                the capture. */
911             if (capture_file_.capFile()->is_tempfile) {
912                 msg_dialog.setText(tr("Save packets before merging?"));
913                 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
914             } else {
915                 /*
916                  * Format the message.
917                  */
918                 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
919                 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
920                 g_free(display_basename);
921                 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
922             }
923
924             msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
925             msg_dialog.setDefaultButton(QMessageBox::Save);
926
927             response = msg_dialog.exec();
928
929             switch (response) {
930
931             case QMessageBox::Save:
932                 /* Save the file but don't close it */
933                 saveCaptureFile(capture_file_.capFile(), FALSE);
934                 break;
935
936             case QMessageBox::Cancel:
937             default:
938                 /* Don't do the merge. */
939                 return;
940             }
941         }
942     }
943
944     for (;;) {
945         CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
946         int file_type;
947         cf_status_t  merge_status;
948         char        *in_filenames[2];
949         char        *tmpname;
950
951         switch (prefs.gui_fileopen_style) {
952
953         case FO_STYLE_LAST_OPENED:
954             /* The user has specified that we should start out in the last directory
955            we looked in.  If we've already opened a file, use its containing
956            directory, if we could determine it, as the directory, otherwise
957            use the "last opened" directory saved in the preferences file if
958            there was one. */
959             /* This is now the default behaviour in file_selection_new() */
960             break;
961
962         case FO_STYLE_SPECIFIED:
963             /* The user has specified that we should always start out in a
964            specified directory; if they've specified that directory,
965            start out by showing the files in that dir. */
966             if (prefs.gui_fileopen_dir[0] != '\0')
967                 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
968             break;
969         }
970
971         if (merge_dlg.merge(file_name)) {
972             gchar *err_msg;
973
974             if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
975                 /* Not valid.  Tell the user, and go back and run the file
976                    selection box again once they dismiss the alert. */
977                 //bad_dfilter_alert_box(top_level, read_filter->str);
978                 QMessageBox::warning(this, tr("Invalid Read Filter"),
979                                      QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
980                                      QMessageBox::Ok);
981                 g_free(err_msg);
982                 continue;
983             }
984         } else {
985             return;
986         }
987
988         file_type = capture_file_.capFile()->cd_t;
989
990         /* Try to merge or append the two files */
991         tmpname = NULL;
992         if (merge_dlg.mergeType() == 0) {
993             /* chronological order */
994             in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
995             in_filenames[1] = qstring_strdup(file_name);
996             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
997         } else if (merge_dlg.mergeType() <= 0) {
998             /* prepend file */
999             in_filenames[0] = qstring_strdup(file_name);
1000             in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1001             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1002         } else {
1003             /* append file */
1004             in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1005             in_filenames[1] = qstring_strdup(file_name);
1006             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1007         }
1008
1009         g_free(in_filenames[0]);
1010         g_free(in_filenames[1]);
1011
1012         if (merge_status != CF_OK) {
1013             if (rfcode != NULL)
1014                 dfilter_free(rfcode);
1015             g_free(tmpname);
1016             continue;
1017         }
1018
1019         cf_close(capture_file_.capFile());
1020
1021         /* Try to open the merged capture file. */
1022         CaptureFile::globalCapFile()->window = this;
1023         if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1024             /* We couldn't open it; fail. */
1025             CaptureFile::globalCapFile()->window = NULL;
1026             if (rfcode != NULL)
1027                 dfilter_free(rfcode);
1028             g_free(tmpname);
1029             return;
1030         }
1031
1032         /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1033            it closed the previous capture file, and thus destroyed any
1034            previous read filter attached to "cf"). */
1035         cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1036
1037         switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1038
1039         case CF_READ_OK:
1040         case CF_READ_ERROR:
1041             /* Just because we got an error, that doesn't mean we were unable
1042              to read any of the file; we handle what we could get from the
1043              file. */
1044             break;
1045
1046         case CF_READ_ABORTED:
1047             /* The user bailed out of re-reading the capture file; the
1048              capture file has been closed - just free the capture file name
1049              string and return (without changing the last containing
1050              directory). */
1051             g_free(tmpname);
1052             return;
1053         }
1054
1055         /* Save the name of the containing directory specified in the path name,
1056            if any; we can write over cf_merged_name, which is a good thing, given that
1057            "get_dirname()" does write over its argument. */
1058         wsApp->setLastOpenDir(get_dirname(tmpname));
1059         g_free(tmpname);
1060         main_ui_->statusBar->showExpert();
1061         return;
1062     }
1063
1064 }
1065
1066 void MainWindow::importCaptureFile() {
1067     ImportTextDialog import_dlg;
1068
1069     QString before_what(tr(" before importing a new capture"));
1070     if (!testCaptureFileClose(FALSE, before_what))
1071         return;
1072
1073     import_dlg.exec();
1074
1075     if (import_dlg.result() != QDialog::Accepted) {
1076         main_ui_->mainStack->setCurrentWidget(main_welcome_);
1077         return;
1078     }
1079
1080     openCaptureFile(import_dlg.capfileName());
1081 }
1082
1083 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1084     QString file_name;
1085     gboolean discard_comments;
1086
1087     if (cf->is_tempfile) {
1088         /* This is a temporary capture file, so saving it means saving
1089            it to a permanent file.  Prompt the user for a location
1090            to which to save it.  Don't require that the file format
1091            support comments - if it's a temporary capture file, it's
1092            probably pcap-ng, which supports comments and, if it's
1093            not pcap-ng, let the user decide what they want to do
1094            if they've added comments. */
1095         saveAsCaptureFile(cf, FALSE, dont_reopen);
1096     } else {
1097         if (cf->unsaved_changes) {
1098             cf_write_status_t status;
1099
1100             /* This is not a temporary capture file, but it has unsaved
1101                changes, so saving it means doing a "safe save" on top
1102                of the existing file, in the same format - no UI needed
1103                unless the file has comments and the file's format doesn't
1104                support them.
1105
1106                If the file has comments, does the file's format support them?
1107                If not, ask the user whether they want to discard the comments
1108                or choose a different format. */
1109             switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1110
1111             case SAVE:
1112                 /* The file can be saved in the specified format as is;
1113                    just drive on and save in the format they selected. */
1114                 discard_comments = FALSE;
1115                 break;
1116
1117             case SAVE_WITHOUT_COMMENTS:
1118                 /* The file can't be saved in the specified format as is,
1119                    but it can be saved without the comments, and the user
1120                    said "OK, discard the comments", so save it in the
1121                    format they specified without the comments. */
1122                 discard_comments = TRUE;
1123                 break;
1124
1125             case SAVE_IN_ANOTHER_FORMAT:
1126                 /* There are file formats in which we can save this that
1127                    support comments, and the user said not to delete the
1128                    comments.  Do a "Save As" so the user can select
1129                    one of those formats and choose a file name. */
1130                 saveAsCaptureFile(cf, TRUE, dont_reopen);
1131                 return;
1132
1133             case CANCELLED:
1134                 /* The user said "forget it".  Just return. */
1135                 return;
1136
1137             default:
1138                 /* Squelch warnings that discard_comments is being used
1139                    uninitialized. */
1140                 g_assert_not_reached();
1141                 return;
1142             }
1143
1144             /* XXX - cf->filename might get freed out from under us, because
1145                the code path through which cf_save_records() goes currently
1146                closes the current file and then opens and reloads the saved file,
1147                so make a copy and free it later. */
1148             file_name = cf->filename;
1149             status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1150                                      discard_comments, dont_reopen);
1151             switch (status) {
1152
1153             case CF_WRITE_OK:
1154                 /* The save succeeded; we're done.
1155                    If we discarded comments, redraw the packet list to reflect
1156                    any packets that no longer have comments. */
1157                 if (discard_comments)
1158                     packet_list_queue_draw();
1159
1160                 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1161                 updateForUnsavedChanges(); // we update the title bar to remove the *
1162                 break;
1163
1164             case CF_WRITE_ERROR:
1165                 /* The write failed.
1166                    XXX - OK, what do we do now?  Let them try a
1167                    "Save As", in case they want to try to save to a
1168                    different directory r file system? */
1169                 break;
1170
1171             case CF_WRITE_ABORTED:
1172                 /* The write was aborted; just drive on. */
1173                 break;
1174             }
1175         }
1176         /* Otherwise just do nothing. */
1177     }
1178 }
1179
1180 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1181     QString file_name = "";
1182     int file_type;
1183     gboolean compressed;
1184     cf_write_status_t status;
1185     gchar   *dirname;
1186     gboolean discard_comments = FALSE;
1187
1188     if (!cf) {
1189         return;
1190     }
1191
1192     for (;;) {
1193         CaptureFileDialog save_as_dlg(this, cf);
1194
1195         switch (prefs.gui_fileopen_style) {
1196
1197         case FO_STYLE_LAST_OPENED:
1198             /* The user has specified that we should start out in the last directory
1199                we looked in.  If we've already opened a file, use its containing
1200                directory, if we could determine it, as the directory, otherwise
1201                use the "last opened" directory saved in the preferences file if
1202                there was one. */
1203             /* This is now the default behaviour in file_selection_new() */
1204             break;
1205
1206         case FO_STYLE_SPECIFIED:
1207             /* The user has specified that we should always start out in a
1208                specified directory; if they've specified that directory,
1209                start out by showing the files in that dir. */
1210             if (prefs.gui_fileopen_dir[0] != '\0')
1211                 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
1212             break;
1213         }
1214
1215         /* If the file has comments, does the format the user selected
1216            support them?  If not, ask the user whether they want to
1217            discard the comments or choose a different format. */
1218         switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1219
1220         case SAVE:
1221             /* The file can be saved in the specified format as is;
1222                just drive on and save in the format they selected. */
1223             discard_comments = FALSE;
1224             break;
1225
1226         case SAVE_WITHOUT_COMMENTS:
1227             /* The file can't be saved in the specified format as is,
1228                but it can be saved without the comments, and the user
1229                said "OK, discard the comments", so save it in the
1230                format they specified without the comments. */
1231             discard_comments = TRUE;
1232             break;
1233
1234         case SAVE_IN_ANOTHER_FORMAT:
1235             /* There are file formats in which we can save this that
1236                support comments, and the user said not to delete the
1237                comments.  The combo box of file formats has had the
1238                formats that don't support comments trimmed from it,
1239                so run the dialog again, to let the user decide
1240                whether to save in one of those formats or give up. */
1241             must_support_comments = TRUE;
1242             continue;
1243
1244         case CANCELLED:
1245             /* The user said "forget it".  Just get rid of the dialog box
1246                and return. */
1247             return;
1248         }
1249         file_type = save_as_dlg.selectedFileType();
1250         compressed = save_as_dlg.isCompressed();
1251
1252         fileAddExtension(file_name, file_type, compressed);
1253
1254 //#ifndef _WIN32
1255 //        /* If the file exists and it's user-immutable or not writable,
1256 //                       ask the user whether they want to override that. */
1257 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1258 //            /* They don't.  Let them try another file name or cancel. */
1259 //            continue;
1260 //        }
1261 //#endif
1262
1263         /* Attempt to save the file */
1264         status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1265                                  discard_comments, dont_reopen);
1266         switch (status) {
1267
1268         case CF_WRITE_OK:
1269             /* The save succeeded; we're done. */
1270             /* Save the directory name for future file dialogs. */
1271             dirname = get_dirname(qstring_strdup(file_name));  /* Overwrites cf_name */
1272             set_last_open_dir(dirname);
1273             g_free(dirname);
1274             /* If we discarded comments, redraw the packet list to reflect
1275                any packets that no longer have comments. */
1276             if (discard_comments)
1277                 packet_list_queue_draw();
1278
1279             cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1280             updateForUnsavedChanges(); // we update the title bar to remove the *
1281             return;
1282
1283         case CF_WRITE_ERROR:
1284             /* The save failed; let the user try again. */
1285             continue;
1286
1287         case CF_WRITE_ABORTED:
1288             /* The user aborted the save; just return. */
1289             return;
1290         }
1291     }
1292     return;
1293 }
1294
1295 void MainWindow::exportSelectedPackets() {
1296     QString file_name = "";
1297     int file_type;
1298     gboolean compressed;
1299     packet_range_t range;
1300     cf_write_status_t status;
1301     gchar   *dirname;
1302     gboolean discard_comments = FALSE;
1303
1304     if (!capture_file_.capFile())
1305         return;
1306
1307     /* Init the packet range */
1308     packet_range_init(&range, capture_file_.capFile());
1309     range.process_filtered = TRUE;
1310     range.include_dependents = TRUE;
1311
1312     for (;;) {
1313         CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1314
1315         switch (prefs.gui_fileopen_style) {
1316
1317         case FO_STYLE_LAST_OPENED:
1318             /* The user has specified that we should start out in the last directory
1319                we looked in.  If we've already opened a file, use its containing
1320                directory, if we could determine it, as the directory, otherwise
1321                use the "last opened" directory saved in the preferences file if
1322                there was one. */
1323             /* This is now the default behaviour in file_selection_new() */
1324             break;
1325
1326         case FO_STYLE_SPECIFIED:
1327             /* The user has specified that we should always start out in a
1328                specified directory; if they've specified that directory,
1329                start out by showing the files in that dir. */
1330             if (prefs.gui_fileopen_dir[0] != '\0')
1331                 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1332             break;
1333         }
1334
1335         /* If the file has comments, does the format the user selected
1336            support them?  If not, ask the user whether they want to
1337            discard the comments or choose a different format. */
1338         switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1339
1340         case SAVE:
1341             /* The file can be saved in the specified format as is;
1342                just drive on and save in the format they selected. */
1343             discard_comments = FALSE;
1344             break;
1345
1346         case SAVE_WITHOUT_COMMENTS:
1347             /* The file can't be saved in the specified format as is,
1348                but it can be saved without the comments, and the user
1349                said "OK, discard the comments", so save it in the
1350                format they specified without the comments. */
1351             discard_comments = TRUE;
1352             break;
1353
1354         case SAVE_IN_ANOTHER_FORMAT:
1355             /* There are file formats in which we can save this that
1356                support comments, and the user said not to delete the
1357                comments.  The combo box of file formats has had the
1358                formats that don't support comments trimmed from it,
1359                so run the dialog again, to let the user decide
1360                whether to save in one of those formats or give up. */
1361             continue;
1362
1363         case CANCELLED:
1364             /* The user said "forget it".  Just get rid of the dialog box
1365                and return. */
1366             return;
1367         }
1368
1369         /*
1370          * Check that we're not going to save on top of the current
1371          * capture file.
1372          * We do it here so we catch all cases ...
1373          * Unfortunately, the file requester gives us an absolute file
1374          * name and the read file name may be relative (if supplied on
1375          * the command line). From Joerg Mayer.
1376          */
1377         if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1378             QMessageBox msg_box;
1379             gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1380
1381             msg_box.setIcon(QMessageBox::Critical);
1382             msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1383             msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1384             msg_box.setStandardButtons(QMessageBox::Ok);
1385             msg_box.setDefaultButton(QMessageBox::Ok);
1386             msg_box.exec();
1387             g_free(display_basename);
1388             continue;
1389         }
1390
1391         file_type = esp_dlg.selectedFileType();
1392         compressed = esp_dlg.isCompressed();
1393         fileAddExtension(file_name, file_type, compressed);
1394
1395 //#ifndef _WIN32
1396 //        /* If the file exists and it's user-immutable or not writable,
1397 //                       ask the user whether they want to override that. */
1398 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1399 //            /* They don't.  Let them try another file name or cancel. */
1400 //            continue;
1401 //        }
1402 //#endif
1403
1404         /* Attempt to save the file */
1405         status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1406         switch (status) {
1407
1408         case CF_WRITE_OK:
1409             /* The save succeeded; we're done. */
1410             /* Save the directory name for future file dialogs. */
1411             dirname = get_dirname(qstring_strdup(file_name));  /* Overwrites cf_name */
1412             set_last_open_dir(dirname);
1413             g_free(dirname);
1414             /* If we discarded comments, redraw the packet list to reflect
1415                any packets that no longer have comments. */
1416             if (discard_comments)
1417                 packet_list_queue_draw();
1418             return;
1419
1420         case CF_WRITE_ERROR:
1421             /* The save failed; let the user try again. */
1422             continue;
1423
1424         case CF_WRITE_ABORTED:
1425             /* The user aborted the save; just return. */
1426             return;
1427         }
1428     }
1429     return;
1430 }
1431
1432 void MainWindow::exportDissections(export_type_e export_type) {
1433     ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1434     packet_range_t range;
1435
1436     if (!capture_file_.capFile())
1437         return;
1438
1439     /* Init the packet range */
1440     packet_range_init(&range, capture_file_.capFile());
1441     range.process_filtered = TRUE;
1442     range.include_dependents = TRUE;
1443
1444     ed_dlg.exec();
1445 }
1446
1447 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1448     QString file_name_lower;
1449     QString file_suffix;
1450     GSList  *extensions_list;
1451     gboolean add_extension;
1452
1453     /*
1454      * Append the default file extension if there's none given by
1455      * the user or if they gave one that's not one of the valid
1456      * extensions for the file type.
1457      */
1458     file_name_lower = file_name.toLower();
1459     extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1460     if (extensions_list != NULL) {
1461         GSList *extension;
1462
1463         /* We have one or more extensions for this file type.
1464            Start out assuming we need to add the default one. */
1465         add_extension = TRUE;
1466
1467         /* OK, see if the file has one of those extensions. */
1468         for (extension = extensions_list; extension != NULL;
1469              extension = g_slist_next(extension)) {
1470             file_suffix += tr(".") + (char *)extension->data;
1471             if (file_name_lower.endsWith(file_suffix)) {
1472                 /*
1473                  * The file name has one of the extensions for
1474                  * this file type.
1475                  */
1476                 add_extension = FALSE;
1477                 break;
1478             }
1479             file_suffix += ".gz";
1480             if (file_name_lower.endsWith(file_suffix)) {
1481                 /*
1482                  * The file name has one of the extensions for
1483                  * this file type.
1484                  */
1485                 add_extension = FALSE;
1486                 break;
1487             }
1488         }
1489     } else {
1490         /* We have no extensions for this file type.  Don't add one. */
1491         add_extension = FALSE;
1492     }
1493     if (add_extension) {
1494         if (wtap_default_file_extension(file_type) != NULL) {
1495             file_name += tr(".") + wtap_default_file_extension(file_type);
1496             if (compressed) {
1497                 file_name += ".gz";
1498             }
1499         }
1500     }
1501 }
1502
1503 bool MainWindow::testCaptureFileClose(bool from_quit, QString before_what, bool restart) {
1504     bool capture_in_progress = FALSE;
1505
1506     if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1507         return true; /* Already closed, nothing to do */
1508
1509 #ifdef HAVE_LIBPCAP
1510     if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS && capture_file_.capFile()->count>0) {
1511         /* This is true if we're reading a capture file *or* if we're doing
1512          a live capture.  If we're reading a capture file, the main loop
1513          is busy reading packets, and only accepting input from the
1514          progress dialog, so we can't get here, so this means we're
1515          doing a capture. */
1516         capture_in_progress = TRUE;
1517     }
1518 #endif
1519
1520     if (prefs.gui_ask_unsaved) {
1521         if (cf_has_unsaved_data(capture_file_.capFile()) || capture_in_progress) {
1522             QMessageBox msg_dialog;
1523             QString question;
1524             QPushButton *saveButton;
1525             QPushButton *discardButton;
1526
1527             msg_dialog.setIcon(QMessageBox::Question);
1528             msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1529             /* This file has unsaved data or there's a capture in
1530                progress; ask the user whether to save the data. */
1531             if (capture_file_.capFile()->is_tempfile) {
1532
1533                 msg_dialog.setText(tr("You have unsaved packets"));
1534                 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1535
1536                 if (capture_in_progress) {
1537                     question.append(tr("Do you want to stop the capture and save the captured packets"));
1538                 } else {
1539                     question.append(tr("Do you want to save the captured packets"));
1540                 }
1541                 question.append(before_what).append(tr("?"));
1542                 msg_dialog.setInformativeText(question);
1543
1544
1545             } else {
1546                 /*
1547                  * Format the message.
1548                  */
1549                 if (capture_in_progress) {
1550                     question.append(tr("Do you want to stop the capture and save the captured packets"));
1551                     question.append(before_what).append(tr("?"));
1552                     msg_dialog.setText(question);
1553                     msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1554                 } else {
1555                     gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1556                     question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1557                                     .arg(display_basename)
1558                                     .arg(before_what)
1559                                     );
1560                     g_free(display_basename);
1561                     msg_dialog.setText(question);
1562                     msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1563                 }
1564             }
1565
1566             // XXX Text comes from ui/gtk/stock_icons.[ch]
1567             // Note that the button roles differ from the GTK+ version.
1568             // Cancel = RejectRole
1569             // Save = AcceptRole
1570             // Don't Save = DestructiveRole
1571             msg_dialog.addButton(QMessageBox::Cancel);
1572
1573             if (capture_in_progress) {
1574                 if (restart) {
1575                     saveButton = msg_dialog.addButton(tr("Save before Continue"), QMessageBox::AcceptRole);
1576                 } else {
1577                     saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1578                 }
1579             } else {
1580                 saveButton = msg_dialog.addButton(QMessageBox::Save);
1581             }
1582             msg_dialog.setDefaultButton(saveButton);
1583
1584             if (from_quit) {
1585                 if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1586                     discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1587                                                          QMessageBox::DestructiveRole);
1588                 } else {
1589                     discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1590                                                          QMessageBox::DestructiveRole);
1591                 }
1592             } else {
1593                 if (capture_in_progress) {
1594                     if (restart) {
1595                         discardButton = msg_dialog.addButton(tr("Continue without Saving"),
1596                                                              QMessageBox::DestructiveRole);
1597                     }
1598                     else {
1599                         discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1600                                                              QMessageBox::DestructiveRole);
1601                     }
1602                 } else {
1603                     discardButton = msg_dialog.addButton(tr("Continue &without Saving"), QMessageBox::DestructiveRole);
1604                 }
1605             }
1606
1607             msg_dialog.exec();
1608             /* According to the Qt doc:
1609              * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1610              *
1611              * Therefore we should use clickedButton() to determine which button was clicked. */
1612
1613             if(msg_dialog.clickedButton() == saveButton)
1614             {
1615 #ifdef HAVE_LIBPCAP
1616                 /* If there's a capture in progress, we have to stop the capture
1617              and then do the save. */
1618                 if (capture_in_progress)
1619                     captureStop();
1620 #endif
1621                 /* Save the file and close it */
1622                 saveCaptureFile(capture_file_.capFile(), TRUE);
1623             }
1624             else if(msg_dialog.clickedButton() == discardButton)
1625             {
1626                 if (!restart)
1627                 {
1628 #ifdef HAVE_LIBPCAP
1629                     /*
1630                      * If there's a capture in progress; we have to stop the capture
1631                      * and then do the close.
1632                      */
1633                     if (capture_in_progress)
1634                         captureStop();
1635 #endif
1636                     /* Just close the file, discarding changes */
1637                     cf_close(capture_file_.capFile());
1638                 }
1639                 return true;
1640             }
1641             else    //cancelButton or some other unspecified button
1642             {
1643                 return false;
1644             }
1645
1646         } else {
1647             /* Unchanged file, just close it */
1648             capture_file_.capFile()->state = FILE_READ_ABORTED;
1649             cf_close(capture_file_.capFile());
1650         }
1651     } else {
1652         /* User asked not to be bothered by those prompts, just close it.
1653          XXX - should that apply only to saving temporary files? */
1654 #ifdef HAVE_LIBPCAP
1655         /* If there's a capture in progress, we have to stop the capture
1656            and then do the close. */
1657         if (capture_in_progress)
1658             captureStop();
1659 #endif
1660         cf_close(capture_file_.capFile());
1661     }
1662
1663     return true; /* File closed */
1664 }
1665
1666 void MainWindow::captureStop() {
1667     stopCapture();
1668
1669     while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1670         WiresharkApplication::processEvents();
1671     }
1672 }
1673
1674 void MainWindow::initMainToolbarIcons()
1675 {
1676     // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1677     int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1678 #if !defined(Q_OS_WIN)
1679     // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1680     // The OS X HIG specifies 32-pixel icons but they're a little too
1681     // large IMHO.
1682     icon_size = icon_size * 3 / 2;
1683 #endif
1684     main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1685
1686     // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1687     // toolbar item but that clutters up our menu. Set menu icons sparingly.
1688
1689     main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1690     main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1691     main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1692     main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1693
1694     // Menu icons are disabled in main_window.ui for these items.
1695     main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1696     main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1697     main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1698     main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1699
1700     main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1701     main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1702     main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1703     main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1704     main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1705     main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1706     main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1707     main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1708 #if defined(Q_OS_MAC)
1709     main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1710     main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1711 #endif
1712     main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1713
1714     main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1715 //    main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1716
1717     QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1718     zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1719     main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1720     main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1721     main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1722     main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1723     main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1724 }
1725
1726 void MainWindow::initShowHideMainWidgets()
1727 {
1728     if (show_hide_actions_) {
1729         return;
1730     }
1731
1732     show_hide_actions_ = new QActionGroup(this);
1733     QMap<QAction *, QWidget *> shmw_actions;
1734
1735     show_hide_actions_->setExclusive(false);
1736     shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1737     shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1738     shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1739     shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1740     shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1741     shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1742     shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1743
1744     foreach (QAction *shmwa, shmw_actions.keys()) {
1745         shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1746         show_hide_actions_->addAction(shmwa);
1747         showHideMainWidgets(shmwa);
1748     }
1749
1750     connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1751 }
1752
1753 Q_DECLARE_METATYPE(ts_type)
1754
1755 void MainWindow::initTimeDisplayFormatMenu()
1756 {
1757     if (time_display_actions_) {
1758         return;
1759     }
1760
1761     time_display_actions_ = new QActionGroup(this);
1762
1763     td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1764     td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1765     td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1766     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1767     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1768     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1769     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1770     td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1771     td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1772     td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1773
1774     foreach (QAction* tda, td_actions.keys()) {
1775         tda->setData(qVariantFromValue(td_actions[tda]));
1776         time_display_actions_->addAction(tda);
1777     }
1778
1779     connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1780 }
1781
1782 Q_DECLARE_METATYPE(ts_precision)
1783
1784 void MainWindow::initTimePrecisionFormatMenu()
1785 {
1786     if (time_precision_actions_) {
1787         return;
1788     }
1789
1790     time_precision_actions_ = new QActionGroup(this);
1791
1792     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1793     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1794     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1795     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1796     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1797     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1798     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1799
1800     foreach (QAction* tpa, tp_actions.keys()) {
1801         tpa->setData(qVariantFromValue(tp_actions[tpa]));
1802         time_precision_actions_->addAction(tpa);
1803     }
1804
1805     connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1806 }
1807
1808 // Menu items which will be disabled when we freeze() and whose state will
1809 // be restored when we thaw(). Add to the list as needed.
1810 void MainWindow::initFreezeActions()
1811 {
1812     QList<QAction *> freeze_actions = QList<QAction *>()
1813             << main_ui_->actionFileClose
1814             << main_ui_->actionViewReload
1815             << main_ui_->actionEditMarkPacket
1816             << main_ui_->actionEditMarkAllDisplayed
1817             << main_ui_->actionEditUnmarkAllDisplayed
1818             << main_ui_->actionEditIgnorePacket
1819             << main_ui_->actionEditIgnoreAllDisplayed
1820             << main_ui_->actionEditUnignoreAllDisplayed
1821             << main_ui_->actionEditSetTimeReference
1822             << main_ui_->actionEditUnsetAllTimeReferences;
1823
1824     foreach (QAction *action, freeze_actions) {
1825         freeze_actions_ << QPair<QAction *, bool>(action, false);
1826     }
1827 }
1828
1829 // Titlebar
1830 void MainWindow::setTitlebarForCaptureFile()
1831 {
1832     if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1833         //
1834         // Qt *REALLY* doesn't like windows that sometimes have a
1835         // title set with setWindowTitle() and other times have a
1836         // file path set; apparently, once you've set the title
1837         // with setWindowTitle(), it sticks, and setWindowFilePath()
1838         // has no effect.  It appears to can clear the title with
1839         // setWindowTitle(NULL), but that clears the actual title in
1840         // the title bar, and setWindowFilePath() then, I guess, sees
1841         // that there's already a file path, and does nothing, leaving
1842         // the title bar empty.  So you then have to clear the file path
1843         // with setWindowFilePath(NULL), and then set it.
1844         //
1845         // Maybe there's a #include "you're holding it wrong" here.
1846         // However, I really don't want to hear from people who think
1847         // that a window can never be associated with something other
1848         // than a user file at time T1 and with a user file at time T2,
1849         // given that, in Wireshark, a window can be associated with a
1850         // live capture at time T1 and then, after you've saved the live
1851         // capture to a user file, associated with a user file at time T2.
1852         //
1853         if (capture_file_.capFile()->is_tempfile) {
1854             //
1855             // For a temporary file, put the source of the data
1856             // in the window title, not whatever random pile
1857             // of characters is the last component of the path
1858             // name.
1859             //
1860             // XXX - on non-Mac platforms, put in the application
1861             // name?
1862             //
1863             setWindowFilePath(NULL);
1864             setWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1865         } else {
1866             //
1867             // For a user file, set the full path; that way,
1868             // for OS X, it'll set the "proxy icon".  Qt
1869             // handles extracting the last component.
1870             //
1871             // Sadly, some UN*Xes don't necessarily use UTF-8
1872             // for their file names, so we have to map the
1873             // file path to UTF-8.  If that fails, we're somewhat
1874             // stuck.
1875             //
1876             char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1877                                                      -1,
1878                                                      NULL,
1879                                                      NULL,
1880                                                      NULL);
1881             if (utf8_filename == NULL) {
1882                 // So what the heck else can we do here?
1883                 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1884             } else {
1885                 setWindowTitle(NULL);
1886                 setWindowFilePath(NULL);
1887                 setWindowFilePath(utf8_filename);
1888                 g_free(utf8_filename);
1889             }
1890         }
1891         setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1892     } else {
1893         /* We have no capture file. */
1894         setWindowFilePath(NULL);
1895         setDefaultWindowTitle();
1896     }
1897 }
1898
1899 void MainWindow::setDefaultWindowTitle()
1900 {
1901     setWindowTitle(tr("The Wireshark Network Analyzer"));
1902 }
1903
1904 void MainWindow::setTitlebarForCaptureInProgress()
1905 {
1906     setWindowFilePath(NULL);
1907     if (capture_file_.capFile()) {
1908         setWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1909     } else {
1910         /* We have no capture in progress. */
1911         setDefaultWindowTitle();
1912     }
1913 }
1914
1915 // Menu state
1916
1917 /* Enable or disable menu items based on whether you have a capture file
1918    you've finished reading and, if you have one, whether it's been saved
1919    and whether it could be saved except by copying the raw packet data. */
1920 void MainWindow::setMenusForCaptureFile(bool force_disable)
1921 {
1922     if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1923         /* We have no capture file or we're currently reading a file */
1924         main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(false);
1925         main_ui_->actionFileMerge->setEnabled(false);
1926         main_ui_->actionFileClose->setEnabled(false);
1927         main_ui_->actionFileSave->setEnabled(false);
1928         main_ui_->actionFileSaveAs->setEnabled(false);
1929         main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1930         main_ui_->actionFileExportPackets->setEnabled(false);
1931         main_ui_->menuFileExportPacketDissections->setEnabled(false);
1932         main_ui_->actionFileExportPacketBytes->setEnabled(false);
1933         main_ui_->actionFileExportPDU->setEnabled(false);
1934         main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1935         main_ui_->menuFileExportObjects->setEnabled(false);
1936         main_ui_->actionViewReload->setEnabled(false);
1937     } else {
1938         main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(true);
1939         main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1940
1941         main_ui_->actionFileClose->setEnabled(true);
1942         main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1943         main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1944         main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1945         /*
1946          * "Export Specified Packets..." should be available only if
1947          * we can write the file out in at least one format.
1948          */
1949         main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1950         main_ui_->menuFileExportPacketDissections->setEnabled(true);
1951         main_ui_->actionFileExportPacketBytes->setEnabled(true);
1952         main_ui_->actionFileExportPDU->setEnabled(true);
1953         main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1954         main_ui_->menuFileExportObjects->setEnabled(true);
1955         main_ui_->actionViewReload->setEnabled(true);
1956     }
1957 }
1958
1959 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1960     /* Either a capture was started or stopped; in either case, it's not
1961        in the process of stopping, so allow quitting. */
1962
1963     main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1964     main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1965     main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1966     main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1967     main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1968     main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1969     main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1970     main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1971     main_ui_->actionFileQuit->setEnabled(true);
1972
1973     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1974
1975     // XXX Fix packet list heading menu sensitivity
1976     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1977     //                         !capture_in_progress);
1978     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1979     //                         !capture_in_progress);
1980     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1981     //                         !capture_in_progress);
1982
1983 #ifdef HAVE_LIBPCAP
1984     main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1985     main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1986     main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1987     main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1988     main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1989     main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1990 #endif /* HAVE_LIBPCAP */
1991
1992 }
1993
1994 void MainWindow::setMenusForCaptureStopping() {
1995     main_ui_->actionFileQuit->setEnabled(false);
1996     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1997 #ifdef HAVE_LIBPCAP
1998     main_ui_->actionCaptureStart->setChecked(false);
1999     main_ui_->actionCaptureStop->setEnabled(false);
2000     main_ui_->actionCaptureRestart->setEnabled(false);
2001 #endif /* HAVE_LIBPCAP */
2002 }
2003
2004 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2005 {
2006     main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2007
2008 //    set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2009 //                         have_captured_packets);
2010
2011     main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2012     main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2013     main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2014
2015     main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2016     main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2017     main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2018     main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2019     main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2020     main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2021     main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2022
2023     main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2024     main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2025     main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2026     main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2027
2028     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2029     main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2030     main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2031 }
2032
2033 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2034     bool enable_next = fileset_get_next() != NULL && enable_list_files;
2035     bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2036
2037     main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2038     main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2039     main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2040 }
2041
2042 void MainWindow::setWindowIcon(const QIcon &icon) {
2043     wsApp->setWindowIcon(icon);
2044     QMainWindow::setWindowIcon(icon);
2045 }
2046
2047 void MainWindow::updateForUnsavedChanges() {
2048     setTitlebarForCaptureFile();
2049     setMenusForCaptureFile();
2050 }
2051
2052 void MainWindow::changeEvent(QEvent* event)
2053 {
2054     if (0 != event)
2055     {
2056         switch (event->type())
2057         {
2058         case QEvent::LanguageChange:
2059             main_ui_->retranslateUi(this);
2060             // make sure that the "Clear Menu" item is retranslated
2061             updateRecentFiles();
2062             break;
2063         case QEvent::LocaleChange:{
2064             QString locale = QLocale::system().name();
2065             locale.truncate(locale.lastIndexOf('_'));
2066             wsApp->loadLanguage(locale);
2067             }
2068             break;
2069         default:
2070             break;
2071         }
2072     }
2073     QMainWindow::changeEvent(event);
2074 }
2075
2076 /* Update main window items based on whether there's a capture in progress. */
2077 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2078 {
2079     setMenusForCaptureInProgress(capture_in_progress);
2080
2081     wireless_frame_->setCaptureInProgress(capture_in_progress);
2082
2083 #ifdef HAVE_LIBPCAP
2084     packet_list_->setCaptureInProgress(capture_in_progress);
2085 //    set_toolbar_for_capture_in_progress(capture_in_progress);
2086
2087 //    set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2088 #endif
2089 }
2090
2091 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2092             << REGISTER_ANALYZE_GROUP_UNSORTED
2093             << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2094             << REGISTER_STAT_GROUP_UNSORTED
2095             << REGISTER_STAT_GROUP_GENERIC
2096             << REGISTER_STAT_GROUP_CONVERSATION_LIST
2097             << REGISTER_STAT_GROUP_ENDPOINT_LIST
2098             << REGISTER_STAT_GROUP_RESPONSE_TIME
2099             << REGISTER_STAT_GROUP_TELEPHONY
2100             << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2101             << REGISTER_STAT_GROUP_TELEPHONY_GSM
2102             << REGISTER_STAT_GROUP_TELEPHONY_LTE
2103             << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2104             << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2105             << REGISTER_TOOLS_GROUP_UNSORTED;
2106
2107 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2108 {
2109     foreach (QAction *action, actions) {
2110         switch (menu_group) {
2111         case REGISTER_ANALYZE_GROUP_UNSORTED:
2112         case REGISTER_STAT_GROUP_UNSORTED:
2113             main_ui_->menuStatistics->insertAction(
2114                             main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2115                             action);
2116             break;
2117         case REGISTER_STAT_GROUP_RESPONSE_TIME:
2118             main_ui_->menuServiceResponseTime->addAction(action);
2119             break;
2120         case REGISTER_STAT_GROUP_TELEPHONY:
2121             main_ui_->menuTelephony->addAction(action);
2122             break;
2123         case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2124             main_ui_->menuANSI->addAction(action);
2125             break;
2126         case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2127             main_ui_->menuGSM->addAction(action);
2128             break;
2129         case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2130             main_ui_->menuLTE->addAction(action);
2131             break;
2132         case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2133             main_ui_->menuMTP3->addAction(action);
2134             break;
2135         case REGISTER_TOOLS_GROUP_UNSORTED:
2136         {
2137             // Allow the creation of submenus. Mimics the behavor of
2138             // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2139             // and GtkUIManager.
2140             //
2141             // For now we limit the insanity to the "Tools" menu.
2142             QStringList menu_path = action->text().split('/');
2143             QMenu *cur_menu = main_ui_->menuTools;
2144             while (menu_path.length() > 1) {
2145                 QString menu_title = menu_path.takeFirst();
2146 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2147                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2148 #else
2149                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2150                 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2151 #endif
2152                 if (!submenu) {
2153                     submenu = cur_menu->addMenu(menu_title);
2154                     submenu->setObjectName(menu_title.toLower());
2155                 }
2156                 cur_menu = submenu;
2157             }
2158             action->setText(menu_path.last());
2159             cur_menu->addAction(action);
2160             break;
2161         }
2162         default:
2163 //            qDebug() << "FIX: Add" << action->text() << "to the menu";
2164             break;
2165         }
2166
2167         // Connect each action type to its corresponding slot. We to
2168         // distinguish various types of actions. Setting their objectName
2169         // seems to work OK.
2170         if (action->objectName() == TapParameterDialog::actionName()) {
2171             connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2172         } else if (action->objectName() == FunnelStatistics::actionName()) {
2173             connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2174         }
2175     }
2176 }
2177 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2178 {
2179     foreach (QAction *action, actions) {
2180         switch (menu_group) {
2181         case REGISTER_ANALYZE_GROUP_UNSORTED:
2182         case REGISTER_STAT_GROUP_UNSORTED:
2183             main_ui_->menuStatistics->removeAction(action);
2184             break;
2185         case REGISTER_STAT_GROUP_RESPONSE_TIME:
2186             main_ui_->menuServiceResponseTime->removeAction(action);
2187             break;
2188         case REGISTER_STAT_GROUP_TELEPHONY:
2189             main_ui_->menuTelephony->removeAction(action);
2190             break;
2191         case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2192             main_ui_->menuANSI->removeAction(action);
2193             break;
2194         case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2195             main_ui_->menuGSM->removeAction(action);
2196             break;
2197         case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2198             main_ui_->menuLTE->removeAction(action);
2199             break;
2200         case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2201             main_ui_->menuMTP3->removeAction(action);
2202             break;
2203         case REGISTER_TOOLS_GROUP_UNSORTED:
2204         {
2205             // Allow removal of submenus.
2206             // For now we limit the insanity to the "Tools" menu.
2207             QStringList menu_path = action->text().split('/');
2208             QMenu *cur_menu = main_ui_->menuTools;
2209             while (menu_path.length() > 1) {
2210                 QString menu_title = menu_path.takeFirst();
2211 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2212                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2213 #else
2214                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2215                 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2216 #endif
2217                 cur_menu = submenu;
2218             }
2219             cur_menu->removeAction(action);
2220             break;
2221         }
2222         default:
2223 //            qDebug() << "FIX: Remove" << action->text() << "from the menu";
2224             break;
2225         }
2226     }
2227 }
2228
2229 void MainWindow::addDynamicMenus()
2230 {
2231     // Manual additions
2232     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2233     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2234     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2235     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2236     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2237     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2238
2239     // Fill in each menu
2240     foreach (register_stat_group_t menu_group, menu_groups) {
2241         QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2242         addMenuActions(actions, menu_group);
2243     }
2244
2245     // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2246     // We've added a placeholder in order to make sure the "Tools" menu is
2247     // visible. Hide it as needed.
2248     if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2249         main_ui_->actionToolsPlaceholder->setVisible(false);
2250     }
2251     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2252         main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2253     }
2254     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2255         main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2256     }
2257     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2258         main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2259     }
2260     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2261         main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2262     }
2263 }
2264
2265 void MainWindow::reloadDynamicMenus()
2266 {
2267     foreach (register_stat_group_t menu_group, menu_groups) {
2268         QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2269         removeMenuActions(actions, menu_group);
2270
2271         actions = wsApp->addedMenuGroupItems(menu_group);
2272         addMenuActions(actions, menu_group);
2273     }
2274
2275     wsApp->clearAddedMenuGroupItems();
2276     wsApp->clearRemovedMenuGroupItems();
2277 }
2278
2279 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu  * subMenu, gint depth)
2280 {
2281     QAction * itemAction = NULL;
2282     ext_menubar_t * item = NULL;
2283     GList * children = NULL;
2284
2285     /* There must exists an xpath parent */
2286     g_assert(subMenu != NULL);
2287
2288     /* If the depth counter exceeds, something must have gone wrong */
2289     g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2290
2291     children = menu->children;
2292     /* Iterate the child entries */
2293     while ( children != NULL && children->data != NULL )
2294     {
2295         item = (ext_menubar_t *) children->data;
2296
2297         if ( item->type == EXT_MENUBAR_MENU )
2298         {
2299             /* Handle Submenu entry */
2300             this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++ );
2301         }
2302         else if ( item->type == EXT_MENUBAR_SEPARATOR )
2303         {
2304             subMenu->addSeparator();
2305         }
2306         else if ( item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL )
2307         {
2308             itemAction = subMenu->addAction(item->name);
2309             itemAction->setData(QVariant::fromValue((void *)item));
2310             itemAction->setText(item->label);
2311             connect(itemAction, SIGNAL(triggered()),
2312                     this, SLOT(externalMenuItem_triggered()));
2313         }
2314
2315         /* Iterate Loop */
2316         children = g_list_next(children);
2317     }
2318 }
2319
2320 QMenu * MainWindow::searchSubMenu(QString objectName)
2321 {
2322     QList<QMenu*> lst;
2323
2324     if ( objectName.length() > 0 )
2325     {
2326         QString searchName = QString("menu") + objectName;
2327
2328         lst = main_ui_->menuBar->findChildren<QMenu*>();
2329         foreach (QMenu* m, lst)
2330         {
2331             if ( QString::compare( m->objectName(), searchName ) == 0 )
2332                 return m;
2333         }
2334     }
2335
2336     return 0;
2337 }
2338
2339 void MainWindow::addExternalMenus()
2340 {
2341     QMenu * subMenu = NULL;
2342     GList * user_menu = NULL;
2343     ext_menu_t * menu = NULL;
2344
2345     user_menu = ext_menubar_get_entries();
2346
2347     while ( ( user_menu != NULL ) && ( user_menu->data != NULL ) )
2348     {
2349         menu = (ext_menu_t *) user_menu->data;
2350
2351         /* On this level only menu items should exist. Not doing an assert here,
2352          * as it could be an honest mistake */
2353         if ( menu->type != EXT_MENUBAR_MENU )
2354         {
2355             user_menu = g_list_next(user_menu);
2356             continue;
2357         }
2358
2359         /* Create main submenu and add it to the menubar */
2360         if ( menu->parent_menu != NULL )
2361         {
2362             QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2363             if ( sortUnderneath != NULL)
2364                 subMenu = sortUnderneath->addMenu(menu->label);
2365         }
2366
2367         if ( subMenu == NULL )
2368             subMenu = main_ui_->menuBar->addMenu(menu->label);
2369
2370         /* This will generate the action structure for each menu. It is recursive,
2371          * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2372         this->externalMenuHelper(menu, subMenu, 0);
2373
2374         /* Iterate Loop */
2375         user_menu = g_list_next (user_menu);
2376     }
2377 }
2378
2379 /*
2380  * Editor modelines
2381  *
2382  * Local Variables:
2383  * c-basic-offset: 4
2384  * tab-width: 8
2385  * indent-tabs-mode: nil
2386  * End:
2387  *
2388  * ex: set shiftwidth=4 tabstop=8 expandtab:
2389  * :indentSize=4:tabSize=8:noTabs=true:
2390  */