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