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