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