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