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